Mercurial > pidgin.yaz
annotate libgaim/blist.c @ 14752:4124030c3f3a
[gaim-migrate @ 17509]
Fix the situation (I think it will only happen while shutting down) where GaimBuddy->proto_data could be accessed after it is freed.
Specifically, it happened when gtkblist was requesting the emblems from the msn prpl.
Big thanks to henningn for noticing the funkiness in valgrind.
I discovered that there is no function to retrieve all the buddies for an account (shock, horror) - so I modified gaim_find_buddies() to return all the account buddies if "name" is NULL.
committer: Tailor Script <tailor@pidgin.im>
author | Daniel Atallah <daniel.atallah@gmail.com> |
---|---|
date | Wed, 18 Oct 2006 03:32:14 +0000 |
parents | 11fd4148f9da |
children | 56dbadfa6543 |
rev | line source |
---|---|
14192 | 1 /* |
2 * gaim | |
3 * | |
4 * Gaim 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 | |
6 * source distribution. | |
7 * | |
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. | |
12 * | |
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. | |
17 * | |
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 * | |
22 */ | |
23 #include "internal.h" | |
24 #include "blist.h" | |
25 #include "conversation.h" | |
26 #include "dbus-maybe.h" | |
27 #include "debug.h" | |
28 #include "notify.h" | |
29 #include "prefs.h" | |
30 #include "privacy.h" | |
31 #include "prpl.h" | |
32 #include "server.h" | |
33 #include "signals.h" | |
34 #include "util.h" | |
35 #include "value.h" | |
36 #include "xmlnode.h" | |
37 | |
38 #define PATHSIZE 1024 | |
39 | |
40 static GaimBlistUiOps *blist_ui_ops = NULL; | |
41 | |
42 static GaimBuddyList *gaimbuddylist = NULL; | |
43 static guint save_timer = 0; | |
44 static gboolean blist_loaded = FALSE; | |
45 | |
46 | |
47 /********************************************************************* | |
48 * Private utility functions * | |
49 *********************************************************************/ | |
50 | |
51 static GaimBlistNode *gaim_blist_get_last_sibling(GaimBlistNode *node) | |
52 { | |
53 GaimBlistNode *n = node; | |
54 if (!n) | |
55 return NULL; | |
56 while (n->next) | |
57 n = n->next; | |
58 return n; | |
59 } | |
60 | |
61 static GaimBlistNode *gaim_blist_get_last_child(GaimBlistNode *node) | |
62 { | |
63 if (!node) | |
64 return NULL; | |
65 return gaim_blist_get_last_sibling(node->child); | |
66 } | |
67 | |
14752
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
68 struct _list_account_buddies { |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
69 GSList *list; |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
70 GaimAccount *account; |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
71 }; |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
72 |
14192 | 73 struct _gaim_hbuddy { |
74 char *name; | |
75 GaimAccount *account; | |
76 GaimBlistNode *group; | |
77 }; | |
78 | |
79 static guint _gaim_blist_hbuddy_hash(struct _gaim_hbuddy *hb) | |
80 { | |
81 return g_str_hash(hb->name); | |
82 } | |
83 | |
84 static guint _gaim_blist_hbuddy_equal(struct _gaim_hbuddy *hb1, struct _gaim_hbuddy *hb2) | |
85 { | |
86 return ((!strcmp(hb1->name, hb2->name)) && hb1->account == hb2->account && hb1->group == hb2->group); | |
87 } | |
88 | |
89 static void _gaim_blist_hbuddy_free_key(struct _gaim_hbuddy *hb) | |
90 { | |
91 g_free(hb->name); | |
92 g_free(hb); | |
93 } | |
94 | |
95 | |
96 /********************************************************************* | |
97 * Writing to disk * | |
98 *********************************************************************/ | |
99 | |
100 static void | |
101 value_to_xmlnode(gpointer key, gpointer hvalue, gpointer user_data) | |
102 { | |
103 const char *name; | |
104 GaimValue *value; | |
105 xmlnode *node, *child; | |
106 char buf[20]; | |
107 | |
108 name = (const char *)key; | |
109 value = (GaimValue *)hvalue; | |
110 node = (xmlnode *)user_data; | |
111 | |
112 g_return_if_fail(value != NULL); | |
113 | |
114 child = xmlnode_new_child(node, "setting"); | |
115 xmlnode_set_attrib(child, "name", name); | |
116 | |
117 if (gaim_value_get_type(value) == GAIM_TYPE_INT) { | |
118 xmlnode_set_attrib(child, "type", "int"); | |
119 snprintf(buf, sizeof(buf), "%d", gaim_value_get_int(value)); | |
120 xmlnode_insert_data(child, buf, -1); | |
121 } | |
122 else if (gaim_value_get_type(value) == GAIM_TYPE_STRING) { | |
123 xmlnode_set_attrib(child, "type", "string"); | |
124 xmlnode_insert_data(child, gaim_value_get_string(value), -1); | |
125 } | |
126 else if (gaim_value_get_type(value) == GAIM_TYPE_BOOLEAN) { | |
127 xmlnode_set_attrib(child, "type", "bool"); | |
128 snprintf(buf, sizeof(buf), "%d", gaim_value_get_boolean(value)); | |
129 xmlnode_insert_data(child, buf, -1); | |
130 } | |
131 } | |
132 | |
133 static void | |
134 chat_component_to_xmlnode(gpointer key, gpointer value, gpointer user_data) | |
135 { | |
136 const char *name; | |
137 const char *data; | |
138 xmlnode *node, *child; | |
139 | |
140 name = (const char *)key; | |
141 data = (const char *)value; | |
142 node = (xmlnode *)user_data; | |
143 | |
144 g_return_if_fail(data != NULL); | |
145 | |
146 child = xmlnode_new_child(node, "component"); | |
147 xmlnode_set_attrib(child, "name", name); | |
148 xmlnode_insert_data(child, data, -1); | |
149 } | |
150 | |
151 static xmlnode * | |
152 buddy_to_xmlnode(GaimBlistNode *bnode) | |
153 { | |
154 xmlnode *node, *child; | |
155 GaimBuddy *buddy; | |
156 | |
157 buddy = (GaimBuddy *)bnode; | |
158 | |
159 node = xmlnode_new("buddy"); | |
160 xmlnode_set_attrib(node, "account", gaim_account_get_username(buddy->account)); | |
161 xmlnode_set_attrib(node, "proto", gaim_account_get_protocol_id(buddy->account)); | |
162 | |
163 child = xmlnode_new_child(node, "name"); | |
164 xmlnode_insert_data(child, buddy->name, -1); | |
165 | |
166 if (buddy->alias != NULL) | |
167 { | |
168 child = xmlnode_new_child(node, "alias"); | |
169 xmlnode_insert_data(child, buddy->alias, -1); | |
170 } | |
171 | |
172 /* Write buddy settings */ | |
173 g_hash_table_foreach(buddy->node.settings, value_to_xmlnode, node); | |
174 | |
175 return node; | |
176 } | |
177 | |
178 static xmlnode * | |
179 contact_to_xmlnode(GaimBlistNode *cnode) | |
180 { | |
181 xmlnode *node, *child; | |
182 GaimContact *contact; | |
183 GaimBlistNode *bnode; | |
184 | |
185 contact = (GaimContact *)cnode; | |
186 | |
187 node = xmlnode_new("contact"); | |
188 | |
189 if (contact->alias != NULL) | |
190 { | |
191 xmlnode_set_attrib(node, "alias", contact->alias); | |
192 } | |
193 | |
194 /* Write buddies */ | |
195 for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) | |
196 { | |
197 if (!GAIM_BLIST_NODE_SHOULD_SAVE(bnode)) | |
198 continue; | |
199 if (GAIM_BLIST_NODE_IS_BUDDY(bnode)) | |
200 { | |
201 child = buddy_to_xmlnode(bnode); | |
202 xmlnode_insert_child(node, child); | |
203 } | |
204 } | |
205 | |
206 /* Write contact settings */ | |
207 g_hash_table_foreach(cnode->settings, value_to_xmlnode, node); | |
208 | |
209 return node; | |
210 } | |
211 | |
212 static xmlnode * | |
213 chat_to_xmlnode(GaimBlistNode *cnode) | |
214 { | |
215 xmlnode *node, *child; | |
216 GaimChat *chat; | |
217 | |
218 chat = (GaimChat *)cnode; | |
219 | |
220 node = xmlnode_new("chat"); | |
221 xmlnode_set_attrib(node, "proto", gaim_account_get_protocol_id(chat->account)); | |
222 xmlnode_set_attrib(node, "account", gaim_account_get_username(chat->account)); | |
223 | |
224 if (chat->alias != NULL) | |
225 { | |
226 child = xmlnode_new_child(node, "alias"); | |
227 xmlnode_insert_data(child, chat->alias, -1); | |
228 } | |
229 | |
230 /* Write chat components */ | |
231 g_hash_table_foreach(chat->components, chat_component_to_xmlnode, node); | |
232 | |
233 /* Write chat settings */ | |
234 g_hash_table_foreach(chat->node.settings, value_to_xmlnode, node); | |
235 | |
236 return node; | |
237 } | |
238 | |
239 static xmlnode * | |
240 group_to_xmlnode(GaimBlistNode *gnode) | |
241 { | |
242 xmlnode *node, *child; | |
243 GaimGroup *group; | |
244 GaimBlistNode *cnode; | |
245 | |
246 group = (GaimGroup *)gnode; | |
247 | |
248 node = xmlnode_new("group"); | |
249 xmlnode_set_attrib(node, "name", group->name); | |
250 | |
251 /* Write settings */ | |
252 g_hash_table_foreach(group->node.settings, value_to_xmlnode, node); | |
253 | |
254 /* Write contacts and chats */ | |
255 for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) | |
256 { | |
257 if (!GAIM_BLIST_NODE_SHOULD_SAVE(cnode)) | |
258 continue; | |
259 if (GAIM_BLIST_NODE_IS_CONTACT(cnode)) | |
260 { | |
261 child = contact_to_xmlnode(cnode); | |
262 xmlnode_insert_child(node, child); | |
263 } | |
264 else if (GAIM_BLIST_NODE_IS_CHAT(cnode)) | |
265 { | |
266 child = chat_to_xmlnode(cnode); | |
267 xmlnode_insert_child(node, child); | |
268 } | |
269 } | |
270 | |
271 return node; | |
272 } | |
273 | |
274 static xmlnode * | |
275 accountprivacy_to_xmlnode(GaimAccount *account) | |
276 { | |
277 xmlnode *node, *child; | |
278 GSList *cur; | |
279 char buf[10]; | |
280 | |
281 node = xmlnode_new("account"); | |
282 xmlnode_set_attrib(node, "proto", gaim_account_get_protocol_id(account)); | |
283 xmlnode_set_attrib(node, "name", gaim_account_get_username(account)); | |
284 snprintf(buf, sizeof(buf), "%d", account->perm_deny); | |
285 xmlnode_set_attrib(node, "mode", buf); | |
286 | |
287 for (cur = account->permit; cur; cur = cur->next) | |
288 { | |
289 child = xmlnode_new_child(node, "permit"); | |
290 xmlnode_insert_data(child, cur->data, -1); | |
291 } | |
292 | |
293 for (cur = account->deny; cur; cur = cur->next) | |
294 { | |
295 child = xmlnode_new_child(node, "block"); | |
296 xmlnode_insert_data(child, cur->data, -1); | |
297 } | |
298 | |
299 return node; | |
300 } | |
301 | |
302 static xmlnode * | |
303 blist_to_xmlnode() | |
304 { | |
305 xmlnode *node, *child, *grandchild; | |
306 GaimBlistNode *gnode; | |
307 GList *cur; | |
308 | |
309 node = xmlnode_new("gaim"); | |
310 xmlnode_set_attrib(node, "version", "1.0"); | |
311 | |
312 /* Write groups */ | |
313 child = xmlnode_new_child(node, "blist"); | |
314 for (gnode = gaimbuddylist->root; gnode != NULL; gnode = gnode->next) | |
315 { | |
316 if (!GAIM_BLIST_NODE_SHOULD_SAVE(gnode)) | |
317 continue; | |
318 if (GAIM_BLIST_NODE_IS_GROUP(gnode)) | |
319 { | |
320 grandchild = group_to_xmlnode(gnode); | |
321 xmlnode_insert_child(child, grandchild); | |
322 } | |
323 } | |
324 | |
325 /* Write privacy settings */ | |
326 child = xmlnode_new_child(node, "privacy"); | |
327 for (cur = gaim_accounts_get_all(); cur != NULL; cur = cur->next) | |
328 { | |
329 grandchild = accountprivacy_to_xmlnode(cur->data); | |
330 xmlnode_insert_child(child, grandchild); | |
331 } | |
332 | |
333 return node; | |
334 } | |
335 | |
336 static void | |
337 gaim_blist_sync() | |
338 { | |
339 xmlnode *node; | |
340 char *data; | |
341 | |
342 if (!blist_loaded) | |
343 { | |
344 gaim_debug_error("blist", "Attempted to save buddy list before it " | |
345 "was read!\n"); | |
346 return; | |
347 } | |
348 | |
349 node = blist_to_xmlnode(); | |
350 data = xmlnode_to_formatted_str(node, NULL); | |
351 gaim_util_write_data_to_file("blist.xml", data, -1); | |
352 g_free(data); | |
353 xmlnode_free(node); | |
354 } | |
355 | |
356 static gboolean | |
357 save_cb(gpointer data) | |
358 { | |
359 gaim_blist_sync(); | |
360 save_timer = 0; | |
361 return FALSE; | |
362 } | |
363 | |
364 void | |
365 gaim_blist_schedule_save() | |
366 { | |
367 if (save_timer == 0) | |
368 save_timer = gaim_timeout_add(5000, save_cb, NULL); | |
369 } | |
370 | |
371 | |
372 /********************************************************************* | |
373 * Reading from disk * | |
374 *********************************************************************/ | |
375 | |
376 static void | |
377 parse_setting(GaimBlistNode *node, xmlnode *setting) | |
378 { | |
379 const char *name = xmlnode_get_attrib(setting, "name"); | |
380 const char *type = xmlnode_get_attrib(setting, "type"); | |
381 char *value = xmlnode_get_data(setting); | |
382 | |
383 if (!value) | |
384 return; | |
385 | |
386 if (!type || !strcmp(type, "string")) | |
387 gaim_blist_node_set_string(node, name, value); | |
388 else if (!strcmp(type, "bool")) | |
389 gaim_blist_node_set_bool(node, name, atoi(value)); | |
390 else if (!strcmp(type, "int")) | |
391 gaim_blist_node_set_int(node, name, atoi(value)); | |
392 | |
393 g_free(value); | |
394 } | |
395 | |
396 static void | |
397 parse_buddy(GaimGroup *group, GaimContact *contact, xmlnode *bnode) | |
398 { | |
399 GaimAccount *account; | |
400 GaimBuddy *buddy; | |
401 char *name = NULL, *alias = NULL; | |
402 const char *acct_name, *proto, *protocol; | |
403 xmlnode *x; | |
404 | |
405 acct_name = xmlnode_get_attrib(bnode, "account"); | |
406 protocol = xmlnode_get_attrib(bnode, "protocol"); | |
407 proto = xmlnode_get_attrib(bnode, "proto"); | |
408 | |
409 if (!acct_name || (!proto && !protocol)) | |
410 return; | |
411 | |
412 account = gaim_accounts_find(acct_name, proto ? proto : protocol); | |
413 | |
414 if (!account) | |
415 return; | |
416 | |
417 if ((x = xmlnode_get_child(bnode, "name"))) | |
418 name = xmlnode_get_data(x); | |
419 | |
420 if (!name) | |
421 return; | |
422 | |
423 if ((x = xmlnode_get_child(bnode, "alias"))) | |
424 alias = xmlnode_get_data(x); | |
425 | |
426 buddy = gaim_buddy_new(account, name, alias); | |
427 gaim_blist_add_buddy(buddy, contact, group, | |
428 gaim_blist_get_last_child((GaimBlistNode*)contact)); | |
429 | |
430 for (x = xmlnode_get_child(bnode, "setting"); x; x = xmlnode_get_next_twin(x)) { | |
431 parse_setting((GaimBlistNode*)buddy, x); | |
432 } | |
433 | |
434 g_free(name); | |
435 g_free(alias); | |
436 } | |
437 | |
438 static void | |
439 parse_contact(GaimGroup *group, xmlnode *cnode) | |
440 { | |
441 GaimContact *contact = gaim_contact_new(); | |
442 xmlnode *x; | |
443 const char *alias; | |
444 | |
445 gaim_blist_add_contact(contact, group, | |
446 gaim_blist_get_last_child((GaimBlistNode*)group)); | |
447 | |
448 if ((alias = xmlnode_get_attrib(cnode, "alias"))) { | |
449 gaim_contact_set_alias(contact, alias); | |
450 } | |
451 | |
452 for (x = cnode->child; x; x = x->next) { | |
453 if (x->type != XMLNODE_TYPE_TAG) | |
454 continue; | |
455 if (!strcmp(x->name, "buddy")) | |
456 parse_buddy(group, contact, x); | |
457 else if (!strcmp(x->name, "setting")) | |
458 parse_setting((GaimBlistNode*)contact, x); | |
459 } | |
460 | |
461 /* if the contact is empty, don't keep it around. it causes problems */ | |
462 if (!((GaimBlistNode*)contact)->child) | |
463 gaim_blist_remove_contact(contact); | |
464 } | |
465 | |
466 static void | |
467 parse_chat(GaimGroup *group, xmlnode *cnode) | |
468 { | |
469 GaimChat *chat; | |
470 GaimAccount *account; | |
471 const char *acct_name, *proto, *protocol; | |
472 xmlnode *x; | |
473 char *alias = NULL; | |
474 GHashTable *components; | |
475 | |
476 acct_name = xmlnode_get_attrib(cnode, "account"); | |
477 protocol = xmlnode_get_attrib(cnode, "protocol"); | |
478 proto = xmlnode_get_attrib(cnode, "proto"); | |
479 | |
480 if (!acct_name || (!proto && !protocol)) | |
481 return; | |
482 | |
483 account = gaim_accounts_find(acct_name, proto ? proto : protocol); | |
484 | |
485 if (!account) | |
486 return; | |
487 | |
488 if ((x = xmlnode_get_child(cnode, "alias"))) | |
489 alias = xmlnode_get_data(x); | |
490 | |
491 components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); | |
492 | |
493 for (x = xmlnode_get_child(cnode, "component"); x; x = xmlnode_get_next_twin(x)) { | |
494 const char *name; | |
495 char *value; | |
496 | |
497 name = xmlnode_get_attrib(x, "name"); | |
498 value = xmlnode_get_data(x); | |
499 g_hash_table_replace(components, g_strdup(name), value); | |
500 } | |
501 | |
502 chat = gaim_chat_new(account, alias, components); | |
503 gaim_blist_add_chat(chat, group, | |
504 gaim_blist_get_last_child((GaimBlistNode*)group)); | |
505 | |
506 for (x = xmlnode_get_child(cnode, "setting"); x; x = xmlnode_get_next_twin(x)) { | |
507 parse_setting((GaimBlistNode*)chat, x); | |
508 } | |
509 | |
510 g_free(alias); | |
511 } | |
512 | |
513 static void | |
514 parse_group(xmlnode *groupnode) | |
515 { | |
516 const char *name = xmlnode_get_attrib(groupnode, "name"); | |
517 GaimGroup *group; | |
518 xmlnode *cnode; | |
519 | |
520 if (!name) | |
521 name = _("Buddies"); | |
522 | |
523 group = gaim_group_new(name); | |
524 gaim_blist_add_group(group, | |
525 gaim_blist_get_last_sibling(gaimbuddylist->root)); | |
526 | |
527 for (cnode = groupnode->child; cnode; cnode = cnode->next) { | |
528 if (cnode->type != XMLNODE_TYPE_TAG) | |
529 continue; | |
530 if (!strcmp(cnode->name, "setting")) | |
531 parse_setting((GaimBlistNode*)group, cnode); | |
532 else if (!strcmp(cnode->name, "contact") || | |
533 !strcmp(cnode->name, "person")) | |
534 parse_contact(group, cnode); | |
535 else if (!strcmp(cnode->name, "chat")) | |
536 parse_chat(group, cnode); | |
537 } | |
538 } | |
539 | |
540 /* TODO: Make static and rename to load_blist */ | |
541 void | |
542 gaim_blist_load() | |
543 { | |
544 xmlnode *gaim, *blist, *privacy; | |
545 | |
546 blist_loaded = TRUE; | |
547 | |
548 gaim = gaim_util_read_xml_from_file("blist.xml", _("buddy list")); | |
549 | |
550 if (gaim == NULL) | |
551 return; | |
552 | |
553 blist = xmlnode_get_child(gaim, "blist"); | |
554 if (blist) { | |
555 xmlnode *groupnode; | |
556 for (groupnode = xmlnode_get_child(blist, "group"); groupnode != NULL; | |
557 groupnode = xmlnode_get_next_twin(groupnode)) { | |
558 parse_group(groupnode); | |
559 } | |
560 } | |
561 | |
562 privacy = xmlnode_get_child(gaim, "privacy"); | |
563 if (privacy) { | |
564 xmlnode *anode; | |
565 for (anode = privacy->child; anode; anode = anode->next) { | |
566 xmlnode *x; | |
567 GaimAccount *account; | |
568 int imode; | |
569 const char *acct_name, *proto, *mode, *protocol; | |
570 | |
571 acct_name = xmlnode_get_attrib(anode, "name"); | |
572 protocol = xmlnode_get_attrib(anode, "protocol"); | |
573 proto = xmlnode_get_attrib(anode, "proto"); | |
574 mode = xmlnode_get_attrib(anode, "mode"); | |
575 | |
576 if (!acct_name || (!proto && !protocol) || !mode) | |
577 continue; | |
578 | |
579 account = gaim_accounts_find(acct_name, proto ? proto : protocol); | |
580 | |
581 if (!account) | |
582 continue; | |
583 | |
584 imode = atoi(mode); | |
585 account->perm_deny = (imode != 0 ? imode : GAIM_PRIVACY_ALLOW_ALL); | |
586 | |
587 for (x = anode->child; x; x = x->next) { | |
588 char *name; | |
589 if (x->type != XMLNODE_TYPE_TAG) | |
590 continue; | |
591 | |
592 if (!strcmp(x->name, "permit")) { | |
593 name = xmlnode_get_data(x); | |
594 gaim_privacy_permit_add(account, name, TRUE); | |
595 g_free(name); | |
596 } else if (!strcmp(x->name, "block")) { | |
597 name = xmlnode_get_data(x); | |
598 gaim_privacy_deny_add(account, name, TRUE); | |
599 g_free(name); | |
600 } | |
601 } | |
602 } | |
603 } | |
604 | |
605 xmlnode_free(gaim); | |
606 } | |
607 | |
608 | |
609 /********************************************************************* | |
610 * Stuff * | |
611 *********************************************************************/ | |
612 | |
613 static void | |
614 gaim_contact_compute_priority_buddy(GaimContact *contact) | |
615 { | |
616 GaimBlistNode *bnode; | |
617 GaimBuddy *new_priority = NULL; | |
618 | |
619 g_return_if_fail(contact != NULL); | |
620 | |
621 contact->priority = NULL; | |
622 for (bnode = ((GaimBlistNode*)contact)->child; | |
623 bnode != NULL; | |
624 bnode = bnode->next) | |
625 { | |
626 GaimBuddy *buddy; | |
627 | |
628 if (!GAIM_BLIST_NODE_IS_BUDDY(bnode)) | |
629 continue; | |
630 | |
631 buddy = (GaimBuddy*)bnode; | |
632 | |
633 if (!gaim_account_is_connected(buddy->account)) | |
634 continue; | |
635 if (new_priority == NULL) | |
636 new_priority = buddy; | |
637 else | |
638 { | |
639 int cmp; | |
640 | |
641 cmp = gaim_presence_compare(gaim_buddy_get_presence(new_priority), | |
642 gaim_buddy_get_presence(buddy)); | |
643 | |
644 if (cmp > 0 || (cmp == 0 && | |
645 gaim_prefs_get_bool("/core/contact/last_match"))) | |
646 { | |
647 new_priority = buddy; | |
648 } | |
649 } | |
650 } | |
651 | |
652 contact->priority = new_priority; | |
653 contact->priority_valid = TRUE; | |
654 } | |
655 | |
656 | |
657 /***************************************************************************** | |
658 * Public API functions * | |
659 *****************************************************************************/ | |
660 | |
661 GaimBuddyList *gaim_blist_new() | |
662 { | |
663 GaimBlistUiOps *ui_ops; | |
664 GaimBuddyList *gbl = g_new0(GaimBuddyList, 1); | |
665 GAIM_DBUS_REGISTER_POINTER(gbl, GaimBuddyList); | |
666 | |
667 ui_ops = gaim_blist_get_ui_ops(); | |
668 | |
669 gbl->buddies = g_hash_table_new_full((GHashFunc)_gaim_blist_hbuddy_hash, | |
670 (GEqualFunc)_gaim_blist_hbuddy_equal, | |
671 (GDestroyNotify)_gaim_blist_hbuddy_free_key, NULL); | |
672 | |
673 if (ui_ops != NULL && ui_ops->new_list != NULL) | |
674 ui_ops->new_list(gbl); | |
675 | |
676 return gbl; | |
677 } | |
678 | |
679 void | |
680 gaim_set_blist(GaimBuddyList *list) | |
681 { | |
682 gaimbuddylist = list; | |
683 } | |
684 | |
685 GaimBuddyList * | |
686 gaim_get_blist() | |
687 { | |
688 return gaimbuddylist; | |
689 } | |
690 | |
691 GaimBlistNode * | |
692 gaim_blist_get_root() | |
693 { | |
694 return gaimbuddylist ? gaimbuddylist->root : NULL; | |
695 } | |
696 | |
697 void gaim_blist_show() | |
698 { | |
699 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
700 | |
701 if (ops && ops->show) | |
702 ops->show(gaimbuddylist); | |
703 } | |
704 | |
705 void gaim_blist_destroy() | |
706 { | |
707 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
708 | |
709 gaim_debug(GAIM_DEBUG_INFO, "blist", "Destroying\n"); | |
710 | |
711 if (ops && ops->destroy) | |
712 ops->destroy(gaimbuddylist); | |
713 } | |
714 | |
715 void gaim_blist_set_visible(gboolean show) | |
716 { | |
717 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
718 | |
719 if (ops && ops->set_visible) | |
720 ops->set_visible(gaimbuddylist, show); | |
721 } | |
722 | |
723 static GaimBlistNode *get_next_node(GaimBlistNode *node, gboolean godeep) | |
724 { | |
725 if (node == NULL) | |
726 return NULL; | |
727 | |
728 if (godeep && node->child) | |
729 return node->child; | |
730 | |
731 if (node->next) | |
732 return node->next; | |
733 | |
734 return get_next_node(node->parent, FALSE); | |
735 } | |
736 | |
737 GaimBlistNode *gaim_blist_node_next(GaimBlistNode *node, gboolean offline) | |
738 { | |
739 GaimBlistNode *ret = node; | |
740 | |
741 if (offline) | |
742 return get_next_node(ret, TRUE); | |
743 do | |
744 { | |
745 ret = get_next_node(ret, TRUE); | |
746 } while (ret && GAIM_BLIST_NODE_IS_BUDDY(ret) && | |
747 !gaim_account_is_connected(gaim_buddy_get_account((GaimBuddy *)ret))); | |
748 | |
749 return ret; | |
750 } | |
751 | |
752 void | |
753 gaim_blist_update_buddy_status(GaimBuddy *buddy, GaimStatus *old_status) | |
754 { | |
755 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
756 GaimPresence *presence; | |
757 GaimStatus *status; | |
758 | |
759 g_return_if_fail(buddy != NULL); | |
760 | |
761 presence = gaim_buddy_get_presence(buddy); | |
762 status = gaim_presence_get_active_status(presence); | |
763 | |
764 gaim_debug_info("blist", "Updating buddy status for %s (%s)\n", | |
765 buddy->name, gaim_account_get_protocol_name(buddy->account)); | |
766 | |
767 if (gaim_status_is_online(status) && | |
768 !gaim_status_is_online(old_status)) { | |
769 | |
770 gaim_signal_emit(gaim_blist_get_handle(), "buddy-signed-on", buddy); | |
771 | |
772 ((GaimContact*)((GaimBlistNode*)buddy)->parent)->online++; | |
773 if (((GaimContact*)((GaimBlistNode*)buddy)->parent)->online == 1) | |
774 ((GaimGroup *)((GaimBlistNode *)buddy)->parent->parent)->online++; | |
775 } else if (!gaim_status_is_online(status) && | |
776 gaim_status_is_online(old_status)) { | |
777 gaim_blist_node_set_int(&buddy->node, "last_seen", time(NULL)); | |
778 gaim_signal_emit(gaim_blist_get_handle(), "buddy-signed-off", buddy); | |
779 ((GaimContact*)((GaimBlistNode*)buddy)->parent)->online--; | |
780 if (((GaimContact*)((GaimBlistNode*)buddy)->parent)->online == 0) | |
781 ((GaimGroup *)((GaimBlistNode *)buddy)->parent->parent)->online--; | |
782 } else { | |
783 gaim_signal_emit(gaim_blist_get_handle(), | |
784 "buddy-status-changed", buddy, old_status, | |
785 status); | |
786 } | |
787 | |
788 /* | |
789 * This function used to only call the following two functions if one of | |
790 * the above signals had been triggered, but that's not good, because | |
791 * if someone's away message changes and they don't go from away to back | |
792 * to away then no signal is triggered. | |
793 * | |
794 * It's a safe assumption that SOMETHING called this function. PROBABLY | |
795 * because something, somewhere changed. Calling the stuff below | |
796 * certainly won't hurt anything. Unless you're on a K6-2 300. | |
797 */ | |
798 gaim_contact_invalidate_priority_buddy(gaim_buddy_get_contact(buddy)); | |
799 if (ops && ops->update) | |
800 ops->update(gaimbuddylist, (GaimBlistNode *)buddy); | |
801 } | |
802 | |
803 void gaim_blist_update_buddy_icon(GaimBuddy *buddy) | |
804 { | |
805 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
806 | |
807 g_return_if_fail(buddy != NULL); | |
808 | |
809 if (ops && ops->update) | |
810 ops->update(gaimbuddylist, (GaimBlistNode *)buddy); | |
811 } | |
812 | |
813 /* | |
814 * TODO: Maybe remove the call to this from server.c and call it | |
815 * from oscar.c and toc.c instead? | |
816 */ | |
817 void gaim_blist_rename_buddy(GaimBuddy *buddy, const char *name) | |
818 { | |
819 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
820 struct _gaim_hbuddy *hb; | |
821 | |
822 g_return_if_fail(buddy != NULL); | |
823 | |
824 hb = g_new(struct _gaim_hbuddy, 1); | |
825 hb->name = g_strdup(gaim_normalize(buddy->account, buddy->name)); | |
826 hb->account = buddy->account; | |
827 hb->group = ((GaimBlistNode *)buddy)->parent->parent; | |
828 g_hash_table_remove(gaimbuddylist->buddies, hb); | |
829 | |
830 g_free(hb->name); | |
831 hb->name = g_strdup(gaim_normalize(buddy->account, name)); | |
832 g_hash_table_replace(gaimbuddylist->buddies, hb, buddy); | |
833 | |
834 g_free(buddy->name); | |
835 buddy->name = g_strdup(name); | |
836 | |
837 gaim_blist_schedule_save(); | |
838 | |
839 if (ops && ops->update) | |
840 ops->update(gaimbuddylist, (GaimBlistNode *)buddy); | |
841 } | |
842 | |
843 void gaim_blist_alias_contact(GaimContact *contact, const char *alias) | |
844 { | |
845 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
846 GaimConversation *conv; | |
847 GaimBlistNode *bnode; | |
14670 | 848 char *old_alias; |
14192 | 849 |
850 g_return_if_fail(contact != NULL); | |
851 | |
14670 | 852 old_alias = contact->alias; |
853 | |
14192 | 854 if ((alias != NULL) && (*alias != '\0')) |
855 contact->alias = g_strdup(alias); | |
856 else | |
857 contact->alias = NULL; | |
858 | |
859 gaim_blist_schedule_save(); | |
860 | |
861 if (ops && ops->update) | |
862 ops->update(gaimbuddylist, (GaimBlistNode *)contact); | |
863 | |
864 for(bnode = ((GaimBlistNode *)contact)->child; bnode != NULL; bnode = bnode->next) | |
865 { | |
866 GaimBuddy *buddy = (GaimBuddy *)bnode; | |
867 | |
868 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, buddy->name, | |
869 buddy->account); | |
870 if (conv) | |
871 gaim_conversation_autoset_title(conv); | |
872 } | |
873 | |
874 gaim_signal_emit(gaim_blist_get_handle(), "blist-node-aliased", | |
875 contact, old_alias); | |
876 g_free(old_alias); | |
877 } | |
878 | |
879 void gaim_blist_alias_chat(GaimChat *chat, const char *alias) | |
880 { | |
881 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
14670 | 882 char *old_alias; |
14192 | 883 |
884 g_return_if_fail(chat != NULL); | |
885 | |
14670 | 886 old_alias = chat->alias; |
887 | |
14192 | 888 if ((alias != NULL) && (*alias != '\0')) |
889 chat->alias = g_strdup(alias); | |
890 else | |
891 chat->alias = NULL; | |
892 | |
893 gaim_blist_schedule_save(); | |
894 | |
895 if (ops && ops->update) | |
896 ops->update(gaimbuddylist, (GaimBlistNode *)chat); | |
897 | |
898 gaim_signal_emit(gaim_blist_get_handle(), "blist-node-aliased", | |
899 chat, old_alias); | |
900 g_free(old_alias); | |
901 } | |
902 | |
903 void gaim_blist_alias_buddy(GaimBuddy *buddy, const char *alias) | |
904 { | |
905 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
906 GaimConversation *conv; | |
14670 | 907 char *old_alias; |
14192 | 908 |
909 g_return_if_fail(buddy != NULL); | |
910 | |
14670 | 911 old_alias = buddy->alias; |
912 | |
14192 | 913 if ((alias != NULL) && (*alias != '\0')) |
914 buddy->alias = g_strdup(alias); | |
915 else | |
916 buddy->alias = NULL; | |
917 | |
918 gaim_blist_schedule_save(); | |
919 | |
920 if (ops && ops->update) | |
921 ops->update(gaimbuddylist, (GaimBlistNode *)buddy); | |
922 | |
923 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, buddy->name, | |
924 buddy->account); | |
925 if (conv) | |
926 gaim_conversation_autoset_title(conv); | |
927 | |
928 gaim_signal_emit(gaim_blist_get_handle(), "blist-node-aliased", | |
929 buddy, old_alias); | |
930 g_free(old_alias); | |
931 } | |
932 | |
933 void gaim_blist_server_alias_buddy(GaimBuddy *buddy, const char *alias) | |
934 { | |
935 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
936 GaimConversation *conv; | |
14670 | 937 char *old_alias; |
14192 | 938 |
939 g_return_if_fail(buddy != NULL); | |
940 | |
14670 | 941 old_alias = buddy->server_alias; |
942 | |
14192 | 943 if ((alias != NULL) && (*alias != '\0') && g_utf8_validate(alias, -1, NULL)) |
944 buddy->server_alias = g_strdup(alias); | |
945 else | |
946 buddy->server_alias = NULL; | |
947 | |
948 gaim_blist_schedule_save(); | |
949 | |
950 if (ops && ops->update) | |
951 ops->update(gaimbuddylist, (GaimBlistNode *)buddy); | |
952 | |
953 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, buddy->name, | |
954 buddy->account); | |
955 if (conv) | |
956 gaim_conversation_autoset_title(conv); | |
957 | |
958 gaim_signal_emit(gaim_blist_get_handle(), "blist-node-aliased", | |
959 buddy, old_alias); | |
960 g_free(old_alias); | |
961 } | |
962 | |
963 /* | |
964 * TODO: If merging, prompt the user if they want to merge. | |
965 */ | |
966 void gaim_blist_rename_group(GaimGroup *source, const char *new_name) | |
967 { | |
968 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
969 GaimGroup *dest; | |
970 gchar *old_name; | |
971 GList *moved_buddies = NULL; | |
972 GSList *accts; | |
973 | |
974 g_return_if_fail(source != NULL); | |
975 g_return_if_fail(new_name != NULL); | |
976 | |
977 if (*new_name == '\0' || !strcmp(new_name, source->name)) | |
978 return; | |
979 | |
980 dest = gaim_find_group(new_name); | |
981 if (dest != NULL) { | |
982 /* We're merging two groups */ | |
983 GaimBlistNode *prev, *child, *next; | |
984 | |
985 prev = gaim_blist_get_last_child((GaimBlistNode*)dest); | |
986 child = ((GaimBlistNode*)source)->child; | |
987 | |
988 /* | |
989 * TODO: This seems like a dumb way to do this... why not just | |
990 * append all children from the old group to the end of the new | |
991 * one? PRPLs might be expecting to receive an add_buddy() for | |
992 * each moved buddy... | |
993 */ | |
994 while (child) | |
995 { | |
996 next = child->next; | |
997 if (GAIM_BLIST_NODE_IS_CONTACT(child)) { | |
998 GaimBlistNode *bnode; | |
999 gaim_blist_add_contact((GaimContact *)child, dest, prev); | |
1000 for (bnode = child->child; bnode != NULL; bnode = bnode->next) { | |
1001 gaim_blist_add_buddy((GaimBuddy *)bnode, (GaimContact *)child, | |
1002 NULL, bnode->prev); | |
1003 moved_buddies = g_list_append(moved_buddies, bnode); | |
1004 } | |
1005 prev = child; | |
1006 } else if (GAIM_BLIST_NODE_IS_CHAT(child)) { | |
1007 gaim_blist_add_chat((GaimChat *)child, dest, prev); | |
1008 prev = child; | |
1009 } else { | |
1010 gaim_debug(GAIM_DEBUG_ERROR, "blist", | |
1011 "Unknown child type in group %s\n", source->name); | |
1012 } | |
1013 child = next; | |
1014 } | |
1015 | |
1016 /* Make a copy of the old group name and then delete the old group */ | |
1017 old_name = g_strdup(source->name); | |
1018 gaim_blist_remove_group(source); | |
1019 source = dest; | |
1020 } else { | |
1021 /* A simple rename */ | |
1022 GaimBlistNode *cnode, *bnode; | |
1023 | |
1024 /* Build a GList of all buddies in this group */ | |
1025 for (cnode = ((GaimBlistNode *)source)->child; cnode != NULL; cnode = cnode->next) { | |
1026 if (GAIM_BLIST_NODE_IS_CONTACT(cnode)) | |
1027 for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) | |
1028 moved_buddies = g_list_append(moved_buddies, bnode); | |
1029 } | |
1030 | |
1031 old_name = source->name; | |
1032 source->name = g_strdup(new_name); | |
1033 } | |
1034 | |
1035 /* Save our changes */ | |
1036 gaim_blist_schedule_save(); | |
1037 | |
1038 /* Update the UI */ | |
1039 if (ops && ops->update) | |
1040 ops->update(gaimbuddylist, (GaimBlistNode*)source); | |
1041 | |
1042 /* Notify all PRPLs */ | |
1043 /* TODO: Is this condition needed? Seems like it would always be TRUE */ | |
1044 if(old_name && source && strcmp(source->name, old_name)) { | |
1045 for (accts = gaim_group_get_accounts(source); accts; accts = g_slist_remove(accts, accts->data)) { | |
1046 GaimAccount *account = accts->data; | |
1047 GaimPluginProtocolInfo *prpl_info = NULL; | |
1048 GList *l = NULL, *buddies = NULL; | |
1049 | |
1050 if(account->gc && account->gc->prpl) | |
1051 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(account->gc->prpl); | |
1052 | |
1053 if(!prpl_info) | |
1054 continue; | |
1055 | |
1056 for(l = moved_buddies; l; l = l->next) { | |
1057 GaimBuddy *buddy = (GaimBuddy *)l->data; | |
1058 | |
1059 if(buddy && buddy->account == account) | |
1060 buddies = g_list_append(buddies, (GaimBlistNode *)buddy); | |
1061 } | |
1062 | |
1063 if(prpl_info->rename_group) { | |
1064 prpl_info->rename_group(account->gc, old_name, source, buddies); | |
1065 } else { | |
1066 GList *cur, *groups = NULL; | |
1067 | |
1068 /* Make a list of what the groups each buddy is in */ | |
1069 for(cur = buddies; cur; cur = cur->next) { | |
1070 GaimBlistNode *node = (GaimBlistNode *)cur->data; | |
1071 groups = g_list_prepend(groups, node->parent->parent); | |
1072 } | |
1073 | |
1074 gaim_account_remove_buddies(account, buddies, groups); | |
1075 g_list_free(groups); | |
1076 gaim_account_add_buddies(account, buddies); | |
1077 } | |
1078 | |
1079 g_list_free(buddies); | |
1080 } | |
1081 } | |
1082 g_list_free(moved_buddies); | |
1083 g_free(old_name); | |
1084 } | |
1085 | |
1086 static void gaim_blist_node_initialize_settings(GaimBlistNode *node); | |
1087 | |
1088 GaimChat *gaim_chat_new(GaimAccount *account, const char *alias, GHashTable *components) | |
1089 { | |
1090 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
1091 GaimChat *chat; | |
1092 | |
1093 g_return_val_if_fail(account != NULL, FALSE); | |
1094 g_return_val_if_fail(components != NULL, FALSE); | |
1095 | |
1096 chat = g_new0(GaimChat, 1); | |
1097 chat->account = account; | |
1098 if ((alias != NULL) && (*alias != '\0')) | |
1099 chat->alias = g_strdup(alias); | |
1100 chat->components = components; | |
1101 gaim_blist_node_initialize_settings((GaimBlistNode *)chat); | |
1102 ((GaimBlistNode *)chat)->type = GAIM_BLIST_CHAT_NODE; | |
1103 | |
1104 if (ops != NULL && ops->new_node != NULL) | |
1105 ops->new_node((GaimBlistNode *)chat); | |
1106 | |
1107 GAIM_DBUS_REGISTER_POINTER(chat, GaimChat); | |
1108 return chat; | |
1109 } | |
1110 | |
1111 GaimBuddy *gaim_buddy_new(GaimAccount *account, const char *screenname, const char *alias) | |
1112 { | |
1113 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
1114 GaimBuddy *buddy; | |
1115 | |
1116 g_return_val_if_fail(account != NULL, FALSE); | |
1117 g_return_val_if_fail(screenname != NULL, FALSE); | |
1118 | |
1119 buddy = g_new0(GaimBuddy, 1); | |
1120 buddy->account = account; | |
1121 buddy->name = g_strdup(screenname); | |
1122 buddy->alias = g_strdup(alias); | |
1123 buddy->presence = gaim_presence_new_for_buddy(buddy); | |
1124 ((GaimBlistNode *)buddy)->type = GAIM_BLIST_BUDDY_NODE; | |
1125 | |
1126 gaim_presence_set_status_active(buddy->presence, "offline", TRUE); | |
1127 | |
1128 gaim_blist_node_initialize_settings((GaimBlistNode *)buddy); | |
1129 | |
1130 if (ops && ops->new_node) | |
1131 ops->new_node((GaimBlistNode *)buddy); | |
1132 | |
1133 GAIM_DBUS_REGISTER_POINTER(buddy, GaimBuddy); | |
1134 return buddy; | |
1135 } | |
1136 | |
1137 void | |
1138 gaim_buddy_set_icon(GaimBuddy *buddy, GaimBuddyIcon *icon) | |
1139 { | |
1140 g_return_if_fail(buddy != NULL); | |
1141 | |
1142 if (buddy->icon != icon) { | |
1143 if (buddy->icon != NULL) | |
1144 gaim_buddy_icon_unref(buddy->icon); | |
1145 | |
1146 buddy->icon = (icon != NULL ? gaim_buddy_icon_ref(icon) : NULL); | |
1147 } | |
1148 | |
1149 if (buddy->icon) | |
1150 gaim_buddy_icon_cache(icon, buddy); | |
1151 else | |
1152 gaim_buddy_icon_uncache(buddy); | |
1153 | |
1154 gaim_blist_schedule_save(); | |
1155 | |
1156 gaim_signal_emit(gaim_blist_get_handle(), "buddy-icon-changed", buddy); | |
1157 | |
1158 gaim_blist_update_buddy_icon(buddy); | |
1159 } | |
1160 | |
1161 GaimAccount * | |
1162 gaim_buddy_get_account(const GaimBuddy *buddy) | |
1163 { | |
1164 g_return_val_if_fail(buddy != NULL, NULL); | |
1165 | |
1166 return buddy->account; | |
1167 } | |
1168 | |
1169 const char * | |
1170 gaim_buddy_get_name(const GaimBuddy *buddy) | |
1171 { | |
1172 g_return_val_if_fail(buddy != NULL, NULL); | |
1173 | |
1174 return buddy->name; | |
1175 } | |
1176 | |
1177 GaimBuddyIcon * | |
1178 gaim_buddy_get_icon(const GaimBuddy *buddy) | |
1179 { | |
1180 g_return_val_if_fail(buddy != NULL, NULL); | |
1181 | |
1182 return buddy->icon; | |
1183 } | |
1184 | |
1185 void gaim_blist_add_chat(GaimChat *chat, GaimGroup *group, GaimBlistNode *node) | |
1186 { | |
1187 GaimBlistNode *cnode = (GaimBlistNode*)chat; | |
1188 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
1189 | |
1190 g_return_if_fail(chat != NULL); | |
1191 g_return_if_fail(GAIM_BLIST_NODE_IS_CHAT((GaimBlistNode *)chat)); | |
1192 | |
1193 if (node == NULL) { | |
1194 if (group == NULL) { | |
1195 group = gaim_group_new(_("Chats")); | |
1196 gaim_blist_add_group(group, | |
1197 gaim_blist_get_last_sibling(gaimbuddylist->root)); | |
1198 } | |
1199 } else { | |
1200 group = (GaimGroup*)node->parent; | |
1201 } | |
1202 | |
1203 /* if we're moving to overtop of ourselves, do nothing */ | |
1204 if (cnode == node) | |
1205 return; | |
1206 | |
1207 if (cnode->parent) { | |
1208 /* This chat was already in the list and is | |
1209 * being moved. | |
1210 */ | |
1211 ((GaimGroup *)cnode->parent)->totalsize--; | |
1212 if (gaim_account_is_connected(chat->account)) { | |
1213 ((GaimGroup *)cnode->parent)->online--; | |
1214 ((GaimGroup *)cnode->parent)->currentsize--; | |
1215 } | |
1216 if (cnode->next) | |
1217 cnode->next->prev = cnode->prev; | |
1218 if (cnode->prev) | |
1219 cnode->prev->next = cnode->next; | |
1220 if (cnode->parent->child == cnode) | |
1221 cnode->parent->child = cnode->next; | |
1222 | |
1223 if (ops && ops->remove) | |
1224 ops->remove(gaimbuddylist, cnode); | |
1225 /* ops->remove() cleaned up the cnode's ui_data, so we need to | |
1226 * reinitialize it */ | |
1227 if (ops && ops->new_node) | |
1228 ops->new_node(cnode); | |
1229 | |
1230 gaim_blist_schedule_save(); | |
1231 } | |
1232 | |
1233 if (node != NULL) { | |
1234 if (node->next) | |
1235 node->next->prev = cnode; | |
1236 cnode->next = node->next; | |
1237 cnode->prev = node; | |
1238 cnode->parent = node->parent; | |
1239 node->next = cnode; | |
1240 ((GaimGroup *)node->parent)->totalsize++; | |
1241 if (gaim_account_is_connected(chat->account)) { | |
1242 ((GaimGroup *)node->parent)->online++; | |
1243 ((GaimGroup *)node->parent)->currentsize++; | |
1244 } | |
1245 } else { | |
1246 if (((GaimBlistNode *)group)->child) | |
1247 ((GaimBlistNode *)group)->child->prev = cnode; | |
1248 cnode->next = ((GaimBlistNode *)group)->child; | |
1249 cnode->prev = NULL; | |
1250 ((GaimBlistNode *)group)->child = cnode; | |
1251 cnode->parent = (GaimBlistNode *)group; | |
1252 group->totalsize++; | |
1253 if (gaim_account_is_connected(chat->account)) { | |
1254 group->online++; | |
1255 group->currentsize++; | |
1256 } | |
1257 } | |
1258 | |
1259 gaim_blist_schedule_save(); | |
1260 | |
1261 if (ops && ops->update) | |
1262 ops->update(gaimbuddylist, (GaimBlistNode *)cnode); | |
1263 } | |
1264 | |
1265 void gaim_blist_add_buddy(GaimBuddy *buddy, GaimContact *contact, GaimGroup *group, GaimBlistNode *node) | |
1266 { | |
1267 GaimBlistNode *cnode, *bnode; | |
1268 GaimGroup *g; | |
1269 GaimContact *c; | |
1270 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
1271 struct _gaim_hbuddy *hb; | |
1272 | |
1273 g_return_if_fail(buddy != NULL); | |
1274 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY((GaimBlistNode*)buddy)); | |
1275 | |
1276 bnode = (GaimBlistNode *)buddy; | |
1277 | |
1278 /* if we're moving to overtop of ourselves, do nothing */ | |
1279 if (bnode == node || (!node && bnode->parent && | |
1280 contact && bnode->parent == (GaimBlistNode*)contact | |
1281 && bnode == bnode->parent->child)) | |
1282 return; | |
1283 | |
1284 if (node && GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
1285 c = (GaimContact*)node->parent; | |
1286 g = (GaimGroup*)node->parent->parent; | |
1287 } else if (contact) { | |
1288 c = contact; | |
1289 g = (GaimGroup *)((GaimBlistNode *)c)->parent; | |
1290 } else { | |
1291 if (group) { | |
1292 g = group; | |
1293 } else { | |
1294 g = gaim_group_new(_("Buddies")); | |
1295 gaim_blist_add_group(g, | |
1296 gaim_blist_get_last_sibling(gaimbuddylist->root)); | |
1297 } | |
1298 c = gaim_contact_new(); | |
1299 gaim_blist_add_contact(c, g, | |
1300 gaim_blist_get_last_child((GaimBlistNode*)g)); | |
1301 } | |
1302 | |
1303 cnode = (GaimBlistNode *)c; | |
1304 | |
1305 if (bnode->parent) { | |
1306 if (GAIM_BUDDY_IS_ONLINE(buddy)) { | |
1307 ((GaimContact*)bnode->parent)->online--; | |
1308 if (((GaimContact*)bnode->parent)->online == 0) | |
1309 ((GaimGroup*)bnode->parent->parent)->online--; | |
1310 } | |
1311 if (gaim_account_is_connected(buddy->account)) { | |
1312 ((GaimContact*)bnode->parent)->currentsize--; | |
1313 if (((GaimContact*)bnode->parent)->currentsize == 0) | |
1314 ((GaimGroup*)bnode->parent->parent)->currentsize--; | |
1315 } | |
1316 ((GaimContact*)bnode->parent)->totalsize--; | |
1317 /* the group totalsize will be taken care of by remove_contact below */ | |
1318 | |
1319 if (bnode->parent->parent != (GaimBlistNode*)g) | |
1320 serv_move_buddy(buddy, (GaimGroup *)bnode->parent->parent, g); | |
1321 | |
1322 if (bnode->next) | |
1323 bnode->next->prev = bnode->prev; | |
1324 if (bnode->prev) | |
1325 bnode->prev->next = bnode->next; | |
1326 if (bnode->parent->child == bnode) | |
1327 bnode->parent->child = bnode->next; | |
1328 | |
1329 if (ops && ops->remove) | |
1330 ops->remove(gaimbuddylist, bnode); | |
1331 | |
1332 gaim_blist_schedule_save(); | |
1333 | |
1334 if (bnode->parent->parent != (GaimBlistNode*)g) { | |
1335 hb = g_new(struct _gaim_hbuddy, 1); | |
1336 hb->name = g_strdup(gaim_normalize(buddy->account, buddy->name)); | |
1337 hb->account = buddy->account; | |
1338 hb->group = bnode->parent->parent; | |
1339 g_hash_table_remove(gaimbuddylist->buddies, hb); | |
1340 g_free(hb->name); | |
1341 g_free(hb); | |
1342 } | |
1343 | |
1344 if (!bnode->parent->child) { | |
1345 gaim_blist_remove_contact((GaimContact*)bnode->parent); | |
1346 } else { | |
1347 gaim_contact_invalidate_priority_buddy((GaimContact*)bnode->parent); | |
1348 if (ops && ops->update) | |
1349 ops->update(gaimbuddylist, bnode->parent); | |
1350 } | |
1351 } | |
1352 | |
1353 if (node && GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
1354 if (node->next) | |
1355 node->next->prev = bnode; | |
1356 bnode->next = node->next; | |
1357 bnode->prev = node; | |
1358 bnode->parent = node->parent; | |
1359 node->next = bnode; | |
1360 } else { | |
1361 if (cnode->child) | |
1362 cnode->child->prev = bnode; | |
1363 bnode->prev = NULL; | |
1364 bnode->next = cnode->child; | |
1365 cnode->child = bnode; | |
1366 bnode->parent = cnode; | |
1367 } | |
1368 | |
1369 if (GAIM_BUDDY_IS_ONLINE(buddy)) { | |
1370 ((GaimContact*)bnode->parent)->online++; | |
1371 if (((GaimContact*)bnode->parent)->online == 1) | |
1372 ((GaimGroup*)bnode->parent->parent)->online++; | |
1373 } | |
1374 if (gaim_account_is_connected(buddy->account)) { | |
1375 ((GaimContact*)bnode->parent)->currentsize++; | |
1376 if (((GaimContact*)bnode->parent)->currentsize == 1) | |
1377 ((GaimGroup*)bnode->parent->parent)->currentsize++; | |
1378 } | |
1379 ((GaimContact*)bnode->parent)->totalsize++; | |
1380 | |
1381 hb = g_new(struct _gaim_hbuddy, 1); | |
1382 hb->name = g_strdup(gaim_normalize(buddy->account, buddy->name)); | |
1383 hb->account = buddy->account; | |
1384 hb->group = ((GaimBlistNode*)buddy)->parent->parent; | |
1385 | |
1386 g_hash_table_replace(gaimbuddylist->buddies, hb, buddy); | |
1387 | |
1388 gaim_contact_invalidate_priority_buddy(gaim_buddy_get_contact(buddy)); | |
1389 | |
1390 gaim_blist_schedule_save(); | |
1391 | |
1392 if (ops && ops->update) | |
1393 ops->update(gaimbuddylist, (GaimBlistNode*)buddy); | |
1394 | |
1395 /* Signal that the buddy has been added */ | |
1396 gaim_signal_emit(gaim_blist_get_handle(), "buddy-added", buddy); | |
1397 } | |
1398 | |
1399 GaimContact *gaim_contact_new() | |
1400 { | |
1401 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
1402 | |
1403 GaimContact *contact = g_new0(GaimContact, 1); | |
1404 contact->totalsize = 0; | |
1405 contact->currentsize = 0; | |
1406 contact->online = 0; | |
1407 gaim_blist_node_initialize_settings((GaimBlistNode *)contact); | |
1408 ((GaimBlistNode *)contact)->type = GAIM_BLIST_CONTACT_NODE; | |
1409 | |
1410 if (ops && ops->new_node) | |
1411 ops->new_node((GaimBlistNode *)contact); | |
1412 | |
1413 GAIM_DBUS_REGISTER_POINTER(contact, GaimContact); | |
1414 return contact; | |
1415 } | |
1416 | |
1417 void gaim_contact_set_alias(GaimContact *contact, const char *alias) | |
1418 { | |
1419 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
14670 | 1420 char *old_alias; |
14192 | 1421 |
1422 g_return_if_fail(contact != NULL); | |
1423 | |
14670 | 1424 old_alias = contact->alias; |
1425 | |
14192 | 1426 if ((alias != NULL) && (*alias != '\0')) |
1427 contact->alias = g_strdup(alias); | |
1428 else | |
1429 contact->alias = NULL; | |
1430 | |
1431 gaim_blist_schedule_save(); | |
1432 | |
1433 if (ops && ops->update) | |
1434 ops->update(gaimbuddylist, (GaimBlistNode*)contact); | |
1435 | |
1436 gaim_signal_emit(gaim_blist_get_handle(), "blist-node-aliased", | |
1437 contact, old_alias); | |
1438 g_free(old_alias); | |
1439 } | |
1440 | |
1441 const char *gaim_contact_get_alias(GaimContact* contact) | |
1442 { | |
1443 g_return_val_if_fail(contact != NULL, NULL); | |
1444 | |
1445 if (contact->alias) | |
1446 return contact->alias; | |
1447 | |
1448 return gaim_buddy_get_alias(gaim_contact_get_priority_buddy(contact)); | |
1449 } | |
1450 | |
1451 gboolean gaim_contact_on_account(GaimContact *c, GaimAccount *account) | |
1452 { | |
1453 GaimBlistNode *bnode, *cnode = (GaimBlistNode *) c; | |
1454 | |
1455 g_return_val_if_fail(c != NULL, FALSE); | |
1456 g_return_val_if_fail(account != NULL, FALSE); | |
1457 | |
1458 for (bnode = cnode->child; bnode; bnode = bnode->next) { | |
1459 GaimBuddy *buddy; | |
1460 | |
1461 if (! GAIM_BLIST_NODE_IS_BUDDY(bnode)) | |
1462 continue; | |
1463 | |
1464 buddy = (GaimBuddy *)bnode; | |
1465 if (buddy->account == account) | |
1466 return TRUE; | |
1467 } | |
1468 return FALSE; | |
1469 } | |
1470 | |
1471 void gaim_contact_invalidate_priority_buddy(GaimContact *contact) | |
1472 { | |
1473 g_return_if_fail(contact != NULL); | |
1474 | |
1475 contact->priority_valid = FALSE; | |
1476 } | |
1477 | |
1478 GaimGroup *gaim_group_new(const char *name) | |
1479 { | |
1480 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
1481 GaimGroup *group; | |
1482 | |
1483 g_return_val_if_fail(name != NULL, NULL); | |
1484 g_return_val_if_fail(*name != '\0', NULL); | |
1485 | |
1486 group = gaim_find_group(name); | |
1487 if (group != NULL) | |
1488 return group; | |
1489 | |
1490 group = g_new0(GaimGroup, 1); | |
1491 group->name = g_strdup(name); | |
1492 group->totalsize = 0; | |
1493 group->currentsize = 0; | |
1494 group->online = 0; | |
1495 gaim_blist_node_initialize_settings((GaimBlistNode *)group); | |
1496 ((GaimBlistNode *)group)->type = GAIM_BLIST_GROUP_NODE; | |
1497 | |
1498 if (ops && ops->new_node) | |
1499 ops->new_node((GaimBlistNode *)group); | |
1500 | |
1501 GAIM_DBUS_REGISTER_POINTER(group, GaimGroup); | |
1502 return group; | |
1503 } | |
1504 | |
1505 void gaim_blist_add_contact(GaimContact *contact, GaimGroup *group, GaimBlistNode *node) | |
1506 { | |
1507 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
1508 GaimGroup *g; | |
1509 GaimBlistNode *gnode, *cnode, *bnode; | |
1510 | |
1511 g_return_if_fail(contact != NULL); | |
1512 g_return_if_fail(GAIM_BLIST_NODE_IS_CONTACT((GaimBlistNode*)contact)); | |
1513 | |
1514 if ((GaimBlistNode*)contact == node) | |
1515 return; | |
1516 | |
1517 if (node && (GAIM_BLIST_NODE_IS_CONTACT(node) || | |
1518 GAIM_BLIST_NODE_IS_CHAT(node))) | |
1519 g = (GaimGroup*)node->parent; | |
1520 else if (group) | |
1521 g = group; | |
1522 else { | |
1523 g = gaim_group_new(_("Buddies")); | |
1524 gaim_blist_add_group(g, | |
1525 gaim_blist_get_last_sibling(gaimbuddylist->root)); | |
1526 } | |
1527 | |
1528 gnode = (GaimBlistNode*)g; | |
1529 cnode = (GaimBlistNode*)contact; | |
1530 | |
1531 if (cnode->parent) { | |
1532 if (cnode->parent->child == cnode) | |
1533 cnode->parent->child = cnode->next; | |
1534 if (cnode->prev) | |
1535 cnode->prev->next = cnode->next; | |
1536 if (cnode->next) | |
1537 cnode->next->prev = cnode->prev; | |
1538 | |
1539 if (cnode->parent != gnode) { | |
1540 bnode = cnode->child; | |
1541 while (bnode) { | |
1542 GaimBlistNode *next_bnode = bnode->next; | |
1543 GaimBuddy *b = (GaimBuddy*)bnode; | |
1544 | |
1545 struct _gaim_hbuddy *hb = g_new(struct _gaim_hbuddy, 1); | |
1546 hb->name = g_strdup(gaim_normalize(b->account, b->name)); | |
1547 hb->account = b->account; | |
1548 hb->group = cnode->parent; | |
1549 | |
1550 g_hash_table_remove(gaimbuddylist->buddies, hb); | |
1551 | |
1552 if (!gaim_find_buddy_in_group(b->account, b->name, g)) { | |
1553 hb->group = gnode; | |
1554 g_hash_table_replace(gaimbuddylist->buddies, hb, b); | |
1555 | |
1556 if (b->account->gc) | |
1557 serv_move_buddy(b, (GaimGroup *)cnode->parent, g); | |
1558 } else { | |
1559 gboolean empty_contact = FALSE; | |
1560 | |
1561 /* this buddy already exists in the group, so we're | |
1562 * gonna delete it instead */ | |
1563 g_free(hb->name); | |
1564 g_free(hb); | |
1565 if (b->account->gc) | |
1566 gaim_account_remove_buddy(b->account, b, (GaimGroup *)cnode->parent); | |
1567 | |
1568 if (!cnode->child->next) | |
1569 empty_contact = TRUE; | |
1570 gaim_blist_remove_buddy(b); | |
1571 | |
1572 /** in gaim_blist_remove_buddy(), if the last buddy in a | |
1573 * contact is removed, the contact is cleaned up and | |
1574 * g_free'd, so we mustn't try to reference bnode->next */ | |
1575 if (empty_contact) | |
1576 return; | |
1577 } | |
1578 bnode = next_bnode; | |
1579 } | |
1580 } | |
1581 | |
1582 if (contact->online > 0) | |
1583 ((GaimGroup*)cnode->parent)->online--; | |
1584 if (contact->currentsize > 0) | |
1585 ((GaimGroup*)cnode->parent)->currentsize--; | |
1586 ((GaimGroup*)cnode->parent)->totalsize--; | |
1587 | |
1588 if (ops && ops->remove) | |
1589 ops->remove(gaimbuddylist, cnode); | |
1590 | |
1591 gaim_blist_schedule_save(); | |
1592 } | |
1593 | |
1594 if (node && (GAIM_BLIST_NODE_IS_CONTACT(node) || | |
1595 GAIM_BLIST_NODE_IS_CHAT(node))) { | |
1596 if (node->next) | |
1597 node->next->prev = cnode; | |
1598 cnode->next = node->next; | |
1599 cnode->prev = node; | |
1600 cnode->parent = node->parent; | |
1601 node->next = cnode; | |
1602 } else { | |
1603 if (gnode->child) | |
1604 gnode->child->prev = cnode; | |
1605 cnode->prev = NULL; | |
1606 cnode->next = gnode->child; | |
1607 gnode->child = cnode; | |
1608 cnode->parent = gnode; | |
1609 } | |
1610 | |
1611 if (contact->online > 0) | |
1612 g->online++; | |
1613 if (contact->currentsize > 0) | |
1614 g->currentsize++; | |
1615 g->totalsize++; | |
1616 | |
1617 gaim_blist_schedule_save(); | |
1618 | |
1619 if (ops && ops->update) | |
1620 { | |
1621 if (cnode->child) | |
1622 ops->update(gaimbuddylist, cnode); | |
1623 | |
1624 for (bnode = cnode->child; bnode; bnode = bnode->next) | |
1625 ops->update(gaimbuddylist, bnode); | |
1626 } | |
1627 } | |
1628 | |
1629 void gaim_blist_merge_contact(GaimContact *source, GaimBlistNode *node) | |
1630 { | |
1631 GaimBlistNode *sourcenode = (GaimBlistNode*)source; | |
1632 GaimBlistNode *targetnode; | |
1633 GaimBlistNode *prev, *cur, *next; | |
1634 GaimContact *target; | |
1635 | |
1636 g_return_if_fail(source != NULL); | |
1637 g_return_if_fail(node != NULL); | |
1638 | |
1639 if (GAIM_BLIST_NODE_IS_CONTACT(node)) { | |
1640 target = (GaimContact *)node; | |
1641 prev = gaim_blist_get_last_child(node); | |
1642 } else if (GAIM_BLIST_NODE_IS_BUDDY(node)) { | |
1643 target = (GaimContact *)node->parent; | |
1644 prev = node; | |
1645 } else { | |
1646 return; | |
1647 } | |
1648 | |
1649 if (source == target || !target) | |
1650 return; | |
1651 | |
1652 targetnode = (GaimBlistNode *)target; | |
1653 next = sourcenode->child; | |
1654 | |
1655 while (next) { | |
1656 cur = next; | |
1657 next = cur->next; | |
1658 if (GAIM_BLIST_NODE_IS_BUDDY(cur)) { | |
1659 gaim_blist_add_buddy((GaimBuddy *)cur, target, NULL, prev); | |
1660 prev = cur; | |
1661 } | |
1662 } | |
1663 } | |
1664 | |
1665 void gaim_blist_add_group(GaimGroup *group, GaimBlistNode *node) | |
1666 { | |
1667 GaimBlistUiOps *ops; | |
1668 GaimBlistNode *gnode = (GaimBlistNode*)group; | |
1669 | |
1670 g_return_if_fail(group != NULL); | |
1671 g_return_if_fail(GAIM_BLIST_NODE_IS_GROUP((GaimBlistNode *)group)); | |
1672 | |
1673 ops = gaim_blist_get_ui_ops(); | |
1674 | |
1675 if (!gaimbuddylist->root) { | |
1676 gaimbuddylist->root = gnode; | |
1677 return; | |
1678 } | |
1679 | |
1680 /* if we're moving to overtop of ourselves, do nothing */ | |
1681 if (gnode == node) | |
1682 return; | |
1683 | |
1684 if (gaim_find_group(group->name)) { | |
1685 /* This is just being moved */ | |
1686 | |
1687 if (ops && ops->remove) | |
1688 ops->remove(gaimbuddylist, (GaimBlistNode *)group); | |
1689 | |
1690 if (gnode == gaimbuddylist->root) | |
1691 gaimbuddylist->root = gnode->next; | |
1692 if (gnode->prev) | |
1693 gnode->prev->next = gnode->next; | |
1694 if (gnode->next) | |
1695 gnode->next->prev = gnode->prev; | |
1696 } | |
1697 | |
1698 if (node && GAIM_BLIST_NODE_IS_GROUP(node)) { | |
1699 gnode->next = node->next; | |
1700 gnode->prev = node; | |
1701 if (node->next) | |
1702 node->next->prev = gnode; | |
1703 node->next = gnode; | |
1704 } else { | |
1705 if (gaimbuddylist->root) | |
1706 gaimbuddylist->root->prev = gnode; | |
1707 gnode->next = gaimbuddylist->root; | |
1708 gnode->prev = NULL; | |
1709 gaimbuddylist->root = gnode; | |
1710 } | |
1711 | |
1712 gaim_blist_schedule_save(); | |
1713 | |
1714 if (ops && ops->update) { | |
1715 ops->update(gaimbuddylist, gnode); | |
1716 for (node = gnode->child; node; node = node->next) | |
1717 ops->update(gaimbuddylist, node); | |
1718 } | |
1719 } | |
1720 | |
1721 void gaim_blist_remove_contact(GaimContact *contact) | |
1722 { | |
1723 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
1724 GaimBlistNode *node, *gnode; | |
1725 | |
1726 g_return_if_fail(contact != NULL); | |
1727 | |
1728 node = (GaimBlistNode *)contact; | |
1729 gnode = node->parent; | |
1730 | |
1731 if (node->child) { | |
1732 /* | |
1733 * If this contact has children then remove them. When the last | |
1734 * buddy is removed from the contact, the contact is automatically | |
1735 * deleted. | |
1736 */ | |
1737 while (node->child->next) { | |
1738 gaim_blist_remove_buddy((GaimBuddy*)node->child); | |
1739 } | |
1740 /* | |
1741 * Remove the last buddy and trigger the deletion of the contact. | |
1742 * It would probably be cleaner if contact-deletion was done after | |
1743 * a timeout? Or if it had to be done manually, like below? | |
1744 */ | |
1745 gaim_blist_remove_buddy((GaimBuddy*)node->child); | |
1746 } else { | |
1747 /* Remove the node from its parent */ | |
1748 if (gnode->child == node) | |
1749 gnode->child = node->next; | |
1750 if (node->prev) | |
1751 node->prev->next = node->next; | |
1752 if (node->next) | |
1753 node->next->prev = node->prev; | |
1754 | |
1755 gaim_blist_schedule_save(); | |
1756 | |
1757 /* Update the UI */ | |
1758 if (ops && ops->remove) | |
1759 ops->remove(gaimbuddylist, node); | |
1760 | |
1761 /* Delete the node */ | |
1762 g_hash_table_destroy(contact->node.settings); | |
1763 GAIM_DBUS_UNREGISTER_POINTER(contact); | |
1764 g_free(contact); | |
1765 } | |
1766 } | |
1767 | |
1768 void gaim_blist_remove_buddy(GaimBuddy *buddy) | |
1769 { | |
1770 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
1771 GaimBlistNode *node, *cnode, *gnode; | |
1772 GaimContact *contact; | |
1773 GaimGroup *group; | |
1774 struct _gaim_hbuddy hb; | |
1775 | |
1776 g_return_if_fail(buddy != NULL); | |
1777 | |
1778 node = (GaimBlistNode *)buddy; | |
1779 cnode = node->parent; | |
1780 gnode = cnode->parent; | |
1781 contact = (GaimContact *)cnode; | |
1782 group = (GaimGroup *)gnode; | |
1783 | |
1784 /* Delete any buddy icon. */ | |
1785 gaim_buddy_icon_uncache(buddy); | |
1786 | |
1787 /* Remove the node from its parent */ | |
1788 if (node->prev) | |
1789 node->prev->next = node->next; | |
1790 if (node->next) | |
1791 node->next->prev = node->prev; | |
1792 if (cnode->child == node) | |
1793 cnode->child = node->next; | |
1794 | |
1795 /* Adjust size counts */ | |
1796 if (GAIM_BUDDY_IS_ONLINE(buddy)) { | |
1797 contact->online--; | |
1798 if (contact->online == 0) | |
1799 group->online--; | |
1800 } | |
1801 if (gaim_account_is_connected(buddy->account)) { | |
1802 contact->currentsize--; | |
1803 if (contact->currentsize == 0) | |
1804 group->currentsize--; | |
1805 } | |
1806 contact->totalsize--; | |
1807 | |
1808 gaim_blist_schedule_save(); | |
1809 | |
1810 /* Re-sort the contact */ | |
1811 if (contact->priority == buddy) { | |
1812 gaim_contact_invalidate_priority_buddy(contact); | |
1813 if (ops && ops->update) | |
1814 ops->update(gaimbuddylist, cnode); | |
1815 } | |
1816 | |
1817 /* Remove this buddy from the buddies hash table */ | |
1818 hb.name = g_strdup(gaim_normalize(buddy->account, buddy->name)); | |
1819 hb.account = buddy->account; | |
1820 hb.group = ((GaimBlistNode*)buddy)->parent->parent; | |
1821 g_hash_table_remove(gaimbuddylist->buddies, &hb); | |
1822 g_free(hb.name); | |
1823 | |
1824 /* Update the UI */ | |
1825 if (ops && ops->remove) | |
1826 ops->remove(gaimbuddylist, node); | |
1827 | |
1828 /* Signal that the buddy has been removed before freeing the memory for it */ | |
1829 gaim_signal_emit(gaim_blist_get_handle(), "buddy-removed", buddy); | |
1830 | |
1831 /* Delete the node */ | |
1832 if (buddy->icon != NULL) | |
1833 gaim_buddy_icon_unref(buddy->icon); | |
1834 g_hash_table_destroy(buddy->node.settings); | |
1835 gaim_presence_remove_buddy(buddy->presence, buddy); | |
1836 gaim_presence_destroy(buddy->presence); | |
1837 g_free(buddy->name); | |
1838 g_free(buddy->alias); | |
1839 g_free(buddy->server_alias); | |
1840 | |
1841 GAIM_DBUS_UNREGISTER_POINTER(buddy); | |
1842 g_free(buddy); | |
1843 | |
1844 /* FIXME: Once GaimBuddy is a GObject, timeout callbacks can | |
1845 * g_object_ref() it when connecting the callback and | |
1846 * g_object_unref() it in the handler. That way, it won't | |
1847 * get freed while the timeout is pending and this line can | |
1848 * be removed. */ | |
1849 while (g_source_remove_by_user_data((gpointer *)buddy)); | |
1850 | |
1851 /* If the contact is empty then remove it */ | |
1852 if (!cnode->child) | |
1853 gaim_blist_remove_contact(contact); | |
1854 } | |
1855 | |
1856 void gaim_blist_remove_chat(GaimChat *chat) | |
1857 { | |
1858 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
1859 GaimBlistNode *node, *gnode; | |
1860 GaimGroup *group; | |
1861 | |
1862 g_return_if_fail(chat != NULL); | |
1863 | |
1864 node = (GaimBlistNode *)chat; | |
1865 gnode = node->parent; | |
1866 group = (GaimGroup *)gnode; | |
1867 | |
1868 if (gnode != NULL) | |
1869 { | |
1870 /* Remove the node from its parent */ | |
1871 if (gnode->child == node) | |
1872 gnode->child = node->next; | |
1873 if (node->prev) | |
1874 node->prev->next = node->next; | |
1875 if (node->next) | |
1876 node->next->prev = node->prev; | |
1877 | |
1878 /* Adjust size counts */ | |
1879 if (gaim_account_is_connected(chat->account)) { | |
1880 group->online--; | |
1881 group->currentsize--; | |
1882 } | |
1883 group->totalsize--; | |
1884 | |
1885 gaim_blist_schedule_save(); | |
1886 } | |
1887 | |
1888 /* Update the UI */ | |
1889 if (ops && ops->remove) | |
1890 ops->remove(gaimbuddylist, node); | |
1891 | |
1892 /* Delete the node */ | |
1893 g_hash_table_destroy(chat->components); | |
1894 g_hash_table_destroy(chat->node.settings); | |
1895 g_free(chat->alias); | |
1896 GAIM_DBUS_UNREGISTER_POINTER(chat); | |
1897 g_free(chat); | |
1898 } | |
1899 | |
1900 void gaim_blist_remove_group(GaimGroup *group) | |
1901 { | |
1902 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
1903 GaimBlistNode *node; | |
1904 GList *l; | |
1905 | |
1906 g_return_if_fail(group != NULL); | |
1907 | |
1908 node = (GaimBlistNode *)group; | |
1909 | |
1910 /* Make sure the group is empty */ | |
1911 if (node->child) { | |
1912 char *buf; | |
1913 int count = 0; | |
1914 GaimBlistNode *child; | |
1915 | |
1916 for (child = node->child; child != NULL; child = child->next) | |
1917 count++; | |
1918 | |
1919 buf = g_strdup_printf(ngettext("%d buddy from group %s was not removed " | |
1920 "because it belongs to an account which is " | |
1921 "disabled or offline. This buddy and the " | |
1922 "group were not removed.\n", | |
1923 "%d buddies from group %s were not " | |
1924 "removed because they belong to accounts " | |
1925 "which are currently disabled or offline. " | |
1926 "These buddies and the group were not " | |
1927 "removed.\n", count), | |
1928 count, group->name); | |
1929 gaim_notify_error(NULL, NULL, _("Group not removed"), buf); | |
1930 g_free(buf); | |
1931 return; | |
1932 } | |
1933 | |
1934 /* Remove the node from its parent */ | |
1935 if (gaimbuddylist->root == node) | |
1936 gaimbuddylist->root = node->next; | |
1937 if (node->prev) | |
1938 node->prev->next = node->next; | |
1939 if (node->next) | |
1940 node->next->prev = node->prev; | |
1941 | |
1942 gaim_blist_schedule_save(); | |
1943 | |
1944 /* Update the UI */ | |
1945 if (ops && ops->remove) | |
1946 ops->remove(gaimbuddylist, node); | |
1947 | |
1948 /* Remove the group from all accounts that are online */ | |
1949 for (l = gaim_connections_get_all(); l != NULL; l = l->next) | |
1950 { | |
1951 GaimConnection *gc = (GaimConnection *)l->data; | |
1952 | |
1953 if (gaim_connection_get_state(gc) == GAIM_CONNECTED) | |
1954 gaim_account_remove_group(gaim_connection_get_account(gc), group); | |
1955 } | |
1956 | |
1957 /* Delete the node */ | |
1958 g_hash_table_destroy(group->node.settings); | |
1959 g_free(group->name); | |
1960 GAIM_DBUS_UNREGISTER_POINTER(group); | |
1961 g_free(group); | |
1962 } | |
1963 | |
1964 GaimBuddy *gaim_contact_get_priority_buddy(GaimContact *contact) | |
1965 { | |
1966 g_return_val_if_fail(contact != NULL, NULL); | |
1967 | |
1968 if (!contact->priority_valid) | |
1969 gaim_contact_compute_priority_buddy(contact); | |
1970 | |
1971 return contact->priority; | |
1972 } | |
1973 | |
1974 const char *gaim_buddy_get_alias_only(GaimBuddy *buddy) | |
1975 { | |
1976 g_return_val_if_fail(buddy != NULL, NULL); | |
1977 | |
1978 if ((buddy->alias != NULL) && (*buddy->alias != '\0')) { | |
1979 return buddy->alias; | |
1980 } else if ((buddy->server_alias != NULL) && | |
1981 (*buddy->server_alias != '\0')) { | |
1982 | |
1983 return buddy->server_alias; | |
1984 } | |
1985 | |
1986 return NULL; | |
1987 } | |
1988 | |
1989 | |
1990 const char *gaim_buddy_get_contact_alias(GaimBuddy *buddy) | |
1991 { | |
1992 GaimContact *c; | |
1993 | |
1994 g_return_val_if_fail(buddy != NULL, NULL); | |
1995 | |
1996 /* Search for an alias for the buddy. In order of precedence: */ | |
1997 /* The buddy alias */ | |
1998 if (buddy->alias != NULL) | |
1999 return buddy->alias; | |
2000 | |
2001 /* The contact alias */ | |
2002 c = gaim_buddy_get_contact(buddy); | |
2003 if ((c != NULL) && (c->alias != NULL)) | |
2004 return c->alias; | |
2005 | |
2006 /* The server alias */ | |
2007 if ((buddy->server_alias) && (*buddy->server_alias)) | |
2008 return buddy->server_alias; | |
2009 | |
2010 /* The buddy's user name (i.e. no alias) */ | |
2011 return buddy->name; | |
2012 } | |
2013 | |
2014 | |
2015 const char *gaim_buddy_get_alias(GaimBuddy *buddy) | |
2016 { | |
2017 g_return_val_if_fail(buddy != NULL, NULL); | |
2018 | |
2019 /* Search for an alias for the buddy. In order of precedence: */ | |
2020 /* The buddy alias */ | |
2021 if (buddy->alias != NULL) | |
2022 return buddy->alias; | |
2023 | |
2024 /* The server alias */ | |
2025 if ((buddy->server_alias) && (*buddy->server_alias)) | |
2026 return buddy->server_alias; | |
2027 | |
2028 /* The buddy's user name (i.e. no alias) */ | |
2029 return buddy->name; | |
2030 } | |
2031 | |
14491
8a1c6ed3dde6
[gaim-migrate @ 17210]
Richard Laager <rlaager@wiktel.com>
parents:
14192
diff
changeset
|
2032 const char *gaim_buddy_get_server_alias(GaimBuddy *buddy) |
8a1c6ed3dde6
[gaim-migrate @ 17210]
Richard Laager <rlaager@wiktel.com>
parents:
14192
diff
changeset
|
2033 { |
8a1c6ed3dde6
[gaim-migrate @ 17210]
Richard Laager <rlaager@wiktel.com>
parents:
14192
diff
changeset
|
2034 g_return_val_if_fail(buddy != NULL, NULL); |
8a1c6ed3dde6
[gaim-migrate @ 17210]
Richard Laager <rlaager@wiktel.com>
parents:
14192
diff
changeset
|
2035 |
8a1c6ed3dde6
[gaim-migrate @ 17210]
Richard Laager <rlaager@wiktel.com>
parents:
14192
diff
changeset
|
2036 if ((buddy->server_alias) && (*buddy->server_alias)) |
8a1c6ed3dde6
[gaim-migrate @ 17210]
Richard Laager <rlaager@wiktel.com>
parents:
14192
diff
changeset
|
2037 return buddy->server_alias; |
8a1c6ed3dde6
[gaim-migrate @ 17210]
Richard Laager <rlaager@wiktel.com>
parents:
14192
diff
changeset
|
2038 |
8a1c6ed3dde6
[gaim-migrate @ 17210]
Richard Laager <rlaager@wiktel.com>
parents:
14192
diff
changeset
|
2039 return NULL; |
8a1c6ed3dde6
[gaim-migrate @ 17210]
Richard Laager <rlaager@wiktel.com>
parents:
14192
diff
changeset
|
2040 } |
8a1c6ed3dde6
[gaim-migrate @ 17210]
Richard Laager <rlaager@wiktel.com>
parents:
14192
diff
changeset
|
2041 |
14192 | 2042 const char *gaim_buddy_get_local_alias(GaimBuddy *buddy) |
2043 { | |
2044 GaimContact *c; | |
2045 | |
2046 g_return_val_if_fail(buddy != NULL, NULL); | |
2047 | |
2048 /* Search for an alias for the buddy. In order of precedence: */ | |
2049 /* The buddy alias */ | |
2050 if (buddy->alias != NULL) | |
2051 return buddy->alias; | |
2052 | |
2053 /* The contact alias */ | |
2054 c = gaim_buddy_get_contact(buddy); | |
2055 if ((c != NULL) && (c->alias != NULL)) | |
2056 return c->alias; | |
2057 | |
2058 /* The buddy's user name (i.e. no alias) */ | |
2059 return buddy->name; | |
2060 } | |
2061 | |
2062 const char *gaim_chat_get_name(GaimChat *chat) | |
2063 { | |
2064 struct proto_chat_entry *pce; | |
2065 GList *parts; | |
2066 char *ret; | |
2067 | |
2068 g_return_val_if_fail(chat != NULL, NULL); | |
2069 | |
2070 if ((chat->alias != NULL) && (*chat->alias != '\0')) | |
2071 return chat->alias; | |
2072 | |
2073 parts = GAIM_PLUGIN_PROTOCOL_INFO(chat->account->gc->prpl)->chat_info(chat->account->gc); | |
2074 pce = parts->data; | |
2075 ret = g_hash_table_lookup(chat->components, pce->identifier); | |
2076 g_list_foreach(parts, (GFunc)g_free, NULL); | |
2077 g_list_free(parts); | |
2078 | |
2079 return ret; | |
2080 } | |
2081 | |
2082 GaimBuddy *gaim_find_buddy(GaimAccount *account, const char *name) | |
2083 { | |
2084 GaimBuddy *buddy; | |
2085 struct _gaim_hbuddy hb; | |
2086 GaimBlistNode *group; | |
2087 | |
2088 g_return_val_if_fail(gaimbuddylist != NULL, NULL); | |
2089 g_return_val_if_fail(account != NULL, NULL); | |
2090 g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL); | |
2091 | |
2092 hb.account = account; | |
2093 hb.name = g_strdup(gaim_normalize(account, name)); | |
2094 | |
2095 for (group = gaimbuddylist->root; group; group = group->next) { | |
2096 hb.group = group; | |
2097 if ((buddy = g_hash_table_lookup(gaimbuddylist->buddies, &hb))) { | |
2098 g_free(hb.name); | |
2099 return buddy; | |
2100 } | |
2101 } | |
2102 g_free(hb.name); | |
2103 | |
2104 return NULL; | |
2105 } | |
2106 | |
2107 GaimBuddy *gaim_find_buddy_in_group(GaimAccount *account, const char *name, | |
2108 GaimGroup *group) | |
2109 { | |
2110 struct _gaim_hbuddy hb; | |
2111 GaimBuddy *ret; | |
2112 | |
2113 g_return_val_if_fail(gaimbuddylist != NULL, NULL); | |
2114 g_return_val_if_fail(account != NULL, NULL); | |
2115 g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL); | |
2116 | |
2117 hb.name = g_strdup(gaim_normalize(account, name)); | |
2118 hb.account = account; | |
2119 hb.group = (GaimBlistNode*)group; | |
2120 | |
2121 ret = g_hash_table_lookup(gaimbuddylist->buddies, &hb); | |
2122 g_free(hb.name); | |
2123 | |
2124 return ret; | |
2125 } | |
2126 | |
14752
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2127 static void find_acct_buddies(gpointer key, gpointer value, gpointer data) |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2128 { |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2129 struct _gaim_hbuddy *hb = key; |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2130 GaimBuddy *buddy = value; |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2131 struct _list_account_buddies *ab = data; |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2132 |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2133 if (hb->account == ab->account) { |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2134 ab->list = g_slist_prepend(ab->list, buddy); |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2135 } |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2136 } |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2137 |
14192 | 2138 GSList *gaim_find_buddies(GaimAccount *account, const char *name) |
2139 { | |
14752
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2140 GaimBuddy *buddy; |
14192 | 2141 GaimBlistNode *node; |
2142 GSList *ret = NULL; | |
2143 | |
2144 g_return_val_if_fail(gaimbuddylist != NULL, NULL); | |
2145 g_return_val_if_fail(account != NULL, NULL); | |
14752
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2146 |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2147 |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2148 if ((name != NULL) && (*name != '\0')) { |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2149 struct _gaim_hbuddy hb; |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2150 |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2151 hb.name = g_strdup(gaim_normalize(account, name)); |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2152 hb.account = account; |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2153 |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2154 for (node = gaimbuddylist->root; node != NULL; node = node->next) { |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2155 hb.group = node; |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2156 if ((buddy = g_hash_table_lookup(gaimbuddylist->buddies, &hb)) != NULL) |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2157 ret = g_slist_prepend(ret, buddy); |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2158 } |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2159 g_free(hb.name); |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2160 } else { |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2161 struct _list_account_buddies *ab = g_new0(struct _list_account_buddies, 1); |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2162 ab->account = account; |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2163 g_hash_table_foreach(gaimbuddylist->buddies, find_acct_buddies, ab); |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2164 ret = ab->list; |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14670
diff
changeset
|
2165 g_free(ab); |
14192 | 2166 } |
2167 | |
2168 return ret; | |
2169 } | |
2170 | |
2171 GaimGroup *gaim_find_group(const char *name) | |
2172 { | |
2173 GaimBlistNode *node; | |
2174 | |
2175 g_return_val_if_fail(gaimbuddylist != NULL, NULL); | |
2176 g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL); | |
2177 | |
2178 for (node = gaimbuddylist->root; node != NULL; node = node->next) { | |
2179 if (!strcmp(((GaimGroup *)node)->name, name)) | |
2180 return (GaimGroup *)node; | |
2181 } | |
2182 | |
2183 return NULL; | |
2184 } | |
2185 | |
2186 GaimChat * | |
2187 gaim_blist_find_chat(GaimAccount *account, const char *name) | |
2188 { | |
2189 char *chat_name; | |
2190 GaimChat *chat; | |
2191 GaimPlugin *prpl; | |
2192 GaimPluginProtocolInfo *prpl_info = NULL; | |
2193 struct proto_chat_entry *pce; | |
2194 GaimBlistNode *node, *group; | |
2195 GList *parts; | |
2196 | |
2197 g_return_val_if_fail(gaimbuddylist != NULL, NULL); | |
2198 g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL); | |
2199 | |
2200 if (!gaim_account_is_connected(account)) | |
2201 return NULL; | |
2202 | |
2203 prpl = gaim_find_prpl(gaim_account_get_protocol_id(account)); | |
2204 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); | |
2205 | |
2206 if (prpl_info->find_blist_chat != NULL) | |
2207 return prpl_info->find_blist_chat(account, name); | |
2208 | |
2209 for (group = gaimbuddylist->root; group != NULL; group = group->next) { | |
2210 for (node = group->child; node != NULL; node = node->next) { | |
2211 if (GAIM_BLIST_NODE_IS_CHAT(node)) { | |
2212 | |
2213 chat = (GaimChat*)node; | |
2214 | |
2215 if (account != chat->account) | |
2216 continue; | |
2217 | |
2218 parts = prpl_info->chat_info( | |
2219 gaim_account_get_connection(chat->account)); | |
2220 | |
2221 pce = parts->data; | |
2222 chat_name = g_hash_table_lookup(chat->components, | |
2223 pce->identifier); | |
2224 | |
2225 if (chat->account == account && chat_name != NULL && | |
2226 name != NULL && !strcmp(chat_name, name)) { | |
2227 | |
2228 return chat; | |
2229 } | |
2230 } | |
2231 } | |
2232 } | |
2233 | |
2234 return NULL; | |
2235 } | |
2236 | |
2237 GaimGroup * | |
2238 gaim_chat_get_group(GaimChat *chat) | |
2239 { | |
2240 g_return_val_if_fail(chat != NULL, NULL); | |
2241 | |
2242 return (GaimGroup *)(((GaimBlistNode *)chat)->parent); | |
2243 } | |
2244 | |
2245 GaimContact *gaim_buddy_get_contact(GaimBuddy *buddy) | |
2246 { | |
2247 g_return_val_if_fail(buddy != NULL, NULL); | |
2248 | |
2249 return (GaimContact*)((GaimBlistNode*)buddy)->parent; | |
2250 } | |
2251 | |
2252 GaimPresence *gaim_buddy_get_presence(const GaimBuddy *buddy) | |
2253 { | |
2254 g_return_val_if_fail(buddy != NULL, NULL); | |
2255 return buddy->presence; | |
2256 } | |
2257 | |
2258 GaimGroup *gaim_buddy_get_group(GaimBuddy *buddy) | |
2259 { | |
2260 g_return_val_if_fail(buddy != NULL, NULL); | |
2261 | |
2262 if (((GaimBlistNode *)buddy)->parent == NULL) | |
2263 return NULL; | |
2264 | |
2265 return (GaimGroup *)(((GaimBlistNode*)buddy)->parent->parent); | |
2266 } | |
2267 | |
2268 GSList *gaim_group_get_accounts(GaimGroup *group) | |
2269 { | |
2270 GSList *l = NULL; | |
2271 GaimBlistNode *gnode, *cnode, *bnode; | |
2272 | |
2273 gnode = (GaimBlistNode *)group; | |
2274 | |
2275 for (cnode = gnode->child; cnode; cnode = cnode->next) { | |
2276 if (GAIM_BLIST_NODE_IS_CHAT(cnode)) { | |
2277 if (!g_slist_find(l, ((GaimChat *)cnode)->account)) | |
2278 l = g_slist_append(l, ((GaimChat *)cnode)->account); | |
2279 } else if (GAIM_BLIST_NODE_IS_CONTACT(cnode)) { | |
2280 for (bnode = cnode->child; bnode; bnode = bnode->next) { | |
2281 if (GAIM_BLIST_NODE_IS_BUDDY(bnode)) { | |
2282 if (!g_slist_find(l, ((GaimBuddy *)bnode)->account)) | |
2283 l = g_slist_append(l, ((GaimBuddy *)bnode)->account); | |
2284 } | |
2285 } | |
2286 } | |
2287 } | |
2288 | |
2289 return l; | |
2290 } | |
2291 | |
2292 void gaim_blist_add_account(GaimAccount *account) | |
2293 { | |
2294 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
2295 GaimBlistNode *gnode, *cnode, *bnode; | |
2296 | |
2297 g_return_if_fail(gaimbuddylist != NULL); | |
2298 | |
2299 if (!ops || !ops->update) | |
2300 return; | |
2301 | |
2302 for (gnode = gaimbuddylist->root; gnode; gnode = gnode->next) { | |
2303 if (!GAIM_BLIST_NODE_IS_GROUP(gnode)) | |
2304 continue; | |
2305 for (cnode = gnode->child; cnode; cnode = cnode->next) { | |
2306 if (GAIM_BLIST_NODE_IS_CONTACT(cnode)) { | |
2307 gboolean recompute = FALSE; | |
2308 for (bnode = cnode->child; bnode; bnode = bnode->next) { | |
2309 if (GAIM_BLIST_NODE_IS_BUDDY(bnode) && | |
2310 ((GaimBuddy*)bnode)->account == account) { | |
2311 recompute = TRUE; | |
2312 ((GaimContact*)cnode)->currentsize++; | |
2313 if (((GaimContact*)cnode)->currentsize == 1) | |
2314 ((GaimGroup*)gnode)->currentsize++; | |
2315 ops->update(gaimbuddylist, bnode); | |
2316 } | |
2317 } | |
2318 if (recompute || | |
2319 gaim_blist_node_get_bool(cnode, "show_offline")) { | |
2320 gaim_contact_invalidate_priority_buddy((GaimContact*)cnode); | |
2321 ops->update(gaimbuddylist, cnode); | |
2322 } | |
2323 } else if (GAIM_BLIST_NODE_IS_CHAT(cnode) && | |
2324 ((GaimChat*)cnode)->account == account) { | |
2325 ((GaimGroup *)gnode)->online++; | |
2326 ((GaimGroup *)gnode)->currentsize++; | |
2327 ops->update(gaimbuddylist, cnode); | |
2328 } | |
2329 } | |
2330 ops->update(gaimbuddylist, gnode); | |
2331 } | |
2332 } | |
2333 | |
2334 void gaim_blist_remove_account(GaimAccount *account) | |
2335 { | |
2336 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); | |
2337 GaimBlistNode *gnode, *cnode, *bnode; | |
2338 GaimBuddy *buddy; | |
2339 GaimChat *chat; | |
2340 GaimContact *contact; | |
2341 GaimGroup *group; | |
2342 GList *list = NULL, *iter = NULL; | |
2343 | |
2344 g_return_if_fail(gaimbuddylist != NULL); | |
2345 | |
2346 for (gnode = gaimbuddylist->root; gnode; gnode = gnode->next) { | |
2347 if (!GAIM_BLIST_NODE_IS_GROUP(gnode)) | |
2348 continue; | |
2349 | |
2350 group = (GaimGroup *)gnode; | |
2351 | |
2352 for (cnode = gnode->child; cnode; cnode = cnode->next) { | |
2353 if (GAIM_BLIST_NODE_IS_CONTACT(cnode)) { | |
2354 gboolean recompute = FALSE; | |
2355 contact = (GaimContact *)cnode; | |
2356 | |
2357 for (bnode = cnode->child; bnode; bnode = bnode->next) { | |
2358 if (!GAIM_BLIST_NODE_IS_BUDDY(bnode)) | |
2359 continue; | |
2360 | |
2361 buddy = (GaimBuddy *)bnode; | |
2362 if (account == buddy->account) { | |
2363 GaimPresence *presence; | |
2364 recompute = TRUE; | |
2365 | |
2366 presence = gaim_buddy_get_presence(buddy); | |
2367 | |
2368 if(gaim_presence_is_online(presence)) { | |
2369 contact->online--; | |
2370 if (contact->online == 0) | |
2371 group->online--; | |
2372 | |
2373 gaim_blist_node_set_int(&buddy->node, | |
2374 "last_seen", time(NULL)); | |
2375 } | |
2376 | |
2377 contact->currentsize--; | |
2378 if (contact->currentsize == 0) | |
2379 group->currentsize--; | |
2380 | |
2381 if (!g_list_find(list, presence)) | |
2382 list = g_list_prepend(list, presence); | |
2383 | |
2384 if (ops && ops->remove) | |
2385 ops->remove(gaimbuddylist, bnode); | |
2386 } | |
2387 } | |
2388 if (recompute) { | |
2389 gaim_contact_invalidate_priority_buddy(contact); | |
2390 if (ops && ops->update) | |
2391 ops->update(gaimbuddylist, cnode); | |
2392 } | |
2393 } else if (GAIM_BLIST_NODE_IS_CHAT(cnode)) { | |
2394 chat = (GaimChat *)cnode; | |
2395 | |
2396 if(chat->account == account) { | |
2397 group->currentsize--; | |
2398 group->online--; | |
2399 | |
2400 if (ops && ops->remove) | |
2401 ops->remove(gaimbuddylist, cnode); | |
2402 } | |
2403 } | |
2404 } | |
2405 } | |
2406 | |
2407 for (iter = list; iter; iter = iter->next) | |
2408 { | |
2409 gaim_presence_set_status_active(iter->data, "offline", TRUE); | |
2410 } | |
2411 g_list_free(list); | |
2412 } | |
2413 | |
2414 gboolean gaim_group_on_account(GaimGroup *g, GaimAccount *account) | |
2415 { | |
2416 GaimBlistNode *cnode; | |
2417 for (cnode = ((GaimBlistNode *)g)->child; cnode; cnode = cnode->next) { | |
2418 if (GAIM_BLIST_NODE_IS_CONTACT(cnode)) { | |
2419 if(gaim_contact_on_account((GaimContact *) cnode, account)) | |
2420 return TRUE; | |
2421 } else if (GAIM_BLIST_NODE_IS_CHAT(cnode)) { | |
2422 GaimChat *chat = (GaimChat *)cnode; | |
2423 if ((!account && gaim_account_is_connected(chat->account)) | |
2424 || chat->account == account) | |
2425 return TRUE; | |
2426 } | |
2427 } | |
2428 return FALSE; | |
2429 } | |
2430 | |
2431 void | |
2432 gaim_blist_request_add_buddy(GaimAccount *account, const char *username, | |
2433 const char *group, const char *alias) | |
2434 { | |
2435 GaimBlistUiOps *ui_ops; | |
2436 | |
2437 ui_ops = gaim_blist_get_ui_ops(); | |
2438 | |
2439 if (ui_ops != NULL && ui_ops->request_add_buddy != NULL) | |
2440 ui_ops->request_add_buddy(account, username, group, alias); | |
2441 } | |
2442 | |
2443 void | |
2444 gaim_blist_request_add_chat(GaimAccount *account, GaimGroup *group, | |
2445 const char *alias, const char *name) | |
2446 { | |
2447 GaimBlistUiOps *ui_ops; | |
2448 | |
2449 ui_ops = gaim_blist_get_ui_ops(); | |
2450 | |
2451 if (ui_ops != NULL && ui_ops->request_add_chat != NULL) | |
2452 ui_ops->request_add_chat(account, group, alias, name); | |
2453 } | |
2454 | |
2455 void | |
2456 gaim_blist_request_add_group(void) | |
2457 { | |
2458 GaimBlistUiOps *ui_ops; | |
2459 | |
2460 ui_ops = gaim_blist_get_ui_ops(); | |
2461 | |
2462 if (ui_ops != NULL && ui_ops->request_add_group != NULL) | |
2463 ui_ops->request_add_group(); | |
2464 } | |
2465 | |
2466 static void | |
2467 gaim_blist_node_setting_free(gpointer data) | |
2468 { | |
2469 GaimValue *value; | |
2470 | |
2471 value = (GaimValue *)data; | |
2472 | |
2473 gaim_value_destroy(value); | |
2474 } | |
2475 | |
2476 static void gaim_blist_node_initialize_settings(GaimBlistNode *node) | |
2477 { | |
2478 if (node->settings) | |
2479 return; | |
2480 | |
2481 node->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, | |
2482 (GDestroyNotify)gaim_blist_node_setting_free); | |
2483 } | |
2484 | |
2485 void gaim_blist_node_remove_setting(GaimBlistNode *node, const char *key) | |
2486 { | |
2487 g_return_if_fail(node != NULL); | |
2488 g_return_if_fail(node->settings != NULL); | |
2489 g_return_if_fail(key != NULL); | |
2490 | |
2491 g_hash_table_remove(node->settings, key); | |
2492 | |
2493 gaim_blist_schedule_save(); | |
2494 } | |
2495 | |
2496 void | |
2497 gaim_blist_node_set_flags(GaimBlistNode *node, GaimBlistNodeFlags flags) | |
2498 { | |
2499 g_return_if_fail(node != NULL); | |
2500 | |
2501 node->flags = flags; | |
2502 } | |
2503 | |
2504 GaimBlistNodeFlags | |
2505 gaim_blist_node_get_flags(GaimBlistNode *node) | |
2506 { | |
2507 g_return_val_if_fail(node != NULL, 0); | |
2508 | |
2509 return node->flags; | |
2510 } | |
2511 | |
2512 void | |
2513 gaim_blist_node_set_bool(GaimBlistNode* node, const char *key, gboolean data) | |
2514 { | |
2515 GaimValue *value; | |
2516 | |
2517 g_return_if_fail(node != NULL); | |
2518 g_return_if_fail(node->settings != NULL); | |
2519 g_return_if_fail(key != NULL); | |
2520 | |
2521 value = gaim_value_new(GAIM_TYPE_BOOLEAN); | |
2522 gaim_value_set_boolean(value, data); | |
2523 | |
2524 g_hash_table_replace(node->settings, g_strdup(key), value); | |
2525 | |
2526 gaim_blist_schedule_save(); | |
2527 } | |
2528 | |
2529 gboolean | |
2530 gaim_blist_node_get_bool(GaimBlistNode* node, const char *key) | |
2531 { | |
2532 GaimValue *value; | |
2533 | |
2534 g_return_val_if_fail(node != NULL, FALSE); | |
2535 g_return_val_if_fail(node->settings != NULL, FALSE); | |
2536 g_return_val_if_fail(key != NULL, FALSE); | |
2537 | |
2538 value = g_hash_table_lookup(node->settings, key); | |
2539 | |
2540 if (value == NULL) | |
2541 return FALSE; | |
2542 | |
2543 g_return_val_if_fail(gaim_value_get_type(value) == GAIM_TYPE_BOOLEAN, FALSE); | |
2544 | |
2545 return gaim_value_get_boolean(value); | |
2546 } | |
2547 | |
2548 void | |
2549 gaim_blist_node_set_int(GaimBlistNode* node, const char *key, int data) | |
2550 { | |
2551 GaimValue *value; | |
2552 | |
2553 g_return_if_fail(node != NULL); | |
2554 g_return_if_fail(node->settings != NULL); | |
2555 g_return_if_fail(key != NULL); | |
2556 | |
2557 value = gaim_value_new(GAIM_TYPE_INT); | |
2558 gaim_value_set_int(value, data); | |
2559 | |
2560 g_hash_table_replace(node->settings, g_strdup(key), value); | |
2561 | |
2562 gaim_blist_schedule_save(); | |
2563 } | |
2564 | |
2565 int | |
2566 gaim_blist_node_get_int(GaimBlistNode* node, const char *key) | |
2567 { | |
2568 GaimValue *value; | |
2569 | |
2570 g_return_val_if_fail(node != NULL, 0); | |
2571 g_return_val_if_fail(node->settings != NULL, 0); | |
2572 g_return_val_if_fail(key != NULL, 0); | |
2573 | |
2574 value = g_hash_table_lookup(node->settings, key); | |
2575 | |
2576 if (value == NULL) | |
2577 return 0; | |
2578 | |
2579 g_return_val_if_fail(gaim_value_get_type(value) == GAIM_TYPE_INT, 0); | |
2580 | |
2581 return gaim_value_get_int(value); | |
2582 } | |
2583 | |
2584 void | |
2585 gaim_blist_node_set_string(GaimBlistNode* node, const char *key, const char *data) | |
2586 { | |
2587 GaimValue *value; | |
2588 | |
2589 g_return_if_fail(node != NULL); | |
2590 g_return_if_fail(node->settings != NULL); | |
2591 g_return_if_fail(key != NULL); | |
2592 | |
2593 value = gaim_value_new(GAIM_TYPE_STRING); | |
2594 gaim_value_set_string(value, data); | |
2595 | |
2596 g_hash_table_replace(node->settings, g_strdup(key), value); | |
2597 | |
2598 gaim_blist_schedule_save(); | |
2599 } | |
2600 | |
2601 const char * | |
2602 gaim_blist_node_get_string(GaimBlistNode* node, const char *key) | |
2603 { | |
2604 GaimValue *value; | |
2605 | |
2606 g_return_val_if_fail(node != NULL, NULL); | |
2607 g_return_val_if_fail(node->settings != NULL, NULL); | |
2608 g_return_val_if_fail(key != NULL, NULL); | |
2609 | |
2610 value = g_hash_table_lookup(node->settings, key); | |
2611 | |
2612 if (value == NULL) | |
2613 return NULL; | |
2614 | |
2615 g_return_val_if_fail(gaim_value_get_type(value) == GAIM_TYPE_STRING, NULL); | |
2616 | |
2617 return gaim_value_get_string(value); | |
2618 } | |
2619 | |
2620 GList * | |
2621 gaim_blist_node_get_extended_menu(GaimBlistNode *n) | |
2622 { | |
2623 GList *menu = NULL; | |
2624 | |
2625 g_return_val_if_fail(n != NULL, NULL); | |
2626 | |
2627 gaim_signal_emit(gaim_blist_get_handle(), | |
2628 "blist-node-extended-menu", | |
2629 n, &menu); | |
2630 return menu; | |
2631 } | |
2632 | |
2633 int gaim_blist_get_group_size(GaimGroup *group, gboolean offline) | |
2634 { | |
2635 if (!group) | |
2636 return 0; | |
2637 | |
2638 return offline ? group->totalsize : group->currentsize; | |
2639 } | |
2640 | |
2641 int gaim_blist_get_group_online_count(GaimGroup *group) | |
2642 { | |
2643 if (!group) | |
2644 return 0; | |
2645 | |
2646 return group->online; | |
2647 } | |
2648 | |
2649 void | |
2650 gaim_blist_set_ui_ops(GaimBlistUiOps *ops) | |
2651 { | |
2652 blist_ui_ops = ops; | |
2653 } | |
2654 | |
2655 GaimBlistUiOps * | |
2656 gaim_blist_get_ui_ops(void) | |
2657 { | |
2658 return blist_ui_ops; | |
2659 } | |
2660 | |
2661 | |
2662 void * | |
2663 gaim_blist_get_handle(void) | |
2664 { | |
2665 static int handle; | |
2666 | |
2667 return &handle; | |
2668 } | |
2669 | |
2670 void | |
2671 gaim_blist_init(void) | |
2672 { | |
2673 void *handle = gaim_blist_get_handle(); | |
2674 | |
2675 gaim_signal_register(handle, "buddy-status-changed", | |
2676 gaim_marshal_VOID__POINTER_POINTER_POINTER, NULL, | |
2677 3, | |
2678 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
2679 GAIM_SUBTYPE_BLIST_BUDDY), | |
2680 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
2681 GAIM_SUBTYPE_STATUS), | |
2682 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
2683 GAIM_SUBTYPE_STATUS)); | |
2684 gaim_signal_register(handle, "buddy-privacy-changed", | |
2685 gaim_marshal_VOID__POINTER, NULL, | |
2686 1, | |
2687 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
2688 GAIM_SUBTYPE_BLIST_BUDDY)); | |
2689 | |
2690 gaim_signal_register(handle, "buddy-idle-changed", | |
2691 gaim_marshal_VOID__POINTER_INT_INT, NULL, | |
2692 3, | |
2693 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
2694 GAIM_SUBTYPE_BLIST_BUDDY), | |
2695 gaim_value_new(GAIM_TYPE_INT), | |
2696 gaim_value_new(GAIM_TYPE_INT)); | |
2697 | |
2698 | |
2699 gaim_signal_register(handle, "buddy-signed-on", | |
2700 gaim_marshal_VOID__POINTER, NULL, 1, | |
2701 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
2702 GAIM_SUBTYPE_BLIST_BUDDY)); | |
2703 | |
2704 gaim_signal_register(handle, "buddy-signed-off", | |
2705 gaim_marshal_VOID__POINTER, NULL, 1, | |
2706 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
2707 GAIM_SUBTYPE_BLIST_BUDDY)); | |
2708 | |
2709 gaim_signal_register(handle, "buddy-added", | |
2710 gaim_marshal_VOID__POINTER, NULL, 1, | |
2711 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
2712 GAIM_SUBTYPE_BLIST_BUDDY)); | |
2713 | |
2714 gaim_signal_register(handle, "buddy-removed", | |
2715 gaim_marshal_VOID__POINTER, NULL, 1, | |
2716 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
2717 GAIM_SUBTYPE_BLIST_BUDDY)); | |
2718 | |
2719 gaim_signal_register(handle, "buddy-icon-changed", | |
2720 gaim_marshal_VOID__POINTER, NULL, 1, | |
2721 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
2722 GAIM_SUBTYPE_BLIST_BUDDY)); | |
2723 | |
2724 gaim_signal_register(handle, "update-idle", gaim_marshal_VOID, NULL, 0); | |
2725 | |
2726 gaim_signal_register(handle, "blist-node-extended-menu", | |
2727 gaim_marshal_VOID__POINTER_POINTER, NULL, 2, | |
2728 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
2729 GAIM_SUBTYPE_BLIST_NODE), | |
2730 gaim_value_new(GAIM_TYPE_BOXED, "GList **")); | |
2731 | |
2732 gaim_signal_register(handle, "blist-node-aliased", | |
2733 gaim_marshal_VOID__POINTER_POINTER, NULL, 2, | |
2734 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
2735 GAIM_SUBTYPE_BLIST_NODE), | |
2736 gaim_value_new(GAIM_TYPE_STRING)); | |
2737 } | |
2738 | |
2739 void | |
2740 gaim_blist_uninit(void) | |
2741 { | |
2742 if (save_timer != 0) | |
2743 { | |
2744 gaim_timeout_remove(save_timer); | |
2745 save_timer = 0; | |
2746 gaim_blist_sync(); | |
2747 } | |
2748 | |
2749 gaim_signals_unregister_by_instance(gaim_blist_get_handle()); | |
2750 } |