<commit_before>/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*  Monkey HTTP Server
 *  ==================
 *  Copyright 2001-2015 Monkey Software LLC <<EMAIL>>
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include <ctype.h>
#include <string.h>
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>


#include <mk_core/mk_rconf.h>
#include <mk_core/mk_utils.h>
#include <mk_core/mk_string.h>
#include <mk_core/mk_list.h>

/* Raise a configuration schema error */
static void mk_config_error(const char *path, int line, const char *msg)
{
    mk_err("File %s", path);
    mk_err("Error in line %i: %s", line, msg);
    exit(EXIT_FAILURE);
}

/* Raise a warning */
static void mk_rconf_warning(const char *path, int line, const char *msg)
{
    mk_warn("Config file warning '%s':\n"
            "\t\t\t\tat line %i: %s",
            path, line, msg);
}


/* Returns a configuration section by [section name] */
struct mk_rconf_section *mk_rconf_section_get(struct mk_rconf *conf,
                                              const char *name)
{
    struct mk_list *head;
    struct mk_rconf_section *section;

    mk_list_foreach(head, &conf->sections) {
        section = mk_list_entry(head, struct mk_rconf_section, _head);
        if (strcasecmp(section->name, name) == 0) {
            return section;
        }
    }

    return NULL;
}

/* Register a key/value entry in the last section available of the struct */
static void mk_rconf_section_entry_add(struct mk_rconf *conf,
                                       const char *key, const char *val)
{
    struct mk_rconf_section *section;
    struct mk_rconf_entry *new;
    struct mk_list *head = &conf->sections;

    if (mk_list_is_empty(&conf->sections) == 0) {
        mk_err("Error: there are not sections available on %s!", conf->file);
        return;
    }

    /* Last section */
    section = mk_list_entry_last(head, struct mk_rconf_section, _head);

    /* Alloc new entry */
    new = mk_mem_alloc(sizeof(struct mk_rconf_entry));
    new->key = mk_string_dup(key);
    new->val = mk_string_dup(val);

    mk_list_add(&new->_head, &section->entries);
}

/* Create a configuration schema */
struct mk_rconf *mk_rconf_create(const char *name)
{
    struct mk_rconf *conf = NULL;

    /* Alloc configuration node */
    conf = mk_mem_alloc_z(sizeof(struct mk_rconf));
    conf->created = time(NULL);
    conf->file = mk_string_dup(name);
    mk_list_init(&conf->sections);

    return conf;
}

static int is_file_included(struct mk_rconf *conf, const char *path)
{
    struct mk_list *head;
    struct mk_rconf_file *file;

    mk_list_foreach(head, &conf->includes) {
        file = mk_list_entry(head, struct mk_rconf_file, _head);
        if (strcmp(file->path, path) == 0) {
            return MK_TRUE;
        }
    }

    return MK_FALSE;
}

char *mk_rconf_meta_get(struct mk_rconf *conf, char *key)
{
    struct mk_list *head;
    struct mk_rconf_entry *meta;

    mk_list_foreach(head, &conf->metas) {
        meta = mk_list_entry(head, struct mk_rconf_entry, _head);
        if (strcmp(meta->key, key) == 0) {
            return meta->val;
        }
    }

    return NULL;
}

static int mk_rconf_meta_add(struct mk_rconf *conf, char *buf, int len)
{
    int xlen;
    char *p;
    char *tmp;
    struct mk_rconf_entry *meta;

    if (buf[0] != '@') {
        return -1;
    }

    meta = mk_mem_alloc(sizeof(struct mk_rconf_entry));
    if (!meta) {
        perror("malloc");
        return -1;
    }

    p = buf;
    tmp = strchr(p, ' ');
    xlen = (tmp - p);
    meta->key = mk_string_copy_substr(buf, 1, xlen);
    mk_string_trim(&meta->key);

    meta->val = mk_string_copy_substr(buf, xlen + 1, len);
    mk_string_trim(&meta->val);

    mk_list_add(&meta->_head, &conf->metas);
    return 0;
}

