diff --git a/src/bootstrap.c b/src/bootstrap.c index 2058c9734..c0d062170 100644 --- a/src/bootstrap.c +++ b/src/bootstrap.c @@ -36,6 +36,7 @@ #include "line_info.h" #include "misc_tools.h" #include "prompt.h" +#include "run_options.h" #include "settings.h" #include "windows.h" diff --git a/src/misc_tools.c b/src/misc_tools.c index 2ae2bf0a9..c61e2f80f 100644 --- a/src/misc_tools.c +++ b/src/misc_tools.c @@ -559,7 +559,7 @@ size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length) returns length of s if char not found or 0 if s is NULL. */ int char_find(int idx, const char *s, char ch) { - if (!s) { + if (s == NULL) { return 0; } @@ -578,7 +578,7 @@ int char_find(int idx, const char *s, char ch) returns 0 if char not found or s is NULL (skips 0th index). */ int char_rfind(const char *s, char ch, int len) { - if (!s) { + if (s == NULL) { return 0; } diff --git a/src/name_lookup.c b/src/name_lookup.c index fb0beff4c..c1b696af5 100644 --- a/src/name_lookup.c +++ b/src/name_lookup.c @@ -30,6 +30,7 @@ #include "global_commands.h" #include "line_info.h" #include "misc_tools.h" +#include "run_options.h" #include "toxic.h" #include "windows.h" diff --git a/src/run_options.h b/src/run_options.h new file mode 100644 index 000000000..07bb2dae1 --- /dev/null +++ b/src/run_options.h @@ -0,0 +1,57 @@ +/* run_options.h + * + * + * Copyright (C) 2024 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * + */ + +#ifndef RUN_OPTIONS_H + +#include +#include // needed for FILE +#include + +#include "toxic_constants.h" + +typedef struct Run_Options { + bool use_ipv4; + bool force_tcp; + bool disable_local_discovery; + bool debug; + bool default_locale; + bool use_custom_data; + bool use_custom_config_file; + bool no_connect; + bool encrypt_data; + bool unencrypt_data; + + char nameserver_path[MAX_STR_SIZE]; + char config_path[MAX_STR_SIZE]; + char nodes_path[MAX_STR_SIZE]; + + bool logging; + FILE *log_fp; + + char proxy_address[256]; + uint8_t proxy_type; + uint16_t proxy_port; + + uint16_t tcp_port; +} Run_Options; + +#endif /* RUN_OPTIONS_H */ diff --git a/src/settings.c b/src/settings.c index a087c5126..b3feff0b2 100644 --- a/src/settings.c +++ b/src/settings.c @@ -31,6 +31,7 @@ #include "groupchats.h" #include "misc_tools.h" #include "notify.h" +#include "run_options.h" #include "toxic.h" #include "windows.h" @@ -340,29 +341,50 @@ static void set_key_binding(int *key, const char **bind) } } -static bool get_settings_path(char *path, unsigned int path_size, const char *patharg) +#define TOXIC_CONF_FILE_EXT ".conf" + +bool settings_load_config_file(Run_Options *run_opts, const char *data_path) { - if (patharg != NULL) { - snprintf(path, path_size, "%s", patharg); - return true; - } + char tmp_path[MAX_STR_SIZE] = {0}; + + if (run_opts->use_custom_config_file) { + snprintf(tmp_path, sizeof(tmp_path), "%s", run_opts->config_path); + } else if (run_opts->use_custom_data) { + char tmp_data[MAX_STR_SIZE - strlen(TOXIC_CONF_FILE_EXT)]; + + if (strlen(data_path) >= sizeof(tmp_data)) { + return false; + } - char *user_config_dir = get_user_config_dir(); - snprintf(path, path_size, "%s%stoxic.conf", user_config_dir, CONFIGDIR); - free(user_config_dir); + snprintf(tmp_data, sizeof(tmp_data), "%s", data_path); + + const int dot_idx = char_rfind(tmp_data, '.', strlen(tmp_data)); + + if (dot_idx > 0) { + tmp_data[dot_idx] = '\0'; // remove .tox file extension (or any others) if it exists + } + + snprintf(tmp_path, sizeof(tmp_path), "%s%s", tmp_data, TOXIC_CONF_FILE_EXT); + } else { + char *user_config_dir = get_user_config_dir(); + snprintf(tmp_path, sizeof(tmp_path), "%s%stoxic%s", user_config_dir, CONFIGDIR, TOXIC_CONF_FILE_EXT); + free(user_config_dir); + } /* make sure path exists or is created on first time running */ - if (!file_exists(path)) { - FILE *fp = fopen(path, "w"); + if (!file_exists(tmp_path)) { + FILE *fp = fopen(tmp_path, "w"); if (fp == NULL) { - fprintf(stderr, "failed to open config path: %s\n", path); + fprintf(stderr, "failed to create config path `%s`\n", tmp_path); return false; } fclose(fp); } + snprintf(run_opts->config_path, sizeof(run_opts->config_path), "%s", tmp_path); + return true; } @@ -404,22 +426,17 @@ static const char *extract_setting_public_key(const config_setting_t *keys) /* * Initializes `cfg` with the contents from the toxic config file. * - * `patharg` points to a user specified config path. If `patharg` is NULL - * the default config path will be used. - * * Return 0 on success. - * Return -1 if we failed to fetch or create the config path. + * Return -1 if the config file was not set by the client. * Return -2 if the config file cannot be read or is invalid. */ -static int settings_init_config(config_t *cfg, const char *patharg) +static int settings_init_config(config_t *cfg, const Run_Options *run_opts) { - char path[MAX_STR_SIZE] = {0}; - - if (!get_settings_path(path, sizeof(path), patharg)) { + if (string_is_empty(run_opts->config_path)) { return -1; } - if (!config_read_file(cfg, path)) { + if (!config_read_file(cfg, run_opts->config_path)) { fprintf(stderr, "config_read_file() error: %s:%d - %s\n", config_error_file(cfg), config_error_line(cfg), config_error_text(cfg)); return -2; @@ -428,12 +445,12 @@ static int settings_init_config(config_t *cfg, const char *patharg) return 0; } -int settings_load_conferences(const char *patharg) +int settings_load_conferences(const Run_Options *run_opts) { config_t cfg[1]; config_init(cfg); - const int c_ret = settings_init_config(cfg, patharg); + const int c_ret = settings_init_config(cfg, run_opts); if (c_ret < 0) { config_destroy(cfg); @@ -480,12 +497,12 @@ int settings_load_conferences(const char *patharg) return 0; } -int settings_load_groups(const char *patharg) +int settings_load_groups(const Run_Options *run_opts) { config_t cfg[1]; config_init(cfg); - const int c_ret = settings_init_config(cfg, patharg); + const int c_ret = settings_init_config(cfg, run_opts); if (c_ret < 0) { config_destroy(cfg); @@ -532,12 +549,12 @@ int settings_load_groups(const char *patharg) return 0; } -int settings_load_friends(const char *patharg) +int settings_load_friends(const Run_Options *run_opts) { config_t cfg[1]; config_init(cfg); - const int c_ret = settings_init_config(cfg, patharg); + const int c_ret = settings_init_config(cfg, run_opts); if (c_ret < 0) { config_destroy(cfg); @@ -600,7 +617,7 @@ int settings_load_friends(const char *patharg) return 0; } -int settings_load_main(Client_Config *s, const char *patharg) +int settings_load_main(Client_Config *s, const Run_Options *run_opts) { config_t cfg[1]; config_init(cfg); @@ -616,7 +633,7 @@ int settings_load_main(Client_Config *s, const char *patharg) audio_defaults(s); #endif - const int c_ret = settings_init_config(cfg, patharg); + const int c_ret = settings_init_config(cfg, run_opts); if (c_ret < 0) { config_destroy(cfg); diff --git a/src/settings.h b/src/settings.h index 354bc8a83..7c3d82428 100644 --- a/src/settings.h +++ b/src/settings.h @@ -141,6 +141,27 @@ enum settings_values { #define LOG_TIMESTAMP_DEFAULT "%Y/%m/%d [%H:%M:%S]" #define MPLEX_AWAY_NOTE "Away from keyboard, be back soon!" +typedef struct Run_Options Run_Options; + +/* + * Loads the config file into `run_opts` and creates an empty file if it does not + * already exist. This function must be called before any other `settings_load` function. + * + * If the client has set a custom config file via the run option it will be + * prioritized. + * + * If the client is using the default tox data file and has not specified a custom + * config file, the default config file in the user config directory is used + * + * If the client is using a custom tox data file, the config path and filename will be + * identical to those of the data file, but with a `.conf` file extension. + * + * `data_path` is the name of the tox profile being used. + * + * Returns true if config file is successfully loaded. + */ +bool settings_load_config_file(Run_Options *run_opts, const char *data_path); + /* * Loads general toxic settings from the toxic config file pointed to by `patharg'. * @@ -148,7 +169,7 @@ enum settings_values { * Return -1 if we fail to open the file path. * Return -2 if libconfig fails to read the config file. */ -int settings_load_main(Client_Config *s, const char *patharg); +int settings_load_main(Client_Config *s, const Run_Options *run_opts); /* * Loads friend config settings from the toxic config file pointed to by `patharg`. @@ -160,7 +181,7 @@ int settings_load_main(Client_Config *s, const char *patharg); * * This function will have no effect on friends that are added in the future. */ -int settings_load_friends(const char *patharg); +int settings_load_friends(const Run_Options *run_opts); /* * Loads groupchat config settings from the toxic config file pointed to by `patharg`. @@ -172,7 +193,7 @@ int settings_load_friends(const char *patharg); * * This function will have no effect on groupchat instances that are created in the future. */ -int settings_load_groups(const char *patharg); +int settings_load_groups(const Run_Options *run_opts); /* * Loads conference config settings from the toxic config file pointed to by `patharg`. @@ -184,6 +205,6 @@ int settings_load_groups(const char *patharg); * * This function will have no effect on conference instances that are created in the future. */ -int settings_load_conferences(const char *patharg); +int settings_load_conferences(const Run_Options *run_opts); #endif /* SETTINGS_H */ diff --git a/src/toxic.c b/src/toxic.c index 0acdc7273..b4d3ee117 100644 --- a/src/toxic.c +++ b/src/toxic.c @@ -62,6 +62,7 @@ #include "name_lookup.h" #include "notify.h" #include "prompt.h" +#include "run_options.h" #include "settings.h" #include "term_mplex.h" #include "toxic.h" @@ -1397,11 +1398,9 @@ static void parse_args(Toxic *toxic, int argc, char *argv[]) } snprintf(run_opts->config_path, sizeof(run_opts->config_path), "%s", optarg); + run_opts->use_custom_config_file = true; - if (!file_exists(run_opts->config_path)) { - queue_init_message("Config file not found"); - } - + queue_init_message("Using '%s' custom config file", run_opts->config_path); break; } @@ -1453,7 +1452,7 @@ static void parse_args(Toxic *toxic, int argc, char *argv[]) strcpy(client_data->block_path, optarg); strcat(client_data->block_path, "-blocklist"); - queue_init_message("Using '%s' data file", client_data->data_path); + queue_init_message("Using '%s' tox profile", client_data->data_path); break; } @@ -1708,14 +1707,20 @@ int main(int argc, char **argv) first_time_encrypt(&toxic->client_data, "Encrypt existing data file? Y/n (q to quit)"); } - const char *config_path = !string_is_empty(run_opts->config_path) ? run_opts->config_path : NULL; + if (!settings_load_config_file(run_opts, toxic->client_data.data_path)) { + queue_init_message("Failed to load config file"); + } - const int ms_ret = settings_load_main(toxic->c_config, config_path); + const int ms_ret = settings_load_main(toxic->c_config, run_opts); if (ms_ret < 0) { queue_init_message("Failed to load user settings: error %d", ms_ret); } + if (!run_opts->use_custom_config_file && run_opts->use_custom_data) { + queue_init_message("Using '%s' config file", run_opts->config_path); + } + const int curl_init = curl_global_init(CURL_GLOBAL_ALL); const int nameserver_ret = name_lookup_init(run_opts->nameserver_path, curl_init); @@ -1753,19 +1758,19 @@ int main(int argc, char **argv) load_groups(toxic); load_conferences(toxic); - const int fs_ret = settings_load_friends(config_path); + const int fs_ret = settings_load_friends(run_opts); if (fs_ret != 0) { queue_init_message("Failed to load friend config settings: error %d", fs_ret); } - const int gs_ret = settings_load_groups(config_path); + const int gs_ret = settings_load_groups(run_opts); if (gs_ret != 0) { queue_init_message("Failed to load groupchat config settings: error %d", gs_ret); } - const int cs_ret = settings_load_conferences(config_path); + const int cs_ret = settings_load_conferences(run_opts); if (cs_ret != 0) { queue_init_message("Failed to load conference config settings: error %d", cs_ret); diff --git a/src/toxic.h b/src/toxic.h index 9c9ad0ebd..102b49ecd 100644 --- a/src/toxic.h +++ b/src/toxic.h @@ -51,31 +51,6 @@ #include "settings.h" #include "toxic_constants.h" -typedef struct Run_Options { - bool use_ipv4; - bool force_tcp; - bool disable_local_discovery; - bool debug; - bool default_locale; - bool use_custom_data; - bool no_connect; - bool encrypt_data; - bool unencrypt_data; - - char nameserver_path[MAX_STR_SIZE]; - char config_path[MAX_STR_SIZE]; - char nodes_path[MAX_STR_SIZE]; - - bool logging; - FILE *log_fp; - - char proxy_address[256]; - uint8_t proxy_type; - uint16_t proxy_port; - - uint16_t tcp_port; -} Run_Options; - typedef struct Client_Data { bool is_encrypted; char pass[MAX_PASSWORD_LEN + 1]; @@ -86,6 +61,7 @@ typedef struct Client_Data { typedef struct ToxAV ToxAV; typedef struct ToxWindow ToxWindow; +typedef struct Run_Options Run_Options; typedef struct Toxic { Tox *tox;