diff --git a/include/applet/adguard.h b/include/applet/adguard.h index 5c749c9..c904eb3 100644 --- a/include/applet/adguard.h +++ b/include/applet/adguard.h @@ -14,6 +14,7 @@ typedef struct { } adguard; adguard* adguard_init(); +void adguard_free(adguard *info); process* adguard_load(adguard *info, const char *dir); #endif diff --git a/include/applet/dnsproxy.h b/include/applet/dnsproxy.h index c8597de..68eb67c 100644 --- a/include/applet/dnsproxy.h +++ b/include/applet/dnsproxy.h @@ -16,6 +16,7 @@ typedef struct { char **primary; } dnsproxy; +void dnsproxy_free(dnsproxy *info); dnsproxy* dnsproxy_init(uint16_t port); void dnsproxy_add_primary(dnsproxy *info, const char *server); void dnsproxy_add_fallback(dnsproxy *info, const char *server); diff --git a/include/applet/overture.h b/include/applet/overture.h index 1c4f046..476942a 100644 --- a/include/applet/overture.h +++ b/include/applet/overture.h @@ -20,6 +20,7 @@ typedef struct { } overture; overture* overture_init(); +void overture_free(overture *info); process* overture_load(overture *info, const char *file); #endif diff --git a/include/common.h b/include/common.h index 5f5db0d..9524aa6 100644 --- a/include/common.h +++ b/include/common.h @@ -62,4 +62,11 @@ char* to_json(const char *config_file); cJSON* json_field_get(cJSON *entry, const char *field); void json_field_replace(cJSON *entry, const char *field, cJSON *content); +uint8_t json_bool_value(char *key, cJSON *json); +uint32_t** json_uint32_list_value(char *key, cJSON *json, uint32_t **uint32_list); +char** json_string_list_value(char *key, cJSON *json, char **string_list); +char* json_string_value(char* key, cJSON *json); +int json_int_value(char *key, cJSON *json); +uint8_t is_json_suffix(const char *file_name); + #endif diff --git a/include/loader/config.h b/include/loader/config.h index 347f06f..de1f2d0 100644 --- a/include/loader/config.h +++ b/include/loader/config.h @@ -1,7 +1,7 @@ #ifndef _CONFIG_H_ #define _CONFIG_H_ -#include "common.h" +#include typedef struct { uint16_t port; diff --git a/include/loader/loader.h b/include/loader/loader.h index 34dd326..d4c54db 100644 --- a/include/loader/loader.h +++ b/include/loader/loader.h @@ -1,14 +1,15 @@ #ifndef _LOADER_H_ #define _LOADER_H_ +#include "adguard.h" #include "dnsproxy.h" #include "overture.h" -struct { +extern struct { dnsproxy *domestic; dnsproxy *foreign; overture *diverter; - // adguard + adguard *filter; } loader; void load_config(const char *config_file); diff --git a/src/applet/adguard.c b/src/applet/adguard.c index 1c27f64..f17726c 100644 --- a/src/applet/adguard.c +++ b/src/applet/adguard.c @@ -7,9 +7,16 @@ void adguard_dump(adguard *info); char *adguard_config(adguard *info, const char *raw_config); -adguard* adguard_init() { +void adguard_free(adguard *info) { // free adguard options + free(info->upstream); + free(info->username); + free(info->password); + free(info); +} + +adguard* adguard_init() { // init adguard options adguard *info = (adguard *)malloc(sizeof(adguard)); - char *port_str = uint32_to_string(DIVERTER_PORT); + char *port_str = uint32_to_string(DIVERTER_PORT); // diverter port in string info->debug = FALSE; info->dns_port = DNS_PORT; info->web_port = ADGUARD_PORT; @@ -20,7 +27,7 @@ adguard* adguard_init() { return info; } -void adguard_dump(adguard *info) { // show adguard info in debug log +void adguard_dump(adguard *info) { // show adguard options in debug log log_debug("AdGuardHome debug -> %s", show_bool(info->debug)); log_debug("AdGuardHome dns port -> %u", info->dns_port); log_debug("AdGuardHome web port -> %u", info->web_port); @@ -35,7 +42,7 @@ char *adguard_config(adguard *info, const char *raw_config) { // modify adguard log_fatal("AdGuardHome configure error"); } - cJSON *user_config = cJSON_CreateObject(); + cJSON *user_config = cJSON_CreateObject(); // setting up username and password cJSON *users_config = cJSON_CreateArray(); char *password = gen_bcrypt(info->password); cJSON_AddItemToObject(user_config, "name", cJSON_CreateString(info->username)); @@ -44,7 +51,7 @@ char *adguard_config(adguard *info, const char *raw_config) { // modify adguard json_field_replace(json, "users", users_config); free(password); - cJSON *dns = json_field_get(json, "dns"); + cJSON *dns = json_field_get(json, "dns"); // setting up dns options cJSON *upstream = cJSON_CreateArray(); cJSON_AddItemToArray(upstream, cJSON_CreateString(info->upstream)); json_field_replace(dns, "port", cJSON_CreateNumber(info->dns_port)); @@ -53,12 +60,12 @@ char *adguard_config(adguard *info, const char *raw_config) { // modify adguard json_field_replace(dns, "upstream_dns_file", cJSON_CreateString("")); json_field_replace(dns, "bootstrap_dns", cJSON_CreateArray()); - char *config = cJSON_Print(json); + char *config = cJSON_Print(json); // generate json string cJSON_free(json); return config; } -process* adguard_load(adguard *info, const char *dir) { +process* adguard_load(adguard *info, const char *dir) { // load adguard options adguard_dump(info); if (!check_port(info->dns_port)) { // invalid dns port log_fatal("Invalid dns port `%u`", info->dns_port); @@ -67,7 +74,7 @@ process* adguard_load(adguard *info, const char *dir) { log_fatal("Invalid web port `%u`", info->web_port); } - create_folder(dir); + create_folder(dir); // ensure adguard work dir exist char *adguard_config_ret; char *adguard_config_file = string_join(dir, "AdGuardHome.yaml"); char *adguard_config_raw = to_json(adguard_config_file); @@ -81,12 +88,12 @@ process* adguard_load(adguard *info, const char *dir) { save_file(adguard_config_file, adguard_config_ret); // save modified configure free(adguard_config_file); - process *proc = process_init("AdGuardHome", ADGUARD_BIN); + process *proc = process_init("AdGuardHome", ADGUARD_BIN); // generate adguard command char *port_str = uint32_to_string(info->web_port); - process_add_arg(proc, "--no-check-update"); + process_add_arg(proc, "--no-check-update"); // disable update check (invalid in docker) process_add_arg(proc, "--work-dir"); process_add_arg(proc, dir); - process_add_arg(proc, "--port"); + process_add_arg(proc, "--port"); // web server port process_add_arg(proc, port_str); if (info->debug) { process_add_arg(proc, "--verbose"); // adguard enable debug mode diff --git a/src/applet/dnsproxy.c b/src/applet/dnsproxy.c index d1cecd5..fc0d148 100644 --- a/src/applet/dnsproxy.c +++ b/src/applet/dnsproxy.c @@ -20,6 +20,13 @@ void dnsproxy_add_bootstrap(dnsproxy *info, const char *server) { // add bootstr info->bootstrap = string_list_append(info->bootstrap, server); } +void dnsproxy_free(dnsproxy *info) { // free dnsproxy options + string_list_free(info->bootstrap); + string_list_free(info->fallback); + string_list_free(info->primary); + free(info); +} + dnsproxy* dnsproxy_init(uint16_t port) { // init dnsproxy options dnsproxy *info = (dnsproxy *)malloc(sizeof(dnsproxy)); info->port = port; @@ -34,7 +41,7 @@ dnsproxy* dnsproxy_init(uint16_t port) { // init dnsproxy options return info; } -void dnsproxy_dump(const char *caption, dnsproxy *info) { // show dnsproxy info in debug log +void dnsproxy_dump(const char *caption, dnsproxy *info) { // show dnsproxy options in debug log char *str_dump; log_debug("%s port -> %u", caption, info->port); log_debug("%s cache -> %u", caption, info->cache); @@ -56,7 +63,7 @@ void dnsproxy_dump(const char *caption, dnsproxy *info) { // show dnsproxy info free(str_dump); } -process* dnsproxy_load(const char *caption, dnsproxy *info, const char *file) { +process* dnsproxy_load(const char *caption, dnsproxy *info, const char *file) { // load dnsproxy options dnsproxy_dump(caption, info); if (!check_port(info->port)) { // invalid server port log_fatal("Invalid port `%u`", info->port); @@ -65,15 +72,15 @@ process* dnsproxy_load(const char *caption, dnsproxy *info, const char *file) { log_fatal("%s without primary dns server", caption); } - create_folder(WORK_DIR); - char *config = dnsproxy_config(info); // string config (JSON format) + create_folder(WORK_DIR); // ensure work dir exist + char *config = dnsproxy_config(info); // string config with json format char *config_file = string_join(WORK_DIR, file); save_file(config_file, config); // load dnsproxy configure free(config_file); free(config); char *option = string_join("--config-path=", file); - process *proc = process_init(caption, DNSPROXY_BIN); + process *proc = process_init(caption, DNSPROXY_BIN); // generate dnsproxy command process_add_arg(proc, option); if (info->debug) { process_add_arg(proc, "--verbose"); // dnsproxy enable debug mode diff --git a/src/applet/overture.c b/src/applet/overture.c index db5061b..6c9debb 100644 --- a/src/applet/overture.c +++ b/src/applet/overture.c @@ -8,11 +8,21 @@ void overture_dump(overture *info); char* overture_config(overture *info); +void overture_free(overture *info) { // free overture options + free(info->ttl_file); // free(NULL) is valid + free(info->host_file); + free(info->foreign_ip_file); + free(info->domestic_ip_file); + free(info->foreign_domain_file); + free(info->domestic_domain_file); + free(info); +} + overture* overture_init() { // init overture options overture *info = (overture *)malloc(sizeof(overture)); info->port = DIVERTER_PORT; info->debug = FALSE; - info->timeout = DIVERTER_TIMEOUT; // default timeout -> 6s + info->timeout = DIVERTER_TIMEOUT; // default timeout info->ttl_file = NULL; info->host_file = NULL; info->foreign_port = FOREIGN_PORT; @@ -25,7 +35,7 @@ overture* overture_init() { // init overture options return info; } -void overture_dump(overture *info) { // show overture info in debug log +void overture_dump(overture *info) { // show overture options in debug log char *reject_type = uint32_list_dump(info->reject_type); log_debug("Overture port -> %u", info->port); log_debug("Overture debug -> %s", show_bool(info->debug)); @@ -42,23 +52,23 @@ void overture_dump(overture *info) { // show overture info in debug log free(reject_type); } -process* overture_load(overture *info, const char *file) { +process* overture_load(overture *info, const char *file) { // load overture options overture_dump(info); if (!check_port(info->port)) { // invalid server port log_fatal("Invalid port `%u`", info->port); } - if (info->timeout == 0) { + if (info->timeout == 0) { // invalid timeout value log_fatal("Timeout of overture with invalid value 0"); } - create_folder(WORK_DIR); + create_folder(WORK_DIR); // ensure work dir exist char *config = overture_config(info); // string config (JSON format) char *config_file = string_join(WORK_DIR, file); save_file(config_file, config); free(config_file); free(config); - process *proc = process_init("Overture", OVERTURE_BIN); + process *proc = process_init("Overture", OVERTURE_BIN); // generate overture command process_add_arg(proc, "-c"); process_add_arg(proc, file); if (info->debug) { diff --git a/src/cleardns.c b/src/cleardns.c index 3302a52..944e263 100644 --- a/src/cleardns.c +++ b/src/cleardns.c @@ -41,8 +41,8 @@ int main(int argc, char *argv[]) { // ClearDNS server log_info("ClearDNS server start (%s)", VERSION); -// load_config("test.json"); -// + load_config("test.json"); + // dnsproxy_load("Domestic", loader.domestic, "domestic.json"); // dnsproxy_load("Foreign", loader.foreign, "foreign.json"); @@ -110,20 +110,20 @@ int main(int argc, char *argv[]) { // ClearDNS server // log_info("cwd -> %s", p->cwd); - adguard *filter = adguard_init(); - - filter->debug = TRUE; - filter->dns_port = 54; - filter->web_port = 8080; - filter->upstream = "127.0.0.1:5454"; - - filter->username = "dnomd343"; - filter->password = "password"; - - process *p = adguard_load(filter, "/cleardns/adguard/"); - log_info("cmd -> %s", string_list_dump(p->cmd)); - log_info("env -> %s", string_list_dump(p->env)); - log_info("cwd -> %s", p->cwd); +// adguard *filter = adguard_init(); +// +// filter->debug = TRUE; +// filter->dns_port = 54; +// filter->web_port = 8080; +// filter->upstream = "127.0.0.1:5454"; +// +// filter->username = "dnomd343"; +// filter->password = "password"; +// +// process *p = adguard_load(filter, "/cleardns/adguard/"); +// log_info("cmd -> %s", string_list_dump(p->cmd)); +// log_info("env -> %s", string_list_dump(p->env)); +// log_info("cwd -> %s", p->cwd); // int debug_mode = 0; diff --git a/src/loader/CMakeLists.txt b/src/loader/CMakeLists.txt index 0acf9e5..dd1ef74 100644 --- a/src/loader/CMakeLists.txt +++ b/src/loader/CMakeLists.txt @@ -1,3 +1,4 @@ cmake_minimum_required(VERSION 2.8.12) add_library(loader config.c loader.c parser.c) +target_link_libraries(loader applet) diff --git a/src/loader/config.c b/src/loader/config.c index d47c2b6..8be2c2f 100644 --- a/src/loader/config.c +++ b/src/loader/config.c @@ -1,13 +1,13 @@ #include -#include "common.h" #include "config.h" +#include "common.h" #include "logger.h" #include "structure.h" cleardns_config* config_init() { // init config struct of cleardns cleardns_config *config = (cleardns_config *)malloc(sizeof(cleardns_config)); config->port = DNS_PORT; - config->cache.size = 0; + config->cache.size = 0; // disable cache in default config->cache.enable = FALSE; config->cache.optimistic = FALSE; diff --git a/src/loader/loader.c b/src/loader/loader.c index 0019858..a6c7f28 100644 --- a/src/loader/loader.c +++ b/src/loader/loader.c @@ -1,4 +1,3 @@ -#include #include "loader.h" #include "config.h" #include "parser.h" @@ -34,9 +33,8 @@ dnsproxy* load_foreign(cleardns_config *config) { } overture* load_diverter(cleardns_config *config) { - overture *diverter = overture_init(config->diverter.port); + overture *diverter = overture_init(); - // timeout // ttl_file // host_file // foreign_port @@ -48,9 +46,26 @@ overture* load_diverter(cleardns_config *config) { // domestic_ip_file // foreign_domain_file // domestic_domain_file + + // TODO: load assets file + return diverter; } +adguard* load_filter(cleardns_config *config) { + adguard *filter = adguard_init(); + + // dns_port + // web_port + + // upstream + + // username + // password + + return filter; +} + void load_config(const char *config_file) { cleardns_config *config = config_init(); config_parser(config, config_file); @@ -61,6 +76,6 @@ void load_config(const char *config_file) { loader.domestic = load_domestic(config); loader.foreign = load_foreign(config); loader.diverter = load_diverter(config); - // load adguard + loader.filter = load_filter(config); config_free(config); } diff --git a/src/loader/parser.c b/src/loader/parser.c index af7442e..9cce1ec 100644 --- a/src/loader/parser.c +++ b/src/loader/parser.c @@ -4,75 +4,8 @@ #include "common.h" #include "logger.h" #include "config.h" -#include "structure.h" -int json_int_value(char *key, cJSON *json) { // json int or string value -> int - if (cJSON_IsNumber(json)) { - return json->valueint; - } else if (cJSON_IsString(json)) { - char *p; - int int_ret = (int)strtol(json->valuestring, &p, 10); - if (int_ret == 0 && strcmp(json->valuestring, "0") != 0) { // invalid number in string - log_fatal("`%s` not a valid number", key); - } - return int_ret; - } else { - log_fatal("`%s` must be number or string", key); - } - return 0; // never reach -} - -char* json_string_value(char* key, cJSON *json) { - if (!cJSON_IsString(json)) { - log_fatal("`%s` must be string", key); - } - return json->valuestring; -} - -char** json_string_list_value(char *key, cJSON *json, char **string_list) { - if (cJSON_IsString(json)) { - string_list = string_list_append(string_list, json->valuestring); - } else if (cJSON_IsArray(json)) { - json = json->child; - while (json != NULL) { - if (!cJSON_IsString(json)) { - log_fatal("`%s` must be string array", key); - } - string_list = string_list_append(string_list, json->valuestring); - json = json->next; // next field - } - } else if (!cJSON_IsNull(json)) { - log_fatal("`%s` must be array or string", key); - } - return string_list; -} - -uint32_t** json_uint32_list_value(char *key, cJSON *json, uint32_t **uint32_list) { - if (cJSON_IsNumber(json)) { - uint32_list = uint32_list_append(uint32_list, json->valueint); - } else if (cJSON_IsArray(json)) { - json = json->child; - while (json != NULL) { - if (!cJSON_IsNumber(json)) { - log_fatal("`%s` must be number array", key); - } - uint32_list = uint32_list_append(uint32_list, json->valueint); - json = json->next; // next field - } - } else if (!cJSON_IsNull(json)) { - log_fatal("`%s` must be array or number", key); - } - return uint32_list; -} - -uint8_t json_bool_value(char *key, cJSON *json) { // json bool value -> bool - if (!cJSON_IsBool(json)) { - log_fatal("`%s` must be boolean", key); - } - return json->valueint; -} - -void json_cache_parser(cache_config *config, cJSON *json) { // cache options parser +void cache_parser(cache_config *config, cJSON *json) { // cache options parser if (!cJSON_IsObject(json)) { log_fatal("`cache` must be object"); } @@ -91,7 +24,7 @@ void json_cache_parser(cache_config *config, cJSON *json) { // cache options par } } -void json_upstream_parser(char *caption, upstream_config *config, cJSON *json) { +void upstream_parser(char *caption, upstream_config *config, cJSON *json) { // upstream options parser if (!cJSON_IsObject(json)) { log_fatal("`%s` must be object", caption); } @@ -132,7 +65,7 @@ void json_upstream_parser(char *caption, upstream_config *config, cJSON *json) { } } -void json_diverter_parser(diverter_config *config, cJSON *json) { // diverter options parser +void diverter_parser(diverter_config *config, cJSON *json) { // diverter options parser if (!cJSON_IsObject(json)) { log_fatal("`diverter` must be object"); } @@ -154,7 +87,7 @@ void json_diverter_parser(diverter_config *config, cJSON *json) { // diverter op } } -void json_adguard_parser(adguard_config *config, cJSON *json) { // adguard options parser +void adguard_parser(adguard_config *config, cJSON *json) { // adguard options parser if (!cJSON_IsObject(json)) { log_fatal("`adguard` must be array"); } @@ -167,19 +100,21 @@ void json_adguard_parser(adguard_config *config, cJSON *json) { // adguard optio config->enable = json_bool_value("adguard.enable", json); } if (!strcmp(json->string, "username")) { + free(config->username); config->username = json_string_value("adguard.username", json); } if (!strcmp(json->string, "password")) { + free(config->password); config->password = json_string_value("adguard.password", json); } json = json->next; // next field } } -void json_config_parser(cleardns_config *config, const char *config_content) { // JSON format configure +void cleardns_parser(cleardns_config *config, const char *config_content) { // JSON format configure cJSON *json = cJSON_Parse(config_content); if (json == NULL) { - log_fatal("JSON configure format error"); + log_fatal("ClearDNS configure format error"); } json = json->child; while (json != NULL) { @@ -187,19 +122,19 @@ void json_config_parser(cleardns_config *config, const char *config_content) { / config->port = json_int_value("port", json); } if (!strcmp(json->string, "cache")) { - json_cache_parser(&config->cache, json); + cache_parser(&config->cache, json); } if (!strcmp(json->string, "domestic")) { - json_upstream_parser("domestic", &config->domestic, json); + upstream_parser("domestic", &config->domestic, json); } if (!strcmp(json->string, "foreign")) { - json_upstream_parser("foreign", &config->foreign, json); + upstream_parser("foreign", &config->foreign, json); } if (!strcmp(json->string, "diverter")) { - json_diverter_parser(&config->diverter, json); + diverter_parser(&config->diverter, json); } if (!strcmp(json->string, "adguard")) { - json_adguard_parser(&config->adguard, json); + adguard_parser(&config->adguard, json); } if (!strcmp(json->string, "reject")) { config->reject = json_uint32_list_value("reject", json, config->reject); @@ -215,31 +150,20 @@ void json_config_parser(cleardns_config *config, const char *config_content) { / cJSON_free(json); // free JSON struct } -void yaml_config_parser(cleardns_config *config, const char *config_content) { // YAML format configure - // TODO: change YAML -> JSON - char *json_content = string_init(config_content); // just demo for now - - json_config_parser(config, json_content); -} - -uint8_t is_json_suffix(const char *file_name) { - if (strlen(file_name) <= 5) { // `.json` - return FALSE; - } - if (!strcmp(file_name + strlen(file_name) - 5, ".json")) { - return TRUE; - } - return FALSE; -} - void config_parser(cleardns_config *config, const char *config_file) { char *config_content = read_file(config_file); - if (is_json_suffix(config_file)) { + if (is_json_suffix(config_file)) { // JSON format log_info("Start JSON configure parser"); - json_config_parser(config, config_content); - } else { - log_info("Start YAML configure parser"); - yaml_config_parser(config, config_content); + cleardns_parser(config, config_content); // configure parser + } else { // YAML or TOML format + log_info("Start configure parser"); + char *json_content = to_json(config_content); // convert to json format + if (json_content == NULL) { + log_fatal("Configure parser error"); + } + cleardns_parser(config, json_content); // configure parser + free(json_content); } + free(config_content); log_info("Configure parser success"); } diff --git a/src/utils/json.c b/src/utils/json.c index e6ae4a2..4250e66 100644 --- a/src/utils/json.c +++ b/src/utils/json.c @@ -4,6 +4,7 @@ #include "cJSON.h" #include "logger.h" #include "common.h" +#include "structure.h" char* to_json(const char *file) { char flag[9]; @@ -36,6 +37,72 @@ void json_field_replace(cJSON *entry, const char *field, cJSON *content) { } } +int json_int_value(char *key, cJSON *json) { // json int or string value -> int + if (cJSON_IsNumber(json)) { + return json->valueint; + } else if (cJSON_IsString(json)) { + char *p; + int int_ret = (int)strtol(json->valuestring, &p, 10); + if (int_ret == 0 && strcmp(json->valuestring, "0") != 0) { // invalid number in string + log_fatal("`%s` not a valid number", key); + } + return int_ret; + } else { + log_fatal("`%s` must be number or string", key); + } + return 0; // never reach +} + +char* json_string_value(char* key, cJSON *json) { + if (!cJSON_IsString(json)) { + log_fatal("`%s` must be string", key); + } + return string_init(json->valuestring); +} + +char** json_string_list_value(char *key, cJSON *json, char **string_list) { + if (cJSON_IsString(json)) { + string_list = string_list_append(string_list, json->valuestring); + } else if (cJSON_IsArray(json)) { + json = json->child; + while (json != NULL) { + if (!cJSON_IsString(json)) { + log_fatal("`%s` must be string array", key); + } + string_list = string_list_append(string_list, json->valuestring); + json = json->next; // next field + } + } else if (!cJSON_IsNull(json)) { + log_fatal("`%s` must be array or string", key); + } + return string_list; +} + +uint32_t** json_uint32_list_value(char *key, cJSON *json, uint32_t **uint32_list) { + if (cJSON_IsNumber(json)) { + uint32_list = uint32_list_append(uint32_list, json->valueint); + } else if (cJSON_IsArray(json)) { + json = json->child; + while (json != NULL) { + if (!cJSON_IsNumber(json)) { + log_fatal("`%s` must be number array", key); + } + uint32_list = uint32_list_append(uint32_list, json->valueint); + json = json->next; // next field + } + } else if (!cJSON_IsNull(json)) { + log_fatal("`%s` must be array or number", key); + } + return uint32_list; +} + +uint8_t json_bool_value(char *key, cJSON *json) { // json bool value -> bool + if (!cJSON_IsBool(json)) { + log_fatal("`%s` must be boolean", key); + } + return json->valueint; +} + cJSON* json_field_get(cJSON *entry, const char *field) { cJSON *sub = entry->child; while (sub != NULL) { @@ -48,3 +115,13 @@ cJSON* json_field_get(cJSON *entry, const char *field) { cJSON_AddItemToObject(entry, field, new); return new; } + +uint8_t is_json_suffix(const char *file_name) { + if (strlen(file_name) <= 5) { // `.json` + return FALSE; + } + if (!strcmp(file_name + strlen(file_name) - 5, ".json")) { + return TRUE; + } + return FALSE; +} diff --git a/src/utils/structure.c b/src/utils/structure.c index 95f081d..87f549d 100644 --- a/src/utils/structure.c +++ b/src/utils/structure.c @@ -1,5 +1,4 @@ #include - #include #include #include "structure.h"