static int mk_rconf_read(struct mk_rconf *conf, const char *path)
{
    int i;
    int len;
    int ret;
    int line = 0;
    int indent_len = -1;
    int n_keys = 0;
    char buf[1024];
    char tmp[PATH_MAX];
    char *section = NULL;
    char *indent = NULL;
    char *key, *val;
    char *cfg_file = (char *) path;
    struct stat st;
    struct mk_rconf_file *file;
    struct mk_rconf_section *current = NULL;
    FILE *f;

    /* Check if the path exists (relative cases for included files) */
    if (conf->level >= 0) {
        ret = stat(path, &st);
        if (ret == -1 && errno == ENOENT) {
            /* Try to resolve the real path (if exists) */
            if (path[0] == '/') {
                return -1;
            }

            if (conf->root_path) {
                snprintf(tmp, PATH_MAX, "%s/%s", conf->root_path, path);
                cfg_file = tmp;
            }
        }
    }

    /* Check this file have not been included before */
    ret = is_file_included(conf, cfg_file);
    if (ret == MK_TRUE) {
        mk_err("[config] file already included %s", cfg_file);
        return -1;
    }

    conf->level++;

    /* Open configuration file */
    if ((f = fopen(cfg_file, "r")) == NULL) {
        mk_warn("[config] I cannot open %s file", cfg_file);
        return -1;
    }

    /* looking for configuration directives */
    while (fgets(buf, 1024, f)) {
        len = strlen(buf);
        if (buf[len - 1] == '\n') {
            buf[--len] = 0;
            if (len && buf[len - 1] == '\r') {
                buf[--len] = 0;
            }
        }

        /* Line number */
        line++;

        if (!buf[0]) {
            continue;
        }

        /* Skip commented lines */
        if (buf[0] == '#') {
            continue;
        }

        if (indent_len == -1 && len > 1) {
            if (len > 9 && strncasecmp(buf, "@INCLUDE ", 9) == 0) {
                ret = mk_rconf_read(conf, buf + 9);
                if (ret == -1) {
                    conf->level--;
                    return -1;
                }
                continue;
            }
            else if (buf[0] == '@' && len > 3) {
                ret = mk_rconf_meta_add(conf, buf, len);
                if (ret == -1) {
                    return -1;
                }
                continue;
            }
        }

        /* Section definition */
        if (buf[0] == '[') {
            int end = -1;
            end = mk_string_char_search(buf, ']', len);
            if (end > 0) {
                /*
                 * Before to add a new section, lets check the previous
                 * one have at least one key set
                 */
                if (current && n_keys == 0) {
                    mk_rconf_warning(path, line, "Previous section did not have keys");
                }

                /* Create new section */
                section = mk_string_copy_substr(buf, 1, end);
                current = mk_rconf_section_add(conf, section);
                mk_mem_free(section);
                n_keys = 0;
                continue;
            }
            else {
                mk_config_error(path, line, "Bad header definition");
            }
        }
<commit_msg>core: rconf: fix leak when config fails (CID 165165)<commit_after>/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*  Monkey HTTP Server
 *  ==================
 *  Copyright 2001-2015 Monkey Software LLC <<EMAIL>>
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include <ctype.h>
#include <string.h>
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>


#include <mk_core/mk_rconf.h>
#include <mk_core/mk_utils.h>
#include <mk_core/mk_string.h>
#include <mk_core/mk_list.h>

/* Raise a configuration schema error */
static void mk_config_error(const char *path, int line, const char *msg)
{
    mk_err("File %s", path);
    mk_err("Error in line %i: %s", line, msg);
    exit(EXIT_FAILURE);
}

/* Raise a warning */
static void mk_rconf_warning(const char *path, int line, const char *msg)
{
    mk_warn("Config file warning '%s':\n"
            "\t\t\t\tat line %i: %s",
            path, line, msg);
}


/* Returns a configuration section by [section name] */
struct mk_rconf_section *mk_rconf_section_get(struct mk_rconf *conf,
                                              const char *name)
{
    struct mk_list *head;
    struct mk_rconf_section *section;

    mk_list_foreach(head, &conf->sections) {
        section = mk_list_entry(head, struct mk_rconf_section, _head);
        if (strcasecmp(section->name, name) == 0) {
            return section;
        }
    }

    return NULL;
}

/* Register a key/value entry in the last section available of the struct */
static void mk_rconf_section_entry_add(struct mk_rconf *conf,
                                       const char *key, const char *val)
{
    struct mk_rconf_section *section;
    struct mk_rconf_entry *new;
    struct mk_list *head = &conf->sections;

    if (mk_list_is_empty(&conf->sections) == 0) {
        mk_err("Error: there are not sections available on %s!", conf->file);
        return;
    }

    /* Last section */
    section = mk_list_entry_last(head, struct mk_rconf_section, _head);

    /* Alloc new entry */
    new = mk_mem_alloc(sizeof(struct mk_rconf_entry));
    new->key = mk_string_dup(key);
    new->val = mk_string_dup(val);

    mk_list_add(&new->_head, &section->entries);
}

/* Create a configuration schema */
struct mk_rconf *mk_rconf_create(const char *name)
{
    struct mk_rconf *conf = NULL;

    /* Alloc configuration node */
    conf = mk_mem_alloc_z(sizeof(struct mk_rconf));
    conf->created = time(NULL);
    conf->file = mk_string_dup(name);
    mk_list_init(&conf->sections);

    return conf;
}

static int is_file_included(struct mk_rconf *conf, const char *path)
{
    struct mk_list *head;
    struct mk_rconf_file *file;

    mk_list_foreach(head, &conf->includes) {
        file = mk_list_entry(head, struct mk_rconf_file, _head);
        if (strcmp(file->path, path) == 0) {
            return MK_TRUE;
        }
    }

    return MK_FALSE;
}

char *mk_rconf_meta_get(struct mk_rconf *conf, char *key)
{
    struct mk_list *head;
    struct mk_rconf_entry *meta;

    mk_list_foreach(head, &conf->metas) {
        meta = mk_list_entry(head, struct mk_rconf_entry, _head);
        if (strcmp(meta->key, key) == 0) {
            return meta->val;
        }
    }

    return NULL;
}

static int mk_rconf_meta_add(struct mk_rconf *conf, char *buf, int len)
{
    int xlen;
    char *p;
    char *tmp;
    struct mk_rconf_entry *meta;

    if (buf[0] != '@') {
        return -1;
    }

    meta = mk_mem_alloc(sizeof(struct mk_rconf_entry));
    if (!meta) {
        perror("malloc");
        return -1;
    }

    p = buf;
    tmp = strchr(p, ' ');
    xlen = (tmp - p);
    meta->key = mk_string_copy_substr(buf, 1, xlen);
    mk_string_trim(&meta->key);

    meta->val = mk_string_copy_substr(buf, xlen + 1, len);
    mk_string_trim(&meta->val);

    mk_list_add(&meta->_head, &conf->metas);
    return 0;
}

static int mk_rconf_read(struct mk_rconf *conf, const char *path)
{
    int i;
    int len;
    int ret;
    int line = 0;
    int indent_len = -1;
    int n_keys = 0;
    char buf[1024];
    char tmp[PATH_MAX];
    char *section = NULL;
    char *indent = NULL;
    char *key, *val;
    char *cfg_file = (char *) path;
    struct stat st;
    struct mk_rconf_file *file;
    struct mk_rconf_section *current = NULL;
    FILE *f;

    /* Check if the path exists (relative cases for included files) */
    if (conf->level >= 0) {
        ret = stat(path, &st);
        if (ret == -1 && errno == ENOENT) {
            /* Try to resolve the real path (if exists) */
            if (path[0] == '/') {
                return -1;
            }

            if (conf->root_path) {
                snprintf(tmp, PATH_MAX, "%s/%s", conf->root_path, path);
                cfg_file = tmp;
            }
        }
    }

    /* Check this file have not been included before */
    ret = is_file_included(conf, cfg_file);
    if (ret == MK_TRUE) {
        mk_err("[config] file already included %s", cfg_file);
        return -1;
    }

    conf->level++;

    /* Open configuration file */
    if ((f = fopen(cfg_file, "r")) == NULL) {
        mk_warn("[config] I cannot open %s file", cfg_file);
        return -1;
    }

    /* looking for configuration directives */
    while (fgets(buf, 1024, f)) {
        len = strlen(buf);
        if (buf[len - 1] == '\n') {
            buf[--len] = 0;
            if (len && buf[len - 1] == '\r') {
                buf[--len] = 0;
            }
        }

        /* Line number */
        line++;

        if (!buf[0]) {
            continue;
        }

        /* Skip commented lines */
        if (buf[0] == '#') {
            continue;
        }

        if (indent_len == -1 && len > 1) {
            if (len > 9 && strncasecmp(buf, "@INCLUDE ", 9) == 0) {
                ret = mk_rconf_read(conf, buf + 9);
                if (ret == -1) {
                    conf->level--;
                    fclose(f);
                    return -1;
                }
                continue;
            }
            else if (buf[0] == '@' && len > 3) {
                ret = mk_rconf_meta_add(conf, buf, len);
                if (ret == -1) {
                    fclose(f);
                    return -1;
                }
                continue;
            }
        }

        /* Section definition */
        if (buf[0] == '[') {
            int end = -1;
            end = mk_string_char_search(buf, ']', len);
            if (end > 0) {
                /*
                 * Before to add a new section, lets check the previous
                 * one have at least one key set
                 */
                if (current && n_keys == 0) {
                    mk_rconf_warning(path, line, "Previous section did not have keys");
                }

                /* Create new section */
                section = mk_string_copy_substr(buf, 1, end);
                current = mk_rconf_section_add(conf, section);
                mk_mem_free(section);
                n_keys = 0;
                continue;
            }
            else {
                mk_config_error(path, line, "Bad header definition");
            }
        }

{'fraction_non_alphanumeric': 0.12907014681892331, 'fraction_numerical': 0.008874388254486134, 'mean_word_length': 1.9209071850581285, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 95, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6494737', 'n_tokens_mistral': 5081, 'n_tokens_neox': 4864, 'n_words': 1356}