comparison libpurple/prefs.c @ 15374:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children 32c366eeeb99
comparison
equal deleted inserted replaced
15373:f79e0f4df793 15374:5fe8042783c1
1 /*
2 * gaim
3 *
4 * Gaim is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
7 *
8 * This program 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 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program 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 this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <string.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <glib.h>
34 #include "internal.h"
35 #include "prefs.h"
36 #include "debug.h"
37 #include "util.h"
38
39 #ifdef _WIN32
40 #include "win32dep.h"
41 #endif
42
43 struct pref_cb {
44 GaimPrefCallback func;
45 gpointer data;
46 guint id;
47 void *handle;
48 };
49
50 /* TODO: This should use GaimValues? */
51 struct gaim_pref {
52 GaimPrefType type;
53 char *name;
54 union {
55 gpointer generic;
56 gboolean boolean;
57 int integer;
58 char *string;
59 GList *stringlist;
60 } value;
61 GSList *callbacks;
62 struct gaim_pref *parent;
63 struct gaim_pref *sibling;
64 struct gaim_pref *first_child;
65 };
66
67
68 static struct gaim_pref prefs = {
69 GAIM_PREF_NONE,
70 NULL,
71 { NULL },
72 NULL,
73 NULL,
74 NULL,
75 NULL
76 };
77
78 static GHashTable *prefs_hash = NULL;
79 static guint save_timer = 0;
80 static gboolean prefs_loaded = FALSE;
81
82
83 /*********************************************************************
84 * Private utility functions *
85 *********************************************************************/
86
87 static struct
88 gaim_pref *find_pref(const char *name)
89 {
90 if (!name || name[0] != '/')
91 return NULL;
92 else if (name[1] == '\0')
93 return &prefs;
94 else
95 return g_hash_table_lookup(prefs_hash, name);
96 }
97
98
99 /*********************************************************************
100 * Writing to disk *
101 *********************************************************************/
102
103 /*
104 * This function recursively creates the xmlnode tree from the prefs
105 * tree structure. Yay recursion!
106 */
107 static void
108 pref_to_xmlnode(xmlnode *parent, struct gaim_pref *pref)
109 {
110 xmlnode *node, *childnode;
111 struct gaim_pref *child;
112 char buf[20];
113 GList *cur;
114
115 /* Create a new node */
116 node = xmlnode_new_child(parent, "pref");
117 xmlnode_set_attrib(node, "name", pref->name);
118
119 /* Set the type of this node (if type == GAIM_PREF_NONE then do nothing) */
120 if (pref->type == GAIM_PREF_INT) {
121 xmlnode_set_attrib(node, "type", "int");
122 snprintf(buf, sizeof(buf), "%d", pref->value.integer);
123 xmlnode_set_attrib(node, "value", buf);
124 }
125 else if (pref->type == GAIM_PREF_STRING) {
126 xmlnode_set_attrib(node, "type", "string");
127 xmlnode_set_attrib(node, "value", pref->value.string ? pref->value.string : "");
128 }
129 else if (pref->type == GAIM_PREF_STRING_LIST) {
130 xmlnode_set_attrib(node, "type", "stringlist");
131 for (cur = pref->value.stringlist; cur != NULL; cur = cur->next)
132 {
133 childnode = xmlnode_new_child(node, "item");
134 xmlnode_set_attrib(childnode, "value", cur->data ? cur->data : "");
135 }
136 }
137 else if (pref->type == GAIM_PREF_PATH) {
138 char *encoded = g_filename_to_utf8(pref->value.string ? pref->value.string : "", -1, NULL, NULL, NULL);
139 xmlnode_set_attrib(node, "type", "path");
140 xmlnode_set_attrib(node, "value", encoded);
141 g_free(encoded);
142 }
143 else if (pref->type == GAIM_PREF_PATH_LIST) {
144 xmlnode_set_attrib(node, "type", "pathlist");
145 for (cur = pref->value.stringlist; cur != NULL; cur = cur->next)
146 {
147 char *encoded = g_filename_to_utf8(cur->data ? cur->data : "", -1, NULL, NULL, NULL);
148 childnode = xmlnode_new_child(node, "item");
149 xmlnode_set_attrib(childnode, "value", encoded);
150 g_free(encoded);
151 }
152 }
153 else if (pref->type == GAIM_PREF_BOOLEAN) {
154 xmlnode_set_attrib(node, "type", "bool");
155 snprintf(buf, sizeof(buf), "%d", pref->value.boolean);
156 xmlnode_set_attrib(node, "value", buf);
157 }
158
159 /* All My Children */
160 for (child = pref->first_child; child != NULL; child = child->sibling)
161 pref_to_xmlnode(node, child);
162 }
163
164 static xmlnode *
165 prefs_to_xmlnode(void)
166 {
167 xmlnode *node;
168 struct gaim_pref *pref, *child;
169
170 pref = &prefs;
171
172 /* Create the root preference node */
173 node = xmlnode_new("pref");
174 xmlnode_set_attrib(node, "version", "1");
175 xmlnode_set_attrib(node, "name", "/");
176
177 /* All My Children */
178 for (child = pref->first_child; child != NULL; child = child->sibling)
179 pref_to_xmlnode(node, child);
180
181 return node;
182 }
183
184 static void
185 sync_prefs(void)
186 {
187 xmlnode *node;
188 char *data;
189
190 if (!prefs_loaded)
191 {
192 /*
193 * TODO: Call schedule_prefs_save()? Ideally we wouldn't need to.
194 * (prefs.xml should be loaded when gaim_prefs_init is called)
195 */
196 gaim_debug_error("prefs", "Attempted to save prefs before "
197 "they were read!\n");
198 return;
199 }
200
201 node = prefs_to_xmlnode();
202 data = xmlnode_to_formatted_str(node, NULL);
203 gaim_util_write_data_to_file("prefs.xml", data, -1);
204 g_free(data);
205 xmlnode_free(node);
206 }
207
208 static gboolean
209 save_cb(gpointer data)
210 {
211 sync_prefs();
212 save_timer = 0;
213 return FALSE;
214 }
215
216 static void
217 schedule_prefs_save(void)
218 {
219 if (save_timer == 0)
220 save_timer = gaim_timeout_add(5000, save_cb, NULL);
221 }
222
223
224 /*********************************************************************
225 * Reading from disk *
226 *********************************************************************/
227
228 static GList *prefs_stack = NULL;
229
230 static void
231 prefs_start_element_handler (GMarkupParseContext *context,
232 const gchar *element_name,
233 const gchar **attribute_names,
234 const gchar **attribute_values,
235 gpointer user_data,
236 GError **error)
237 {
238 GaimPrefType pref_type = GAIM_PREF_NONE;
239 int i;
240 const char *pref_name = NULL, *pref_value = NULL;
241 GString *pref_name_full;
242 GList *tmp;
243
244 if(strcmp(element_name, "pref") && strcmp(element_name, "item"))
245 return;
246
247 for(i = 0; attribute_names[i]; i++) {
248 if(!strcmp(attribute_names[i], "name")) {
249 pref_name = attribute_values[i];
250 } else if(!strcmp(attribute_names[i], "type")) {
251 if(!strcmp(attribute_values[i], "bool"))
252 pref_type = GAIM_PREF_BOOLEAN;
253 else if(!strcmp(attribute_values[i], "int"))
254 pref_type = GAIM_PREF_INT;
255 else if(!strcmp(attribute_values[i], "string"))
256 pref_type = GAIM_PREF_STRING;
257 else if(!strcmp(attribute_values[i], "stringlist"))
258 pref_type = GAIM_PREF_STRING_LIST;
259 else if(!strcmp(attribute_values[i], "path"))
260 pref_type = GAIM_PREF_PATH;
261 else if(!strcmp(attribute_values[i], "pathlist"))
262 pref_type = GAIM_PREF_PATH_LIST;
263 else
264 return;
265 } else if(!strcmp(attribute_names[i], "value")) {
266 pref_value = attribute_values[i];
267 }
268 }
269
270 if(!strcmp(element_name, "item")) {
271 struct gaim_pref *pref;
272
273 pref_name_full = g_string_new("");
274
275 for(tmp = prefs_stack; tmp; tmp = tmp->next) {
276 pref_name_full = g_string_prepend(pref_name_full, tmp->data);
277 pref_name_full = g_string_prepend_c(pref_name_full, '/');
278 }
279
280 pref = find_pref(pref_name_full->str);
281
282 if(pref) {
283 if(pref->type == GAIM_PREF_STRING_LIST) {
284 pref->value.stringlist = g_list_append(pref->value.stringlist,
285 g_strdup(pref_value));
286 } else if(pref->type == GAIM_PREF_PATH_LIST) {
287 pref->value.stringlist = g_list_append(pref->value.stringlist,
288 g_filename_from_utf8(pref_value, -1, NULL, NULL, NULL));
289 }
290 }
291 } else {
292 char *decoded;
293
294 if(!pref_name || !strcmp(pref_name, "/"))
295 return;
296
297 pref_name_full = g_string_new(pref_name);
298
299 for(tmp = prefs_stack; tmp; tmp = tmp->next) {
300 pref_name_full = g_string_prepend_c(pref_name_full, '/');
301 pref_name_full = g_string_prepend(pref_name_full, tmp->data);
302 }
303
304 pref_name_full = g_string_prepend_c(pref_name_full, '/');
305
306 switch(pref_type) {
307 case GAIM_PREF_NONE:
308 gaim_prefs_add_none(pref_name_full->str);
309 break;
310 case GAIM_PREF_BOOLEAN:
311 gaim_prefs_set_bool(pref_name_full->str, atoi(pref_value));
312 break;
313 case GAIM_PREF_INT:
314 gaim_prefs_set_int(pref_name_full->str, atoi(pref_value));
315 break;
316 case GAIM_PREF_STRING:
317 gaim_prefs_set_string(pref_name_full->str, pref_value);
318 break;
319 case GAIM_PREF_STRING_LIST:
320 gaim_prefs_set_string_list(pref_name_full->str, NULL);
321 break;
322 case GAIM_PREF_PATH:
323 decoded = g_filename_from_utf8(pref_value, -1, NULL, NULL, NULL);
324 gaim_prefs_set_path(pref_name_full->str, decoded);
325 g_free(decoded);
326 break;
327 case GAIM_PREF_PATH_LIST:
328 gaim_prefs_set_path_list(pref_name_full->str, NULL);
329 break;
330 }
331 prefs_stack = g_list_prepend(prefs_stack, g_strdup(pref_name));
332 g_string_free(pref_name_full, TRUE);
333 }
334 }
335
336 static void
337 prefs_end_element_handler(GMarkupParseContext *context,
338 const gchar *element_name,
339 gpointer user_data, GError **error)
340 {
341 if(prefs_stack && !strcmp(element_name, "pref")) {
342 g_free(prefs_stack->data);
343 prefs_stack = g_list_delete_link(prefs_stack, prefs_stack);
344 }
345 }
346
347 static GMarkupParser prefs_parser = {
348 prefs_start_element_handler,
349 prefs_end_element_handler,
350 NULL,
351 NULL,
352 NULL
353 };
354
355 gboolean
356 gaim_prefs_load()
357 {
358 gchar *filename = g_build_filename(gaim_user_dir(), "prefs.xml", NULL);
359 gchar *contents = NULL;
360 gsize length;
361 GMarkupParseContext *context;
362 GError *error = NULL;
363
364 if (!filename) {
365 prefs_loaded = TRUE;
366 return FALSE;
367 }
368
369 gaim_debug_info("prefs", "Reading %s\n", filename);
370
371 if(!g_file_get_contents(filename, &contents, &length, &error)) {
372 #ifndef _WIN32
373 g_free(filename);
374 g_error_free(error);
375
376 error = NULL;
377
378 filename = g_build_filename(SYSCONFDIR, "gaim", "prefs.xml", NULL);
379
380 gaim_debug_info("prefs", "Reading %s\n", filename);
381
382 if (!g_file_get_contents(filename, &contents, &length, &error)) {
383 gaim_debug_error("prefs", "Error reading prefs: %s\n",
384 error->message);
385 g_error_free(error);
386 g_free(filename);
387 prefs_loaded = TRUE;
388
389 return FALSE;
390 }
391 #else /* _WIN32 */
392 gaim_debug_error("prefs", "Error reading prefs: %s\n",
393 error->message);
394 g_error_free(error);
395 g_free(filename);
396 prefs_loaded = TRUE;
397
398 return FALSE;
399 #endif /* _WIN32 */
400 }
401
402 context = g_markup_parse_context_new(&prefs_parser, 0, NULL, NULL);
403
404 if(!g_markup_parse_context_parse(context, contents, length, NULL)) {
405 g_markup_parse_context_free(context);
406 g_free(contents);
407 g_free(filename);
408 prefs_loaded = TRUE;
409
410 return FALSE;
411 }
412
413 if(!g_markup_parse_context_end_parse(context, NULL)) {
414 gaim_debug_error("prefs", "Error parsing %s\n", filename);
415 g_markup_parse_context_free(context);
416 g_free(contents);
417 g_free(filename);
418 prefs_loaded = TRUE;
419
420 return FALSE;
421 }
422
423 gaim_debug_info("prefs", "Finished reading %s\n", filename);
424 g_markup_parse_context_free(context);
425 g_free(contents);
426 g_free(filename);
427 prefs_loaded = TRUE;
428
429 /* I introduced a bug in 2.0.0beta2. This fixes the broken
430 * scores on upgrade. This can be removed sometime shortly
431 * after 2.0.0 final is released. -- rlaager */
432 if (gaim_prefs_get_int("/core/status/scores/offline") == -500 &&
433 gaim_prefs_get_int("/core/status/scores/available") == 100 &&
434 gaim_prefs_get_int("/core/status/scores/invisible") == -50 &&
435 gaim_prefs_get_int("/core/status/scores/away") == -100 &&
436 gaim_prefs_get_int("/core/status/scores/extended_away") == -200 &&
437 gaim_prefs_get_int("/core/status/scores/idle") == -400)
438 {
439 gaim_prefs_set_int("/core/status/scores/idle", -10);
440 }
441
442 return TRUE;
443 }
444
445
446
447 static void
448 prefs_save_cb(const char *name, GaimPrefType type, gconstpointer val,
449 gpointer user_data)
450 {
451
452 if(!prefs_loaded)
453 return;
454
455 gaim_debug_misc("prefs", "%s changed, scheduling save.\n", name);
456
457 schedule_prefs_save();
458 }
459
460 static char *
461 get_path_dirname(const char *name)
462 {
463 char *c, *str;
464
465 str = g_strdup(name);
466
467 if ((c = strrchr(str, '/')) != NULL) {
468 *c = '\0';
469
470 if (*str == '\0') {
471 g_free(str);
472
473 str = g_strdup("/");
474 }
475 }
476 else {
477 g_free(str);
478
479 str = g_strdup(".");
480 }
481
482 return str;
483 }
484
485 static char *
486 get_path_basename(const char *name)
487 {
488 const char *c;
489
490 if ((c = strrchr(name, '/')) != NULL)
491 return g_strdup(c + 1);
492
493 return g_strdup(name);
494 }
495
496 static char *
497 pref_full_name(struct gaim_pref *pref)
498 {
499 GString *name;
500 struct gaim_pref *parent;
501
502 if(!pref)
503 return NULL;
504
505 if(pref == &prefs)
506 return g_strdup("/");
507
508 name = g_string_new(pref->name);
509 parent = pref->parent;
510
511 for(parent = pref->parent; parent && parent->name; parent = parent->parent) {
512 name = g_string_prepend_c(name, '/');
513 name = g_string_prepend(name, parent->name);
514 }
515 name = g_string_prepend_c(name, '/');
516 return g_string_free(name, FALSE);
517 }
518
519 static struct gaim_pref *
520 find_pref_parent(const char *name)
521 {
522 char *parent_name = get_path_dirname(name);
523 struct gaim_pref *ret = &prefs;
524
525 if(strcmp(parent_name, "/")) {
526 ret = find_pref(parent_name);
527 }
528
529 g_free(parent_name);
530 return ret;
531 }
532
533 static void
534 free_pref_value(struct gaim_pref *pref)
535 {
536 switch(pref->type) {
537 case GAIM_PREF_BOOLEAN:
538 pref->value.boolean = FALSE;
539 break;
540 case GAIM_PREF_INT:
541 pref->value.integer = 0;
542 break;
543 case GAIM_PREF_STRING:
544 case GAIM_PREF_PATH:
545 g_free(pref->value.string);
546 pref->value.string = NULL;
547 break;
548 case GAIM_PREF_STRING_LIST:
549 case GAIM_PREF_PATH_LIST:
550 {
551 g_list_foreach(pref->value.stringlist, (GFunc)g_free, NULL);
552 g_list_free(pref->value.stringlist);
553 } break;
554 case GAIM_PREF_NONE:
555 break;
556 }
557 }
558
559 static struct gaim_pref *
560 add_pref(GaimPrefType type, const char *name)
561 {
562 struct gaim_pref *parent;
563 struct gaim_pref *me;
564 struct gaim_pref *sibling;
565 char *my_name;
566
567 parent = find_pref_parent(name);
568
569 if(!parent)
570 return NULL;
571
572 my_name = get_path_basename(name);
573
574 for(sibling = parent->first_child; sibling; sibling = sibling->sibling) {
575 if(!strcmp(sibling->name, my_name)) {
576 g_free(my_name);
577 return NULL;
578 }
579 }
580
581 me = g_new0(struct gaim_pref, 1);
582 me->type = type;
583 me->name = my_name;
584
585 me->parent = parent;
586 if(parent->first_child) {
587 /* blatant abuse of a for loop */
588 for(sibling = parent->first_child; sibling->sibling;
589 sibling = sibling->sibling);
590 sibling->sibling = me;
591 } else {
592 parent->first_child = me;
593 }
594
595 g_hash_table_insert(prefs_hash, g_strdup(name), (gpointer)me);
596
597 return me;
598 }
599
600 void
601 gaim_prefs_add_none(const char *name)
602 {
603 add_pref(GAIM_PREF_NONE, name);
604 }
605
606 void
607 gaim_prefs_add_bool(const char *name, gboolean value)
608 {
609 struct gaim_pref *pref = add_pref(GAIM_PREF_BOOLEAN, name);
610
611 if(!pref)
612 return;
613
614 pref->value.boolean = value;
615 }
616
617 void
618 gaim_prefs_add_int(const char *name, int value)
619 {
620 struct gaim_pref *pref = add_pref(GAIM_PREF_INT, name);
621
622 if(!pref)
623 return;
624
625 pref->value.integer = value;
626 }
627
628 void
629 gaim_prefs_add_string(const char *name, const char *value)
630 {
631 struct gaim_pref *pref;
632
633 if(value != NULL && !g_utf8_validate(value, -1, NULL)) {
634 gaim_debug_error("prefs", "gaim_prefs_add_string: Cannot store invalid UTF8 for string pref %s\n", name);
635 return;
636 }
637
638 pref = add_pref(GAIM_PREF_STRING, name);
639
640 if(!pref)
641 return;
642
643 pref->value.string = g_strdup(value);
644 }
645
646 void
647 gaim_prefs_add_string_list(const char *name, GList *value)
648 {
649 struct gaim_pref *pref = add_pref(GAIM_PREF_STRING_LIST, name);
650 GList *tmp;
651
652 if(!pref)
653 return;
654
655 for(tmp = value; tmp; tmp = tmp->next) {
656 if(tmp->data != NULL && !g_utf8_validate(tmp->data, -1, NULL)) {
657 gaim_debug_error("prefs", "gaim_prefs_add_string_list: Skipping invalid UTF8 for string list pref %s\n", name);
658 continue;
659 }
660 pref->value.stringlist = g_list_append(pref->value.stringlist,
661 g_strdup(tmp->data));
662 }
663 }
664
665 void
666 gaim_prefs_add_path(const char *name, const char *value)
667 {
668 struct gaim_pref *pref = add_pref(GAIM_PREF_PATH, name);
669
670 if(!pref)
671 return;
672
673 pref->value.string = g_strdup(value);
674 }
675
676 void
677 gaim_prefs_add_path_list(const char *name, GList *value)
678 {
679 struct gaim_pref *pref = add_pref(GAIM_PREF_PATH_LIST, name);
680 GList *tmp;
681
682 if(!pref)
683 return;
684
685 for(tmp = value; tmp; tmp = tmp->next)
686 pref->value.stringlist = g_list_append(pref->value.stringlist,
687 g_strdup(tmp->data));
688 }
689
690
691 static void
692 remove_pref(struct gaim_pref *pref)
693 {
694 char *name;
695 GSList *l;
696
697 if(!pref || pref == &prefs)
698 return;
699
700 while(pref->first_child)
701 remove_pref(pref->first_child);
702
703 if(pref->parent->first_child == pref) {
704 pref->parent->first_child = pref->sibling;
705 } else {
706 struct gaim_pref *sib = pref->parent->first_child;
707 while(sib && sib->sibling != pref)
708 sib = sib->sibling;
709 if(sib)
710 sib->sibling = pref->sibling;
711 }
712
713 name = pref_full_name(pref);
714
715 gaim_debug_info("prefs", "removing pref %s\n", name);
716
717 g_hash_table_remove(prefs_hash, name);
718 g_free(name);
719
720 free_pref_value(pref);
721
722 while((l = pref->callbacks) != NULL) {
723 pref->callbacks = pref->callbacks->next;
724 g_free(l->data);
725 g_slist_free_1(l);
726 }
727 g_free(pref->name);
728 g_free(pref);
729 }
730
731 void
732 gaim_prefs_remove(const char *name)
733 {
734 struct gaim_pref *pref = find_pref(name);
735
736 if(!pref)
737 return;
738
739 remove_pref(pref);
740 }
741
742 void
743 gaim_prefs_destroy()
744 {
745 gaim_prefs_remove("/");
746 }
747
748 static void
749 do_callbacks(const char* name, struct gaim_pref *pref)
750 {
751 GSList *cbs;
752 struct gaim_pref *cb_pref;
753 for(cb_pref = pref; cb_pref; cb_pref = cb_pref->parent) {
754 for(cbs = cb_pref->callbacks; cbs; cbs = cbs->next) {
755 struct pref_cb *cb = cbs->data;
756 cb->func(name, pref->type, pref->value.generic, cb->data);
757 }
758 }
759 }
760
761 void
762 gaim_prefs_trigger_callback(const char *name)
763 {
764 struct gaim_pref *pref = find_pref(name);
765
766 if(!pref) {
767 gaim_debug_error("prefs",
768 "gaim_prefs_trigger_callback: Unknown pref %s\n", name);
769 return;
770 }
771
772 do_callbacks(name, pref);
773 }
774
775 void
776 gaim_prefs_set_generic(const char *name, gpointer value)
777 {
778 struct gaim_pref *pref = find_pref(name);
779
780 if(!pref) {
781 gaim_debug_error("prefs",
782 "gaim_prefs_set_generic: Unknown pref %s\n", name);
783 return;
784 }
785
786 pref->value.generic = value;
787 do_callbacks(name, pref);
788 }
789
790 void
791 gaim_prefs_set_bool(const char *name, gboolean value)
792 {
793 struct gaim_pref *pref = find_pref(name);
794
795 if(pref) {
796 if(pref->type != GAIM_PREF_BOOLEAN) {
797 gaim_debug_error("prefs",
798 "gaim_prefs_set_bool: %s not a boolean pref\n", name);
799 return;
800 }
801
802 if(pref->value.boolean != value) {
803 pref->value.boolean = value;
804 do_callbacks(name, pref);
805 }
806 } else {
807 gaim_prefs_add_bool(name, value);
808 }
809 }
810
811 void
812 gaim_prefs_set_int(const char *name, int value)
813 {
814 struct gaim_pref *pref = find_pref(name);
815
816 if(pref) {
817 if(pref->type != GAIM_PREF_INT) {
818 gaim_debug_error("prefs",
819 "gaim_prefs_set_int: %s not an integer pref\n", name);
820 return;
821 }
822
823 if(pref->value.integer != value) {
824 pref->value.integer = value;
825 do_callbacks(name, pref);
826 }
827 } else {
828 gaim_prefs_add_int(name, value);
829 }
830 }
831
832 void
833 gaim_prefs_set_string(const char *name, const char *value)
834 {
835 struct gaim_pref *pref = find_pref(name);
836
837 if(value != NULL && !g_utf8_validate(value, -1, NULL)) {
838 gaim_debug_error("prefs", "gaim_prefs_set_string: Cannot store invalid UTF8 for string pref %s\n", name);
839 return;
840 }
841
842 if(pref) {
843 if(pref->type != GAIM_PREF_STRING && pref->type != GAIM_PREF_PATH) {
844 gaim_debug_error("prefs",
845 "gaim_prefs_set_string: %s not a string pref\n", name);
846 return;
847 }
848
849 if((value && !pref->value.string) ||
850 (!value && pref->value.string) ||
851 (value && pref->value.string &&
852 strcmp(pref->value.string, value))) {
853 g_free(pref->value.string);
854 pref->value.string = g_strdup(value);
855 do_callbacks(name, pref);
856 }
857 } else {
858 gaim_prefs_add_string(name, value);
859 }
860 }
861
862 void
863 gaim_prefs_set_string_list(const char *name, GList *value)
864 {
865 struct gaim_pref *pref = find_pref(name);
866 if(pref) {
867 GList *tmp;
868
869 if(pref->type != GAIM_PREF_STRING_LIST) {
870 gaim_debug_error("prefs",
871 "gaim_prefs_set_string_list: %s not a string list pref\n",
872 name);
873 return;
874 }
875
876 g_list_foreach(pref->value.stringlist, (GFunc)g_free, NULL);
877 g_list_free(pref->value.stringlist);
878 pref->value.stringlist = NULL;
879
880 for(tmp = value; tmp; tmp = tmp->next) {
881 if(tmp->data != NULL && !g_utf8_validate(tmp->data, -1, NULL)) {
882 gaim_debug_error("prefs", "gaim_prefs_set_string_list: Skipping invalid UTF8 for string list pref %s\n", name);
883 continue;
884 }
885 pref->value.stringlist = g_list_prepend(pref->value.stringlist,
886 g_strdup(tmp->data));
887 }
888 pref->value.stringlist = g_list_reverse(pref->value.stringlist);
889
890 do_callbacks(name, pref);
891
892 } else {
893 gaim_prefs_add_string_list(name, value);
894 }
895 }
896
897 void
898 gaim_prefs_set_path(const char *name, const char *value)
899 {
900 struct gaim_pref *pref = find_pref(name);
901
902 if(pref) {
903 if(pref->type != GAIM_PREF_PATH) {
904 gaim_debug_error("prefs",
905 "gaim_prefs_set_path: %s not a string pref\n", name);
906 return;
907 }
908
909 if((value && !pref->value.string) ||
910 (!value && pref->value.string) ||
911 (value && pref->value.string &&
912 strcmp(pref->value.string, value))) {
913 g_free(pref->value.string);
914 pref->value.string = g_strdup(value);
915 do_callbacks(name, pref);
916 }
917 } else {
918 gaim_prefs_add_path(name, value);
919 }
920 }
921
922 void
923 gaim_prefs_set_path_list(const char *name, GList *value)
924 {
925 struct gaim_pref *pref = find_pref(name);
926 if(pref) {
927 GList *tmp;
928
929 if(pref->type != GAIM_PREF_PATH_LIST) {
930 gaim_debug_error("prefs",
931 "gaim_prefs_set_path_list: %s not a string list pref\n",
932 name);
933 return;
934 }
935
936 g_list_foreach(pref->value.stringlist, (GFunc)g_free, NULL);
937 g_list_free(pref->value.stringlist);
938 pref->value.stringlist = NULL;
939
940 for(tmp = value; tmp; tmp = tmp->next)
941 pref->value.stringlist = g_list_prepend(pref->value.stringlist,
942 g_strdup(tmp->data));
943 pref->value.stringlist = g_list_reverse(pref->value.stringlist);
944
945 do_callbacks(name, pref);
946
947 } else {
948 gaim_prefs_add_path_list(name, value);
949 }
950 }
951
952
953 gboolean
954 gaim_prefs_exists(const char *name)
955 {
956 struct gaim_pref *pref = find_pref(name);
957
958 if (pref != NULL)
959 return TRUE;
960
961 return FALSE;
962 }
963
964 GaimPrefType
965 gaim_prefs_get_type(const char *name)
966 {
967 struct gaim_pref *pref = find_pref(name);
968
969 if (pref == NULL)
970 return GAIM_PREF_NONE;
971
972 return (pref->type);
973 }
974
975 gboolean
976 gaim_prefs_get_bool(const char *name)
977 {
978 struct gaim_pref *pref = find_pref(name);
979
980 if(!pref) {
981 gaim_debug_error("prefs",
982 "gaim_prefs_get_bool: Unknown pref %s\n", name);
983 return FALSE;
984 } else if(pref->type != GAIM_PREF_BOOLEAN) {
985 gaim_debug_error("prefs",
986 "gaim_prefs_get_bool: %s not a boolean pref\n", name);
987 return FALSE;
988 }
989
990 return pref->value.boolean;
991 }
992
993 int
994 gaim_prefs_get_int(const char *name)
995 {
996 struct gaim_pref *pref = find_pref(name);
997
998 if(!pref) {
999 gaim_debug_error("prefs",
1000 "gaim_prefs_get_int: Unknown pref %s\n", name);
1001 return 0;
1002 } else if(pref->type != GAIM_PREF_INT) {
1003 gaim_debug_error("prefs",
1004 "gaim_prefs_get_int: %s not an integer pref\n", name);
1005 return 0;
1006 }
1007
1008 return pref->value.integer;
1009 }
1010
1011 const char *
1012 gaim_prefs_get_string(const char *name)
1013 {
1014 struct gaim_pref *pref = find_pref(name);
1015
1016 if(!pref) {
1017 gaim_debug_error("prefs",
1018 "gaim_prefs_get_string: Unknown pref %s\n", name);
1019 return NULL;
1020 } else if(pref->type != GAIM_PREF_STRING) {
1021 gaim_debug_error("prefs",
1022 "gaim_prefs_get_string: %s not a string pref\n", name);
1023 return NULL;
1024 }
1025
1026 return pref->value.string;
1027 }
1028
1029 GList *
1030 gaim_prefs_get_string_list(const char *name)
1031 {
1032 struct gaim_pref *pref = find_pref(name);
1033 GList *ret = NULL, *tmp;
1034
1035 if(!pref) {
1036 gaim_debug_error("prefs",
1037 "gaim_prefs_get_string_list: Unknown pref %s\n", name);
1038 return NULL;
1039 } else if(pref->type != GAIM_PREF_STRING_LIST) {
1040 gaim_debug_error("prefs",
1041 "gaim_prefs_get_string_list: %s not a string list pref\n", name);
1042 return NULL;
1043 }
1044
1045 for(tmp = pref->value.stringlist; tmp; tmp = tmp->next)
1046 ret = g_list_prepend(ret, g_strdup(tmp->data));
1047 ret = g_list_reverse(ret);
1048
1049 return ret;
1050 }
1051
1052 const char *
1053 gaim_prefs_get_path(const char *name)
1054 {
1055 struct gaim_pref *pref = find_pref(name);
1056
1057 if(!pref) {
1058 gaim_debug_error("prefs",
1059 "gaim_prefs_get_path: Unknown pref %s\n", name);
1060 return NULL;
1061 } else if(pref->type != GAIM_PREF_PATH) {
1062 gaim_debug_error("prefs",
1063 "gaim_prefs_get_path: %s not a path pref\n", name);
1064 return NULL;
1065 }
1066
1067 return pref->value.string;
1068 }
1069
1070 GList *
1071 gaim_prefs_get_path_list(const char *name)
1072 {
1073 struct gaim_pref *pref = find_pref(name);
1074 GList *ret = NULL, *tmp;
1075
1076 if(!pref) {
1077 gaim_debug_error("prefs",
1078 "gaim_prefs_get_path_list: Unknown pref %s\n", name);
1079 return NULL;
1080 } else if(pref->type != GAIM_PREF_PATH_LIST) {
1081 gaim_debug_error("prefs",
1082 "gaim_prefs_get_path_list: %s not a path list pref\n", name);
1083 return NULL;
1084 }
1085
1086 for(tmp = pref->value.stringlist; tmp; tmp = tmp->next)
1087 ret = g_list_prepend(ret, g_strdup(tmp->data));
1088 ret = g_list_reverse(ret);
1089
1090 return ret;
1091 }
1092
1093 void
1094 gaim_prefs_rename(const char *oldname, const char *newname)
1095 {
1096 struct gaim_pref *oldpref, *newpref;
1097
1098 oldpref = find_pref(oldname);
1099
1100 /* it's already been renamed, call off the dogs */
1101 if(!oldpref)
1102 return;
1103
1104 if (oldpref->first_child != NULL) /* can't rename parents */
1105 {
1106 gaim_debug_error("prefs", "Unable to rename %s to %s: can't rename parents\n", oldname, newname);
1107 return;
1108 }
1109
1110
1111 newpref = find_pref(newname);
1112
1113 if (newpref == NULL)
1114 {
1115 gaim_debug_error("prefs", "Unable to rename %s to %s: new pref not created\n", oldname, newname);
1116 return;
1117 }
1118
1119 if (oldpref->type != newpref->type)
1120 {
1121 gaim_debug_error("prefs", "Unable to rename %s to %s: differing types\n", oldname, newname);
1122 return;
1123 }
1124
1125 gaim_debug_info("prefs", "Renaming %s to %s\n", oldname, newname);
1126
1127 switch(oldpref->type) {
1128 case GAIM_PREF_NONE:
1129 break;
1130 case GAIM_PREF_BOOLEAN:
1131 gaim_prefs_set_bool(newname, oldpref->value.boolean);
1132 break;
1133 case GAIM_PREF_INT:
1134 gaim_prefs_set_int(newname, oldpref->value.integer);
1135 break;
1136 case GAIM_PREF_STRING:
1137 gaim_prefs_set_string(newname, oldpref->value.string);
1138 break;
1139 case GAIM_PREF_STRING_LIST:
1140 gaim_prefs_set_string_list(newname, oldpref->value.stringlist);
1141 break;
1142 case GAIM_PREF_PATH:
1143 gaim_prefs_set_path(newname, oldpref->value.string);
1144 break;
1145 case GAIM_PREF_PATH_LIST:
1146 gaim_prefs_set_path_list(newname, oldpref->value.stringlist);
1147 break;
1148 }
1149
1150 remove_pref(oldpref);
1151 }
1152
1153 void
1154 gaim_prefs_rename_boolean_toggle(const char *oldname, const char *newname)
1155 {
1156 struct gaim_pref *oldpref, *newpref;
1157
1158 oldpref = find_pref(oldname);
1159
1160 /* it's already been renamed, call off the cats */
1161 if(!oldpref)
1162 return;
1163
1164 if (oldpref->type != GAIM_PREF_BOOLEAN)
1165 {
1166 gaim_debug_error("prefs", "Unable to rename %s to %s: old pref not a boolean\n", oldname, newname);
1167 return;
1168 }
1169
1170 if (oldpref->first_child != NULL) /* can't rename parents */
1171 {
1172 gaim_debug_error("prefs", "Unable to rename %s to %s: can't rename parents\n", oldname, newname);
1173 return;
1174 }
1175
1176
1177 newpref = find_pref(newname);
1178
1179 if (newpref == NULL)
1180 {
1181 gaim_debug_error("prefs", "Unable to rename %s to %s: new pref not created\n", oldname, newname);
1182 return;
1183 }
1184
1185 if (oldpref->type != newpref->type)
1186 {
1187 gaim_debug_error("prefs", "Unable to rename %s to %s: differing types\n", oldname, newname);
1188 return;
1189 }
1190
1191 gaim_debug_info("prefs", "Renaming and toggling %s to %s\n", oldname, newname);
1192 gaim_prefs_set_bool(newname, !(oldpref->value.boolean));
1193
1194 remove_pref(oldpref);
1195 }
1196
1197 guint
1198 gaim_prefs_connect_callback(void *handle, const char *name, GaimPrefCallback func, gpointer data)
1199 {
1200 struct gaim_pref *pref;
1201 struct pref_cb *cb;
1202 static guint cb_id = 0;
1203
1204 g_return_val_if_fail(name != NULL, 0);
1205 g_return_val_if_fail(func != NULL, 0);
1206
1207 pref = find_pref(name);
1208 if (pref == NULL) {
1209 gaim_debug_error("prefs", "gaim_prefs_connect_callback: Unknown pref %s\n", name);
1210 return 0;
1211 }
1212
1213 cb = g_new0(struct pref_cb, 1);
1214
1215 cb->func = func;
1216 cb->data = data;
1217 cb->id = ++cb_id;
1218 cb->handle = handle;
1219
1220 pref->callbacks = g_slist_append(pref->callbacks, cb);
1221
1222 return cb->id;
1223 }
1224
1225 static gboolean
1226 disco_callback_helper(struct gaim_pref *pref, guint callback_id)
1227 {
1228 GSList *cbs;
1229 struct gaim_pref *child;
1230
1231 if(!pref)
1232 return FALSE;
1233
1234 for(cbs = pref->callbacks; cbs; cbs = cbs->next) {
1235 struct pref_cb *cb = cbs->data;
1236 if(cb->id == callback_id) {
1237 pref->callbacks = g_slist_delete_link(pref->callbacks, cbs);
1238 g_free(cb);
1239 return TRUE;
1240 }
1241 }
1242
1243 for(child = pref->first_child; child; child = child->sibling) {
1244 if(disco_callback_helper(child, callback_id))
1245 return TRUE;
1246 }
1247
1248 return FALSE;
1249 }
1250
1251 void
1252 gaim_prefs_disconnect_callback(guint callback_id)
1253 {
1254 disco_callback_helper(&prefs, callback_id);
1255 }
1256
1257 static void
1258 disco_callback_helper_handle(struct gaim_pref *pref, void *handle)
1259 {
1260 GSList *cbs;
1261 struct gaim_pref *child;
1262
1263 if(!pref)
1264 return;
1265
1266 cbs = pref->callbacks;
1267 while (cbs != NULL) {
1268 struct pref_cb *cb = cbs->data;
1269 if(cb->handle == handle) {
1270 pref->callbacks = g_slist_delete_link(pref->callbacks, cbs);
1271 g_free(cb);
1272 cbs = pref->callbacks;
1273 } else
1274 cbs = cbs->next;
1275 }
1276
1277 for(child = pref->first_child; child; child = child->sibling)
1278 disco_callback_helper_handle(child, handle);
1279 }
1280
1281 void
1282 gaim_prefs_disconnect_by_handle(void *handle)
1283 {
1284 g_return_if_fail(handle != NULL);
1285
1286 disco_callback_helper_handle(&prefs, handle);
1287 }
1288
1289 void
1290 gaim_prefs_update_old()
1291 {
1292 /* Remove some no-longer-used prefs */
1293 gaim_prefs_remove("/core/away/auto_response/enabled");
1294 gaim_prefs_remove("/core/away/auto_response/idle_only");
1295 gaim_prefs_remove("/core/away/auto_response/in_active_conv");
1296 gaim_prefs_remove("/core/away/auto_response/sec_before_resend");
1297 gaim_prefs_remove("/core/away/auto_response");
1298 gaim_prefs_remove("/core/away/default_message");
1299 gaim_prefs_remove("/core/buddies/use_server_alias");
1300 gaim_prefs_remove("/core/conversations/away_back_on_send");
1301 gaim_prefs_remove("/core/conversations/send_urls_as_links");
1302 gaim_prefs_remove("/core/conversations/im/show_login");
1303 gaim_prefs_remove("/core/conversations/chat/show_join");
1304 gaim_prefs_remove("/core/conversations/chat/show_leave");
1305 gaim_prefs_remove("/core/conversations/combine_chat_im");
1306 gaim_prefs_remove("/core/conversations/use_alias_for_title");
1307 gaim_prefs_remove("/core/logging/log_signon_signoff");
1308 gaim_prefs_remove("/core/logging/log_idle_state");
1309 gaim_prefs_remove("/core/logging/log_away_state");
1310 gaim_prefs_remove("/core/logging/log_own_states");
1311 gaim_prefs_remove("/core/status/scores/hidden");
1312 gaim_prefs_remove("/plugins/core/autorecon/hide_connected_error");
1313 gaim_prefs_remove("/plugins/core/autorecon/hide_connecting_error");
1314 gaim_prefs_remove("/plugins/core/autorecon/hide_reconnecting_dialog");
1315 gaim_prefs_remove("/plugins/core/autorecon/restore_state");
1316 gaim_prefs_remove("/plugins/core/autorecon");
1317
1318 /* Convert old sounds while_away pref to new 3-way pref. */
1319 if (gaim_prefs_exists("/core/sound/while_away") &&
1320 gaim_prefs_get_bool("/core/sound/while_away"))
1321 {
1322 gaim_prefs_set_int("/core/sound/while_status", 3);
1323 }
1324 gaim_prefs_remove("/core/sound/while_away");
1325 }
1326
1327 void *
1328 gaim_prefs_get_handle(void)
1329 {
1330 static int handle;
1331
1332 return &handle;
1333 }
1334
1335 void
1336 gaim_prefs_init(void)
1337 {
1338 void *handle = gaim_prefs_get_handle();
1339
1340 prefs_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1341
1342 gaim_prefs_connect_callback(handle, "/", prefs_save_cb, NULL);
1343
1344 gaim_prefs_add_none("/core");
1345 gaim_prefs_add_none("/plugins");
1346 gaim_prefs_add_none("/plugins/core");
1347 gaim_prefs_add_none("/plugins/lopl");
1348 gaim_prefs_add_none("/plugins/prpl");
1349
1350 /* Away */
1351 gaim_prefs_add_none("/core/away");
1352 gaim_prefs_add_string("/core/away/idle_reporting", "system");
1353 gaim_prefs_add_bool("/core/away/away_when_idle", TRUE);
1354 gaim_prefs_add_int("/core/away/mins_before_away", 5);
1355
1356 /* Away -> Auto-Reply */
1357 if (!gaim_prefs_exists("/core/away/auto_response/enabled") ||
1358 !gaim_prefs_exists("/core/away/auto_response/idle_only"))
1359 {
1360 gaim_prefs_add_string("/core/away/auto_reply", "awayidle");
1361 }
1362 else
1363 {
1364 if (!gaim_prefs_get_bool("/core/away/auto_response/enabled"))
1365 {
1366 gaim_prefs_add_string("/core/away/auto_reply", "never");
1367 }
1368 else
1369 {
1370 if (gaim_prefs_get_bool("/core/away/auto_response/idle_only"))
1371 {
1372 gaim_prefs_add_string("/core/away/auto_reply", "awayidle");
1373 }
1374 else
1375 {
1376 gaim_prefs_add_string("/core/away/auto_reply", "away");
1377 }
1378 }
1379 }
1380
1381 /* Buddies */
1382 gaim_prefs_add_none("/core/buddies");
1383
1384 /* Contact Priority Settings */
1385 gaim_prefs_add_none("/core/contact");
1386 gaim_prefs_add_bool("/core/contact/last_match", FALSE);
1387 gaim_prefs_remove("/core/contact/offline_score");
1388 gaim_prefs_remove("/core/contact/away_score");
1389 gaim_prefs_remove("/core/contact/idle_score");
1390 }
1391
1392 void
1393 gaim_prefs_uninit()
1394 {
1395 if (save_timer != 0)
1396 {
1397 gaim_timeout_remove(save_timer);
1398 save_timer = 0;
1399 sync_prefs();
1400 }
1401
1402 gaim_prefs_disconnect_by_handle(gaim_prefs_get_handle());
1403 }