| 1 /* |
|
| 2 * Purple - Internet Messaging Library |
|
| 3 * Copyright (C) Pidgin Developers <devel@pidgin.im> |
|
| 4 * |
|
| 5 * Purple is the legal property of its developers, whose names are too numerous |
|
| 6 * to list here. Please refer to the COPYRIGHT file distributed with this |
|
| 7 * source distribution. |
|
| 8 * |
|
| 9 * This library is free software; you can redistribute it and/or modify it |
|
| 10 * under the terms of the GNU General Public License as published by the Free |
|
| 11 * Software Foundation; either version 2 of the License, or (at your option) |
|
| 12 * any later version. |
|
| 13 * |
|
| 14 * This library is distributed in the hope that it will be useful, but WITHOUT |
|
| 15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
| 16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
|
| 17 * more details. |
|
| 18 * |
|
| 19 * You should have received a copy of the GNU General Public License along with |
|
| 20 * this library; if not, see <https://www.gnu.org/licenses/>. |
|
| 21 */ |
|
| 22 |
|
| 23 #include <glib/gi18n-lib.h> |
|
| 24 |
|
| 25 #include "buddylist.h" |
|
| 26 #include "debug.h" |
|
| 27 #include "notify.h" |
|
| 28 #include "prefs.h" |
|
| 29 #include "purpleaccount.h" |
|
| 30 #include "purpleaccountmanager.h" |
|
| 31 #include "purpleprivate.h" |
|
| 32 #include "purpleprotocol.h" |
|
| 33 #include "purpleprotocolclient.h" |
|
| 34 #include "purpleconversation.h" |
|
| 35 #include "signals.h" |
|
| 36 #include "util.h" |
|
| 37 #include "xmlnode.h" |
|
| 38 |
|
| 39 /* Private data for a buddy list. */ |
|
| 40 typedef struct { |
|
| 41 PurpleBlistNode *root; |
|
| 42 GHashTable *buddies; /* Every buddy in this list */ |
|
| 43 } PurpleBuddyListPrivate; |
|
| 44 |
|
| 45 static GType buddy_list_type = G_TYPE_INVALID; |
|
| 46 static PurpleBuddyList *purplebuddylist = NULL; |
|
| 47 |
|
| 48 G_DEFINE_TYPE_WITH_PRIVATE(PurpleBuddyList, purple_buddy_list, G_TYPE_OBJECT); |
|
| 49 |
|
| 50 /* |
|
| 51 * A hash table used for efficient lookups of buddies by name. |
|
| 52 * PurpleAccount* => GHashTable*, with the inner hash table being |
|
| 53 * struct _purple_hbuddy => PurpleBuddy* |
|
| 54 */ |
|
| 55 static GHashTable *buddies_cache = NULL; |
|
| 56 |
|
| 57 /* |
|
| 58 * A hash table used for efficient lookups of groups by name. |
|
| 59 * UTF-8 collate-key => PurpleGroup*. |
|
| 60 */ |
|
| 61 static GHashTable *groups_cache = NULL; |
|
| 62 |
|
| 63 static guint save_timer = 0; |
|
| 64 static gboolean blist_loaded = FALSE; |
|
| 65 static gchar *localized_default_group_name = NULL; |
|
| 66 |
|
| 67 /********************************************************************* |
|
| 68 * Private utility functions * |
|
| 69 *********************************************************************/ |
|
| 70 |
|
| 71 static gchar * |
|
| 72 purple_blist_fold_name(const gchar *name) |
|
| 73 { |
|
| 74 gchar *res, *tmp; |
|
| 75 |
|
| 76 if (name == NULL) |
|
| 77 return NULL; |
|
| 78 |
|
| 79 tmp = g_utf8_casefold(name, -1); |
|
| 80 res = g_utf8_collate_key(tmp, -1); |
|
| 81 g_free(tmp); |
|
| 82 |
|
| 83 return res; |
|
| 84 } |
|
| 85 |
|
| 86 static PurpleBlistNode *purple_blist_get_last_sibling(PurpleBlistNode *node) |
|
| 87 { |
|
| 88 PurpleBlistNode *n = node; |
|
| 89 if (!n) |
|
| 90 return NULL; |
|
| 91 while (n->next) |
|
| 92 n = n->next; |
|
| 93 return n; |
|
| 94 } |
|
| 95 |
|
| 96 PurpleBlistNode *_purple_blist_get_last_child(PurpleBlistNode *node) |
|
| 97 { |
|
| 98 if (!node) |
|
| 99 return NULL; |
|
| 100 return purple_blist_get_last_sibling(node->child); |
|
| 101 } |
|
| 102 |
|
| 103 struct _list_account_buddies { |
|
| 104 GSList *list; |
|
| 105 PurpleAccount *account; |
|
| 106 }; |
|
| 107 |
|
| 108 struct _purple_hbuddy { |
|
| 109 char *name; |
|
| 110 PurpleAccount *account; |
|
| 111 PurpleBlistNode *group; |
|
| 112 }; |
|
| 113 |
|
| 114 /* This function must not use purple_normalize */ |
|
| 115 static guint _purple_blist_hbuddy_hash(struct _purple_hbuddy *hb) |
|
| 116 { |
|
| 117 return g_str_hash(hb->name) ^ g_direct_hash(hb->group) ^ g_direct_hash(hb->account); |
|
| 118 } |
|
| 119 |
|
| 120 /* This function must not use purple_normalize */ |
|
| 121 static guint _purple_blist_hbuddy_equal(struct _purple_hbuddy *hb1, struct _purple_hbuddy *hb2) |
|
| 122 { |
|
| 123 return (hb1->group == hb2->group && |
|
| 124 hb1->account == hb2->account && |
|
| 125 purple_strequal(hb1->name, hb2->name)); |
|
| 126 } |
|
| 127 |
|
| 128 static void _purple_blist_hbuddy_free_key(struct _purple_hbuddy *hb) |
|
| 129 { |
|
| 130 g_free(hb->name); |
|
| 131 g_free(hb); |
|
| 132 } |
|
| 133 |
|
| 134 static void |
|
| 135 purple_blist_buddies_cache_add_account(PurpleAccount *account) |
|
| 136 { |
|
| 137 GHashTable *account_buddies = g_hash_table_new_full((GHashFunc)_purple_blist_hbuddy_hash, |
|
| 138 (GEqualFunc)_purple_blist_hbuddy_equal, |
|
| 139 (GDestroyNotify)_purple_blist_hbuddy_free_key, NULL); |
|
| 140 g_hash_table_insert(buddies_cache, account, account_buddies); |
|
| 141 } |
|
| 142 |
|
| 143 static void |
|
| 144 purple_buddy_list_account_added_cb(G_GNUC_UNUSED PurpleAccountManager *manager, |
|
| 145 PurpleAccount *account, |
|
| 146 G_GNUC_UNUSED gpointer data) |
|
| 147 { |
|
| 148 purple_blist_buddies_cache_add_account(account); |
|
| 149 } |
|
| 150 |
|
| 151 static void |
|
| 152 purple_blist_buddies_cache_remove_account(const PurpleAccount *account) |
|
| 153 { |
|
| 154 g_hash_table_remove(buddies_cache, account); |
|
| 155 } |
|
| 156 |
|
| 157 static void |
|
| 158 purple_buddy_list_account_removed_cb(G_GNUC_UNUSED PurpleAccountManager *manager, |
|
| 159 PurpleAccount *account, |
|
| 160 G_GNUC_UNUSED gpointer data) |
|
| 161 { |
|
| 162 purple_blist_buddies_cache_remove_account(account); |
|
| 163 } |
|
| 164 |
|
| 165 /********************************************************************* |
|
| 166 * Writing to disk * |
|
| 167 *********************************************************************/ |
|
| 168 |
|
| 169 static void |
|
| 170 value_to_xmlnode(gpointer key, gpointer hvalue, gpointer user_data) |
|
| 171 { |
|
| 172 const char *name; |
|
| 173 GValue *value; |
|
| 174 PurpleXmlNode *node, *child; |
|
| 175 char buf[21]; |
|
| 176 |
|
| 177 name = (const char *)key; |
|
| 178 value = (GValue *)hvalue; |
|
| 179 node = (PurpleXmlNode *)user_data; |
|
| 180 |
|
| 181 g_return_if_fail(value != NULL); |
|
| 182 |
|
| 183 child = purple_xmlnode_new_child(node, "setting"); |
|
| 184 purple_xmlnode_set_attrib(child, "name", name); |
|
| 185 |
|
| 186 if (G_VALUE_HOLDS_INT(value)) { |
|
| 187 purple_xmlnode_set_attrib(child, "type", "int"); |
|
| 188 g_snprintf(buf, sizeof(buf), "%d", g_value_get_int(value)); |
|
| 189 purple_xmlnode_insert_data(child, buf, -1); |
|
| 190 } |
|
| 191 else if (G_VALUE_HOLDS_STRING(value)) { |
|
| 192 purple_xmlnode_set_attrib(child, "type", "string"); |
|
| 193 purple_xmlnode_insert_data(child, g_value_get_string(value), -1); |
|
| 194 } |
|
| 195 else if (G_VALUE_HOLDS_BOOLEAN(value)) { |
|
| 196 purple_xmlnode_set_attrib(child, "type", "bool"); |
|
| 197 g_snprintf(buf, sizeof(buf), "%d", g_value_get_boolean(value)); |
|
| 198 purple_xmlnode_insert_data(child, buf, -1); |
|
| 199 } |
|
| 200 } |
|
| 201 |
|
| 202 static PurpleXmlNode * |
|
| 203 contact_to_xmlnode(PurpleMetaContact *contact) |
|
| 204 { |
|
| 205 PurpleXmlNode *node; |
|
| 206 gchar *alias; |
|
| 207 |
|
| 208 node = purple_xmlnode_new("contact"); |
|
| 209 g_object_get(contact, "alias", &alias, NULL); |
|
| 210 |
|
| 211 if (alias != NULL) |
|
| 212 { |
|
| 213 purple_xmlnode_set_attrib(node, "alias", alias); |
|
| 214 } |
|
| 215 |
|
| 216 /* Write contact settings */ |
|
| 217 g_hash_table_foreach(purple_blist_node_get_settings(PURPLE_BLIST_NODE(contact)), |
|
| 218 value_to_xmlnode, node); |
|
| 219 |
|
| 220 g_free(alias); |
|
| 221 return node; |
|
| 222 } |
|
| 223 |
|
| 224 static PurpleXmlNode * |
|
| 225 group_to_xmlnode(PurpleGroup *group) |
|
| 226 { |
|
| 227 PurpleXmlNode *node, *child; |
|
| 228 PurpleBlistNode *cnode; |
|
| 229 |
|
| 230 node = purple_xmlnode_new("group"); |
|
| 231 if (group != purple_blist_get_default_group()) |
|
| 232 purple_xmlnode_set_attrib(node, "name", purple_group_get_name(group)); |
|
| 233 |
|
| 234 /* Write settings */ |
|
| 235 g_hash_table_foreach(purple_blist_node_get_settings(PURPLE_BLIST_NODE(group)), |
|
| 236 value_to_xmlnode, node); |
|
| 237 |
|
| 238 /* Write contacts and chats */ |
|
| 239 for (cnode = PURPLE_BLIST_NODE(group)->child; cnode != NULL; cnode = cnode->next) |
|
| 240 { |
|
| 241 if (purple_blist_node_is_transient(cnode)) |
|
| 242 continue; |
|
| 243 if (PURPLE_IS_META_CONTACT(cnode)) |
|
| 244 { |
|
| 245 child = contact_to_xmlnode(PURPLE_META_CONTACT(cnode)); |
|
| 246 purple_xmlnode_insert_child(node, child); |
|
| 247 } |
|
| 248 } |
|
| 249 |
|
| 250 return node; |
|
| 251 } |
|
| 252 |
|
| 253 static PurpleXmlNode * |
|
| 254 blist_to_xmlnode(void) { |
|
| 255 PurpleXmlNode *node, *child, *grandchild; |
|
| 256 PurpleBlistNode *gnode; |
|
| 257 const gchar *localized_default; |
|
| 258 |
|
| 259 node = purple_xmlnode_new("purple"); |
|
| 260 purple_xmlnode_set_attrib(node, "version", "1.0"); |
|
| 261 |
|
| 262 /* Write groups */ |
|
| 263 child = purple_xmlnode_new_child(node, "blist"); |
|
| 264 |
|
| 265 localized_default = localized_default_group_name; |
|
| 266 if (!purple_strequal(_("Buddies"), "Buddies")) |
|
| 267 localized_default = _("Buddies"); |
|
| 268 if (localized_default != NULL) { |
|
| 269 purple_xmlnode_set_attrib(child, |
|
| 270 "localized-default-group", localized_default); |
|
| 271 } |
|
| 272 |
|
| 273 for (gnode = purple_blist_get_default_root(); gnode != NULL; |
|
| 274 gnode = gnode->next) { |
|
| 275 if (purple_blist_node_is_transient(gnode)) |
|
| 276 continue; |
|
| 277 if (PURPLE_IS_GROUP(gnode)) |
|
| 278 { |
|
| 279 grandchild = group_to_xmlnode(PURPLE_GROUP(gnode)); |
|
| 280 purple_xmlnode_insert_child(child, grandchild); |
|
| 281 } |
|
| 282 } |
|
| 283 |
|
| 284 return node; |
|
| 285 } |
|
| 286 |
|
| 287 static void |
|
| 288 purple_blist_sync(void) |
|
| 289 { |
|
| 290 PurpleXmlNode *node; |
|
| 291 char *data; |
|
| 292 |
|
| 293 if (!blist_loaded) |
|
| 294 { |
|
| 295 purple_debug_error("buddylist", "Attempted to save buddy list before it " |
|
| 296 "was read!\n"); |
|
| 297 return; |
|
| 298 } |
|
| 299 |
|
| 300 node = blist_to_xmlnode(); |
|
| 301 data = purple_xmlnode_to_formatted_str(node, NULL); |
|
| 302 purple_util_write_data_to_config_file("blist.xml", data, -1); |
|
| 303 g_free(data); |
|
| 304 purple_xmlnode_free(node); |
|
| 305 } |
|
| 306 |
|
| 307 static gboolean |
|
| 308 save_cb(G_GNUC_UNUSED gpointer data) |
|
| 309 { |
|
| 310 purple_blist_sync(); |
|
| 311 save_timer = 0; |
|
| 312 return FALSE; |
|
| 313 } |
|
| 314 |
|
| 315 static void |
|
| 316 purple_blist_real_schedule_save(void) |
|
| 317 { |
|
| 318 if (save_timer == 0) |
|
| 319 save_timer = g_timeout_add_seconds(5, save_cb, NULL); |
|
| 320 } |
|
| 321 |
|
| 322 static void |
|
| 323 purple_blist_real_save_account(G_GNUC_UNUSED PurpleBuddyList *list, |
|
| 324 G_GNUC_UNUSED PurpleAccount *account) |
|
| 325 { |
|
| 326 #if 1 |
|
| 327 purple_blist_real_schedule_save(); |
|
| 328 #else |
|
| 329 if (account != NULL) { |
|
| 330 /* Save the buddies and privacy data for this account */ |
|
| 331 } else { |
|
| 332 /* Save all buddies and privacy data */ |
|
| 333 } |
|
| 334 #endif |
|
| 335 } |
|
| 336 |
|
| 337 static void |
|
| 338 purple_blist_real_save_node(G_GNUC_UNUSED PurpleBuddyList *list, |
|
| 339 G_GNUC_UNUSED PurpleBlistNode *node) |
|
| 340 { |
|
| 341 purple_blist_real_schedule_save(); |
|
| 342 } |
|
| 343 |
|
| 344 void |
|
| 345 purple_blist_schedule_save(void) |
|
| 346 { |
|
| 347 PurpleBuddyListClass *klass = NULL; |
|
| 348 |
|
| 349 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist)); |
|
| 350 |
|
| 351 klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist); |
|
| 352 |
|
| 353 /* Save everything */ |
|
| 354 if (klass && klass->save_account) { |
|
| 355 klass->save_account(purplebuddylist, NULL); |
|
| 356 } |
|
| 357 } |
|
| 358 |
|
| 359 /********************************************************************* |
|
| 360 * Reading from disk * |
|
| 361 *********************************************************************/ |
|
| 362 |
|
| 363 static void |
|
| 364 parse_setting(PurpleBlistNode *node, PurpleXmlNode *setting) |
|
| 365 { |
|
| 366 const char *name = purple_xmlnode_get_attrib(setting, "name"); |
|
| 367 const char *type = purple_xmlnode_get_attrib(setting, "type"); |
|
| 368 char *value = purple_xmlnode_get_data(setting); |
|
| 369 |
|
| 370 if (!value) |
|
| 371 return; |
|
| 372 |
|
| 373 if (!type || purple_strequal(type, "string")) |
|
| 374 purple_blist_node_set_string(node, name, value); |
|
| 375 else if (purple_strequal(type, "bool")) |
|
| 376 purple_blist_node_set_bool(node, name, atoi(value)); |
|
| 377 else if (purple_strequal(type, "int")) |
|
| 378 purple_blist_node_set_int(node, name, atoi(value)); |
|
| 379 |
|
| 380 g_free(value); |
|
| 381 } |
|
| 382 |
|
| 383 static void |
|
| 384 parse_contact(PurpleGroup *group, PurpleXmlNode *cnode) |
|
| 385 { |
|
| 386 PurpleMetaContact *contact = purple_meta_contact_new(); |
|
| 387 PurpleXmlNode *x; |
|
| 388 const char *alias; |
|
| 389 |
|
| 390 purple_blist_add_contact(contact, group, |
|
| 391 _purple_blist_get_last_child((PurpleBlistNode*)group)); |
|
| 392 |
|
| 393 if ((alias = purple_xmlnode_get_attrib(cnode, "alias"))) { |
|
| 394 purple_meta_contact_set_alias(contact, alias); |
|
| 395 } |
|
| 396 |
|
| 397 for (x = cnode->child; x; x = x->next) { |
|
| 398 if (x->type != PURPLE_XMLNODE_TYPE_TAG) |
|
| 399 continue; |
|
| 400 if (purple_strequal(x->name, "setting")) |
|
| 401 parse_setting(PURPLE_BLIST_NODE(contact), x); |
|
| 402 } |
|
| 403 |
|
| 404 /* if the contact is empty, don't keep it around. it causes problems */ |
|
| 405 if (!PURPLE_BLIST_NODE(contact)->child) |
|
| 406 purple_blist_remove_contact(contact); |
|
| 407 } |
|
| 408 |
|
| 409 static void |
|
| 410 parse_group(PurpleXmlNode *groupnode) |
|
| 411 { |
|
| 412 const char *name = purple_xmlnode_get_attrib(groupnode, "name"); |
|
| 413 PurpleGroup *group; |
|
| 414 PurpleXmlNode *cnode; |
|
| 415 |
|
| 416 group = purple_group_new(name); |
|
| 417 purple_blist_add_group(group, purple_blist_get_last_sibling( |
|
| 418 purple_blist_get_default_root())); |
|
| 419 |
|
| 420 for (cnode = groupnode->child; cnode; cnode = cnode->next) { |
|
| 421 if (cnode->type != PURPLE_XMLNODE_TYPE_TAG) |
|
| 422 continue; |
|
| 423 if (purple_strequal(cnode->name, "setting")) |
|
| 424 parse_setting((PurpleBlistNode*)group, cnode); |
|
| 425 else if (purple_strequal(cnode->name, "contact") || |
|
| 426 purple_strequal(cnode->name, "person")) |
|
| 427 parse_contact(group, cnode); |
|
| 428 } |
|
| 429 } |
|
| 430 |
|
| 431 static void |
|
| 432 load_blist(void) |
|
| 433 { |
|
| 434 PurpleXmlNode *purple, *blist; |
|
| 435 |
|
| 436 blist_loaded = TRUE; |
|
| 437 |
|
| 438 purple = purple_util_read_xml_from_config_file("blist.xml", _("buddy list")); |
|
| 439 |
|
| 440 if(purple == NULL) { |
|
| 441 return; |
|
| 442 } |
|
| 443 |
|
| 444 blist = purple_xmlnode_get_child(purple, "blist"); |
|
| 445 if(blist) { |
|
| 446 PurpleXmlNode *groupnode; |
|
| 447 |
|
| 448 localized_default_group_name = g_strdup( |
|
| 449 purple_xmlnode_get_attrib(blist, |
|
| 450 "localized-default-group")); |
|
| 451 |
|
| 452 for(groupnode = purple_xmlnode_get_child(blist, "group"); groupnode != NULL; |
|
| 453 groupnode = purple_xmlnode_get_next_twin(groupnode)) { |
|
| 454 parse_group(groupnode); |
|
| 455 } |
|
| 456 } else { |
|
| 457 g_free(localized_default_group_name); |
|
| 458 localized_default_group_name = NULL; |
|
| 459 } |
|
| 460 |
|
| 461 purple_xmlnode_free(purple); |
|
| 462 } |
|
| 463 |
|
| 464 /***************************************************************************** |
|
| 465 * Public API functions * |
|
| 466 *****************************************************************************/ |
|
| 467 |
|
| 468 void |
|
| 469 purple_blist_set_ui(GType type) |
|
| 470 { |
|
| 471 g_return_if_fail(g_type_is_a(type, PURPLE_TYPE_BUDDY_LIST) || |
|
| 472 type == G_TYPE_INVALID); |
|
| 473 buddy_list_type = type; |
|
| 474 } |
|
| 475 |
|
| 476 void |
|
| 477 purple_blist_boot(void) |
|
| 478 { |
|
| 479 GListModel *manager_model = NULL; |
|
| 480 PurpleBuddyList *gbl = g_object_new(buddy_list_type, NULL); |
|
| 481 guint n_items = 0; |
|
| 482 |
|
| 483 buddies_cache = g_hash_table_new_full(g_direct_hash, g_direct_equal, |
|
| 484 NULL, (GDestroyNotify)g_hash_table_destroy); |
|
| 485 |
|
| 486 groups_cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); |
|
| 487 |
|
| 488 manager_model = purple_account_manager_get_default_as_model(); |
|
| 489 n_items = g_list_model_get_n_items(manager_model); |
|
| 490 for(guint index = 0; index < n_items; index++) { |
|
| 491 PurpleAccount *account = g_list_model_get_item(manager_model, index); |
|
| 492 purple_blist_buddies_cache_add_account(account); |
|
| 493 g_object_unref(account); |
|
| 494 } |
|
| 495 |
|
| 496 purplebuddylist = gbl; |
|
| 497 |
|
| 498 load_blist(); |
|
| 499 } |
|
| 500 |
|
| 501 PurpleBuddyList * |
|
| 502 purple_blist_get_default(void) |
|
| 503 { |
|
| 504 return purplebuddylist; |
|
| 505 } |
|
| 506 |
|
| 507 PurpleBlistNode * |
|
| 508 purple_blist_get_default_root(void) |
|
| 509 { |
|
| 510 if (purplebuddylist) { |
|
| 511 PurpleBuddyListPrivate *priv = |
|
| 512 purple_buddy_list_get_instance_private(purplebuddylist); |
|
| 513 return priv->root; |
|
| 514 } |
|
| 515 return NULL; |
|
| 516 } |
|
| 517 |
|
| 518 PurpleBlistNode * |
|
| 519 purple_blist_get_root(PurpleBuddyList *list) |
|
| 520 { |
|
| 521 PurpleBuddyListPrivate *priv = NULL; |
|
| 522 |
|
| 523 g_return_val_if_fail(PURPLE_IS_BUDDY_LIST(list), NULL); |
|
| 524 priv = purple_buddy_list_get_instance_private(list); |
|
| 525 |
|
| 526 return priv->root; |
|
| 527 } |
|
| 528 |
|
| 529 static void |
|
| 530 append_buddy(G_GNUC_UNUSED gpointer key, gpointer value, gpointer user_data) |
|
| 531 { |
|
| 532 GSList **list = user_data; |
|
| 533 *list = g_slist_prepend(*list, value); |
|
| 534 } |
|
| 535 |
|
| 536 GSList * |
|
| 537 purple_blist_get_buddies(void) |
|
| 538 { |
|
| 539 PurpleBuddyListPrivate *priv; |
|
| 540 GSList *buddies = NULL; |
|
| 541 |
|
| 542 if (!purplebuddylist) |
|
| 543 return NULL; |
|
| 544 |
|
| 545 priv = purple_buddy_list_get_instance_private(purplebuddylist); |
|
| 546 g_hash_table_foreach(priv->buddies, append_buddy, &buddies); |
|
| 547 return buddies; |
|
| 548 } |
|
| 549 |
|
| 550 void |
|
| 551 purple_blist_show(void) |
|
| 552 { |
|
| 553 PurpleBuddyListClass *klass = NULL; |
|
| 554 |
|
| 555 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist)); |
|
| 556 klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist); |
|
| 557 |
|
| 558 if (klass && klass->show) { |
|
| 559 klass->show(purplebuddylist); |
|
| 560 } |
|
| 561 } |
|
| 562 |
|
| 563 void purple_blist_set_visible(gboolean show) |
|
| 564 { |
|
| 565 PurpleBuddyListClass *klass = NULL; |
|
| 566 |
|
| 567 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist)); |
|
| 568 klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist); |
|
| 569 |
|
| 570 if (klass && klass->set_visible) { |
|
| 571 klass->set_visible(purplebuddylist, show); |
|
| 572 } |
|
| 573 } |
|
| 574 |
|
| 575 void purple_blist_update_groups_cache(PurpleGroup *group, const char *new_name) |
|
| 576 { |
|
| 577 gchar* key; |
|
| 578 |
|
| 579 key = purple_blist_fold_name(purple_group_get_name(group)); |
|
| 580 g_hash_table_remove(groups_cache, key); |
|
| 581 g_free(key); |
|
| 582 |
|
| 583 g_hash_table_insert(groups_cache, |
|
| 584 purple_blist_fold_name(new_name), group); |
|
| 585 } |
|
| 586 |
|
| 587 void purple_blist_add_contact(PurpleMetaContact *contact, PurpleGroup *group, PurpleBlistNode *node) |
|
| 588 { |
|
| 589 PurpleBuddyListClass *klass = NULL; |
|
| 590 PurpleGroup *g; |
|
| 591 PurpleBlistNode *gnode, *cnode, *bnode; |
|
| 592 PurpleCountingNode *contact_counter, *group_counter; |
|
| 593 |
|
| 594 g_return_if_fail(PURPLE_IS_META_CONTACT(contact)); |
|
| 595 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist)); |
|
| 596 |
|
| 597 if (PURPLE_BLIST_NODE(contact) == node) |
|
| 598 return; |
|
| 599 |
|
| 600 klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist); |
|
| 601 |
|
| 602 if (node && (PURPLE_IS_META_CONTACT(node))) |
|
| 603 g = PURPLE_GROUP(node->parent); |
|
| 604 else if (group) |
|
| 605 g = group; |
|
| 606 else |
|
| 607 g = purple_blist_get_default_group(); |
|
| 608 |
|
| 609 gnode = (PurpleBlistNode*)g; |
|
| 610 cnode = (PurpleBlistNode*)contact; |
|
| 611 |
|
| 612 if (cnode->parent) { |
|
| 613 if (cnode->parent->child == cnode) |
|
| 614 cnode->parent->child = cnode->next; |
|
| 615 if (cnode->prev) |
|
| 616 cnode->prev->next = cnode->next; |
|
| 617 if (cnode->next) |
|
| 618 cnode->next->prev = cnode->prev; |
|
| 619 |
|
| 620 contact_counter = PURPLE_COUNTING_NODE(contact); |
|
| 621 group_counter = PURPLE_COUNTING_NODE(cnode->parent); |
|
| 622 |
|
| 623 if (purple_counting_node_get_online_count(contact_counter) > 0) |
|
| 624 purple_counting_node_change_online_count(group_counter, -1); |
|
| 625 if (purple_counting_node_get_current_size(contact_counter) > 0) |
|
| 626 purple_counting_node_change_current_size(group_counter, -1); |
|
| 627 purple_counting_node_change_total_size(group_counter, -1); |
|
| 628 |
|
| 629 if (klass && klass->remove) { |
|
| 630 klass->remove(purplebuddylist, cnode); |
|
| 631 } |
|
| 632 |
|
| 633 if (klass && klass->remove_node) { |
|
| 634 klass->remove_node(purplebuddylist, cnode); |
|
| 635 } |
|
| 636 } |
|
| 637 |
|
| 638 if (node && (PURPLE_IS_META_CONTACT(node))) { |
|
| 639 if (node->next) |
|
| 640 node->next->prev = cnode; |
|
| 641 cnode->next = node->next; |
|
| 642 cnode->prev = node; |
|
| 643 cnode->parent = node->parent; |
|
| 644 node->next = cnode; |
|
| 645 } else { |
|
| 646 if (gnode->child) |
|
| 647 gnode->child->prev = cnode; |
|
| 648 cnode->prev = NULL; |
|
| 649 cnode->next = gnode->child; |
|
| 650 gnode->child = cnode; |
|
| 651 cnode->parent = gnode; |
|
| 652 } |
|
| 653 |
|
| 654 contact_counter = PURPLE_COUNTING_NODE(contact); |
|
| 655 group_counter = PURPLE_COUNTING_NODE(g); |
|
| 656 |
|
| 657 if (purple_counting_node_get_online_count(contact_counter) > 0) |
|
| 658 purple_counting_node_change_online_count(group_counter, +1); |
|
| 659 if (purple_counting_node_get_current_size(contact_counter) > 0) |
|
| 660 purple_counting_node_change_current_size(group_counter, +1); |
|
| 661 purple_counting_node_change_total_size(group_counter, +1); |
|
| 662 |
|
| 663 if (klass && klass->save_node) { |
|
| 664 if (cnode->child) { |
|
| 665 klass->save_node(purplebuddylist, cnode); |
|
| 666 } |
|
| 667 for (bnode = cnode->child; bnode; bnode = bnode->next) { |
|
| 668 klass->save_node(purplebuddylist, bnode); |
|
| 669 } |
|
| 670 } |
|
| 671 |
|
| 672 if (klass && klass->update) { |
|
| 673 if (cnode->child) { |
|
| 674 klass->update(purplebuddylist, cnode); |
|
| 675 } |
|
| 676 |
|
| 677 for (bnode = cnode->child; bnode; bnode = bnode->next) { |
|
| 678 klass->update(purplebuddylist, bnode); |
|
| 679 } |
|
| 680 } |
|
| 681 } |
|
| 682 |
|
| 683 void purple_blist_add_group(PurpleGroup *group, PurpleBlistNode *node) |
|
| 684 { |
|
| 685 PurpleBuddyListClass *klass = NULL; |
|
| 686 PurpleBuddyListPrivate *priv = NULL; |
|
| 687 PurpleBlistNode *gnode = (PurpleBlistNode*)group; |
|
| 688 gchar* key; |
|
| 689 |
|
| 690 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist)); |
|
| 691 g_return_if_fail(PURPLE_IS_GROUP(group)); |
|
| 692 |
|
| 693 klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist); |
|
| 694 priv = purple_buddy_list_get_instance_private(purplebuddylist); |
|
| 695 |
|
| 696 /* if we're moving to overtop of ourselves, do nothing */ |
|
| 697 if (gnode == node) { |
|
| 698 if (!priv->root) { |
|
| 699 node = NULL; |
|
| 700 } else { |
|
| 701 return; |
|
| 702 } |
|
| 703 } |
|
| 704 |
|
| 705 if (purple_blist_find_group(purple_group_get_name(group))) { |
|
| 706 /* This is just being moved */ |
|
| 707 |
|
| 708 if (klass && klass->remove) { |
|
| 709 klass->remove(purplebuddylist, |
|
| 710 (PurpleBlistNode *)group); |
|
| 711 } |
|
| 712 |
|
| 713 if (gnode == priv->root) { |
|
| 714 priv->root = gnode->next; |
|
| 715 } |
|
| 716 if (gnode->prev) |
|
| 717 gnode->prev->next = gnode->next; |
|
| 718 if (gnode->next) |
|
| 719 gnode->next->prev = gnode->prev; |
|
| 720 } else { |
|
| 721 key = purple_blist_fold_name(purple_group_get_name(group)); |
|
| 722 g_hash_table_insert(groups_cache, key, group); |
|
| 723 } |
|
| 724 |
|
| 725 if (node && PURPLE_IS_GROUP(node)) { |
|
| 726 gnode->next = node->next; |
|
| 727 gnode->prev = node; |
|
| 728 if (node->next) |
|
| 729 node->next->prev = gnode; |
|
| 730 node->next = gnode; |
|
| 731 } else { |
|
| 732 if (priv->root) { |
|
| 733 priv->root->prev = gnode; |
|
| 734 } |
|
| 735 gnode->next = priv->root; |
|
| 736 gnode->prev = NULL; |
|
| 737 priv->root = gnode; |
|
| 738 } |
|
| 739 |
|
| 740 if (klass && klass->save_node) { |
|
| 741 klass->save_node(purplebuddylist, gnode); |
|
| 742 for (node = gnode->child; node; node = node->next) { |
|
| 743 klass->save_node(purplebuddylist, node); |
|
| 744 } |
|
| 745 } |
|
| 746 |
|
| 747 if (klass && klass->update) { |
|
| 748 klass->update(purplebuddylist, gnode); |
|
| 749 for (node = gnode->child; node; node = node->next) { |
|
| 750 klass->update(purplebuddylist, node); |
|
| 751 } |
|
| 752 } |
|
| 753 |
|
| 754 purple_signal_emit(purple_blist_get_handle(), "blist-node-added", |
|
| 755 gnode); |
|
| 756 } |
|
| 757 |
|
| 758 void purple_blist_remove_contact(PurpleMetaContact *contact) |
|
| 759 { |
|
| 760 PurpleBuddyListClass *klass = NULL; |
|
| 761 PurpleBlistNode *node, *gnode; |
|
| 762 PurpleGroup *group; |
|
| 763 |
|
| 764 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist)); |
|
| 765 g_return_if_fail(PURPLE_IS_META_CONTACT(contact)); |
|
| 766 |
|
| 767 klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist); |
|
| 768 node = (PurpleBlistNode *)contact; |
|
| 769 gnode = node->parent; |
|
| 770 group = PURPLE_GROUP(gnode); |
|
| 771 |
|
| 772 /* Remove the node from its parent */ |
|
| 773 if (gnode->child == node) |
|
| 774 gnode->child = node->next; |
|
| 775 if (node->prev) |
|
| 776 node->prev->next = node->next; |
|
| 777 if (node->next) |
|
| 778 node->next->prev = node->prev; |
|
| 779 purple_counting_node_change_total_size(PURPLE_COUNTING_NODE(group), -1); |
|
| 780 |
|
| 781 /* Update the UI */ |
|
| 782 if (klass && klass->remove) { |
|
| 783 klass->remove(purplebuddylist, node); |
|
| 784 } |
|
| 785 |
|
| 786 if (klass && klass->remove_node) { |
|
| 787 klass->remove_node(purplebuddylist, node); |
|
| 788 } |
|
| 789 |
|
| 790 purple_signal_emit(purple_blist_get_handle(), "blist-node-removed", |
|
| 791 PURPLE_BLIST_NODE(contact)); |
|
| 792 |
|
| 793 /* Delete the node */ |
|
| 794 g_object_unref(contact); |
|
| 795 } |
|
| 796 |
|
| 797 void purple_blist_remove_group(PurpleGroup *group) |
|
| 798 { |
|
| 799 PurpleAccountManager *manager = NULL; |
|
| 800 PurpleBuddyListClass *klass = NULL; |
|
| 801 PurpleBuddyListPrivate *priv = NULL; |
|
| 802 PurpleBlistNode *node; |
|
| 803 GList *accounts = NULL; |
|
| 804 gchar* key; |
|
| 805 |
|
| 806 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist)); |
|
| 807 g_return_if_fail(PURPLE_IS_GROUP(group)); |
|
| 808 |
|
| 809 if (group == purple_blist_get_default_group()) |
|
| 810 purple_debug_warning("buddylist", "cannot remove default group"); |
|
| 811 |
|
| 812 klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist); |
|
| 813 priv = purple_buddy_list_get_instance_private(purplebuddylist); |
|
| 814 node = (PurpleBlistNode *)group; |
|
| 815 |
|
| 816 /* Make sure the group is empty */ |
|
| 817 if (node->child) |
|
| 818 return; |
|
| 819 |
|
| 820 /* Remove the node from its parent */ |
|
| 821 if (priv->root == node) { |
|
| 822 priv->root = node->next; |
|
| 823 } |
|
| 824 if (node->prev) |
|
| 825 node->prev->next = node->next; |
|
| 826 if (node->next) |
|
| 827 node->next->prev = node->prev; |
|
| 828 |
|
| 829 key = purple_blist_fold_name(purple_group_get_name(group)); |
|
| 830 g_hash_table_remove(groups_cache, key); |
|
| 831 g_free(key); |
|
| 832 |
|
| 833 /* Update the UI */ |
|
| 834 if (klass && klass->remove) { |
|
| 835 klass->remove(purplebuddylist, node); |
|
| 836 } |
|
| 837 |
|
| 838 if (klass && klass->remove_node) { |
|
| 839 klass->remove_node(purplebuddylist, node); |
|
| 840 } |
|
| 841 |
|
| 842 purple_signal_emit(purple_blist_get_handle(), "blist-node-removed", |
|
| 843 PURPLE_BLIST_NODE(group)); |
|
| 844 |
|
| 845 /* Remove the group from all accounts that are online */ |
|
| 846 manager = purple_account_manager_get_default(); |
|
| 847 accounts = purple_account_manager_get_connected(manager); |
|
| 848 while(accounts != NULL) { |
|
| 849 purple_account_remove_group(accounts->data, group); |
|
| 850 |
|
| 851 accounts = g_list_delete_link(accounts, accounts); |
|
| 852 } |
|
| 853 |
|
| 854 /* Delete the node */ |
|
| 855 g_object_unref(group); |
|
| 856 } |
|
| 857 |
|
| 858 PurpleGroup *purple_blist_find_group(const char *name) |
|
| 859 { |
|
| 860 gchar* key; |
|
| 861 PurpleGroup *group; |
|
| 862 |
|
| 863 g_return_val_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist), NULL); |
|
| 864 |
|
| 865 if (name == NULL || name[0] == '\0') |
|
| 866 name = PURPLE_BLIST_DEFAULT_GROUP_NAME; |
|
| 867 if (purple_strequal(name, "Buddies")) |
|
| 868 name = PURPLE_BLIST_DEFAULT_GROUP_NAME; |
|
| 869 if (purple_strequal(name, localized_default_group_name)) |
|
| 870 name = PURPLE_BLIST_DEFAULT_GROUP_NAME; |
|
| 871 |
|
| 872 key = purple_blist_fold_name(name); |
|
| 873 group = g_hash_table_lookup(groups_cache, key); |
|
| 874 g_free(key); |
|
| 875 |
|
| 876 return group; |
|
| 877 } |
|
| 878 |
|
| 879 PurpleGroup * |
|
| 880 purple_blist_get_default_group(void) |
|
| 881 { |
|
| 882 PurpleGroup *group; |
|
| 883 |
|
| 884 group = purple_blist_find_group(PURPLE_BLIST_DEFAULT_GROUP_NAME); |
|
| 885 if (!group) { |
|
| 886 group = purple_group_new(PURPLE_BLIST_DEFAULT_GROUP_NAME); |
|
| 887 purple_blist_add_group(group, NULL); |
|
| 888 } |
|
| 889 |
|
| 890 return group; |
|
| 891 } |
|
| 892 |
|
| 893 void |
|
| 894 purple_blist_walk(PurpleBlistWalkFunc group_func, |
|
| 895 PurpleBlistWalkFunc meta_contact_func, |
|
| 896 PurpleBlistWalkFunc contact_func, |
|
| 897 gpointer data) |
|
| 898 { |
|
| 899 PurpleBlistNode *group = NULL, *meta_contact = NULL, *contact = NULL; |
|
| 900 |
|
| 901 for (group = purple_blist_get_default_root(); group != NULL; |
|
| 902 group = group->next) { |
|
| 903 if(group_func != NULL) { |
|
| 904 group_func(group, data); |
|
| 905 } |
|
| 906 |
|
| 907 for(meta_contact = group->child; meta_contact != NULL; meta_contact = meta_contact->next) { |
|
| 908 if(PURPLE_IS_META_CONTACT(meta_contact)) { |
|
| 909 if(meta_contact_func != NULL) { |
|
| 910 meta_contact_func(meta_contact, data); |
|
| 911 } |
|
| 912 |
|
| 913 if(contact_func != NULL) { |
|
| 914 for(contact = meta_contact->child; contact != NULL; contact = contact->next) { |
|
| 915 contact_func(contact, data); |
|
| 916 } |
|
| 917 } |
|
| 918 } |
|
| 919 } |
|
| 920 } |
|
| 921 } |
|
| 922 |
|
| 923 const gchar * |
|
| 924 purple_blist_get_default_group_name(void) { |
|
| 925 return _("Buddies"); |
|
| 926 } |
|
| 927 |
|
| 928 |
|
| 929 void |
|
| 930 purple_blist_request_add_buddy(PurpleAccount *account, const char *username, |
|
| 931 const char *group, const char *alias) |
|
| 932 { |
|
| 933 PurpleBuddyListClass *klass = NULL; |
|
| 934 |
|
| 935 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist)); |
|
| 936 |
|
| 937 klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist); |
|
| 938 if (klass != NULL && klass->request_add_buddy != NULL) { |
|
| 939 klass->request_add_buddy(purplebuddylist, account, username, |
|
| 940 group, alias); |
|
| 941 } |
|
| 942 } |
|
| 943 |
|
| 944 void |
|
| 945 purple_blist_request_add_group(void) |
|
| 946 { |
|
| 947 PurpleBuddyListClass *klass = NULL; |
|
| 948 |
|
| 949 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist)); |
|
| 950 |
|
| 951 klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist); |
|
| 952 if (klass != NULL && klass->request_add_group != NULL) { |
|
| 953 klass->request_add_group(purplebuddylist); |
|
| 954 } |
|
| 955 } |
|
| 956 |
|
| 957 void |
|
| 958 purple_blist_new_node(PurpleBuddyList *list, PurpleBlistNode *node) |
|
| 959 { |
|
| 960 PurpleBuddyListClass *klass = NULL; |
|
| 961 |
|
| 962 g_return_if_fail(PURPLE_IS_BUDDY_LIST(list)); |
|
| 963 |
|
| 964 klass = PURPLE_BUDDY_LIST_GET_CLASS(list); |
|
| 965 if (klass && klass->new_node) { |
|
| 966 klass->new_node(list, node); |
|
| 967 } |
|
| 968 } |
|
| 969 |
|
| 970 void |
|
| 971 purple_blist_update_node(PurpleBuddyList *list, PurpleBlistNode *node) |
|
| 972 { |
|
| 973 PurpleBuddyListClass *klass = NULL; |
|
| 974 |
|
| 975 g_return_if_fail(PURPLE_IS_BUDDY_LIST(list)); |
|
| 976 |
|
| 977 klass = PURPLE_BUDDY_LIST_GET_CLASS(list); |
|
| 978 if (klass && klass->update) { |
|
| 979 klass->update(list, node); |
|
| 980 } |
|
| 981 } |
|
| 982 |
|
| 983 void |
|
| 984 purple_blist_save_node(PurpleBuddyList *list, PurpleBlistNode *node) |
|
| 985 { |
|
| 986 PurpleBuddyListClass *klass = NULL; |
|
| 987 |
|
| 988 g_return_if_fail(PURPLE_IS_BUDDY_LIST(list)); |
|
| 989 |
|
| 990 klass = PURPLE_BUDDY_LIST_GET_CLASS(list); |
|
| 991 if (klass && klass->save_node) { |
|
| 992 klass->save_node(list, node); |
|
| 993 } |
|
| 994 } |
|
| 995 |
|
| 996 void |
|
| 997 purple_blist_save_account(PurpleBuddyList *list, PurpleAccount *account) |
|
| 998 { |
|
| 999 PurpleBuddyListClass *klass = NULL; |
|
| 1000 |
|
| 1001 /* XXX: There's a chicken and egg problem with the accounts api, where |
|
| 1002 * it'll call this function before purple_blist_init is called, this will |
|
| 1003 * cause the following g_return_if_fail to fail, and muck up the logs. We |
|
| 1004 * need to find a better fix for this, but this gets rid of it for now. |
|
| 1005 */ |
|
| 1006 if(G_UNLIKELY(list == NULL && purplebuddylist == NULL)) { |
|
| 1007 return; |
|
| 1008 } |
|
| 1009 |
|
| 1010 g_return_if_fail(PURPLE_IS_BUDDY_LIST(list)); |
|
| 1011 |
|
| 1012 klass = PURPLE_BUDDY_LIST_GET_CLASS(list); |
|
| 1013 if (klass && klass->save_account) { |
|
| 1014 klass->save_account(list, account); |
|
| 1015 } |
|
| 1016 } |
|
| 1017 |
|
| 1018 const gchar * |
|
| 1019 _purple_blist_get_localized_default_group_name(void) |
|
| 1020 { |
|
| 1021 return localized_default_group_name; |
|
| 1022 } |
|
| 1023 |
|
| 1024 void * |
|
| 1025 purple_blist_get_handle(void) |
|
| 1026 { |
|
| 1027 static int handle; |
|
| 1028 |
|
| 1029 return &handle; |
|
| 1030 } |
|
| 1031 |
|
| 1032 void |
|
| 1033 purple_blist_init(void) |
|
| 1034 { |
|
| 1035 void *handle = purple_blist_get_handle(); |
|
| 1036 |
|
| 1037 /* Set a default, which can't be done as a static initializer. */ |
|
| 1038 buddy_list_type = PURPLE_TYPE_BUDDY_LIST; |
|
| 1039 |
|
| 1040 purple_signal_register(handle, "blist-node-added", |
|
| 1041 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1, |
|
| 1042 PURPLE_TYPE_BLIST_NODE); |
|
| 1043 |
|
| 1044 purple_signal_register(handle, "blist-node-removed", |
|
| 1045 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1, |
|
| 1046 PURPLE_TYPE_BLIST_NODE); |
|
| 1047 |
|
| 1048 purple_signal_register(handle, "update-idle", purple_marshal_VOID, |
|
| 1049 G_TYPE_NONE, 0); |
|
| 1050 |
|
| 1051 purple_signal_register(handle, "blist-node-extended-menu", |
|
| 1052 purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, |
|
| 1053 PURPLE_TYPE_BLIST_NODE, |
|
| 1054 G_TYPE_POINTER); /* (GList **) */ |
|
| 1055 |
|
| 1056 purple_signal_register(handle, "blist-node-aliased", |
|
| 1057 purple_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, |
|
| 1058 PURPLE_TYPE_BLIST_NODE, G_TYPE_STRING); |
|
| 1059 } |
|
| 1060 |
|
| 1061 static void |
|
| 1062 blist_node_destroy(PurpleBuddyListClass *klass, PurpleBuddyList *list, |
|
| 1063 PurpleBlistNode *node) |
|
| 1064 { |
|
| 1065 PurpleBlistNode *child, *next_child; |
|
| 1066 |
|
| 1067 child = node->child; |
|
| 1068 while (child) { |
|
| 1069 next_child = child->next; |
|
| 1070 blist_node_destroy(klass, list, child); |
|
| 1071 child = next_child; |
|
| 1072 } |
|
| 1073 |
|
| 1074 /* Allow the UI to free data */ |
|
| 1075 node->parent = NULL; |
|
| 1076 node->child = NULL; |
|
| 1077 node->next = NULL; |
|
| 1078 node->prev = NULL; |
|
| 1079 if (klass && klass->remove) { |
|
| 1080 klass->remove(list, node); |
|
| 1081 } |
|
| 1082 |
|
| 1083 g_object_unref(node); |
|
| 1084 } |
|
| 1085 |
|
| 1086 void |
|
| 1087 purple_blist_uninit(void) |
|
| 1088 { |
|
| 1089 /* This happens if we quit before purple_set_blist is called. */ |
|
| 1090 if (purplebuddylist == NULL) |
|
| 1091 return; |
|
| 1092 |
|
| 1093 if(save_timer != 0) { |
|
| 1094 g_clear_handle_id(&save_timer, g_source_remove); |
|
| 1095 purple_blist_sync(); |
|
| 1096 } |
|
| 1097 |
|
| 1098 purple_debug_info("buddylist", "Destroying"); |
|
| 1099 |
|
| 1100 g_clear_pointer(&buddies_cache, g_hash_table_destroy); |
|
| 1101 g_clear_pointer(&groups_cache, g_hash_table_destroy); |
|
| 1102 |
|
| 1103 g_clear_object(&purplebuddylist); |
|
| 1104 |
|
| 1105 g_clear_pointer(&localized_default_group_name, g_free); |
|
| 1106 |
|
| 1107 purple_signals_disconnect_by_handle(purple_blist_get_handle()); |
|
| 1108 purple_signals_unregister_by_instance(purple_blist_get_handle()); |
|
| 1109 } |
|
| 1110 |
|
| 1111 /************************************************************************** |
|
| 1112 * GObject code |
|
| 1113 **************************************************************************/ |
|
| 1114 |
|
| 1115 /* GObject initialization function */ |
|
| 1116 static void |
|
| 1117 purple_buddy_list_init(PurpleBuddyList *blist) |
|
| 1118 { |
|
| 1119 PurpleBuddyListPrivate *priv = NULL; |
|
| 1120 PurpleAccountManager *manager = NULL; |
|
| 1121 |
|
| 1122 priv = purple_buddy_list_get_instance_private(blist); |
|
| 1123 |
|
| 1124 priv->buddies = g_hash_table_new_full( |
|
| 1125 (GHashFunc)_purple_blist_hbuddy_hash, |
|
| 1126 (GEqualFunc)_purple_blist_hbuddy_equal, |
|
| 1127 (GDestroyNotify)_purple_blist_hbuddy_free_key, NULL); |
|
| 1128 |
|
| 1129 manager = purple_account_manager_get_default(); |
|
| 1130 g_signal_connect_object(manager, "added", |
|
| 1131 G_CALLBACK(purple_buddy_list_account_added_cb), |
|
| 1132 blist, 0); |
|
| 1133 g_signal_connect_object(manager, "removed", |
|
| 1134 G_CALLBACK(purple_buddy_list_account_removed_cb), |
|
| 1135 blist, 0); |
|
| 1136 } |
|
| 1137 |
|
| 1138 /* GObject finalize function */ |
|
| 1139 static void |
|
| 1140 purple_buddy_list_finalize(GObject *object) |
|
| 1141 { |
|
| 1142 PurpleBuddyList *list = PURPLE_BUDDY_LIST(object); |
|
| 1143 PurpleBuddyListClass *klass = PURPLE_BUDDY_LIST_GET_CLASS(list); |
|
| 1144 PurpleBuddyListPrivate *priv = |
|
| 1145 purple_buddy_list_get_instance_private(list); |
|
| 1146 PurpleBlistNode *node, *next_node; |
|
| 1147 |
|
| 1148 g_hash_table_destroy(priv->buddies); |
|
| 1149 |
|
| 1150 node = priv->root; |
|
| 1151 while (node) { |
|
| 1152 next_node = node->next; |
|
| 1153 blist_node_destroy(klass, list, node); |
|
| 1154 node = next_node; |
|
| 1155 } |
|
| 1156 priv->root = NULL; |
|
| 1157 |
|
| 1158 G_OBJECT_CLASS(purple_buddy_list_parent_class)->finalize(object); |
|
| 1159 } |
|
| 1160 |
|
| 1161 /* Class initializer function */ |
|
| 1162 static void purple_buddy_list_class_init(PurpleBuddyListClass *klass) |
|
| 1163 { |
|
| 1164 GObjectClass *obj_class = G_OBJECT_CLASS(klass); |
|
| 1165 |
|
| 1166 obj_class->finalize = purple_buddy_list_finalize; |
|
| 1167 |
|
| 1168 klass->save_node = purple_blist_real_save_node; |
|
| 1169 klass->remove_node = purple_blist_real_save_node; |
|
| 1170 klass->save_account = purple_blist_real_save_account; |
|
| 1171 } |
|