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
:
599 g_list_foreach(pref
->value
.stringlist
, (GFunc
)g_free
, NULL
);
600 g_list_free(pref
->value
.stringlist
);
602 case PURPLE_PREF_NONE
:
607 static struct purple_pref
*
608 add_pref(PurplePrefType type
, const char *name
)
610 struct purple_pref
*parent
;
611 struct purple_pref
*me
;
612 struct purple_pref
*sibling
;
615 parent
= find_pref_parent(name
);
617 g_return_val_if_fail(parent
, NULL
);
619 my_name
= get_path_basename(name
);
621 for(sibling
= parent
->first_child
; sibling
; sibling
= sibling
->sibling
) {
622 if(purple_strequal(sibling
->name
, my_name
)) {
628 me
= g_new0(struct purple_pref
, 1);
633 if(parent
->first_child
) {
634 /* blatant abuse of a for loop */
635 for(sibling
= parent
->first_child
; sibling
->sibling
;
636 sibling
= sibling
->sibling
);
637 sibling
->sibling
= me
;
639 parent
->first_child
= me
;
642 g_hash_table_insert(prefs_hash
, g_strdup(name
), (gpointer
)me
);
648 purple_prefs_add_none(const char *name
)
650 PURPLE_PREFS_UI_OP_CALL(add_none
, name
);
652 add_pref(PURPLE_PREF_NONE
, name
);
656 purple_prefs_add_bool(const char *name
, gboolean value
)
658 struct purple_pref
*pref
;
660 PURPLE_PREFS_UI_OP_CALL(add_bool
, name
, value
);
662 pref
= add_pref(PURPLE_PREF_BOOLEAN
, name
);
667 pref
->value
.boolean
= value
;
671 purple_prefs_add_int(const char *name
, int value
)
673 struct purple_pref
*pref
;
675 PURPLE_PREFS_UI_OP_CALL(add_int
, name
, value
);
677 pref
= add_pref(PURPLE_PREF_INT
, name
);
682 pref
->value
.integer
= value
;
686 purple_prefs_add_string(const char *name
, const char *value
)
688 struct purple_pref
*pref
;
690 if(value
!= NULL
&& !g_utf8_validate(value
, -1, NULL
)) {
691 purple_debug_error("prefs", "purple_prefs_add_string: Cannot store invalid UTF8 for string pref %s\n", name
);
695 PURPLE_PREFS_UI_OP_CALL(add_string
, name
, value
);
697 pref
= add_pref(PURPLE_PREF_STRING
, name
);
702 pref
->value
.string
= g_strdup(value
);
706 purple_prefs_add_string_list(const char *name
, GList
*value
)
708 struct purple_pref
*pref
;
711 PURPLE_PREFS_UI_OP_CALL(add_string_list
, name
, value
);
713 pref
= add_pref(PURPLE_PREF_STRING_LIST
, name
);
718 for(tmp
= value
; tmp
; tmp
= tmp
->next
) {
719 if(tmp
->data
!= NULL
&& !g_utf8_validate(tmp
->data
, -1, NULL
)) {
720 purple_debug_error("prefs", "purple_prefs_add_string_list: Skipping invalid UTF8 for string list pref %s\n", name
);
723 pref
->value
.stringlist
= g_list_append(pref
->value
.stringlist
,
724 g_strdup(tmp
->data
));
729 purple_prefs_add_path(const char *name
, const char *value
)
731 struct purple_pref
*pref
;
733 /* re-use the string UI OP */
734 PURPLE_PREFS_UI_OP_CALL(add_string
, name
, value
);
736 pref
= add_pref(PURPLE_PREF_PATH
, name
);
741 pref
->value
.string
= g_strdup(value
);
745 purple_prefs_add_path_list(const char *name
, GList
*value
)
747 struct purple_pref
*pref
;
750 /* re-use the string list UI OP */
751 PURPLE_PREFS_UI_OP_CALL(add_string_list
, name
, value
);
753 pref
= add_pref(PURPLE_PREF_PATH_LIST
, name
);
758 for(tmp
= value
; tmp
; tmp
= tmp
->next
)
759 pref
->value
.stringlist
= g_list_append(pref
->value
.stringlist
,
760 g_strdup(tmp
->data
));
765 remove_pref(struct purple_pref
*pref
)
773 while(pref
->first_child
)
774 remove_pref(pref
->first_child
);
779 if(pref
->parent
->first_child
== pref
) {
780 pref
->parent
->first_child
= pref
->sibling
;
782 struct purple_pref
*sib
= pref
->parent
->first_child
;
783 while(sib
&& sib
->sibling
!= pref
)
786 sib
->sibling
= pref
->sibling
;
789 name
= pref_full_name(pref
);
792 purple_debug_info("prefs", "removing pref %s\n", name
);
794 g_hash_table_remove(prefs_hash
, name
);
797 free_pref_value(pref
);
799 while((l
= pref
->callbacks
) != NULL
) {
800 pref
->callbacks
= pref
->callbacks
->next
;
809 purple_prefs_remove(const char *name
)
811 struct purple_pref
*pref
;
813 PURPLE_PREFS_UI_OP_CALL(remove
, name
);
815 pref
= find_pref(name
);
824 purple_prefs_destroy()
826 purple_prefs_remove("/");
830 do_callbacks(const char* name
, struct purple_pref
*pref
)
833 struct purple_pref
*cb_pref
;
834 for(cb_pref
= pref
; cb_pref
; cb_pref
= cb_pref
->parent
) {
835 for(cbs
= cb_pref
->callbacks
; cbs
; cbs
= cbs
->next
) {
836 PurplePrefCallbackData
*cb
= cbs
->data
;
837 cb
->func(name
, pref
->type
, pref
->value
.generic
, cb
->data
);
843 do_ui_callbacks(const char *name
)
847 purple_debug_misc("prefs", "trigger callback %s\n", name
);
849 for (cbs
= ui_callbacks
; cbs
; cbs
= cbs
->next
) {
850 PurplePrefCallbackData
*cb
= cbs
->data
;
851 const char *cb_name
= cb
->name
;
852 size_t len
= strlen(cb_name
);
853 if (!strncmp(cb_name
, name
, len
) &&
854 (name
[len
] == 0 || name
[len
] == '/' ||
855 (len
&& name
[len
- 1] == '/'))) {
856 /* This test should behave like this:
858 * cb_name = /toto/tata --> true
859 * cb_name = /toto/tatatiti --> false
860 * cb_name = / --> true
861 * cb_name = /toto --> true
862 * cb_name = /toto/ --> true
864 purple_prefs_trigger_callback_object(cbs
->data
);
870 purple_prefs_trigger_callback(const char *name
)
872 struct purple_pref
*pref
;
873 PurplePrefsUiOps
*uiop
= purple_prefs_get_ui_ops();
875 if (uiop
&& uiop
->connect_callback
) {
876 do_ui_callbacks(name
);
880 pref
= find_pref(name
);
883 purple_debug_error("prefs",
884 "purple_prefs_trigger_callback: Unknown pref %s\n", name
);
888 do_callbacks(name
, pref
);
891 /* this function is deprecated, so it doesn't get the new UI ops */
893 purple_prefs_set_bool(const char *name
, gboolean value
)
895 struct purple_pref
*pref
;
897 PURPLE_PREFS_UI_OP_CALL(set_bool
, name
, value
);
899 pref
= find_pref(name
);
902 if(pref
->type
!= PURPLE_PREF_BOOLEAN
) {
903 purple_debug_error("prefs",
904 "purple_prefs_set_bool: %s not a boolean pref\n", name
);
908 if(pref
->value
.boolean
!= value
) {
909 pref
->value
.boolean
= value
;
910 do_callbacks(name
, pref
);
913 purple_prefs_add_bool(name
, value
);
918 purple_prefs_set_int(const char *name
, int value
)
920 struct purple_pref
*pref
;
922 PURPLE_PREFS_UI_OP_CALL(set_int
, name
, value
);
924 pref
= find_pref(name
);
927 if(pref
->type
!= PURPLE_PREF_INT
) {
928 purple_debug_error("prefs",
929 "purple_prefs_set_int: %s not an integer pref\n", name
);
933 if(pref
->value
.integer
!= value
) {
934 pref
->value
.integer
= value
;
935 do_callbacks(name
, pref
);
938 purple_prefs_add_int(name
, value
);
943 purple_prefs_set_string(const char *name
, const char *value
)
945 struct purple_pref
*pref
;
947 if(value
!= NULL
&& !g_utf8_validate(value
, -1, NULL
)) {
948 purple_debug_error("prefs", "purple_prefs_set_string: Cannot store invalid UTF8 for string pref %s\n", name
);
952 PURPLE_PREFS_UI_OP_CALL(set_string
, name
, value
);
954 pref
= find_pref(name
);
957 if(pref
->type
!= PURPLE_PREF_STRING
&& pref
->type
!= PURPLE_PREF_PATH
) {
958 purple_debug_error("prefs",
959 "purple_prefs_set_string: %s not a string pref\n", name
);
963 if (!purple_strequal(pref
->value
.string
, value
)) {
964 g_free(pref
->value
.string
);
965 pref
->value
.string
= g_strdup(value
);
966 do_callbacks(name
, pref
);
969 purple_prefs_add_string(name
, value
);
974 purple_prefs_set_string_list(const char *name
, GList
*value
)
976 struct purple_pref
*pref
;
978 PURPLE_PREFS_UI_OP_CALL(set_string_list
, name
, value
);
980 pref
= find_pref(name
);
985 if(pref
->type
!= PURPLE_PREF_STRING_LIST
) {
986 purple_debug_error("prefs",
987 "purple_prefs_set_string_list: %s not a string list pref\n",
992 g_list_foreach(pref
->value
.stringlist
, (GFunc
)g_free
, NULL
);
993 g_list_free(pref
->value
.stringlist
);
994 pref
->value
.stringlist
= NULL
;
996 for(tmp
= value
; tmp
; tmp
= tmp
->next
) {
997 if(tmp
->data
!= NULL
&& !g_utf8_validate(tmp
->data
, -1, NULL
)) {
998 purple_debug_error("prefs", "purple_prefs_set_string_list: Skipping invalid UTF8 for string list pref %s\n", name
);
1001 pref
->value
.stringlist
= g_list_prepend(pref
->value
.stringlist
,
1002 g_strdup(tmp
->data
));
1004 pref
->value
.stringlist
= g_list_reverse(pref
->value
.stringlist
);
1006 do_callbacks(name
, pref
);
1009 purple_prefs_add_string_list(name
, value
);
1014 purple_prefs_set_path(const char *name
, const char *value
)
1016 struct purple_pref
*pref
;
1018 PURPLE_PREFS_UI_OP_CALL(set_string
, name
, value
);
1020 pref
= find_pref(name
);
1023 if(pref
->type
!= PURPLE_PREF_PATH
) {
1024 purple_debug_error("prefs",
1025 "purple_prefs_set_path: %s not a path pref\n", name
);
1029 if (!purple_strequal(pref
->value
.string
, value
)) {
1030 g_free(pref
->value
.string
);
1031 pref
->value
.string
= g_strdup(value
);
1032 do_callbacks(name
, pref
);
1035 purple_prefs_add_path(name
, value
);
1040 purple_prefs_set_path_list(const char *name
, GList
*value
)
1042 struct purple_pref
*pref
;
1044 PURPLE_PREFS_UI_OP_CALL(set_string_list
, name
, value
);
1046 pref
= find_pref(name
);
1051 if(pref
->type
!= PURPLE_PREF_PATH_LIST
) {
1052 purple_debug_error("prefs",
1053 "purple_prefs_set_path_list: %s not a path list pref\n",
1058 g_list_foreach(pref
->value
.stringlist
, (GFunc
)g_free
, NULL
);
1059 g_list_free(pref
->value
.stringlist
);
1060 pref
->value
.stringlist
= NULL
;
1062 for(tmp
= value
; tmp
; tmp
= tmp
->next
)
1063 pref
->value
.stringlist
= g_list_prepend(pref
->value
.stringlist
,
1064 g_strdup(tmp
->data
));
1065 pref
->value
.stringlist
= g_list_reverse(pref
->value
.stringlist
);
1067 do_callbacks(name
, pref
);
1070 purple_prefs_add_path_list(name
, value
);
1076 purple_prefs_exists(const char *name
)
1078 struct purple_pref
*pref
;
1080 PURPLE_PREFS_UI_OP_CALL_RETURN(exists
, name
);
1082 pref
= find_pref(name
);
1091 purple_prefs_get_pref_type(const char *name
)
1093 struct purple_pref
*pref
;
1095 PURPLE_PREFS_UI_OP_CALL_RETURN(get_type
, name
);
1097 pref
= find_pref(name
);
1100 return PURPLE_PREF_NONE
;
1102 return (pref
->type
);
1106 purple_prefs_get_bool(const char *name
)
1108 struct purple_pref
*pref
;
1110 PURPLE_PREFS_UI_OP_CALL_RETURN(get_bool
, name
);
1112 pref
= find_pref(name
);
1115 purple_debug_error("prefs",
1116 "purple_prefs_get_bool: Unknown pref %s\n", name
);
1118 } else if(pref
->type
!= PURPLE_PREF_BOOLEAN
) {
1119 purple_debug_error("prefs",
1120 "purple_prefs_get_bool: %s not a boolean pref\n", name
);
1124 return pref
->value
.boolean
;
1128 purple_prefs_get_int(const char *name
)
1130 struct purple_pref
*pref
;
1132 PURPLE_PREFS_UI_OP_CALL_RETURN(get_int
, name
);
1134 pref
= find_pref(name
);
1137 purple_debug_error("prefs",
1138 "purple_prefs_get_int: Unknown pref %s\n", name
);
1140 } else if(pref
->type
!= PURPLE_PREF_INT
) {
1141 purple_debug_error("prefs",
1142 "purple_prefs_get_int: %s not an integer pref\n", name
);
1146 return pref
->value
.integer
;
1150 purple_prefs_get_string(const char *name
)
1152 struct purple_pref
*pref
;
1154 PURPLE_PREFS_UI_OP_CALL_RETURN(get_string
, name
);
1156 pref
= find_pref(name
);
1159 purple_debug_error("prefs",
1160 "purple_prefs_get_string: Unknown pref %s\n", name
);
1162 } else if(pref
->type
!= PURPLE_PREF_STRING
) {
1163 purple_debug_error("prefs",
1164 "purple_prefs_get_string: %s not a string pref\n", name
);
1168 return pref
->value
.string
;
1172 purple_prefs_get_string_list(const char *name
)
1174 struct purple_pref
*pref
;
1175 GList
*ret
= NULL
, *tmp
;
1177 PURPLE_PREFS_UI_OP_CALL_RETURN(get_string_list
, name
);
1179 pref
= find_pref(name
);
1182 purple_debug_error("prefs",
1183 "purple_prefs_get_string_list: Unknown pref %s\n", name
);
1185 } else if(pref
->type
!= PURPLE_PREF_STRING_LIST
) {
1186 purple_debug_error("prefs",
1187 "purple_prefs_get_string_list: %s not a string list pref\n", name
);
1191 for(tmp
= pref
->value
.stringlist
; tmp
; tmp
= tmp
->next
)
1192 ret
= g_list_prepend(ret
, g_strdup(tmp
->data
));
1193 ret
= g_list_reverse(ret
);
1199 purple_prefs_get_path(const char *name
)
1201 struct purple_pref
*pref
;
1203 PURPLE_PREFS_UI_OP_CALL_RETURN(get_string
, name
);
1205 pref
= find_pref(name
);
1208 purple_debug_error("prefs",
1209 "purple_prefs_get_path: Unknown pref %s\n", name
);
1211 } else if(pref
->type
!= PURPLE_PREF_PATH
) {
1212 purple_debug_error("prefs",
1213 "purple_prefs_get_path: %s not a path pref\n", name
);
1217 return pref
->value
.string
;
1221 purple_prefs_get_path_list(const char *name
)
1223 struct purple_pref
*pref
;
1224 GList
*ret
= NULL
, *tmp
;
1226 PURPLE_PREFS_UI_OP_CALL_RETURN(get_string_list
, name
);
1228 pref
= find_pref(name
);
1231 purple_debug_error("prefs",
1232 "purple_prefs_get_path_list: Unknown pref %s\n", name
);
1234 } else if(pref
->type
!= PURPLE_PREF_PATH_LIST
) {
1235 purple_debug_error("prefs",
1236 "purple_prefs_get_path_list: %s not a path list pref\n", name
);
1240 for(tmp
= pref
->value
.stringlist
; tmp
; tmp
= tmp
->next
)
1241 ret
= g_list_prepend(ret
, g_strdup(tmp
->data
));
1242 ret
= g_list_reverse(ret
);
1248 purple_prefs_rename_node(struct purple_pref
*oldpref
, struct purple_pref
*newpref
)
1250 struct purple_pref
*child
, *next
;
1251 char *oldname
, *newname
;
1253 /* if we're a parent, rename the kids first */
1254 for(child
= oldpref
->first_child
; child
!= NULL
; child
= next
)
1256 struct purple_pref
*newchild
;
1257 next
= child
->sibling
;
1258 for(newchild
= newpref
->first_child
; newchild
!= NULL
; newchild
= newchild
->sibling
)
1260 if(purple_strequal(child
->name
, newchild
->name
))
1262 purple_prefs_rename_node(child
, newchild
);
1266 if(newchild
== NULL
) {
1267 /* no rename happened, we weren't able to find the new pref */
1268 char *tmpname
= pref_full_name(child
);
1269 purple_debug_error("prefs", "Unable to find rename pref for %s\n", tmpname
);
1274 oldname
= pref_full_name(oldpref
);
1275 newname
= pref_full_name(newpref
);
1277 if (oldpref
->type
!= newpref
->type
)
1279 purple_debug_error("prefs", "Unable to rename %s to %s: differing types\n", oldname
, newname
);
1285 purple_debug_info("prefs", "Renaming %s to %s\n", oldname
, newname
);
1288 switch(oldpref
->type
) {
1289 case PURPLE_PREF_NONE
:
1291 case PURPLE_PREF_BOOLEAN
:
1292 purple_prefs_set_bool(newname
, oldpref
->value
.boolean
);
1294 case PURPLE_PREF_INT
:
1295 purple_prefs_set_int(newname
, oldpref
->value
.integer
);
1297 case PURPLE_PREF_STRING
:
1298 purple_prefs_set_string(newname
, oldpref
->value
.string
);
1300 case PURPLE_PREF_STRING_LIST
:
1301 purple_prefs_set_string_list(newname
, oldpref
->value
.stringlist
);
1303 case PURPLE_PREF_PATH
:
1304 purple_prefs_set_path(newname
, oldpref
->value
.string
);
1306 case PURPLE_PREF_PATH_LIST
:
1307 purple_prefs_set_path_list(newname
, oldpref
->value
.stringlist
);
1312 remove_pref(oldpref
);
1316 purple_prefs_rename(const char *oldname
, const char *newname
)
1318 struct purple_pref
*oldpref
, *newpref
;
1320 /* win32dep.h causes rename to be defined as wpurple_rename, so we need to undefine it here */
1321 #if defined(_WIN32) && defined(rename)
1325 PURPLE_PREFS_UI_OP_CALL(rename
, oldname
, newname
);
1327 oldpref
= find_pref(oldname
);
1329 /* it's already been renamed, call off the dogs */
1333 newpref
= find_pref(newname
);
1335 if (newpref
== NULL
)
1337 purple_debug_error("prefs", "Unable to rename %s to %s: new pref not created\n", oldname
, newname
);
1341 purple_prefs_rename_node(oldpref
, newpref
);
1345 purple_prefs_rename_boolean_toggle(const char *oldname
, const char *newname
)
1347 struct purple_pref
*oldpref
, *newpref
;
1349 PURPLE_PREFS_UI_OP_CALL(rename_boolean_toggle
, oldname
, newname
);
1351 oldpref
= find_pref(oldname
);
1353 /* it's already been renamed, call off the cats */
1357 if (oldpref
->type
!= PURPLE_PREF_BOOLEAN
)
1359 purple_debug_error("prefs", "Unable to rename %s to %s: old pref not a boolean\n", oldname
, newname
);
1363 if (oldpref
->first_child
!= NULL
) /* can't rename parents */
1365 purple_debug_error("prefs", "Unable to rename %s to %s: can't rename parents\n", oldname
, newname
);
1370 newpref
= find_pref(newname
);
1372 if (newpref
== NULL
)
1374 purple_debug_error("prefs", "Unable to rename %s to %s: new pref not created\n", oldname
, newname
);
1378 if (oldpref
->type
!= newpref
->type
)
1380 purple_debug_error("prefs", "Unable to rename %s to %s: differing types\n", oldname
, newname
);
1384 purple_debug_info("prefs", "Renaming and toggling %s to %s\n", oldname
, newname
);
1385 purple_prefs_set_bool(newname
, !(oldpref
->value
.boolean
));
1387 remove_pref(oldpref
);
1391 purple_prefs_connect_callback(void *handle
, const char *name
, PurplePrefCallback func
, gpointer data
)
1393 struct purple_pref
*pref
= NULL
;
1394 PurplePrefCallbackData
*cb
;
1395 static guint cb_id
= 0;
1396 PurplePrefsUiOps
*uiop
= NULL
;
1398 g_return_val_if_fail(name
!= NULL
, 0);
1399 g_return_val_if_fail(func
!= NULL
, 0);
1401 uiop
= purple_prefs_get_ui_ops();
1403 if (!(uiop
&& uiop
->connect_callback
)) {
1404 pref
= find_pref(name
);
1406 purple_debug_error("prefs", "purple_prefs_connect_callback: Unknown pref %s\n", name
);
1411 cb
= g_new0(PurplePrefCallbackData
, 1);
1416 cb
->handle
= handle
;
1417 cb
->name
= g_strdup(name
);
1419 if (uiop
&& uiop
->connect_callback
) {
1420 cb
->ui_data
= uiop
->connect_callback(name
, cb
);
1422 if (cb
->ui_data
== NULL
) {
1423 purple_debug_error("prefs", "purple_prefs_connect_callback: connect failed for %s\n", name
);
1429 ui_callbacks
= g_slist_append(ui_callbacks
, cb
);
1431 pref
->callbacks
= g_slist_append(pref
->callbacks
, cb
);
1438 purple_prefs_trigger_ui_callback_object(PurplePrefCallbackData
*cb
)
1440 PurplePrefsUiOps
*uiop
= purple_prefs_get_ui_ops();
1441 gconstpointer value
= NULL
;
1442 PurplePrefType type
= PURPLE_PREF_NONE
;
1444 type
= uiop
->get_type(cb
->name
);
1447 case PURPLE_PREF_INT
:
1448 if (uiop
->get_int
) {
1449 value
= GINT_TO_POINTER(uiop
->get_int(cb
->name
));
1452 case PURPLE_PREF_BOOLEAN
:
1453 if (uiop
->get_bool
) {
1454 value
= GINT_TO_POINTER(uiop
->get_bool(cb
->name
));
1457 case PURPLE_PREF_STRING
:
1458 case PURPLE_PREF_PATH
:
1459 if (uiop
->get_string
) {
1460 value
= uiop
->get_string(cb
->name
);
1463 case PURPLE_PREF_STRING_LIST
:
1464 case PURPLE_PREF_PATH_LIST
:
1465 if (uiop
->get_string_list
) {
1466 value
= uiop
->get_string_list(cb
->name
);
1469 case PURPLE_PREF_NONE
:
1472 purple_debug_error("prefs", "Unexpected type = %i\n", type
);
1475 cb
->func(cb
->name
, type
, value
, cb
->data
);
1479 purple_prefs_trigger_callback_object(PurplePrefCallbackData
*cb
)
1481 PurplePrefsUiOps
*uiop
= purple_prefs_get_ui_ops();
1483 if (uiop
&& uiop
->connect_callback
&& uiop
->get_type
) {
1484 purple_prefs_trigger_ui_callback_object(cb
);
1486 purple_prefs_trigger_callback(cb
->name
);
1491 disco_callback_helper(struct purple_pref
*pref
, guint callback_id
)
1494 struct purple_pref
*child
;
1499 for(cbs
= pref
->callbacks
; cbs
; cbs
= cbs
->next
) {
1500 PurplePrefCallbackData
*cb
= cbs
->data
;
1501 if(cb
->id
== callback_id
) {
1502 pref
->callbacks
= g_slist_delete_link(pref
->callbacks
, cbs
);
1509 for(child
= pref
->first_child
; child
; child
= child
->sibling
) {
1510 if(disco_callback_helper(child
, callback_id
))
1518 disco_ui_callback_helper(guint callback_id
)
1521 PurplePrefsUiOps
*uiop
= purple_prefs_get_ui_ops();
1523 for (cbs
= ui_callbacks
; cbs
; cbs
= cbs
->next
) {
1524 PurplePrefCallbackData
*cb
= cbs
->data
;
1525 if (cb
->id
== callback_id
) {
1526 uiop
->disconnect_callback(cb
->name
, cb
->ui_data
);
1528 ui_callbacks
= g_slist_delete_link(ui_callbacks
, cbs
);
1537 purple_prefs_disconnect_callback(guint callback_id
)
1539 PurplePrefsUiOps
*uiop
= purple_prefs_get_ui_ops();
1541 if (uiop
&& uiop
->disconnect_callback
) {
1542 disco_ui_callback_helper(callback_id
);
1544 disco_callback_helper(&prefs
, callback_id
);
1549 disco_callback_helper_handle(struct purple_pref
*pref
, void *handle
)
1552 struct purple_pref
*child
;
1557 cbs
= pref
->callbacks
;
1558 while (cbs
!= NULL
) {
1559 PurplePrefCallbackData
*cb
= cbs
->data
;
1560 if(cb
->handle
== handle
) {
1561 pref
->callbacks
= g_slist_delete_link(pref
->callbacks
, cbs
);
1564 cbs
= pref
->callbacks
;
1569 for(child
= pref
->first_child
; child
; child
= child
->sibling
)
1570 disco_callback_helper_handle(child
, handle
);
1574 disco_ui_callback_helper_handle(void *handle
)
1577 PurplePrefsUiOps
*uiop
= purple_prefs_get_ui_ops();
1580 for (cbs
= ui_callbacks
; cbs
; cbs
= cbs
->next
) {
1581 PurplePrefCallbackData
*cb
= cbs
->data
;
1582 if (cb
->handle
!= handle
) {
1587 uiop
->disconnect_callback(cb
->name
, cb
->ui_data
);
1589 ui_callbacks
= g_slist_delete_link(ui_callbacks
, cbs
);
1597 purple_prefs_disconnect_by_handle(void *handle
)
1599 PurplePrefsUiOps
*uiop
= purple_prefs_get_ui_ops();
1601 g_return_if_fail(handle
!= NULL
);
1603 if (uiop
&& uiop
->disconnect_callback
) {
1604 disco_ui_callback_helper_handle(handle
);
1606 disco_callback_helper_handle(&prefs
, handle
);
1611 purple_prefs_get_children_names(const char *name
)
1613 GList
* list
= NULL
;
1614 struct purple_pref
*pref
, *child
;
1615 char sep
[2] = "\0\0";;
1617 PURPLE_PREFS_UI_OP_CALL_RETURN(get_children_names
, name
);
1619 pref
= find_pref(name
);
1624 if (name
[strlen(name
) - 1] != '/')
1626 for (child
= pref
->first_child
; child
; child
= child
->sibling
) {
1627 list
= g_list_append(list
, g_strdup_printf("%s%s%s", name
, sep
, child
->name
));
1633 prefs_update_old(void)
1635 purple_prefs_rename("/core", "/purple");
1637 /* Remove some no-longer-used prefs */
1638 purple_prefs_remove("/purple/away/auto_response/enabled");
1639 purple_prefs_remove("/purple/away/auto_response/idle_only");
1640 purple_prefs_remove("/purple/away/auto_response/in_active_conv");
1641 purple_prefs_remove("/purple/away/auto_response/sec_before_resend");
1642 purple_prefs_remove("/purple/away/auto_response");
1643 purple_prefs_remove("/purple/away/default_message");
1644 purple_prefs_remove("/purple/buddies/use_server_alias");
1645 purple_prefs_remove("/purple/conversations/away_back_on_send");
1646 purple_prefs_remove("/purple/conversations/send_urls_as_links");
1647 purple_prefs_remove("/purple/conversations/im/show_login");
1648 purple_prefs_remove("/purple/conversations/chat/show_join");
1649 purple_prefs_remove("/purple/conversations/chat/show_leave");
1650 purple_prefs_remove("/purple/conversations/combine_chat_im");
1651 purple_prefs_remove("/purple/conversations/use_alias_for_title");
1652 purple_prefs_remove("/purple/debug/timestamps");
1653 purple_prefs_remove("/purple/logging/log_signon_signoff");
1654 purple_prefs_remove("/purple/logging/log_idle_state");
1655 purple_prefs_remove("/purple/logging/log_away_state");
1656 purple_prefs_remove("/purple/logging/log_own_states");
1657 purple_prefs_remove("/purple/status/scores/hidden");
1658 purple_prefs_remove("/plugins/core/autorecon/hide_connected_error");
1659 purple_prefs_remove("/plugins/core/autorecon/hide_connecting_error");
1660 purple_prefs_remove("/plugins/core/autorecon/hide_reconnecting_dialog");
1661 purple_prefs_remove("/plugins/core/autorecon/restore_state");
1662 purple_prefs_remove("/plugins/core/autorecon");
1663 purple_prefs_remove("/plugins/lopl");
1665 /* Convert old sounds while_away pref to new 3-way pref. */
1666 if (purple_prefs_exists("/purple/sound/while_away") &&
1667 purple_prefs_get_bool("/purple/sound/while_away"))
1669 purple_prefs_set_int("/purple/sound/while_status", 3);
1671 purple_prefs_remove("/purple/sound/while_away");
1675 purple_prefs_get_handle(void)
1683 purple_prefs_init(void)
1685 void *handle
= purple_prefs_get_handle();
1687 prefs_hash
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
1689 purple_prefs_connect_callback(handle
, "/", prefs_save_cb
, NULL
);
1691 purple_prefs_add_none("/purple");
1692 purple_prefs_add_none("/plugins");
1693 purple_prefs_add_none("/plugins/core");
1694 purple_prefs_add_none("/plugins/prpl");
1697 purple_prefs_add_none("/purple/away");
1698 purple_prefs_add_string("/purple/away/idle_reporting", "system");
1699 purple_prefs_add_bool("/purple/away/away_when_idle", TRUE
);
1700 purple_prefs_add_int("/purple/away/mins_before_away", 5);
1702 /* Away -> Auto-Reply */
1703 if (!purple_prefs_exists("/purple/away/auto_response/enabled") ||
1704 !purple_prefs_exists("/purple/away/auto_response/idle_only"))
1706 purple_prefs_add_string("/purple/away/auto_reply", "awayidle");
1710 if (!purple_prefs_get_bool("/purple/away/auto_response/enabled"))
1712 purple_prefs_add_string("/purple/away/auto_reply", "never");
1716 if (purple_prefs_get_bool("/purple/away/auto_response/idle_only"))
1718 purple_prefs_add_string("/purple/away/auto_reply", "awayidle");
1722 purple_prefs_add_string("/purple/away/auto_reply", "away");
1728 purple_prefs_add_none("/purple/buddies");
1730 /* Contact Priority Settings */
1731 purple_prefs_add_none("/purple/contact");
1732 purple_prefs_add_bool("/purple/contact/last_match", FALSE
);
1733 purple_prefs_remove("/purple/contact/offline_score");
1734 purple_prefs_remove("/purple/contact/away_score");
1735 purple_prefs_remove("/purple/contact/idle_score");
1737 purple_prefs_load();
1742 purple_prefs_uninit()
1744 if (save_timer
!= 0)
1746 g_source_remove(save_timer
);
1750 purple_prefs_disconnect_by_handle(purple_prefs_get_handle());
1752 prefs_loaded
= FALSE
;
1753 purple_prefs_destroy();
1754 g_hash_table_destroy(prefs_hash
);
1760 purple_prefs_set_ui_ops(PurplePrefsUiOps
*ops
)
1766 purple_prefs_get_ui_ops(void)
1768 return prefs_ui_ops
;