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
23 #define _PURPLE_BLIST_C_
27 #include "conversation.h"
28 #include "dbus-maybe.h"
41 static PurpleBlistUiOps
*blist_ui_ops
= NULL
;
43 static PurpleBuddyList
*purplebuddylist
= NULL
;
46 * A hash table used for efficient lookups of buddies by name.
47 * PurpleAccount* => GHashTable*, with the inner hash table being
48 * struct _purple_hbuddy => PurpleBuddy*
50 static GHashTable
*buddies_cache
= NULL
;
53 * A hash table used for efficient lookups of groups by name.
54 * UTF-8 collate-key => PurpleGroup*.
56 static GHashTable
*groups_cache
= NULL
;
58 static guint save_timer
= 0;
59 static gboolean blist_loaded
= FALSE
;
61 /*********************************************************************
62 * Private utility functions *
63 *********************************************************************/
65 static PurpleBlistNode
*purple_blist_get_last_sibling(PurpleBlistNode
*node
)
67 PurpleBlistNode
*n
= node
;
75 static PurpleBlistNode
*purple_blist_get_last_child(PurpleBlistNode
*node
)
79 return purple_blist_get_last_sibling(node
->child
);
82 struct _list_account_buddies
{
84 PurpleAccount
*account
;
87 struct _purple_hbuddy
{
89 PurpleAccount
*account
;
90 PurpleBlistNode
*group
;
93 /* This function must not use purple_normalize */
94 static guint
_purple_blist_hbuddy_hash(struct _purple_hbuddy
*hb
)
96 return g_str_hash(hb
->name
) ^ g_direct_hash(hb
->group
) ^ g_direct_hash(hb
->account
);
99 /* This function must not use purple_normalize */
100 static guint
_purple_blist_hbuddy_equal(struct _purple_hbuddy
*hb1
, struct _purple_hbuddy
*hb2
)
102 return (hb1
->group
== hb2
->group
&&
103 hb1
->account
== hb2
->account
&&
104 g_str_equal(hb1
->name
, hb2
->name
));
107 static void _purple_blist_hbuddy_free_key(struct _purple_hbuddy
*hb
)
114 purple_blist_buddies_cache_add_account(PurpleAccount
*account
)
116 GHashTable
*account_buddies
= g_hash_table_new_full((GHashFunc
)_purple_blist_hbuddy_hash
,
117 (GEqualFunc
)_purple_blist_hbuddy_equal
,
118 (GDestroyNotify
)_purple_blist_hbuddy_free_key
, NULL
);
119 g_hash_table_insert(buddies_cache
, account
, account_buddies
);
123 purple_blist_buddies_cache_remove_account(const PurpleAccount
*account
)
125 g_hash_table_remove(buddies_cache
, account
);
129 /*********************************************************************
131 *********************************************************************/
134 value_to_xmlnode(gpointer key
, gpointer hvalue
, gpointer user_data
)
138 xmlnode
*node
, *child
;
141 name
= (const char *)key
;
142 value
= (PurpleValue
*)hvalue
;
143 node
= (xmlnode
*)user_data
;
145 g_return_if_fail(value
!= NULL
);
147 child
= xmlnode_new_child(node
, "setting");
148 xmlnode_set_attrib(child
, "name", name
);
150 if (purple_value_get_type(value
) == PURPLE_TYPE_INT
) {
151 xmlnode_set_attrib(child
, "type", "int");
152 g_snprintf(buf
, sizeof(buf
), "%d", purple_value_get_int(value
));
153 xmlnode_insert_data(child
, buf
, -1);
155 else if (purple_value_get_type(value
) == PURPLE_TYPE_STRING
) {
156 xmlnode_set_attrib(child
, "type", "string");
157 xmlnode_insert_data(child
, purple_value_get_string(value
), -1);
159 else if (purple_value_get_type(value
) == PURPLE_TYPE_BOOLEAN
) {
160 xmlnode_set_attrib(child
, "type", "bool");
161 g_snprintf(buf
, sizeof(buf
), "%d", purple_value_get_boolean(value
));
162 xmlnode_insert_data(child
, buf
, -1);
167 chat_component_to_xmlnode(gpointer key
, gpointer value
, gpointer user_data
)
171 xmlnode
*node
, *child
;
173 name
= (const char *)key
;
174 data
= (const char *)value
;
175 node
= (xmlnode
*)user_data
;
177 g_return_if_fail(data
!= NULL
);
179 child
= xmlnode_new_child(node
, "component");
180 xmlnode_set_attrib(child
, "name", name
);
181 xmlnode_insert_data(child
, data
, -1);
185 buddy_to_xmlnode(PurpleBlistNode
*bnode
)
187 xmlnode
*node
, *child
;
190 buddy
= (PurpleBuddy
*)bnode
;
192 node
= xmlnode_new("buddy");
193 xmlnode_set_attrib(node
, "account", purple_account_get_username(buddy
->account
));
194 xmlnode_set_attrib(node
, "proto", purple_account_get_protocol_id(buddy
->account
));
196 child
= xmlnode_new_child(node
, "name");
197 xmlnode_insert_data(child
, buddy
->name
, -1);
199 if (buddy
->alias
!= NULL
)
201 child
= xmlnode_new_child(node
, "alias");
202 xmlnode_insert_data(child
, buddy
->alias
, -1);
205 /* Write buddy settings */
206 g_hash_table_foreach(buddy
->node
.settings
, value_to_xmlnode
, node
);
212 contact_to_xmlnode(PurpleBlistNode
*cnode
)
214 xmlnode
*node
, *child
;
215 PurpleContact
*contact
;
216 PurpleBlistNode
*bnode
;
218 contact
= (PurpleContact
*)cnode
;
220 node
= xmlnode_new("contact");
222 if (contact
->alias
!= NULL
)
224 xmlnode_set_attrib(node
, "alias", contact
->alias
);
228 for (bnode
= cnode
->child
; bnode
!= NULL
; bnode
= bnode
->next
)
230 if (!PURPLE_BLIST_NODE_SHOULD_SAVE(bnode
))
232 if (PURPLE_BLIST_NODE_IS_BUDDY(bnode
))
234 child
= buddy_to_xmlnode(bnode
);
235 xmlnode_insert_child(node
, child
);
239 /* Write contact settings */
240 g_hash_table_foreach(cnode
->settings
, value_to_xmlnode
, node
);
246 chat_to_xmlnode(PurpleBlistNode
*cnode
)
248 xmlnode
*node
, *child
;
251 chat
= (PurpleChat
*)cnode
;
253 node
= xmlnode_new("chat");
254 xmlnode_set_attrib(node
, "proto", purple_account_get_protocol_id(chat
->account
));
255 xmlnode_set_attrib(node
, "account", purple_account_get_username(chat
->account
));
257 if (chat
->alias
!= NULL
)
259 child
= xmlnode_new_child(node
, "alias");
260 xmlnode_insert_data(child
, chat
->alias
, -1);
263 /* Write chat components */
264 g_hash_table_foreach(chat
->components
, chat_component_to_xmlnode
, node
);
266 /* Write chat settings */
267 g_hash_table_foreach(chat
->node
.settings
, value_to_xmlnode
, node
);
273 group_to_xmlnode(PurpleBlistNode
*gnode
)
275 xmlnode
*node
, *child
;
277 PurpleBlistNode
*cnode
;
279 group
= (PurpleGroup
*)gnode
;
281 node
= xmlnode_new("group");
282 xmlnode_set_attrib(node
, "name", group
->name
);
285 g_hash_table_foreach(group
->node
.settings
, value_to_xmlnode
, node
);
287 /* Write contacts and chats */
288 for (cnode
= gnode
->child
; cnode
!= NULL
; cnode
= cnode
->next
)
290 if (!PURPLE_BLIST_NODE_SHOULD_SAVE(cnode
))
292 if (PURPLE_BLIST_NODE_IS_CONTACT(cnode
))
294 child
= contact_to_xmlnode(cnode
);
295 xmlnode_insert_child(node
, child
);
297 else if (PURPLE_BLIST_NODE_IS_CHAT(cnode
))
299 child
= chat_to_xmlnode(cnode
);
300 xmlnode_insert_child(node
, child
);
308 accountprivacy_to_xmlnode(PurpleAccount
*account
)
310 xmlnode
*node
, *child
;
314 node
= xmlnode_new("account");
315 xmlnode_set_attrib(node
, "proto", purple_account_get_protocol_id(account
));
316 xmlnode_set_attrib(node
, "name", purple_account_get_username(account
));
317 g_snprintf(buf
, sizeof(buf
), "%d", account
->perm_deny
);
318 xmlnode_set_attrib(node
, "mode", buf
);
320 for (cur
= account
->permit
; cur
; cur
= cur
->next
)
322 child
= xmlnode_new_child(node
, "permit");
323 xmlnode_insert_data(child
, cur
->data
, -1);
326 for (cur
= account
->deny
; cur
; cur
= cur
->next
)
328 child
= xmlnode_new_child(node
, "block");
329 xmlnode_insert_data(child
, cur
->data
, -1);
336 blist_to_xmlnode(void)
338 xmlnode
*node
, *child
, *grandchild
;
339 PurpleBlistNode
*gnode
;
342 node
= xmlnode_new("purple");
343 xmlnode_set_attrib(node
, "version", "1.0");
346 child
= xmlnode_new_child(node
, "blist");
347 for (gnode
= purplebuddylist
->root
; gnode
!= NULL
; gnode
= gnode
->next
)
349 if (!PURPLE_BLIST_NODE_SHOULD_SAVE(gnode
))
351 if (PURPLE_BLIST_NODE_IS_GROUP(gnode
))
353 grandchild
= group_to_xmlnode(gnode
);
354 xmlnode_insert_child(child
, grandchild
);
358 /* Write privacy settings */
359 child
= xmlnode_new_child(node
, "privacy");
360 for (cur
= purple_accounts_get_all(); cur
!= NULL
; cur
= cur
->next
)
362 grandchild
= accountprivacy_to_xmlnode(cur
->data
);
363 xmlnode_insert_child(child
, grandchild
);
370 purple_blist_sync(void)
377 purple_debug_error("blist", "Attempted to save buddy list before it "
382 node
= blist_to_xmlnode();
383 data
= xmlnode_to_formatted_str(node
, NULL
);
384 purple_util_write_data_to_file("blist.xml", data
, -1);
390 save_cb(gpointer data
)
398 _purple_blist_schedule_save()
401 save_timer
= purple_timeout_add_seconds(5, save_cb
, NULL
);
405 purple_blist_save_account(PurpleAccount
*account
)
408 _purple_blist_schedule_save();
410 if (account
!= NULL
) {
411 /* Save the buddies and privacy data for this account */
413 /* Save all buddies and privacy data */
419 purple_blist_save_node(PurpleBlistNode
*node
)
421 _purple_blist_schedule_save();
424 void purple_blist_schedule_save()
426 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
428 /* Save everything */
429 if (ops
&& ops
->save_account
)
430 ops
->save_account(NULL
);
434 /*********************************************************************
435 * Reading from disk *
436 *********************************************************************/
439 parse_setting(PurpleBlistNode
*node
, xmlnode
*setting
)
441 const char *name
= xmlnode_get_attrib(setting
, "name");
442 const char *type
= xmlnode_get_attrib(setting
, "type");
443 char *value
= xmlnode_get_data(setting
);
448 if (!type
|| purple_strequal(type
, "string"))
449 purple_blist_node_set_string(node
, name
, value
);
450 else if (purple_strequal(type
, "bool"))
451 purple_blist_node_set_bool(node
, name
, atoi(value
));
452 else if (purple_strequal(type
, "int"))
453 purple_blist_node_set_int(node
, name
, atoi(value
));
459 parse_buddy(PurpleGroup
*group
, PurpleContact
*contact
, xmlnode
*bnode
)
461 PurpleAccount
*account
;
463 char *name
= NULL
, *alias
= NULL
;
464 const char *acct_name
, *proto
, *protocol
;
467 acct_name
= xmlnode_get_attrib(bnode
, "account");
468 protocol
= xmlnode_get_attrib(bnode
, "protocol");
469 protocol
= _purple_oscar_convert(acct_name
, protocol
); /* XXX: Remove */
470 proto
= xmlnode_get_attrib(bnode
, "proto");
471 proto
= _purple_oscar_convert(acct_name
, proto
); /* XXX: Remove */
473 if (!acct_name
|| (!proto
&& !protocol
))
476 account
= purple_accounts_find(acct_name
, proto
? proto
: protocol
);
481 if ((x
= xmlnode_get_child(bnode
, "name")))
482 name
= xmlnode_get_data(x
);
487 if ((x
= xmlnode_get_child(bnode
, "alias")))
488 alias
= xmlnode_get_data(x
);
490 buddy
= purple_buddy_new(account
, name
, alias
);
491 purple_blist_add_buddy(buddy
, contact
, group
,
492 purple_blist_get_last_child((PurpleBlistNode
*)contact
));
494 for (x
= xmlnode_get_child(bnode
, "setting"); x
; x
= xmlnode_get_next_twin(x
)) {
495 parse_setting((PurpleBlistNode
*)buddy
, x
);
503 parse_contact(PurpleGroup
*group
, xmlnode
*cnode
)
505 PurpleContact
*contact
= purple_contact_new();
509 purple_blist_add_contact(contact
, group
,
510 purple_blist_get_last_child((PurpleBlistNode
*)group
));
512 if ((alias
= xmlnode_get_attrib(cnode
, "alias"))) {
513 purple_blist_alias_contact(contact
, alias
);
516 for (x
= cnode
->child
; x
; x
= x
->next
) {
517 if (x
->type
!= XMLNODE_TYPE_TAG
)
519 if (purple_strequal(x
->name
, "buddy"))
520 parse_buddy(group
, contact
, x
);
521 else if (purple_strequal(x
->name
, "setting"))
522 parse_setting((PurpleBlistNode
*)contact
, x
);
525 /* if the contact is empty, don't keep it around. it causes problems */
526 if (!((PurpleBlistNode
*)contact
)->child
)
527 purple_blist_remove_contact(contact
);
531 parse_chat(PurpleGroup
*group
, xmlnode
*cnode
)
534 PurpleAccount
*account
;
535 const char *acct_name
, *proto
, *protocol
;
538 GHashTable
*components
;
540 acct_name
= xmlnode_get_attrib(cnode
, "account");
541 protocol
= xmlnode_get_attrib(cnode
, "protocol");
542 proto
= xmlnode_get_attrib(cnode
, "proto");
544 if (!acct_name
|| (!proto
&& !protocol
))
547 account
= purple_accounts_find(acct_name
, proto
? proto
: protocol
);
552 if ((x
= xmlnode_get_child(cnode
, "alias")))
553 alias
= xmlnode_get_data(x
);
555 components
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
557 for (x
= xmlnode_get_child(cnode
, "component"); x
; x
= xmlnode_get_next_twin(x
)) {
561 name
= xmlnode_get_attrib(x
, "name");
562 value
= xmlnode_get_data(x
);
563 g_hash_table_replace(components
, g_strdup(name
), value
);
566 chat
= purple_chat_new(account
, alias
, components
);
567 purple_blist_add_chat(chat
, group
,
568 purple_blist_get_last_child((PurpleBlistNode
*)group
));
570 for (x
= xmlnode_get_child(cnode
, "setting"); x
; x
= xmlnode_get_next_twin(x
)) {
571 parse_setting((PurpleBlistNode
*)chat
, x
);
578 parse_group(xmlnode
*groupnode
)
580 const char *name
= xmlnode_get_attrib(groupnode
, "name");
587 group
= purple_group_new(name
);
588 purple_blist_add_group(group
,
589 purple_blist_get_last_sibling(purplebuddylist
->root
));
591 for (cnode
= groupnode
->child
; cnode
; cnode
= cnode
->next
) {
592 if (cnode
->type
!= XMLNODE_TYPE_TAG
)
594 if (purple_strequal(cnode
->name
, "setting"))
595 parse_setting((PurpleBlistNode
*)group
, cnode
);
596 else if (purple_strequal(cnode
->name
, "contact") ||
597 purple_strequal(cnode
->name
, "person"))
598 parse_contact(group
, cnode
);
599 else if (purple_strequal(cnode
->name
, "chat"))
600 parse_chat(group
, cnode
);
604 /* TODO: Make static and rename to load_blist */
608 xmlnode
*purple
, *blist
, *privacy
;
612 purple
= purple_util_read_xml_from_file("blist.xml", _("buddy list"));
617 blist
= xmlnode_get_child(purple
, "blist");
620 for (groupnode
= xmlnode_get_child(blist
, "group"); groupnode
!= NULL
;
621 groupnode
= xmlnode_get_next_twin(groupnode
)) {
622 parse_group(groupnode
);
626 privacy
= xmlnode_get_child(purple
, "privacy");
629 for (anode
= privacy
->child
; anode
; anode
= anode
->next
) {
631 PurpleAccount
*account
;
633 const char *acct_name
, *proto
, *mode
, *protocol
;
635 acct_name
= xmlnode_get_attrib(anode
, "name");
636 protocol
= xmlnode_get_attrib(anode
, "protocol");
637 proto
= xmlnode_get_attrib(anode
, "proto");
638 mode
= xmlnode_get_attrib(anode
, "mode");
640 if (!acct_name
|| (!proto
&& !protocol
) || !mode
)
643 account
= purple_accounts_find(acct_name
, proto
? proto
: protocol
);
649 account
->perm_deny
= (imode
!= 0 ? imode
: PURPLE_PRIVACY_ALLOW_ALL
);
651 for (x
= anode
->child
; x
; x
= x
->next
) {
653 if (x
->type
!= XMLNODE_TYPE_TAG
)
656 if (purple_strequal(x
->name
, "permit")) {
657 name
= xmlnode_get_data(x
);
658 purple_privacy_permit_add(account
, name
, TRUE
);
660 } else if (purple_strequal(x
->name
, "block")) {
661 name
= xmlnode_get_data(x
);
662 purple_privacy_deny_add(account
, name
, TRUE
);
669 xmlnode_free(purple
);
671 /* This tells the buddy icon code to do its thing. */
672 _purple_buddy_icons_blist_loaded_cb();
676 /*********************************************************************
678 *********************************************************************/
681 purple_contact_compute_priority_buddy(PurpleContact
*contact
)
683 PurpleBlistNode
*bnode
;
684 PurpleBuddy
*new_priority
= NULL
;
686 g_return_if_fail(contact
!= NULL
);
688 contact
->priority
= NULL
;
689 for (bnode
= ((PurpleBlistNode
*)contact
)->child
;
695 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode
))
698 buddy
= (PurpleBuddy
*)bnode
;
699 if (new_priority
== NULL
)
701 new_priority
= buddy
;
705 if (purple_account_is_connected(buddy
->account
))
708 if (purple_account_is_connected(new_priority
->account
))
709 cmp
= purple_presence_compare(purple_buddy_get_presence(new_priority
),
710 purple_buddy_get_presence(buddy
));
712 if (cmp
> 0 || (cmp
== 0 &&
713 purple_prefs_get_bool("/purple/contact/last_match")))
715 new_priority
= buddy
;
720 contact
->priority
= new_priority
;
721 contact
->priority_valid
= TRUE
;
725 /*****************************************************************************
726 * Public API functions *
727 *****************************************************************************/
729 PurpleBuddyList
*purple_blist_new()
731 PurpleBlistUiOps
*ui_ops
;
733 PurpleBuddyList
*gbl
= g_new0(PurpleBuddyList
, 1);
734 PURPLE_DBUS_REGISTER_POINTER(gbl
, PurpleBuddyList
);
736 ui_ops
= purple_blist_get_ui_ops();
738 gbl
->buddies
= g_hash_table_new_full((GHashFunc
)_purple_blist_hbuddy_hash
,
739 (GEqualFunc
)_purple_blist_hbuddy_equal
,
740 (GDestroyNotify
)_purple_blist_hbuddy_free_key
, NULL
);
742 buddies_cache
= g_hash_table_new_full(g_direct_hash
, g_direct_equal
,
743 NULL
, (GDestroyNotify
)g_hash_table_destroy
);
745 groups_cache
= g_hash_table_new_full((GHashFunc
)g_str_hash
,
746 (GEqualFunc
)g_str_equal
,
747 (GDestroyNotify
)g_free
, NULL
);
749 for (account
= purple_accounts_get_all(); account
!= NULL
; account
= account
->next
)
751 purple_blist_buddies_cache_add_account(account
->data
);
754 if (ui_ops
!= NULL
&& ui_ops
->new_list
!= NULL
)
755 ui_ops
->new_list(gbl
);
761 purple_set_blist(PurpleBuddyList
*list
)
763 purplebuddylist
= list
;
769 return purplebuddylist
;
773 purple_blist_get_root()
775 return purplebuddylist
? purplebuddylist
->root
: NULL
;
779 append_buddy(gpointer key
, gpointer value
, gpointer user_data
)
781 GSList
**list
= user_data
;
782 *list
= g_slist_prepend(*list
, value
);
786 purple_blist_get_buddies()
788 GSList
*buddies
= NULL
;
790 if (!purplebuddylist
)
793 g_hash_table_foreach(purplebuddylist
->buddies
, append_buddy
, &buddies
);
798 purple_blist_get_ui_data()
800 return purplebuddylist
->ui_data
;
804 purple_blist_set_ui_data(void *ui_data
)
806 purplebuddylist
->ui_data
= ui_data
;
809 void purple_blist_show()
811 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
813 if (ops
&& ops
->show
)
814 ops
->show(purplebuddylist
);
817 void purple_blist_destroy()
819 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
821 purple_debug(PURPLE_DEBUG_INFO
, "blist", "Destroying\n");
823 if (ops
&& ops
->destroy
)
824 ops
->destroy(purplebuddylist
);
827 void purple_blist_set_visible(gboolean show
)
829 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
831 if (ops
&& ops
->set_visible
)
832 ops
->set_visible(purplebuddylist
, show
);
835 static PurpleBlistNode
*get_next_node(PurpleBlistNode
*node
, gboolean godeep
)
840 if (godeep
&& node
->child
)
846 return get_next_node(node
->parent
, FALSE
);
849 PurpleBlistNode
*purple_blist_node_next(PurpleBlistNode
*node
, gboolean offline
)
851 PurpleBlistNode
*ret
= node
;
854 return get_next_node(ret
, TRUE
);
857 ret
= get_next_node(ret
, TRUE
);
858 } while (ret
&& PURPLE_BLIST_NODE_IS_BUDDY(ret
) &&
859 !purple_account_is_connected(purple_buddy_get_account((PurpleBuddy
*)ret
)));
864 PurpleBlistNode
*purple_blist_node_get_parent(PurpleBlistNode
*node
)
866 return node
? node
->parent
: NULL
;
869 PurpleBlistNode
*purple_blist_node_get_first_child(PurpleBlistNode
*node
)
871 return node
? node
->child
: NULL
;
874 PurpleBlistNode
*purple_blist_node_get_sibling_next(PurpleBlistNode
*node
)
876 return node
? node
->next
: NULL
;
879 PurpleBlistNode
*purple_blist_node_get_sibling_prev(PurpleBlistNode
*node
)
881 return node
? node
->prev
: NULL
;
885 purple_blist_node_get_ui_data(const PurpleBlistNode
*node
)
887 g_return_val_if_fail(node
, NULL
);
889 return node
->ui_data
;
893 purple_blist_node_set_ui_data(PurpleBlistNode
*node
, void *ui_data
) {
894 g_return_if_fail(node
);
896 node
->ui_data
= ui_data
;
900 purple_blist_update_buddy_status(PurpleBuddy
*buddy
, PurpleStatus
*old_status
)
902 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
903 PurplePresence
*presence
;
904 PurpleStatus
*status
;
905 PurpleBlistNode
*cnode
;
907 g_return_if_fail(buddy
!= NULL
);
909 presence
= purple_buddy_get_presence(buddy
);
910 status
= purple_presence_get_active_status(presence
);
912 purple_debug_info("blist", "Updating buddy status for %s (%s)\n",
913 buddy
->name
, purple_account_get_protocol_name(buddy
->account
));
915 if (purple_status_is_online(status
) &&
916 !purple_status_is_online(old_status
)) {
918 purple_signal_emit(purple_blist_get_handle(), "buddy-signed-on", buddy
);
920 cnode
= buddy
->node
.parent
;
921 if (++(PURPLE_CONTACT(cnode
)->online
) == 1)
922 PURPLE_GROUP(cnode
->parent
)->online
++;
923 } else if (!purple_status_is_online(status
) &&
924 purple_status_is_online(old_status
)) {
926 purple_blist_node_set_int(&buddy
->node
, "last_seen", time(NULL
));
927 purple_signal_emit(purple_blist_get_handle(), "buddy-signed-off", buddy
);
929 cnode
= buddy
->node
.parent
;
930 if (--(PURPLE_CONTACT(cnode
)->online
) == 0)
931 PURPLE_GROUP(cnode
->parent
)->online
--;
933 purple_signal_emit(purple_blist_get_handle(),
934 "buddy-status-changed", buddy
, old_status
,
939 * This function used to only call the following two functions if one of
940 * the above signals had been triggered, but that's not good, because
941 * if someone's away message changes and they don't go from away to back
942 * to away then no signal is triggered.
944 * It's a safe assumption that SOMETHING called this function. PROBABLY
945 * because something, somewhere changed. Calling the stuff below
946 * certainly won't hurt anything. Unless you're on a K6-2 300.
948 purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy
));
949 if (ops
&& ops
->update
)
950 ops
->update(purplebuddylist
, (PurpleBlistNode
*)buddy
);
954 purple_blist_update_node_icon(PurpleBlistNode
*node
)
956 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
958 g_return_if_fail(node
!= NULL
);
960 if (ops
&& ops
->update
)
961 ops
->update(purplebuddylist
, node
);
965 purple_blist_update_buddy_icon(PurpleBuddy
*buddy
)
967 purple_blist_update_node_icon((PurpleBlistNode
*)buddy
);
971 * TODO: Maybe remove the call to this from server.c and call it
972 * from oscar.c and toc.c instead?
974 void purple_blist_rename_buddy(PurpleBuddy
*buddy
, const char *name
)
976 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
977 struct _purple_hbuddy
*hb
, *hb2
;
978 GHashTable
*account_buddies
;
980 g_return_if_fail(buddy
!= NULL
);
982 hb
= g_new(struct _purple_hbuddy
, 1);
983 hb
->name
= (gchar
*)purple_normalize(buddy
->account
, buddy
->name
);
984 hb
->account
= buddy
->account
;
985 hb
->group
= ((PurpleBlistNode
*)buddy
)->parent
->parent
;
986 g_hash_table_remove(purplebuddylist
->buddies
, hb
);
988 account_buddies
= g_hash_table_lookup(buddies_cache
, buddy
->account
);
989 g_hash_table_remove(account_buddies
, hb
);
991 hb
->name
= g_strdup(purple_normalize(buddy
->account
, name
));
992 g_hash_table_replace(purplebuddylist
->buddies
, hb
, buddy
);
994 hb2
= g_new(struct _purple_hbuddy
, 1);
995 hb2
->name
= g_strdup(hb
->name
);
996 hb2
->account
= buddy
->account
;
997 hb2
->group
= ((PurpleBlistNode
*)buddy
)->parent
->parent
;
999 g_hash_table_replace(account_buddies
, hb2
, buddy
);
1001 g_free(buddy
->name
);
1002 buddy
->name
= g_strdup(name
);
1004 if (ops
&& ops
->save_node
)
1005 ops
->save_node((PurpleBlistNode
*) buddy
);
1007 if (ops
&& ops
->update
)
1008 ops
->update(purplebuddylist
, (PurpleBlistNode
*)buddy
);
1012 purple_strings_are_different(const char *one
, const char *two
)
1014 return !((one
&& two
&& g_utf8_collate(one
, two
) == 0) ||
1015 ((one
== NULL
|| *one
== '\0') && (two
== NULL
|| *two
== '\0')));
1018 void purple_blist_alias_contact(PurpleContact
*contact
, const char *alias
)
1020 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
1021 PurpleConversation
*conv
;
1022 PurpleBlistNode
*bnode
;
1024 char *new_alias
= NULL
;
1026 g_return_if_fail(contact
!= NULL
);
1028 if ((alias
!= NULL
) && (*alias
!= '\0'))
1029 new_alias
= purple_utf8_strip_unprintables(alias
);
1031 if (!purple_strings_are_different(contact
->alias
, new_alias
)) {
1036 old_alias
= contact
->alias
;
1038 if ((new_alias
!= NULL
) && (*new_alias
!= '\0'))
1039 contact
->alias
= new_alias
;
1041 contact
->alias
= NULL
;
1042 g_free(new_alias
); /* could be "\0" */
1045 if (ops
&& ops
->save_node
)
1046 ops
->save_node((PurpleBlistNode
*) contact
);
1048 if (ops
&& ops
->update
)
1049 ops
->update(purplebuddylist
, (PurpleBlistNode
*)contact
);
1051 for(bnode
= ((PurpleBlistNode
*)contact
)->child
; bnode
!= NULL
; bnode
= bnode
->next
)
1053 PurpleBuddy
*buddy
= (PurpleBuddy
*)bnode
;
1055 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, buddy
->name
,
1058 purple_conversation_autoset_title(conv
);
1061 purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
1062 contact
, old_alias
);
1066 void purple_blist_alias_chat(PurpleChat
*chat
, const char *alias
)
1068 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
1070 char *new_alias
= NULL
;
1072 g_return_if_fail(chat
!= NULL
);
1074 if ((alias
!= NULL
) && (*alias
!= '\0'))
1075 new_alias
= purple_utf8_strip_unprintables(alias
);
1077 if (!purple_strings_are_different(chat
->alias
, new_alias
)) {
1082 old_alias
= chat
->alias
;
1084 if ((new_alias
!= NULL
) && (*new_alias
!= '\0'))
1085 chat
->alias
= new_alias
;
1088 g_free(new_alias
); /* could be "\0" */
1091 if (ops
&& ops
->save_node
)
1092 ops
->save_node((PurpleBlistNode
*) chat
);
1094 if (ops
&& ops
->update
)
1095 ops
->update(purplebuddylist
, (PurpleBlistNode
*)chat
);
1097 purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
1102 void purple_blist_alias_buddy(PurpleBuddy
*buddy
, const char *alias
)
1104 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
1105 PurpleConversation
*conv
;
1107 char *new_alias
= NULL
;
1109 g_return_if_fail(buddy
!= NULL
);
1111 if ((alias
!= NULL
) && (*alias
!= '\0'))
1112 new_alias
= purple_utf8_strip_unprintables(alias
);
1114 if (!purple_strings_are_different(buddy
->alias
, new_alias
)) {
1119 old_alias
= buddy
->alias
;
1121 if ((new_alias
!= NULL
) && (*new_alias
!= '\0'))
1122 buddy
->alias
= new_alias
;
1124 buddy
->alias
= NULL
;
1125 g_free(new_alias
); /* could be "\0" */
1128 if (ops
&& ops
->save_node
)
1129 ops
->save_node((PurpleBlistNode
*) buddy
);
1131 if (ops
&& ops
->update
)
1132 ops
->update(purplebuddylist
, (PurpleBlistNode
*)buddy
);
1134 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, buddy
->name
,
1137 purple_conversation_autoset_title(conv
);
1139 purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
1144 void purple_blist_server_alias_buddy(PurpleBuddy
*buddy
, const char *alias
)
1146 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
1147 PurpleConversation
*conv
;
1149 char *new_alias
= NULL
;
1151 g_return_if_fail(buddy
!= NULL
);
1153 if ((alias
!= NULL
) && (*alias
!= '\0') && g_utf8_validate(alias
, -1, NULL
))
1154 new_alias
= purple_utf8_strip_unprintables(alias
);
1156 if (!purple_strings_are_different(buddy
->server_alias
, new_alias
)) {
1161 old_alias
= buddy
->server_alias
;
1163 if ((new_alias
!= NULL
) && (*new_alias
!= '\0'))
1164 buddy
->server_alias
= new_alias
;
1166 buddy
->server_alias
= NULL
;
1167 g_free(new_alias
); /* could be "\0"; */
1170 if (ops
&& ops
->save_node
)
1171 ops
->save_node((PurpleBlistNode
*) buddy
);
1173 if (ops
&& ops
->update
)
1174 ops
->update(purplebuddylist
, (PurpleBlistNode
*)buddy
);
1176 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, buddy
->name
,
1179 purple_conversation_autoset_title(conv
);
1181 purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
1187 * TODO: If merging, prompt the user if they want to merge.
1189 void purple_blist_rename_group(PurpleGroup
*source
, const char *name
)
1191 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
1195 GList
*moved_buddies
= NULL
;
1198 g_return_if_fail(source
!= NULL
);
1199 g_return_if_fail(name
!= NULL
);
1201 new_name
= purple_utf8_strip_unprintables(name
);
1203 if (*new_name
== '\0' || purple_strequal(new_name
, source
->name
)) {
1208 dest
= purple_find_group(new_name
);
1209 if (dest
!= NULL
&& purple_utf8_strcasecmp(source
->name
, dest
->name
) != 0) {
1210 /* We're merging two groups */
1211 PurpleBlistNode
*prev
, *child
, *next
;
1213 prev
= purple_blist_get_last_child((PurpleBlistNode
*)dest
);
1214 child
= ((PurpleBlistNode
*)source
)->child
;
1217 * TODO: This seems like a dumb way to do this... why not just
1218 * append all children from the old group to the end of the new
1219 * one? PRPLs might be expecting to receive an add_buddy() for
1220 * each moved buddy...
1225 if (PURPLE_BLIST_NODE_IS_CONTACT(child
)) {
1226 PurpleBlistNode
*bnode
;
1227 purple_blist_add_contact((PurpleContact
*)child
, dest
, prev
);
1228 for (bnode
= child
->child
; bnode
!= NULL
; bnode
= bnode
->next
) {
1229 purple_blist_add_buddy((PurpleBuddy
*)bnode
, (PurpleContact
*)child
,
1231 moved_buddies
= g_list_append(moved_buddies
, bnode
);
1234 } else if (PURPLE_BLIST_NODE_IS_CHAT(child
)) {
1235 purple_blist_add_chat((PurpleChat
*)child
, dest
, prev
);
1238 purple_debug(PURPLE_DEBUG_ERROR
, "blist",
1239 "Unknown child type in group %s\n", source
->name
);
1244 /* Make a copy of the old group name and then delete the old group */
1245 old_name
= g_strdup(source
->name
);
1246 purple_blist_remove_group(source
);
1250 /* A simple rename */
1251 PurpleBlistNode
*cnode
, *bnode
;
1254 /* Build a GList of all buddies in this group */
1255 for (cnode
= ((PurpleBlistNode
*)source
)->child
; cnode
!= NULL
; cnode
= cnode
->next
) {
1256 if (PURPLE_BLIST_NODE_IS_CONTACT(cnode
))
1257 for (bnode
= cnode
->child
; bnode
!= NULL
; bnode
= bnode
->next
)
1258 moved_buddies
= g_list_append(moved_buddies
, bnode
);
1261 old_name
= source
->name
;
1262 source
->name
= new_name
;
1264 key
= g_utf8_collate_key(old_name
, -1);
1265 g_hash_table_remove(groups_cache
, key
);
1268 key
= g_utf8_collate_key(new_name
, -1);
1269 g_hash_table_insert(groups_cache
, key
, source
);
1272 /* Save our changes */
1273 if (ops
&& ops
->save_node
)
1274 ops
->save_node((PurpleBlistNode
*) source
);
1277 if (ops
&& ops
->update
)
1278 ops
->update(purplebuddylist
, (PurpleBlistNode
*)source
);
1280 /* Notify all PRPLs */
1281 /* TODO: Is this condition needed? Seems like it would always be TRUE */
1282 if(old_name
&& !purple_strequal(source
->name
, old_name
)) {
1283 for (accts
= purple_group_get_accounts(source
); accts
; accts
= g_slist_remove(accts
, accts
->data
)) {
1284 PurpleAccount
*account
= accts
->data
;
1285 PurpleConnection
*gc
= NULL
;
1286 PurplePlugin
*prpl
= NULL
;
1287 PurplePluginProtocolInfo
*prpl_info
= NULL
;
1288 GList
*l
= NULL
, *buddies
= NULL
;
1290 gc
= purple_account_get_connection(account
);
1293 prpl
= purple_connection_get_prpl(gc
);
1296 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(prpl
);
1301 for(l
= moved_buddies
; l
; l
= l
->next
) {
1302 PurpleBuddy
*buddy
= (PurpleBuddy
*)l
->data
;
1304 if(buddy
&& buddy
->account
== account
)
1305 buddies
= g_list_append(buddies
, (PurpleBlistNode
*)buddy
);
1308 if(prpl_info
->rename_group
) {
1309 prpl_info
->rename_group(gc
, old_name
, source
, buddies
);
1311 GList
*cur
, *groups
= NULL
;
1313 /* Make a list of what the groups each buddy is in */
1314 for(cur
= buddies
; cur
; cur
= cur
->next
) {
1315 PurpleBlistNode
*node
= (PurpleBlistNode
*)cur
->data
;
1316 groups
= g_list_prepend(groups
, node
->parent
->parent
);
1319 purple_account_remove_buddies(account
, buddies
, groups
);
1320 g_list_free(groups
);
1321 purple_account_add_buddies(account
, buddies
);
1324 g_list_free(buddies
);
1327 g_list_free(moved_buddies
);
1331 static void purple_blist_node_initialize_settings(PurpleBlistNode
*node
);
1333 PurpleChat
*purple_chat_new(PurpleAccount
*account
, const char *alias
, GHashTable
*components
)
1335 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
1338 g_return_val_if_fail(account
!= NULL
, NULL
);
1339 g_return_val_if_fail(components
!= NULL
, NULL
);
1341 chat
= g_new0(PurpleChat
, 1);
1342 chat
->account
= account
;
1343 if ((alias
!= NULL
) && (*alias
!= '\0'))
1344 chat
->alias
= purple_utf8_strip_unprintables(alias
);
1345 chat
->components
= components
;
1346 purple_blist_node_initialize_settings((PurpleBlistNode
*)chat
);
1347 ((PurpleBlistNode
*)chat
)->type
= PURPLE_BLIST_CHAT_NODE
;
1349 if (ops
!= NULL
&& ops
->new_node
!= NULL
)
1350 ops
->new_node((PurpleBlistNode
*)chat
);
1352 PURPLE_DBUS_REGISTER_POINTER(chat
, PurpleChat
);
1357 purple_chat_destroy(PurpleChat
*chat
)
1359 g_hash_table_destroy(chat
->components
);
1360 g_hash_table_destroy(chat
->node
.settings
);
1361 g_free(chat
->alias
);
1362 PURPLE_DBUS_UNREGISTER_POINTER(chat
);
1366 PurpleBuddy
*purple_buddy_new(PurpleAccount
*account
, const char *name
, const char *alias
)
1368 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
1371 g_return_val_if_fail(account
!= NULL
, NULL
);
1372 g_return_val_if_fail(name
!= NULL
, NULL
);
1374 buddy
= g_new0(PurpleBuddy
, 1);
1375 buddy
->account
= account
;
1376 buddy
->name
= purple_utf8_strip_unprintables(name
);
1377 buddy
->alias
= purple_utf8_strip_unprintables(alias
);
1378 buddy
->presence
= purple_presence_new_for_buddy(buddy
);
1379 ((PurpleBlistNode
*)buddy
)->type
= PURPLE_BLIST_BUDDY_NODE
;
1381 purple_presence_set_status_active(buddy
->presence
, "offline", TRUE
);
1383 purple_blist_node_initialize_settings((PurpleBlistNode
*)buddy
);
1385 if (ops
&& ops
->new_node
)
1386 ops
->new_node((PurpleBlistNode
*)buddy
);
1388 PURPLE_DBUS_REGISTER_POINTER(buddy
, PurpleBuddy
);
1393 purple_buddy_destroy(PurpleBuddy
*buddy
)
1396 PurplePluginProtocolInfo
*prpl_info
;
1399 * Tell the owner PRPL that we're about to free the buddy so it
1400 * can free proto_data
1402 prpl
= purple_find_prpl(purple_account_get_protocol_id(buddy
->account
));
1404 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(prpl
);
1405 if (prpl_info
&& prpl_info
->buddy_free
)
1406 prpl_info
->buddy_free(buddy
);
1409 /* Delete the node */
1410 purple_buddy_icon_unref(buddy
->icon
);
1411 g_hash_table_destroy(buddy
->node
.settings
);
1412 purple_presence_destroy(buddy
->presence
);
1413 g_free(buddy
->name
);
1414 g_free(buddy
->alias
);
1415 g_free(buddy
->server_alias
);
1417 PURPLE_DBUS_UNREGISTER_POINTER(buddy
);
1420 /* FIXME: Once PurpleBuddy is a GObject, timeout callbacks can
1421 * g_object_ref() it when connecting the callback and
1422 * g_object_unref() it in the handler. That way, it won't
1423 * get freed while the timeout is pending and this line can
1425 while (g_source_remove_by_user_data((gpointer
*)buddy
));
1429 purple_buddy_set_icon(PurpleBuddy
*buddy
, PurpleBuddyIcon
*icon
)
1431 g_return_if_fail(buddy
!= NULL
);
1433 if (buddy
->icon
!= icon
)
1435 purple_buddy_icon_unref(buddy
->icon
);
1436 buddy
->icon
= (icon
!= NULL
? purple_buddy_icon_ref(icon
) : NULL
);
1439 purple_signal_emit(purple_blist_get_handle(), "buddy-icon-changed", buddy
);
1441 purple_blist_update_node_icon((PurpleBlistNode
*)buddy
);
1445 purple_buddy_get_account(const PurpleBuddy
*buddy
)
1447 g_return_val_if_fail(buddy
!= NULL
, NULL
);
1449 return buddy
->account
;
1453 purple_buddy_get_name(const PurpleBuddy
*buddy
)
1455 g_return_val_if_fail(buddy
!= NULL
, NULL
);
1461 purple_buddy_get_icon(const PurpleBuddy
*buddy
)
1463 g_return_val_if_fail(buddy
!= NULL
, NULL
);
1469 purple_buddy_get_protocol_data(const PurpleBuddy
*buddy
)
1471 g_return_val_if_fail(buddy
!= NULL
, NULL
);
1473 return buddy
->proto_data
;
1477 purple_buddy_set_protocol_data(PurpleBuddy
*buddy
, gpointer data
)
1479 g_return_if_fail(buddy
!= NULL
);
1481 buddy
->proto_data
= data
;
1485 void purple_blist_add_chat(PurpleChat
*chat
, PurpleGroup
*group
, PurpleBlistNode
*node
)
1487 PurpleBlistNode
*cnode
= (PurpleBlistNode
*)chat
;
1488 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
1490 g_return_if_fail(chat
!= NULL
);
1491 g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT((PurpleBlistNode
*)chat
));
1495 group
= purple_group_new(_("Chats"));
1497 /* Add group to blist if isn't already on it. Fixes #2752. */
1498 if (!purple_find_group(group
->name
)) {
1499 purple_blist_add_group(group
,
1500 purple_blist_get_last_sibling(purplebuddylist
->root
));
1503 group
= (PurpleGroup
*)node
->parent
;
1506 /* if we're moving to overtop of ourselves, do nothing */
1510 if (cnode
->parent
) {
1511 /* This chat was already in the list and is
1514 ((PurpleGroup
*)cnode
->parent
)->totalsize
--;
1515 if (purple_account_is_connected(chat
->account
)) {
1516 ((PurpleGroup
*)cnode
->parent
)->online
--;
1517 ((PurpleGroup
*)cnode
->parent
)->currentsize
--;
1520 cnode
->next
->prev
= cnode
->prev
;
1522 cnode
->prev
->next
= cnode
->next
;
1523 if (cnode
->parent
->child
== cnode
)
1524 cnode
->parent
->child
= cnode
->next
;
1526 if (ops
&& ops
->remove
)
1527 ops
->remove(purplebuddylist
, cnode
);
1528 /* ops->remove() cleaned up the cnode's ui_data, so we need to
1529 * reinitialize it */
1530 if (ops
&& ops
->new_node
)
1531 ops
->new_node(cnode
);
1536 node
->next
->prev
= cnode
;
1537 cnode
->next
= node
->next
;
1539 cnode
->parent
= node
->parent
;
1541 ((PurpleGroup
*)node
->parent
)->totalsize
++;
1542 if (purple_account_is_connected(chat
->account
)) {
1543 ((PurpleGroup
*)node
->parent
)->online
++;
1544 ((PurpleGroup
*)node
->parent
)->currentsize
++;
1547 if (((PurpleBlistNode
*)group
)->child
)
1548 ((PurpleBlistNode
*)group
)->child
->prev
= cnode
;
1549 cnode
->next
= ((PurpleBlistNode
*)group
)->child
;
1551 ((PurpleBlistNode
*)group
)->child
= cnode
;
1552 cnode
->parent
= (PurpleBlistNode
*)group
;
1554 if (purple_account_is_connected(chat
->account
)) {
1556 group
->currentsize
++;
1560 if (ops
&& ops
->save_node
)
1561 ops
->save_node(cnode
);
1563 if (ops
&& ops
->update
)
1564 ops
->update(purplebuddylist
, (PurpleBlistNode
*)cnode
);
1566 purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
1570 void purple_blist_add_buddy(PurpleBuddy
*buddy
, PurpleContact
*contact
, PurpleGroup
*group
, PurpleBlistNode
*node
)
1572 PurpleBlistNode
*cnode
, *bnode
;
1575 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
1576 struct _purple_hbuddy
*hb
, *hb2
;
1577 GHashTable
*account_buddies
;
1579 g_return_if_fail(buddy
!= NULL
);
1580 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY((PurpleBlistNode
*)buddy
));
1582 bnode
= (PurpleBlistNode
*)buddy
;
1584 /* if we're moving to overtop of ourselves, do nothing */
1585 if (bnode
== node
|| (!node
&& bnode
->parent
&&
1586 contact
&& bnode
->parent
== (PurpleBlistNode
*)contact
1587 && bnode
== bnode
->parent
->child
))
1590 if (node
&& PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
1591 c
= (PurpleContact
*)node
->parent
;
1592 g
= (PurpleGroup
*)node
->parent
->parent
;
1593 } else if (contact
) {
1595 g
= PURPLE_GROUP(PURPLE_BLIST_NODE(c
)->parent
);
1599 g
= purple_group_new(_("Buddies"));
1600 /* Add group to blist if isn't already on it. Fixes #2752. */
1601 if (!purple_find_group(g
->name
)) {
1602 purple_blist_add_group(g
,
1603 purple_blist_get_last_sibling(purplebuddylist
->root
));
1605 c
= purple_contact_new();
1606 purple_blist_add_contact(c
, g
,
1607 purple_blist_get_last_child((PurpleBlistNode
*)g
));
1610 cnode
= (PurpleBlistNode
*)c
;
1612 if (bnode
->parent
) {
1613 if (PURPLE_BUDDY_IS_ONLINE(buddy
)) {
1614 ((PurpleContact
*)bnode
->parent
)->online
--;
1615 if (((PurpleContact
*)bnode
->parent
)->online
== 0)
1616 ((PurpleGroup
*)bnode
->parent
->parent
)->online
--;
1618 if (purple_account_is_connected(buddy
->account
)) {
1619 ((PurpleContact
*)bnode
->parent
)->currentsize
--;
1620 if (((PurpleContact
*)bnode
->parent
)->currentsize
== 0)
1621 ((PurpleGroup
*)bnode
->parent
->parent
)->currentsize
--;
1623 ((PurpleContact
*)bnode
->parent
)->totalsize
--;
1624 /* the group totalsize will be taken care of by remove_contact below */
1626 if (bnode
->parent
->parent
!= (PurpleBlistNode
*)g
)
1627 serv_move_buddy(buddy
, (PurpleGroup
*)bnode
->parent
->parent
, g
);
1630 bnode
->next
->prev
= bnode
->prev
;
1632 bnode
->prev
->next
= bnode
->next
;
1633 if (bnode
->parent
->child
== bnode
)
1634 bnode
->parent
->child
= bnode
->next
;
1636 if (ops
&& ops
->remove
)
1637 ops
->remove(purplebuddylist
, bnode
);
1639 if (bnode
->parent
->parent
!= (PurpleBlistNode
*)g
) {
1640 struct _purple_hbuddy hb
;
1641 hb
.name
= (gchar
*)purple_normalize(buddy
->account
, buddy
->name
);
1642 hb
.account
= buddy
->account
;
1643 hb
.group
= bnode
->parent
->parent
;
1644 g_hash_table_remove(purplebuddylist
->buddies
, &hb
);
1646 account_buddies
= g_hash_table_lookup(buddies_cache
, buddy
->account
);
1647 g_hash_table_remove(account_buddies
, &hb
);
1650 if (!bnode
->parent
->child
) {
1651 purple_blist_remove_contact((PurpleContact
*)bnode
->parent
);
1653 purple_contact_invalidate_priority_buddy((PurpleContact
*)bnode
->parent
);
1654 if (ops
&& ops
->update
)
1655 ops
->update(purplebuddylist
, bnode
->parent
);
1659 if (node
&& PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
1661 node
->next
->prev
= bnode
;
1662 bnode
->next
= node
->next
;
1664 bnode
->parent
= node
->parent
;
1668 cnode
->child
->prev
= bnode
;
1670 bnode
->next
= cnode
->child
;
1671 cnode
->child
= bnode
;
1672 bnode
->parent
= cnode
;
1675 if (PURPLE_BUDDY_IS_ONLINE(buddy
)) {
1676 if (++(PURPLE_CONTACT(bnode
->parent
)->online
) == 1)
1677 PURPLE_GROUP(bnode
->parent
->parent
)->online
++;
1679 if (purple_account_is_connected(buddy
->account
)) {
1680 if (++(PURPLE_CONTACT(bnode
->parent
)->currentsize
) == 1)
1681 PURPLE_GROUP(bnode
->parent
->parent
)->currentsize
++;
1683 PURPLE_CONTACT(bnode
->parent
)->totalsize
++;
1685 hb
= g_new(struct _purple_hbuddy
, 1);
1686 hb
->name
= g_strdup(purple_normalize(buddy
->account
, buddy
->name
));
1687 hb
->account
= buddy
->account
;
1688 hb
->group
= ((PurpleBlistNode
*)buddy
)->parent
->parent
;
1690 g_hash_table_replace(purplebuddylist
->buddies
, hb
, buddy
);
1692 account_buddies
= g_hash_table_lookup(buddies_cache
, buddy
->account
);
1694 hb2
= g_new(struct _purple_hbuddy
, 1);
1695 hb2
->name
= g_strdup(hb
->name
);
1696 hb2
->account
= buddy
->account
;
1697 hb2
->group
= ((PurpleBlistNode
*)buddy
)->parent
->parent
;
1699 g_hash_table_replace(account_buddies
, hb2
, buddy
);
1701 purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy
));
1703 if (ops
&& ops
->save_node
)
1704 ops
->save_node((PurpleBlistNode
*) buddy
);
1706 if (ops
&& ops
->update
)
1707 ops
->update(purplebuddylist
, (PurpleBlistNode
*)buddy
);
1709 /* Signal that the buddy has been added */
1710 purple_signal_emit(purple_blist_get_handle(), "buddy-added", buddy
);
1712 purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
1713 PURPLE_BLIST_NODE(buddy
));
1716 PurpleContact
*purple_contact_new()
1718 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
1720 PurpleContact
*contact
= g_new0(PurpleContact
, 1);
1721 contact
->totalsize
= 0;
1722 contact
->currentsize
= 0;
1723 contact
->online
= 0;
1724 purple_blist_node_initialize_settings((PurpleBlistNode
*)contact
);
1725 ((PurpleBlistNode
*)contact
)->type
= PURPLE_BLIST_CONTACT_NODE
;
1727 if (ops
&& ops
->new_node
)
1728 ops
->new_node((PurpleBlistNode
*)contact
);
1730 PURPLE_DBUS_REGISTER_POINTER(contact
, PurpleContact
);
1735 purple_contact_destroy(PurpleContact
*contact
)
1737 g_hash_table_destroy(contact
->node
.settings
);
1738 g_free(contact
->alias
);
1739 PURPLE_DBUS_UNREGISTER_POINTER(contact
);
1744 purple_contact_get_group(const PurpleContact
*contact
)
1746 g_return_val_if_fail(contact
, NULL
);
1748 return (PurpleGroup
*)(((PurpleBlistNode
*)contact
)->parent
);
1751 void purple_contact_set_alias(PurpleContact
*contact
, const char *alias
)
1753 purple_blist_alias_contact(contact
,alias
);
1756 const char *purple_contact_get_alias(PurpleContact
* contact
)
1758 g_return_val_if_fail(contact
!= NULL
, NULL
);
1761 return contact
->alias
;
1763 return purple_buddy_get_alias(purple_contact_get_priority_buddy(contact
));
1766 gboolean
purple_contact_on_account(PurpleContact
*c
, PurpleAccount
*account
)
1768 PurpleBlistNode
*bnode
, *cnode
= (PurpleBlistNode
*) c
;
1770 g_return_val_if_fail(c
!= NULL
, FALSE
);
1771 g_return_val_if_fail(account
!= NULL
, FALSE
);
1773 for (bnode
= cnode
->child
; bnode
; bnode
= bnode
->next
) {
1776 if (! PURPLE_BLIST_NODE_IS_BUDDY(bnode
))
1779 buddy
= (PurpleBuddy
*)bnode
;
1780 if (buddy
->account
== account
)
1786 void purple_contact_invalidate_priority_buddy(PurpleContact
*contact
)
1788 g_return_if_fail(contact
!= NULL
);
1790 contact
->priority_valid
= FALSE
;
1793 PurpleGroup
*purple_group_new(const char *name
)
1795 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
1798 g_return_val_if_fail(name
!= NULL
, NULL
);
1799 g_return_val_if_fail(*name
!= '\0', NULL
);
1801 group
= purple_find_group(name
);
1805 group
= g_new0(PurpleGroup
, 1);
1806 group
->name
= purple_utf8_strip_unprintables(name
);
1807 group
->totalsize
= 0;
1808 group
->currentsize
= 0;
1810 purple_blist_node_initialize_settings((PurpleBlistNode
*)group
);
1811 ((PurpleBlistNode
*)group
)->type
= PURPLE_BLIST_GROUP_NODE
;
1813 if (ops
&& ops
->new_node
)
1814 ops
->new_node((PurpleBlistNode
*)group
);
1816 PURPLE_DBUS_REGISTER_POINTER(group
, PurpleGroup
);
1821 purple_group_destroy(PurpleGroup
*group
)
1823 g_hash_table_destroy(group
->node
.settings
);
1824 g_free(group
->name
);
1825 PURPLE_DBUS_UNREGISTER_POINTER(group
);
1829 void purple_blist_add_contact(PurpleContact
*contact
, PurpleGroup
*group
, PurpleBlistNode
*node
)
1831 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
1833 PurpleBlistNode
*gnode
, *cnode
, *bnode
;
1835 g_return_if_fail(contact
!= NULL
);
1836 g_return_if_fail(PURPLE_BLIST_NODE_IS_CONTACT((PurpleBlistNode
*)contact
));
1838 if (PURPLE_BLIST_NODE(contact
) == node
)
1841 if (node
&& (PURPLE_BLIST_NODE_IS_CONTACT(node
) ||
1842 PURPLE_BLIST_NODE_IS_CHAT(node
)))
1843 g
= (PurpleGroup
*)node
->parent
;
1847 g
= purple_find_group(_("Buddies"));
1849 g
= purple_group_new(_("Buddies"));
1850 purple_blist_add_group(g
,
1851 purple_blist_get_last_sibling(purplebuddylist
->root
));
1855 gnode
= (PurpleBlistNode
*)g
;
1856 cnode
= (PurpleBlistNode
*)contact
;
1858 if (cnode
->parent
) {
1859 if (cnode
->parent
->child
== cnode
)
1860 cnode
->parent
->child
= cnode
->next
;
1862 cnode
->prev
->next
= cnode
->next
;
1864 cnode
->next
->prev
= cnode
->prev
;
1866 if (cnode
->parent
!= gnode
) {
1867 bnode
= cnode
->child
;
1869 PurpleBlistNode
*next_bnode
= bnode
->next
;
1870 PurpleBuddy
*b
= (PurpleBuddy
*)bnode
;
1871 GHashTable
*account_buddies
;
1873 struct _purple_hbuddy
*hb
, *hb2
;
1875 hb
= g_new(struct _purple_hbuddy
, 1);
1876 hb
->name
= g_strdup(purple_normalize(b
->account
, b
->name
));
1877 hb
->account
= b
->account
;
1878 hb
->group
= cnode
->parent
;
1880 g_hash_table_remove(purplebuddylist
->buddies
, hb
);
1882 account_buddies
= g_hash_table_lookup(buddies_cache
, b
->account
);
1883 g_hash_table_remove(account_buddies
, hb
);
1885 if (!purple_find_buddy_in_group(b
->account
, b
->name
, g
)) {
1887 g_hash_table_replace(purplebuddylist
->buddies
, hb
, b
);
1889 hb2
= g_new(struct _purple_hbuddy
, 1);
1890 hb2
->name
= g_strdup(hb
->name
);
1891 hb2
->account
= b
->account
;
1894 g_hash_table_replace(account_buddies
, hb2
, b
);
1896 if (purple_account_get_connection(b
->account
))
1897 serv_move_buddy(b
, (PurpleGroup
*)cnode
->parent
, g
);
1899 gboolean empty_contact
= FALSE
;
1901 /* this buddy already exists in the group, so we're
1902 * gonna delete it instead */
1905 if (purple_account_get_connection(b
->account
))
1906 purple_account_remove_buddy(b
->account
, b
, (PurpleGroup
*)cnode
->parent
);
1908 if (!cnode
->child
->next
)
1909 empty_contact
= TRUE
;
1910 purple_blist_remove_buddy(b
);
1912 /** in purple_blist_remove_buddy(), if the last buddy in a
1913 * contact is removed, the contact is cleaned up and
1914 * g_free'd, so we mustn't try to reference bnode->next */
1922 if (contact
->online
> 0)
1923 ((PurpleGroup
*)cnode
->parent
)->online
--;
1924 if (contact
->currentsize
> 0)
1925 ((PurpleGroup
*)cnode
->parent
)->currentsize
--;
1926 ((PurpleGroup
*)cnode
->parent
)->totalsize
--;
1928 if (ops
&& ops
->remove
)
1929 ops
->remove(purplebuddylist
, cnode
);
1931 if (ops
&& ops
->remove_node
)
1932 ops
->remove_node(cnode
);
1935 if (node
&& (PURPLE_BLIST_NODE_IS_CONTACT(node
) ||
1936 PURPLE_BLIST_NODE_IS_CHAT(node
))) {
1938 node
->next
->prev
= cnode
;
1939 cnode
->next
= node
->next
;
1941 cnode
->parent
= node
->parent
;
1945 gnode
->child
->prev
= cnode
;
1947 cnode
->next
= gnode
->child
;
1948 gnode
->child
= cnode
;
1949 cnode
->parent
= gnode
;
1952 if (contact
->online
> 0)
1954 if (contact
->currentsize
> 0)
1958 if (ops
&& ops
->save_node
)
1961 ops
->save_node(cnode
);
1962 for (bnode
= cnode
->child
; bnode
; bnode
= bnode
->next
)
1963 ops
->save_node(bnode
);
1966 if (ops
&& ops
->update
)
1969 ops
->update(purplebuddylist
, cnode
);
1971 for (bnode
= cnode
->child
; bnode
; bnode
= bnode
->next
)
1972 ops
->update(purplebuddylist
, bnode
);
1976 void purple_blist_merge_contact(PurpleContact
*source
, PurpleBlistNode
*node
)
1978 PurpleBlistNode
*sourcenode
= (PurpleBlistNode
*)source
;
1979 PurpleBlistNode
*prev
, *cur
, *next
;
1980 PurpleContact
*target
;
1982 g_return_if_fail(source
!= NULL
);
1983 g_return_if_fail(node
!= NULL
);
1985 if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
1986 target
= (PurpleContact
*)node
;
1987 prev
= purple_blist_get_last_child(node
);
1988 } else if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
1989 target
= (PurpleContact
*)node
->parent
;
1995 if (source
== target
|| !target
)
1998 next
= sourcenode
->child
;
2003 if (PURPLE_BLIST_NODE_IS_BUDDY(cur
)) {
2004 purple_blist_add_buddy((PurpleBuddy
*)cur
, target
, NULL
, prev
);
2010 void purple_blist_add_group(PurpleGroup
*group
, PurpleBlistNode
*node
)
2012 PurpleBlistUiOps
*ops
;
2013 PurpleBlistNode
*gnode
= (PurpleBlistNode
*)group
;
2016 g_return_if_fail(group
!= NULL
);
2017 g_return_if_fail(PURPLE_BLIST_NODE_IS_GROUP((PurpleBlistNode
*)group
));
2019 ops
= purple_blist_get_ui_ops();
2021 /* if we're moving to overtop of ourselves, do nothing */
2022 if (gnode
== node
) {
2023 if (!purplebuddylist
->root
)
2029 if (purple_find_group(group
->name
)) {
2030 /* This is just being moved */
2032 if (ops
&& ops
->remove
)
2033 ops
->remove(purplebuddylist
, (PurpleBlistNode
*)group
);
2035 if (gnode
== purplebuddylist
->root
)
2036 purplebuddylist
->root
= gnode
->next
;
2038 gnode
->prev
->next
= gnode
->next
;
2040 gnode
->next
->prev
= gnode
->prev
;
2042 key
= g_utf8_collate_key(group
->name
, -1);
2043 g_hash_table_insert(groups_cache
, key
, group
);
2046 if (node
&& PURPLE_BLIST_NODE_IS_GROUP(node
)) {
2047 gnode
->next
= node
->next
;
2050 node
->next
->prev
= gnode
;
2053 if (purplebuddylist
->root
)
2054 purplebuddylist
->root
->prev
= gnode
;
2055 gnode
->next
= purplebuddylist
->root
;
2057 purplebuddylist
->root
= gnode
;
2060 if (ops
&& ops
->save_node
) {
2061 ops
->save_node(gnode
);
2062 for (node
= gnode
->child
; node
; node
= node
->next
)
2063 ops
->save_node(node
);
2066 if (ops
&& ops
->update
) {
2067 ops
->update(purplebuddylist
, gnode
);
2068 for (node
= gnode
->child
; node
; node
= node
->next
)
2069 ops
->update(purplebuddylist
, node
);
2072 purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
2076 void purple_blist_remove_contact(PurpleContact
*contact
)
2078 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
2079 PurpleBlistNode
*node
, *gnode
;
2081 g_return_if_fail(contact
!= NULL
);
2083 node
= (PurpleBlistNode
*)contact
;
2084 gnode
= node
->parent
;
2088 * If this contact has children then remove them. When the last
2089 * buddy is removed from the contact, the contact is automatically
2092 while (node
->child
->next
) {
2093 purple_blist_remove_buddy((PurpleBuddy
*)node
->child
);
2096 * Remove the last buddy and trigger the deletion of the contact.
2097 * It would probably be cleaner if contact-deletion was done after
2098 * a timeout? Or if it had to be done manually, like below?
2100 purple_blist_remove_buddy((PurpleBuddy
*)node
->child
);
2102 /* Remove the node from its parent */
2103 if (gnode
->child
== node
)
2104 gnode
->child
= node
->next
;
2106 node
->prev
->next
= node
->next
;
2108 node
->next
->prev
= node
->prev
;
2111 if (ops
&& ops
->remove
)
2112 ops
->remove(purplebuddylist
, node
);
2114 if (ops
&& ops
->remove_node
)
2115 ops
->remove_node(node
);
2117 purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
2118 PURPLE_BLIST_NODE(contact
));
2120 /* Delete the node */
2121 purple_contact_destroy(contact
);
2125 void purple_blist_remove_buddy(PurpleBuddy
*buddy
)
2127 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
2128 PurpleBlistNode
*node
, *cnode
, *gnode
;
2129 PurpleContact
*contact
;
2131 struct _purple_hbuddy hb
;
2132 GHashTable
*account_buddies
;
2134 g_return_if_fail(buddy
!= NULL
);
2136 node
= (PurpleBlistNode
*)buddy
;
2137 cnode
= node
->parent
;
2138 gnode
= (cnode
!= NULL
) ? cnode
->parent
: NULL
;
2139 contact
= (PurpleContact
*)cnode
;
2140 group
= (PurpleGroup
*)gnode
;
2142 /* Remove the node from its parent */
2144 node
->prev
->next
= node
->next
;
2146 node
->next
->prev
= node
->prev
;
2147 if ((cnode
!= NULL
) && (cnode
->child
== node
))
2148 cnode
->child
= node
->next
;
2150 /* Adjust size counts */
2151 if (contact
!= NULL
) {
2152 if (PURPLE_BUDDY_IS_ONLINE(buddy
)) {
2154 if (contact
->online
== 0)
2157 if (purple_account_is_connected(buddy
->account
)) {
2158 contact
->currentsize
--;
2159 if (contact
->currentsize
== 0)
2160 group
->currentsize
--;
2162 contact
->totalsize
--;
2164 /* Re-sort the contact */
2165 if (cnode
->child
&& contact
->priority
== buddy
) {
2166 purple_contact_invalidate_priority_buddy(contact
);
2167 if (ops
&& ops
->update
)
2168 ops
->update(purplebuddylist
, cnode
);
2172 /* Remove this buddy from the buddies hash table */
2173 hb
.name
= (gchar
*)purple_normalize(buddy
->account
, buddy
->name
);
2174 hb
.account
= buddy
->account
;
2176 g_hash_table_remove(purplebuddylist
->buddies
, &hb
);
2178 account_buddies
= g_hash_table_lookup(buddies_cache
, buddy
->account
);
2179 g_hash_table_remove(account_buddies
, &hb
);
2182 if (ops
&& ops
->remove
)
2183 ops
->remove(purplebuddylist
, node
);
2185 if (ops
&& ops
->remove_node
)
2186 ops
->remove_node(node
);
2188 /* Remove this buddy's pounces */
2189 purple_pounce_destroy_all_by_buddy(buddy
);
2191 /* Signal that the buddy has been removed before freeing the memory for it */
2192 purple_signal_emit(purple_blist_get_handle(), "buddy-removed", buddy
);
2194 purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
2195 PURPLE_BLIST_NODE(buddy
));
2197 purple_buddy_destroy(buddy
);
2199 /* If the contact is empty then remove it */
2200 if ((contact
!= NULL
) && !cnode
->child
)
2201 purple_blist_remove_contact(contact
);
2204 void purple_blist_remove_chat(PurpleChat
*chat
)
2206 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
2207 PurpleBlistNode
*node
, *gnode
;
2210 g_return_if_fail(chat
!= NULL
);
2212 node
= (PurpleBlistNode
*)chat
;
2213 gnode
= node
->parent
;
2214 group
= (PurpleGroup
*)gnode
;
2218 /* Remove the node from its parent */
2219 if (gnode
->child
== node
)
2220 gnode
->child
= node
->next
;
2222 node
->prev
->next
= node
->next
;
2224 node
->next
->prev
= node
->prev
;
2226 /* Adjust size counts */
2227 if (purple_account_is_connected(chat
->account
)) {
2229 group
->currentsize
--;
2236 if (ops
&& ops
->remove
)
2237 ops
->remove(purplebuddylist
, node
);
2239 if (ops
&& ops
->remove_node
)
2240 ops
->remove_node(node
);
2242 purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
2243 PURPLE_BLIST_NODE(chat
));
2245 /* Delete the node */
2246 purple_chat_destroy(chat
);
2249 void purple_blist_remove_group(PurpleGroup
*group
)
2251 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
2252 PurpleBlistNode
*node
;
2256 g_return_if_fail(group
!= NULL
);
2258 node
= (PurpleBlistNode
*)group
;
2260 /* Make sure the group is empty */
2264 /* Remove the node from its parent */
2265 if (purplebuddylist
->root
== node
)
2266 purplebuddylist
->root
= node
->next
;
2268 node
->prev
->next
= node
->next
;
2270 node
->next
->prev
= node
->prev
;
2272 key
= g_utf8_collate_key(group
->name
, -1);
2273 g_hash_table_remove(groups_cache
, key
);
2277 if (ops
&& ops
->remove
)
2278 ops
->remove(purplebuddylist
, node
);
2280 if (ops
&& ops
->remove_node
)
2281 ops
->remove_node(node
);
2283 purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
2284 PURPLE_BLIST_NODE(group
));
2286 /* Remove the group from all accounts that are online */
2287 for (l
= purple_connections_get_all(); l
!= NULL
; l
= l
->next
)
2289 PurpleConnection
*gc
= (PurpleConnection
*)l
->data
;
2291 if (purple_connection_get_state(gc
) == PURPLE_CONNECTED
)
2292 purple_account_remove_group(purple_connection_get_account(gc
), group
);
2295 /* Delete the node */
2296 purple_group_destroy(group
);
2299 PurpleBuddy
*purple_contact_get_priority_buddy(PurpleContact
*contact
)
2301 g_return_val_if_fail(contact
!= NULL
, NULL
);
2303 if (!contact
->priority_valid
)
2304 purple_contact_compute_priority_buddy(contact
);
2306 return contact
->priority
;
2309 const char *purple_buddy_get_alias_only(PurpleBuddy
*buddy
)
2311 g_return_val_if_fail(buddy
!= NULL
, NULL
);
2313 if ((buddy
->alias
!= NULL
) && (*buddy
->alias
!= '\0')) {
2314 return buddy
->alias
;
2315 } else if ((buddy
->server_alias
!= NULL
) &&
2316 (*buddy
->server_alias
!= '\0')) {
2318 return buddy
->server_alias
;
2325 const char *purple_buddy_get_contact_alias(PurpleBuddy
*buddy
)
2329 g_return_val_if_fail(buddy
!= NULL
, NULL
);
2331 /* Search for an alias for the buddy. In order of precedence: */
2332 /* The buddy alias */
2333 if (buddy
->alias
!= NULL
)
2334 return buddy
->alias
;
2336 /* The contact alias */
2337 c
= purple_buddy_get_contact(buddy
);
2338 if ((c
!= NULL
) && (c
->alias
!= NULL
))
2341 /* The server alias */
2342 if ((buddy
->server_alias
) && (*buddy
->server_alias
))
2343 return buddy
->server_alias
;
2345 /* The buddy's user name (i.e. no alias) */
2350 const char *purple_buddy_get_alias(PurpleBuddy
*buddy
)
2352 g_return_val_if_fail(buddy
!= NULL
, NULL
);
2354 /* Search for an alias for the buddy. In order of precedence: */
2355 /* The buddy alias */
2356 if (buddy
->alias
!= NULL
)
2357 return buddy
->alias
;
2359 /* The server alias */
2360 if ((buddy
->server_alias
) && (*buddy
->server_alias
))
2361 return buddy
->server_alias
;
2363 /* The buddy's user name (i.e. no alias) */
2367 const char *purple_buddy_get_local_buddy_alias(PurpleBuddy
*buddy
)
2369 g_return_val_if_fail(buddy
, NULL
);
2370 return buddy
->alias
;
2373 const char *purple_buddy_get_server_alias(PurpleBuddy
*buddy
)
2375 g_return_val_if_fail(buddy
!= NULL
, NULL
);
2377 if ((buddy
->server_alias
) && (*buddy
->server_alias
))
2378 return buddy
->server_alias
;
2383 const char *purple_buddy_get_local_alias(PurpleBuddy
*buddy
)
2387 g_return_val_if_fail(buddy
!= NULL
, NULL
);
2389 /* Search for an alias for the buddy. In order of precedence: */
2390 /* The buddy alias */
2391 if (buddy
->alias
!= NULL
)
2392 return buddy
->alias
;
2394 /* The contact alias */
2395 c
= purple_buddy_get_contact(buddy
);
2396 if ((c
!= NULL
) && (c
->alias
!= NULL
))
2399 /* The buddy's user name (i.e. no alias) */
2403 const char *purple_chat_get_name(PurpleChat
*chat
)
2407 PurplePluginProtocolInfo
*prpl_info
= NULL
;
2409 g_return_val_if_fail(chat
!= NULL
, NULL
);
2411 if ((chat
->alias
!= NULL
) && (*chat
->alias
!= '\0'))
2414 prpl
= purple_find_prpl(purple_account_get_protocol_id(chat
->account
));
2415 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(prpl
);
2417 if (prpl_info
->chat_info
) {
2418 struct proto_chat_entry
*pce
;
2419 GList
*parts
= prpl_info
->chat_info(purple_account_get_connection(chat
->account
));
2421 ret
= g_hash_table_lookup(chat
->components
, pce
->identifier
);
2422 g_list_foreach(parts
, (GFunc
)g_free
, NULL
);
2429 PurpleBuddy
*purple_find_buddy(PurpleAccount
*account
, const char *name
)
2432 struct _purple_hbuddy hb
;
2433 PurpleBlistNode
*group
;
2435 g_return_val_if_fail(purplebuddylist
!= NULL
, NULL
);
2436 g_return_val_if_fail(account
!= NULL
, NULL
);
2437 g_return_val_if_fail((name
!= NULL
) && (*name
!= '\0'), NULL
);
2439 hb
.account
= account
;
2440 hb
.name
= (gchar
*)purple_normalize(account
, name
);
2442 for (group
= purplebuddylist
->root
; group
; group
= group
->next
) {
2447 if ((buddy
= g_hash_table_lookup(purplebuddylist
->buddies
, &hb
))) {
2455 PurpleBuddy
*purple_find_buddy_in_group(PurpleAccount
*account
, const char *name
,
2458 struct _purple_hbuddy hb
;
2460 g_return_val_if_fail(purplebuddylist
!= NULL
, NULL
);
2461 g_return_val_if_fail(account
!= NULL
, NULL
);
2462 g_return_val_if_fail((name
!= NULL
) && (*name
!= '\0'), NULL
);
2464 hb
.name
= (gchar
*)purple_normalize(account
, name
);
2465 hb
.account
= account
;
2466 hb
.group
= (PurpleBlistNode
*)group
;
2468 return g_hash_table_lookup(purplebuddylist
->buddies
, &hb
);
2471 static void find_acct_buddies(gpointer key
, gpointer value
, gpointer data
)
2473 PurpleBuddy
*buddy
= value
;
2474 GSList
**list
= data
;
2476 *list
= g_slist_prepend(*list
, buddy
);
2479 GSList
*purple_find_buddies(PurpleAccount
*account
, const char *name
)
2482 PurpleBlistNode
*node
;
2485 g_return_val_if_fail(purplebuddylist
!= NULL
, NULL
);
2486 g_return_val_if_fail(account
!= NULL
, NULL
);
2488 if ((name
!= NULL
) && (*name
!= '\0')) {
2489 struct _purple_hbuddy hb
;
2491 hb
.name
= (gchar
*)purple_normalize(account
, name
);
2492 hb
.account
= account
;
2494 for (node
= purplebuddylist
->root
; node
!= NULL
; node
= node
->next
) {
2499 if ((buddy
= g_hash_table_lookup(purplebuddylist
->buddies
, &hb
)) != NULL
)
2500 ret
= g_slist_prepend(ret
, buddy
);
2503 GSList
*list
= NULL
;
2504 GHashTable
*buddies
= g_hash_table_lookup(buddies_cache
, account
);
2505 g_hash_table_foreach(buddies
, find_acct_buddies
, &list
);
2512 PurpleGroup
*purple_find_group(const char *name
)
2517 g_return_val_if_fail(purplebuddylist
!= NULL
, NULL
);
2518 g_return_val_if_fail((name
!= NULL
) && (*name
!= '\0'), NULL
);
2520 key
= g_utf8_collate_key(name
, -1);
2521 group
= g_hash_table_lookup(groups_cache
, key
);
2528 purple_blist_find_chat(PurpleAccount
*account
, const char *name
)
2533 PurplePluginProtocolInfo
*prpl_info
= NULL
;
2534 struct proto_chat_entry
*pce
;
2535 PurpleBlistNode
*node
, *group
;
2539 g_return_val_if_fail(purplebuddylist
!= NULL
, NULL
);
2540 g_return_val_if_fail((name
!= NULL
) && (*name
!= '\0'), NULL
);
2542 if (!purple_account_is_connected(account
))
2545 prpl
= purple_find_prpl(purple_account_get_protocol_id(account
));
2546 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(prpl
);
2548 if (prpl_info
->find_blist_chat
!= NULL
)
2549 return prpl_info
->find_blist_chat(account
, name
);
2551 normname
= g_strdup(purple_normalize(account
, name
));
2552 for (group
= purplebuddylist
->root
; group
!= NULL
; group
= group
->next
) {
2553 for (node
= group
->child
; node
!= NULL
; node
= node
->next
) {
2554 if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
2556 chat
= (PurpleChat
*)node
;
2558 if (account
!= chat
->account
)
2561 parts
= prpl_info
->chat_info(
2562 purple_account_get_connection(chat
->account
));
2565 chat_name
= g_hash_table_lookup(chat
->components
,
2567 g_list_foreach(parts
, (GFunc
)g_free
, NULL
);
2570 if (chat
->account
== account
&& chat_name
!= NULL
&&
2571 normname
!= NULL
&& !strcmp(purple_normalize(account
, chat_name
), normname
)) {
2584 purple_chat_get_group(PurpleChat
*chat
)
2586 g_return_val_if_fail(chat
!= NULL
, NULL
);
2588 return (PurpleGroup
*)(((PurpleBlistNode
*)chat
)->parent
);
2592 purple_chat_get_account(PurpleChat
*chat
)
2594 g_return_val_if_fail(chat
!= NULL
, NULL
);
2596 return chat
->account
;
2600 purple_chat_get_components(PurpleChat
*chat
)
2602 g_return_val_if_fail(chat
!= NULL
, NULL
);
2604 return chat
->components
;
2607 PurpleContact
*purple_buddy_get_contact(PurpleBuddy
*buddy
)
2609 g_return_val_if_fail(buddy
!= NULL
, NULL
);
2611 return PURPLE_CONTACT(PURPLE_BLIST_NODE(buddy
)->parent
);
2614 PurplePresence
*purple_buddy_get_presence(const PurpleBuddy
*buddy
)
2616 g_return_val_if_fail(buddy
!= NULL
, NULL
);
2617 return buddy
->presence
;
2620 PurpleMediaCaps
purple_buddy_get_media_caps(const PurpleBuddy
*buddy
)
2622 g_return_val_if_fail(buddy
!= NULL
, 0);
2623 return buddy
->media_caps
;
2626 void purple_buddy_set_media_caps(PurpleBuddy
*buddy
, PurpleMediaCaps media_caps
)
2628 g_return_if_fail(buddy
!= NULL
);
2629 buddy
->media_caps
= media_caps
;
2632 PurpleGroup
*purple_buddy_get_group(PurpleBuddy
*buddy
)
2634 g_return_val_if_fail(buddy
!= NULL
, NULL
);
2636 if (((PurpleBlistNode
*)buddy
)->parent
== NULL
)
2639 return (PurpleGroup
*)(((PurpleBlistNode
*)buddy
)->parent
->parent
);
2642 GSList
*purple_group_get_accounts(PurpleGroup
*group
)
2645 PurpleBlistNode
*gnode
, *cnode
, *bnode
;
2647 gnode
= (PurpleBlistNode
*)group
;
2649 for (cnode
= gnode
->child
; cnode
; cnode
= cnode
->next
) {
2650 if (PURPLE_BLIST_NODE_IS_CHAT(cnode
)) {
2651 if (!g_slist_find(l
, ((PurpleChat
*)cnode
)->account
))
2652 l
= g_slist_append(l
, ((PurpleChat
*)cnode
)->account
);
2653 } else if (PURPLE_BLIST_NODE_IS_CONTACT(cnode
)) {
2654 for (bnode
= cnode
->child
; bnode
; bnode
= bnode
->next
) {
2655 if (PURPLE_BLIST_NODE_IS_BUDDY(bnode
)) {
2656 if (!g_slist_find(l
, ((PurpleBuddy
*)bnode
)->account
))
2657 l
= g_slist_append(l
, ((PurpleBuddy
*)bnode
)->account
);
2666 void purple_blist_add_account(PurpleAccount
*account
)
2668 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
2669 PurpleBlistNode
*gnode
, *cnode
, *bnode
;
2671 g_return_if_fail(purplebuddylist
!= NULL
);
2673 if (!ops
|| !ops
->update
)
2676 for (gnode
= purplebuddylist
->root
; gnode
; gnode
= gnode
->next
) {
2677 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode
))
2679 for (cnode
= gnode
->child
; cnode
; cnode
= cnode
->next
) {
2680 if (PURPLE_BLIST_NODE_IS_CONTACT(cnode
)) {
2681 gboolean recompute
= FALSE
;
2682 for (bnode
= cnode
->child
; bnode
; bnode
= bnode
->next
) {
2683 if (PURPLE_BLIST_NODE_IS_BUDDY(bnode
) &&
2684 ((PurpleBuddy
*)bnode
)->account
== account
) {
2686 ((PurpleContact
*)cnode
)->currentsize
++;
2687 if (((PurpleContact
*)cnode
)->currentsize
== 1)
2688 ((PurpleGroup
*)gnode
)->currentsize
++;
2689 ops
->update(purplebuddylist
, bnode
);
2693 purple_blist_node_get_bool(cnode
, "show_offline")) {
2694 purple_contact_invalidate_priority_buddy((PurpleContact
*)cnode
);
2695 ops
->update(purplebuddylist
, cnode
);
2697 } else if (PURPLE_BLIST_NODE_IS_CHAT(cnode
) &&
2698 ((PurpleChat
*)cnode
)->account
== account
) {
2699 ((PurpleGroup
*)gnode
)->online
++;
2700 ((PurpleGroup
*)gnode
)->currentsize
++;
2701 ops
->update(purplebuddylist
, cnode
);
2704 ops
->update(purplebuddylist
, gnode
);
2708 void purple_blist_remove_account(PurpleAccount
*account
)
2710 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
2711 PurpleBlistNode
*gnode
, *cnode
, *bnode
;
2714 PurpleContact
*contact
;
2716 GList
*list
= NULL
, *iter
= NULL
;
2718 g_return_if_fail(purplebuddylist
!= NULL
);
2720 for (gnode
= purplebuddylist
->root
; gnode
; gnode
= gnode
->next
) {
2721 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode
))
2724 group
= (PurpleGroup
*)gnode
;
2726 for (cnode
= gnode
->child
; cnode
; cnode
= cnode
->next
) {
2727 if (PURPLE_BLIST_NODE_IS_CONTACT(cnode
)) {
2728 gboolean recompute
= FALSE
;
2729 contact
= (PurpleContact
*)cnode
;
2731 for (bnode
= cnode
->child
; bnode
; bnode
= bnode
->next
) {
2732 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode
))
2735 buddy
= (PurpleBuddy
*)bnode
;
2736 if (account
== buddy
->account
) {
2737 PurplePresence
*presence
;
2739 presence
= purple_buddy_get_presence(buddy
);
2741 if(purple_presence_is_online(presence
)) {
2743 if (contact
->online
== 0)
2746 purple_blist_node_set_int(&buddy
->node
,
2747 "last_seen", time(NULL
));
2750 contact
->currentsize
--;
2751 if (contact
->currentsize
== 0)
2752 group
->currentsize
--;
2754 if (!g_list_find(list
, presence
))
2755 list
= g_list_prepend(list
, presence
);
2757 if (contact
->priority
== buddy
)
2758 purple_contact_invalidate_priority_buddy(contact
);
2762 if (ops
&& ops
->remove
) {
2763 ops
->remove(purplebuddylist
, bnode
);
2768 purple_contact_invalidate_priority_buddy(contact
);
2769 if (ops
&& ops
->update
)
2770 ops
->update(purplebuddylist
, cnode
);
2772 } else if (PURPLE_BLIST_NODE_IS_CHAT(cnode
)) {
2773 chat
= (PurpleChat
*)cnode
;
2775 if(chat
->account
== account
) {
2776 group
->currentsize
--;
2779 if (ops
&& ops
->remove
)
2780 ops
->remove(purplebuddylist
, cnode
);
2786 for (iter
= list
; iter
; iter
= iter
->next
)
2788 purple_presence_set_status_active(iter
->data
, "offline", TRUE
);
2793 gboolean
purple_group_on_account(PurpleGroup
*g
, PurpleAccount
*account
)
2795 PurpleBlistNode
*cnode
;
2796 for (cnode
= ((PurpleBlistNode
*)g
)->child
; cnode
; cnode
= cnode
->next
) {
2797 if (PURPLE_BLIST_NODE_IS_CONTACT(cnode
)) {
2798 if(purple_contact_on_account((PurpleContact
*) cnode
, account
))
2800 } else if (PURPLE_BLIST_NODE_IS_CHAT(cnode
)) {
2801 PurpleChat
*chat
= (PurpleChat
*)cnode
;
2802 if ((!account
&& purple_account_is_connected(chat
->account
))
2803 || chat
->account
== account
)
2810 const char *purple_group_get_name(PurpleGroup
*group
)
2812 g_return_val_if_fail(group
!= NULL
, NULL
);
2818 purple_blist_request_add_buddy(PurpleAccount
*account
, const char *username
,
2819 const char *group
, const char *alias
)
2821 PurpleBlistUiOps
*ui_ops
;
2823 ui_ops
= purple_blist_get_ui_ops();
2825 if (ui_ops
!= NULL
&& ui_ops
->request_add_buddy
!= NULL
)
2826 ui_ops
->request_add_buddy(account
, username
, group
, alias
);
2830 purple_blist_request_add_chat(PurpleAccount
*account
, PurpleGroup
*group
,
2831 const char *alias
, const char *name
)
2833 PurpleBlistUiOps
*ui_ops
;
2835 ui_ops
= purple_blist_get_ui_ops();
2837 if (ui_ops
!= NULL
&& ui_ops
->request_add_chat
!= NULL
)
2838 ui_ops
->request_add_chat(account
, group
, alias
, name
);
2842 purple_blist_request_add_group(void)
2844 PurpleBlistUiOps
*ui_ops
;
2846 ui_ops
= purple_blist_get_ui_ops();
2848 if (ui_ops
!= NULL
&& ui_ops
->request_add_group
!= NULL
)
2849 ui_ops
->request_add_group();
2853 purple_blist_node_destroy(PurpleBlistNode
*node
)
2855 PurpleBlistUiOps
*ui_ops
;
2856 PurpleBlistNode
*child
, *next_child
;
2858 ui_ops
= purple_blist_get_ui_ops();
2859 child
= node
->child
;
2861 next_child
= child
->next
;
2862 purple_blist_node_destroy(child
);
2866 /* Allow the UI to free data */
2867 node
->parent
= NULL
;
2871 if (ui_ops
&& ui_ops
->remove
)
2872 ui_ops
->remove(purplebuddylist
, node
);
2874 if (PURPLE_BLIST_NODE_IS_BUDDY(node
))
2875 purple_buddy_destroy((PurpleBuddy
*)node
);
2876 else if (PURPLE_BLIST_NODE_IS_CHAT(node
))
2877 purple_chat_destroy((PurpleChat
*)node
);
2878 else if (PURPLE_BLIST_NODE_IS_CONTACT(node
))
2879 purple_contact_destroy((PurpleContact
*)node
);
2880 else if (PURPLE_BLIST_NODE_IS_GROUP(node
))
2881 purple_group_destroy((PurpleGroup
*)node
);
2885 purple_blist_node_setting_free(gpointer data
)
2889 value
= (PurpleValue
*)data
;
2891 purple_value_destroy(value
);
2894 static void purple_blist_node_initialize_settings(PurpleBlistNode
*node
)
2899 node
->settings
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
,
2900 (GDestroyNotify
)purple_blist_node_setting_free
);
2903 void purple_blist_node_remove_setting(PurpleBlistNode
*node
, const char *key
)
2905 PurpleBlistUiOps
*ops
;
2906 g_return_if_fail(node
!= NULL
);
2907 g_return_if_fail(node
->settings
!= NULL
);
2908 g_return_if_fail(key
!= NULL
);
2910 g_hash_table_remove(node
->settings
, key
);
2912 ops
= purple_blist_get_ui_ops();
2913 if (ops
&& ops
->save_node
)
2914 ops
->save_node(node
);
2918 purple_blist_node_set_flags(PurpleBlistNode
*node
, PurpleBlistNodeFlags flags
)
2920 g_return_if_fail(node
!= NULL
);
2922 node
->flags
= flags
;
2925 PurpleBlistNodeFlags
2926 purple_blist_node_get_flags(PurpleBlistNode
*node
)
2928 g_return_val_if_fail(node
!= NULL
, 0);
2934 purple_blist_node_get_type(PurpleBlistNode
*node
)
2936 g_return_val_if_fail(node
!= NULL
, PURPLE_BLIST_OTHER_NODE
);
2941 purple_blist_node_set_bool(PurpleBlistNode
* node
, const char *key
, gboolean data
)
2944 PurpleBlistUiOps
*ops
;
2946 g_return_if_fail(node
!= NULL
);
2947 g_return_if_fail(node
->settings
!= NULL
);
2948 g_return_if_fail(key
!= NULL
);
2950 value
= purple_value_new(PURPLE_TYPE_BOOLEAN
);
2951 purple_value_set_boolean(value
, data
);
2953 g_hash_table_replace(node
->settings
, g_strdup(key
), value
);
2955 ops
= purple_blist_get_ui_ops();
2956 if (ops
&& ops
->save_node
)
2957 ops
->save_node(node
);
2961 purple_blist_node_get_bool(PurpleBlistNode
* node
, const char *key
)
2965 g_return_val_if_fail(node
!= NULL
, FALSE
);
2966 g_return_val_if_fail(node
->settings
!= NULL
, FALSE
);
2967 g_return_val_if_fail(key
!= NULL
, FALSE
);
2969 value
= g_hash_table_lookup(node
->settings
, key
);
2974 g_return_val_if_fail(purple_value_get_type(value
) == PURPLE_TYPE_BOOLEAN
, FALSE
);
2976 return purple_value_get_boolean(value
);
2980 purple_blist_node_set_int(PurpleBlistNode
* node
, const char *key
, int data
)
2983 PurpleBlistUiOps
*ops
;
2985 g_return_if_fail(node
!= NULL
);
2986 g_return_if_fail(node
->settings
!= NULL
);
2987 g_return_if_fail(key
!= NULL
);
2989 value
= purple_value_new(PURPLE_TYPE_INT
);
2990 purple_value_set_int(value
, data
);
2992 g_hash_table_replace(node
->settings
, g_strdup(key
), value
);
2994 ops
= purple_blist_get_ui_ops();
2995 if (ops
&& ops
->save_node
)
2996 ops
->save_node(node
);
3000 purple_blist_node_get_int(PurpleBlistNode
* node
, const char *key
)
3004 g_return_val_if_fail(node
!= NULL
, 0);
3005 g_return_val_if_fail(node
->settings
!= NULL
, 0);
3006 g_return_val_if_fail(key
!= NULL
, 0);
3008 value
= g_hash_table_lookup(node
->settings
, key
);
3013 g_return_val_if_fail(purple_value_get_type(value
) == PURPLE_TYPE_INT
, 0);
3015 return purple_value_get_int(value
);
3019 purple_blist_node_set_string(PurpleBlistNode
* node
, const char *key
, const char *data
)
3022 PurpleBlistUiOps
*ops
;
3024 g_return_if_fail(node
!= NULL
);
3025 g_return_if_fail(node
->settings
!= NULL
);
3026 g_return_if_fail(key
!= NULL
);
3028 value
= purple_value_new(PURPLE_TYPE_STRING
);
3029 purple_value_set_string(value
, data
);
3031 g_hash_table_replace(node
->settings
, g_strdup(key
), value
);
3033 ops
= purple_blist_get_ui_ops();
3034 if (ops
&& ops
->save_node
)
3035 ops
->save_node(node
);
3039 purple_blist_node_get_string(PurpleBlistNode
* node
, const char *key
)
3043 g_return_val_if_fail(node
!= NULL
, NULL
);
3044 g_return_val_if_fail(node
->settings
!= NULL
, NULL
);
3045 g_return_val_if_fail(key
!= NULL
, NULL
);
3047 value
= g_hash_table_lookup(node
->settings
, key
);
3052 g_return_val_if_fail(purple_value_get_type(value
) == PURPLE_TYPE_STRING
, NULL
);
3054 return purple_value_get_string(value
);
3058 purple_blist_node_get_extended_menu(PurpleBlistNode
*n
)
3062 g_return_val_if_fail(n
!= NULL
, NULL
);
3064 purple_signal_emit(purple_blist_get_handle(),
3065 "blist-node-extended-menu",
3070 int purple_blist_get_group_size(PurpleGroup
*group
, gboolean offline
)
3075 return offline
? group
->totalsize
: group
->currentsize
;
3078 int purple_blist_get_group_online_count(PurpleGroup
*group
)
3083 return group
->online
;
3087 purple_blist_set_ui_ops(PurpleBlistUiOps
*ops
)
3089 gboolean overrode
= FALSE
;
3095 if (!ops
->save_node
) {
3096 ops
->save_node
= purple_blist_save_node
;
3099 if (!ops
->remove_node
) {
3100 ops
->remove_node
= purple_blist_save_node
;
3103 if (!ops
->save_account
) {
3104 ops
->save_account
= purple_blist_save_account
;
3108 if (overrode
&& (ops
->save_node
!= purple_blist_save_node
||
3109 ops
->remove_node
!= purple_blist_save_node
||
3110 ops
->save_account
!= purple_blist_save_account
)) {
3111 purple_debug_warning("blist", "Only some of the blist saving UI ops "
3112 "were overridden. This probably is not what you want!\n");
3117 purple_blist_get_ui_ops(void)
3119 return blist_ui_ops
;
3124 purple_blist_get_handle(void)
3132 purple_blist_init(void)
3134 void *handle
= purple_blist_get_handle();
3136 purple_signal_register(handle
, "buddy-status-changed",
3137 purple_marshal_VOID__POINTER_POINTER_POINTER
, NULL
,
3139 purple_value_new(PURPLE_TYPE_SUBTYPE
,
3140 PURPLE_SUBTYPE_BLIST_BUDDY
),
3141 purple_value_new(PURPLE_TYPE_SUBTYPE
,
3142 PURPLE_SUBTYPE_STATUS
),
3143 purple_value_new(PURPLE_TYPE_SUBTYPE
,
3144 PURPLE_SUBTYPE_STATUS
));
3145 purple_signal_register(handle
, "buddy-privacy-changed",
3146 purple_marshal_VOID__POINTER
, NULL
,
3148 purple_value_new(PURPLE_TYPE_SUBTYPE
,
3149 PURPLE_SUBTYPE_BLIST_BUDDY
));
3151 purple_signal_register(handle
, "buddy-idle-changed",
3152 purple_marshal_VOID__POINTER_INT_INT
, NULL
,
3154 purple_value_new(PURPLE_TYPE_SUBTYPE
,
3155 PURPLE_SUBTYPE_BLIST_BUDDY
),
3156 purple_value_new(PURPLE_TYPE_INT
),
3157 purple_value_new(PURPLE_TYPE_INT
));
3160 purple_signal_register(handle
, "buddy-signed-on",
3161 purple_marshal_VOID__POINTER
, NULL
, 1,
3162 purple_value_new(PURPLE_TYPE_SUBTYPE
,
3163 PURPLE_SUBTYPE_BLIST_BUDDY
));
3165 purple_signal_register(handle
, "buddy-signed-off",
3166 purple_marshal_VOID__POINTER
, NULL
, 1,
3167 purple_value_new(PURPLE_TYPE_SUBTYPE
,
3168 PURPLE_SUBTYPE_BLIST_BUDDY
));
3170 purple_signal_register(handle
, "buddy-got-login-time",
3171 purple_marshal_VOID__POINTER
, NULL
, 1,
3172 purple_value_new(PURPLE_TYPE_SUBTYPE
,
3173 PURPLE_SUBTYPE_BLIST_BUDDY
));
3175 purple_signal_register(handle
, "blist-node-added",
3176 purple_marshal_VOID__POINTER
, NULL
, 1,
3177 purple_value_new(PURPLE_TYPE_SUBTYPE
,
3178 PURPLE_SUBTYPE_BLIST_NODE
));
3180 purple_signal_register(handle
, "blist-node-removed",
3181 purple_marshal_VOID__POINTER
, NULL
, 1,
3182 purple_value_new(PURPLE_TYPE_SUBTYPE
,
3183 PURPLE_SUBTYPE_BLIST_NODE
));
3185 purple_signal_register(handle
, "buddy-added",
3186 purple_marshal_VOID__POINTER
, NULL
, 1,
3187 purple_value_new(PURPLE_TYPE_SUBTYPE
,
3188 PURPLE_SUBTYPE_BLIST_BUDDY
));
3190 purple_signal_register(handle
, "buddy-removed",
3191 purple_marshal_VOID__POINTER
, NULL
, 1,
3192 purple_value_new(PURPLE_TYPE_SUBTYPE
,
3193 PURPLE_SUBTYPE_BLIST_BUDDY
));
3195 purple_signal_register(handle
, "buddy-icon-changed",
3196 purple_marshal_VOID__POINTER
, NULL
, 1,
3197 purple_value_new(PURPLE_TYPE_SUBTYPE
,
3198 PURPLE_SUBTYPE_BLIST_BUDDY
));
3200 purple_signal_register(handle
, "update-idle", purple_marshal_VOID
, NULL
, 0);
3202 purple_signal_register(handle
, "blist-node-extended-menu",
3203 purple_marshal_VOID__POINTER_POINTER
, NULL
, 2,
3204 purple_value_new(PURPLE_TYPE_SUBTYPE
,
3205 PURPLE_SUBTYPE_BLIST_NODE
),
3206 purple_value_new(PURPLE_TYPE_BOXED
, "GList **"));
3208 purple_signal_register(handle
, "blist-node-aliased",
3209 purple_marshal_VOID__POINTER_POINTER
, NULL
, 2,
3210 purple_value_new(PURPLE_TYPE_SUBTYPE
,
3211 PURPLE_SUBTYPE_BLIST_NODE
),
3212 purple_value_new(PURPLE_TYPE_STRING
));
3214 purple_signal_register(handle
, "buddy-caps-changed",
3215 purple_marshal_VOID__POINTER_INT_INT
, NULL
,
3216 3, purple_value_new(PURPLE_TYPE_SUBTYPE
,
3217 PURPLE_SUBTYPE_BLIST_BUDDY
),
3218 purple_value_new(PURPLE_TYPE_INT
),
3219 purple_value_new(PURPLE_TYPE_INT
));
3221 purple_signal_connect(purple_accounts_get_handle(), "account-created",
3223 PURPLE_CALLBACK(purple_blist_buddies_cache_add_account
),
3226 purple_signal_connect(purple_accounts_get_handle(), "account-destroying",
3228 PURPLE_CALLBACK(purple_blist_buddies_cache_remove_account
),
3233 purple_blist_uninit(void)
3235 PurpleBlistNode
*node
, *next_node
;
3237 /* This happens if we quit before purple_set_blist is called. */
3238 if (purplebuddylist
== NULL
)
3241 if (save_timer
!= 0) {
3242 purple_timeout_remove(save_timer
);
3244 purple_blist_sync();
3247 purple_blist_destroy();
3249 node
= purple_blist_get_root();
3251 next_node
= node
->next
;
3252 purple_blist_node_destroy(node
);
3255 purplebuddylist
->root
= NULL
;
3257 g_hash_table_destroy(purplebuddylist
->buddies
);
3258 g_hash_table_destroy(buddies_cache
);
3259 g_hash_table_destroy(groups_cache
);
3261 buddies_cache
= NULL
;
3262 groups_cache
= NULL
;
3264 PURPLE_DBUS_UNREGISTER_POINTER(purplebuddylist
);
3265 g_free(purplebuddylist
);
3266 purplebuddylist
= NULL
;
3268 purple_signals_disconnect_by_handle(purple_blist_get_handle());
3269 purple_signals_unregister_by_instance(purple_blist_get_handle());