1 /*
2  * preferences.c
3  * vim: expandtab:ts=4:sts=4:sw=4
4  *
5  * Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
6  * Copyright (C) 2019 - 2021 Michael Vetter <jubalh@iodoru.org>
7  *
8  * This file is part of Profanity.
9  *
10  * Profanity is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * Profanity is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Profanity.  If not, see <https://www.gnu.org/licenses/>.
22  *
23  * In addition, as a special exception, the copyright holders give permission to
24  * link the code of portions of this program with the OpenSSL library under
25  * certain conditions as described in each individual source file, and
26  * distribute linked combinations including the two.
27  *
28  * You must obey the GNU General Public License in all respects for all of the
29  * code used other than OpenSSL. If you modify file(s) with this exception, you
30  * may extend this exception to your version of the file(s), but you are not
31  * obligated to do so. If you do not wish to do so, delete this exception
32  * statement from your version. If you delete this exception statement from all
33  * source files in the program, then also delete it here.
34  *
35  */
36 
37 #include "config.h"
38 
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <string.h>
42 
43 #include <glib.h>
44 #include <glib/gstdio.h>
45 
46 #include "common.h"
47 #include "log.h"
48 #include "preferences.h"
49 #include "tools/autocomplete.h"
50 #include "config/files.h"
51 #include "config/conflists.h"
52 
53 // preference groups refer to the sections in .profrc or theme files
54 // for example [ui] but not [colours] which is handled in theme.c
55 #define PREF_GROUP_LOGGING       "logging"
56 #define PREF_GROUP_CHATSTATES    "chatstates"
57 #define PREF_GROUP_UI            "ui"
58 #define PREF_GROUP_NOTIFICATIONS "notifications"
59 #define PREF_GROUP_PRESENCE      "presence"
60 #define PREF_GROUP_CONNECTION    "connection"
61 #define PREF_GROUP_ALIAS         "alias"
62 #define PREF_GROUP_OTR           "otr"
63 #define PREF_GROUP_PGP           "pgp"
64 #define PREF_GROUP_OMEMO         "omemo"
65 #define PREF_GROUP_OX            "ox"
66 #define PREF_GROUP_MUC           "muc"
67 #define PREF_GROUP_PLUGINS       "plugins"
68 #define PREF_GROUP_EXECUTABLES   "executables"
69 
70 #define INPBLOCK_DEFAULT 1000
71 
72 static gchar* prefs_loc;
73 static GKeyFile* prefs;
74 gint log_maxsize = 0;
75 
76 static Autocomplete boolean_choice_ac;
77 static Autocomplete room_trigger_ac;
78 
79 static void _save_prefs(void);
80 static const char* _get_group(preference_t pref);
81 static const char* _get_key(preference_t pref);
82 static gboolean _get_default_boolean(preference_t pref);
83 static char* _get_default_string(preference_t pref);
84 
85 static void
_prefs_load(void)86 _prefs_load(void)
87 {
88     GError* err = NULL;
89     log_maxsize = g_key_file_get_integer(prefs, PREF_GROUP_LOGGING, "maxsize", &err);
90     if (err) {
91         log_maxsize = 0;
92         g_error_free(err);
93     }
94 
95     // move pre 0.5.0 autoaway.time to autoaway.awaytime
96     if (g_key_file_has_key(prefs, PREF_GROUP_PRESENCE, "autoaway.time", NULL)) {
97         gint time = g_key_file_get_integer(prefs, PREF_GROUP_PRESENCE, "autoaway.time", NULL);
98         g_key_file_set_integer(prefs, PREF_GROUP_PRESENCE, "autoaway.awaytime", time);
99         g_key_file_remove_key(prefs, PREF_GROUP_PRESENCE, "autoaway.time", NULL);
100     }
101 
102     // move pre 0.5.0 autoaway.message to autoaway.awaymessage
103     if (g_key_file_has_key(prefs, PREF_GROUP_PRESENCE, "autoaway.message", NULL)) {
104         char* message = g_key_file_get_string(prefs, PREF_GROUP_PRESENCE, "autoaway.message", NULL);
105         g_key_file_set_string(prefs, PREF_GROUP_PRESENCE, "autoaway.awaymessage", message);
106         g_key_file_remove_key(prefs, PREF_GROUP_PRESENCE, "autoaway.message", NULL);
107         g_free(message);
108     }
109 
110     // migrate pre 0.5.0 time settings
111     if (g_key_file_has_key(prefs, PREF_GROUP_UI, "time", NULL)) {
112         char* time = g_key_file_get_string(prefs, PREF_GROUP_UI, "time", NULL);
113         char* val = NULL;
114         if (time) {
115             val = time;
116         } else {
117             val = "off";
118         }
119         g_key_file_set_string(prefs, PREF_GROUP_UI, "time.console", val);
120         g_key_file_set_string(prefs, PREF_GROUP_UI, "time.chat", val);
121         g_key_file_set_string(prefs, PREF_GROUP_UI, "time.muc", val);
122         g_key_file_set_string(prefs, PREF_GROUP_UI, "time.config", val);
123         g_key_file_set_string(prefs, PREF_GROUP_UI, "time.private", val);
124         g_key_file_set_string(prefs, PREF_GROUP_UI, "time.xmlconsole", val);
125         g_key_file_remove_key(prefs, PREF_GROUP_UI, "time", NULL);
126         g_free(time);
127     }
128 
129     // move pre 0.5.0 notify settings
130     if (g_key_file_has_key(prefs, PREF_GROUP_NOTIFICATIONS, "room", NULL)) {
131         char* value = g_key_file_get_string(prefs, PREF_GROUP_NOTIFICATIONS, "room", NULL);
132         if (g_strcmp0(value, "on") == 0) {
133             g_key_file_set_boolean(prefs, PREF_GROUP_NOTIFICATIONS, "room", TRUE);
134         } else if (g_strcmp0(value, "off") == 0) {
135             g_key_file_set_boolean(prefs, PREF_GROUP_NOTIFICATIONS, "room", FALSE);
136         } else if (g_strcmp0(value, "mention") == 0) {
137             g_key_file_set_boolean(prefs, PREF_GROUP_NOTIFICATIONS, "room", FALSE);
138             g_key_file_set_boolean(prefs, PREF_GROUP_NOTIFICATIONS, "room.mention", TRUE);
139         }
140         g_free(value);
141     }
142 
143     // move pre 0.6.0 titlebar settings to wintitle
144     if (g_key_file_has_key(prefs, PREF_GROUP_UI, "titlebar.show", NULL)) {
145         gboolean show = g_key_file_get_boolean(prefs, PREF_GROUP_UI, "titlebar.show", NULL);
146         g_key_file_set_boolean(prefs, PREF_GROUP_UI, "wintitle.show", show);
147         g_key_file_remove_key(prefs, PREF_GROUP_UI, "titlebar.show", NULL);
148     }
149     if (g_key_file_has_key(prefs, PREF_GROUP_UI, "titlebar.goodbye", NULL)) {
150         gboolean goodbye = g_key_file_get_boolean(prefs, PREF_GROUP_UI, "titlebar.goodbye", NULL);
151         g_key_file_set_boolean(prefs, PREF_GROUP_UI, "wintitle.goodbye", goodbye);
152         g_key_file_remove_key(prefs, PREF_GROUP_UI, "titlebar.goodbye", NULL);
153     }
154 
155     // after 0.8.1: titlebar use jid|name -> titlebar show|hide jid|name
156     if (g_key_file_has_key(prefs, PREF_GROUP_UI, "titlebar.muc.title", NULL)) {
157         char* value = g_key_file_get_string(prefs, PREF_GROUP_UI, "titlebar.muc.title", NULL);
158         if (g_strcmp0(value, "name") == 0) {
159             g_key_file_set_boolean(prefs, PREF_GROUP_UI, "titlebar.muc.title.name", TRUE);
160         } else if (g_strcmp0(value, "jid") == 0) {
161             g_key_file_set_boolean(prefs, PREF_GROUP_UI, "titlebar.muc.title.jid", TRUE);
162         }
163     }
164 
165     // 0.9.0 introduced /urlopen. It was saved under "logging" section. Now we have a new "executables" section.
166     if (g_key_file_has_key(prefs, PREF_GROUP_LOGGING, "urlopen.cmd", NULL)) {
167         char* val = g_key_file_get_string(prefs, PREF_GROUP_LOGGING, "urlopen.cmd", NULL);
168 
169         GString* value = g_string_new("false;");
170         value = g_string_append(value, val);
171         value = g_string_append(value, " %u;");
172 
173         g_key_file_set_locale_string(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd", "DEF", value->str);
174         g_key_file_remove_key(prefs, PREF_GROUP_LOGGING, "urlopen.cmd", NULL);
175 
176         g_string_free(value, TRUE);
177     }
178 
179     // 0.9.0 introduced configurable /avatar. It was saved under "logging" section. Now we have a new "executables" section.
180     if (g_key_file_has_key(prefs, PREF_GROUP_LOGGING, "avatar.cmd", NULL)) {
181         char* value = g_key_file_get_string(prefs, PREF_GROUP_LOGGING, "avatar.cmd", NULL);
182         g_key_file_set_string(prefs, PREF_GROUP_EXECUTABLES, "avatar.cmd", value);
183         g_key_file_remove_key(prefs, PREF_GROUP_LOGGING, "avatar.cmd", NULL);
184     }
185 
186     // 0.10 will have omemo media sharing. So disabling of sendfile introduced in 0.9 is not needed (#1270)
187     if (g_key_file_has_key(prefs, PREF_GROUP_OMEMO, "sendfile", NULL)) {
188         g_key_file_remove_key(prefs, PREF_GROUP_OMEMO, "sendfile", NULL);
189     }
190 
191     // 0.10 have changed the behavior of /url open and /url save to not use any
192     // file type or scheme matching. Move value saved under 'DEF' locale to a
193     // simple key-value string not under any locale.
194     {
195         char** values = g_key_file_get_locale_string_list(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd", "DEF", NULL, NULL);
196         if (values && !g_key_file_has_key(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd", NULL)) {
197             // First value in array is `require_save` option -- we ignore that
198             // one as there is no such option anymore.
199             char* executable = values[1];
200 
201             g_key_file_set_string(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd", executable);
202             g_key_file_set_comment(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd", " Migrated from url.open.cmd[DEF]. `require_save` option has been removed in v0.10 and was discarded.", NULL);
203             g_key_file_remove_key(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd[DEF]", NULL);
204 
205             g_strfreev(values);
206         }
207 
208         char* value = g_key_file_get_locale_string(prefs, PREF_GROUP_EXECUTABLES, "url.save.cmd", "DEF", NULL);
209         if (value && !g_key_file_has_key(prefs, PREF_GROUP_EXECUTABLES, "url.save.cmd", NULL)) {
210             g_key_file_set_string(prefs, PREF_GROUP_EXECUTABLES, "url.save.cmd", value);
211             g_key_file_set_comment(prefs, PREF_GROUP_EXECUTABLES, "url.save.cmd", " Migrated from url.save.cmd[DEF].", NULL);
212             g_key_file_remove_key(prefs, PREF_GROUP_EXECUTABLES, "url.save.cmd[DEF]", NULL);
213             g_free(value);
214         }
215     }
216 
217     _save_prefs();
218 
219     boolean_choice_ac = autocomplete_new();
220     autocomplete_add(boolean_choice_ac, "on");
221     autocomplete_add(boolean_choice_ac, "off");
222 
223     room_trigger_ac = autocomplete_new();
224     gsize len = 0;
225     gchar** triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", &len, NULL);
226 
227     for (int i = 0; i < len; i++) {
228         autocomplete_add(room_trigger_ac, triggers[i]);
229     }
230     g_strfreev(triggers);
231 }
232 
233 /* Clean up after _prefs_load() */
234 static void
_prefs_close(void)235 _prefs_close(void)
236 {
237     autocomplete_free(boolean_choice_ac);
238     autocomplete_free(room_trigger_ac);
239 }
240 
241 void
prefs_reload(void)242 prefs_reload(void)
243 {
244     /*
245      * Current function contains copy-paste, but we wanted to avoid config_file
246      * manipulation from prefs_load/prefs_close
247      */
248 
249     _prefs_close();
250 
251     g_key_file_free(prefs);
252     prefs = NULL;
253 
254     prefs = g_key_file_new();
255     g_key_file_load_from_file(prefs, prefs_loc, G_KEY_FILE_KEEP_COMMENTS, NULL);
256 
257     _prefs_load();
258 }
259 
260 void
prefs_load(char * config_file)261 prefs_load(char* config_file)
262 {
263 
264     if (config_file == NULL) {
265         prefs_loc = files_get_config_path(FILE_PROFRC);
266     } else {
267         prefs_loc = g_strdup(config_file);
268     }
269 
270     if (g_file_test(prefs_loc, G_FILE_TEST_EXISTS)) {
271         g_chmod(prefs_loc, S_IRUSR | S_IWUSR);
272     }
273 
274     prefs = g_key_file_new();
275     g_key_file_load_from_file(prefs, prefs_loc, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, NULL);
276 
277     _prefs_load();
278 }
279 
280 void
prefs_save(void)281 prefs_save(void)
282 {
283     _save_prefs();
284 }
285 
286 void
prefs_close(void)287 prefs_close(void)
288 {
289     _prefs_close();
290 
291     g_key_file_free(prefs);
292     prefs = NULL;
293 
294     g_free(prefs_loc);
295     prefs_loc = NULL;
296 }
297 
298 char*
prefs_autocomplete_boolean_choice(const char * const prefix,gboolean previous,void * context)299 prefs_autocomplete_boolean_choice(const char* const prefix, gboolean previous, void* context)
300 {
301     return autocomplete_complete(boolean_choice_ac, prefix, TRUE, previous);
302 }
303 
304 void
prefs_reset_boolean_choice(void)305 prefs_reset_boolean_choice(void)
306 {
307     autocomplete_reset(boolean_choice_ac);
308 }
309 
310 char*
prefs_autocomplete_room_trigger(const char * const prefix,gboolean previous,void * context)311 prefs_autocomplete_room_trigger(const char* const prefix, gboolean previous, void* context)
312 {
313     return autocomplete_complete(room_trigger_ac, prefix, TRUE, previous);
314 }
315 
316 void
prefs_reset_room_trigger_ac(void)317 prefs_reset_room_trigger_ac(void)
318 {
319     autocomplete_reset(room_trigger_ac);
320 }
321 
322 gboolean
prefs_do_chat_notify(gboolean current_win)323 prefs_do_chat_notify(gboolean current_win)
324 {
325     if (prefs_get_boolean(PREF_NOTIFY_CHAT) == FALSE) {
326         return FALSE;
327     } else {
328         if ((current_win == FALSE) || ((current_win == TRUE) && prefs_get_boolean(PREF_NOTIFY_CHAT_CURRENT))) {
329             return TRUE;
330         } else {
331             return FALSE;
332         }
333     }
334 }
335 
336 GList*
prefs_message_get_triggers(const char * const message)337 prefs_message_get_triggers(const char* const message)
338 {
339     GList* result = NULL;
340 
341     char* message_lower = g_utf8_strdown(message, -1);
342     gsize len = 0;
343     gchar** triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", &len, NULL);
344 
345     for (int i = 0; i < len; i++) {
346         char* trigger_lower = g_utf8_strdown(triggers[i], -1);
347         if (g_strrstr(message_lower, trigger_lower)) {
348             result = g_list_append(result, strdup(triggers[i]));
349         }
350         g_free(trigger_lower);
351     }
352 
353     g_strfreev(triggers);
354     g_free(message_lower);
355 
356     return result;
357 }
358 
359 gboolean
prefs_do_room_notify(gboolean current_win,const char * const roomjid,const char * const mynick,const char * const theirnick,const char * const message,gboolean mention,gboolean trigger_found)360 prefs_do_room_notify(gboolean current_win, const char* const roomjid, const char* const mynick,
361                      const char* const theirnick, const char* const message, gboolean mention, gboolean trigger_found)
362 {
363     if (g_strcmp0(mynick, theirnick) == 0) {
364         return FALSE;
365     }
366 
367     gboolean notify_current = prefs_get_boolean(PREF_NOTIFY_ROOM_CURRENT);
368     gboolean notify_window = FALSE;
369     if (!current_win || (current_win && notify_current)) {
370         notify_window = TRUE;
371     }
372     if (!notify_window) {
373         return FALSE;
374     }
375 
376     gboolean notify_room = FALSE;
377     if (g_key_file_has_key(prefs, roomjid, "notify", NULL)) {
378         notify_room = g_key_file_get_boolean(prefs, roomjid, "notify", NULL);
379     } else {
380         notify_room = prefs_get_boolean(PREF_NOTIFY_ROOM);
381     }
382     if (notify_room) {
383         return TRUE;
384     }
385 
386     gboolean notify_mention = FALSE;
387     if (g_key_file_has_key(prefs, roomjid, "notify.mention", NULL)) {
388         notify_mention = g_key_file_get_boolean(prefs, roomjid, "notify.mention", NULL);
389     } else {
390         notify_mention = prefs_get_boolean(PREF_NOTIFY_ROOM_MENTION);
391     }
392     if (notify_mention && mention) {
393         return TRUE;
394     }
395 
396     gboolean notify_trigger = FALSE;
397     if (g_key_file_has_key(prefs, roomjid, "notify.trigger", NULL)) {
398         notify_trigger = g_key_file_get_boolean(prefs, roomjid, "notify.trigger", NULL);
399     } else {
400         notify_trigger = prefs_get_boolean(PREF_NOTIFY_ROOM_TRIGGER);
401     }
402     if (notify_trigger && trigger_found) {
403         return TRUE;
404     }
405 
406     return FALSE;
407 }
408 
409 gboolean
prefs_do_room_notify_mention(const char * const roomjid,int unread,gboolean mention,gboolean trigger)410 prefs_do_room_notify_mention(const char* const roomjid, int unread, gboolean mention, gboolean trigger)
411 {
412     gboolean notify_room = FALSE;
413     if (g_key_file_has_key(prefs, roomjid, "notify", NULL)) {
414         notify_room = g_key_file_get_boolean(prefs, roomjid, "notify", NULL);
415     } else {
416         notify_room = prefs_get_boolean(PREF_NOTIFY_ROOM);
417     }
418     if (notify_room && unread > 0) {
419         return TRUE;
420     }
421 
422     gboolean notify_mention = FALSE;
423     if (g_key_file_has_key(prefs, roomjid, "notify.mention", NULL)) {
424         notify_mention = g_key_file_get_boolean(prefs, roomjid, "notify.mention", NULL);
425     } else {
426         notify_mention = prefs_get_boolean(PREF_NOTIFY_ROOM_MENTION);
427     }
428     if (notify_mention && mention) {
429         return TRUE;
430     }
431 
432     gboolean notify_trigger = FALSE;
433     if (g_key_file_has_key(prefs, roomjid, "notify.trigger", NULL)) {
434         notify_trigger = g_key_file_get_boolean(prefs, roomjid, "notify.trigger", NULL);
435     } else {
436         notify_trigger = prefs_get_boolean(PREF_NOTIFY_ROOM_TRIGGER);
437     }
438     if (notify_trigger && trigger) {
439         return TRUE;
440     }
441 
442     return FALSE;
443 }
444 
445 void
prefs_set_room_notify(const char * const roomjid,gboolean value)446 prefs_set_room_notify(const char* const roomjid, gboolean value)
447 {
448     g_key_file_set_boolean(prefs, roomjid, "notify", value);
449 }
450 
451 void
prefs_set_room_notify_mention(const char * const roomjid,gboolean value)452 prefs_set_room_notify_mention(const char* const roomjid, gboolean value)
453 {
454     g_key_file_set_boolean(prefs, roomjid, "notify.mention", value);
455 }
456 
457 void
prefs_set_room_notify_trigger(const char * const roomjid,gboolean value)458 prefs_set_room_notify_trigger(const char* const roomjid, gboolean value)
459 {
460     g_key_file_set_boolean(prefs, roomjid, "notify.trigger", value);
461 }
462 
463 gboolean
prefs_has_room_notify(const char * const roomjid)464 prefs_has_room_notify(const char* const roomjid)
465 {
466     return g_key_file_has_key(prefs, roomjid, "notify", NULL);
467 }
468 
469 gboolean
prefs_has_room_notify_mention(const char * const roomjid)470 prefs_has_room_notify_mention(const char* const roomjid)
471 {
472     return g_key_file_has_key(prefs, roomjid, "notify.mention", NULL);
473 }
474 
475 gboolean
prefs_has_room_notify_trigger(const char * const roomjid)476 prefs_has_room_notify_trigger(const char* const roomjid)
477 {
478     return g_key_file_has_key(prefs, roomjid, "notify.trigger", NULL);
479 }
480 
481 gboolean
prefs_get_room_notify(const char * const roomjid)482 prefs_get_room_notify(const char* const roomjid)
483 {
484     return g_key_file_get_boolean(prefs, roomjid, "notify", NULL);
485 }
486 
487 gboolean
prefs_get_room_notify_mention(const char * const roomjid)488 prefs_get_room_notify_mention(const char* const roomjid)
489 {
490     return g_key_file_get_boolean(prefs, roomjid, "notify.mention", NULL);
491 }
492 
493 gboolean
prefs_get_room_notify_trigger(const char * const roomjid)494 prefs_get_room_notify_trigger(const char* const roomjid)
495 {
496     return g_key_file_get_boolean(prefs, roomjid, "notify.trigger", NULL);
497 }
498 
499 gboolean
prefs_reset_room_notify(const char * const roomjid)500 prefs_reset_room_notify(const char* const roomjid)
501 {
502     if (g_key_file_has_group(prefs, roomjid)) {
503         g_key_file_remove_group(prefs, roomjid, NULL);
504         return TRUE;
505     }
506 
507     return FALSE;
508 }
509 
510 gboolean
prefs_get_boolean(preference_t pref)511 prefs_get_boolean(preference_t pref)
512 {
513     const char* group = _get_group(pref);
514     const char* key = _get_key(pref);
515     gboolean def = _get_default_boolean(pref);
516 
517     if (!g_key_file_has_key(prefs, group, key, NULL)) {
518         return def;
519     }
520 
521     return g_key_file_get_boolean(prefs, group, key, NULL);
522 }
523 
524 void
prefs_set_boolean(preference_t pref,gboolean value)525 prefs_set_boolean(preference_t pref, gboolean value)
526 {
527     const char* group = _get_group(pref);
528     const char* key = _get_key(pref);
529     g_key_file_set_boolean(prefs, group, key, value);
530 }
531 
532 char*
prefs_get_string(preference_t pref)533 prefs_get_string(preference_t pref)
534 {
535     const char* group = _get_group(pref);
536     const char* key = _get_key(pref);
537     char* def = _get_default_string(pref);
538 
539     char* result = g_key_file_get_string(prefs, group, key, NULL);
540 
541     if (result == NULL) {
542         if (def) {
543             return g_strdup(def);
544         } else {
545             return NULL;
546         }
547     } else {
548         return result;
549     }
550 }
551 
552 char*
prefs_get_string_with_option(preference_t pref,gchar * option)553 prefs_get_string_with_option(preference_t pref, gchar* option)
554 {
555     const char* group = _get_group(pref);
556     const char* key = _get_key(pref);
557     char* def = _get_default_string(pref);
558 
559     char* result = g_key_file_get_locale_string(prefs, group, key, option, NULL);
560 
561     if (result == NULL) {
562         // check for user set default
563         result = g_key_file_get_locale_string(prefs, group, key, "*", NULL);
564         if (result == NULL) {
565             if (def) {
566                 // use hardcoded profanity default
567                 return g_strdup(def);
568             } else {
569                 return NULL;
570             }
571         }
572     }
573 
574     return result;
575 }
576 
577 void
prefs_set_string(preference_t pref,char * value)578 prefs_set_string(preference_t pref, char* value)
579 {
580     const char* group = _get_group(pref);
581     const char* key = _get_key(pref);
582     if (value == NULL) {
583         g_key_file_remove_key(prefs, group, key, NULL);
584     } else {
585         g_key_file_set_string(prefs, group, key, value);
586     }
587 }
588 
589 void
prefs_set_string_with_option(preference_t pref,char * option,char * value)590 prefs_set_string_with_option(preference_t pref, char* option, char* value)
591 {
592     const char* group = _get_group(pref);
593     const char* key = _get_key(pref);
594     if (value == NULL) {
595         g_key_file_remove_key(prefs, group, key, NULL);
596     } else {
597         g_key_file_set_locale_string(prefs, group, key, option, value);
598     }
599 }
600 
601 void
prefs_set_string_list_with_option(preference_t pref,char * option,const gchar * const * values)602 prefs_set_string_list_with_option(preference_t pref, char* option, const gchar* const* values)
603 {
604     const char* group = _get_group(pref);
605     const char* key = _get_key(pref);
606     if (values == NULL || *values == NULL) {
607         if (g_strcmp0(option, "*") == 0) {
608             g_key_file_set_string_list(prefs, group, key, NULL, 0);
609         } else {
610             g_key_file_set_locale_string_list(prefs, group, key, option, NULL, 0);
611         }
612     } else {
613         guint num_values = 0;
614         while (values[num_values]) {
615             num_values++;
616         }
617         if (g_strcmp0(option, "*") == 0) {
618             g_key_file_set_string_list(prefs, group, key, values, num_values);
619         } else {
620             g_key_file_set_locale_string_list(prefs, group, key, option, values, num_values);
621         }
622     }
623 }
624 
625 char*
prefs_get_tls_certpath(void)626 prefs_get_tls_certpath(void)
627 {
628     const char* group = _get_group(PREF_TLS_CERTPATH);
629     const char* key = _get_key(PREF_TLS_CERTPATH);
630 
631     char* setting = g_key_file_get_string(prefs, group, key, NULL);
632 
633     if (g_strcmp0(setting, "none") == 0) {
634         g_free(setting);
635         return NULL;
636     }
637 
638     if (setting == NULL) {
639         if (g_file_test("/etc/ssl/certs", G_FILE_TEST_IS_DIR)) {
640             return strdup("/etc/ssl/certs");
641         }
642         if (g_file_test("/etc/pki/tls/certs", G_FILE_TEST_IS_DIR)) {
643             return strdup("/etc/pki/tls/certs");
644         }
645         if (g_file_test("/etc/ssl", G_FILE_TEST_IS_DIR)) {
646             return strdup("/etc/ssl");
647         }
648         if (g_file_test("/etc/pki/tls", G_FILE_TEST_IS_DIR)) {
649             return strdup("/etc/pki/tls");
650         }
651         if (g_file_test("/system/etc/security/cacerts", G_FILE_TEST_IS_DIR)) {
652             return strdup("/system/etc/security/cacerts");
653         }
654 
655         return NULL;
656     }
657 
658     char* result = strdup(setting);
659     g_free(setting);
660 
661     return result;
662 }
663 
664 gint
prefs_get_gone(void)665 prefs_get_gone(void)
666 {
667     return g_key_file_get_integer(prefs, PREF_GROUP_CHATSTATES, "gone", NULL);
668 }
669 
670 void
prefs_set_gone(gint value)671 prefs_set_gone(gint value)
672 {
673     g_key_file_set_integer(prefs, PREF_GROUP_CHATSTATES, "gone", value);
674 }
675 
676 gint
prefs_get_notify_remind(void)677 prefs_get_notify_remind(void)
678 {
679     return g_key_file_get_integer(prefs, PREF_GROUP_NOTIFICATIONS, "remind", NULL);
680 }
681 
682 void
prefs_set_notify_remind(gint value)683 prefs_set_notify_remind(gint value)
684 {
685     g_key_file_set_integer(prefs, PREF_GROUP_NOTIFICATIONS, "remind", value);
686 }
687 
688 gint
prefs_get_max_log_size(void)689 prefs_get_max_log_size(void)
690 {
691     if (log_maxsize < PREFS_MIN_LOG_SIZE)
692         return PREFS_MAX_LOG_SIZE;
693     else
694         return log_maxsize;
695 }
696 
697 void
prefs_set_max_log_size(gint value)698 prefs_set_max_log_size(gint value)
699 {
700     log_maxsize = value;
701     g_key_file_set_integer(prefs, PREF_GROUP_LOGGING, "maxsize", value);
702 }
703 
704 gint
prefs_get_inpblock(void)705 prefs_get_inpblock(void)
706 {
707     int val = g_key_file_get_integer(prefs, PREF_GROUP_UI, "inpblock", NULL);
708     if (val == 0) {
709         return INPBLOCK_DEFAULT;
710     } else {
711         return val;
712     }
713 }
714 
715 void
prefs_set_inpblock(gint value)716 prefs_set_inpblock(gint value)
717 {
718     g_key_file_set_integer(prefs, PREF_GROUP_UI, "inpblock", value);
719 }
720 
721 gint
prefs_get_reconnect(void)722 prefs_get_reconnect(void)
723 {
724     if (!g_key_file_has_key(prefs, PREF_GROUP_CONNECTION, "reconnect", NULL)) {
725         return 30;
726     } else {
727         return g_key_file_get_integer(prefs, PREF_GROUP_CONNECTION, "reconnect", NULL);
728     }
729 }
730 
731 void
prefs_set_reconnect(gint value)732 prefs_set_reconnect(gint value)
733 {
734     g_key_file_set_integer(prefs, PREF_GROUP_CONNECTION, "reconnect", value);
735 }
736 
737 gint
prefs_get_autoping(void)738 prefs_get_autoping(void)
739 {
740     if (!g_key_file_has_key(prefs, PREF_GROUP_CONNECTION, "autoping", NULL)) {
741         return 60;
742     } else {
743         return g_key_file_get_integer(prefs, PREF_GROUP_CONNECTION, "autoping", NULL);
744     }
745 }
746 
747 void
prefs_set_autoping(gint value)748 prefs_set_autoping(gint value)
749 {
750     g_key_file_set_integer(prefs, PREF_GROUP_CONNECTION, "autoping", value);
751 }
752 
753 gint
prefs_get_autoping_timeout(void)754 prefs_get_autoping_timeout(void)
755 {
756     if (!g_key_file_has_key(prefs, PREF_GROUP_CONNECTION, "autoping.timeout", NULL)) {
757         return 20;
758     } else {
759         return g_key_file_get_integer(prefs, PREF_GROUP_CONNECTION, "autoping.timeout", NULL);
760     }
761 }
762 
763 void
prefs_set_autoping_timeout(gint value)764 prefs_set_autoping_timeout(gint value)
765 {
766     g_key_file_set_integer(prefs, PREF_GROUP_CONNECTION, "autoping.timeout", value);
767 }
768 
769 gint
prefs_get_autoaway_time(void)770 prefs_get_autoaway_time(void)
771 {
772     gint result = g_key_file_get_integer(prefs, PREF_GROUP_PRESENCE, "autoaway.awaytime", NULL);
773 
774     if (result == 0) {
775         return 15;
776     } else {
777         return result;
778     }
779 }
780 
781 gint
prefs_get_autoxa_time(void)782 prefs_get_autoxa_time(void)
783 {
784     return g_key_file_get_integer(prefs, PREF_GROUP_PRESENCE, "autoaway.xatime", NULL);
785 }
786 
787 void
prefs_set_autoaway_time(gint value)788 prefs_set_autoaway_time(gint value)
789 {
790     g_key_file_set_integer(prefs, PREF_GROUP_PRESENCE, "autoaway.awaytime", value);
791 }
792 
793 void
prefs_set_autoxa_time(gint value)794 prefs_set_autoxa_time(gint value)
795 {
796     g_key_file_set_integer(prefs, PREF_GROUP_PRESENCE, "autoaway.xatime", value);
797 }
798 
799 void
prefs_set_tray_timer(gint value)800 prefs_set_tray_timer(gint value)
801 {
802     g_key_file_set_integer(prefs, PREF_GROUP_NOTIFICATIONS, "tray.timer", value);
803 }
804 
805 gint
prefs_get_tray_timer(void)806 prefs_get_tray_timer(void)
807 {
808     gint result = g_key_file_get_integer(prefs, PREF_GROUP_NOTIFICATIONS, "tray.timer", NULL);
809 
810     if (result == 0) {
811         return 5;
812     } else {
813         return result;
814     }
815 }
816 
817 gint
prefs_get_statusbartabs(void)818 prefs_get_statusbartabs(void)
819 {
820     if (!g_key_file_has_key(prefs, PREF_GROUP_UI, "statusbar.tabs", NULL)) {
821         return 10;
822     } else {
823         return g_key_file_get_integer(prefs, PREF_GROUP_UI, "statusbar.tabs", NULL);
824     }
825 }
826 
827 void
prefs_set_statusbartabs(gint value)828 prefs_set_statusbartabs(gint value)
829 {
830     g_key_file_set_integer(prefs, PREF_GROUP_UI, "statusbar.tabs", value);
831 }
832 
833 gint
prefs_get_statusbartablen(void)834 prefs_get_statusbartablen(void)
835 {
836     if (!g_key_file_has_key(prefs, PREF_GROUP_UI, "statusbar.tablen", NULL)) {
837         return 0;
838     } else {
839         return g_key_file_get_integer(prefs, PREF_GROUP_UI, "statusbar.tablen", NULL);
840     }
841 }
842 
843 void
prefs_set_statusbartablen(gint value)844 prefs_set_statusbartablen(gint value)
845 {
846     g_key_file_set_integer(prefs, PREF_GROUP_UI, "statusbar.tablen", value);
847 }
848 
849 gchar**
prefs_get_plugins(void)850 prefs_get_plugins(void)
851 {
852     if (!g_key_file_has_group(prefs, PREF_GROUP_PLUGINS)) {
853         return NULL;
854     }
855     if (!g_key_file_has_key(prefs, PREF_GROUP_PLUGINS, "load", NULL)) {
856         return NULL;
857     }
858 
859     return g_key_file_get_string_list(prefs, PREF_GROUP_PLUGINS, "load", NULL, NULL);
860 }
861 
862 void
prefs_add_plugin(const char * const name)863 prefs_add_plugin(const char* const name)
864 {
865     conf_string_list_add(prefs, PREF_GROUP_PLUGINS, "load", name);
866     _save_prefs();
867 }
868 
869 void
prefs_remove_plugin(const char * const name)870 prefs_remove_plugin(const char* const name)
871 {
872     conf_string_list_remove(prefs, PREF_GROUP_PLUGINS, "load", name);
873     _save_prefs();
874 }
875 
876 void
prefs_free_plugins(gchar ** plugins)877 prefs_free_plugins(gchar** plugins)
878 {
879     g_strfreev(plugins);
880 }
881 
882 void
prefs_set_occupants_size(gint value)883 prefs_set_occupants_size(gint value)
884 {
885     g_key_file_set_integer(prefs, PREF_GROUP_UI, "occupants.size", value);
886 }
887 
888 gint
prefs_get_occupants_size(void)889 prefs_get_occupants_size(void)
890 {
891     gint result = g_key_file_get_integer(prefs, PREF_GROUP_UI, "occupants.size", NULL);
892 
893     if (result > 99 || result < 1) {
894         return 15;
895     } else {
896         return result;
897     }
898 }
899 
900 char
prefs_get_occupants_char(void)901 prefs_get_occupants_char(void)
902 {
903     char result = 0;
904 
905     char* resultstr = g_key_file_get_string(prefs, PREF_GROUP_UI, "occupants.char", NULL);
906     if (!resultstr) {
907         result = 0;
908     } else {
909         result = resultstr[0];
910     }
911     free(resultstr);
912 
913     return result;
914 }
915 
916 void
prefs_set_occupants_char(char ch)917 prefs_set_occupants_char(char ch)
918 {
919     char str[2];
920     str[0] = ch;
921     str[1] = '\0';
922 
923     g_key_file_set_string(prefs, PREF_GROUP_UI, "occupants.char", str);
924 }
925 
926 void
prefs_clear_occupants_char(void)927 prefs_clear_occupants_char(void)
928 {
929     g_key_file_remove_key(prefs, PREF_GROUP_UI, "occupants.char", NULL);
930 }
931 
932 gint
prefs_get_occupants_indent(void)933 prefs_get_occupants_indent(void)
934 {
935     if (!g_key_file_has_key(prefs, PREF_GROUP_UI, "occupants.indent", NULL)) {
936         return 2;
937     }
938 
939     gint result = g_key_file_get_integer(prefs, PREF_GROUP_UI, "occupants.indent", NULL);
940     if (result < 0) {
941         result = 0;
942     }
943 
944     return result;
945 }
946 
947 void
prefs_set_occupants_indent(gint value)948 prefs_set_occupants_indent(gint value)
949 {
950     g_key_file_set_integer(prefs, PREF_GROUP_UI, "occupants.indent", value);
951 }
952 
953 char
prefs_get_occupants_header_char(void)954 prefs_get_occupants_header_char(void)
955 {
956     char result = 0;
957 
958     char* resultstr = g_key_file_get_string(prefs, PREF_GROUP_UI, "occupants.header.char", NULL);
959     if (!resultstr) {
960         result = 0;
961     } else {
962         result = resultstr[0];
963     }
964     free(resultstr);
965 
966     return result;
967 }
968 
969 void
prefs_set_occupants_header_char(char ch)970 prefs_set_occupants_header_char(char ch)
971 {
972     char str[2];
973     str[0] = ch;
974     str[1] = '\0';
975 
976     g_key_file_set_string(prefs, PREF_GROUP_UI, "occupants.header.char", str);
977 }
978 
979 void
prefs_clear_occupants_header_char(void)980 prefs_clear_occupants_header_char(void)
981 {
982     g_key_file_remove_key(prefs, PREF_GROUP_UI, "occupants.header.char", NULL);
983 }
984 
985 void
prefs_set_roster_size(gint value)986 prefs_set_roster_size(gint value)
987 {
988     g_key_file_set_integer(prefs, PREF_GROUP_UI, "roster.size", value);
989 }
990 
991 gint
prefs_get_roster_size(void)992 prefs_get_roster_size(void)
993 {
994     gint result = g_key_file_get_integer(prefs, PREF_GROUP_UI, "roster.size", NULL);
995 
996     if (result > 99 || result < 1) {
997         return 25;
998     } else {
999         return result;
1000     }
1001 }
1002 
1003 static char*
_prefs_get_encryption_char(const char * const ch,const char * const pref_group,const char * const key)1004 _prefs_get_encryption_char(const char* const ch, const char* const pref_group, const char* const key)
1005 {
1006     char* result = NULL;
1007 
1008     char* resultstr = g_key_file_get_string(prefs, pref_group, key, NULL);
1009     if (!resultstr) {
1010         result = strdup(ch);
1011     } else {
1012         result = resultstr;
1013     }
1014 
1015     return result;
1016 }
1017 
1018 static gboolean
_prefs_set_encryption_char(const char * const ch,const char * const pref_group,const char * const key)1019 _prefs_set_encryption_char(const char* const ch, const char* const pref_group, const char* const key)
1020 {
1021     if (g_utf8_strlen(ch, 4) == 1) {
1022         g_key_file_set_string(prefs, pref_group, key, ch);
1023         return TRUE;
1024     } else {
1025         log_error("Could not set %s encryption char to: %s", key, ch);
1026     }
1027     return FALSE;
1028 }
1029 
1030 char*
prefs_get_otr_char(void)1031 prefs_get_otr_char(void)
1032 {
1033     return _prefs_get_encryption_char("~", PREF_GROUP_OTR, "otr.char");
1034 }
1035 
1036 gboolean
prefs_set_otr_char(char * ch)1037 prefs_set_otr_char(char* ch)
1038 {
1039     return _prefs_set_encryption_char(ch, PREF_GROUP_OTR, "otr.char");
1040 }
1041 
1042 char*
prefs_get_pgp_char(void)1043 prefs_get_pgp_char(void)
1044 {
1045     return _prefs_get_encryption_char("~", PREF_GROUP_PGP, "pgp.char");
1046 }
1047 
1048 gboolean
prefs_set_pgp_char(char * ch)1049 prefs_set_pgp_char(char* ch)
1050 {
1051     return _prefs_set_encryption_char(ch, PREF_GROUP_PGP, "pgp.char");
1052 }
1053 
1054 char*
prefs_get_ox_char(void)1055 prefs_get_ox_char(void)
1056 {
1057     return _prefs_get_encryption_char("%", PREF_GROUP_OX, "ox.char");
1058 }
1059 
1060 gboolean
prefs_set_ox_char(char * ch)1061 prefs_set_ox_char(char* ch)
1062 {
1063     return _prefs_set_encryption_char(ch, PREF_GROUP_OX, "ox.char");
1064 }
1065 
1066 char*
prefs_get_omemo_char(void)1067 prefs_get_omemo_char(void)
1068 {
1069     return _prefs_get_encryption_char("~", PREF_GROUP_OMEMO, "omemo.char");
1070 }
1071 
1072 gboolean
prefs_set_omemo_char(char * ch)1073 prefs_set_omemo_char(char* ch)
1074 {
1075     return _prefs_set_encryption_char(ch, PREF_GROUP_OMEMO, "omemo.char");
1076 }
1077 
1078 char
prefs_get_roster_header_char(void)1079 prefs_get_roster_header_char(void)
1080 {
1081     char result = 0;
1082 
1083     char* resultstr = g_key_file_get_string(prefs, PREF_GROUP_UI, "roster.header.char", NULL);
1084     if (!resultstr) {
1085         result = 0;
1086     } else {
1087         result = resultstr[0];
1088     }
1089     free(resultstr);
1090 
1091     return result;
1092 }
1093 
1094 void
prefs_set_roster_header_char(char ch)1095 prefs_set_roster_header_char(char ch)
1096 {
1097     char str[2];
1098     str[0] = ch;
1099     str[1] = '\0';
1100 
1101     g_key_file_set_string(prefs, PREF_GROUP_UI, "roster.header.char", str);
1102 }
1103 
1104 void
prefs_clear_roster_header_char(void)1105 prefs_clear_roster_header_char(void)
1106 {
1107     g_key_file_remove_key(prefs, PREF_GROUP_UI, "roster.header.char", NULL);
1108 }
1109 
1110 char
prefs_get_roster_contact_char(void)1111 prefs_get_roster_contact_char(void)
1112 {
1113     char result = 0;
1114 
1115     char* resultstr = g_key_file_get_string(prefs, PREF_GROUP_UI, "roster.contact.char", NULL);
1116     if (!resultstr) {
1117         result = 0;
1118     } else {
1119         result = resultstr[0];
1120     }
1121     free(resultstr);
1122 
1123     return result;
1124 }
1125 
1126 void
prefs_set_roster_contact_char(char ch)1127 prefs_set_roster_contact_char(char ch)
1128 {
1129     char str[2];
1130     str[0] = ch;
1131     str[1] = '\0';
1132 
1133     g_key_file_set_string(prefs, PREF_GROUP_UI, "roster.contact.char", str);
1134 }
1135 
1136 void
prefs_clear_roster_contact_char(void)1137 prefs_clear_roster_contact_char(void)
1138 {
1139     g_key_file_remove_key(prefs, PREF_GROUP_UI, "roster.contact.char", NULL);
1140 }
1141 
1142 char
prefs_get_roster_resource_char(void)1143 prefs_get_roster_resource_char(void)
1144 {
1145     char result = 0;
1146 
1147     char* resultstr = g_key_file_get_string(prefs, PREF_GROUP_UI, "roster.resource.char", NULL);
1148     if (!resultstr) {
1149         result = 0;
1150     } else {
1151         result = resultstr[0];
1152     }
1153     free(resultstr);
1154 
1155     return result;
1156 }
1157 
1158 void
prefs_set_roster_resource_char(char ch)1159 prefs_set_roster_resource_char(char ch)
1160 {
1161     char str[2];
1162     str[0] = ch;
1163     str[1] = '\0';
1164 
1165     g_key_file_set_string(prefs, PREF_GROUP_UI, "roster.resource.char", str);
1166 }
1167 
1168 void
prefs_clear_roster_resource_char(void)1169 prefs_clear_roster_resource_char(void)
1170 {
1171     g_key_file_remove_key(prefs, PREF_GROUP_UI, "roster.resource.char", NULL);
1172 }
1173 
1174 char
prefs_get_roster_private_char(void)1175 prefs_get_roster_private_char(void)
1176 {
1177     char result = 0;
1178 
1179     char* resultstr = g_key_file_get_string(prefs, PREF_GROUP_UI, "roster.private.char", NULL);
1180     if (!resultstr) {
1181         result = 0;
1182     } else {
1183         result = resultstr[0];
1184     }
1185     free(resultstr);
1186 
1187     return result;
1188 }
1189 
1190 void
prefs_set_roster_private_char(char ch)1191 prefs_set_roster_private_char(char ch)
1192 {
1193     char str[2];
1194     str[0] = ch;
1195     str[1] = '\0';
1196 
1197     g_key_file_set_string(prefs, PREF_GROUP_UI, "roster.private.char", str);
1198 }
1199 
1200 void
prefs_clear_roster_private_char(void)1201 prefs_clear_roster_private_char(void)
1202 {
1203     g_key_file_remove_key(prefs, PREF_GROUP_UI, "roster.private.char", NULL);
1204 }
1205 
1206 char
prefs_get_roster_room_char(void)1207 prefs_get_roster_room_char(void)
1208 {
1209     char result = 0;
1210 
1211     char* resultstr = g_key_file_get_string(prefs, PREF_GROUP_UI, "roster.rooms.char", NULL);
1212     if (!resultstr) {
1213         result = 0;
1214     } else {
1215         result = resultstr[0];
1216     }
1217     free(resultstr);
1218 
1219     return result;
1220 }
1221 
1222 void
prefs_set_roster_room_char(char ch)1223 prefs_set_roster_room_char(char ch)
1224 {
1225     char str[2];
1226     str[0] = ch;
1227     str[1] = '\0';
1228 
1229     g_key_file_set_string(prefs, PREF_GROUP_UI, "roster.rooms.char", str);
1230 }
1231 
1232 void
prefs_clear_roster_room_char(void)1233 prefs_clear_roster_room_char(void)
1234 {
1235     g_key_file_remove_key(prefs, PREF_GROUP_UI, "roster.rooms.char", NULL);
1236 }
1237 
1238 char
prefs_get_roster_room_private_char(void)1239 prefs_get_roster_room_private_char(void)
1240 {
1241     char result = 0;
1242 
1243     char* resultstr = g_key_file_get_string(prefs, PREF_GROUP_UI, "roster.rooms.private.char", NULL);
1244     if (!resultstr) {
1245         result = 0;
1246     } else {
1247         result = resultstr[0];
1248     }
1249     free(resultstr);
1250 
1251     return result;
1252 }
1253 
1254 void
prefs_set_roster_room_private_char(char ch)1255 prefs_set_roster_room_private_char(char ch)
1256 {
1257     char str[2];
1258     str[0] = ch;
1259     str[1] = '\0';
1260 
1261     g_key_file_set_string(prefs, PREF_GROUP_UI, "roster.rooms.private.char", str);
1262 }
1263 
1264 void
prefs_clear_roster_room_private_char(void)1265 prefs_clear_roster_room_private_char(void)
1266 {
1267     g_key_file_remove_key(prefs, PREF_GROUP_UI, "roster.rooms.pruvate.char", NULL);
1268 }
1269 
1270 gint
prefs_get_roster_contact_indent(void)1271 prefs_get_roster_contact_indent(void)
1272 {
1273     if (!g_key_file_has_key(prefs, PREF_GROUP_UI, "roster.contact.indent", NULL)) {
1274         return 2;
1275     }
1276 
1277     gint result = g_key_file_get_integer(prefs, PREF_GROUP_UI, "roster.contact.indent", NULL);
1278     if (result < 0) {
1279         result = 0;
1280     }
1281 
1282     return result;
1283 }
1284 
1285 void
prefs_set_roster_contact_indent(gint value)1286 prefs_set_roster_contact_indent(gint value)
1287 {
1288     g_key_file_set_integer(prefs, PREF_GROUP_UI, "roster.contact.indent", value);
1289 }
1290 
1291 gint
prefs_get_roster_resource_indent(void)1292 prefs_get_roster_resource_indent(void)
1293 {
1294     if (!g_key_file_has_key(prefs, PREF_GROUP_UI, "roster.resource.indent", NULL)) {
1295         return 2;
1296     }
1297 
1298     gint result = g_key_file_get_integer(prefs, PREF_GROUP_UI, "roster.resource.indent", NULL);
1299     if (result < 0) {
1300         result = 0;
1301     }
1302 
1303     return result;
1304 }
1305 
1306 void
prefs_set_roster_resource_indent(gint value)1307 prefs_set_roster_resource_indent(gint value)
1308 {
1309     g_key_file_set_integer(prefs, PREF_GROUP_UI, "roster.resource.indent", value);
1310 }
1311 
1312 gint
prefs_get_roster_presence_indent(void)1313 prefs_get_roster_presence_indent(void)
1314 {
1315     if (!g_key_file_has_key(prefs, PREF_GROUP_UI, "roster.presence.indent", NULL)) {
1316         return 2;
1317     }
1318 
1319     gint result = g_key_file_get_integer(prefs, PREF_GROUP_UI, "roster.presence.indent", NULL);
1320     if (result < -1) {
1321         result = 0;
1322     }
1323 
1324     return result;
1325 }
1326 
1327 void
prefs_set_roster_presence_indent(gint value)1328 prefs_set_roster_presence_indent(gint value)
1329 {
1330     g_key_file_set_integer(prefs, PREF_GROUP_UI, "roster.presence.indent", value);
1331 }
1332 
1333 char*
prefs_get_correction_char(void)1334 prefs_get_correction_char(void)
1335 {
1336     char* result = "+";
1337 
1338     char* resultstr = g_key_file_get_string(prefs, PREF_GROUP_UI, "correction.char", NULL);
1339     if (!resultstr) {
1340         result = strdup("+");
1341     } else {
1342         result = resultstr;
1343     }
1344 
1345     return result;
1346 }
1347 
1348 void
prefs_set_correction_char(char ch)1349 prefs_set_correction_char(char ch)
1350 {
1351     char str[2];
1352     str[0] = ch;
1353     str[1] = '\0';
1354 
1355     g_key_file_set_string(prefs, PREF_GROUP_UI, "correction.char", str);
1356 }
1357 
1358 gboolean
prefs_add_room_notify_trigger(const char * const text)1359 prefs_add_room_notify_trigger(const char* const text)
1360 {
1361     gboolean res = conf_string_list_add(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", text);
1362 
1363     if (res) {
1364         autocomplete_add(room_trigger_ac, text);
1365     }
1366 
1367     return res;
1368 }
1369 
1370 gboolean
prefs_remove_room_notify_trigger(const char * const text)1371 prefs_remove_room_notify_trigger(const char* const text)
1372 {
1373     gboolean res = conf_string_list_remove(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", text);
1374     _save_prefs();
1375 
1376     if (res) {
1377         autocomplete_remove(room_trigger_ac, text);
1378     }
1379 
1380     return res;
1381 }
1382 
1383 GList*
prefs_get_room_notify_triggers(void)1384 prefs_get_room_notify_triggers(void)
1385 {
1386     GList* result = NULL;
1387     gsize len = 0;
1388     gchar** triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", &len, NULL);
1389 
1390     for (int i = 0; i < len; i++) {
1391         result = g_list_append(result, strdup(triggers[i]));
1392     }
1393 
1394     g_strfreev(triggers);
1395 
1396     return result;
1397 }
1398 
1399 ProfWinPlacement*
prefs_create_profwin_placement(int titlebar,int mainwin,int statusbar,int inputwin)1400 prefs_create_profwin_placement(int titlebar, int mainwin, int statusbar, int inputwin)
1401 {
1402     ProfWinPlacement* placement = malloc(sizeof(ProfWinPlacement));
1403     placement->titlebar_pos = titlebar;
1404     placement->mainwin_pos = mainwin;
1405     placement->statusbar_pos = statusbar;
1406     placement->inputwin_pos = inputwin;
1407 
1408     return placement;
1409 }
1410 
1411 void
prefs_free_win_placement(ProfWinPlacement * placement)1412 prefs_free_win_placement(ProfWinPlacement* placement)
1413 {
1414     free(placement);
1415 }
1416 
1417 ProfWinPlacement*
prefs_get_win_placement(void)1418 prefs_get_win_placement(void)
1419 {
1420     // read from settings file
1421     int titlebar_pos = g_key_file_get_integer(prefs, PREF_GROUP_UI, "titlebar.position", NULL);
1422     int mainwin_pos = g_key_file_get_integer(prefs, PREF_GROUP_UI, "mainwin.position", NULL);
1423     int statusbar_pos = g_key_file_get_integer(prefs, PREF_GROUP_UI, "statusbar.position", NULL);
1424     int inputwin_pos = g_key_file_get_integer(prefs, PREF_GROUP_UI, "inputwin.position", NULL);
1425 
1426     // default if setting invalid, or not present
1427     if (titlebar_pos < 1 || titlebar_pos > 4) {
1428         titlebar_pos = 1;
1429     }
1430     if (mainwin_pos < 1 || mainwin_pos > 4) {
1431         mainwin_pos = 2;
1432     }
1433     if (statusbar_pos < 1 || statusbar_pos > 4) {
1434         statusbar_pos = 3;
1435     }
1436     if (inputwin_pos < 1 || inputwin_pos > 4) {
1437         inputwin_pos = 4;
1438     }
1439 
1440     // return default if duplicates found
1441     if (titlebar_pos == mainwin_pos) {
1442         return prefs_create_profwin_placement(1, 2, 3, 4);
1443     }
1444     if (titlebar_pos == statusbar_pos) {
1445         return prefs_create_profwin_placement(1, 2, 3, 4);
1446     }
1447     if (titlebar_pos == inputwin_pos) {
1448         return prefs_create_profwin_placement(1, 2, 3, 4);
1449     }
1450 
1451     if (mainwin_pos == statusbar_pos) {
1452         return prefs_create_profwin_placement(1, 2, 3, 4);
1453     }
1454     if (mainwin_pos == inputwin_pos) {
1455         return prefs_create_profwin_placement(1, 2, 3, 4);
1456     }
1457 
1458     if (statusbar_pos == inputwin_pos) {
1459         return prefs_create_profwin_placement(1, 2, 3, 4);
1460     }
1461 
1462     // return settings
1463     return prefs_create_profwin_placement(titlebar_pos, mainwin_pos, statusbar_pos, inputwin_pos);
1464 }
1465 
1466 void
prefs_save_win_placement(ProfWinPlacement * placement)1467 prefs_save_win_placement(ProfWinPlacement* placement)
1468 {
1469     g_key_file_set_integer(prefs, PREF_GROUP_UI, "titlebar.position", placement->titlebar_pos);
1470     g_key_file_set_integer(prefs, PREF_GROUP_UI, "mainwin.position", placement->mainwin_pos);
1471     g_key_file_set_integer(prefs, PREF_GROUP_UI, "statusbar.position", placement->statusbar_pos);
1472     g_key_file_set_integer(prefs, PREF_GROUP_UI, "inputwin.position", placement->inputwin_pos);
1473     _save_prefs();
1474 }
1475 
1476 gboolean
prefs_titlebar_pos_up(void)1477 prefs_titlebar_pos_up(void)
1478 {
1479     ProfWinPlacement* placement = prefs_get_win_placement();
1480 
1481     for (int pos = 2; pos < 5; pos++) {
1482         if (placement->titlebar_pos == pos) {
1483             placement->titlebar_pos = pos - 1;
1484 
1485             if (placement->mainwin_pos == pos - 1) {
1486                 placement->mainwin_pos = pos;
1487             } else if (placement->statusbar_pos == pos - 1) {
1488                 placement->statusbar_pos = pos;
1489             } else if (placement->inputwin_pos == pos - 1) {
1490                 placement->inputwin_pos = pos;
1491             }
1492 
1493             prefs_save_win_placement(placement);
1494             prefs_free_win_placement(placement);
1495             return TRUE;
1496         }
1497     }
1498 
1499     prefs_free_win_placement(placement);
1500     return FALSE;
1501 }
1502 
1503 gboolean
prefs_mainwin_pos_up(void)1504 prefs_mainwin_pos_up(void)
1505 {
1506     ProfWinPlacement* placement = prefs_get_win_placement();
1507 
1508     for (int pos = 2; pos < 5; pos++) {
1509         if (placement->mainwin_pos == pos) {
1510             placement->mainwin_pos = pos - 1;
1511 
1512             if (placement->titlebar_pos == pos - 1) {
1513                 placement->titlebar_pos = pos;
1514             } else if (placement->statusbar_pos == pos - 1) {
1515                 placement->statusbar_pos = pos;
1516             } else if (placement->inputwin_pos == pos - 1) {
1517                 placement->inputwin_pos = pos;
1518             }
1519 
1520             prefs_save_win_placement(placement);
1521             prefs_free_win_placement(placement);
1522             return TRUE;
1523         }
1524     }
1525 
1526     prefs_free_win_placement(placement);
1527     return FALSE;
1528 }
1529 
1530 gboolean
prefs_statusbar_pos_up(void)1531 prefs_statusbar_pos_up(void)
1532 {
1533     ProfWinPlacement* placement = prefs_get_win_placement();
1534 
1535     for (int pos = 2; pos < 5; pos++) {
1536         if (placement->statusbar_pos == pos) {
1537             placement->statusbar_pos = pos - 1;
1538 
1539             if (placement->titlebar_pos == pos - 1) {
1540                 placement->titlebar_pos = pos;
1541             } else if (placement->mainwin_pos == pos - 1) {
1542                 placement->mainwin_pos = pos;
1543             } else if (placement->inputwin_pos == pos - 1) {
1544                 placement->inputwin_pos = pos;
1545             }
1546 
1547             prefs_save_win_placement(placement);
1548             prefs_free_win_placement(placement);
1549             return TRUE;
1550         }
1551     }
1552 
1553     prefs_free_win_placement(placement);
1554     return FALSE;
1555 }
1556 
1557 gboolean
prefs_inputwin_pos_up(void)1558 prefs_inputwin_pos_up(void)
1559 {
1560     ProfWinPlacement* placement = prefs_get_win_placement();
1561 
1562     for (int pos = 2; pos < 5; pos++) {
1563         if (placement->inputwin_pos == pos) {
1564             placement->inputwin_pos = pos - 1;
1565 
1566             if (placement->titlebar_pos == pos - 1) {
1567                 placement->titlebar_pos = pos;
1568             } else if (placement->mainwin_pos == pos - 1) {
1569                 placement->mainwin_pos = pos;
1570             } else if (placement->statusbar_pos == pos - 1) {
1571                 placement->statusbar_pos = pos;
1572             }
1573 
1574             prefs_save_win_placement(placement);
1575             prefs_free_win_placement(placement);
1576             return TRUE;
1577         }
1578     }
1579 
1580     prefs_free_win_placement(placement);
1581     return FALSE;
1582 }
1583 
1584 gboolean
prefs_titlebar_pos_down(void)1585 prefs_titlebar_pos_down(void)
1586 {
1587     ProfWinPlacement* placement = prefs_get_win_placement();
1588 
1589     for (int pos = 1; pos < 4; pos++) {
1590         if (placement->titlebar_pos == pos) {
1591             placement->titlebar_pos = pos + 1;
1592 
1593             if (placement->mainwin_pos == pos + 1) {
1594                 placement->mainwin_pos = pos;
1595             } else if (placement->statusbar_pos == pos + 1) {
1596                 placement->statusbar_pos = pos;
1597             } else if (placement->inputwin_pos == pos + 1) {
1598                 placement->inputwin_pos = pos;
1599             }
1600 
1601             prefs_save_win_placement(placement);
1602             prefs_free_win_placement(placement);
1603             return TRUE;
1604         }
1605     }
1606 
1607     prefs_free_win_placement(placement);
1608     return FALSE;
1609 }
1610 
1611 gboolean
prefs_mainwin_pos_down(void)1612 prefs_mainwin_pos_down(void)
1613 {
1614     ProfWinPlacement* placement = prefs_get_win_placement();
1615 
1616     for (int pos = 1; pos < 4; pos++) {
1617         if (placement->mainwin_pos == pos) {
1618             placement->mainwin_pos = pos + 1;
1619 
1620             if (placement->titlebar_pos == pos + 1) {
1621                 placement->titlebar_pos = pos;
1622             } else if (placement->statusbar_pos == pos + 1) {
1623                 placement->statusbar_pos = pos;
1624             } else if (placement->inputwin_pos == pos + 1) {
1625                 placement->inputwin_pos = pos;
1626             }
1627 
1628             prefs_save_win_placement(placement);
1629             prefs_free_win_placement(placement);
1630             return TRUE;
1631         }
1632     }
1633 
1634     prefs_free_win_placement(placement);
1635     return FALSE;
1636 }
1637 
1638 gboolean
prefs_statusbar_pos_down(void)1639 prefs_statusbar_pos_down(void)
1640 {
1641     ProfWinPlacement* placement = prefs_get_win_placement();
1642 
1643     for (int pos = 1; pos < 4; pos++) {
1644         if (placement->statusbar_pos == pos) {
1645             placement->statusbar_pos = pos + 1;
1646 
1647             if (placement->titlebar_pos == pos + 1) {
1648                 placement->titlebar_pos = pos;
1649             } else if (placement->mainwin_pos == pos + 1) {
1650                 placement->mainwin_pos = pos;
1651             } else if (placement->inputwin_pos == pos + 1) {
1652                 placement->inputwin_pos = pos;
1653             }
1654 
1655             prefs_save_win_placement(placement);
1656             prefs_free_win_placement(placement);
1657             return TRUE;
1658         }
1659     }
1660 
1661     prefs_free_win_placement(placement);
1662     return FALSE;
1663 }
1664 
1665 gboolean
prefs_inputwin_pos_down(void)1666 prefs_inputwin_pos_down(void)
1667 {
1668     ProfWinPlacement* placement = prefs_get_win_placement();
1669 
1670     for (int pos = 1; pos < 4; pos++) {
1671         if (placement->inputwin_pos == pos) {
1672             placement->inputwin_pos = pos + 1;
1673 
1674             if (placement->titlebar_pos == pos + 1) {
1675                 placement->titlebar_pos = pos;
1676             } else if (placement->mainwin_pos == pos + 1) {
1677                 placement->mainwin_pos = pos;
1678             } else if (placement->statusbar_pos == pos + 1) {
1679                 placement->statusbar_pos = pos;
1680             }
1681 
1682             prefs_save_win_placement(placement);
1683             prefs_free_win_placement(placement);
1684             return TRUE;
1685         }
1686     }
1687 
1688     prefs_free_win_placement(placement);
1689     return FALSE;
1690 }
1691 
1692 gboolean
prefs_add_alias(const char * const name,const char * const value)1693 prefs_add_alias(const char* const name, const char* const value)
1694 {
1695     if (g_key_file_has_key(prefs, PREF_GROUP_ALIAS, name, NULL)) {
1696         return FALSE;
1697     } else {
1698         g_key_file_set_string(prefs, PREF_GROUP_ALIAS, name, value);
1699         return TRUE;
1700     }
1701 }
1702 
1703 char*
prefs_get_alias(const char * const name)1704 prefs_get_alias(const char* const name)
1705 {
1706     return g_key_file_get_string(prefs, PREF_GROUP_ALIAS, name, NULL);
1707 }
1708 
1709 gboolean
prefs_remove_alias(const char * const name)1710 prefs_remove_alias(const char* const name)
1711 {
1712     if (!g_key_file_has_key(prefs, PREF_GROUP_ALIAS, name, NULL)) {
1713         return FALSE;
1714     } else {
1715         g_key_file_remove_key(prefs, PREF_GROUP_ALIAS, name, NULL);
1716         return TRUE;
1717     }
1718 }
1719 
1720 static gint
_alias_cmp(gconstpointer * p1,gconstpointer * p2)1721 _alias_cmp(gconstpointer* p1, gconstpointer* p2)
1722 {
1723     ProfAlias* alias1 = (ProfAlias*)p1;
1724     ProfAlias* alias2 = (ProfAlias*)p2;
1725 
1726     return strcmp(alias1->name, alias2->name);
1727 }
1728 
1729 GList*
prefs_get_aliases(void)1730 prefs_get_aliases(void)
1731 {
1732     if (!g_key_file_has_group(prefs, PREF_GROUP_ALIAS)) {
1733         return NULL;
1734     } else {
1735         GList* result = NULL;
1736         gsize len;
1737         gchar** keys = g_key_file_get_keys(prefs, PREF_GROUP_ALIAS, &len, NULL);
1738 
1739         for (int i = 0; i < len; i++) {
1740             char* name = keys[i];
1741             char* value = g_key_file_get_string(prefs, PREF_GROUP_ALIAS, name, NULL);
1742 
1743             if (value) {
1744                 ProfAlias* alias = malloc(sizeof(struct prof_alias_t));
1745                 alias->name = strdup(name);
1746                 alias->value = strdup(value);
1747 
1748                 free(value);
1749 
1750                 result = g_list_insert_sorted(result, alias, (GCompareFunc)_alias_cmp);
1751             }
1752         }
1753 
1754         g_strfreev(keys);
1755 
1756         return result;
1757     }
1758 }
1759 
1760 void
_free_alias(ProfAlias * alias)1761 _free_alias(ProfAlias* alias)
1762 {
1763     FREE_SET_NULL(alias->name);
1764     FREE_SET_NULL(alias->value);
1765     FREE_SET_NULL(alias);
1766 }
1767 
1768 void
prefs_free_aliases(GList * aliases)1769 prefs_free_aliases(GList* aliases)
1770 {
1771     g_list_free_full(aliases, (GDestroyNotify)_free_alias);
1772 }
1773 
1774 static void
_save_prefs(void)1775 _save_prefs(void)
1776 {
1777     gsize g_data_size;
1778     gchar* g_prefs_data = g_key_file_to_data(prefs, &g_data_size, NULL);
1779     gchar* base = g_path_get_dirname(prefs_loc);
1780     gchar* true_loc = get_file_or_linked(prefs_loc, base);
1781 
1782     g_file_set_contents(true_loc, g_prefs_data, g_data_size, NULL);
1783     g_chmod(prefs_loc, S_IRUSR | S_IWUSR);
1784 
1785     g_free(base);
1786     free(true_loc);
1787     g_free(g_prefs_data);
1788 }
1789 
1790 // get the preference group for a specific preference
1791 // for example the PREF_BEEP setting ("beep" in .profrc, see _get_key) belongs
1792 // to the [ui] section.
1793 static const char*
_get_group(preference_t pref)1794 _get_group(preference_t pref)
1795 {
1796     switch (pref) {
1797     case PREF_CLEAR_PERSIST_HISTORY:
1798     case PREF_SPLASH:
1799     case PREF_BEEP:
1800     case PREF_THEME:
1801     case PREF_VERCHECK:
1802     case PREF_WINTITLE_SHOW:
1803     case PREF_WINTITLE_GOODBYE:
1804     case PREF_FLASH:
1805     case PREF_INTYPE:
1806     case PREF_INTYPE_CONSOLE:
1807     case PREF_HISTORY:
1808     case PREF_OCCUPANTS:
1809     case PREF_OCCUPANTS_JID:
1810     case PREF_OCCUPANTS_OFFLINE:
1811     case PREF_OCCUPANTS_WRAP:
1812     case PREF_STATUSES:
1813     case PREF_STATUSES_CONSOLE:
1814     case PREF_STATUSES_CHAT:
1815     case PREF_STATUSES_MUC:
1816     case PREF_MUC_PRIVILEGES:
1817     case PREF_PRESENCE:
1818     case PREF_WRAP:
1819     case PREF_TIME_CONSOLE:
1820     case PREF_TIME_CHAT:
1821     case PREF_TIME_MUC:
1822     case PREF_TIME_CONFIG:
1823     case PREF_TIME_PRIVATE:
1824     case PREF_TIME_XMLCONSOLE:
1825     case PREF_TIME_STATUSBAR:
1826     case PREF_TIME_LASTACTIVITY:
1827     case PREF_ROSTER:
1828     case PREF_ROSTER_OFFLINE:
1829     case PREF_ROSTER_RESOURCE:
1830     case PREF_ROSTER_PRESENCE:
1831     case PREF_ROSTER_STATUS:
1832     case PREF_ROSTER_EMPTY:
1833     case PREF_ROSTER_BY:
1834     case PREF_ROSTER_ORDER:
1835     case PREF_ROSTER_UNREAD:
1836     case PREF_ROSTER_COUNT:
1837     case PREF_ROSTER_COUNT_ZERO:
1838     case PREF_ROSTER_PRIORITY:
1839     case PREF_ROSTER_WRAP:
1840     case PREF_ROSTER_RESOURCE_JOIN:
1841     case PREF_ROSTER_CONTACTS:
1842     case PREF_ROSTER_UNSUBSCRIBED:
1843     case PREF_ROSTER_ROOMS:
1844     case PREF_ROSTER_ROOMS_POS:
1845     case PREF_ROSTER_ROOMS_BY:
1846     case PREF_ROSTER_ROOMS_ORDER:
1847     case PREF_ROSTER_ROOMS_UNREAD:
1848     case PREF_ROSTER_ROOMS_SERVER:
1849     case PREF_ROSTER_ROOMS_USE_AS_NAME:
1850     case PREF_ROSTER_PRIVATE:
1851     case PREF_RESOURCE_TITLE:
1852     case PREF_RESOURCE_MESSAGE:
1853     case PREF_ENC_WARN:
1854     case PREF_INPBLOCK_DYNAMIC:
1855     case PREF_TLS_SHOW:
1856     case PREF_CONSOLE_MUC:
1857     case PREF_CONSOLE_PRIVATE:
1858     case PREF_CONSOLE_CHAT:
1859     case PREF_COLOR_NICK:
1860     case PREF_COLOR_NICK_OWN:
1861     case PREF_ROSTER_COLOR_NICK:
1862     case PREF_OCCUPANTS_COLOR_NICK:
1863     case PREF_STATUSBAR_SHOW_NAME:
1864     case PREF_STATUSBAR_SHOW_NUMBER:
1865     case PREF_STATUSBAR_SHOW_READ:
1866     case PREF_STATUSBAR_SELF:
1867     case PREF_STATUSBAR_CHAT:
1868     case PREF_STATUSBAR_ROOM:
1869     case PREF_TITLEBAR_MUC_TITLE_JID:
1870     case PREF_TITLEBAR_MUC_TITLE_NAME:
1871     case PREF_SLASH_GUARD:
1872     case PREF_COMPOSE_EDITOR:
1873         return PREF_GROUP_UI;
1874     case PREF_STATES:
1875     case PREF_OUTTYPE:
1876         return PREF_GROUP_CHATSTATES;
1877     case PREF_NOTIFY_TYPING:
1878     case PREF_NOTIFY_TYPING_CURRENT:
1879     case PREF_NOTIFY_CHAT:
1880     case PREF_NOTIFY_CHAT_CURRENT:
1881     case PREF_NOTIFY_CHAT_TEXT:
1882     case PREF_NOTIFY_ROOM:
1883     case PREF_NOTIFY_ROOM_MENTION:
1884     case PREF_NOTIFY_ROOM_TRIGGER:
1885     case PREF_NOTIFY_ROOM_CURRENT:
1886     case PREF_NOTIFY_ROOM_TEXT:
1887     case PREF_NOTIFY_INVITE:
1888     case PREF_NOTIFY_SUB:
1889     case PREF_NOTIFY_MENTION_CASE_SENSITIVE:
1890     case PREF_NOTIFY_MENTION_WHOLE_WORD:
1891     case PREF_TRAY:
1892     case PREF_TRAY_READ:
1893     case PREF_ADV_NOTIFY_DISCO_OR_VERSION:
1894         return PREF_GROUP_NOTIFICATIONS;
1895     case PREF_CHLOG:
1896     case PREF_GRLOG:
1897     case PREF_LOG_ROTATE:
1898     case PREF_LOG_SHARED:
1899         return PREF_GROUP_LOGGING;
1900     case PREF_AVATAR_CMD:
1901     case PREF_URL_OPEN_CMD:
1902     case PREF_URL_SAVE_CMD:
1903         return PREF_GROUP_EXECUTABLES;
1904     case PREF_AUTOAWAY_CHECK:
1905     case PREF_AUTOAWAY_MODE:
1906     case PREF_AUTOAWAY_MESSAGE:
1907     case PREF_AUTOXA_MESSAGE:
1908     case PREF_LASTACTIVITY:
1909         return PREF_GROUP_PRESENCE;
1910     case PREF_CONNECT_ACCOUNT:
1911     case PREF_DEFAULT_ACCOUNT:
1912     case PREF_CARBONS:
1913     case PREF_RECEIPTS_SEND:
1914     case PREF_RECEIPTS_REQUEST:
1915     case PREF_REVEAL_OS:
1916     case PREF_TLS_CERTPATH:
1917     case PREF_CORRECTION_ALLOW:
1918     case PREF_MAM:
1919     case PREF_SILENCE_NON_ROSTER:
1920         return PREF_GROUP_CONNECTION;
1921     case PREF_OTR_LOG:
1922     case PREF_OTR_POLICY:
1923     case PREF_OTR_SENDFILE:
1924         return PREF_GROUP_OTR;
1925     case PREF_PGP_LOG:
1926     case PREF_PGP_SENDFILE:
1927         return PREF_GROUP_PGP;
1928     case PREF_BOOKMARK_INVITE:
1929     case PREF_ROOM_LIST_CACHE:
1930         return PREF_GROUP_MUC;
1931     case PREF_PLUGINS_SOURCEPATH:
1932         return PREF_GROUP_PLUGINS;
1933     case PREF_OMEMO_LOG:
1934     case PREF_OMEMO_POLICY:
1935     case PREF_OMEMO_TRUST_MODE:
1936         return PREF_GROUP_OMEMO;
1937     default:
1938         return NULL;
1939     }
1940 }
1941 
1942 // get the key used in .profrc for the preference
1943 // for example the PREF_AUTOAWAY_MODE maps to "autoaway.mode" in .profrc
1944 static const char*
_get_key(preference_t pref)1945 _get_key(preference_t pref)
1946 {
1947     switch (pref) {
1948     case PREF_CLEAR_PERSIST_HISTORY:
1949         return "clear.persist_history";
1950     case PREF_SPLASH:
1951         return "splash";
1952     case PREF_BEEP:
1953         return "beep";
1954     case PREF_THEME:
1955         return "theme";
1956     case PREF_VERCHECK:
1957         return "vercheck";
1958     case PREF_WINTITLE_SHOW:
1959         return "wintitle.show";
1960     case PREF_WINTITLE_GOODBYE:
1961         return "wintitle.goodbye";
1962     case PREF_FLASH:
1963         return "flash";
1964     case PREF_TRAY:
1965         return "tray";
1966     case PREF_TRAY_READ:
1967         return "tray.read";
1968     case PREF_ADV_NOTIFY_DISCO_OR_VERSION:
1969         return "adv.notify.discoversion";
1970     case PREF_INTYPE:
1971         return "intype";
1972     case PREF_INTYPE_CONSOLE:
1973         return "intype.console";
1974     case PREF_HISTORY:
1975         return "history";
1976     case PREF_CARBONS:
1977         return "carbons";
1978     case PREF_RECEIPTS_SEND:
1979         return "receipts.send";
1980     case PREF_RECEIPTS_REQUEST:
1981         return "receipts.request";
1982     case PREF_REVEAL_OS:
1983         return "reveal.os";
1984     case PREF_OCCUPANTS:
1985         return "occupants";
1986     case PREF_OCCUPANTS_JID:
1987         return "occupants.jid";
1988     case PREF_OCCUPANTS_OFFLINE:
1989         return "occupants.offline";
1990     case PREF_OCCUPANTS_WRAP:
1991         return "occupants.wrap";
1992     case PREF_MUC_PRIVILEGES:
1993         return "privileges";
1994     case PREF_STATUSES:
1995         return "statuses";
1996     case PREF_STATUSES_CONSOLE:
1997         return "statuses.console";
1998     case PREF_STATUSES_CHAT:
1999         return "statuses.chat";
2000     case PREF_STATUSES_MUC:
2001         return "statuses.muc";
2002     case PREF_STATES:
2003         return "enabled";
2004     case PREF_OUTTYPE:
2005         return "outtype";
2006     case PREF_NOTIFY_TYPING:
2007         return "typing";
2008     case PREF_NOTIFY_TYPING_CURRENT:
2009         return "typing.current";
2010     case PREF_NOTIFY_CHAT:
2011         return "message";
2012     case PREF_NOTIFY_CHAT_CURRENT:
2013         return "message.current";
2014     case PREF_NOTIFY_CHAT_TEXT:
2015         return "message.text";
2016     case PREF_NOTIFY_ROOM:
2017         return "room";
2018     case PREF_NOTIFY_ROOM_TRIGGER:
2019         return "room.trigger";
2020     case PREF_NOTIFY_ROOM_MENTION:
2021         return "room.mention";
2022     case PREF_NOTIFY_ROOM_CURRENT:
2023         return "room.current";
2024     case PREF_NOTIFY_ROOM_TEXT:
2025         return "room.text";
2026     case PREF_NOTIFY_INVITE:
2027         return "invite";
2028     case PREF_NOTIFY_SUB:
2029         return "sub";
2030     case PREF_NOTIFY_MENTION_CASE_SENSITIVE:
2031         return "room.mention.casesensitive";
2032     case PREF_NOTIFY_MENTION_WHOLE_WORD:
2033         return "room.mention.wholeword";
2034     case PREF_CHLOG:
2035         return "chlog";
2036     case PREF_GRLOG:
2037         return "grlog";
2038     case PREF_AUTOAWAY_CHECK:
2039         return "autoaway.check";
2040     case PREF_AUTOAWAY_MODE:
2041         return "autoaway.mode";
2042     case PREF_AUTOAWAY_MESSAGE:
2043         return "autoaway.awaymessage";
2044     case PREF_AUTOXA_MESSAGE:
2045         return "autoaway.xamessage";
2046     case PREF_CONNECT_ACCOUNT:
2047         return "account";
2048     case PREF_DEFAULT_ACCOUNT:
2049         return "defaccount";
2050     case PREF_OTR_LOG:
2051         return "log";
2052     case PREF_OTR_POLICY:
2053         return "policy";
2054     case PREF_OTR_SENDFILE:
2055         return "sendfile";
2056     case PREF_LOG_ROTATE:
2057         return "rotate";
2058     case PREF_LOG_SHARED:
2059         return "shared";
2060     case PREF_PRESENCE:
2061         return "presence";
2062     case PREF_WRAP:
2063         return "wrap";
2064     case PREF_TIME_CONSOLE:
2065         return "time.console";
2066     case PREF_TIME_CHAT:
2067         return "time.chat";
2068     case PREF_TIME_MUC:
2069         return "time.muc";
2070     case PREF_TIME_CONFIG:
2071         return "time.config";
2072     case PREF_TIME_PRIVATE:
2073         return "time.private";
2074     case PREF_TIME_XMLCONSOLE:
2075         return "time.xmlconsole";
2076     case PREF_TIME_STATUSBAR:
2077         return "time.statusbar";
2078     case PREF_TIME_LASTACTIVITY:
2079         return "time.lastactivity";
2080     case PREF_ROSTER:
2081         return "roster";
2082     case PREF_ROSTER_OFFLINE:
2083         return "roster.offline";
2084     case PREF_ROSTER_RESOURCE:
2085         return "roster.resource";
2086     case PREF_ROSTER_PRESENCE:
2087         return "roster.presence";
2088     case PREF_ROSTER_STATUS:
2089         return "roster.status";
2090     case PREF_ROSTER_EMPTY:
2091         return "roster.empty";
2092     case PREF_ROSTER_BY:
2093         return "roster.by";
2094     case PREF_ROSTER_ORDER:
2095         return "roster.order";
2096     case PREF_ROSTER_UNREAD:
2097         return "roster.unread";
2098     case PREF_ROSTER_COUNT:
2099         return "roster.count";
2100     case PREF_ROSTER_COUNT_ZERO:
2101         return "roster.count.zero";
2102     case PREF_ROSTER_PRIORITY:
2103         return "roster.priority";
2104     case PREF_ROSTER_WRAP:
2105         return "roster.wrap";
2106     case PREF_ROSTER_RESOURCE_JOIN:
2107         return "roster.resource.join";
2108     case PREF_ROSTER_CONTACTS:
2109         return "roster.contacts";
2110     case PREF_ROSTER_UNSUBSCRIBED:
2111         return "roster.unsubscribed";
2112     case PREF_ROSTER_ROOMS:
2113         return "roster.rooms";
2114     case PREF_ROSTER_ROOMS_POS:
2115         return "roster.rooms.pos";
2116     case PREF_ROSTER_ROOMS_BY:
2117         return "roster.rooms.by";
2118     case PREF_ROSTER_ROOMS_ORDER:
2119         return "roster.rooms.order";
2120     case PREF_ROSTER_ROOMS_UNREAD:
2121         return "roster.rooms.unread";
2122     case PREF_ROSTER_ROOMS_SERVER:
2123         return "roster.rooms.server";
2124     case PREF_ROSTER_ROOMS_USE_AS_NAME:
2125         return "roster.rooms.use.name";
2126     case PREF_ROSTER_PRIVATE:
2127         return "roster.private";
2128     case PREF_RESOURCE_TITLE:
2129         return "resource.title";
2130     case PREF_RESOURCE_MESSAGE:
2131         return "resource.message";
2132     case PREF_INPBLOCK_DYNAMIC:
2133         return "inpblock.dynamic";
2134     case PREF_ENC_WARN:
2135         return "enc.warn";
2136     case PREF_TITLEBAR_MUC_TITLE_JID:
2137         return "titlebar.muc.title.jid";
2138     case PREF_TITLEBAR_MUC_TITLE_NAME:
2139         return "titlebar.muc.title.name";
2140     case PREF_PGP_LOG:
2141         return "log";
2142     case PREF_PGP_SENDFILE:
2143         return "sendfile";
2144     case PREF_TLS_CERTPATH:
2145         return "tls.certpath";
2146     case PREF_TLS_SHOW:
2147         return "tls.show";
2148     case PREF_LASTACTIVITY:
2149         return "lastactivity";
2150     case PREF_CONSOLE_MUC:
2151         return "console.muc";
2152     case PREF_CONSOLE_PRIVATE:
2153         return "console.private";
2154     case PREF_CONSOLE_CHAT:
2155         return "console.chat";
2156     case PREF_COLOR_NICK:
2157         return "color.nick";
2158     case PREF_COLOR_NICK_OWN:
2159         return "color.nick.own";
2160     case PREF_ROSTER_COLOR_NICK:
2161         return "color.roster.nick";
2162     case PREF_OCCUPANTS_COLOR_NICK:
2163         return "color.occupants.nick";
2164     case PREF_BOOKMARK_INVITE:
2165         return "bookmark.invite";
2166     case PREF_PLUGINS_SOURCEPATH:
2167         return "sourcepath";
2168     case PREF_ROOM_LIST_CACHE:
2169         return "rooms.cache";
2170     case PREF_STATUSBAR_SHOW_NAME:
2171         return "statusbar.show.name";
2172     case PREF_STATUSBAR_SHOW_NUMBER:
2173         return "statusbar.show.number";
2174     case PREF_STATUSBAR_SHOW_READ:
2175         return "statusbar.show.read";
2176     case PREF_STATUSBAR_SELF:
2177         return "statusbar.self";
2178     case PREF_STATUSBAR_CHAT:
2179         return "statusbar.chat";
2180     case PREF_STATUSBAR_ROOM:
2181         return "statusbar.room";
2182     case PREF_OMEMO_LOG:
2183         return "log";
2184     case PREF_OMEMO_POLICY:
2185         return "policy";
2186     case PREF_OMEMO_TRUST_MODE:
2187         return "trustmode";
2188     case PREF_CORRECTION_ALLOW:
2189         return "correction.allow";
2190     case PREF_AVATAR_CMD:
2191         return "avatar.cmd";
2192     case PREF_SLASH_GUARD:
2193         return "slashguard";
2194     case PREF_MAM:
2195         return "mam";
2196     case PREF_URL_OPEN_CMD:
2197         return "url.open.cmd";
2198     case PREF_URL_SAVE_CMD:
2199         return "url.save.cmd";
2200     case PREF_COMPOSE_EDITOR:
2201         return "compose.editor";
2202     case PREF_SILENCE_NON_ROSTER:
2203         return "silence.incoming.nonroster";
2204     default:
2205         return NULL;
2206     }
2207 }
2208 
2209 // the default setting for a boolean type preference
2210 // if it is not specified in .profrc
2211 static gboolean
_get_default_boolean(preference_t pref)2212 _get_default_boolean(preference_t pref)
2213 {
2214     switch (pref) {
2215     case PREF_ENC_WARN:
2216     case PREF_AUTOAWAY_CHECK:
2217     case PREF_LOG_ROTATE:
2218     case PREF_LOG_SHARED:
2219     case PREF_SPLASH:
2220     case PREF_OCCUPANTS:
2221     case PREF_MUC_PRIVILEGES:
2222     case PREF_PRESENCE:
2223     case PREF_WRAP:
2224     case PREF_INPBLOCK_DYNAMIC:
2225     case PREF_RESOURCE_TITLE:
2226     case PREF_RESOURCE_MESSAGE:
2227     case PREF_ROSTER:
2228     case PREF_ROSTER_OFFLINE:
2229     case PREF_ROSTER_EMPTY:
2230     case PREF_ROSTER_COUNT_ZERO:
2231     case PREF_ROSTER_PRIORITY:
2232     case PREF_ROSTER_RESOURCE_JOIN:
2233     case PREF_ROSTER_CONTACTS:
2234     case PREF_ROSTER_UNSUBSCRIBED:
2235     case PREF_ROSTER_ROOMS:
2236     case PREF_ROSTER_ROOMS_SERVER:
2237     case PREF_TLS_SHOW:
2238     case PREF_LASTACTIVITY:
2239     case PREF_TRAY_READ:
2240     case PREF_BOOKMARK_INVITE:
2241     case PREF_ROOM_LIST_CACHE:
2242     case PREF_STATUSBAR_SHOW_NUMBER:
2243     case PREF_STATUSBAR_SHOW_READ:
2244     case PREF_REVEAL_OS:
2245     case PREF_CORRECTION_ALLOW:
2246     case PREF_RECEIPTS_SEND:
2247     case PREF_CARBONS:
2248     case PREF_STATES:
2249     case PREF_OUTTYPE:
2250     case PREF_TITLEBAR_MUC_TITLE_NAME:
2251     case PREF_COLOR_NICK_OWN:
2252     case PREF_INTYPE:
2253     case PREF_INTYPE_CONSOLE:
2254     case PREF_NOTIFY_MENTION_WHOLE_WORD:
2255         return TRUE;
2256     default:
2257         return FALSE;
2258     }
2259 }
2260 
2261 // the default setting for a string type preference
2262 // if it is not specified in .profrc
2263 static char*
_get_default_string(preference_t pref)2264 _get_default_string(preference_t pref)
2265 {
2266     switch (pref) {
2267     case PREF_AUTOAWAY_MODE:
2268         return "off";
2269     case PREF_OTR_LOG:
2270         return "redact";
2271     case PREF_OTR_POLICY:
2272         return "manual";
2273     case PREF_STATUSES_CONSOLE:
2274     case PREF_STATUSES_CHAT:
2275     case PREF_STATUSES_MUC:
2276         return "all";
2277     case PREF_ROSTER_BY:
2278         return "presence";
2279     case PREF_ROSTER_COUNT:
2280         return "unread";
2281     case PREF_ROSTER_ORDER:
2282         return "presence";
2283     case PREF_ROSTER_UNREAD:
2284         return "after";
2285     case PREF_ROSTER_ROOMS_POS:
2286         return "last";
2287     case PREF_ROSTER_ROOMS_BY:
2288         return "none";
2289     case PREF_ROSTER_ROOMS_USE_AS_NAME:
2290         return "name";
2291     case PREF_ROSTER_ROOMS_ORDER:
2292         return "name";
2293     case PREF_ROSTER_ROOMS_UNREAD:
2294         return "after";
2295     case PREF_ROSTER_PRIVATE:
2296         return "room";
2297     case PREF_TIME_CONSOLE:
2298         return "%H:%M:%S";
2299     case PREF_TIME_CHAT:
2300         return "%H:%M:%S";
2301     case PREF_TIME_MUC:
2302         return "%H:%M:%S";
2303     case PREF_TIME_CONFIG:
2304         return "%H:%M:%S";
2305     case PREF_TIME_PRIVATE:
2306         return "%H:%M:%S";
2307     case PREF_TIME_XMLCONSOLE:
2308         return "%H:%M:%S";
2309     case PREF_TIME_STATUSBAR:
2310         return "%H:%M";
2311     case PREF_TIME_LASTACTIVITY:
2312         return "%d/%m/%y %H:%M:%S";
2313     case PREF_PGP_LOG:
2314         return "redact";
2315     case PREF_CONSOLE_MUC:
2316     case PREF_CONSOLE_PRIVATE:
2317     case PREF_CONSOLE_CHAT:
2318         return "all";
2319     case PREF_STATUSBAR_SELF:
2320         return "fulljid";
2321     case PREF_STATUSBAR_CHAT:
2322         return "user";
2323     case PREF_STATUSBAR_ROOM:
2324         return "room";
2325     case PREF_OMEMO_LOG:
2326         return "redact";
2327     case PREF_OMEMO_POLICY:
2328         return "automatic";
2329     case PREF_OMEMO_TRUST_MODE:
2330         return "manual";
2331     case PREF_COLOR_NICK:
2332         return "false";
2333     case PREF_AVATAR_CMD:
2334         return "xdg-open";
2335     case PREF_URL_OPEN_CMD:
2336         return "xdg-open %u";
2337     case PREF_COMPOSE_EDITOR:
2338         return "vim";
2339     case PREF_URL_SAVE_CMD:
2340         return NULL; // Default to built-in method.
2341     default:
2342         return NULL;
2343     }
2344 }
2345