1 #include "settings.h"
2 
3 #include "debug.h"
4 #include "flist.h"
5 #include "groups.h"
6 #include "tox.h"
7 
8 // TODO do we want to include the UI headers here?
9 // Or would it be better to supply a callback after settings are loaded?
10 #include "ui/edit.h"
11 #include "ui/switch.h"
12 #include "ui/dropdown.h"
13 
14 #include "layout/settings.h"
15 
16 #include "native/filesys.h"
17 #include "native/keyboard.h"
18 
19 #include "main.h" // UTOX_VERSION_NUMBER, MAIN_HEIGHT, MAIN_WIDTH, all save things..
20 
21 #include <stdlib.h>
22 #include <string.h>
23 #include <minIni.h>
24 
25 #define MATCH(x, y) (strcasecmp(x, y) == 0)
26 #define BOOL_TO_STR(b) b ? "true" : "false"
27 #define STR_TO_BOOL(s) (strcasecmp(s, "true") == 0)
28 #define NAMEOF(s) strchr((const char *)(#s), '>') == NULL ? #s : (strchr((const char *)(#s), '>') + 1)
29 
30 uint16_t loaded_audio_out_device = 0;
31 uint16_t loaded_audio_in_device  = 0;
32 
33 static const char *config_file_name     = "utox_save.ini";
34 static const char *config_file_name_old = "utox_save";
35 
36 static const uint16_t proxy_address_size = 256; // Magic number inside Toxcore.
37 
38 /**
39  * Config section names.
40  */
41 typedef enum {
42     GENERAL_SECTION,
43     INTERFACE_SECTION,
44     AV_SECTION,
45     NOTIFICATIONS_SECTION,
46     ADVANCED_SECTION,
47     UNKNOWN_SECTION
48 } CONFIG_SECTION;
49 
50 static const char *config_sections[UNKNOWN_SECTION + 1] = {
51     "general",
52     "interface",
53     "av",
54     "notifications",
55     "advanced",
56     NULL
57 };
58 
59 SETTINGS settings = {
60     // .last_version                // included here to match the full struct
61     .curr_version = UTOX_VERSION_NUMBER,
62     .next_version = UTOX_VERSION_NUMBER,
63 
64     .show_splash = false,
65 
66     // Low level settings (network, profile, portable-mode)
67     .enable_udp     = true,
68     .enable_ipv6    = true,
69 
70     .use_proxy      = false,
71     .force_proxy    = false,
72     .proxy_port     = 0,
73 
74     // Tox level settings
75     .block_friend_requests  = false,
76     .save_encryption        = true,
77 
78     // .portable_mode               // included here to match the full struct
79 
80     // User interface settings
81     .language               = LANG_EN,
82     .audiofilter_enabled    = true,
83     .push_to_talk           = false,
84     .audio_preview          = false,
85     .video_preview          = false,
86     .send_typing_status     = false,
87     // .inline_video                // included here to match the full struct
88     .use_long_time_msg      = true,
89     .accept_inline_images   = true,
90 
91     // UX Settings
92     .logging_enabled        = true,
93     .close_to_tray          = false,
94     .start_in_tray          = false,
95     .start_with_system      = false,
96     .use_mini_flist         = false,
97     .magic_flist_enabled    = false,
98 
99     .video_fps              = DEFAULT_FPS,
100 
101     // Notifications / Alerts
102     .ringtone_enabled       = true,
103     .status_notifications   = true,
104     .group_notifications    = GNOTIFY_ALWAYS,
105 
106     .verbose = LOG_LVL_ERROR,
107     .debug_file = NULL,
108 
109     .theme                = UINT32_MAX,
110     // OS interface settings
111     .window_x             = 0,
112     .window_y             = 0,
113     .window_height        = MAIN_HEIGHT,
114     .window_width         = MAIN_WIDTH,
115     .window_baseline      = 0,
116 
117     .window_maximized     = 0,
118 };
119 
write_config_value_int(const char * filename,const char * section,const char * key,const long value)120 static void write_config_value_int(const char *filename, const char *section, const char *key, const long value) {
121     if (ini_putl(section, key, value, filename) != 1) {
122         LOG_ERR("Settings", "Unable to save config value: %lu.", value);
123     }
124 }
125 
write_config_value_str(const char * filename,const char * section,const char * key,const char * value)126 static void write_config_value_str(const char *filename, const char *section, const char *key, const char *value) {
127     if (ini_puts(section, key, value, filename) != 1) {
128         LOG_ERR("Settings", "Unable to save config value: %s.", value);
129     }
130 }
131 
write_config_value_bool(const char * filename,const char * section,const char * key,const bool value)132 static void write_config_value_bool(const char *filename, const char *section, const char *key, const bool value) {
133     if (ini_puts(section, key, BOOL_TO_STR(value), filename) != 1) {
134         LOG_ERR("Settings", "Unable to save config value: %s.", value);
135     }
136 }
137 
get_section(const char * section)138 static CONFIG_SECTION get_section(const char* section) {
139     if (MATCH(config_sections[GENERAL_SECTION], section)) {
140         return GENERAL_SECTION;
141     } else if (MATCH(config_sections[INTERFACE_SECTION], section)) {
142         return INTERFACE_SECTION;
143     } else if (MATCH(config_sections[AV_SECTION], section)) {
144         return AV_SECTION;
145     } else if (MATCH(config_sections[NOTIFICATIONS_SECTION], section)) {
146         return NOTIFICATIONS_SECTION;
147     } else if (MATCH(config_sections[ADVANCED_SECTION], section)) {
148         return ADVANCED_SECTION;
149     } else {
150         return UNKNOWN_SECTION;
151     }
152 }
153 
parse_general_section(UTOX_SAVE * config,const char * key,const char * value)154 static void parse_general_section(UTOX_SAVE *config, const char* key, const char* value) {
155     if (MATCH(NAMEOF(config->save_version), key)) {
156         config->save_version = atoi(value);
157     } else if (MATCH(NAMEOF(config->utox_last_version), key)) {
158         config->utox_last_version = atoi(value);
159     } else if (MATCH(NAMEOF(config->send_version), key)) {
160         config->send_version = STR_TO_BOOL(value);
161     } else if (MATCH(NAMEOF(config->update_to_develop), key)) {
162         config->update_to_develop = STR_TO_BOOL(value);
163     }
164 }
165 
parse_interface_section(UTOX_SAVE * config,const char * key,const char * value)166 static void parse_interface_section(UTOX_SAVE *config, const char* key, const char* value) {
167     if (MATCH(NAMEOF(config->language), key)) {
168         config->language = atoi(value);
169     } else if (MATCH(NAMEOF(config->window_x), key)) {
170         config->window_x = atoi(value);
171     } else if (MATCH(NAMEOF(config->window_y), key)) {
172         config->window_y = atoi(value);
173     } else if (MATCH(NAMEOF(config->window_width), key)) {
174         config->window_width = atoi(value);
175     } else if (MATCH(NAMEOF(config->window_height), key)) {
176         config->window_height = atoi(value);
177     } else if (MATCH(NAMEOF(config->theme), key)) {
178         config->theme = atoi(value);
179     } else if (MATCH(NAMEOF(config->scale), key)) {
180         config->scale = atoi(value);
181     } else if (MATCH(NAMEOF(config->logging_enabled), key)) {
182         config->logging_enabled = STR_TO_BOOL(value);
183     } else if (MATCH(NAMEOF(config->close_to_tray), key)) {
184         config->close_to_tray = STR_TO_BOOL(value);
185     } else if (MATCH(NAMEOF(config->start_in_tray), key)) {
186         config->start_in_tray = STR_TO_BOOL(value);
187     } else if (MATCH(NAMEOF(config->auto_startup), key)) {
188         config->auto_startup = STR_TO_BOOL(value);
189     } else if (MATCH(NAMEOF(config->use_mini_flist), key)) {
190         config->use_mini_flist = STR_TO_BOOL(value);
191     } else if (MATCH(NAMEOF(config->filter), key)) {
192         config->filter = STR_TO_BOOL(value);
193     } else if (MATCH(NAMEOF(config->magic_flist_enabled), key)) {
194         config->magic_flist_enabled = STR_TO_BOOL(value);
195     } else if (MATCH(NAMEOF(config->use_long_time_msg), key)) {
196         config->use_long_time_msg = STR_TO_BOOL(value);
197     }
198 }
199 
parse_av_section(UTOX_SAVE * config,const char * key,const char * value)200 static void parse_av_section(UTOX_SAVE *config, const char* key, const char* value) {
201     if (MATCH(NAMEOF(config->push_to_talk), key)) {
202         config->push_to_talk = STR_TO_BOOL(value);
203     } else if (MATCH(NAMEOF(config->audio_filtering_enabled), key)) {
204         config->audio_filtering_enabled = STR_TO_BOOL(value);
205     } else if (MATCH(NAMEOF(config->audio_device_in), key)) {
206         config->audio_device_in = atoi(value);
207     } else if (MATCH(NAMEOF(config->audio_device_out), key)) {
208         config->audio_device_out = atoi(value);
209     } else if (MATCH(NAMEOF(config->video_fps), key)) {
210         char *temp;
211         uint16_t value_fps = strtol((char *)value, &temp, 0);
212 
213         if (*temp == '\0' && value_fps >= 1 && value_fps <= UINT8_MAX) {
214             settings.video_fps = value_fps;
215             return;
216         }
217 
218         LOG_WARN("Settings", "Fps value (%s) is invalid. It must be integer in range of [1,%u].",
219              value, UINT8_MAX);
220     }
221 }
222 
parse_notifications_section(UTOX_SAVE * config,const char * key,const char * value)223 static void parse_notifications_section(UTOX_SAVE *config, const char* key, const char* value) {
224     if (MATCH(NAMEOF(config->audible_notifications_enabled), key)) {
225         config->audible_notifications_enabled = STR_TO_BOOL(value);
226     } else if (MATCH(NAMEOF(config->status_notifications), key)) {
227         config->status_notifications = STR_TO_BOOL(value);
228     } else if (MATCH(NAMEOF(config->no_typing_notifications), key)) {
229         config->no_typing_notifications = STR_TO_BOOL(value);
230     } else if (MATCH(NAMEOF(config->group_notifications), key)) {
231         config->group_notifications = atoi(value);
232     }
233 }
234 
parse_advanced_section(UTOX_SAVE * config,const char * key,const char * value)235 static void parse_advanced_section(UTOX_SAVE *config, const char* key, const char* value) {
236     if (MATCH(NAMEOF(config->enableipv6), key)) {
237         config->enableipv6 = STR_TO_BOOL(value);
238     } else if (MATCH(NAMEOF(config->disableudp), key)) {
239         config->disableudp = STR_TO_BOOL(value);
240     } else if (MATCH(NAMEOF(config->proxyenable), key)) {
241         config->proxyenable = STR_TO_BOOL(value);
242     } else if (MATCH(NAMEOF(config->proxy_port), key)) {
243         config->proxy_port = atoi(value);
244     } else if (MATCH(NAMEOF(config->proxy_ip), key)) {
245         strcpy((char *)config->proxy_ip, value);
246     } else if (MATCH(NAMEOF(config->force_proxy), key)) {
247         config->force_proxy = STR_TO_BOOL(value);
248     } else if (MATCH(NAMEOF(config->auto_update), key)) {
249         config->auto_update = STR_TO_BOOL(value);
250     }
251 }
252 
config_parser(const char * section,const char * key,const char * value,void * config_v)253 static int config_parser(const char* section, const char* key, const char* value, void* config_v) {
254     UTOX_SAVE *config = (UTOX_SAVE*) config_v;
255 
256     switch(get_section(section)) {
257         case GENERAL_SECTION: {
258             parse_general_section(config, key, value);
259             break;
260         }
261         case INTERFACE_SECTION: {
262             parse_interface_section(config, key, value);
263             break;
264         }
265         case AV_SECTION: {
266             parse_av_section(config, key, value);
267             break;
268         }
269         case NOTIFICATIONS_SECTION: {
270             parse_notifications_section(config, key, value);
271             break;
272         }
273         case ADVANCED_SECTION: {
274             parse_advanced_section(config, key, value);
275             break;
276         }
277         case UNKNOWN_SECTION: {
278             LOG_NOTE("Settings", "Unknown section in config file: %s", section);
279             break;
280         }
281     }
282 
283     return 1;
284 }
285 
utox_load_config(void)286 static UTOX_SAVE *utox_load_config(void) {
287     UTOX_SAVE *save = calloc(1, sizeof(UTOX_SAVE) + proxy_address_size + 1);
288     if (!save) {
289         LOG_ERR("Settings", "Unable to calloc for UTOX_SAVE.");
290         return NULL;
291     }
292 
293     char *config_path = utox_get_filepath(config_file_name);
294 
295     if (!config_path) {
296         LOG_ERR("Settings", "Unable to get %s path.", config_file_name);
297         free(save);
298         return NULL;
299     }
300 
301     if (!ini_browse(config_parser, save, config_path)) {
302         LOG_ERR("Settings", "Unable to parse %s.", config_file_name);
303         free(config_path);
304         free(save);
305         return NULL;
306     }
307 
308     free(config_path);
309 
310     return save;
311 }
312 
utox_save_config(UTOX_SAVE * config)313 static bool utox_save_config(UTOX_SAVE *config) {
314     char *config_path = utox_get_filepath(config_file_name);
315 
316     if (!config_path) {
317         LOG_ERR("Settings", "Unable to get %s path.", config_file_name);
318         return false;
319     }
320 
321     // general
322     write_config_value_int(config_path, config_sections[GENERAL_SECTION], NAMEOF(config->save_version), config->save_version);
323     write_config_value_int(config_path, config_sections[GENERAL_SECTION], NAMEOF(config->utox_last_version), config->utox_last_version);
324     write_config_value_bool(config_path, config_sections[GENERAL_SECTION], NAMEOF(config->send_version), config->send_version);
325     write_config_value_bool(config_path, config_sections[GENERAL_SECTION], NAMEOF(config->update_to_develop), config->update_to_develop);
326 
327     // interface
328     write_config_value_int(config_path, config_sections[INTERFACE_SECTION], NAMEOF(config->language), config->language);
329     write_config_value_int(config_path, config_sections[INTERFACE_SECTION], NAMEOF(config->window_x), config->window_x);
330     write_config_value_int(config_path, config_sections[INTERFACE_SECTION], NAMEOF(config->window_y), config->window_y);
331     write_config_value_int(config_path, config_sections[INTERFACE_SECTION], NAMEOF(config->window_width), config->window_width);
332     write_config_value_int(config_path, config_sections[INTERFACE_SECTION], NAMEOF(config->window_height), config->window_height);
333     write_config_value_int(config_path, config_sections[INTERFACE_SECTION], NAMEOF(config->theme), config->theme);
334     write_config_value_int(config_path, config_sections[INTERFACE_SECTION], NAMEOF(config->scale), config->scale);
335     write_config_value_bool(config_path, config_sections[INTERFACE_SECTION], NAMEOF(config->logging_enabled), config->logging_enabled);
336     write_config_value_bool(config_path, config_sections[INTERFACE_SECTION], NAMEOF(config->close_to_tray), config->close_to_tray);
337     write_config_value_bool(config_path, config_sections[INTERFACE_SECTION], NAMEOF(config->start_in_tray), config->start_in_tray);
338     write_config_value_bool(config_path, config_sections[INTERFACE_SECTION], NAMEOF(config->auto_startup), config->auto_startup);
339     write_config_value_bool(config_path, config_sections[INTERFACE_SECTION], NAMEOF(config->use_mini_flist), config->use_mini_flist);
340     write_config_value_bool(config_path, config_sections[INTERFACE_SECTION], NAMEOF(config->filter), config->filter);
341     write_config_value_bool(config_path, config_sections[INTERFACE_SECTION], NAMEOF(config->magic_flist_enabled), config->magic_flist_enabled);
342     write_config_value_bool(config_path, config_sections[INTERFACE_SECTION], NAMEOF(config->use_long_time_msg), config->use_long_time_msg);
343 
344     // av
345     write_config_value_bool(config_path, config_sections[AV_SECTION], NAMEOF(config->push_to_talk), config->push_to_talk);
346     write_config_value_bool(config_path, config_sections[AV_SECTION], NAMEOF(config->audio_filtering_enabled), config->audio_filtering_enabled);
347     write_config_value_int(config_path, config_sections[AV_SECTION], NAMEOF(config->audio_device_in), config->audio_device_in);
348     write_config_value_int(config_path, config_sections[AV_SECTION], NAMEOF(config->audio_device_out), config->audio_device_out);
349     write_config_value_int(config_path, config_sections[AV_SECTION], NAMEOF(config->video_fps), config->video_fps);
350     // TODO: video_input_device
351 
352     // notifications
353     write_config_value_bool(config_path, config_sections[NOTIFICATIONS_SECTION], NAMEOF(config->audible_notifications_enabled), config->audible_notifications_enabled);
354     write_config_value_bool(config_path, config_sections[NOTIFICATIONS_SECTION], NAMEOF(config->status_notifications), config->status_notifications);
355     write_config_value_bool(config_path, config_sections[NOTIFICATIONS_SECTION], NAMEOF(config->no_typing_notifications), config->no_typing_notifications);
356     write_config_value_int(config_path, config_sections[NOTIFICATIONS_SECTION], NAMEOF(config->group_notifications), config->group_notifications);
357 
358     // advanced
359     write_config_value_bool(config_path, config_sections[ADVANCED_SECTION], NAMEOF(config->enableipv6), config->enableipv6);
360     write_config_value_bool(config_path, config_sections[ADVANCED_SECTION], NAMEOF(config->disableudp), config->disableudp);
361     write_config_value_bool(config_path, config_sections[ADVANCED_SECTION], NAMEOF(config->proxyenable), config->proxyenable);
362     write_config_value_int(config_path, config_sections[ADVANCED_SECTION], NAMEOF(config->proxy_port), config->proxy_port);
363     write_config_value_str(config_path, config_sections[ADVANCED_SECTION], NAMEOF(config->proxy_ip), (const char *)config->proxy_ip);
364     write_config_value_bool(config_path, config_sections[ADVANCED_SECTION], NAMEOF(config->force_proxy), config->force_proxy);
365     write_config_value_bool(config_path, config_sections[ADVANCED_SECTION], NAMEOF(config->auto_update), config->auto_update);
366     // TODO: block_friend_requests
367 
368     free(config_path);
369 
370     return true;
371 }
372 
init_default_settings(void)373 static UTOX_SAVE *init_default_settings(void) {
374     UTOX_SAVE *save = calloc(1, sizeof(UTOX_SAVE));
375     if (!save) {
376         LOG_FATAL_ERR(EXIT_MALLOC, "Settings", "Unable to malloc for default settings.");
377     }
378 
379     save->enableipv6  = true;
380     save->disableudp  = false;
381     save->proxyenable = false;
382     save->force_proxy = false;
383 
384     save->audio_filtering_enabled       = true;
385     save->audible_notifications_enabled = true;
386 
387     return save;
388 }
389 
390 // TODO refactor to match same order in main.h
config_load(void)391 UTOX_SAVE *config_load(void) {
392     UTOX_SAVE *save = utox_load_config();
393 
394     // TODO: Remove this in ~0.18.0 release
395     if (!save) {
396         LOG_NOTE("Settings", "Unable to load uTox settings from %s. Trying old %s.", config_file_name, config_file_name_old);
397         save = utox_data_load_utox();
398     }
399 
400     if (!save) {
401         LOG_ERR("Settings", "Unable to load uTox settings. Use defaults.");
402         save = init_default_settings();
403     }
404 
405     if (save->scale > 30) {
406         save->scale = 30;
407     } else if (save->scale < 5) {
408         save->scale = 10;
409     }
410 
411     if (save->window_width < MAIN_WIDTH) {
412         save->window_width = MAIN_WIDTH;
413     }
414     if (save->window_height < MAIN_HEIGHT) {
415         save->window_height = MAIN_HEIGHT;
416     }
417 
418     /* UX Settings */
419 
420     dropdown_language.selected = dropdown_language.over = settings.language = save->language;
421 
422     dropdown_dpi.selected = dropdown_dpi.over = save->scale - 5;
423 
424     switch_save_chat_history.switch_on = save->logging_enabled;
425     switch_close_to_tray.switch_on     = save->close_to_tray;
426     switch_start_in_tray.switch_on     = save->start_in_tray;
427     switch_mini_contacts.switch_on     = save->use_mini_flist;
428     switch_magic_sidebar.switch_on     = save->magic_flist_enabled;
429 
430     switch_ipv6.switch_on             = save->enableipv6;
431     switch_udp.switch_on              = !save->disableudp;
432     switch_udp.panel.disabled         = save->force_proxy;
433     switch_proxy.switch_on            = save->proxyenable;
434     switch_proxy_force.switch_on      = save->force_proxy;
435     switch_proxy_force.panel.disabled = !save->proxyenable;
436 
437     switch_auto_startup.switch_on = save->auto_startup;
438 
439     settings.group_notifications = dropdown_global_group_notifications.selected =
440         dropdown_global_group_notifications.over = save->group_notifications;
441 
442     switch_audible_notifications.switch_on = save->audible_notifications_enabled;
443     switch_audio_filtering.switch_on       = save->audio_filtering_enabled;
444     switch_push_to_talk.switch_on          = save->push_to_talk;
445     switch_status_notifications.switch_on  = save->status_notifications;
446 
447     dropdown_theme.selected = dropdown_theme.over = save->theme;
448 
449     switch_typing_notes.switch_on = !save->no_typing_notifications;
450 
451     flist_set_filter(save->filter); /* roster list filtering */
452 
453     /* Network settings */
454     settings.enable_ipv6 = save->enableipv6;
455     settings.enable_udp  = !save->disableudp;
456     settings.use_proxy   = !!save->proxyenable;
457     settings.proxy_port  = save->proxy_port;
458     settings.force_proxy = save->force_proxy;
459 
460     if (strlen((char *)save->proxy_ip) <= proxy_address_size){
461         strcpy((char *)proxy_address, (char *)save->proxy_ip);
462     }
463 
464     edit_proxy_ip.length = strlen((char *)save->proxy_ip);
465 
466     strcpy((char *)edit_proxy_ip.data, (char *)save->proxy_ip);
467 
468     if (save->proxy_port) {
469         snprintf((char *)edit_proxy_port.data, edit_proxy_port.data_size,
470                  "%u", save->proxy_port);
471         edit_proxy_port.length = strnlen((char *)edit_proxy_port.data,
472                                          edit_proxy_port.data_size - 1);
473     }
474 
475     /* UX settings */
476     settings.logging_enabled      = save->logging_enabled;
477     settings.close_to_tray        = save->close_to_tray;
478     settings.start_in_tray        = save->start_in_tray;
479     settings.start_with_system    = save->auto_startup;
480     settings.use_mini_flist       = save->use_mini_flist;
481     settings.magic_flist_enabled  = save->magic_flist_enabled;
482     settings.use_long_time_msg    = save->use_long_time_msg;
483 
484     settings.ringtone_enabled     = save->audible_notifications_enabled;
485     settings.audiofilter_enabled  = save->audio_filtering_enabled;
486 
487     settings.send_typing_status   = !save->no_typing_notifications;
488     settings.status_notifications = save->status_notifications;
489 
490     settings.window_x             = save->window_x;
491     settings.window_y             = save->window_y;
492     settings.window_width         = save->window_width;
493     settings.window_height        = save->window_height;
494 
495     settings.last_version         = save->utox_last_version;
496 
497     loaded_audio_out_device       = save->audio_device_out;
498     loaded_audio_in_device        = save->audio_device_in;
499 
500     settings.video_fps = save->video_fps != 0 ? save->video_fps : DEFAULT_FPS;
501 
502     snprintf((char *)edit_video_fps.data, edit_video_fps.data_size,
503              "%u", settings.video_fps);
504     edit_video_fps.length = strnlen((char *)edit_video_fps.data,
505                                     edit_video_fps.data_size - 1);
506 
507     // TODO: Don't clobber (and start saving) commandline flags.
508 
509     // Allow users to override theme on the cmdline.
510     if (settings.theme == UINT32_MAX) {
511         settings.theme = save->theme;
512     }
513 
514     ui_set_scale(save->scale);
515 
516     if (save->push_to_talk) {
517         init_ptt();
518     }
519 
520     return save;
521 }
522 
523 // TODO refactor to match order in main.h
config_save(UTOX_SAVE * save_in)524 void config_save(UTOX_SAVE *save_in) {
525     UTOX_SAVE *save = calloc(1, sizeof(UTOX_SAVE) + proxy_address_size);
526     if (!save) {
527         LOG_ERR("Settings", "Could not allocate memory to save settings");
528         return;
529     }
530 
531     /* Copy the data from the in data to protect the calloc */
532     save->window_x                      = save_in->window_x;
533     save->window_y                      = save_in->window_y;
534     save->window_width                  = save_in->window_width;
535     save->window_height                 = save_in->window_height;
536 
537     save->save_version                  = UTOX_SAVE_VERSION;
538     save->scale                         = ui_scale;
539     save->proxyenable                   = switch_proxy.switch_on;
540     save->audible_notifications_enabled = settings.ringtone_enabled;
541     save->audio_filtering_enabled       = settings.audiofilter_enabled;
542     save->push_to_talk                  = settings.push_to_talk;
543 
544     /* UX Settings */
545     save->logging_enabled               = settings.logging_enabled;
546     save->close_to_tray                 = settings.close_to_tray;
547     save->start_in_tray                 = settings.start_in_tray;
548     save->auto_startup                  = settings.start_with_system;
549     save->use_mini_flist                = settings.use_mini_flist;
550     save->magic_flist_enabled           = settings.magic_flist_enabled;
551     save->use_long_time_msg             = settings.use_long_time_msg;
552     save->video_fps                     = settings.video_fps;
553 
554     save->disableudp                    = !settings.enable_udp;
555     save->enableipv6                    = settings.enable_ipv6;
556     save->no_typing_notifications       = !settings.send_typing_status;
557 
558     save->filter                        = flist_get_filter();
559     save->proxy_port                    = settings.proxy_port;
560     save->force_proxy                   = settings.force_proxy;
561 
562     save->audio_device_in               = dropdown_audio_in.selected;
563     save->audio_device_out              = dropdown_audio_out.selected;
564     save->theme                         = settings.theme;
565 
566     save->utox_last_version             = settings.curr_version;
567     save->group_notifications           = settings.group_notifications;
568     save->status_notifications          = settings.status_notifications;
569 
570     save->language                      = settings.language;
571 
572     memcpy(save->proxy_ip, proxy_address, proxy_address_size);
573 
574     LOG_NOTE("uTox", "Saving uTox settings.");
575 
576     if (!utox_save_config(save)) {
577         LOG_ERR("uTox", "Unable to save uTox settings.");
578     }
579 
580     free(save);
581 }
582 
583 // TODO: Remove this in ~0.18.0 release
utox_data_load_utox(void)584 UTOX_SAVE *utox_data_load_utox(void) {
585     size_t size = 0;
586     FILE *fp = utox_get_file(config_file_name_old, &size, UTOX_FILE_OPTS_READ);
587 
588     if (!fp) {
589         LOG_ERR("Settings", "Unable to open %s.", config_file_name_old);
590         return NULL;
591     }
592 
593     UTOX_SAVE *save = calloc(1, size + 1);
594     if (!save) {
595         LOG_ERR("Settings", "Unable to malloc for %s.", config_file_name_old);
596         fclose(fp);
597         return NULL;
598     }
599 
600     if (fread(save, size, 1, fp) != 1) {
601         LOG_ERR("Settings", "Could not read save file %s", config_file_name_old);
602         fclose(fp);
603         free(save);
604         return NULL;
605     }
606 
607     fclose(fp);
608     return save;
609 }
610