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
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
31 #include <sys/types.h>
38 static PurplePrefsUiOps
*prefs_ui_ops
= NULL
;
40 struct _PurplePrefCallbackData
{
41 PurplePrefCallback func
;
50 PurplePrefCallback func
;
58 /* TODO: This should use PurpleValues? */
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
75 struct purple_pref
*parent
;
76 struct purple_pref
*sibling
;
77 struct purple_pref
*first_child
;
81 static struct purple_pref prefs
= {
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, ...) \
98 PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops(); \
99 if (uiop && uiop->member) { \
100 uiop->member(__VA_ARGS__); \
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 *********************************************************************/
119 purple_pref
*find_pref(const char *name
)
121 g_return_val_if_fail(name
!= NULL
&& name
[0] == '/', NULL
);
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. */
132 return g_hash_table_lookup(prefs_hash
, name
);
139 /*********************************************************************
141 *********************************************************************/
144 * This function recursively creates the PurpleXmlNode tree from the prefs
145 * tree structure. Yay recursion!
148 pref_to_xmlnode(PurpleXmlNode
*parent
, struct purple_pref
*pref
)
150 PurpleXmlNode
*node
, *childnode
;
151 struct purple_pref
*child
;
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
);
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
);
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)
208 struct purple_pref
*pref
, *child
;
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
);
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");
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);
247 purple_xmlnode_free(node
);
251 save_cb(gpointer data
)
259 schedule_prefs_save(void)
261 PURPLE_PREFS_UI_OP_CALL(schedule_save
);
264 save_timer
= g_timeout_add_seconds(5, save_cb
, NULL
);
268 /*********************************************************************
269 * Reading from disk *
270 *********************************************************************/
272 static GList
*prefs_stack
= NULL
;
275 prefs_start_element_handler (GMarkupParseContext
*context
,
276 const gchar
*element_name
,
277 const gchar
**attribute_names
,
278 const gchar
**attribute_values
,
282 PurplePrefType pref_type
= PURPLE_PREF_NONE
;
284 const char *pref_name
= NULL
, *pref_value
= NULL
;
285 GString
*pref_name_full
;
288 if(!purple_strequal(element_name
, "pref") &&
289 !purple_strequal(element_name
, "item"))
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
;
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 */
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
);
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
);
346 if(!pref_name
|| purple_strequal(pref_name
, "/"))
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
, '/');
359 case PURPLE_PREF_NONE
:
360 purple_prefs_add_none(pref_name_full
->str
);
362 case PURPLE_PREF_BOOLEAN
:
363 purple_prefs_set_bool(pref_name_full
->str
, atoi(pref_value
));
365 case PURPLE_PREF_INT
:
366 purple_prefs_set_int(pref_name_full
->str
, atoi(pref_value
));
368 case PURPLE_PREF_STRING
:
369 purple_prefs_set_string(pref_name_full
->str
, pref_value
);
371 case PURPLE_PREF_STRING_LIST
:
372 purple_prefs_set_string_list(pref_name_full
->str
, NULL
);
374 case PURPLE_PREF_PATH
:
376 decoded
= g_filename_from_utf8(pref_value
, -1, NULL
, NULL
, NULL
);
377 purple_prefs_set_path(pref_name_full
->str
, decoded
);
380 purple_prefs_set_path(pref_name_full
->str
, NULL
);
383 case PURPLE_PREF_PATH_LIST
:
384 purple_prefs_set_path_list(pref_name_full
->str
, NULL
);
387 prefs_stack
= g_list_prepend(prefs_stack
, g_strdup(pref_name
));
388 g_string_free(pref_name_full
, TRUE
);
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
,
415 gchar
*contents
= NULL
;
417 GMarkupParseContext
*context
;
418 GError
*error
= NULL
;
420 PurplePrefsUiOps
*uiop
= purple_prefs_get_ui_ops();
422 if (uiop
&& uiop
->load
) {
427 filename
= g_build_filename(purple_config_dir(), "prefs.xml", NULL
);
434 purple_debug_misc("prefs", "Reading %s", filename
);
436 if(!g_file_get_contents(filename
, &contents
, &length
, &error
)) {
437 const gchar
*sysconfdir
= PURPLE_SYSCONFDIR
;
444 /* coverity dead_error_line false positive */
445 if (sysconfdir
== NULL
)
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",
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
);
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
);
484 if (purple_debug_is_verbose())
485 purple_debug_misc("prefs", "Finished reading %s", filename
);
486 g_markup_parse_context_free(context
);
497 prefs_save_cb(const char *name
, PurplePrefType type
, gconstpointer val
,
504 purple_debug_misc("prefs", "%s changed, scheduling save.\n", name
);
506 schedule_prefs_save();
510 get_path_dirname(const char *name
)
514 str
= g_strdup(name
);
516 if ((c
= strrchr(str
, '/')) != NULL
) {
535 get_path_basename(const char *name
)
539 if ((c
= strrchr(name
, '/')) != NULL
)
540 return g_strdup(c
+ 1);
542 return g_strdup(name
);
546 pref_full_name(struct purple_pref
*pref
)
549 struct purple_pref
*parent
;
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
);
582 free_pref_value(struct purple_pref
*pref
)
585 case PURPLE_PREF_BOOLEAN
:
586 pref
->value
.boolean
= FALSE
;
588 case PURPLE_PREF_INT
:
589 pref
->value
.integer
= 0;
591 case PURPLE_PREF_STRING
:
592 case PURPLE_PREF_PATH
:
593 g_free(pref
->value
.string
);
594 pref
->value
.string
= NULL
;
596 case PURPLE_PREF_STRING_LIST
:
597 case PURPLE_PREF_PATH_LIST
:
598 g_list_free_full(pref
->value
.stringlist
, g_free
);
600 case PURPLE_PREF_NONE
:
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
;
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
)) {
626 me
= g_new0(struct purple_pref
, 1);
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
;
637 parent
->first_child
= me
;
640 g_hash_table_insert(prefs_hash
, g_strdup(name
), (gpointer
)me
);
646 purple_prefs_add_none(const char *name
)
648 PURPLE_PREFS_UI_OP_CALL(add_none
, name
);
650 add_pref(PURPLE_PREF_NONE
, name
);
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
);
665 pref
->value
.boolean
= value
;
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
);
680 pref
->value
.integer
= value
;
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
);
693 PURPLE_PREFS_UI_OP_CALL(add_string
, name
, value
);
695 pref
= add_pref(PURPLE_PREF_STRING
, name
);
700 pref
->value
.string
= g_strdup(value
);
704 purple_prefs_add_string_list(const char *name
, GList
*value
)
706 struct purple_pref
*pref
;
709 PURPLE_PREFS_UI_OP_CALL(add_string_list
, name
, value
);
711 pref
= add_pref(PURPLE_PREF_STRING_LIST
, name
);
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
);
721 pref
->value
.stringlist
= g_list_append(pref
->value
.stringlist
,
722 g_strdup(tmp
->data
));
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
);
739 pref
->value
.string
= g_strdup(value
);
743 purple_prefs_add_path_list(const char *name
, GList
*value
)
745 struct purple_pref
*pref
;
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
);
756 for(tmp
= value
; tmp
; tmp
= tmp
->next
)
757 pref
->value
.stringlist
= g_list_append(pref
->value
.stringlist
,
758 g_strdup(tmp
->data
));
763 remove_pref(struct purple_pref
*pref
)
771 while(pref
->first_child
)
772 remove_pref(pref
->first_child
);
777 if(pref
->parent
->first_child
== pref
) {
778 pref
->parent
->first_child
= pref
->sibling
;
780 struct purple_pref
*sib
= pref
->parent
->first_child
;
781 while(sib
&& sib
->sibling
!= pref
)
784 sib
->sibling
= pref
->sibling
;
787 name
= pref_full_name(pref
);
790 purple_debug_info("prefs", "removing pref %s\n", name
);
792 g_hash_table_remove(prefs_hash
, name
);
795 free_pref_value(pref
);
797 while((l
= pref
->callbacks
) != NULL
) {
798 pref
->callbacks
= pref
->callbacks
->next
;
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
);
822 purple_prefs_destroy()
824 purple_prefs_remove("/");
828 do_callbacks(const char* name
, struct purple_pref
*pref
)
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
);
841 do_ui_callbacks(const char *name
)
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:
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
);
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
);
878 pref
= find_pref(name
);
881 purple_debug_error("prefs",
882 "purple_prefs_trigger_callback: Unknown pref %s\n", name
);
886 do_callbacks(name
, pref
);
889 /* this function is deprecated, so it doesn't get the new UI ops */
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
);
900 if(pref
->type
!= PURPLE_PREF_BOOLEAN
) {
901 purple_debug_error("prefs",
902 "purple_prefs_set_bool: %s not a boolean pref\n", name
);
906 if(pref
->value
.boolean
!= value
) {
907 pref
->value
.boolean
= value
;
908 do_callbacks(name
, pref
);
911 purple_prefs_add_bool(name
, value
);
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
);
925 if(pref
->type
!= PURPLE_PREF_INT
) {
926 purple_debug_error("prefs",
927 "purple_prefs_set_int: %s not an integer pref\n", name
);
931 if(pref
->value
.integer
!= value
) {
932 pref
->value
.integer
= value
;
933 do_callbacks(name
, pref
);
936 purple_prefs_add_int(name
, value
);
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
);
950 PURPLE_PREFS_UI_OP_CALL(set_string
, name
, value
);
952 pref
= find_pref(name
);
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
);
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
);
967 purple_prefs_add_string(name
, value
);
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
);
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",
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
);
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
);
1006 purple_prefs_add_string_list(name
, value
);
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
);
1020 if(pref
->type
!= PURPLE_PREF_PATH
) {
1021 purple_debug_error("prefs",
1022 "purple_prefs_set_path: %s not a path pref\n", name
);
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
);
1032 purple_prefs_add_path(name
, value
);
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
);
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",
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
);
1066 purple_prefs_add_path_list(name
, value
);
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
);
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
);
1096 return PURPLE_PREF_NONE
;
1098 return (pref
->type
);
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
);
1111 purple_debug_error("prefs",
1112 "purple_prefs_get_bool: Unknown pref %s\n", name
);
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
);
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
);
1133 purple_debug_error("prefs",
1134 "purple_prefs_get_int: Unknown pref %s\n", name
);
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
);
1142 return pref
->value
.integer
;
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
);
1155 purple_debug_error("prefs",
1156 "purple_prefs_get_string: Unknown pref %s\n", name
);
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
);
1164 return pref
->value
.string
;
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
);
1178 purple_debug_error("prefs",
1179 "purple_prefs_get_string_list: Unknown pref %s\n", name
);
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
);
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
);
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
);
1204 purple_debug_error("prefs",
1205 "purple_prefs_get_path: Unknown pref %s\n", name
);
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
);
1213 return pref
->value
.string
;
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
);
1227 purple_debug_error("prefs",
1228 "purple_prefs_get_path_list: Unknown pref %s\n", name
);
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
);
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
);
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
);
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
);
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
);
1281 purple_debug_info("prefs", "Renaming %s to %s\n", oldname
, newname
);
1284 switch(oldpref
->type
) {
1285 case PURPLE_PREF_NONE
:
1287 case PURPLE_PREF_BOOLEAN
:
1288 purple_prefs_set_bool(newname
, oldpref
->value
.boolean
);
1290 case PURPLE_PREF_INT
:
1291 purple_prefs_set_int(newname
, oldpref
->value
.integer
);
1293 case PURPLE_PREF_STRING
:
1294 purple_prefs_set_string(newname
, oldpref
->value
.string
);
1296 case PURPLE_PREF_STRING_LIST
:
1297 purple_prefs_set_string_list(newname
, oldpref
->value
.stringlist
);
1299 case PURPLE_PREF_PATH
:
1300 purple_prefs_set_path(newname
, oldpref
->value
.string
);
1302 case PURPLE_PREF_PATH_LIST
:
1303 purple_prefs_set_path_list(newname
, oldpref
->value
.stringlist
);
1308 remove_pref(oldpref
);
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)
1321 PURPLE_PREFS_UI_OP_CALL(rename
, oldname
, newname
);
1323 oldpref
= find_pref(oldname
);
1325 /* it's already been renamed, call off the dogs */
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
);
1337 purple_prefs_rename_node(oldpref
, newpref
);
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 */
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
);
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
);
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
);
1374 if (oldpref
->type
!= newpref
->type
)
1376 purple_debug_error("prefs", "Unable to rename %s to %s: differing types\n", oldname
, newname
);
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
);
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
);
1402 purple_debug_error("prefs", "purple_prefs_connect_callback: Unknown pref %s\n", name
);
1407 cb
= g_new0(PurplePrefCallbackData
, 1);
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
);
1425 ui_callbacks
= g_slist_append(ui_callbacks
, cb
);
1427 pref
->callbacks
= g_slist_append(pref
->callbacks
, cb
);
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
);
1443 case PURPLE_PREF_INT
:
1444 if (uiop
->get_int
) {
1445 value
= GINT_TO_POINTER(uiop
->get_int(cb
->name
));
1448 case PURPLE_PREF_BOOLEAN
:
1449 if (uiop
->get_bool
) {
1450 value
= GINT_TO_POINTER(uiop
->get_bool(cb
->name
));
1453 case PURPLE_PREF_STRING
:
1454 case PURPLE_PREF_PATH
:
1455 if (uiop
->get_string
) {
1456 value
= uiop
->get_string(cb
->name
);
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
);
1465 case PURPLE_PREF_NONE
:
1468 purple_debug_error("prefs", "Unexpected type = %i\n", type
);
1471 cb
->func(cb
->name
, type
, value
, cb
->data
);
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
);
1482 purple_prefs_trigger_callback(cb
->name
);
1487 disco_callback_helper(struct purple_pref
*pref
, guint callback_id
)
1490 struct purple_pref
*child
;
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
);
1505 for(child
= pref
->first_child
; child
; child
= child
->sibling
) {
1506 if(disco_callback_helper(child
, callback_id
))
1514 disco_ui_callback_helper(guint callback_id
)
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
);
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
);
1540 disco_callback_helper(&prefs
, callback_id
);
1545 disco_callback_helper_handle(struct purple_pref
*pref
, void *handle
)
1548 struct purple_pref
*child
;
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
);
1560 cbs
= pref
->callbacks
;
1565 for(child
= pref
->first_child
; child
; child
= child
->sibling
)
1566 disco_callback_helper_handle(child
, handle
);
1570 disco_ui_callback_helper_handle(void *handle
)
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
) {
1582 uiop
->disconnect_callback(cb
->name
, cb
->ui_data
);
1584 ui_callbacks
= g_slist_delete_link(ui_callbacks
, cbs
);
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
);
1601 disco_callback_helper_handle(&prefs
, handle
);
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
);
1619 if (name
[strlen(name
) - 1] != '/')
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
));
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");
1670 purple_prefs_get_handle(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");
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");
1705 if (!purple_prefs_get_bool("/purple/away/auto_response/enabled"))
1707 purple_prefs_add_string("/purple/away/auto_reply", "never");
1711 if (purple_prefs_get_bool("/purple/away/auto_response/idle_only"))
1713 purple_prefs_add_string("/purple/away/auto_reply", "awayidle");
1717 purple_prefs_add_string("/purple/away/auto_reply", "away");
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();
1737 purple_prefs_uninit()
1739 if (save_timer
!= 0)
1741 g_source_remove(save_timer
);
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
);
1755 purple_prefs_set_ui_ops(PurplePrefsUiOps
*ops
)
1761 purple_prefs_get_ui_ops(void)
1763 return prefs_ui_ops
;