1 /* settings.c
2 *
3 *
4 * Copyright (C) 2014 Toxic All Rights Reserved.
5 *
6 * This file is part of Toxic.
7 *
8 * Toxic is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * Toxic is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with Toxic. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23 #include <ctype.h>
24 #include <libconfig.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "configdir.h"
29 #include "misc_tools.h"
30 #include "notify.h"
31 #include "toxic.h"
32 #include "windows.h"
33
34 #ifdef AUDIO
35 #include "audio_device.h"
36 #endif /* AUDIO */
37
38 #include "line_info.h"
39 #include "settings.h"
40
41 #ifndef PACKAGE_DATADIR
42 #define PACKAGE_DATADIR "."
43 #endif
44
45 #define NO_SOUND "silent"
46
47 static struct ui_strings {
48 const char *self;
49 const char *timestamps;
50 const char *time_format;
51 const char *timestamp_format;
52 const char *log_timestamp_format;
53 const char *alerts;
54 const char *bell_on_message;
55 const char *bell_on_filetrans;
56 const char *bell_on_filetrans_accept;
57 const char *bell_on_invite;
58 const char *native_colors;
59 const char *autolog;
60 const char *history_size;
61 const char *notification_timeout;
62 const char *show_typing_self;
63 const char *show_typing_other;
64 const char *show_welcome_msg;
65 const char *show_connection_msg;
66 const char *nodeslist_update_freq;
67 const char *autosave_freq;
68
69 const char *line_join;
70 const char *line_quit;
71 const char *line_alert;
72 const char *line_normal;
73
74 const char *mplex_away;
75 const char *mplex_away_note;
76
77 const char *color_bar_bg;
78 const char *color_bar_fg;
79 const char *color_bar_accent;
80 const char *color_bar_notify;
81 } ui_strings = {
82 "ui",
83 "timestamps",
84 "time_format",
85 "timestamp_format",
86 "log_timestamp_format",
87 "alerts",
88 "bell_on_message",
89 "bell_on_filetrans",
90 "bell_on_filetrans_accept",
91 "bell_on_invite",
92 "native_colors",
93 "autolog",
94 "history_size",
95 "notification_timeout",
96 "show_typing_self",
97 "show_typing_other",
98 "show_welcome_msg",
99 "show_connection_msg",
100 "nodeslist_update_freq",
101 "autosave_freq",
102 "line_join",
103 "line_quit",
104 "line_alert",
105 "line_normal",
106 "mplex_away",
107 "mplex_away_note",
108 "color_bar_bg",
109 "color_bar_fg",
110 "color_bar_accent",
111 "color_bar_notify",
112 };
113
ui_defaults(struct user_settings * settings)114 static void ui_defaults(struct user_settings *settings)
115 {
116 settings->timestamps = TIMESTAMPS_ON;
117 snprintf(settings->timestamp_format, sizeof(settings->timestamp_format), "%s", TIMESTAMP_DEFAULT);
118 snprintf(settings->log_timestamp_format, sizeof(settings->log_timestamp_format), "%s", LOG_TIMESTAMP_DEFAULT);
119
120 settings->autolog = AUTOLOG_OFF;
121 settings->alerts = ALERTS_ENABLED;
122 settings->bell_on_message = 0;
123 settings->bell_on_filetrans = 0;
124 settings->bell_on_filetrans_accept = 0;
125 settings->bell_on_invite = 0;
126 settings->colour_theme = DFLT_COLS;
127 settings->history_size = 700;
128 settings->notification_timeout = 6000;
129 settings->show_typing_self = SHOW_TYPING_ON;
130 settings->show_typing_other = SHOW_TYPING_ON;
131 settings->show_welcome_msg = SHOW_WELCOME_MSG_ON;
132 settings->show_connection_msg = SHOW_CONNECTION_MSG_ON;
133 settings->nodeslist_update_freq = 7;
134 settings->autosave_freq = 600;
135
136 snprintf(settings->line_join, LINE_HINT_MAX + 1, "%s", LINE_JOIN);
137 snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT);
138 snprintf(settings->line_alert, LINE_HINT_MAX + 1, "%s", LINE_ALERT);
139 snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL);
140
141 settings->mplex_away = MPLEX_ON;
142 snprintf(settings->mplex_away_note, sizeof(settings->mplex_away_note), "%s", MPLEX_AWAY_NOTE);
143 }
144
145 static const struct keys_strings {
146 const char *self;
147 const char *next_tab;
148 const char *prev_tab;
149 const char *scroll_line_up;
150 const char *scroll_line_down;
151 const char *half_page_up;
152 const char *half_page_down;
153 const char *page_bottom;
154 const char *toggle_peerlist;
155 const char *toggle_pastemode;
156 } key_strings = {
157 "keys",
158 "next_tab",
159 "prev_tab",
160 "scroll_line_up",
161 "scroll_line_down",
162 "half_page_up",
163 "half_page_down",
164 "page_bottom",
165 "toggle_peerlist",
166 "toggle_paste_mode",
167 };
168
169 /* defines from toxic.h */
key_defaults(struct user_settings * settings)170 static void key_defaults(struct user_settings *settings)
171 {
172 settings->key_next_tab = T_KEY_NEXT;
173 settings->key_prev_tab = T_KEY_PREV;
174 settings->key_scroll_line_up = KEY_PPAGE;
175 settings->key_scroll_line_down = KEY_NPAGE;
176 settings->key_half_page_up = T_KEY_C_F;
177 settings->key_half_page_down = T_KEY_C_V;
178 settings->key_page_bottom = T_KEY_C_H;
179 settings->key_toggle_peerlist = T_KEY_C_B;
180 settings->key_toggle_pastemode = T_KEY_C_T;
181 }
182
183 static const struct tox_strings {
184 const char *self;
185 const char *download_path;
186 const char *chatlogs_path;
187 const char *avatar_path;
188 const char *autorun_path;
189 const char *password_eval;
190 } tox_strings = {
191 "tox",
192 "download_path",
193 "chatlogs_path",
194 "avatar_path",
195 "autorun_path",
196 "password_eval",
197 };
198
tox_defaults(struct user_settings * settings)199 static void tox_defaults(struct user_settings *settings)
200 {
201 strcpy(settings->download_path, "");
202 strcpy(settings->chatlogs_path, "");
203 strcpy(settings->avatar_path, "");
204 strcpy(settings->autorun_path, "");
205 strcpy(settings->password_eval, "");
206 }
207
208 #ifdef AUDIO
209 static const struct audio_strings {
210 const char *self;
211 const char *input_device;
212 const char *output_device;
213 const char *VAD_threshold;
214 const char *conference_audio_channels;
215 const char *chat_audio_channels;
216 const char *push_to_talk;
217 } audio_strings = {
218 "audio",
219 "input_device",
220 "output_device",
221 "VAD_threshold",
222 "conference_audio_channels",
223 "chat_audio_channels",
224 "push_to_talk",
225 };
226
audio_defaults(struct user_settings * settings)227 static void audio_defaults(struct user_settings *settings)
228 {
229 settings->audio_in_dev = 0;
230 settings->audio_out_dev = 0;
231 settings->VAD_threshold = 5.0;
232 settings->conference_audio_channels = 1;
233 settings->chat_audio_channels = 2;
234 settings->push_to_talk = 0;
235 }
236 #endif
237
238 #ifdef SOUND_NOTIFY
239 static const struct sound_strings {
240 const char *self;
241 const char *notif_error;
242 const char *self_log_in;
243 const char *self_log_out;
244 const char *user_log_in;
245 const char *user_log_out;
246 const char *call_incoming;
247 const char *call_outgoing;
248 const char *generic_message;
249 const char *transfer_pending;
250 const char *transfer_completed;
251 } sound_strings = {
252 "sounds",
253 "notif_error",
254 "self_log_in",
255 "self_log_out",
256 "user_log_in",
257 "user_log_out",
258 "call_incoming",
259 "call_outgoing",
260 "generic_message",
261 "transfer_pending",
262 "transfer_completed",
263 };
264 #endif
265
key_parse(const char ** bind)266 static int key_parse(const char **bind)
267 {
268 int len = strlen(*bind);
269
270 if (len > 5) {
271 if (strncasecmp(*bind, "ctrl+", 5) == 0 && toupper(bind[0][5]) != 'M') { /* ctrl+m cannot be used */
272 return toupper(bind[0][5]) - 'A' + 1;
273 }
274 }
275
276 if (strncasecmp(*bind, "tab", 3) == 0) {
277 return T_KEY_TAB;
278 }
279
280 if (strncasecmp(*bind, "page", 4) == 0) {
281 return len == 6 ? KEY_PPAGE : KEY_NPAGE;
282 }
283
284 return -1;
285 }
286
set_key_binding(int * key,const char ** bind)287 static void set_key_binding(int *key, const char **bind)
288 {
289 int code = key_parse(bind);
290
291 if (code != -1) {
292 *key = code;
293 }
294 }
295
settings_load(struct user_settings * s,const char * patharg)296 int settings_load(struct user_settings *s, const char *patharg)
297 {
298 config_t cfg[1];
299 config_setting_t *setting;
300 const char *str = NULL;
301
302 /* Load default settings */
303 ui_defaults(s);
304 tox_defaults(s);
305 key_defaults(s);
306
307 #ifdef AUDIO
308 audio_defaults(s);
309 #endif
310
311 config_init(cfg);
312
313 char path[MAX_STR_SIZE];
314
315 /* use default config file path */
316 if (patharg == NULL) {
317 char *user_config_dir = get_user_config_dir();
318 snprintf(path, sizeof(path), "%s%stoxic.conf", user_config_dir, CONFIGDIR);
319 free(user_config_dir);
320
321 /* make sure path exists or is created on first time running */
322 if (!file_exists(path)) {
323 FILE *fp = fopen(path, "w");
324
325 if (fp == NULL) {
326 return -1;
327 }
328
329 fclose(fp);
330 }
331 } else {
332 snprintf(path, sizeof(path), "%s", patharg);
333 }
334
335 if (!config_read_file(cfg, path)) {
336 config_destroy(cfg);
337 return -1;
338 }
339
340 /* ui */
341 if ((setting = config_lookup(cfg, ui_strings.self)) != NULL) {
342 config_setting_lookup_bool(setting, ui_strings.timestamps, &s->timestamps);
343
344 int time = 24;
345
346 if (config_setting_lookup_int(setting, ui_strings.time_format, &time)) {
347 if (time == 12) {
348 snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M %p");
349 snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", "%Y/%m/%d [%I:%M:%S %p]");
350 }
351 }
352
353 if (config_setting_lookup_string(setting, ui_strings.timestamp_format, &str)) {
354 snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", str);
355 }
356
357 if (config_setting_lookup_string(setting, ui_strings.color_bar_bg, &str)) {
358 snprintf(s->color_bar_bg, sizeof(s->color_bar_bg), "%s", str);
359 }
360
361 if (config_setting_lookup_string(setting, ui_strings.color_bar_fg, &str)) {
362 snprintf(s->color_bar_fg, sizeof(s->color_bar_fg), "%s", str);
363 }
364
365 if (config_setting_lookup_string(setting, ui_strings.color_bar_accent, &str)) {
366 snprintf(s->color_bar_accent, sizeof(s->color_bar_accent), "%s", str);
367 }
368
369 if (config_setting_lookup_string(setting, ui_strings.color_bar_notify, &str)) {
370 snprintf(s->color_bar_notify, sizeof(s->color_bar_notify), "%s", str);
371 }
372
373 if (config_setting_lookup_string(setting, ui_strings.log_timestamp_format, &str)) {
374 snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", str);
375 }
376
377 config_setting_lookup_bool(setting, ui_strings.alerts, &s->alerts);
378
379 if (config_setting_lookup_bool(setting, ui_strings.bell_on_message, &s->bell_on_message)) {
380 s->bell_on_message = s->bell_on_message ? NT_BEEP : 0;
381 }
382
383 if (config_setting_lookup_bool(setting, ui_strings.bell_on_filetrans, &s->bell_on_filetrans)) {
384 s->bell_on_filetrans = s->bell_on_filetrans ? NT_BEEP : 0;
385 }
386
387 if (config_setting_lookup_bool(setting, ui_strings.bell_on_filetrans_accept, &s->bell_on_filetrans_accept)) {
388 s->bell_on_filetrans_accept = s->bell_on_filetrans_accept ? NT_BEEP : 0;
389 }
390
391 if (config_setting_lookup_bool(setting, ui_strings.bell_on_invite, &s->bell_on_invite)) {
392 s->bell_on_invite = s->bell_on_invite ? NT_BEEP : 0;
393 }
394
395 config_setting_lookup_bool(setting, ui_strings.autolog, &s->autolog);
396 config_setting_lookup_bool(setting, ui_strings.native_colors, &s->colour_theme);
397 config_setting_lookup_bool(setting, ui_strings.show_typing_self, &s->show_typing_self);
398 config_setting_lookup_bool(setting, ui_strings.show_typing_other, &s->show_typing_other);
399 config_setting_lookup_bool(setting, ui_strings.show_welcome_msg, &s->show_welcome_msg);
400 config_setting_lookup_bool(setting, ui_strings.show_connection_msg, &s->show_connection_msg);
401
402 config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size);
403 config_setting_lookup_int(setting, ui_strings.notification_timeout, &s->notification_timeout);
404 config_setting_lookup_int(setting, ui_strings.nodeslist_update_freq, &s->nodeslist_update_freq);
405 config_setting_lookup_int(setting, ui_strings.autosave_freq, &s->autosave_freq);
406
407 if (config_setting_lookup_string(setting, ui_strings.line_join, &str)) {
408 snprintf(s->line_join, sizeof(s->line_join), "%s", str);
409 }
410
411 if (config_setting_lookup_string(setting, ui_strings.line_quit, &str)) {
412 snprintf(s->line_quit, sizeof(s->line_quit), "%s", str);
413 }
414
415 if (config_setting_lookup_string(setting, ui_strings.line_alert, &str)) {
416 snprintf(s->line_alert, sizeof(s->line_alert), "%s", str);
417 }
418
419 if (config_setting_lookup_string(setting, ui_strings.line_normal, &str)) {
420 snprintf(s->line_normal, sizeof(s->line_normal), "%s", str);
421 }
422
423 config_setting_lookup_bool(setting, ui_strings.mplex_away, &s->mplex_away);
424
425 if (config_setting_lookup_string(setting, ui_strings.mplex_away_note, &str)) {
426 snprintf(s->mplex_away_note, sizeof(s->mplex_away_note), "%s", str);
427 }
428 }
429
430 /* paths */
431 if ((setting = config_lookup(cfg, tox_strings.self)) != NULL) {
432 if (config_setting_lookup_string(setting, tox_strings.download_path, &str)) {
433 snprintf(s->download_path, sizeof(s->download_path), "%s", str);
434 int len = strlen(s->download_path);
435
436 /* make sure path ends with a '/' */
437 if (len >= sizeof(s->download_path) - 2) {
438 s->download_path[0] = '\0';
439 } else if (s->download_path[len - 1] != '/') {
440 strcat(&s->download_path[len - 1], "/");
441 }
442 }
443
444 if (config_setting_lookup_string(setting, tox_strings.chatlogs_path, &str)) {
445 snprintf(s->chatlogs_path, sizeof(s->chatlogs_path), "%s", str);
446 int len = strlen(s->chatlogs_path);
447
448 if (len >= sizeof(s->chatlogs_path) - 2) {
449 s->chatlogs_path[0] = '\0';
450 } else if (s->chatlogs_path[len - 1] != '/') {
451 strcat(&s->chatlogs_path[len - 1], "/");
452 }
453 }
454
455 if (config_setting_lookup_string(setting, tox_strings.avatar_path, &str)) {
456 snprintf(s->avatar_path, sizeof(s->avatar_path), "%s", str);
457 int len = strlen(str);
458
459 if (len >= sizeof(s->avatar_path)) {
460 s->avatar_path[0] = '\0';
461 }
462 }
463
464 #ifdef PYTHON
465
466 if (config_setting_lookup_string(setting, tox_strings.autorun_path, &str)) {
467 snprintf(s->autorun_path, sizeof(s->autorun_path), "%s", str);
468 int len = strlen(str);
469
470 if (len >= sizeof(s->autorun_path) - 2) {
471 s->autorun_path[0] = '\0';
472 } else if (s->autorun_path[len - 1] != '/') {
473 strcat(&s->autorun_path[len - 1], "/");
474 }
475 }
476
477 #endif
478
479 if (config_setting_lookup_string(setting, tox_strings.password_eval, &str)) {
480 snprintf(s->password_eval, sizeof(s->password_eval), "%s", str);
481 int len = strlen(str);
482
483 if (len >= sizeof(s->password_eval)) {
484 s->password_eval[0] = '\0';
485 }
486 }
487 }
488
489 /* keys */
490 if ((setting = config_lookup(cfg, key_strings.self)) != NULL) {
491 const char *tmp = NULL;
492
493 if (config_setting_lookup_string(setting, key_strings.next_tab, &tmp)) {
494 set_key_binding(&s->key_next_tab, &tmp);
495 }
496
497 if (config_setting_lookup_string(setting, key_strings.prev_tab, &tmp)) {
498 set_key_binding(&s->key_prev_tab, &tmp);
499 }
500
501 if (config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp)) {
502 set_key_binding(&s->key_scroll_line_up, &tmp);
503 }
504
505 if (config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp)) {
506 set_key_binding(&s->key_scroll_line_down, &tmp);
507 }
508
509 if (config_setting_lookup_string(setting, key_strings.half_page_up, &tmp)) {
510 set_key_binding(&s->key_half_page_up, &tmp);
511 }
512
513 if (config_setting_lookup_string(setting, key_strings.half_page_down, &tmp)) {
514 set_key_binding(&s->key_half_page_down, &tmp);
515 }
516
517 if (config_setting_lookup_string(setting, key_strings.page_bottom, &tmp)) {
518 set_key_binding(&s->key_page_bottom, &tmp);
519 }
520
521 if (config_setting_lookup_string(setting, key_strings.toggle_peerlist, &tmp)) {
522 set_key_binding(&s->key_toggle_peerlist, &tmp);
523 }
524
525 if (config_setting_lookup_string(setting, key_strings.toggle_pastemode, &tmp)) {
526 set_key_binding(&s->key_toggle_pastemode, &tmp);
527 }
528 }
529
530 #ifdef AUDIO
531
532 if ((setting = config_lookup(cfg, audio_strings.self)) != NULL) {
533 config_setting_lookup_int(setting, audio_strings.input_device, &s->audio_in_dev);
534 s->audio_in_dev = s->audio_in_dev < 0 || s->audio_in_dev > MAX_DEVICES ? 0 : s->audio_in_dev;
535
536 config_setting_lookup_int(setting, audio_strings.output_device, &s->audio_out_dev);
537 s->audio_out_dev = s->audio_out_dev < 0 || s->audio_out_dev > MAX_DEVICES ? 0 : s->audio_out_dev;
538
539 config_setting_lookup_float(setting, audio_strings.VAD_threshold, &s->VAD_threshold);
540
541 config_setting_lookup_int(setting, audio_strings.conference_audio_channels, &s->conference_audio_channels);
542 s->conference_audio_channels = s->conference_audio_channels <= 0
543 || s->conference_audio_channels > 2 ? 1 : s->conference_audio_channels;
544
545 config_setting_lookup_int(setting, audio_strings.chat_audio_channels, &s->chat_audio_channels);
546 s->chat_audio_channels = s->chat_audio_channels <= 0 || s->chat_audio_channels > 2 ? 2 : s->chat_audio_channels;
547
548 config_setting_lookup_bool(setting, audio_strings.push_to_talk, &s->push_to_talk);
549 }
550
551 #endif
552
553 #ifdef SOUND_NOTIFY
554
555 if ((setting = config_lookup(cfg, sound_strings.self)) != NULL) {
556 if ((config_setting_lookup_string(setting, sound_strings.notif_error, &str) != CONFIG_TRUE) ||
557 !set_sound(notif_error, str)) {
558 if (str && strcasecmp(str, NO_SOUND) != 0) {
559 set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav");
560 }
561 }
562
563 if (!config_setting_lookup_string(setting, sound_strings.user_log_in, &str) ||
564 !set_sound(user_log_in, str)) {
565 if (str && strcasecmp(str, NO_SOUND) != 0) {
566 set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ToxicContactOnline.wav");
567 }
568 }
569
570 if (!config_setting_lookup_string(setting, sound_strings.user_log_out, &str) ||
571 !set_sound(user_log_out, str)) {
572 if (str && strcasecmp(str, NO_SOUND) != 0) {
573 set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ToxicContactOffline.wav");
574 }
575 }
576
577 if (!config_setting_lookup_string(setting, sound_strings.call_incoming, &str) ||
578 !set_sound(call_incoming, str)) {
579 if (str && strcasecmp(str, NO_SOUND) != 0) {
580 set_sound(call_incoming, PACKAGE_DATADIR "/sounds/ToxicIncomingCall.wav");
581 }
582 }
583
584 if (!config_setting_lookup_string(setting, sound_strings.call_outgoing, &str) ||
585 !set_sound(call_outgoing, str)) {
586 if (str && strcasecmp(str, NO_SOUND) != 0) {
587 set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/ToxicOutgoingCall.wav");
588 }
589 }
590
591 if (!config_setting_lookup_string(setting, sound_strings.generic_message, &str) ||
592 !set_sound(generic_message, str)) {
593 if (str && strcasecmp(str, NO_SOUND) != 0) {
594 set_sound(generic_message, PACKAGE_DATADIR "/sounds/ToxicRecvMessage.wav");
595 }
596 }
597
598 if (!config_setting_lookup_string(setting, sound_strings.transfer_pending, &str) ||
599 !set_sound(transfer_pending, str)) {
600 if (str && strcasecmp(str, NO_SOUND) != 0) {
601 set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/ToxicTransferStart.wav");
602 }
603 }
604
605 if (!config_setting_lookup_string(setting, sound_strings.transfer_completed, &str) ||
606 !set_sound(transfer_completed, str)) {
607 if (str && strcasecmp(str, NO_SOUND) != 0) {
608 set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav");
609 }
610 }
611 } else {
612 set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav");
613 set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ToxicContactOnline.wav");
614 set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ToxicContactOffline.wav");
615 set_sound(call_incoming, PACKAGE_DATADIR "/sounds/ToxicIncomingCall.wav");
616 set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/ToxicOutgoingCall.wav");
617 set_sound(generic_message, PACKAGE_DATADIR "/sounds/ToxicRecvMessage.wav");
618 set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/ToxicTransferStart.wav");
619 set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav");
620 }
621
622 #endif
623
624 config_destroy(cfg);
625 return 0;
626 }
627