Fix an incorrect call to soup_message_set_request.
[pidgin-git.git] / libpurple / prefs.c
blobcaec049908905e2cb1708904b02f6f6f5b7d861b
1 /*
2 * purple
4 * Purple 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.
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #include <string.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <sys/types.h>
32 #include <glib.h>
33 #include "internal.h"
34 #include "prefs.h"
35 #include "debug.h"
36 #include "util.h"
38 static PurplePrefsUiOps *prefs_ui_ops = NULL;
40 struct _PurplePrefCallbackData {
41 PurplePrefCallback func;
42 gpointer data;
43 guint id;
44 void *handle;
45 void *ui_data;
46 char *name;
49 struct pref_cb {
50 PurplePrefCallback func;
51 gpointer data;
52 guint id;
53 void *handle;
54 void *ui_data;
55 char *name;
58 /* TODO: This should use PurpleValues? */
59 struct purple_pref {
60 PurplePrefType type;
61 char *name;
62 union {
63 /* 'generic' is kind of ugly. We use it as an elegant way to refer to
64 the value of this pref when calling callback functions. We could
65 use 'boolean' or 'integer' or any other field... but it feels
66 mildly cleaner to use a gpointer. Maybe it would be best to use a
67 GValue? */
68 gpointer generic;
69 gboolean boolean;
70 int integer;
71 char *string;
72 GList *stringlist;
73 } value;
74 GSList *callbacks;
75 struct purple_pref *parent;
76 struct purple_pref *sibling;
77 struct purple_pref *first_child;
81 static struct purple_pref prefs = {
82 PURPLE_PREF_NONE,
83 NULL,
84 { NULL },
85 NULL,
86 NULL,
87 NULL,
88 NULL
91 static GHashTable *prefs_hash = NULL;
92 static guint save_timer = 0;
93 static gboolean prefs_loaded = FALSE;
94 static GSList *ui_callbacks = NULL;
96 #define PURPLE_PREFS_UI_OP_CALL(member, ...) \
97 { \
98 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops(); \
99 if (uiop && uiop->member) { \
100 uiop->member(__VA_ARGS__); \
101 return; \
105 #define PURPLE_PREFS_UI_OP_CALL_RETURN(member, ...) \
107 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops(); \
108 if (uiop && uiop->member) { \
109 return uiop->member(__VA_ARGS__); \
114 /*********************************************************************
115 * Private utility functions *
116 *********************************************************************/
118 static struct
119 purple_pref *find_pref(const char *name)
121 g_return_val_if_fail(name != NULL && name[0] == '/', NULL);
123 if (name[1] == '\0')
124 return &prefs;
125 else
127 /* When we're initializing, the debug system is
128 * initialized before the prefs system, but debug
129 * calls will end up calling prefs functions, so we
130 * need to deal cleanly here. */
131 if (prefs_hash)
132 return g_hash_table_lookup(prefs_hash, name);
133 else
134 return NULL;
139 /*********************************************************************
140 * Writing to disk *
141 *********************************************************************/
144 * This function recursively creates the PurpleXmlNode tree from the prefs
145 * tree structure. Yay recursion!
147 static void
148 pref_to_xmlnode(PurpleXmlNode *parent, struct purple_pref *pref)
150 PurpleXmlNode *node, *childnode;
151 struct purple_pref *child;
152 char buf[21];
153 GList *cur;
155 /* Create a new node */
156 node = purple_xmlnode_new_child(parent, "pref");
157 purple_xmlnode_set_attrib(node, "name", pref->name);
159 /* Set the type of this node (if type == PURPLE_PREF_NONE then do nothing) */
160 if (pref->type == PURPLE_PREF_INT) {
161 purple_xmlnode_set_attrib(node, "type", "int");
162 g_snprintf(buf, sizeof(buf), "%d", pref->value.integer);
163 purple_xmlnode_set_attrib(node, "value", buf);
165 else if (pref->type == PURPLE_PREF_STRING) {
166 purple_xmlnode_set_attrib(node, "type", "string");
167 purple_xmlnode_set_attrib(node, "value", pref->value.string ? pref->value.string : "");
169 else if (pref->type == PURPLE_PREF_STRING_LIST) {
170 purple_xmlnode_set_attrib(node, "type", "stringlist");
171 for (cur = pref->value.stringlist; cur != NULL; cur = cur->next)
173 childnode = purple_xmlnode_new_child(node, "item");
174 purple_xmlnode_set_attrib(childnode, "value", cur->data ? cur->data : "");
177 else if (pref->type == PURPLE_PREF_PATH) {
178 char *encoded = g_filename_to_utf8(pref->value.string ? pref->value.string : "", -1, NULL, NULL, NULL);
179 purple_xmlnode_set_attrib(node, "type", "path");
180 purple_xmlnode_set_attrib(node, "value", encoded);
181 g_free(encoded);
183 else if (pref->type == PURPLE_PREF_PATH_LIST) {
184 purple_xmlnode_set_attrib(node, "type", "pathlist");
185 for (cur = pref->value.stringlist; cur != NULL; cur = cur->next)
187 char *encoded = g_filename_to_utf8(cur->data ? cur->data : "", -1, NULL, NULL, NULL);
188 childnode = purple_xmlnode_new_child(node, "item");
189 purple_xmlnode_set_attrib(childnode, "value", encoded);
190 g_free(encoded);
193 else if (pref->type == PURPLE_PREF_BOOLEAN) {
194 purple_xmlnode_set_attrib(node, "type", "bool");
195 g_snprintf(buf, sizeof(buf), "%d", pref->value.boolean);
196 purple_xmlnode_set_attrib(node, "value", buf);
199 /* All My Children */
200 for (child = pref->first_child; child != NULL; child = child->sibling)
201 pref_to_xmlnode(node, child);
204 static PurpleXmlNode *
205 prefs_to_xmlnode(void)
207 PurpleXmlNode *node;
208 struct purple_pref *pref, *child;
210 pref = &prefs;
212 /* Create the root preference node */
213 node = purple_xmlnode_new("pref");
214 purple_xmlnode_set_attrib(node, "version", "1");
215 purple_xmlnode_set_attrib(node, "name", "/");
217 /* All My Children */
218 for (child = pref->first_child; child != NULL; child = child->sibling)
219 pref_to_xmlnode(node, child);
221 return node;
224 static void
225 sync_prefs(void)
227 PurpleXmlNode *node;
228 char *data;
230 if (!prefs_loaded)
233 * TODO: Call schedule_prefs_save()? Ideally we wouldn't need to.
234 * (prefs.xml should be loaded when purple_prefs_init is called)
236 purple_debug_error("prefs", "Attempted to save prefs before "
237 "they were read!\n");
238 return;
241 PURPLE_PREFS_UI_OP_CALL(save);
243 node = prefs_to_xmlnode();
244 data = purple_xmlnode_to_formatted_str(node, NULL);
245 purple_util_write_data_to_config_file("prefs.xml", data, -1);
246 g_free(data);
247 purple_xmlnode_free(node);
250 static gboolean
251 save_cb(gpointer data)
253 sync_prefs();
254 save_timer = 0;
255 return FALSE;
258 static void
259 schedule_prefs_save(void)
261 PURPLE_PREFS_UI_OP_CALL(schedule_save);
263 if (save_timer == 0)
264 save_timer = g_timeout_add_seconds(5, save_cb, NULL);
268 /*********************************************************************
269 * Reading from disk *
270 *********************************************************************/
272 static GList *prefs_stack = NULL;
274 static void
275 prefs_start_element_handler (GMarkupParseContext *context,
276 const gchar *element_name,
277 const gchar **attribute_names,
278 const gchar **attribute_values,
279 gpointer user_data,
280 GError **error)
282 PurplePrefType pref_type = PURPLE_PREF_NONE;
283 int i;
284 const char *pref_name = NULL, *pref_value = NULL;
285 GString *pref_name_full;
286 GList *tmp;
288 if(!purple_strequal(element_name, "pref") &&
289 !purple_strequal(element_name, "item"))
290 return;
292 for(i = 0; attribute_names[i]; i++) {
293 if(purple_strequal(attribute_names[i], "name")) {
294 pref_name = attribute_values[i];
295 } else if(purple_strequal(attribute_names[i], "type")) {
296 if(purple_strequal(attribute_values[i], "bool"))
297 pref_type = PURPLE_PREF_BOOLEAN;
298 else if(purple_strequal(attribute_values[i], "int"))
299 pref_type = PURPLE_PREF_INT;
300 else if(purple_strequal(attribute_values[i], "string"))
301 pref_type = PURPLE_PREF_STRING;
302 else if(purple_strequal(attribute_values[i], "stringlist"))
303 pref_type = PURPLE_PREF_STRING_LIST;
304 else if(purple_strequal(attribute_values[i], "path"))
305 pref_type = PURPLE_PREF_PATH;
306 else if(purple_strequal(attribute_values[i], "pathlist"))
307 pref_type = PURPLE_PREF_PATH_LIST;
308 else
309 return;
310 } else if(purple_strequal(attribute_names[i], "value")) {
311 pref_value = attribute_values[i];
315 if ((pref_type == PURPLE_PREF_BOOLEAN || pref_type == PURPLE_PREF_INT) &&
316 pref_value == NULL) {
317 /* Missing a value attribute */
318 return;
321 if(purple_strequal(element_name, "item")) {
322 struct purple_pref *pref;
324 pref_name_full = g_string_new("");
326 for(tmp = prefs_stack; tmp; tmp = tmp->next) {
327 pref_name_full = g_string_prepend(pref_name_full, tmp->data);
328 pref_name_full = g_string_prepend_c(pref_name_full, '/');
331 pref = find_pref(pref_name_full->str);
333 if(pref) {
334 if(pref->type == PURPLE_PREF_STRING_LIST) {
335 pref->value.stringlist = g_list_append(pref->value.stringlist,
336 g_strdup(pref_value));
337 } else if(pref->type == PURPLE_PREF_PATH_LIST) {
338 pref->value.stringlist = g_list_append(pref->value.stringlist,
339 g_filename_from_utf8(pref_value, -1, NULL, NULL, NULL));
342 g_string_free(pref_name_full, TRUE);
343 } else {
344 char *decoded;
346 if(!pref_name || purple_strequal(pref_name, "/"))
347 return;
349 pref_name_full = g_string_new(pref_name);
351 for(tmp = prefs_stack; tmp; tmp = tmp->next) {
352 pref_name_full = g_string_prepend_c(pref_name_full, '/');
353 pref_name_full = g_string_prepend(pref_name_full, tmp->data);
356 pref_name_full = g_string_prepend_c(pref_name_full, '/');
358 switch(pref_type) {
359 case PURPLE_PREF_NONE:
360 purple_prefs_add_none(pref_name_full->str);
361 break;
362 case PURPLE_PREF_BOOLEAN:
363 purple_prefs_set_bool(pref_name_full->str, atoi(pref_value));
364 break;
365 case PURPLE_PREF_INT:
366 purple_prefs_set_int(pref_name_full->str, atoi(pref_value));
367 break;
368 case PURPLE_PREF_STRING:
369 purple_prefs_set_string(pref_name_full->str, pref_value);
370 break;
371 case PURPLE_PREF_STRING_LIST:
372 purple_prefs_set_string_list(pref_name_full->str, NULL);
373 break;
374 case PURPLE_PREF_PATH:
375 if (pref_value) {
376 decoded = g_filename_from_utf8(pref_value, -1, NULL, NULL, NULL);
377 purple_prefs_set_path(pref_name_full->str, decoded);
378 g_free(decoded);
379 } else {
380 purple_prefs_set_path(pref_name_full->str, NULL);
382 break;
383 case PURPLE_PREF_PATH_LIST:
384 purple_prefs_set_path_list(pref_name_full->str, NULL);
385 break;
387 prefs_stack = g_list_prepend(prefs_stack, g_strdup(pref_name));
388 g_string_free(pref_name_full, TRUE);
392 static void
393 prefs_end_element_handler(GMarkupParseContext *context,
394 const gchar *element_name,
395 gpointer user_data, GError **error)
397 if(prefs_stack && purple_strequal(element_name, "pref")) {
398 g_free(prefs_stack->data);
399 prefs_stack = g_list_delete_link(prefs_stack, prefs_stack);
403 static GMarkupParser prefs_parser = {
404 prefs_start_element_handler,
405 prefs_end_element_handler,
406 NULL,
407 NULL,
408 NULL
411 gboolean
412 purple_prefs_load()
414 gchar *filename;
415 gchar *contents = NULL;
416 gsize length;
417 GMarkupParseContext *context;
418 GError *error = NULL;
420 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
422 if (uiop && uiop->load) {
423 prefs_loaded = TRUE;
424 return uiop->load();
427 filename = g_build_filename(purple_config_dir(), "prefs.xml", NULL);
429 if (!filename) {
430 prefs_loaded = TRUE;
431 return FALSE;
434 purple_debug_misc("prefs", "Reading %s", filename);
436 if(!g_file_get_contents(filename, &contents, &length, &error)) {
437 const gchar *sysconfdir = PURPLE_SYSCONFDIR;
438 g_free(filename);
439 g_error_free(error);
441 error = NULL;
443 #ifndef __COVERITY__
444 /* coverity dead_error_line false positive */
445 if (sysconfdir == NULL)
446 sysconfdir = "";
447 #endif
448 filename = g_build_filename(sysconfdir, "purple", "prefs.xml", NULL);
450 purple_debug_info("prefs", "Reading %s\n", filename);
452 if (!g_file_get_contents(filename, &contents, &length, &error)) {
453 purple_debug_error("prefs", "Error reading prefs: %s\n",
454 error->message);
455 g_error_free(error);
456 g_free(filename);
457 prefs_loaded = TRUE;
459 return FALSE;
463 context = g_markup_parse_context_new(&prefs_parser, 0, NULL, NULL);
465 if(!g_markup_parse_context_parse(context, contents, length, NULL)) {
466 g_markup_parse_context_free(context);
467 g_free(contents);
468 g_free(filename);
469 prefs_loaded = TRUE;
471 return FALSE;
474 if(!g_markup_parse_context_end_parse(context, NULL)) {
475 purple_debug_error("prefs", "Error parsing %s\n", filename);
476 g_markup_parse_context_free(context);
477 g_free(contents);
478 g_free(filename);
479 prefs_loaded = TRUE;
481 return FALSE;
484 if (purple_debug_is_verbose())
485 purple_debug_misc("prefs", "Finished reading %s", filename);
486 g_markup_parse_context_free(context);
487 g_free(contents);
488 g_free(filename);
489 prefs_loaded = TRUE;
491 return TRUE;
496 static void
497 prefs_save_cb(const char *name, PurplePrefType type, gconstpointer val,
498 gpointer user_data)
501 if(!prefs_loaded)
502 return;
504 purple_debug_misc("prefs", "%s changed, scheduling save.\n", name);
506 schedule_prefs_save();
509 static char *
510 get_path_dirname(const char *name)
512 char *c, *str;
514 str = g_strdup(name);
516 if ((c = strrchr(str, '/')) != NULL) {
517 *c = '\0';
519 if (*str == '\0') {
520 g_free(str);
522 str = g_strdup("/");
525 else {
526 g_free(str);
528 str = g_strdup(".");
531 return str;
534 static char *
535 get_path_basename(const char *name)
537 const char *c;
539 if ((c = strrchr(name, '/')) != NULL)
540 return g_strdup(c + 1);
542 return g_strdup(name);
545 static char *
546 pref_full_name(struct purple_pref *pref)
548 GString *name;
549 struct purple_pref *parent;
551 if(!pref)
552 return NULL;
554 if(pref == &prefs)
555 return g_strdup("/");
557 name = g_string_new(pref->name);
559 for(parent = pref->parent; parent && parent->name; parent = parent->parent) {
560 name = g_string_prepend_c(name, '/');
561 name = g_string_prepend(name, parent->name);
563 name = g_string_prepend_c(name, '/');
564 return g_string_free(name, FALSE);
567 static struct purple_pref *
568 find_pref_parent(const char *name)
570 char *parent_name = get_path_dirname(name);
571 struct purple_pref *ret = &prefs;
573 if(!purple_strequal(parent_name, "/")) {
574 ret = find_pref(parent_name);
577 g_free(parent_name);
578 return ret;
581 static void
582 free_pref_value(struct purple_pref *pref)
584 switch(pref->type) {
585 case PURPLE_PREF_BOOLEAN:
586 pref->value.boolean = FALSE;
587 break;
588 case PURPLE_PREF_INT:
589 pref->value.integer = 0;
590 break;
591 case PURPLE_PREF_STRING:
592 case PURPLE_PREF_PATH:
593 g_free(pref->value.string);
594 pref->value.string = NULL;
595 break;
596 case PURPLE_PREF_STRING_LIST:
597 case PURPLE_PREF_PATH_LIST:
598 g_list_free_full(pref->value.stringlist, g_free);
599 break;
600 case PURPLE_PREF_NONE:
601 break;
605 static struct purple_pref *
606 add_pref(PurplePrefType type, const char *name)
608 struct purple_pref *parent;
609 struct purple_pref *me;
610 struct purple_pref *sibling;
611 char *my_name;
613 parent = find_pref_parent(name);
615 g_return_val_if_fail(parent, NULL);
617 my_name = get_path_basename(name);
619 for(sibling = parent->first_child; sibling; sibling = sibling->sibling) {
620 if(purple_strequal(sibling->name, my_name)) {
621 g_free(my_name);
622 return NULL;
626 me = g_new0(struct purple_pref, 1);
627 me->type = type;
628 me->name = my_name;
630 me->parent = parent;
631 if(parent->first_child) {
632 /* blatant abuse of a for loop */
633 for(sibling = parent->first_child; sibling->sibling;
634 sibling = sibling->sibling);
635 sibling->sibling = me;
636 } else {
637 parent->first_child = me;
640 g_hash_table_insert(prefs_hash, g_strdup(name), (gpointer)me);
642 return me;
645 void
646 purple_prefs_add_none(const char *name)
648 PURPLE_PREFS_UI_OP_CALL(add_none, name);
650 add_pref(PURPLE_PREF_NONE, name);
653 void
654 purple_prefs_add_bool(const char *name, gboolean value)
656 struct purple_pref *pref;
658 PURPLE_PREFS_UI_OP_CALL(add_bool, name, value);
660 pref = add_pref(PURPLE_PREF_BOOLEAN, name);
662 if(!pref)
663 return;
665 pref->value.boolean = value;
668 void
669 purple_prefs_add_int(const char *name, int value)
671 struct purple_pref *pref;
673 PURPLE_PREFS_UI_OP_CALL(add_int, name, value);
675 pref = add_pref(PURPLE_PREF_INT, name);
677 if(!pref)
678 return;
680 pref->value.integer = value;
683 void
684 purple_prefs_add_string(const char *name, const char *value)
686 struct purple_pref *pref;
688 if(value != NULL && !g_utf8_validate(value, -1, NULL)) {
689 purple_debug_error("prefs", "purple_prefs_add_string: Cannot store invalid UTF8 for string pref %s\n", name);
690 return;
693 PURPLE_PREFS_UI_OP_CALL(add_string, name, value);
695 pref = add_pref(PURPLE_PREF_STRING, name);
697 if(!pref)
698 return;
700 pref->value.string = g_strdup(value);
703 void
704 purple_prefs_add_string_list(const char *name, GList *value)
706 struct purple_pref *pref;
707 GList *tmp;
709 PURPLE_PREFS_UI_OP_CALL(add_string_list, name, value);
711 pref = add_pref(PURPLE_PREF_STRING_LIST, name);
713 if(!pref)
714 return;
716 for(tmp = value; tmp; tmp = tmp->next) {
717 if(tmp->data != NULL && !g_utf8_validate(tmp->data, -1, NULL)) {
718 purple_debug_error("prefs", "purple_prefs_add_string_list: Skipping invalid UTF8 for string list pref %s\n", name);
719 continue;
721 pref->value.stringlist = g_list_append(pref->value.stringlist,
722 g_strdup(tmp->data));
726 void
727 purple_prefs_add_path(const char *name, const char *value)
729 struct purple_pref *pref;
731 /* re-use the string UI OP */
732 PURPLE_PREFS_UI_OP_CALL(add_string, name, value);
734 pref = add_pref(PURPLE_PREF_PATH, name);
736 if(!pref)
737 return;
739 pref->value.string = g_strdup(value);
742 void
743 purple_prefs_add_path_list(const char *name, GList *value)
745 struct purple_pref *pref;
746 GList *tmp;
748 /* re-use the string list UI OP */
749 PURPLE_PREFS_UI_OP_CALL(add_string_list, name, value);
751 pref = add_pref(PURPLE_PREF_PATH_LIST, name);
753 if(!pref)
754 return;
756 for(tmp = value; tmp; tmp = tmp->next)
757 pref->value.stringlist = g_list_append(pref->value.stringlist,
758 g_strdup(tmp->data));
762 static void
763 remove_pref(struct purple_pref *pref)
765 char *name;
766 GSList *l;
768 if(!pref)
769 return;
771 while(pref->first_child)
772 remove_pref(pref->first_child);
774 if(pref == &prefs)
775 return;
777 if(pref->parent->first_child == pref) {
778 pref->parent->first_child = pref->sibling;
779 } else {
780 struct purple_pref *sib = pref->parent->first_child;
781 while(sib && sib->sibling != pref)
782 sib = sib->sibling;
783 if(sib)
784 sib->sibling = pref->sibling;
787 name = pref_full_name(pref);
789 if (prefs_loaded)
790 purple_debug_info("prefs", "removing pref %s\n", name);
792 g_hash_table_remove(prefs_hash, name);
793 g_free(name);
795 free_pref_value(pref);
797 while((l = pref->callbacks) != NULL) {
798 pref->callbacks = pref->callbacks->next;
799 g_free(l->data);
800 g_slist_free_1(l);
802 g_free(pref->name);
803 g_free(pref);
806 void
807 purple_prefs_remove(const char *name)
809 struct purple_pref *pref;
811 PURPLE_PREFS_UI_OP_CALL(remove, name);
813 pref = find_pref(name);
815 if(!pref)
816 return;
818 remove_pref(pref);
821 void
822 purple_prefs_destroy()
824 purple_prefs_remove("/");
827 static void
828 do_callbacks(const char* name, struct purple_pref *pref)
830 GSList *cbs;
831 struct purple_pref *cb_pref;
832 for(cb_pref = pref; cb_pref; cb_pref = cb_pref->parent) {
833 for(cbs = cb_pref->callbacks; cbs; cbs = cbs->next) {
834 PurplePrefCallbackData *cb = cbs->data;
835 cb->func(name, pref->type, pref->value.generic, cb->data);
840 static void
841 do_ui_callbacks(const char *name)
843 GSList *cbs;
845 purple_debug_misc("prefs", "trigger callback %s\n", name);
847 for (cbs = ui_callbacks; cbs; cbs = cbs->next) {
848 PurplePrefCallbackData *cb = cbs->data;
849 const char *cb_name = cb->name;
850 size_t len = strlen(cb_name);
851 if (!strncmp(cb_name, name, len) &&
852 (name[len] == 0 || name[len] == '/' ||
853 (len && name[len - 1] == '/'))) {
854 /* This test should behave like this:
855 * name = /toto/tata
856 * cb_name = /toto/tata --> true
857 * cb_name = /toto/tatatiti --> false
858 * cb_name = / --> true
859 * cb_name = /toto --> true
860 * cb_name = /toto/ --> true
862 purple_prefs_trigger_callback_object(cbs->data);
867 void
868 purple_prefs_trigger_callback(const char *name)
870 struct purple_pref *pref;
871 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
873 if (uiop && uiop->connect_callback) {
874 do_ui_callbacks(name);
875 return;
878 pref = find_pref(name);
880 if(!pref) {
881 purple_debug_error("prefs",
882 "purple_prefs_trigger_callback: Unknown pref %s\n", name);
883 return;
886 do_callbacks(name, pref);
889 /* this function is deprecated, so it doesn't get the new UI ops */
890 void
891 purple_prefs_set_bool(const char *name, gboolean value)
893 struct purple_pref *pref;
895 PURPLE_PREFS_UI_OP_CALL(set_bool, name, value);
897 pref = find_pref(name);
899 if(pref) {
900 if(pref->type != PURPLE_PREF_BOOLEAN) {
901 purple_debug_error("prefs",
902 "purple_prefs_set_bool: %s not a boolean pref\n", name);
903 return;
906 if(pref->value.boolean != value) {
907 pref->value.boolean = value;
908 do_callbacks(name, pref);
910 } else {
911 purple_prefs_add_bool(name, value);
915 void
916 purple_prefs_set_int(const char *name, int value)
918 struct purple_pref *pref;
920 PURPLE_PREFS_UI_OP_CALL(set_int, name, value);
922 pref = find_pref(name);
924 if(pref) {
925 if(pref->type != PURPLE_PREF_INT) {
926 purple_debug_error("prefs",
927 "purple_prefs_set_int: %s not an integer pref\n", name);
928 return;
931 if(pref->value.integer != value) {
932 pref->value.integer = value;
933 do_callbacks(name, pref);
935 } else {
936 purple_prefs_add_int(name, value);
940 void
941 purple_prefs_set_string(const char *name, const char *value)
943 struct purple_pref *pref;
945 if(value != NULL && !g_utf8_validate(value, -1, NULL)) {
946 purple_debug_error("prefs", "purple_prefs_set_string: Cannot store invalid UTF8 for string pref %s\n", name);
947 return;
950 PURPLE_PREFS_UI_OP_CALL(set_string, name, value);
952 pref = find_pref(name);
954 if(pref) {
955 if(pref->type != PURPLE_PREF_STRING && pref->type != PURPLE_PREF_PATH) {
956 purple_debug_error("prefs",
957 "purple_prefs_set_string: %s not a string pref\n", name);
958 return;
961 if (!purple_strequal(pref->value.string, value)) {
962 g_free(pref->value.string);
963 pref->value.string = g_strdup(value);
964 do_callbacks(name, pref);
966 } else {
967 purple_prefs_add_string(name, value);
971 void
972 purple_prefs_set_string_list(const char *name, GList *value)
974 struct purple_pref *pref;
976 PURPLE_PREFS_UI_OP_CALL(set_string_list, name, value);
978 pref = find_pref(name);
980 if(pref) {
981 GList *tmp;
983 if(pref->type != PURPLE_PREF_STRING_LIST) {
984 purple_debug_error("prefs",
985 "purple_prefs_set_string_list: %s not a string list pref\n",
986 name);
987 return;
990 g_list_free_full(pref->value.stringlist, g_free);
991 pref->value.stringlist = NULL;
993 for(tmp = value; tmp; tmp = tmp->next) {
994 if(tmp->data != NULL && !g_utf8_validate(tmp->data, -1, NULL)) {
995 purple_debug_error("prefs", "purple_prefs_set_string_list: Skipping invalid UTF8 for string list pref %s\n", name);
996 continue;
998 pref->value.stringlist = g_list_prepend(pref->value.stringlist,
999 g_strdup(tmp->data));
1001 pref->value.stringlist = g_list_reverse(pref->value.stringlist);
1003 do_callbacks(name, pref);
1005 } else {
1006 purple_prefs_add_string_list(name, value);
1010 void
1011 purple_prefs_set_path(const char *name, const char *value)
1013 struct purple_pref *pref;
1015 PURPLE_PREFS_UI_OP_CALL(set_string, name, value);
1017 pref = find_pref(name);
1019 if(pref) {
1020 if(pref->type != PURPLE_PREF_PATH) {
1021 purple_debug_error("prefs",
1022 "purple_prefs_set_path: %s not a path pref\n", name);
1023 return;
1026 if (!purple_strequal(pref->value.string, value)) {
1027 g_free(pref->value.string);
1028 pref->value.string = g_strdup(value);
1029 do_callbacks(name, pref);
1031 } else {
1032 purple_prefs_add_path(name, value);
1036 void
1037 purple_prefs_set_path_list(const char *name, GList *value)
1039 struct purple_pref *pref;
1041 PURPLE_PREFS_UI_OP_CALL(set_string_list, name, value);
1043 pref = find_pref(name);
1045 if(pref) {
1046 GList *tmp;
1048 if(pref->type != PURPLE_PREF_PATH_LIST) {
1049 purple_debug_error("prefs",
1050 "purple_prefs_set_path_list: %s not a path list pref\n",
1051 name);
1052 return;
1055 g_list_free_full(pref->value.stringlist, g_free);
1056 pref->value.stringlist = NULL;
1058 for(tmp = value; tmp; tmp = tmp->next)
1059 pref->value.stringlist = g_list_prepend(pref->value.stringlist,
1060 g_strdup(tmp->data));
1061 pref->value.stringlist = g_list_reverse(pref->value.stringlist);
1063 do_callbacks(name, pref);
1065 } else {
1066 purple_prefs_add_path_list(name, value);
1071 gboolean
1072 purple_prefs_exists(const char *name)
1074 struct purple_pref *pref;
1076 PURPLE_PREFS_UI_OP_CALL_RETURN(exists, name);
1078 pref = find_pref(name);
1080 if (pref != NULL)
1081 return TRUE;
1083 return FALSE;
1086 PurplePrefType
1087 purple_prefs_get_pref_type(const char *name)
1089 struct purple_pref *pref;
1091 PURPLE_PREFS_UI_OP_CALL_RETURN(get_type, name);
1093 pref = find_pref(name);
1095 if (pref == NULL)
1096 return PURPLE_PREF_NONE;
1098 return (pref->type);
1101 gboolean
1102 purple_prefs_get_bool(const char *name)
1104 struct purple_pref *pref;
1106 PURPLE_PREFS_UI_OP_CALL_RETURN(get_bool, name);
1108 pref = find_pref(name);
1110 if(!pref) {
1111 purple_debug_error("prefs",
1112 "purple_prefs_get_bool: Unknown pref %s\n", name);
1113 return FALSE;
1114 } else if(pref->type != PURPLE_PREF_BOOLEAN) {
1115 purple_debug_error("prefs",
1116 "purple_prefs_get_bool: %s not a boolean pref\n", name);
1117 return FALSE;
1120 return pref->value.boolean;
1124 purple_prefs_get_int(const char *name)
1126 struct purple_pref *pref;
1128 PURPLE_PREFS_UI_OP_CALL_RETURN(get_int, name);
1130 pref = find_pref(name);
1132 if(!pref) {
1133 purple_debug_error("prefs",
1134 "purple_prefs_get_int: Unknown pref %s\n", name);
1135 return 0;
1136 } else if(pref->type != PURPLE_PREF_INT) {
1137 purple_debug_error("prefs",
1138 "purple_prefs_get_int: %s not an integer pref\n", name);
1139 return 0;
1142 return pref->value.integer;
1145 const char *
1146 purple_prefs_get_string(const char *name)
1148 struct purple_pref *pref;
1150 PURPLE_PREFS_UI_OP_CALL_RETURN(get_string, name);
1152 pref = find_pref(name);
1154 if(!pref) {
1155 purple_debug_error("prefs",
1156 "purple_prefs_get_string: Unknown pref %s\n", name);
1157 return NULL;
1158 } else if(pref->type != PURPLE_PREF_STRING) {
1159 purple_debug_error("prefs",
1160 "purple_prefs_get_string: %s not a string pref\n", name);
1161 return NULL;
1164 return pref->value.string;
1167 GList *
1168 purple_prefs_get_string_list(const char *name)
1170 struct purple_pref *pref;
1171 GList *ret = NULL, *tmp;
1173 PURPLE_PREFS_UI_OP_CALL_RETURN(get_string_list, name);
1175 pref = find_pref(name);
1177 if(!pref) {
1178 purple_debug_error("prefs",
1179 "purple_prefs_get_string_list: Unknown pref %s\n", name);
1180 return NULL;
1181 } else if(pref->type != PURPLE_PREF_STRING_LIST) {
1182 purple_debug_error("prefs",
1183 "purple_prefs_get_string_list: %s not a string list pref\n", name);
1184 return NULL;
1187 for(tmp = pref->value.stringlist; tmp; tmp = tmp->next)
1188 ret = g_list_prepend(ret, g_strdup(tmp->data));
1189 ret = g_list_reverse(ret);
1191 return ret;
1194 const char *
1195 purple_prefs_get_path(const char *name)
1197 struct purple_pref *pref;
1199 PURPLE_PREFS_UI_OP_CALL_RETURN(get_string, name);
1201 pref = find_pref(name);
1203 if(!pref) {
1204 purple_debug_error("prefs",
1205 "purple_prefs_get_path: Unknown pref %s\n", name);
1206 return NULL;
1207 } else if(pref->type != PURPLE_PREF_PATH) {
1208 purple_debug_error("prefs",
1209 "purple_prefs_get_path: %s not a path pref\n", name);
1210 return NULL;
1213 return pref->value.string;
1216 GList *
1217 purple_prefs_get_path_list(const char *name)
1219 struct purple_pref *pref;
1220 GList *ret = NULL, *tmp;
1222 PURPLE_PREFS_UI_OP_CALL_RETURN(get_string_list, name);
1224 pref = find_pref(name);
1226 if(!pref) {
1227 purple_debug_error("prefs",
1228 "purple_prefs_get_path_list: Unknown pref %s\n", name);
1229 return NULL;
1230 } else if(pref->type != PURPLE_PREF_PATH_LIST) {
1231 purple_debug_error("prefs",
1232 "purple_prefs_get_path_list: %s not a path list pref\n", name);
1233 return NULL;
1236 for(tmp = pref->value.stringlist; tmp; tmp = tmp->next)
1237 ret = g_list_prepend(ret, g_strdup(tmp->data));
1238 ret = g_list_reverse(ret);
1240 return ret;
1243 static void
1244 purple_prefs_rename_node(struct purple_pref *oldpref, struct purple_pref *newpref)
1246 struct purple_pref *child, *next;
1247 char *oldname, *newname;
1249 /* if we're a parent, rename the kids first */
1250 for(child = oldpref->first_child; child != NULL; child = next)
1252 struct purple_pref *newchild;
1253 next = child->sibling;
1254 for(newchild = newpref->first_child; newchild != NULL; newchild = newchild->sibling)
1256 if(purple_strequal(child->name, newchild->name))
1258 purple_prefs_rename_node(child, newchild);
1259 break;
1262 if(newchild == NULL) {
1263 /* no rename happened, we weren't able to find the new pref */
1264 char *tmpname = pref_full_name(child);
1265 purple_debug_error("prefs", "Unable to find rename pref for %s\n", tmpname);
1266 g_free(tmpname);
1270 oldname = pref_full_name(oldpref);
1271 newname = pref_full_name(newpref);
1273 if (oldpref->type != newpref->type)
1275 purple_debug_error("prefs", "Unable to rename %s to %s: differing types\n", oldname, newname);
1276 g_free(oldname);
1277 g_free(newname);
1278 return;
1281 purple_debug_info("prefs", "Renaming %s to %s\n", oldname, newname);
1282 g_free(oldname);
1284 switch(oldpref->type) {
1285 case PURPLE_PREF_NONE:
1286 break;
1287 case PURPLE_PREF_BOOLEAN:
1288 purple_prefs_set_bool(newname, oldpref->value.boolean);
1289 break;
1290 case PURPLE_PREF_INT:
1291 purple_prefs_set_int(newname, oldpref->value.integer);
1292 break;
1293 case PURPLE_PREF_STRING:
1294 purple_prefs_set_string(newname, oldpref->value.string);
1295 break;
1296 case PURPLE_PREF_STRING_LIST:
1297 purple_prefs_set_string_list(newname, oldpref->value.stringlist);
1298 break;
1299 case PURPLE_PREF_PATH:
1300 purple_prefs_set_path(newname, oldpref->value.string);
1301 break;
1302 case PURPLE_PREF_PATH_LIST:
1303 purple_prefs_set_path_list(newname, oldpref->value.stringlist);
1304 break;
1306 g_free(newname);
1308 remove_pref(oldpref);
1311 void
1312 purple_prefs_rename(const char *oldname, const char *newname)
1314 struct purple_pref *oldpref, *newpref;
1316 /* win32dep.h causes rename to be defined as wpurple_rename, so we need to undefine it here */
1317 #if defined(_WIN32) && defined(rename)
1318 #undef rename
1319 #endif
1321 PURPLE_PREFS_UI_OP_CALL(rename, oldname, newname);
1323 oldpref = find_pref(oldname);
1325 /* it's already been renamed, call off the dogs */
1326 if(!oldpref)
1327 return;
1329 newpref = find_pref(newname);
1331 if (newpref == NULL)
1333 purple_debug_error("prefs", "Unable to rename %s to %s: new pref not created\n", oldname, newname);
1334 return;
1337 purple_prefs_rename_node(oldpref, newpref);
1340 void
1341 purple_prefs_rename_boolean_toggle(const char *oldname, const char *newname)
1343 struct purple_pref *oldpref, *newpref;
1345 PURPLE_PREFS_UI_OP_CALL(rename_boolean_toggle, oldname, newname);
1347 oldpref = find_pref(oldname);
1349 /* it's already been renamed, call off the cats */
1350 if(!oldpref)
1351 return;
1353 if (oldpref->type != PURPLE_PREF_BOOLEAN)
1355 purple_debug_error("prefs", "Unable to rename %s to %s: old pref not a boolean\n", oldname, newname);
1356 return;
1359 if (oldpref->first_child != NULL) /* can't rename parents */
1361 purple_debug_error("prefs", "Unable to rename %s to %s: can't rename parents\n", oldname, newname);
1362 return;
1366 newpref = find_pref(newname);
1368 if (newpref == NULL)
1370 purple_debug_error("prefs", "Unable to rename %s to %s: new pref not created\n", oldname, newname);
1371 return;
1374 if (oldpref->type != newpref->type)
1376 purple_debug_error("prefs", "Unable to rename %s to %s: differing types\n", oldname, newname);
1377 return;
1380 purple_debug_info("prefs", "Renaming and toggling %s to %s\n", oldname, newname);
1381 purple_prefs_set_bool(newname, !(oldpref->value.boolean));
1383 remove_pref(oldpref);
1386 guint
1387 purple_prefs_connect_callback(void *handle, const char *name, PurplePrefCallback func, gpointer data)
1389 struct purple_pref *pref = NULL;
1390 PurplePrefCallbackData *cb;
1391 static guint cb_id = 0;
1392 PurplePrefsUiOps *uiop = NULL;
1394 g_return_val_if_fail(name != NULL, 0);
1395 g_return_val_if_fail(func != NULL, 0);
1397 uiop = purple_prefs_get_ui_ops();
1399 if (!(uiop && uiop->connect_callback)) {
1400 pref = find_pref(name);
1401 if (pref == NULL) {
1402 purple_debug_error("prefs", "purple_prefs_connect_callback: Unknown pref %s\n", name);
1403 return 0;
1407 cb = g_new0(PurplePrefCallbackData, 1);
1409 cb->func = func;
1410 cb->data = data;
1411 cb->id = ++cb_id;
1412 cb->handle = handle;
1413 cb->name = g_strdup(name);
1415 if (uiop && uiop->connect_callback) {
1416 cb->ui_data = uiop->connect_callback(name, cb);
1418 if (cb->ui_data == NULL) {
1419 purple_debug_error("prefs", "purple_prefs_connect_callback: connect failed for %s\n", name);
1420 g_free(cb->name);
1421 g_free(cb);
1422 return 0;
1425 ui_callbacks = g_slist_append(ui_callbacks, cb);
1426 } else {
1427 pref->callbacks = g_slist_append(pref->callbacks, cb);
1430 return cb->id;
1433 static void
1434 purple_prefs_trigger_ui_callback_object(PurplePrefCallbackData *cb)
1436 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
1437 gconstpointer value = NULL;
1438 PurplePrefType type = PURPLE_PREF_NONE;
1440 type = uiop->get_type(cb->name);
1442 switch (type) {
1443 case PURPLE_PREF_INT:
1444 if (uiop->get_int) {
1445 value = GINT_TO_POINTER(uiop->get_int(cb->name));
1447 break;
1448 case PURPLE_PREF_BOOLEAN:
1449 if (uiop->get_bool) {
1450 value = GINT_TO_POINTER(uiop->get_bool(cb->name));
1452 break;
1453 case PURPLE_PREF_STRING:
1454 case PURPLE_PREF_PATH:
1455 if (uiop->get_string) {
1456 value = uiop->get_string(cb->name);
1458 break;
1459 case PURPLE_PREF_STRING_LIST:
1460 case PURPLE_PREF_PATH_LIST:
1461 if (uiop->get_string_list) {
1462 value = uiop->get_string_list(cb->name);
1464 break;
1465 case PURPLE_PREF_NONE:
1466 break;
1467 default:
1468 purple_debug_error("prefs", "Unexpected type = %i\n", type);
1471 cb->func(cb->name, type, value, cb->data);
1474 void
1475 purple_prefs_trigger_callback_object(PurplePrefCallbackData *cb)
1477 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
1479 if (uiop && uiop->connect_callback && uiop->get_type) {
1480 purple_prefs_trigger_ui_callback_object(cb);
1481 } else {
1482 purple_prefs_trigger_callback(cb->name);
1486 static gboolean
1487 disco_callback_helper(struct purple_pref *pref, guint callback_id)
1489 GSList *cbs;
1490 struct purple_pref *child;
1492 if(!pref)
1493 return FALSE;
1495 for(cbs = pref->callbacks; cbs; cbs = cbs->next) {
1496 PurplePrefCallbackData *cb = cbs->data;
1497 if(cb->id == callback_id) {
1498 pref->callbacks = g_slist_delete_link(pref->callbacks, cbs);
1499 g_free(cb->name);
1500 g_free(cb);
1501 return TRUE;
1505 for(child = pref->first_child; child; child = child->sibling) {
1506 if(disco_callback_helper(child, callback_id))
1507 return TRUE;
1510 return FALSE;
1513 static void
1514 disco_ui_callback_helper(guint callback_id)
1516 GSList *cbs;
1517 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
1519 for (cbs = ui_callbacks; cbs; cbs = cbs->next) {
1520 PurplePrefCallbackData *cb = cbs->data;
1521 if (cb->id == callback_id) {
1522 uiop->disconnect_callback(cb->name, cb->ui_data);
1524 ui_callbacks = g_slist_delete_link(ui_callbacks, cbs);
1525 g_free(cb->name);
1526 g_free(cb);
1527 return;
1532 void
1533 purple_prefs_disconnect_callback(guint callback_id)
1535 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
1537 if (uiop && uiop->disconnect_callback) {
1538 disco_ui_callback_helper(callback_id);
1539 } else {
1540 disco_callback_helper(&prefs, callback_id);
1544 static void
1545 disco_callback_helper_handle(struct purple_pref *pref, void *handle)
1547 GSList *cbs;
1548 struct purple_pref *child;
1550 if(!pref)
1551 return;
1553 cbs = pref->callbacks;
1554 while (cbs != NULL) {
1555 PurplePrefCallbackData *cb = cbs->data;
1556 if(cb->handle == handle) {
1557 pref->callbacks = g_slist_delete_link(pref->callbacks, cbs);
1558 g_free(cb->name);
1559 g_free(cb);
1560 cbs = pref->callbacks;
1561 } else
1562 cbs = cbs->next;
1565 for(child = pref->first_child; child; child = child->sibling)
1566 disco_callback_helper_handle(child, handle);
1569 static void
1570 disco_ui_callback_helper_handle(void *handle)
1572 GSList *cbs;
1573 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
1575 for (cbs = ui_callbacks; cbs; cbs = cbs->next) {
1576 PurplePrefCallbackData *cb = cbs->data;
1577 if (cb->handle != handle) {
1578 cbs = cbs->next;
1579 continue;
1582 uiop->disconnect_callback(cb->name, cb->ui_data);
1584 ui_callbacks = g_slist_delete_link(ui_callbacks, cbs);
1585 g_free(cb->name);
1586 g_free(cb);
1587 cbs = ui_callbacks;
1591 void
1592 purple_prefs_disconnect_by_handle(void *handle)
1594 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
1596 g_return_if_fail(handle != NULL);
1598 if (uiop && uiop->disconnect_callback) {
1599 disco_ui_callback_helper_handle(handle);
1600 } else {
1601 disco_callback_helper_handle(&prefs, handle);
1605 GList *
1606 purple_prefs_get_children_names(const char *name)
1608 GList * list = NULL;
1609 struct purple_pref *pref, *child;
1610 char sep[2] = "\0\0";;
1612 PURPLE_PREFS_UI_OP_CALL_RETURN(get_children_names, name);
1614 pref = find_pref(name);
1616 if (pref == NULL)
1617 return NULL;
1619 if (name[strlen(name) - 1] != '/')
1620 sep[0] = '/';
1621 for (child = pref->first_child; child; child = child->sibling) {
1622 list = g_list_append(list, g_strdup_printf("%s%s%s", name, sep, child->name));
1624 return list;
1627 static void
1628 prefs_update_old(void)
1630 purple_prefs_rename("/core", "/purple");
1632 /* Remove some no-longer-used prefs */
1633 purple_prefs_remove("/purple/away/auto_response/enabled");
1634 purple_prefs_remove("/purple/away/auto_response/idle_only");
1635 purple_prefs_remove("/purple/away/auto_response/in_active_conv");
1636 purple_prefs_remove("/purple/away/auto_response/sec_before_resend");
1637 purple_prefs_remove("/purple/away/auto_response");
1638 purple_prefs_remove("/purple/away/default_message");
1639 purple_prefs_remove("/purple/buddies/use_server_alias");
1640 purple_prefs_remove("/purple/conversations/away_back_on_send");
1641 purple_prefs_remove("/purple/conversations/send_urls_as_links");
1642 purple_prefs_remove("/purple/conversations/im/show_login");
1643 purple_prefs_remove("/purple/conversations/chat/show_join");
1644 purple_prefs_remove("/purple/conversations/chat/show_leave");
1645 purple_prefs_remove("/purple/conversations/combine_chat_im");
1646 purple_prefs_remove("/purple/conversations/use_alias_for_title");
1647 purple_prefs_remove("/purple/debug/timestamps");
1648 purple_prefs_remove("/purple/logging/log_signon_signoff");
1649 purple_prefs_remove("/purple/logging/log_idle_state");
1650 purple_prefs_remove("/purple/logging/log_away_state");
1651 purple_prefs_remove("/purple/logging/log_own_states");
1652 purple_prefs_remove("/purple/status/scores/hidden");
1653 purple_prefs_remove("/plugins/core/autorecon/hide_connected_error");
1654 purple_prefs_remove("/plugins/core/autorecon/hide_connecting_error");
1655 purple_prefs_remove("/plugins/core/autorecon/hide_reconnecting_dialog");
1656 purple_prefs_remove("/plugins/core/autorecon/restore_state");
1657 purple_prefs_remove("/plugins/core/autorecon");
1658 purple_prefs_remove("/plugins/lopl");
1660 /* Convert old sounds while_away pref to new 3-way pref. */
1661 if (purple_prefs_exists("/purple/sound/while_away") &&
1662 purple_prefs_get_bool("/purple/sound/while_away"))
1664 purple_prefs_set_int("/purple/sound/while_status", 3);
1666 purple_prefs_remove("/purple/sound/while_away");
1669 void *
1670 purple_prefs_get_handle(void)
1672 static int handle;
1674 return &handle;
1677 void
1678 purple_prefs_init(void)
1680 void *handle = purple_prefs_get_handle();
1682 prefs_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1684 purple_prefs_connect_callback(handle, "/", prefs_save_cb, NULL);
1686 purple_prefs_add_none("/purple");
1687 purple_prefs_add_none("/plugins");
1688 purple_prefs_add_none("/plugins/core");
1689 purple_prefs_add_none("/plugins/prpl");
1691 /* Away */
1692 purple_prefs_add_none("/purple/away");
1693 purple_prefs_add_string("/purple/away/idle_reporting", "system");
1694 purple_prefs_add_bool("/purple/away/away_when_idle", TRUE);
1695 purple_prefs_add_int("/purple/away/mins_before_away", 5);
1697 /* Away -> Auto-Reply */
1698 if (!purple_prefs_exists("/purple/away/auto_response/enabled") ||
1699 !purple_prefs_exists("/purple/away/auto_response/idle_only"))
1701 purple_prefs_add_string("/purple/away/auto_reply", "awayidle");
1703 else
1705 if (!purple_prefs_get_bool("/purple/away/auto_response/enabled"))
1707 purple_prefs_add_string("/purple/away/auto_reply", "never");
1709 else
1711 if (purple_prefs_get_bool("/purple/away/auto_response/idle_only"))
1713 purple_prefs_add_string("/purple/away/auto_reply", "awayidle");
1715 else
1717 purple_prefs_add_string("/purple/away/auto_reply", "away");
1722 /* Buddies */
1723 purple_prefs_add_none("/purple/buddies");
1725 /* Contact Priority Settings */
1726 purple_prefs_add_none("/purple/contact");
1727 purple_prefs_add_bool("/purple/contact/last_match", FALSE);
1728 purple_prefs_remove("/purple/contact/offline_score");
1729 purple_prefs_remove("/purple/contact/away_score");
1730 purple_prefs_remove("/purple/contact/idle_score");
1732 purple_prefs_load();
1733 prefs_update_old();
1736 void
1737 purple_prefs_uninit()
1739 if (save_timer != 0)
1741 g_source_remove(save_timer);
1742 save_cb(NULL);
1745 purple_prefs_disconnect_by_handle(purple_prefs_get_handle());
1747 prefs_loaded = FALSE;
1748 purple_prefs_destroy();
1749 g_hash_table_destroy(prefs_hash);
1750 prefs_hash = NULL;
1754 void
1755 purple_prefs_set_ui_ops(PurplePrefsUiOps *ops)
1757 prefs_ui_ops = ops;
1760 PurplePrefsUiOps *
1761 purple_prefs_get_ui_ops(void)
1763 return prefs_ui_ops;