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