* Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA static PurplePrefsUiOps *prefs_ui_ops = NULL; struct _PurplePrefCallbackData { /* TODO: This should use PurpleValues? */ /* 'generic' is kind of ugly. We use it as an elegant way to refer to the value of this pref when calling callback functions. We could use 'boolean' or 'integer' or any other field... but it feels mildly cleaner to use a gpointer. Maybe it would be best to use a struct purple_pref *parent; struct purple_pref *sibling; struct purple_pref *first_child; static struct purple_pref prefs = { static GHashTable *prefs_hash = NULL; static guint save_timer = 0; static gboolean prefs_loaded = FALSE; static GSList *ui_callbacks = NULL; #define PURPLE_PREFS_UI_OP_CALL(member, ...) \ PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops(); \ if (uiop && uiop->member) { \ uiop->member(__VA_ARGS__); \ #define PURPLE_PREFS_UI_OP_CALL_RETURN(member, ...) \ PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops(); \ if (uiop && uiop->member) { \ return uiop->member(__VA_ARGS__); \ /********************************************************************* * Private utility functions * *********************************************************************/ purple_pref *find_pref(const char *name) g_return_val_if_fail(name != NULL && name[0] == '/', NULL); /* When we're initializing, the debug system is * initialized before the prefs system, but debug * calls will end up calling prefs functions, so we * need to deal cleanly here. */ return g_hash_table_lookup(prefs_hash, name); /********************************************************************* *********************************************************************/ * This function recursively creates the PurpleXmlNode tree from the prefs * tree structure. Yay recursion! pref_to_xmlnode(PurpleXmlNode *parent, struct purple_pref *pref) PurpleXmlNode *node, *childnode; struct purple_pref *child; node = purple_xmlnode_new_child(parent, "pref"); purple_xmlnode_set_attrib(node, "name", pref->name); /* Set the type of this node (if type == PURPLE_PREF_NONE then do nothing) */ if (pref->type == PURPLE_PREF_INT) { purple_xmlnode_set_attrib(node, "type", "int"); g_snprintf(buf, sizeof(buf), "%d", pref->value.integer); purple_xmlnode_set_attrib(node, "value", buf); else if (pref->type == PURPLE_PREF_STRING) { purple_xmlnode_set_attrib(node, "type", "string"); purple_xmlnode_set_attrib(node, "value", pref->value.string ? pref->value.string : ""); else if (pref->type == PURPLE_PREF_STRING_LIST) { purple_xmlnode_set_attrib(node, "type", "stringlist"); for (cur = pref->value.stringlist; cur != NULL; cur = cur->next) childnode = purple_xmlnode_new_child(node, "item"); purple_xmlnode_set_attrib(childnode, "value", cur->data ? cur->data : ""); else if (pref->type == PURPLE_PREF_PATH) { char *encoded = g_filename_to_utf8(pref->value.string ? pref->value.string : "", -1, NULL, NULL, NULL); purple_xmlnode_set_attrib(node, "type", "path"); purple_xmlnode_set_attrib(node, "value", encoded); else if (pref->type == PURPLE_PREF_PATH_LIST) { purple_xmlnode_set_attrib(node, "type", "pathlist"); for (cur = pref->value.stringlist; cur != NULL; cur = cur->next) char *encoded = g_filename_to_utf8(cur->data ? cur->data : "", -1, NULL, NULL, NULL); childnode = purple_xmlnode_new_child(node, "item"); purple_xmlnode_set_attrib(childnode, "value", encoded); else if (pref->type == PURPLE_PREF_BOOLEAN) { purple_xmlnode_set_attrib(node, "type", "bool"); g_snprintf(buf, sizeof(buf), "%d", pref->value.boolean); purple_xmlnode_set_attrib(node, "value", buf); for (child = pref->first_child; child != NULL; child = child->sibling) pref_to_xmlnode(node, child); struct purple_pref *pref, *child; /* Create the root preference node */ node = purple_xmlnode_new("pref"); purple_xmlnode_set_attrib(node, "version", "1"); purple_xmlnode_set_attrib(node, "name", "/"); for (child = pref->first_child; child != NULL; child = child->sibling) pref_to_xmlnode(node, child); * TODO: Call schedule_prefs_save()? Ideally we wouldn't need to. * (prefs.xml should be loaded when purple_prefs_init is called) purple_debug_error("prefs", "Attempted to save prefs before " PURPLE_PREFS_UI_OP_CALL(save); node = prefs_to_xmlnode(); data = purple_xmlnode_to_formatted_str(node, NULL); purple_util_write_data_to_config_file("prefs.xml", data, -1); purple_xmlnode_free(node); schedule_prefs_save(void) PURPLE_PREFS_UI_OP_CALL(schedule_save); save_timer = g_timeout_add_seconds(5, save_cb, NULL); /********************************************************************* *********************************************************************/ static GList *prefs_stack = NULL; prefs_start_element_handler (GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, PurplePrefType pref_type = PURPLE_PREF_NONE; const char *pref_name = NULL, *pref_value = NULL; if(!purple_strequal(element_name, "pref") && !purple_strequal(element_name, "item")) for(i = 0; attribute_names[i]; i++) { if(purple_strequal(attribute_names[i], "name")) { pref_name = attribute_values[i]; } else if(purple_strequal(attribute_names[i], "type")) { if(purple_strequal(attribute_values[i], "bool")) pref_type = PURPLE_PREF_BOOLEAN; else if(purple_strequal(attribute_values[i], "int")) pref_type = PURPLE_PREF_INT; else if(purple_strequal(attribute_values[i], "string")) pref_type = PURPLE_PREF_STRING; else if(purple_strequal(attribute_values[i], "stringlist")) pref_type = PURPLE_PREF_STRING_LIST; else if(purple_strequal(attribute_values[i], "path")) pref_type = PURPLE_PREF_PATH; else if(purple_strequal(attribute_values[i], "pathlist")) pref_type = PURPLE_PREF_PATH_LIST; } else if(purple_strequal(attribute_names[i], "value")) { pref_value = attribute_values[i]; if ((pref_type == PURPLE_PREF_BOOLEAN || pref_type == PURPLE_PREF_INT) && /* Missing a value attribute */ if(purple_strequal(element_name, "item")) { struct purple_pref *pref; pref_name_full = g_string_new(""); for(tmp = prefs_stack; tmp; tmp = tmp->next) { pref_name_full = g_string_prepend(pref_name_full, tmp->data); pref_name_full = g_string_prepend_c(pref_name_full, '/'); pref = find_pref(pref_name_full->str); if(pref->type == PURPLE_PREF_STRING_LIST) { pref->value.stringlist = g_list_append(pref->value.stringlist, } else if(pref->type == PURPLE_PREF_PATH_LIST) { pref->value.stringlist = g_list_append(pref->value.stringlist, g_filename_from_utf8(pref_value, -1, NULL, NULL, NULL)); g_string_free(pref_name_full, TRUE); if(!pref_name || purple_strequal(pref_name, "/")) pref_name_full = g_string_new(pref_name); for(tmp = prefs_stack; tmp; tmp = tmp->next) { pref_name_full = g_string_prepend_c(pref_name_full, '/'); pref_name_full = g_string_prepend(pref_name_full, tmp->data); pref_name_full = g_string_prepend_c(pref_name_full, '/'); purple_prefs_add_none(pref_name_full->str); case PURPLE_PREF_BOOLEAN: purple_prefs_set_bool(pref_name_full->str, atoi(pref_value)); purple_prefs_set_int(pref_name_full->str, atoi(pref_value)); purple_prefs_set_string(pref_name_full->str, pref_value); case PURPLE_PREF_STRING_LIST: purple_prefs_set_string_list(pref_name_full->str, NULL); decoded = g_filename_from_utf8(pref_value, -1, NULL, NULL, NULL); purple_prefs_set_path(pref_name_full->str, decoded); purple_prefs_set_path(pref_name_full->str, NULL); case PURPLE_PREF_PATH_LIST: purple_prefs_set_path_list(pref_name_full->str, NULL); prefs_stack = g_list_prepend(prefs_stack, g_strdup(pref_name)); g_string_free(pref_name_full, TRUE); prefs_end_element_handler(GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) if(prefs_stack && purple_strequal(element_name, "pref")) { g_free(prefs_stack->data); prefs_stack = g_list_delete_link(prefs_stack, prefs_stack); static GMarkupParser prefs_parser = { prefs_start_element_handler, prefs_end_element_handler, GMarkupParseContext *context; PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops(); if (uiop && uiop->load) { filename = g_build_filename(purple_config_dir(), "prefs.xml", NULL); purple_debug_misc("prefs", "Reading %s", filename); if(!g_file_get_contents(filename, &contents, &length, &error)) { const gchar *sysconfdir = PURPLE_SYSCONFDIR; /* coverity dead_error_line false positive */ filename = g_build_filename(sysconfdir, "purple", "prefs.xml", NULL); purple_debug_info("prefs", "Reading %s\n", filename); if (!g_file_get_contents(filename, &contents, &length, &error)) { purple_debug_error("prefs", "Error reading prefs: %s\n", context = g_markup_parse_context_new(&prefs_parser, 0, NULL, NULL); if(!g_markup_parse_context_parse(context, contents, length, NULL)) { g_markup_parse_context_free(context); if(!g_markup_parse_context_end_parse(context, NULL)) { purple_debug_error("prefs", "Error parsing %s\n", filename); g_markup_parse_context_free(context); if (purple_debug_is_verbose()) purple_debug_misc("prefs", "Finished reading %s", filename); g_markup_parse_context_free(context); prefs_save_cb(const char *name, PurplePrefType type, gconstpointer val, purple_debug_misc("prefs", "%s changed, scheduling save.\n", name); get_path_dirname(const char *name) if ((c = strrchr(str, '/')) != NULL) { get_path_basename(const char *name) if ((c = strrchr(name, '/')) != NULL) pref_full_name(struct purple_pref *pref) struct purple_pref *parent; name = g_string_new(pref->name); for(parent = pref->parent; parent && parent->name; parent = parent->parent) { name = g_string_prepend_c(name, '/'); name = g_string_prepend(name, parent->name); name = g_string_prepend_c(name, '/'); return g_string_free(name, FALSE); static struct purple_pref * find_pref_parent(const char *name) char *parent_name = get_path_dirname(name); struct purple_pref *ret = &prefs; if(!purple_strequal(parent_name, "/")) { ret = find_pref(parent_name); free_pref_value(struct purple_pref *pref) case PURPLE_PREF_BOOLEAN: pref->value.boolean = FALSE; g_free(pref->value.string); pref->value.string = NULL; case PURPLE_PREF_STRING_LIST: case PURPLE_PREF_PATH_LIST: g_list_free_full(pref->value.stringlist, g_free); static struct purple_pref * add_pref(PurplePrefType type, const char *name) struct purple_pref *parent; struct purple_pref *sibling; parent = find_pref_parent(name); g_return_val_if_fail(parent, NULL); my_name = get_path_basename(name); for(sibling = parent->first_child; sibling; sibling = sibling->sibling) { if(purple_strequal(sibling->name, my_name)) { me = g_new0(struct purple_pref, 1); if(parent->first_child) { /* blatant abuse of a for loop */ for(sibling = parent->first_child; sibling->sibling; sibling = sibling->sibling); parent->first_child = me; g_hash_table_insert(prefs_hash, g_strdup(name), (gpointer)me); purple_prefs_add_none(const char *name) PURPLE_PREFS_UI_OP_CALL(add_none, name); add_pref(PURPLE_PREF_NONE, name); purple_prefs_add_bool(const char *name, gboolean value) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL(add_bool, name, value); pref = add_pref(PURPLE_PREF_BOOLEAN, name); pref->value.boolean = value; purple_prefs_add_int(const char *name, int value) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL(add_int, name, value); pref = add_pref(PURPLE_PREF_INT, name); pref->value.integer = value; purple_prefs_add_string(const char *name, const char *value) struct purple_pref *pref; if(value != NULL && !g_utf8_validate(value, -1, NULL)) { purple_debug_error("prefs", "purple_prefs_add_string: Cannot store invalid UTF8 for string pref %s\n", name); PURPLE_PREFS_UI_OP_CALL(add_string, name, value); pref = add_pref(PURPLE_PREF_STRING, name); pref->value.string = g_strdup(value); purple_prefs_add_string_list(const char *name, GList *value) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL(add_string_list, name, value); pref = add_pref(PURPLE_PREF_STRING_LIST, name); for(tmp = value; tmp; tmp = tmp->next) { if(tmp->data != NULL && !g_utf8_validate(tmp->data, -1, NULL)) { purple_debug_error("prefs", "purple_prefs_add_string_list: Skipping invalid UTF8 for string list pref %s\n", name); pref->value.stringlist = g_list_append(pref->value.stringlist, purple_prefs_add_path(const char *name, const char *value) struct purple_pref *pref; /* re-use the string UI OP */ PURPLE_PREFS_UI_OP_CALL(add_string, name, value); pref = add_pref(PURPLE_PREF_PATH, name); pref->value.string = g_strdup(value); purple_prefs_add_path_list(const char *name, GList *value) struct purple_pref *pref; /* re-use the string list UI OP */ PURPLE_PREFS_UI_OP_CALL(add_string_list, name, value); pref = add_pref(PURPLE_PREF_PATH_LIST, name); for(tmp = value; tmp; tmp = tmp->next) pref->value.stringlist = g_list_append(pref->value.stringlist, remove_pref(struct purple_pref *pref) remove_pref(pref->first_child); if(pref->parent->first_child == pref) { pref->parent->first_child = pref->sibling; struct purple_pref *sib = pref->parent->first_child; while(sib && sib->sibling != pref) sib->sibling = pref->sibling; name = pref_full_name(pref); purple_debug_info("prefs", "removing pref %s\n", name); g_hash_table_remove(prefs_hash, name); g_slist_free_full(pref->callbacks, g_free); purple_prefs_remove(const char *name) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL(remove, name); purple_prefs_remove("/"); do_callbacks(const char* name, struct purple_pref *pref) struct purple_pref *cb_pref; for(cb_pref = pref; cb_pref; cb_pref = cb_pref->parent) { for(cbs = cb_pref->callbacks; cbs; cbs = cbs->next) { PurplePrefCallbackData *cb = cbs->data; cb->func(name, pref->type, pref->value.generic, cb->data); do_ui_callbacks(const char *name) purple_debug_misc("prefs", "trigger callback %s\n", name); for (cbs = ui_callbacks; cbs; cbs = cbs->next) { PurplePrefCallbackData *cb = cbs->data; const char *cb_name = cb->name; size_t len = strlen(cb_name); if (!strncmp(cb_name, name, len) && (name[len] == 0 || name[len] == '/' || (len && name[len - 1] == '/'))) { /* This test should behave like this: * cb_name = /toto/tata --> true * cb_name = /toto/tatatiti --> false * cb_name = /toto --> true * cb_name = /toto/ --> true purple_prefs_trigger_callback_object(cbs->data); purple_prefs_trigger_callback(const char *name) struct purple_pref *pref; PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops(); if (uiop && uiop->connect_callback) { purple_debug_error("prefs", "purple_prefs_trigger_callback: Unknown pref %s\n", name); do_callbacks(name, pref); /* this function is deprecated, so it doesn't get the new UI ops */ purple_prefs_set_bool(const char *name, gboolean value) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL(set_bool, name, value); if(pref->type != PURPLE_PREF_BOOLEAN) { purple_debug_error("prefs", "purple_prefs_set_bool: %s not a boolean pref\n", name); if(pref->value.boolean != value) { pref->value.boolean = value; do_callbacks(name, pref); purple_prefs_add_bool(name, value); purple_prefs_set_int(const char *name, int value) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL(set_int, name, value); if(pref->type != PURPLE_PREF_INT) { purple_debug_error("prefs", "purple_prefs_set_int: %s not an integer pref\n", name); if(pref->value.integer != value) { pref->value.integer = value; do_callbacks(name, pref); purple_prefs_add_int(name, value); purple_prefs_set_string(const char *name, const char *value) struct purple_pref *pref; if(value != NULL && !g_utf8_validate(value, -1, NULL)) { purple_debug_error("prefs", "purple_prefs_set_string: Cannot store invalid UTF8 for string pref %s\n", name); PURPLE_PREFS_UI_OP_CALL(set_string, name, value); if(pref->type != PURPLE_PREF_STRING && pref->type != PURPLE_PREF_PATH) { purple_debug_error("prefs", "purple_prefs_set_string: %s not a string pref\n", name); if (!purple_strequal(pref->value.string, value)) { g_free(pref->value.string); pref->value.string = g_strdup(value); do_callbacks(name, pref); purple_prefs_add_string(name, value); purple_prefs_set_string_list(const char *name, GList *value) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL(set_string_list, name, value); if(pref->type != PURPLE_PREF_STRING_LIST) { purple_debug_error("prefs", "purple_prefs_set_string_list: %s not a string list pref\n", g_list_free_full(pref->value.stringlist, g_free); pref->value.stringlist = NULL; for(tmp = value; tmp; tmp = tmp->next) { if(tmp->data != NULL && !g_utf8_validate(tmp->data, -1, NULL)) { purple_debug_error("prefs", "purple_prefs_set_string_list: Skipping invalid UTF8 for string list pref %s\n", name); pref->value.stringlist = g_list_prepend(pref->value.stringlist, pref->value.stringlist = g_list_reverse(pref->value.stringlist); do_callbacks(name, pref); purple_prefs_add_string_list(name, value); purple_prefs_set_path(const char *name, const char *value) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL(set_string, name, value); if(pref->type != PURPLE_PREF_PATH) { purple_debug_error("prefs", "purple_prefs_set_path: %s not a path pref\n", name); if (!purple_strequal(pref->value.string, value)) { g_free(pref->value.string); pref->value.string = g_strdup(value); do_callbacks(name, pref); purple_prefs_add_path(name, value); purple_prefs_set_path_list(const char *name, GList *value) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL(set_string_list, name, value); if(pref->type != PURPLE_PREF_PATH_LIST) { purple_debug_error("prefs", "purple_prefs_set_path_list: %s not a path list pref\n", g_list_free_full(pref->value.stringlist, g_free); pref->value.stringlist = NULL; for(tmp = value; tmp; tmp = tmp->next) pref->value.stringlist = g_list_prepend(pref->value.stringlist, pref->value.stringlist = g_list_reverse(pref->value.stringlist); do_callbacks(name, pref); purple_prefs_add_path_list(name, value); purple_prefs_exists(const char *name) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL_RETURN(exists, name); purple_prefs_get_pref_type(const char *name) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL_RETURN(get_type, name); purple_prefs_get_bool(const char *name) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL_RETURN(get_bool, name); purple_debug_error("prefs", "purple_prefs_get_bool: Unknown pref %s\n", name); } else if(pref->type != PURPLE_PREF_BOOLEAN) { purple_debug_error("prefs", "purple_prefs_get_bool: %s not a boolean pref\n", name); return pref->value.boolean; purple_prefs_get_int(const char *name) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL_RETURN(get_int, name); purple_debug_error("prefs", "purple_prefs_get_int: Unknown pref %s\n", name); } else if(pref->type != PURPLE_PREF_INT) { purple_debug_error("prefs", "purple_prefs_get_int: %s not an integer pref\n", name); return pref->value.integer; purple_prefs_get_string(const char *name) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL_RETURN(get_string, name); purple_debug_error("prefs", "purple_prefs_get_string: Unknown pref %s\n", name); } else if(pref->type != PURPLE_PREF_STRING) { purple_debug_error("prefs", "purple_prefs_get_string: %s not a string pref\n", name); return pref->value.string; purple_prefs_get_string_list(const char *name) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL_RETURN(get_string_list, name); purple_debug_error("prefs", "purple_prefs_get_string_list: Unknown pref %s\n", name); } else if(pref->type != PURPLE_PREF_STRING_LIST) { purple_debug_error("prefs", "purple_prefs_get_string_list: %s not a string list pref\n", name); for(tmp = pref->value.stringlist; tmp; tmp = tmp->next) ret = g_list_prepend(ret, g_strdup(tmp->data)); ret = g_list_reverse(ret); purple_prefs_get_path(const char *name) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL_RETURN(get_string, name); purple_debug_error("prefs", "purple_prefs_get_path: Unknown pref %s\n", name); } else if(pref->type != PURPLE_PREF_PATH) { purple_debug_error("prefs", "purple_prefs_get_path: %s not a path pref\n", name); return pref->value.string; purple_prefs_get_path_list(const char *name) struct purple_pref *pref; PURPLE_PREFS_UI_OP_CALL_RETURN(get_string_list, name); purple_debug_error("prefs", "purple_prefs_get_path_list: Unknown pref %s\n", name); } else if(pref->type != PURPLE_PREF_PATH_LIST) { purple_debug_error("prefs", "purple_prefs_get_path_list: %s not a path list pref\n", name); for(tmp = pref->value.stringlist; tmp; tmp = tmp->next) ret = g_list_prepend(ret, g_strdup(tmp->data)); ret = g_list_reverse(ret); purple_prefs_rename_node(struct purple_pref *oldpref, struct purple_pref *newpref) struct purple_pref *child, *next; /* if we're a parent, rename the kids first */ for(child = oldpref->first_child; child != NULL; child = next) struct purple_pref *newchild; for(newchild = newpref->first_child; newchild != NULL; newchild = newchild->sibling) if(purple_strequal(child->name, newchild->name)) purple_prefs_rename_node(child, newchild); /* no rename happened, we weren't able to find the new pref */ char *tmpname = pref_full_name(child); purple_debug_error("prefs", "Unable to find rename pref for %s\n", tmpname); oldname = pref_full_name(oldpref); newname = pref_full_name(newpref); if (oldpref->type != newpref->type) purple_debug_error("prefs", "Unable to rename %s to %s: differing types\n", oldname, newname); purple_debug_info("prefs", "Renaming %s to %s\n", oldname, newname); case PURPLE_PREF_BOOLEAN: purple_prefs_set_bool(newname, oldpref->value.boolean); purple_prefs_set_int(newname, oldpref->value.integer); purple_prefs_set_string(newname, oldpref->value.string); case PURPLE_PREF_STRING_LIST: purple_prefs_set_string_list(newname, oldpref->value.stringlist); purple_prefs_set_path(newname, oldpref->value.string); case PURPLE_PREF_PATH_LIST: purple_prefs_set_path_list(newname, oldpref->value.stringlist); purple_prefs_rename(const char *oldname, const char *newname) struct purple_pref *oldpref, *newpref; PURPLE_PREFS_UI_OP_CALL(rename, oldname, newname); oldpref = find_pref(oldname); /* it's already been renamed, call off the dogs */ newpref = find_pref(newname); purple_debug_error("prefs", "Unable to rename %s to %s: new pref not created\n", oldname, newname); purple_prefs_rename_node(oldpref, newpref); purple_prefs_rename_boolean_toggle(const char *oldname, const char *newname) struct purple_pref *oldpref, *newpref; PURPLE_PREFS_UI_OP_CALL(rename_boolean_toggle, oldname, newname); oldpref = find_pref(oldname); /* it's already been renamed, call off the cats */ if (oldpref->type != PURPLE_PREF_BOOLEAN) purple_debug_error("prefs", "Unable to rename %s to %s: old pref not a boolean\n", oldname, newname); if (oldpref->first_child != NULL) /* can't rename parents */ purple_debug_error("prefs", "Unable to rename %s to %s: can't rename parents\n", oldname, newname); newpref = find_pref(newname); purple_debug_error("prefs", "Unable to rename %s to %s: new pref not created\n", oldname, newname); if (oldpref->type != newpref->type) purple_debug_error("prefs", "Unable to rename %s to %s: differing types\n", oldname, newname); purple_debug_info("prefs", "Renaming and toggling %s to %s\n", oldname, newname); purple_prefs_set_bool(newname, !(oldpref->value.boolean)); purple_prefs_connect_callback(void *handle, const char *name, PurplePrefCallback func, gpointer data) struct purple_pref *pref = NULL; PurplePrefCallbackData *cb; PurplePrefsUiOps *uiop = NULL; g_return_val_if_fail(name != NULL, 0); g_return_val_if_fail(func != NULL, 0); uiop = purple_prefs_get_ui_ops(); if (!(uiop && uiop->connect_callback)) { purple_debug_error("prefs", "purple_prefs_connect_callback: Unknown pref %s\n", name); cb = g_new0(PurplePrefCallbackData, 1); cb->name = g_strdup(name); if (uiop && uiop->connect_callback) { cb->ui_data = uiop->connect_callback(name, cb); if (cb->ui_data == NULL) { purple_debug_error("prefs", "purple_prefs_connect_callback: connect failed for %s\n", name); ui_callbacks = g_slist_append(ui_callbacks, cb); pref->callbacks = g_slist_append(pref->callbacks, cb); purple_prefs_trigger_ui_callback_object(PurplePrefCallbackData *cb) PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops(); gconstpointer value = NULL; PurplePrefType type = PURPLE_PREF_NONE; type = uiop->get_type(cb->name); value = GINT_TO_POINTER(uiop->get_int(cb->name)); case PURPLE_PREF_BOOLEAN: value = GINT_TO_POINTER(uiop->get_bool(cb->name)); value = uiop->get_string(cb->name); case PURPLE_PREF_STRING_LIST: case PURPLE_PREF_PATH_LIST: if (uiop->get_string_list) { value = uiop->get_string_list(cb->name); purple_debug_error("prefs", "Unexpected type = %i\n", type); cb->func(cb->name, type, value, cb->data); purple_prefs_trigger_callback_object(PurplePrefCallbackData *cb) PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops(); if (uiop && uiop->connect_callback && uiop->get_type) { purple_prefs_trigger_ui_callback_object(cb); purple_prefs_trigger_callback(cb->name); disco_callback_helper(struct purple_pref *pref, guint callback_id) struct purple_pref *child; for(cbs = pref->callbacks; cbs; cbs = cbs->next) { PurplePrefCallbackData *cb = cbs->data; if(cb->id == callback_id) { pref->callbacks = g_slist_delete_link(pref->callbacks, cbs); for(child = pref->first_child; child; child = child->sibling) { if(disco_callback_helper(child, callback_id)) disco_ui_callback_helper(guint callback_id) PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops(); for (cbs = ui_callbacks; cbs; cbs = cbs->next) { PurplePrefCallbackData *cb = cbs->data; if (cb->id == callback_id) { uiop->disconnect_callback(cb->name, cb->ui_data); ui_callbacks = g_slist_delete_link(ui_callbacks, cbs); purple_prefs_disconnect_callback(guint callback_id) PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops(); if (uiop && uiop->disconnect_callback) { disco_ui_callback_helper(callback_id); disco_callback_helper(&prefs, callback_id); disco_callback_helper_handle(struct purple_pref *pref, void *handle) struct purple_pref *child; PurplePrefCallbackData *cb = cbs->data; if(cb->handle == handle) { pref->callbacks = g_slist_delete_link(pref->callbacks, cbs); for(child = pref->first_child; child; child = child->sibling) disco_callback_helper_handle(child, handle); disco_ui_callback_helper_handle(void *handle) PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops(); for (cbs = ui_callbacks; cbs; cbs = cbs->next) { PurplePrefCallbackData *cb = cbs->data; if (cb->handle != handle) { uiop->disconnect_callback(cb->name, cb->ui_data); ui_callbacks = g_slist_delete_link(ui_callbacks, cbs); purple_prefs_disconnect_by_handle(void *handle) PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops(); g_return_if_fail(handle != NULL); if (uiop && uiop->disconnect_callback) { disco_ui_callback_helper_handle(handle); disco_callback_helper_handle(&prefs, handle); purple_prefs_get_children_names(const char *name) struct purple_pref *pref, *child; PURPLE_PREFS_UI_OP_CALL_RETURN(get_children_names, name); if (name[strlen(name) - 1] != '/') for (child = pref->first_child; child; child = child->sibling) { list = g_list_append(list, g_strdup_printf("%s%s%s", name, sep, child->name)); purple_prefs_rename("/core", "/purple"); /* Remove some no-longer-used prefs */ purple_prefs_remove("/purple/away/auto_response/enabled"); purple_prefs_remove("/purple/away/auto_response/idle_only"); purple_prefs_remove("/purple/away/auto_response/in_active_conv"); purple_prefs_remove("/purple/away/auto_response/sec_before_resend"); purple_prefs_remove("/purple/away/auto_response"); purple_prefs_remove("/purple/away/default_message"); purple_prefs_remove("/purple/buddies/use_server_alias"); purple_prefs_remove("/purple/conversations/away_back_on_send"); purple_prefs_remove("/purple/conversations/send_urls_as_links"); purple_prefs_remove("/purple/conversations/im/show_login"); purple_prefs_remove("/purple/conversations/chat/show_join"); purple_prefs_remove("/purple/conversations/chat/show_leave"); purple_prefs_remove("/purple/conversations/combine_chat_im"); purple_prefs_remove("/purple/conversations/use_alias_for_title"); purple_prefs_remove("/purple/debug/timestamps"); purple_prefs_remove("/purple/logging/log_signon_signoff"); purple_prefs_remove("/purple/logging/log_idle_state"); purple_prefs_remove("/purple/logging/log_away_state"); purple_prefs_remove("/purple/logging/log_own_states"); purple_prefs_remove("/purple/status/scores/hidden"); purple_prefs_remove("/plugins/core/autorecon/hide_connected_error"); purple_prefs_remove("/plugins/core/autorecon/hide_connecting_error"); purple_prefs_remove("/plugins/core/autorecon/hide_reconnecting_dialog"); purple_prefs_remove("/plugins/core/autorecon/restore_state"); purple_prefs_remove("/plugins/core/autorecon"); purple_prefs_remove("/plugins/lopl"); /* Convert old sounds while_away pref to new 3-way pref. */ if (purple_prefs_exists("/purple/sound/while_away") && purple_prefs_get_bool("/purple/sound/while_away")) purple_prefs_set_int("/purple/sound/while_status", 3); purple_prefs_remove("/purple/sound/while_away"); purple_prefs_get_handle(void) void *handle = purple_prefs_get_handle(); prefs_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); purple_prefs_connect_callback(handle, "/", prefs_save_cb, NULL); purple_prefs_add_none("/purple"); purple_prefs_add_none("/plugins"); purple_prefs_add_none("/plugins/core"); purple_prefs_add_none("/plugins/prpl"); purple_prefs_add_none("/purple/away"); purple_prefs_add_string("/purple/away/idle_reporting", "system"); purple_prefs_add_bool("/purple/away/away_when_idle", TRUE); purple_prefs_add_int("/purple/away/mins_before_away", 5); if (!purple_prefs_exists("/purple/away/auto_response/enabled") || !purple_prefs_exists("/purple/away/auto_response/idle_only")) purple_prefs_add_string("/purple/away/auto_reply", "awayidle"); if (!purple_prefs_get_bool("/purple/away/auto_response/enabled")) purple_prefs_add_string("/purple/away/auto_reply", "never"); if (purple_prefs_get_bool("/purple/away/auto_response/idle_only")) purple_prefs_add_string("/purple/away/auto_reply", "awayidle"); purple_prefs_add_string("/purple/away/auto_reply", "away"); purple_prefs_add_none("/purple/buddies"); /* Contact Priority Settings */ purple_prefs_add_none("/purple/contact"); purple_prefs_add_bool("/purple/contact/last_match", FALSE); purple_prefs_remove("/purple/contact/offline_score"); purple_prefs_remove("/purple/contact/away_score"); purple_prefs_remove("/purple/contact/idle_score"); g_source_remove(save_timer); purple_prefs_disconnect_by_handle(purple_prefs_get_handle()); g_hash_table_destroy(prefs_hash); purple_prefs_set_ui_ops(PurplePrefsUiOps *ops) purple_prefs_get_ui_ops(void)