Coverage Report

Created: 2026-02-14 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pidgin/libpurple/blist.c
Line
Count
Source
1
/*
2
 * purple
3
 *
4
 * Purple is the legal property of its developers, whose names are too numerous
5
 * to list here.  Please refer to the COPYRIGHT file distributed with this
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., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
21
 *
22
 */
23
#define _PURPLE_BLIST_C_
24
25
#include "internal.h"
26
#include "blist.h"
27
#include "conversation.h"
28
#include "dbus-maybe.h"
29
#include "debug.h"
30
#include "glibcompat.h"
31
#include "notify.h"
32
#include "pounce.h"
33
#include "prefs.h"
34
#include "privacy.h"
35
#include "prpl.h"
36
#include "server.h"
37
#include "signals.h"
38
#include "util.h"
39
#include "value.h"
40
#include "xmlnode.h"
41
42
static PurpleBlistUiOps *blist_ui_ops = NULL;
43
44
static PurpleBuddyList *purplebuddylist = NULL;
45
46
/**
47
 * A hash table used for efficient lookups of buddies by name.
48
 * PurpleAccount* => GHashTable*, with the inner hash table being
49
 * struct _purple_hbuddy => PurpleBuddy*
50
 */
51
static GHashTable *buddies_cache = NULL;
52
53
/**
54
 * A hash table used for efficient lookups of groups by name.
55
 * UTF-8 collate-key => PurpleGroup*.
56
 */
57
static GHashTable *groups_cache = NULL;
58
59
static guint          save_timer = 0;
60
static gboolean       blist_loaded = FALSE;
61
62
/*********************************************************************
63
 * Private utility functions                                         *
64
 *********************************************************************/
65
66
static PurpleBlistNode *purple_blist_get_last_sibling(PurpleBlistNode *node)
67
0
{
68
0
  PurpleBlistNode *n = node;
69
0
  if (!n)
70
0
    return NULL;
71
0
  while (n->next)
72
0
    n = n->next;
73
0
  return n;
74
0
}
75
76
static PurpleBlistNode *purple_blist_get_last_child(PurpleBlistNode *node)
77
0
{
78
0
  if (!node)
79
0
    return NULL;
80
0
  return purple_blist_get_last_sibling(node->child);
81
0
}
82
83
struct _list_account_buddies {
84
  GSList *list;
85
  PurpleAccount *account;
86
};
87
88
struct _purple_hbuddy {
89
  char *name;
90
  PurpleAccount *account;
91
  PurpleBlistNode *group;
92
};
93
94
/* This function must not use purple_normalize */
95
static guint _purple_blist_hbuddy_hash(struct _purple_hbuddy *hb)
96
0
{
97
0
  return g_str_hash(hb->name) ^ g_direct_hash(hb->group) ^ g_direct_hash(hb->account);
98
0
}
99
100
/* This function must not use purple_normalize */
101
static guint _purple_blist_hbuddy_equal(struct _purple_hbuddy *hb1, struct _purple_hbuddy *hb2)
102
0
{
103
0
  return (hb1->group == hb2->group &&
104
0
          hb1->account == hb2->account &&
105
0
          purple_strequal(hb1->name, hb2->name));
106
0
}
107
108
static void _purple_blist_hbuddy_free_key(struct _purple_hbuddy *hb)
109
0
{
110
0
  g_free(hb->name);
111
0
  g_free(hb);
112
0
}
113
114
static void
115
purple_blist_buddies_cache_add_account(PurpleAccount *account)
116
0
{
117
0
  GHashTable *account_buddies = g_hash_table_new_full((GHashFunc)_purple_blist_hbuddy_hash,
118
0
            (GEqualFunc)_purple_blist_hbuddy_equal,
119
0
            (GDestroyNotify)_purple_blist_hbuddy_free_key, NULL);
120
0
  g_hash_table_insert(buddies_cache, account, account_buddies);
121
0
}
122
123
static void
124
purple_blist_buddies_cache_remove_account(const PurpleAccount *account)
125
0
{
126
0
  g_hash_table_remove(buddies_cache, account);
127
0
}
128
129
130
/*********************************************************************
131
 * Writing to disk                                                   *
132
 *********************************************************************/
133
134
static void
135
value_to_xmlnode(gpointer key, gpointer hvalue, gpointer user_data)
136
0
{
137
0
  const char *name;
138
0
  PurpleValue *value;
139
0
  xmlnode *node, *child;
140
0
  char buf[21];
141
142
0
  name    = (const char *)key;
143
0
  value   = (PurpleValue *)hvalue;
144
0
  node    = (xmlnode *)user_data;
145
146
0
  g_return_if_fail(value != NULL);
147
148
0
  child = xmlnode_new_child(node, "setting");
149
0
  xmlnode_set_attrib(child, "name", name);
150
151
0
  if (purple_value_get_type(value) == PURPLE_TYPE_INT) {
152
0
    xmlnode_set_attrib(child, "type", "int");
153
0
    g_snprintf(buf, sizeof(buf), "%d", purple_value_get_int(value));
154
0
    xmlnode_insert_data(child, buf, -1);
155
0
  }
156
0
  else if (purple_value_get_type(value) == PURPLE_TYPE_STRING) {
157
0
    xmlnode_set_attrib(child, "type", "string");
158
0
    xmlnode_insert_data(child, purple_value_get_string(value), -1);
159
0
  }
160
0
  else if (purple_value_get_type(value) == PURPLE_TYPE_BOOLEAN) {
161
0
    xmlnode_set_attrib(child, "type", "bool");
162
0
    g_snprintf(buf, sizeof(buf), "%d", purple_value_get_boolean(value));
163
0
    xmlnode_insert_data(child, buf, -1);
164
0
  }
165
0
}
166
167
static void
168
chat_component_to_xmlnode(gpointer key, gpointer value, gpointer user_data)
169
0
{
170
0
  const char *name;
171
0
  const char *data;
172
0
  xmlnode *node, *child;
173
174
0
  name = (const char *)key;
175
0
  data = (const char *)value;
176
0
  node = (xmlnode *)user_data;
177
178
0
  g_return_if_fail(data != NULL);
179
180
0
  child = xmlnode_new_child(node, "component");
181
0
  xmlnode_set_attrib(child, "name", name);
182
0
  xmlnode_insert_data(child, data, -1);
183
0
}
184
185
static xmlnode *
186
buddy_to_xmlnode(PurpleBlistNode *bnode)
187
0
{
188
0
  xmlnode *node, *child;
189
0
  PurpleBuddy *buddy;
190
191
0
  buddy = (PurpleBuddy *)bnode;
192
193
0
  node = xmlnode_new("buddy");
194
0
  xmlnode_set_attrib(node, "account", purple_account_get_username(buddy->account));
195
0
  xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(buddy->account));
196
197
0
  child = xmlnode_new_child(node, "name");
198
0
  xmlnode_insert_data(child, buddy->name, -1);
199
200
0
  if (buddy->alias != NULL)
201
0
  {
202
0
    child = xmlnode_new_child(node, "alias");
203
0
    xmlnode_insert_data(child, buddy->alias, -1);
204
0
  }
205
206
  /* Write buddy settings */
207
0
  g_hash_table_foreach(buddy->node.settings, value_to_xmlnode, node);
208
209
0
  return node;
210
0
}
211
212
static xmlnode *
213
contact_to_xmlnode(PurpleBlistNode *cnode)
214
0
{
215
0
  xmlnode *node, *child;
216
0
  PurpleContact *contact;
217
0
  PurpleBlistNode *bnode;
218
219
0
  contact = (PurpleContact *)cnode;
220
221
0
  node = xmlnode_new("contact");
222
223
0
  if (contact->alias != NULL)
224
0
  {
225
0
    xmlnode_set_attrib(node, "alias", contact->alias);
226
0
  }
227
228
  /* Write buddies */
229
0
  for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
230
0
  {
231
0
    if (!PURPLE_BLIST_NODE_SHOULD_SAVE(bnode))
232
0
      continue;
233
0
    if (PURPLE_BLIST_NODE_IS_BUDDY(bnode))
234
0
    {
235
0
      child = buddy_to_xmlnode(bnode);
236
0
      xmlnode_insert_child(node, child);
237
0
    }
238
0
  }
239
240
  /* Write contact settings */
241
0
  g_hash_table_foreach(cnode->settings, value_to_xmlnode, node);
242
243
0
  return node;
244
0
}
245
246
static xmlnode *
247
chat_to_xmlnode(PurpleBlistNode *cnode)
248
0
{
249
0
  xmlnode *node, *child;
250
0
  PurpleChat *chat;
251
252
0
  chat = (PurpleChat *)cnode;
253
254
0
  node = xmlnode_new("chat");
255
0
  xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(chat->account));
256
0
  xmlnode_set_attrib(node, "account", purple_account_get_username(chat->account));
257
258
0
  if (chat->alias != NULL)
259
0
  {
260
0
    child = xmlnode_new_child(node, "alias");
261
0
    xmlnode_insert_data(child, chat->alias, -1);
262
0
  }
263
264
  /* Write chat components */
265
0
  g_hash_table_foreach(chat->components, chat_component_to_xmlnode, node);
266
267
  /* Write chat settings */
268
0
  g_hash_table_foreach(chat->node.settings, value_to_xmlnode, node);
269
270
0
  return node;
271
0
}
272
273
static xmlnode *
274
group_to_xmlnode(PurpleBlistNode *gnode)
275
0
{
276
0
  xmlnode *node, *child;
277
0
  PurpleGroup *group;
278
0
  PurpleBlistNode *cnode;
279
280
0
  group = (PurpleGroup *)gnode;
281
282
0
  node = xmlnode_new("group");
283
0
  if (!purple_strequal(group->name, _("Buddies")))
284
0
    xmlnode_set_attrib(node, "name", group->name);
285
286
  /* Write settings */
287
0
  g_hash_table_foreach(group->node.settings, value_to_xmlnode, node);
288
289
  /* Write contacts and chats */
290
0
  for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
291
0
  {
292
0
    if (!PURPLE_BLIST_NODE_SHOULD_SAVE(cnode))
293
0
      continue;
294
0
    if (PURPLE_BLIST_NODE_IS_CONTACT(cnode))
295
0
    {
296
0
      child = contact_to_xmlnode(cnode);
297
0
      xmlnode_insert_child(node, child);
298
0
    }
299
0
    else if (PURPLE_BLIST_NODE_IS_CHAT(cnode))
300
0
    {
301
0
      child = chat_to_xmlnode(cnode);
302
0
      xmlnode_insert_child(node, child);
303
0
    }
304
0
  }
305
306
0
  return node;
307
0
}
308
309
static xmlnode *
310
accountprivacy_to_xmlnode(PurpleAccount *account)
311
0
{
312
0
  xmlnode *node, *child;
313
0
  GSList *cur;
314
0
  char buf[10];
315
316
0
  node = xmlnode_new("account");
317
0
  xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(account));
318
0
  xmlnode_set_attrib(node, "name", purple_account_get_username(account));
319
0
  g_snprintf(buf, sizeof(buf), "%d", account->perm_deny);
320
0
  xmlnode_set_attrib(node, "mode", buf);
321
322
0
  for (cur = account->permit; cur; cur = cur->next)
323
0
  {
324
0
    child = xmlnode_new_child(node, "permit");
325
0
    xmlnode_insert_data(child, cur->data, -1);
326
0
  }
327
328
0
  for (cur = account->deny; cur; cur = cur->next)
329
0
  {
330
0
    child = xmlnode_new_child(node, "block");
331
0
    xmlnode_insert_data(child, cur->data, -1);
332
0
  }
333
334
0
  return node;
335
0
}
336
337
static xmlnode *
338
blist_to_xmlnode(void)
339
0
{
340
0
  xmlnode *node, *child, *grandchild;
341
0
  PurpleBlistNode *gnode;
342
0
  GList *cur;
343
344
0
  node = xmlnode_new("purple");
345
0
  xmlnode_set_attrib(node, "version", "1.0");
346
347
  /* Write groups */
348
0
  child = xmlnode_new_child(node, "blist");
349
0
  for (gnode = purplebuddylist->root; gnode != NULL; gnode = gnode->next)
350
0
  {
351
0
    if (!PURPLE_BLIST_NODE_SHOULD_SAVE(gnode))
352
0
      continue;
353
0
    if (PURPLE_BLIST_NODE_IS_GROUP(gnode))
354
0
    {
355
0
      grandchild = group_to_xmlnode(gnode);
356
0
      xmlnode_insert_child(child, grandchild);
357
0
    }
358
0
  }
359
360
  /* Write privacy settings */
361
0
  child = xmlnode_new_child(node, "privacy");
362
0
  for (cur = purple_accounts_get_all(); cur != NULL; cur = cur->next)
363
0
  {
364
0
    grandchild = accountprivacy_to_xmlnode(cur->data);
365
0
    xmlnode_insert_child(child, grandchild);
366
0
  }
367
368
0
  return node;
369
0
}
370
371
static void
372
purple_blist_sync(void)
373
0
{
374
0
  xmlnode *node;
375
0
  char *data;
376
377
0
  if (!blist_loaded)
378
0
  {
379
0
    purple_debug_error("blist", "Attempted to save buddy list before it "
380
0
             "was read!\n");
381
0
    return;
382
0
  }
383
384
0
  node = blist_to_xmlnode();
385
0
  data = xmlnode_to_formatted_str(node, NULL);
386
0
  purple_util_write_data_to_file("blist.xml", data, -1);
387
0
  g_free(data);
388
0
  xmlnode_free(node);
389
0
}
390
391
static gboolean
392
save_cb(gpointer data)
393
0
{
394
0
  purple_blist_sync();
395
0
  save_timer = 0;
396
0
  return FALSE;
397
0
}
398
399
static void
400
_purple_blist_schedule_save()
401
0
{
402
0
  if (save_timer == 0)
403
0
    save_timer = purple_timeout_add_seconds(5, save_cb, NULL);
404
0
}
405
406
static void
407
purple_blist_save_account(PurpleAccount *account)
408
0
{
409
0
#if 1
410
0
  _purple_blist_schedule_save();
411
#else
412
  if (account != NULL) {
413
    /* Save the buddies and privacy data for this account */
414
  } else {
415
    /* Save all buddies and privacy data */
416
  }
417
#endif
418
0
}
419
420
static void
421
purple_blist_save_node(PurpleBlistNode *node)
422
0
{
423
0
  _purple_blist_schedule_save();
424
0
}
425
426
void purple_blist_schedule_save()
427
0
{
428
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
429
430
  /* Save everything */
431
0
  if (ops && ops->save_account)
432
0
    ops->save_account(NULL);
433
0
}
434
435
436
/*********************************************************************
437
 * Reading from disk                                                 *
438
 *********************************************************************/
439
440
static void
441
parse_setting(PurpleBlistNode *node, xmlnode *setting)
442
0
{
443
0
  const char *name = xmlnode_get_attrib(setting, "name");
444
0
  const char *type = xmlnode_get_attrib(setting, "type");
445
0
  char *value = xmlnode_get_data(setting);
446
447
0
  if (!value)
448
0
    return;
449
450
0
  if (!type || purple_strequal(type, "string"))
451
0
    purple_blist_node_set_string(node, name, value);
452
0
  else if (purple_strequal(type, "bool"))
453
0
    purple_blist_node_set_bool(node, name, atoi(value));
454
0
  else if (purple_strequal(type, "int"))
455
0
    purple_blist_node_set_int(node, name, atoi(value));
456
457
0
  g_free(value);
458
0
}
459
460
static void
461
parse_buddy(PurpleGroup *group, PurpleContact *contact, xmlnode *bnode)
462
0
{
463
0
  PurpleAccount *account;
464
0
  PurpleBuddy *buddy;
465
0
  char *name = NULL, *alias = NULL;
466
0
  const char *acct_name, *proto, *protocol;
467
0
  xmlnode *x;
468
469
0
  acct_name = xmlnode_get_attrib(bnode, "account");
470
0
  protocol = xmlnode_get_attrib(bnode, "protocol");
471
0
  protocol = _purple_oscar_convert(acct_name, protocol); /* XXX: Remove */
472
0
  proto = xmlnode_get_attrib(bnode, "proto");
473
0
  proto = _purple_oscar_convert(acct_name, proto); /* XXX: Remove */
474
475
0
  if (!acct_name || (!proto && !protocol))
476
0
    return;
477
478
0
  account = purple_accounts_find(acct_name, proto ? proto : protocol);
479
480
0
  if (!account)
481
0
    return;
482
483
0
  if ((x = xmlnode_get_child(bnode, "name")))
484
0
    name = xmlnode_get_data(x);
485
486
0
  if (!name)
487
0
    return;
488
489
0
  if ((x = xmlnode_get_child(bnode, "alias")))
490
0
    alias = xmlnode_get_data(x);
491
492
0
  buddy = purple_buddy_new(account, name, alias);
493
0
  purple_blist_add_buddy(buddy, contact, group,
494
0
      purple_blist_get_last_child((PurpleBlistNode*)contact));
495
496
0
  for (x = xmlnode_get_child(bnode, "setting"); x; x = xmlnode_get_next_twin(x)) {
497
0
    parse_setting((PurpleBlistNode*)buddy, x);
498
0
  }
499
500
0
  g_free(name);
501
0
  g_free(alias);
502
0
}
503
504
static void
505
parse_contact(PurpleGroup *group, xmlnode *cnode)
506
0
{
507
0
  PurpleContact *contact = purple_contact_new();
508
0
  xmlnode *x;
509
0
  const char *alias;
510
511
0
  purple_blist_add_contact(contact, group,
512
0
      purple_blist_get_last_child((PurpleBlistNode*)group));
513
514
0
  if ((alias = xmlnode_get_attrib(cnode, "alias"))) {
515
0
    purple_blist_alias_contact(contact, alias);
516
0
  }
517
518
0
  for (x = cnode->child; x; x = x->next) {
519
0
    if (x->type != XMLNODE_TYPE_TAG)
520
0
      continue;
521
0
    if (purple_strequal(x->name, "buddy"))
522
0
      parse_buddy(group, contact, x);
523
0
    else if (purple_strequal(x->name, "setting"))
524
0
      parse_setting((PurpleBlistNode*)contact, x);
525
0
  }
526
527
  /* if the contact is empty, don't keep it around.  it causes problems */
528
0
  if (!((PurpleBlistNode*)contact)->child)
529
0
    purple_blist_remove_contact(contact);
530
0
}
531
532
static void
533
parse_chat(PurpleGroup *group, xmlnode *cnode)
534
0
{
535
0
  PurpleChat *chat;
536
0
  PurpleAccount *account;
537
0
  const char *acct_name, *proto, *protocol;
538
0
  xmlnode *x;
539
0
  char *alias = NULL;
540
0
  GHashTable *components;
541
542
0
  acct_name = xmlnode_get_attrib(cnode, "account");
543
0
  protocol = xmlnode_get_attrib(cnode, "protocol");
544
0
  proto = xmlnode_get_attrib(cnode, "proto");
545
546
0
  if (!acct_name || (!proto && !protocol))
547
0
    return;
548
549
0
  account = purple_accounts_find(acct_name, proto ? proto : protocol);
550
551
0
  if (!account)
552
0
    return;
553
554
0
  if ((x = xmlnode_get_child(cnode, "alias")))
555
0
    alias = xmlnode_get_data(x);
556
557
0
  components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
558
559
0
  for (x = xmlnode_get_child(cnode, "component"); x; x = xmlnode_get_next_twin(x)) {
560
0
    const char *name;
561
0
    char *value;
562
563
0
    name = xmlnode_get_attrib(x, "name");
564
0
    value = xmlnode_get_data(x);
565
0
    g_hash_table_replace(components, g_strdup(name), value);
566
0
  }
567
568
0
  chat = purple_chat_new(account, alias, components);
569
0
  purple_blist_add_chat(chat, group,
570
0
      purple_blist_get_last_child((PurpleBlistNode*)group));
571
572
0
  for (x = xmlnode_get_child(cnode, "setting"); x; x = xmlnode_get_next_twin(x)) {
573
0
    parse_setting((PurpleBlistNode*)chat, x);
574
0
  }
575
576
0
  g_free(alias);
577
0
}
578
579
static void
580
parse_group(xmlnode *groupnode)
581
0
{
582
0
  const char *name = xmlnode_get_attrib(groupnode, "name");
583
0
  PurpleGroup *group;
584
0
  xmlnode *cnode;
585
586
0
  if (!name)
587
0
    name = _("Buddies");
588
589
0
  group = purple_group_new(name);
590
0
  purple_blist_add_group(group,
591
0
      purple_blist_get_last_sibling(purplebuddylist->root));
592
593
0
  for (cnode = groupnode->child; cnode; cnode = cnode->next) {
594
0
    if (cnode->type != XMLNODE_TYPE_TAG)
595
0
      continue;
596
0
    if (purple_strequal(cnode->name, "setting"))
597
0
      parse_setting((PurpleBlistNode*)group, cnode);
598
0
    else if (purple_strequal(cnode->name, "contact") ||
599
0
        purple_strequal(cnode->name, "person"))
600
0
      parse_contact(group, cnode);
601
0
    else if (purple_strequal(cnode->name, "chat"))
602
0
      parse_chat(group, cnode);
603
0
  }
604
0
}
605
606
/* TODO: Make static and rename to load_blist */
607
void
608
purple_blist_load()
609
0
{
610
0
  xmlnode *purple, *blist, *privacy;
611
612
0
  blist_loaded = TRUE;
613
614
0
  purple = purple_util_read_xml_from_file("blist.xml", _("buddy list"));
615
616
0
  if (purple == NULL)
617
0
    return;
618
619
0
  blist = xmlnode_get_child(purple, "blist");
620
0
  if (blist) {
621
0
    xmlnode *groupnode;
622
0
    for (groupnode = xmlnode_get_child(blist, "group"); groupnode != NULL;
623
0
        groupnode = xmlnode_get_next_twin(groupnode)) {
624
0
      parse_group(groupnode);
625
0
    }
626
0
  }
627
628
0
  privacy = xmlnode_get_child(purple, "privacy");
629
0
  if (privacy) {
630
0
    xmlnode *anode;
631
0
    for (anode = privacy->child; anode; anode = anode->next) {
632
0
      xmlnode *x;
633
0
      PurpleAccount *account;
634
0
      int imode;
635
0
      const char *acct_name, *proto, *mode, *protocol;
636
637
0
      acct_name = xmlnode_get_attrib(anode, "name");
638
0
      protocol = xmlnode_get_attrib(anode, "protocol");
639
0
      proto = xmlnode_get_attrib(anode, "proto");
640
0
      mode = xmlnode_get_attrib(anode, "mode");
641
642
0
      if (!acct_name || (!proto && !protocol) || !mode)
643
0
        continue;
644
645
0
      account = purple_accounts_find(acct_name, proto ? proto : protocol);
646
647
0
      if (!account)
648
0
        continue;
649
650
0
      imode = atoi(mode);
651
0
      account->perm_deny = (imode != 0 ? imode : PURPLE_PRIVACY_ALLOW_ALL);
652
653
0
      for (x = anode->child; x; x = x->next) {
654
0
        char *name;
655
0
        if (x->type != XMLNODE_TYPE_TAG)
656
0
          continue;
657
658
0
        if (purple_strequal(x->name, "permit")) {
659
0
          name = xmlnode_get_data(x);
660
0
          purple_privacy_permit_add(account, name, TRUE);
661
0
          g_free(name);
662
0
        } else if (purple_strequal(x->name, "block")) {
663
0
          name = xmlnode_get_data(x);
664
0
          purple_privacy_deny_add(account, name, TRUE);
665
0
          g_free(name);
666
0
        }
667
0
      }
668
0
    }
669
0
  }
670
671
0
  xmlnode_free(purple);
672
673
  /* This tells the buddy icon code to do its thing. */
674
0
  _purple_buddy_icons_blist_loaded_cb();
675
0
}
676
677
678
/*********************************************************************
679
 * Stuff                                                             *
680
 *********************************************************************/
681
682
static void
683
purple_contact_compute_priority_buddy(PurpleContact *contact)
684
0
{
685
0
  PurpleBlistNode *bnode;
686
0
  PurpleBuddy *new_priority = NULL;
687
688
0
  g_return_if_fail(contact != NULL);
689
690
0
  contact->priority = NULL;
691
0
  for (bnode = ((PurpleBlistNode*)contact)->child;
692
0
      bnode != NULL;
693
0
      bnode = bnode->next)
694
0
  {
695
0
    PurpleBuddy *buddy;
696
697
0
    if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
698
0
      continue;
699
700
0
    buddy = (PurpleBuddy*)bnode;
701
0
    if (new_priority == NULL)
702
0
    {
703
0
      new_priority = buddy;
704
0
      continue;
705
0
    }
706
707
0
    if (purple_account_is_connected(buddy->account))
708
0
    {
709
0
      int cmp = 1;
710
0
      if (purple_account_is_connected(new_priority->account))
711
0
        cmp = purple_presence_compare(purple_buddy_get_presence(new_priority),
712
0
            purple_buddy_get_presence(buddy));
713
714
0
      if (cmp > 0 || (cmp == 0 &&
715
0
                      purple_prefs_get_bool("/purple/contact/last_match")))
716
0
      {
717
0
        new_priority = buddy;
718
0
      }
719
0
    }
720
0
  }
721
722
0
  contact->priority = new_priority;
723
0
  contact->priority_valid = TRUE;
724
0
}
725
726
727
/*****************************************************************************
728
 * Public API functions                                                      *
729
 *****************************************************************************/
730
731
PurpleBuddyList *purple_blist_new()
732
0
{
733
0
  PurpleBlistUiOps *ui_ops;
734
0
  GList *account;
735
0
  PurpleBuddyList *gbl = g_new0(PurpleBuddyList, 1);
736
0
  PURPLE_DBUS_REGISTER_POINTER(gbl, PurpleBuddyList);
737
738
0
  ui_ops = purple_blist_get_ui_ops();
739
740
0
  gbl->buddies = g_hash_table_new_full((GHashFunc)_purple_blist_hbuddy_hash,
741
0
           (GEqualFunc)_purple_blist_hbuddy_equal,
742
0
           (GDestroyNotify)_purple_blist_hbuddy_free_key, NULL);
743
744
0
  buddies_cache = g_hash_table_new_full(g_direct_hash, g_direct_equal,
745
0
           NULL, (GDestroyNotify)g_hash_table_destroy);
746
747
0
  groups_cache = g_hash_table_new_full((GHashFunc)g_str_hash,
748
0
           (GEqualFunc)g_str_equal,
749
0
           (GDestroyNotify)g_free, NULL);
750
751
0
  for (account = purple_accounts_get_all(); account != NULL; account = account->next)
752
0
  {
753
0
    purple_blist_buddies_cache_add_account(account->data);
754
0
  }
755
756
0
  if (ui_ops != NULL && ui_ops->new_list != NULL)
757
0
    ui_ops->new_list(gbl);
758
759
0
  return gbl;
760
0
}
761
762
void
763
purple_set_blist(PurpleBuddyList *list)
764
0
{
765
0
  purplebuddylist = list;
766
0
}
767
768
PurpleBuddyList *
769
purple_get_blist()
770
0
{
771
0
  return purplebuddylist;
772
0
}
773
774
PurpleBlistNode *
775
purple_blist_get_root()
776
0
{
777
0
  return purplebuddylist ? purplebuddylist->root : NULL;
778
0
}
779
780
static void
781
append_buddy(gpointer key, gpointer value, gpointer user_data)
782
0
{
783
0
  GSList **list = user_data;
784
0
  *list = g_slist_prepend(*list, value);
785
0
}
786
787
GSList *
788
purple_blist_get_buddies()
789
0
{
790
0
  GSList *buddies = NULL;
791
792
0
  if (!purplebuddylist)
793
0
    return NULL;
794
795
0
  g_hash_table_foreach(purplebuddylist->buddies, append_buddy, &buddies);
796
0
  return buddies;
797
0
}
798
799
void *
800
purple_blist_get_ui_data()
801
0
{
802
0
  return purplebuddylist->ui_data;
803
0
}
804
805
void
806
purple_blist_set_ui_data(void *ui_data)
807
0
{
808
0
  purplebuddylist->ui_data = ui_data;
809
0
}
810
811
void purple_blist_show()
812
0
{
813
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
814
815
0
  if (ops && ops->show)
816
0
    ops->show(purplebuddylist);
817
0
}
818
819
void purple_blist_destroy()
820
0
{
821
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
822
823
0
  purple_debug(PURPLE_DEBUG_INFO, "blist", "Destroying\n");
824
825
0
  if (ops && ops->destroy)
826
0
    ops->destroy(purplebuddylist);
827
0
}
828
829
void purple_blist_set_visible(gboolean show)
830
0
{
831
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
832
833
0
  if (ops && ops->set_visible)
834
0
    ops->set_visible(purplebuddylist, show);
835
0
}
836
837
static PurpleBlistNode *get_next_node(PurpleBlistNode *node, gboolean godeep)
838
0
{
839
0
  if (node == NULL)
840
0
    return NULL;
841
842
0
  if (godeep && node->child)
843
0
    return node->child;
844
845
0
  if (node->next)
846
0
    return node->next;
847
848
0
  return get_next_node(node->parent, FALSE);
849
0
}
850
851
PurpleBlistNode *purple_blist_node_next(PurpleBlistNode *node, gboolean offline)
852
0
{
853
0
  PurpleBlistNode *ret = node;
854
855
0
  if (offline)
856
0
    return get_next_node(ret, TRUE);
857
0
  do
858
0
  {
859
0
    ret = get_next_node(ret, TRUE);
860
0
  } while (ret && PURPLE_BLIST_NODE_IS_BUDDY(ret) &&
861
0
      !purple_account_is_connected(purple_buddy_get_account((PurpleBuddy *)ret)));
862
863
0
  return ret;
864
0
}
865
866
PurpleBlistNode *purple_blist_node_get_parent(PurpleBlistNode *node)
867
0
{
868
0
  return node ? node->parent : NULL;
869
0
}
870
871
PurpleBlistNode *purple_blist_node_get_first_child(PurpleBlistNode *node)
872
0
{
873
0
  return node ? node->child : NULL;
874
0
}
875
876
PurpleBlistNode *purple_blist_node_get_sibling_next(PurpleBlistNode *node)
877
0
{
878
0
  return node? node->next : NULL;
879
0
}
880
881
PurpleBlistNode *purple_blist_node_get_sibling_prev(PurpleBlistNode *node)
882
0
{
883
0
  return node? node->prev : NULL;
884
0
}
885
886
void *
887
purple_blist_node_get_ui_data(const PurpleBlistNode *node)
888
0
{
889
0
  g_return_val_if_fail(node, NULL);
890
891
0
  return node->ui_data;
892
0
}
893
894
void
895
0
purple_blist_node_set_ui_data(PurpleBlistNode *node, void *ui_data) {
896
0
  g_return_if_fail(node);
897
898
0
  node->ui_data = ui_data;
899
0
}
900
901
void
902
purple_blist_update_buddy_status(PurpleBuddy *buddy, PurpleStatus *old_status)
903
0
{
904
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
905
0
  PurplePresence *presence;
906
0
  PurpleStatus *status;
907
0
  PurpleBlistNode *cnode;
908
909
0
  g_return_if_fail(buddy != NULL);
910
911
0
  presence = purple_buddy_get_presence(buddy);
912
0
  status = purple_presence_get_active_status(presence);
913
914
0
  purple_debug_info("blist", "Updating buddy status for %s (%s)\n",
915
0
      buddy->name, purple_account_get_protocol_name(buddy->account));
916
917
0
  if (purple_status_is_online(status) &&
918
0
    !purple_status_is_online(old_status)) {
919
920
0
    purple_signal_emit(purple_blist_get_handle(), "buddy-signed-on", buddy);
921
922
0
    cnode = buddy->node.parent;
923
0
    if (++(PURPLE_CONTACT(cnode)->online) == 1)
924
0
      PURPLE_GROUP(cnode->parent)->online++;
925
0
  } else if (!purple_status_is_online(status) &&
926
0
        purple_status_is_online(old_status)) {
927
928
0
    purple_blist_node_set_int(&buddy->node, "last_seen", time(NULL));
929
0
    purple_signal_emit(purple_blist_get_handle(), "buddy-signed-off", buddy);
930
931
0
    cnode = buddy->node.parent;
932
0
    if (--(PURPLE_CONTACT(cnode)->online) == 0)
933
0
      PURPLE_GROUP(cnode->parent)->online--;
934
0
  } else {
935
0
    purple_signal_emit(purple_blist_get_handle(),
936
0
                     "buddy-status-changed", buddy, old_status,
937
0
                     status);
938
0
  }
939
940
  /*
941
   * This function used to only call the following two functions if one of
942
   * the above signals had been triggered, but that's not good, because
943
   * if someone's away message changes and they don't go from away to back
944
   * to away then no signal is triggered.
945
   *
946
   * It's a safe assumption that SOMETHING called this function.  PROBABLY
947
   * because something, somewhere changed.  Calling the stuff below
948
   * certainly won't hurt anything.  Unless you're on a K6-2 300.
949
   */
950
0
  purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
951
0
  if (ops && ops->update)
952
0
    ops->update(purplebuddylist, (PurpleBlistNode *)buddy);
953
0
}
954
955
void
956
purple_blist_update_node_icon(PurpleBlistNode *node)
957
0
{
958
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
959
960
0
  g_return_if_fail(node != NULL);
961
962
0
  if (ops && ops->update)
963
0
    ops->update(purplebuddylist, node);
964
0
}
965
966
void
967
purple_blist_update_buddy_icon(PurpleBuddy *buddy)
968
0
{
969
0
  purple_blist_update_node_icon((PurpleBlistNode *)buddy);
970
0
}
971
972
/*
973
 * TODO: Maybe remove the call to this from server.c and call it
974
 * from oscar.c and toc.c instead?
975
 */
976
void purple_blist_rename_buddy(PurpleBuddy *buddy, const char *name)
977
0
{
978
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
979
0
  struct _purple_hbuddy *hb, *hb2;
980
0
  GHashTable *account_buddies;
981
982
0
  g_return_if_fail(buddy != NULL);
983
984
0
  hb = g_new(struct _purple_hbuddy, 1);
985
0
  hb->name = (gchar *)purple_normalize(buddy->account, buddy->name);
986
0
  hb->account = buddy->account;
987
0
  hb->group = ((PurpleBlistNode *)buddy)->parent->parent;
988
0
  g_hash_table_remove(purplebuddylist->buddies, hb);
989
990
0
  account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
991
0
  g_hash_table_remove(account_buddies, hb);
992
993
0
  hb->name = g_strdup(purple_normalize(buddy->account, name));
994
0
  g_hash_table_replace(purplebuddylist->buddies, hb, buddy);
995
996
0
  hb2 = g_new(struct _purple_hbuddy, 1);
997
0
  hb2->name = g_strdup(hb->name);
998
0
  hb2->account = buddy->account;
999
0
  hb2->group = ((PurpleBlistNode *)buddy)->parent->parent;
1000
1001
0
  g_hash_table_replace(account_buddies, hb2, buddy);
1002
1003
0
  g_free(buddy->name);
1004
0
  buddy->name = g_strdup(name);
1005
1006
0
  if (ops && ops->save_node)
1007
0
    ops->save_node((PurpleBlistNode *) buddy);
1008
1009
0
  if (ops && ops->update)
1010
0
    ops->update(purplebuddylist, (PurpleBlistNode *)buddy);
1011
0
}
1012
1013
static gboolean
1014
purple_strings_are_different(const char *one, const char *two)
1015
0
{
1016
0
  return !((one && two && g_utf8_collate(one, two) == 0) ||
1017
0
      ((one == NULL || *one == '\0') && (two == NULL || *two == '\0')));
1018
0
}
1019
1020
void purple_blist_alias_contact(PurpleContact *contact, const char *alias)
1021
0
{
1022
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
1023
0
  PurpleConversation *conv;
1024
0
  PurpleBlistNode *bnode;
1025
0
  char *old_alias;
1026
0
  char *new_alias = NULL;
1027
1028
0
  g_return_if_fail(contact != NULL);
1029
1030
0
  if ((alias != NULL) && (*alias != '\0'))
1031
0
    new_alias = purple_utf8_strip_unprintables(alias);
1032
1033
0
  if (!purple_strings_are_different(contact->alias, new_alias)) {
1034
0
    g_free(new_alias);
1035
0
    return;
1036
0
  }
1037
1038
0
  old_alias = contact->alias;
1039
1040
0
  if ((new_alias != NULL) && (*new_alias != '\0'))
1041
0
    contact->alias = new_alias;
1042
0
  else {
1043
0
    contact->alias = NULL;
1044
0
    g_free(new_alias); /* could be "\0" */
1045
0
  }
1046
1047
0
  if (ops && ops->save_node)
1048
0
    ops->save_node((PurpleBlistNode*) contact);
1049
1050
0
  if (ops && ops->update)
1051
0
    ops->update(purplebuddylist, (PurpleBlistNode *)contact);
1052
1053
0
  for(bnode = ((PurpleBlistNode *)contact)->child; bnode != NULL; bnode = bnode->next)
1054
0
  {
1055
0
    PurpleBuddy *buddy = (PurpleBuddy *)bnode;
1056
1057
0
    conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name,
1058
0
                           buddy->account);
1059
0
    if (conv)
1060
0
      purple_conversation_autoset_title(conv);
1061
0
  }
1062
1063
0
  purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
1064
0
           contact, old_alias);
1065
0
  g_free(old_alias);
1066
0
}
1067
1068
void purple_blist_alias_chat(PurpleChat *chat, const char *alias)
1069
0
{
1070
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
1071
0
  char *old_alias;
1072
0
  char *new_alias = NULL;
1073
1074
0
  g_return_if_fail(chat != NULL);
1075
1076
0
  if ((alias != NULL) && (*alias != '\0'))
1077
0
    new_alias = purple_utf8_strip_unprintables(alias);
1078
1079
0
  if (!purple_strings_are_different(chat->alias, new_alias)) {
1080
0
    g_free(new_alias);
1081
0
    return;
1082
0
  }
1083
1084
0
  old_alias = chat->alias;
1085
1086
0
  if ((new_alias != NULL) && (*new_alias != '\0'))
1087
0
    chat->alias = new_alias;
1088
0
  else {
1089
0
    chat->alias = NULL;
1090
0
    g_free(new_alias); /* could be "\0" */
1091
0
  }
1092
1093
0
  if (ops && ops->save_node)
1094
0
    ops->save_node((PurpleBlistNode*) chat);
1095
1096
0
  if (ops && ops->update)
1097
0
    ops->update(purplebuddylist, (PurpleBlistNode *)chat);
1098
1099
0
  purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
1100
0
           chat, old_alias);
1101
0
  g_free(old_alias);
1102
0
}
1103
1104
void purple_blist_alias_buddy(PurpleBuddy *buddy, const char *alias)
1105
0
{
1106
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
1107
0
  PurpleConversation *conv;
1108
0
  char *old_alias;
1109
0
  char *new_alias = NULL;
1110
1111
0
  g_return_if_fail(buddy != NULL);
1112
1113
0
  if ((alias != NULL) && (*alias != '\0'))
1114
0
    new_alias = purple_utf8_strip_unprintables(alias);
1115
1116
0
  if (!purple_strings_are_different(buddy->alias, new_alias)) {
1117
0
    g_free(new_alias);
1118
0
    return;
1119
0
  }
1120
1121
0
  old_alias = buddy->alias;
1122
1123
0
  if ((new_alias != NULL) && (*new_alias != '\0'))
1124
0
    buddy->alias = new_alias;
1125
0
  else {
1126
0
    buddy->alias = NULL;
1127
0
    g_free(new_alias); /* could be "\0" */
1128
0
  }
1129
1130
0
  if (ops && ops->save_node)
1131
0
    ops->save_node((PurpleBlistNode*) buddy);
1132
1133
0
  if (ops && ops->update)
1134
0
    ops->update(purplebuddylist, (PurpleBlistNode *)buddy);
1135
1136
0
  conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name,
1137
0
                         buddy->account);
1138
0
  if (conv)
1139
0
    purple_conversation_autoset_title(conv);
1140
1141
0
  purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
1142
0
           buddy, old_alias);
1143
0
  g_free(old_alias);
1144
0
}
1145
1146
void purple_blist_server_alias_buddy(PurpleBuddy *buddy, const char *alias)
1147
0
{
1148
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
1149
0
  PurpleConversation *conv;
1150
0
  char *old_alias;
1151
0
  char *new_alias = NULL;
1152
1153
0
  g_return_if_fail(buddy != NULL);
1154
1155
0
  if ((alias != NULL) && (*alias != '\0') && g_utf8_validate(alias, -1, NULL))
1156
0
    new_alias = purple_utf8_strip_unprintables(alias);
1157
1158
0
  if (!purple_strings_are_different(buddy->server_alias, new_alias)) {
1159
0
    g_free(new_alias);
1160
0
    return;
1161
0
  }
1162
1163
0
  old_alias = buddy->server_alias;
1164
1165
0
  if ((new_alias != NULL) && (*new_alias != '\0'))
1166
0
    buddy->server_alias = new_alias;
1167
0
  else {
1168
0
    buddy->server_alias = NULL;
1169
0
    g_free(new_alias); /* could be "\0"; */
1170
0
  }
1171
1172
0
  if (ops && ops->save_node)
1173
0
    ops->save_node((PurpleBlistNode*) buddy);
1174
1175
0
  if (ops && ops->update)
1176
0
    ops->update(purplebuddylist, (PurpleBlistNode *)buddy);
1177
1178
0
  conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name,
1179
0
                         buddy->account);
1180
0
  if (conv)
1181
0
    purple_conversation_autoset_title(conv);
1182
1183
0
  purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
1184
0
           buddy, old_alias);
1185
0
  g_free(old_alias);
1186
0
}
1187
1188
/*
1189
 * TODO: If merging, prompt the user if they want to merge.
1190
 */
1191
void purple_blist_rename_group(PurpleGroup *source, const char *name)
1192
0
{
1193
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
1194
0
  PurpleGroup *dest;
1195
0
  gchar *old_name;
1196
0
  gchar *new_name;
1197
0
  GList *moved_buddies = NULL;
1198
0
  GSList *accts;
1199
1200
0
  g_return_if_fail(source != NULL);
1201
0
  g_return_if_fail(name != NULL);
1202
1203
0
  new_name = purple_utf8_strip_unprintables(name);
1204
1205
0
  if (*new_name == '\0' || purple_strequal(new_name, source->name)) {
1206
0
    g_free(new_name);
1207
0
    return;
1208
0
  }
1209
1210
0
  dest = purple_find_group(new_name);
1211
0
  if (dest != NULL && purple_utf8_strcasecmp(source->name, dest->name) != 0) {
1212
    /* We're merging two groups */
1213
0
    PurpleBlistNode *prev, *child, *next;
1214
1215
0
    prev = purple_blist_get_last_child((PurpleBlistNode*)dest);
1216
0
    child = ((PurpleBlistNode*)source)->child;
1217
1218
    /*
1219
     * TODO: This seems like a dumb way to do this... why not just
1220
     * append all children from the old group to the end of the new
1221
     * one?  PRPLs might be expecting to receive an add_buddy() for
1222
     * each moved buddy...
1223
     */
1224
0
    while (child)
1225
0
    {
1226
0
      next = child->next;
1227
0
      if (PURPLE_BLIST_NODE_IS_CONTACT(child)) {
1228
0
        PurpleBlistNode *bnode;
1229
0
        purple_blist_add_contact((PurpleContact *)child, dest, prev);
1230
0
        for (bnode = child->child; bnode != NULL; bnode = bnode->next) {
1231
0
          purple_blist_add_buddy((PurpleBuddy *)bnode, (PurpleContact *)child,
1232
0
              NULL, bnode->prev);
1233
0
          moved_buddies = g_list_append(moved_buddies, bnode);
1234
0
        }
1235
0
        prev = child;
1236
0
      } else if (PURPLE_BLIST_NODE_IS_CHAT(child)) {
1237
0
        purple_blist_add_chat((PurpleChat *)child, dest, prev);
1238
0
        prev = child;
1239
0
      } else {
1240
0
        purple_debug(PURPLE_DEBUG_ERROR, "blist",
1241
0
            "Unknown child type in group %s\n", source->name);
1242
0
      }
1243
0
      child = next;
1244
0
    }
1245
1246
    /* Make a copy of the old group name and then delete the old group */
1247
0
    old_name = g_strdup(source->name);
1248
0
    purple_blist_remove_group(source);
1249
0
    source = dest;
1250
0
    g_free(new_name);
1251
0
  } else {
1252
    /* A simple rename */
1253
0
    PurpleBlistNode *cnode, *bnode;
1254
0
    gchar* key;
1255
1256
    /* Build a GList of all buddies in this group */
1257
0
    for (cnode = ((PurpleBlistNode *)source)->child; cnode != NULL; cnode = cnode->next) {
1258
0
      if (PURPLE_BLIST_NODE_IS_CONTACT(cnode))
1259
0
        for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
1260
0
          moved_buddies = g_list_append(moved_buddies, bnode);
1261
0
    }
1262
1263
0
    old_name = source->name;
1264
0
    source->name = new_name;
1265
1266
0
    key = g_utf8_collate_key(old_name, -1);
1267
0
    g_hash_table_remove(groups_cache, key);
1268
0
    g_free(key);
1269
1270
0
    key = g_utf8_collate_key(new_name, -1);
1271
0
    g_hash_table_insert(groups_cache, key, source);
1272
0
  }
1273
1274
  /* Save our changes */
1275
0
  if (ops && ops->save_node)
1276
0
    ops->save_node((PurpleBlistNode*) source);
1277
1278
  /* Update the UI */
1279
0
  if (ops && ops->update)
1280
0
    ops->update(purplebuddylist, (PurpleBlistNode*)source);
1281
1282
  /* Notify all PRPLs */
1283
  /* TODO: Is this condition needed?  Seems like it would always be TRUE */
1284
0
  if(old_name && !purple_strequal(source->name, old_name)) {
1285
0
    for (accts = purple_group_get_accounts(source); accts; accts = g_slist_remove(accts, accts->data)) {
1286
0
      PurpleAccount *account = accts->data;
1287
0
      PurpleConnection *gc = NULL;
1288
0
      PurplePlugin *prpl = NULL;
1289
0
      PurplePluginProtocolInfo *prpl_info = NULL;
1290
0
      GList *l = NULL, *buddies = NULL;
1291
1292
0
      gc = purple_account_get_connection(account);
1293
1294
0
      if(gc)
1295
0
        prpl = purple_connection_get_prpl(gc);
1296
1297
0
      if(gc && prpl)
1298
0
        prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
1299
1300
0
      if(!prpl_info)
1301
0
        continue;
1302
1303
0
      for(l = moved_buddies; l; l = l->next) {
1304
0
        PurpleBuddy *buddy = (PurpleBuddy *)l->data;
1305
1306
0
        if(buddy && buddy->account == account)
1307
0
          buddies = g_list_append(buddies, (PurpleBlistNode *)buddy);
1308
0
      }
1309
1310
0
      if(prpl_info->rename_group) {
1311
0
        prpl_info->rename_group(gc, old_name, source, buddies);
1312
0
      } else {
1313
0
        GList *cur, *groups = NULL;
1314
1315
        /* Make a list of what the groups each buddy is in */
1316
0
        for(cur = buddies; cur; cur = cur->next) {
1317
0
          PurpleBlistNode *node = (PurpleBlistNode *)cur->data;
1318
0
          groups = g_list_prepend(groups, node->parent->parent);
1319
0
        }
1320
1321
0
        purple_account_remove_buddies(account, buddies, groups);
1322
0
        g_list_free(groups);
1323
0
        purple_account_add_buddies(account, buddies);
1324
0
      }
1325
1326
0
      g_list_free(buddies);
1327
0
    }
1328
0
  }
1329
0
  g_list_free(moved_buddies);
1330
0
  g_free(old_name);
1331
0
}
1332
1333
static void purple_blist_node_initialize_settings(PurpleBlistNode *node);
1334
1335
PurpleChat *purple_chat_new(PurpleAccount *account, const char *alias, GHashTable *components)
1336
0
{
1337
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
1338
0
  PurpleChat *chat;
1339
1340
0
  g_return_val_if_fail(account != NULL, NULL);
1341
0
  g_return_val_if_fail(components != NULL, NULL);
1342
1343
0
  chat = g_new0(PurpleChat, 1);
1344
0
  chat->account = account;
1345
0
  if ((alias != NULL) && (*alias != '\0'))
1346
0
    chat->alias = purple_utf8_strip_unprintables(alias);
1347
0
  chat->components = components;
1348
0
  purple_blist_node_initialize_settings((PurpleBlistNode *)chat);
1349
0
  ((PurpleBlistNode *)chat)->type = PURPLE_BLIST_CHAT_NODE;
1350
1351
0
  if (ops != NULL && ops->new_node != NULL)
1352
0
    ops->new_node((PurpleBlistNode *)chat);
1353
1354
0
  PURPLE_DBUS_REGISTER_POINTER(chat, PurpleChat);
1355
0
  return chat;
1356
0
}
1357
1358
void
1359
purple_chat_destroy(PurpleChat *chat)
1360
0
{
1361
0
  g_hash_table_destroy(chat->components);
1362
0
  g_hash_table_destroy(chat->node.settings);
1363
0
  g_free(chat->alias);
1364
0
  PURPLE_DBUS_UNREGISTER_POINTER(chat);
1365
0
  g_free(chat);
1366
0
}
1367
1368
PurpleBuddy *purple_buddy_new(PurpleAccount *account, const char *name, const char *alias)
1369
0
{
1370
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
1371
0
  PurpleBuddy *buddy;
1372
1373
0
  g_return_val_if_fail(account != NULL, NULL);
1374
0
  g_return_val_if_fail(name != NULL, NULL);
1375
1376
0
  buddy = g_new0(PurpleBuddy, 1);
1377
0
  buddy->account  = account;
1378
0
  buddy->name     = purple_utf8_strip_unprintables(name);
1379
0
  buddy->alias    = purple_utf8_strip_unprintables(alias);
1380
0
  buddy->presence = purple_presence_new_for_buddy(buddy);
1381
0
  ((PurpleBlistNode *)buddy)->type = PURPLE_BLIST_BUDDY_NODE;
1382
1383
0
  purple_presence_set_status_active(buddy->presence, "offline", TRUE);
1384
1385
0
  purple_blist_node_initialize_settings((PurpleBlistNode *)buddy);
1386
1387
0
  if (ops && ops->new_node)
1388
0
    ops->new_node((PurpleBlistNode *)buddy);
1389
1390
0
  PURPLE_DBUS_REGISTER_POINTER(buddy, PurpleBuddy);
1391
0
  return buddy;
1392
0
}
1393
1394
void
1395
purple_buddy_destroy(PurpleBuddy *buddy)
1396
0
{
1397
0
  PurplePlugin *prpl;
1398
0
  PurplePluginProtocolInfo *prpl_info;
1399
1400
  /*
1401
   * Tell the owner PRPL that we're about to free the buddy so it
1402
   * can free proto_data
1403
   */
1404
0
  prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account));
1405
0
  if (prpl) {
1406
0
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
1407
0
    if (prpl_info && prpl_info->buddy_free)
1408
0
      prpl_info->buddy_free(buddy);
1409
0
  }
1410
1411
  /* Delete the node */
1412
0
  purple_buddy_icon_unref(buddy->icon);
1413
0
  g_hash_table_destroy(buddy->node.settings);
1414
0
  purple_presence_destroy(buddy->presence);
1415
0
  g_free(buddy->name);
1416
0
  g_free(buddy->alias);
1417
0
  g_free(buddy->server_alias);
1418
1419
0
  PURPLE_DBUS_UNREGISTER_POINTER(buddy);
1420
1421
  /* FIXME: Once PurpleBuddy is a GObject, timeout callbacks can
1422
   * g_object_ref() it when connecting the callback and
1423
   * g_object_unref() it in the handler.  That way, it won't
1424
   * get freed while the timeout is pending and this line can
1425
   * be removed. */
1426
0
  while (g_source_remove_by_user_data((gpointer *)buddy)) {
1427
0
  }
1428
1429
0
  g_free(buddy);
1430
0
}
1431
1432
void
1433
purple_buddy_set_icon(PurpleBuddy *buddy, PurpleBuddyIcon *icon)
1434
0
{
1435
0
  g_return_if_fail(buddy != NULL);
1436
1437
0
  if (buddy->icon != icon)
1438
0
  {
1439
0
    purple_buddy_icon_unref(buddy->icon);
1440
0
    buddy->icon = (icon != NULL ? purple_buddy_icon_ref(icon) : NULL);
1441
0
  }
1442
1443
0
  purple_signal_emit(purple_blist_get_handle(), "buddy-icon-changed", buddy);
1444
1445
0
  purple_blist_update_node_icon((PurpleBlistNode*)buddy);
1446
0
}
1447
1448
PurpleAccount *
1449
purple_buddy_get_account(const PurpleBuddy *buddy)
1450
0
{
1451
0
  g_return_val_if_fail(buddy != NULL, NULL);
1452
1453
0
  return buddy->account;
1454
0
}
1455
1456
const char *
1457
purple_buddy_get_name(const PurpleBuddy *buddy)
1458
0
{
1459
0
  g_return_val_if_fail(buddy != NULL, NULL);
1460
1461
0
  return buddy->name;
1462
0
}
1463
1464
PurpleBuddyIcon *
1465
purple_buddy_get_icon(const PurpleBuddy *buddy)
1466
0
{
1467
0
  g_return_val_if_fail(buddy != NULL, NULL);
1468
1469
0
  return buddy->icon;
1470
0
}
1471
1472
gpointer
1473
purple_buddy_get_protocol_data(const PurpleBuddy *buddy)
1474
0
{
1475
0
  g_return_val_if_fail(buddy != NULL, NULL);
1476
1477
0
  return buddy->proto_data;
1478
0
}
1479
1480
void
1481
purple_buddy_set_protocol_data(PurpleBuddy *buddy, gpointer data)
1482
0
{
1483
0
  g_return_if_fail(buddy != NULL);
1484
1485
0
  buddy->proto_data = data;
1486
0
}
1487
1488
1489
void purple_blist_add_chat(PurpleChat *chat, PurpleGroup *group, PurpleBlistNode *node)
1490
0
{
1491
0
  PurpleBlistNode *cnode = (PurpleBlistNode*)chat;
1492
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
1493
1494
0
  g_return_if_fail(chat != NULL);
1495
0
  g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT((PurpleBlistNode *)chat));
1496
1497
0
  if (node == NULL) {
1498
0
    if (group == NULL)
1499
0
      group = purple_group_new(_("Chats"));
1500
1501
    /* Add group to blist if isn't already on it. Fixes #2752. */
1502
0
    if (!purple_find_group(group->name)) {
1503
0
      purple_blist_add_group(group,
1504
0
          purple_blist_get_last_sibling(purplebuddylist->root));
1505
0
    }
1506
0
  } else {
1507
0
    group = (PurpleGroup*)node->parent;
1508
0
  }
1509
1510
  /* if we're moving to overtop of ourselves, do nothing */
1511
0
  if (cnode == node)
1512
0
    return;
1513
1514
0
  if (cnode->parent) {
1515
    /* This chat was already in the list and is
1516
     * being moved.
1517
     */
1518
0
    ((PurpleGroup *)cnode->parent)->totalsize--;
1519
0
    if (purple_account_is_connected(chat->account)) {
1520
0
      ((PurpleGroup *)cnode->parent)->online--;
1521
0
      ((PurpleGroup *)cnode->parent)->currentsize--;
1522
0
    }
1523
0
    if (cnode->next)
1524
0
      cnode->next->prev = cnode->prev;
1525
0
    if (cnode->prev)
1526
0
      cnode->prev->next = cnode->next;
1527
0
    if (cnode->parent->child == cnode)
1528
0
      cnode->parent->child = cnode->next;
1529
1530
0
    if (ops && ops->remove)
1531
0
      ops->remove(purplebuddylist, cnode);
1532
    /* ops->remove() cleaned up the cnode's ui_data, so we need to
1533
     * reinitialize it */
1534
0
    if (ops && ops->new_node)
1535
0
      ops->new_node(cnode);
1536
0
  }
1537
1538
0
  if (node != NULL) {
1539
0
    if (node->next)
1540
0
      node->next->prev = cnode;
1541
0
    cnode->next = node->next;
1542
0
    cnode->prev = node;
1543
0
    cnode->parent = node->parent;
1544
0
    node->next = cnode;
1545
0
    ((PurpleGroup *)node->parent)->totalsize++;
1546
0
    if (purple_account_is_connected(chat->account)) {
1547
0
      ((PurpleGroup *)node->parent)->online++;
1548
0
      ((PurpleGroup *)node->parent)->currentsize++;
1549
0
    }
1550
0
  } else {
1551
0
    if (((PurpleBlistNode *)group)->child)
1552
0
      ((PurpleBlistNode *)group)->child->prev = cnode;
1553
0
    cnode->next = ((PurpleBlistNode *)group)->child;
1554
0
    cnode->prev = NULL;
1555
0
    ((PurpleBlistNode *)group)->child = cnode;
1556
0
    cnode->parent = (PurpleBlistNode *)group;
1557
0
    group->totalsize++;
1558
0
    if (purple_account_is_connected(chat->account)) {
1559
0
      group->online++;
1560
0
      group->currentsize++;
1561
0
    }
1562
0
  }
1563
1564
0
  if (ops && ops->save_node)
1565
0
    ops->save_node(cnode);
1566
1567
0
  if (ops && ops->update)
1568
0
    ops->update(purplebuddylist, (PurpleBlistNode *)cnode);
1569
1570
0
  purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
1571
0
      cnode);
1572
0
}
1573
1574
void purple_blist_add_buddy(PurpleBuddy *buddy, PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node)
1575
0
{
1576
0
  PurpleBlistNode *cnode, *bnode;
1577
0
  PurpleGroup *g;
1578
0
  PurpleContact *c;
1579
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
1580
0
  struct _purple_hbuddy *hb, *hb2;
1581
0
  GHashTable *account_buddies;
1582
1583
0
  g_return_if_fail(buddy != NULL);
1584
0
  g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY((PurpleBlistNode*)buddy));
1585
1586
0
  bnode = (PurpleBlistNode *)buddy;
1587
1588
  /* if we're moving to overtop of ourselves, do nothing */
1589
0
  if (bnode == node || (!node && bnode->parent &&
1590
0
        contact && bnode->parent == (PurpleBlistNode*)contact
1591
0
        && bnode == bnode->parent->child))
1592
0
    return;
1593
1594
0
  if (node && PURPLE_BLIST_NODE_IS_BUDDY(node)) {
1595
0
    c = (PurpleContact*)node->parent;
1596
0
    g = (PurpleGroup*)node->parent->parent;
1597
0
  } else if (contact) {
1598
0
    c = contact;
1599
0
    g = PURPLE_GROUP(PURPLE_BLIST_NODE(c)->parent);
1600
0
  } else {
1601
0
    g = group;
1602
0
    if (g == NULL)
1603
0
      g = purple_group_new(_("Buddies"));
1604
    /* Add group to blist if isn't already on it. Fixes #2752. */
1605
0
    if (!purple_find_group(g->name)) {
1606
0
      purple_blist_add_group(g,
1607
0
          purple_blist_get_last_sibling(purplebuddylist->root));
1608
0
    }
1609
0
    c = purple_contact_new();
1610
0
    purple_blist_add_contact(c, g,
1611
0
        purple_blist_get_last_child((PurpleBlistNode*)g));
1612
0
  }
1613
1614
0
  cnode = (PurpleBlistNode *)c;
1615
1616
0
  if (bnode->parent) {
1617
0
    if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
1618
0
      ((PurpleContact*)bnode->parent)->online--;
1619
0
      if (((PurpleContact*)bnode->parent)->online == 0)
1620
0
        ((PurpleGroup*)bnode->parent->parent)->online--;
1621
0
    }
1622
0
    if (purple_account_is_connected(buddy->account)) {
1623
0
      ((PurpleContact*)bnode->parent)->currentsize--;
1624
0
      if (((PurpleContact*)bnode->parent)->currentsize == 0)
1625
0
        ((PurpleGroup*)bnode->parent->parent)->currentsize--;
1626
0
    }
1627
0
    ((PurpleContact*)bnode->parent)->totalsize--;
1628
    /* the group totalsize will be taken care of by remove_contact below */
1629
1630
0
    if (bnode->parent->parent != (PurpleBlistNode*)g) {
1631
0
      purple_signal_emit(purple_blist_get_handle(), "buddy-removed-from-group", buddy);
1632
0
      serv_move_buddy(buddy, (PurpleGroup *)bnode->parent->parent, g);
1633
0
    }
1634
1635
0
    if (bnode->next)
1636
0
      bnode->next->prev = bnode->prev;
1637
0
    if (bnode->prev)
1638
0
      bnode->prev->next = bnode->next;
1639
0
    if (bnode->parent->child == bnode)
1640
0
      bnode->parent->child = bnode->next;
1641
1642
0
    if (ops && ops->remove)
1643
0
      ops->remove(purplebuddylist, bnode);
1644
1645
0
    if (bnode->parent->parent != (PurpleBlistNode*)g) {
1646
0
      struct _purple_hbuddy hb;
1647
0
      hb.name = (gchar *)purple_normalize(buddy->account, buddy->name);
1648
0
      hb.account = buddy->account;
1649
0
      hb.group = bnode->parent->parent;
1650
0
      g_hash_table_remove(purplebuddylist->buddies, &hb);
1651
1652
0
      account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
1653
0
      g_hash_table_remove(account_buddies, &hb);
1654
0
    }
1655
1656
0
    if (!bnode->parent->child) {
1657
0
      purple_blist_remove_contact((PurpleContact*)bnode->parent);
1658
0
    } else {
1659
0
      purple_contact_invalidate_priority_buddy((PurpleContact*)bnode->parent);
1660
0
      if (ops && ops->update)
1661
0
        ops->update(purplebuddylist, bnode->parent);
1662
0
    }
1663
0
  }
1664
1665
0
  if (node && PURPLE_BLIST_NODE_IS_BUDDY(node)) {
1666
0
    if (node->next)
1667
0
      node->next->prev = bnode;
1668
0
    bnode->next = node->next;
1669
0
    bnode->prev = node;
1670
0
    bnode->parent = node->parent;
1671
0
    node->next = bnode;
1672
0
  } else {
1673
0
    if (cnode->child)
1674
0
      cnode->child->prev = bnode;
1675
0
    bnode->prev = NULL;
1676
0
    bnode->next = cnode->child;
1677
0
    cnode->child = bnode;
1678
0
    bnode->parent = cnode;
1679
0
  }
1680
1681
0
  if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
1682
0
    if (++(PURPLE_CONTACT(bnode->parent)->online) == 1)
1683
0
      PURPLE_GROUP(bnode->parent->parent)->online++;
1684
0
  }
1685
0
  if (purple_account_is_connected(buddy->account)) {
1686
0
    if (++(PURPLE_CONTACT(bnode->parent)->currentsize) == 1)
1687
0
      PURPLE_GROUP(bnode->parent->parent)->currentsize++;
1688
0
  }
1689
0
  PURPLE_CONTACT(bnode->parent)->totalsize++;
1690
1691
0
  hb = g_new(struct _purple_hbuddy, 1);
1692
0
  hb->name = g_strdup(purple_normalize(buddy->account, buddy->name));
1693
0
  hb->account = buddy->account;
1694
0
  hb->group = ((PurpleBlistNode*)buddy)->parent->parent;
1695
1696
0
  g_hash_table_replace(purplebuddylist->buddies, hb, buddy);
1697
1698
0
  account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
1699
1700
0
  hb2 = g_new(struct _purple_hbuddy, 1);
1701
0
  hb2->name = g_strdup(hb->name);
1702
0
  hb2->account = buddy->account;
1703
0
  hb2->group = ((PurpleBlistNode*)buddy)->parent->parent;
1704
1705
0
  g_hash_table_replace(account_buddies, hb2, buddy);
1706
1707
0
  purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
1708
1709
0
  if (ops && ops->save_node)
1710
0
    ops->save_node((PurpleBlistNode*) buddy);
1711
1712
0
  if (ops && ops->update)
1713
0
    ops->update(purplebuddylist, (PurpleBlistNode*)buddy);
1714
1715
  /* Signal that the buddy has been added */
1716
0
  purple_signal_emit(purple_blist_get_handle(), "buddy-added", buddy);
1717
1718
0
  purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
1719
0
      PURPLE_BLIST_NODE(buddy));
1720
0
}
1721
1722
PurpleContact *purple_contact_new()
1723
0
{
1724
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
1725
1726
0
  PurpleContact *contact = g_new0(PurpleContact, 1);
1727
0
  contact->totalsize = 0;
1728
0
  contact->currentsize = 0;
1729
0
  contact->online = 0;
1730
0
  purple_blist_node_initialize_settings((PurpleBlistNode *)contact);
1731
0
  ((PurpleBlistNode *)contact)->type = PURPLE_BLIST_CONTACT_NODE;
1732
1733
0
  if (ops && ops->new_node)
1734
0
    ops->new_node((PurpleBlistNode *)contact);
1735
1736
0
  PURPLE_DBUS_REGISTER_POINTER(contact, PurpleContact);
1737
0
  return contact;
1738
0
}
1739
1740
void
1741
purple_contact_destroy(PurpleContact *contact)
1742
0
{
1743
0
  g_hash_table_destroy(contact->node.settings);
1744
0
  g_free(contact->alias);
1745
0
  PURPLE_DBUS_UNREGISTER_POINTER(contact);
1746
0
  g_free(contact);
1747
0
}
1748
1749
PurpleGroup *
1750
purple_contact_get_group(const PurpleContact *contact)
1751
0
{
1752
0
  g_return_val_if_fail(contact, NULL);
1753
1754
0
  return (PurpleGroup *)(((PurpleBlistNode *)contact)->parent);
1755
0
}
1756
1757
void purple_contact_set_alias(PurpleContact *contact, const char *alias)
1758
0
{
1759
0
  purple_blist_alias_contact(contact,alias);
1760
0
}
1761
1762
const char *purple_contact_get_alias(PurpleContact* contact)
1763
0
{
1764
0
  g_return_val_if_fail(contact != NULL, NULL);
1765
1766
0
  if (contact->alias)
1767
0
    return contact->alias;
1768
1769
0
  return purple_buddy_get_alias(purple_contact_get_priority_buddy(contact));
1770
0
}
1771
1772
gboolean purple_contact_on_account(PurpleContact *c, PurpleAccount *account)
1773
0
{
1774
0
  PurpleBlistNode *bnode, *cnode = (PurpleBlistNode *) c;
1775
1776
0
  g_return_val_if_fail(c != NULL, FALSE);
1777
0
  g_return_val_if_fail(account != NULL, FALSE);
1778
1779
0
  for (bnode = cnode->child; bnode; bnode = bnode->next) {
1780
0
    PurpleBuddy *buddy;
1781
1782
0
    if (! PURPLE_BLIST_NODE_IS_BUDDY(bnode))
1783
0
      continue;
1784
1785
0
    buddy = (PurpleBuddy *)bnode;
1786
0
    if (buddy->account == account)
1787
0
      return TRUE;
1788
0
  }
1789
0
  return FALSE;
1790
0
}
1791
1792
void purple_contact_invalidate_priority_buddy(PurpleContact *contact)
1793
0
{
1794
0
  g_return_if_fail(contact != NULL);
1795
1796
0
  contact->priority_valid = FALSE;
1797
0
}
1798
1799
PurpleGroup *purple_group_new(const char *name)
1800
0
{
1801
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
1802
0
  PurpleGroup *group;
1803
1804
0
  g_return_val_if_fail(name  != NULL, NULL);
1805
0
  g_return_val_if_fail(*name != '\0', NULL);
1806
1807
0
  group = purple_find_group(name);
1808
0
  if (group != NULL)
1809
0
    return group;
1810
1811
0
  group = g_new0(PurpleGroup, 1);
1812
0
  group->name = purple_utf8_strip_unprintables(name);
1813
0
  group->totalsize = 0;
1814
0
  group->currentsize = 0;
1815
0
  group->online = 0;
1816
0
  purple_blist_node_initialize_settings((PurpleBlistNode *)group);
1817
0
  ((PurpleBlistNode *)group)->type = PURPLE_BLIST_GROUP_NODE;
1818
1819
0
  if (ops && ops->new_node)
1820
0
    ops->new_node((PurpleBlistNode *)group);
1821
1822
0
  PURPLE_DBUS_REGISTER_POINTER(group, PurpleGroup);
1823
0
  return group;
1824
0
}
1825
1826
void
1827
purple_group_destroy(PurpleGroup *group)
1828
0
{
1829
0
  g_hash_table_destroy(group->node.settings);
1830
0
  g_free(group->name);
1831
0
  PURPLE_DBUS_UNREGISTER_POINTER(group);
1832
0
  g_free(group);
1833
0
}
1834
1835
void purple_blist_add_contact(PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node)
1836
0
{
1837
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
1838
0
  PurpleGroup *g;
1839
0
  PurpleBlistNode *gnode, *cnode, *bnode;
1840
1841
0
  g_return_if_fail(contact != NULL);
1842
0
  g_return_if_fail(PURPLE_BLIST_NODE_IS_CONTACT((PurpleBlistNode*)contact));
1843
1844
0
  if (PURPLE_BLIST_NODE(contact) == node)
1845
0
    return;
1846
1847
0
  if (node && (PURPLE_BLIST_NODE_IS_CONTACT(node) ||
1848
0
        PURPLE_BLIST_NODE_IS_CHAT(node)))
1849
0
    g = (PurpleGroup*)node->parent;
1850
0
  else if (group)
1851
0
    g = group;
1852
0
  else {
1853
0
    g = purple_find_group(_("Buddies"));
1854
0
    if (g == NULL) {
1855
0
      g = purple_group_new(_("Buddies"));
1856
0
      purple_blist_add_group(g,
1857
0
          purple_blist_get_last_sibling(purplebuddylist->root));
1858
0
    }
1859
0
  }
1860
1861
0
  gnode = (PurpleBlistNode*)g;
1862
0
  cnode = (PurpleBlistNode*)contact;
1863
1864
0
  if (cnode->parent) {
1865
0
    if (cnode->parent->child == cnode)
1866
0
      cnode->parent->child = cnode->next;
1867
0
    if (cnode->prev)
1868
0
      cnode->prev->next = cnode->next;
1869
0
    if (cnode->next)
1870
0
      cnode->next->prev = cnode->prev;
1871
1872
0
    if (cnode->parent != gnode) {
1873
0
      bnode = cnode->child;
1874
0
      while (bnode) {
1875
0
        PurpleBlistNode *next_bnode = bnode->next;
1876
0
        PurpleBuddy *b = (PurpleBuddy*)bnode;
1877
0
        GHashTable *account_buddies;
1878
1879
0
        struct _purple_hbuddy *hb, *hb2;
1880
1881
0
        hb = g_new(struct _purple_hbuddy, 1);
1882
0
        hb->name = g_strdup(purple_normalize(b->account, b->name));
1883
0
        hb->account = b->account;
1884
0
        hb->group = cnode->parent;
1885
1886
0
        g_hash_table_remove(purplebuddylist->buddies, hb);
1887
1888
0
        account_buddies = g_hash_table_lookup(buddies_cache, b->account);
1889
0
        g_hash_table_remove(account_buddies, hb);
1890
1891
0
        if (!purple_find_buddy_in_group(b->account, b->name, g)) {
1892
0
          hb->group = gnode;
1893
0
          g_hash_table_replace(purplebuddylist->buddies, hb, b);
1894
1895
0
          hb2 = g_new(struct _purple_hbuddy, 1);
1896
0
          hb2->name = g_strdup(hb->name);
1897
0
          hb2->account = b->account;
1898
0
          hb2->group = gnode;
1899
1900
0
          g_hash_table_replace(account_buddies, hb2, b);
1901
1902
0
          if (purple_account_get_connection(b->account))
1903
0
            serv_move_buddy(b, (PurpleGroup *)cnode->parent, g);
1904
0
        } else {
1905
0
          gboolean empty_contact = FALSE;
1906
1907
          /* this buddy already exists in the group, so we're
1908
           * gonna delete it instead */
1909
0
          g_free(hb->name);
1910
0
          g_free(hb);
1911
0
          if (purple_account_get_connection(b->account))
1912
0
            purple_account_remove_buddy(b->account, b, (PurpleGroup *)cnode->parent);
1913
1914
0
          if (!cnode->child->next)
1915
0
            empty_contact = TRUE;
1916
0
          purple_blist_remove_buddy(b);
1917
1918
          /** in purple_blist_remove_buddy(), if the last buddy in a
1919
           * contact is removed, the contact is cleaned up and
1920
           * g_free'd, so we mustn't try to reference bnode->next */
1921
0
          if (empty_contact)
1922
0
            return;
1923
0
        }
1924
0
        bnode = next_bnode;
1925
0
      }
1926
0
    }
1927
1928
0
    if (contact->online > 0)
1929
0
      ((PurpleGroup*)cnode->parent)->online--;
1930
0
    if (contact->currentsize > 0)
1931
0
      ((PurpleGroup*)cnode->parent)->currentsize--;
1932
0
    ((PurpleGroup*)cnode->parent)->totalsize--;
1933
1934
0
    if (ops && ops->remove)
1935
0
      ops->remove(purplebuddylist, cnode);
1936
1937
0
    if (ops && ops->remove_node)
1938
0
      ops->remove_node(cnode);
1939
0
  }
1940
1941
0
  if (node && (PURPLE_BLIST_NODE_IS_CONTACT(node) ||
1942
0
        PURPLE_BLIST_NODE_IS_CHAT(node))) {
1943
0
    if (node->next)
1944
0
      node->next->prev = cnode;
1945
0
    cnode->next = node->next;
1946
0
    cnode->prev = node;
1947
0
    cnode->parent = node->parent;
1948
0
    node->next = cnode;
1949
0
  } else {
1950
0
    if (gnode->child)
1951
0
      gnode->child->prev = cnode;
1952
0
    cnode->prev = NULL;
1953
0
    cnode->next = gnode->child;
1954
0
    gnode->child = cnode;
1955
0
    cnode->parent = gnode;
1956
0
  }
1957
1958
0
  if (contact->online > 0)
1959
0
    g->online++;
1960
0
  if (contact->currentsize > 0)
1961
0
    g->currentsize++;
1962
0
  g->totalsize++;
1963
1964
0
  if (ops && ops->save_node)
1965
0
  {
1966
0
    if (cnode->child)
1967
0
      ops->save_node(cnode);
1968
0
    for (bnode = cnode->child; bnode; bnode = bnode->next)
1969
0
      ops->save_node(bnode);
1970
0
  }
1971
1972
0
  if (ops && ops->update)
1973
0
  {
1974
0
    if (cnode->child)
1975
0
      ops->update(purplebuddylist, cnode);
1976
1977
0
    for (bnode = cnode->child; bnode; bnode = bnode->next)
1978
0
      ops->update(purplebuddylist, bnode);
1979
0
  }
1980
0
}
1981
1982
void purple_blist_merge_contact(PurpleContact *source, PurpleBlistNode *node)
1983
0
{
1984
0
  PurpleBlistNode *sourcenode = (PurpleBlistNode*)source;
1985
0
  PurpleBlistNode *prev, *cur, *next;
1986
0
  PurpleContact *target;
1987
1988
0
  g_return_if_fail(source != NULL);
1989
0
  g_return_if_fail(node != NULL);
1990
1991
0
  if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
1992
0
    target = (PurpleContact *)node;
1993
0
    prev = purple_blist_get_last_child(node);
1994
0
  } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
1995
0
    target = (PurpleContact *)node->parent;
1996
0
    prev = node;
1997
0
  } else {
1998
0
    return;
1999
0
  }
2000
2001
0
  if (source == target || !target)
2002
0
    return;
2003
2004
0
  next = sourcenode->child;
2005
2006
0
  while (next) {
2007
0
    cur = next;
2008
0
    next = cur->next;
2009
0
    if (PURPLE_BLIST_NODE_IS_BUDDY(cur)) {
2010
0
      purple_blist_add_buddy((PurpleBuddy *)cur, target, NULL, prev);
2011
0
      prev = cur;
2012
0
    }
2013
0
  }
2014
0
}
2015
2016
void purple_blist_add_group(PurpleGroup *group, PurpleBlistNode *node)
2017
0
{
2018
0
  PurpleBlistUiOps *ops;
2019
0
  PurpleBlistNode *gnode = (PurpleBlistNode*)group;
2020
0
  gchar* key;
2021
2022
0
  g_return_if_fail(group != NULL);
2023
0
  g_return_if_fail(PURPLE_BLIST_NODE_IS_GROUP((PurpleBlistNode *)group));
2024
2025
0
  ops = purple_blist_get_ui_ops();
2026
2027
  /* if we're moving to overtop of ourselves, do nothing */
2028
0
  if (gnode == node) {
2029
0
    if (!purplebuddylist->root)
2030
0
      node = NULL;
2031
0
    else
2032
0
      return;
2033
0
  }
2034
2035
0
  if (purple_find_group(group->name)) {
2036
    /* This is just being moved */
2037
2038
0
    if (ops && ops->remove)
2039
0
      ops->remove(purplebuddylist, (PurpleBlistNode *)group);
2040
2041
0
    if (gnode == purplebuddylist->root)
2042
0
      purplebuddylist->root = gnode->next;
2043
0
    if (gnode->prev)
2044
0
      gnode->prev->next = gnode->next;
2045
0
    if (gnode->next)
2046
0
      gnode->next->prev = gnode->prev;
2047
0
  } else {
2048
0
    key = g_utf8_collate_key(group->name, -1);
2049
0
    g_hash_table_insert(groups_cache, key, group);
2050
0
  }
2051
2052
0
  if (node && PURPLE_BLIST_NODE_IS_GROUP(node)) {
2053
0
    gnode->next = node->next;
2054
0
    gnode->prev = node;
2055
0
    if (node->next)
2056
0
      node->next->prev = gnode;
2057
0
    node->next = gnode;
2058
0
  } else {
2059
0
    if (purplebuddylist->root)
2060
0
      purplebuddylist->root->prev = gnode;
2061
0
    gnode->next = purplebuddylist->root;
2062
0
    gnode->prev = NULL;
2063
0
    purplebuddylist->root = gnode;
2064
0
  }
2065
2066
0
  if (ops && ops->save_node) {
2067
0
    ops->save_node(gnode);
2068
0
    for (node = gnode->child; node; node = node->next)
2069
0
      ops->save_node(node);
2070
0
  }
2071
2072
0
  if (ops && ops->update) {
2073
0
    ops->update(purplebuddylist, gnode);
2074
0
    for (node = gnode->child; node; node = node->next)
2075
0
      ops->update(purplebuddylist, node);
2076
0
  }
2077
2078
0
  purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
2079
0
      gnode);
2080
0
}
2081
2082
void purple_blist_remove_contact(PurpleContact *contact)
2083
0
{
2084
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
2085
0
  PurpleBlistNode *node, *gnode;
2086
2087
0
  g_return_if_fail(contact != NULL);
2088
2089
0
  node = (PurpleBlistNode *)contact;
2090
0
  gnode = node->parent;
2091
2092
0
  if (node->child) {
2093
    /*
2094
     * If this contact has children then remove them.  When the last
2095
     * buddy is removed from the contact, the contact is automatically
2096
     * deleted.
2097
     */
2098
0
    while (node->child->next) {
2099
0
      purple_blist_remove_buddy((PurpleBuddy*)node->child);
2100
0
    }
2101
    /*
2102
     * Remove the last buddy and trigger the deletion of the contact.
2103
     * It would probably be cleaner if contact-deletion was done after
2104
     * a timeout?  Or if it had to be done manually, like below?
2105
     */
2106
0
    purple_blist_remove_buddy((PurpleBuddy*)node->child);
2107
0
  } else {
2108
    /* Remove the node from its parent */
2109
0
    if (gnode->child == node)
2110
0
      gnode->child = node->next;
2111
0
    if (node->prev)
2112
0
      node->prev->next = node->next;
2113
0
    if (node->next)
2114
0
      node->next->prev = node->prev;
2115
2116
    /* Update the UI */
2117
0
    if (ops && ops->remove)
2118
0
      ops->remove(purplebuddylist, node);
2119
2120
0
    if (ops && ops->remove_node)
2121
0
      ops->remove_node(node);
2122
2123
0
    purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
2124
0
        PURPLE_BLIST_NODE(contact));
2125
2126
    /* Delete the node */
2127
0
    purple_contact_destroy(contact);
2128
0
  }
2129
0
}
2130
2131
void purple_blist_remove_buddy(PurpleBuddy *buddy)
2132
0
{
2133
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
2134
0
  PurpleBlistNode *node, *cnode, *gnode;
2135
0
  PurpleContact *contact;
2136
0
  PurpleGroup *group;
2137
0
  struct _purple_hbuddy hb;
2138
0
  GHashTable *account_buddies;
2139
2140
0
  g_return_if_fail(buddy != NULL);
2141
2142
0
  node = (PurpleBlistNode *)buddy;
2143
0
  cnode = node->parent;
2144
0
  gnode = (cnode != NULL) ? cnode->parent : NULL;
2145
0
  contact = (PurpleContact *)cnode;
2146
0
  group = (PurpleGroup *)gnode;
2147
2148
  /* Remove the node from its parent */
2149
0
  if (node->prev)
2150
0
    node->prev->next = node->next;
2151
0
  if (node->next)
2152
0
    node->next->prev = node->prev;
2153
0
  if ((cnode != NULL) && (cnode->child == node))
2154
0
    cnode->child = node->next;
2155
2156
  /* Adjust size counts */
2157
0
  if (contact != NULL) {
2158
0
    if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
2159
0
      contact->online--;
2160
0
      if (contact->online == 0)
2161
0
        group->online--;
2162
0
    }
2163
0
    if (purple_account_is_connected(buddy->account)) {
2164
0
      contact->currentsize--;
2165
0
      if (contact->currentsize == 0)
2166
0
        group->currentsize--;
2167
0
    }
2168
0
    contact->totalsize--;
2169
2170
    /* Re-sort the contact */
2171
0
    if (cnode->child && contact->priority == buddy) {
2172
0
      purple_contact_invalidate_priority_buddy(contact);
2173
0
      if (ops && ops->update)
2174
0
        ops->update(purplebuddylist, cnode);
2175
0
    }
2176
0
  }
2177
2178
  /* Remove this buddy from the buddies hash table */
2179
0
  hb.name = (gchar *)purple_normalize(buddy->account, buddy->name);
2180
0
  hb.account = buddy->account;
2181
0
  hb.group = gnode;
2182
0
  g_hash_table_remove(purplebuddylist->buddies, &hb);
2183
2184
0
  account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
2185
0
  g_hash_table_remove(account_buddies, &hb);
2186
2187
  /* Update the UI */
2188
0
  if (ops && ops->remove)
2189
0
    ops->remove(purplebuddylist, node);
2190
2191
0
  if (ops && ops->remove_node)
2192
0
    ops->remove_node(node);
2193
2194
  /* Remove this buddy's pounces */
2195
0
  purple_pounce_destroy_all_by_buddy(buddy);
2196
2197
  /* Signal that the buddy has been removed before freeing the memory for it */
2198
0
  purple_signal_emit(purple_blist_get_handle(), "buddy-removed", buddy);
2199
2200
0
  purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
2201
0
      PURPLE_BLIST_NODE(buddy));
2202
2203
0
  purple_buddy_destroy(buddy);
2204
2205
  /* If the contact is empty then remove it */
2206
0
  if ((contact != NULL) && !cnode->child)
2207
0
    purple_blist_remove_contact(contact);
2208
0
}
2209
2210
void purple_blist_remove_chat(PurpleChat *chat)
2211
0
{
2212
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
2213
0
  PurpleBlistNode *node, *gnode;
2214
0
  PurpleGroup *group;
2215
2216
0
  g_return_if_fail(chat != NULL);
2217
2218
0
  node = (PurpleBlistNode *)chat;
2219
0
  gnode = node->parent;
2220
0
  group = (PurpleGroup *)gnode;
2221
2222
0
  if (gnode != NULL)
2223
0
  {
2224
    /* Remove the node from its parent */
2225
0
    if (gnode->child == node)
2226
0
      gnode->child = node->next;
2227
0
    if (node->prev)
2228
0
      node->prev->next = node->next;
2229
0
    if (node->next)
2230
0
      node->next->prev = node->prev;
2231
2232
    /* Adjust size counts */
2233
0
    if (purple_account_is_connected(chat->account)) {
2234
0
      group->online--;
2235
0
      group->currentsize--;
2236
0
    }
2237
0
    group->totalsize--;
2238
2239
0
  }
2240
2241
  /* Update the UI */
2242
0
  if (ops && ops->remove)
2243
0
    ops->remove(purplebuddylist, node);
2244
2245
0
  if (ops && ops->remove_node)
2246
0
    ops->remove_node(node);
2247
2248
0
  purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
2249
0
      PURPLE_BLIST_NODE(chat));
2250
2251
  /* Delete the node */
2252
0
  purple_chat_destroy(chat);
2253
0
}
2254
2255
void purple_blist_remove_group(PurpleGroup *group)
2256
0
{
2257
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
2258
0
  PurpleBlistNode *node;
2259
0
  GList *l;
2260
0
  gchar* key;
2261
2262
0
  g_return_if_fail(group != NULL);
2263
2264
0
  node = (PurpleBlistNode *)group;
2265
2266
  /* Make sure the group is empty */
2267
0
  if (node->child)
2268
0
    return;
2269
2270
  /* Remove the node from its parent */
2271
0
  if (purplebuddylist->root == node)
2272
0
    purplebuddylist->root = node->next;
2273
0
  if (node->prev)
2274
0
    node->prev->next = node->next;
2275
0
  if (node->next)
2276
0
    node->next->prev = node->prev;
2277
2278
0
  key = g_utf8_collate_key(group->name, -1);
2279
0
  g_hash_table_remove(groups_cache, key);
2280
0
  g_free(key);
2281
2282
  /* Update the UI */
2283
0
  if (ops && ops->remove)
2284
0
    ops->remove(purplebuddylist, node);
2285
2286
0
  if (ops && ops->remove_node)
2287
0
    ops->remove_node(node);
2288
2289
0
  purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
2290
0
      PURPLE_BLIST_NODE(group));
2291
2292
  /* Remove the group from all accounts that are online */
2293
0
  for (l = purple_connections_get_all(); l != NULL; l = l->next)
2294
0
  {
2295
0
    PurpleConnection *gc = (PurpleConnection *)l->data;
2296
2297
0
    if (purple_connection_get_state(gc) == PURPLE_CONNECTED)
2298
0
      purple_account_remove_group(purple_connection_get_account(gc), group);
2299
0
  }
2300
2301
  /* Delete the node */
2302
0
  purple_group_destroy(group);
2303
0
}
2304
2305
PurpleBuddy *purple_contact_get_priority_buddy(PurpleContact *contact)
2306
0
{
2307
0
  g_return_val_if_fail(contact != NULL, NULL);
2308
2309
0
  if (!contact->priority_valid)
2310
0
    purple_contact_compute_priority_buddy(contact);
2311
2312
0
  return contact->priority;
2313
0
}
2314
2315
const char *purple_buddy_get_alias_only(PurpleBuddy *buddy)
2316
0
{
2317
0
  g_return_val_if_fail(buddy != NULL, NULL);
2318
2319
0
  if ((buddy->alias != NULL) && (*buddy->alias != '\0')) {
2320
0
    return buddy->alias;
2321
0
  } else if ((buddy->server_alias != NULL) &&
2322
0
       (*buddy->server_alias != '\0')) {
2323
2324
0
    return buddy->server_alias;
2325
0
  }
2326
2327
0
  return NULL;
2328
0
}
2329
2330
2331
const char *purple_buddy_get_contact_alias(PurpleBuddy *buddy)
2332
0
{
2333
0
  PurpleContact *c;
2334
2335
0
  g_return_val_if_fail(buddy != NULL, NULL);
2336
2337
  /* Search for an alias for the buddy. In order of precedence: */
2338
  /* The buddy alias */
2339
0
  if (buddy->alias != NULL)
2340
0
    return buddy->alias;
2341
2342
  /* The contact alias */
2343
0
  c = purple_buddy_get_contact(buddy);
2344
0
  if ((c != NULL) && (c->alias != NULL))
2345
0
    return c->alias;
2346
2347
  /* The server alias */
2348
0
  if ((buddy->server_alias) && (*buddy->server_alias))
2349
0
    return buddy->server_alias;
2350
2351
  /* The buddy's user name (i.e. no alias) */
2352
0
  return buddy->name;
2353
0
}
2354
2355
2356
const char *purple_buddy_get_alias(PurpleBuddy *buddy)
2357
0
{
2358
0
  g_return_val_if_fail(buddy != NULL, NULL);
2359
2360
  /* Search for an alias for the buddy. In order of precedence: */
2361
  /* The buddy alias */
2362
0
  if (buddy->alias != NULL)
2363
0
    return buddy->alias;
2364
2365
  /* The server alias */
2366
0
  if ((buddy->server_alias) && (*buddy->server_alias))
2367
0
    return buddy->server_alias;
2368
2369
  /* The buddy's user name (i.e. no alias) */
2370
0
  return buddy->name;
2371
0
}
2372
2373
const char *purple_buddy_get_local_buddy_alias(PurpleBuddy *buddy)
2374
0
{
2375
0
  g_return_val_if_fail(buddy, NULL);
2376
0
  return buddy->alias;
2377
0
}
2378
2379
const char *purple_buddy_get_server_alias(PurpleBuddy *buddy)
2380
0
{
2381
0
        g_return_val_if_fail(buddy != NULL, NULL);
2382
2383
0
  if ((buddy->server_alias) && (*buddy->server_alias))
2384
0
      return buddy->server_alias;
2385
2386
0
  return NULL;
2387
0
}
2388
2389
const char *purple_buddy_get_local_alias(PurpleBuddy *buddy)
2390
0
{
2391
0
  PurpleContact *c;
2392
2393
0
  g_return_val_if_fail(buddy != NULL, NULL);
2394
2395
  /* Search for an alias for the buddy. In order of precedence: */
2396
  /* The buddy alias */
2397
0
  if (buddy->alias != NULL)
2398
0
    return buddy->alias;
2399
2400
  /* The contact alias */
2401
0
  c = purple_buddy_get_contact(buddy);
2402
0
  if ((c != NULL) && (c->alias != NULL))
2403
0
    return c->alias;
2404
2405
  /* The buddy's user name (i.e. no alias) */
2406
0
  return buddy->name;
2407
0
}
2408
2409
const char *purple_chat_get_name(PurpleChat *chat)
2410
0
{
2411
0
  char *ret = NULL;
2412
0
  PurplePlugin *prpl;
2413
0
  PurplePluginProtocolInfo *prpl_info = NULL;
2414
2415
0
  g_return_val_if_fail(chat != NULL, NULL);
2416
2417
0
  if ((chat->alias != NULL) && (*chat->alias != '\0'))
2418
0
    return chat->alias;
2419
2420
0
  prpl = purple_find_prpl(purple_account_get_protocol_id(chat->account));
2421
0
  prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
2422
2423
0
  if (prpl_info->chat_info) {
2424
0
    struct proto_chat_entry *pce;
2425
0
    GList *parts = prpl_info->chat_info(purple_account_get_connection(chat->account));
2426
0
    pce = parts->data;
2427
0
    ret = g_hash_table_lookup(chat->components, pce->identifier);
2428
0
    g_list_free_full(parts, (GDestroyNotify)g_free);
2429
0
  }
2430
2431
0
  return ret;
2432
0
}
2433
2434
PurpleBuddy *purple_find_buddy(PurpleAccount *account, const char *name)
2435
0
{
2436
0
  PurpleBuddy *buddy, *fallback_buddy = NULL;
2437
0
  struct _purple_hbuddy hb;
2438
0
  PurpleBlistNode *group;
2439
2440
0
  g_return_val_if_fail(purplebuddylist != NULL, NULL);
2441
0
  g_return_val_if_fail(account != NULL, NULL);
2442
0
  g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
2443
2444
0
  hb.account = account;
2445
0
  hb.name = (gchar *)purple_normalize(account, name);
2446
2447
0
  for (group = purplebuddylist->root; group; group = group->next) {
2448
0
    if (!group->child)
2449
0
      continue;
2450
2451
0
    hb.group = group;
2452
0
    if ((buddy = g_hash_table_lookup(purplebuddylist->buddies, &hb))) {
2453
0
      if (PURPLE_BLIST_NODE_IS_VISIBLE(buddy))
2454
0
        return buddy;
2455
      /* Only return invisible buddies if there are no visible ones */
2456
0
      fallback_buddy = buddy;
2457
0
    }
2458
0
  }
2459
2460
0
  return fallback_buddy;
2461
0
}
2462
2463
PurpleBuddy *purple_find_buddy_in_group(PurpleAccount *account, const char *name,
2464
    PurpleGroup *group)
2465
0
{
2466
0
  struct _purple_hbuddy hb;
2467
2468
0
  g_return_val_if_fail(purplebuddylist != NULL, NULL);
2469
0
  g_return_val_if_fail(account != NULL, NULL);
2470
0
  g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
2471
2472
0
  hb.name = (gchar *)purple_normalize(account, name);
2473
0
  hb.account = account;
2474
0
  hb.group = (PurpleBlistNode*)group;
2475
2476
0
  return g_hash_table_lookup(purplebuddylist->buddies, &hb);
2477
0
}
2478
2479
static void find_acct_buddies(gpointer key, gpointer value, gpointer data)
2480
0
{
2481
0
  PurpleBuddy *buddy = value;
2482
0
  GSList **list = data;
2483
2484
0
  *list = g_slist_prepend(*list, buddy);
2485
0
}
2486
2487
GSList *purple_find_buddies(PurpleAccount *account, const char *name)
2488
0
{
2489
0
  PurpleBuddy *buddy;
2490
0
  PurpleBlistNode *node;
2491
0
  GSList *ret = NULL;
2492
2493
0
  g_return_val_if_fail(purplebuddylist != NULL, NULL);
2494
0
  g_return_val_if_fail(account != NULL, NULL);
2495
2496
0
  if ((name != NULL) && (*name != '\0')) {
2497
0
    struct _purple_hbuddy hb;
2498
2499
0
    hb.name = (gchar *)purple_normalize(account, name);
2500
0
    hb.account = account;
2501
2502
0
    for (node = purplebuddylist->root; node != NULL; node = node->next) {
2503
0
      if (!node->child)
2504
0
        continue;
2505
2506
0
      hb.group = node;
2507
0
      if ((buddy = g_hash_table_lookup(purplebuddylist->buddies, &hb)) != NULL)
2508
0
        ret = g_slist_prepend(ret, buddy);
2509
0
    }
2510
0
  } else {
2511
0
    GSList *list = NULL;
2512
0
    GHashTable *buddies = g_hash_table_lookup(buddies_cache, account);
2513
0
    g_hash_table_foreach(buddies, find_acct_buddies, &list);
2514
0
    ret = list;
2515
0
  }
2516
2517
0
  return ret;
2518
0
}
2519
2520
PurpleGroup *purple_find_group(const char *name)
2521
0
{
2522
0
  gchar* key;
2523
0
  PurpleGroup *group;
2524
2525
0
  g_return_val_if_fail(purplebuddylist != NULL, NULL);
2526
0
  g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
2527
2528
0
  key = g_utf8_collate_key(name, -1);
2529
0
  group = g_hash_table_lookup(groups_cache, key);
2530
0
  g_free(key);
2531
2532
0
  return group;
2533
0
}
2534
2535
PurpleChat *
2536
purple_blist_find_chat(PurpleAccount *account, const char *name)
2537
0
{
2538
0
  char *chat_name;
2539
0
  PurpleChat *chat;
2540
0
  PurplePlugin *prpl;
2541
0
  PurplePluginProtocolInfo *prpl_info = NULL;
2542
0
  struct proto_chat_entry *pce;
2543
0
  PurpleBlistNode *node, *group;
2544
0
  GList *parts;
2545
0
  char *normname;
2546
2547
0
  g_return_val_if_fail(purplebuddylist != NULL, NULL);
2548
0
  g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
2549
2550
0
  if (!purple_account_is_connected(account))
2551
0
    return NULL;
2552
2553
0
  prpl = purple_find_prpl(purple_account_get_protocol_id(account));
2554
0
  prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
2555
2556
0
  if (prpl_info->find_blist_chat != NULL)
2557
0
    return prpl_info->find_blist_chat(account, name);
2558
2559
0
  normname = g_strdup(purple_normalize(account, name));
2560
0
  for (group = purplebuddylist->root; group != NULL; group = group->next) {
2561
0
    for (node = group->child; node != NULL; node = node->next) {
2562
0
      if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
2563
2564
0
        chat = (PurpleChat*)node;
2565
2566
0
        if (account != chat->account)
2567
0
          continue;
2568
2569
0
        parts = prpl_info->chat_info(
2570
0
          purple_account_get_connection(chat->account));
2571
2572
0
        pce = parts->data;
2573
0
        chat_name = g_hash_table_lookup(chat->components,
2574
0
                        pce->identifier);
2575
0
        g_list_free_full(parts, (GDestroyNotify)g_free);
2576
2577
0
        if (chat->account == account && chat_name != NULL &&
2578
0
          purple_strequal(purple_normalize(account, chat_name), normname)) {
2579
0
          g_free(normname);
2580
0
          return chat;
2581
0
        }
2582
0
      }
2583
0
    }
2584
0
  }
2585
2586
0
  g_free(normname);
2587
0
  return NULL;
2588
0
}
2589
2590
PurpleGroup *
2591
purple_chat_get_group(PurpleChat *chat)
2592
0
{
2593
0
  g_return_val_if_fail(chat != NULL, NULL);
2594
2595
0
  return (PurpleGroup *)(((PurpleBlistNode *)chat)->parent);
2596
0
}
2597
2598
PurpleAccount *
2599
purple_chat_get_account(PurpleChat *chat)
2600
0
{
2601
0
  g_return_val_if_fail(chat != NULL, NULL);
2602
2603
0
  return chat->account;
2604
0
}
2605
2606
GHashTable *
2607
purple_chat_get_components(PurpleChat *chat)
2608
0
{
2609
0
  g_return_val_if_fail(chat != NULL, NULL);
2610
2611
0
  return chat->components;
2612
0
}
2613
2614
PurpleContact *purple_buddy_get_contact(PurpleBuddy *buddy)
2615
0
{
2616
0
  g_return_val_if_fail(buddy != NULL, NULL);
2617
2618
0
  return PURPLE_CONTACT(PURPLE_BLIST_NODE(buddy)->parent);
2619
0
}
2620
2621
PurplePresence *purple_buddy_get_presence(const PurpleBuddy *buddy)
2622
0
{
2623
0
  g_return_val_if_fail(buddy != NULL, NULL);
2624
0
  return buddy->presence;
2625
0
}
2626
2627
PurpleMediaCaps purple_buddy_get_media_caps(const PurpleBuddy *buddy)
2628
0
{
2629
0
  g_return_val_if_fail(buddy != NULL, 0);
2630
0
  return buddy->media_caps;
2631
0
}
2632
2633
void purple_buddy_set_media_caps(PurpleBuddy *buddy, PurpleMediaCaps media_caps)
2634
0
{
2635
0
  g_return_if_fail(buddy != NULL);
2636
0
  buddy->media_caps = media_caps;
2637
0
}
2638
2639
PurpleGroup *purple_buddy_get_group(PurpleBuddy *buddy)
2640
0
{
2641
0
  g_return_val_if_fail(buddy != NULL, NULL);
2642
2643
0
  if (((PurpleBlistNode *)buddy)->parent == NULL)
2644
0
    return NULL;
2645
2646
0
  return (PurpleGroup *)(((PurpleBlistNode*)buddy)->parent->parent);
2647
0
}
2648
2649
GSList *purple_group_get_accounts(PurpleGroup *group)
2650
0
{
2651
0
  GSList *l = NULL;
2652
0
  PurpleBlistNode *gnode, *cnode, *bnode;
2653
2654
0
  gnode = (PurpleBlistNode *)group;
2655
2656
0
  for (cnode = gnode->child;  cnode; cnode = cnode->next) {
2657
0
    if (PURPLE_BLIST_NODE_IS_CHAT(cnode)) {
2658
0
      if (!g_slist_find(l, ((PurpleChat *)cnode)->account))
2659
0
        l = g_slist_append(l, ((PurpleChat *)cnode)->account);
2660
0
    } else if (PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
2661
0
      for (bnode = cnode->child; bnode; bnode = bnode->next) {
2662
0
        if (PURPLE_BLIST_NODE_IS_BUDDY(bnode)) {
2663
0
          if (!g_slist_find(l, ((PurpleBuddy *)bnode)->account))
2664
0
            l = g_slist_append(l, ((PurpleBuddy *)bnode)->account);
2665
0
        }
2666
0
      }
2667
0
    }
2668
0
  }
2669
2670
0
  return l;
2671
0
}
2672
2673
void purple_blist_add_account(PurpleAccount *account)
2674
0
{
2675
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
2676
0
  PurpleBlistNode *gnode, *cnode, *bnode;
2677
2678
0
  g_return_if_fail(purplebuddylist != NULL);
2679
2680
0
  if (!ops || !ops->update)
2681
0
    return;
2682
2683
0
  for (gnode = purplebuddylist->root; gnode; gnode = gnode->next) {
2684
0
    if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
2685
0
      continue;
2686
0
    for (cnode = gnode->child; cnode; cnode = cnode->next) {
2687
0
      if (PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
2688
0
        gboolean recompute = FALSE;
2689
0
          for (bnode = cnode->child; bnode; bnode = bnode->next) {
2690
0
            if (PURPLE_BLIST_NODE_IS_BUDDY(bnode) &&
2691
0
                ((PurpleBuddy*)bnode)->account == account) {
2692
0
              recompute = TRUE;
2693
0
              ((PurpleContact*)cnode)->currentsize++;
2694
0
              if (((PurpleContact*)cnode)->currentsize == 1)
2695
0
                ((PurpleGroup*)gnode)->currentsize++;
2696
0
              ops->update(purplebuddylist, bnode);
2697
0
            }
2698
0
          }
2699
0
          if (recompute ||
2700
0
              purple_blist_node_get_bool(cnode, "show_offline")) {
2701
0
            purple_contact_invalidate_priority_buddy((PurpleContact*)cnode);
2702
0
            ops->update(purplebuddylist, cnode);
2703
0
          }
2704
0
      } else if (PURPLE_BLIST_NODE_IS_CHAT(cnode) &&
2705
0
          ((PurpleChat*)cnode)->account == account) {
2706
0
        ((PurpleGroup *)gnode)->online++;
2707
0
        ((PurpleGroup *)gnode)->currentsize++;
2708
0
        ops->update(purplebuddylist, cnode);
2709
0
      }
2710
0
    }
2711
0
    ops->update(purplebuddylist, gnode);
2712
0
  }
2713
0
}
2714
2715
void purple_blist_remove_account(PurpleAccount *account)
2716
0
{
2717
0
  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
2718
0
  PurpleBlistNode *gnode, *cnode, *bnode;
2719
0
  PurpleBuddy *buddy;
2720
0
  PurpleChat *chat;
2721
0
  PurpleContact *contact;
2722
0
  PurpleGroup *group;
2723
0
  GList *list = NULL, *iter = NULL;
2724
2725
0
  g_return_if_fail(purplebuddylist != NULL);
2726
2727
0
  for (gnode = purplebuddylist->root; gnode; gnode = gnode->next) {
2728
0
    if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
2729
0
      continue;
2730
2731
0
    group = (PurpleGroup *)gnode;
2732
2733
0
    for (cnode = gnode->child; cnode; cnode = cnode->next) {
2734
0
      if (PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
2735
0
        gboolean recompute = FALSE;
2736
0
        contact = (PurpleContact *)cnode;
2737
2738
0
        for (bnode = cnode->child; bnode; bnode = bnode->next) {
2739
0
          if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
2740
0
            continue;
2741
2742
0
          buddy = (PurpleBuddy *)bnode;
2743
0
          if (account == buddy->account) {
2744
0
            PurplePresence *presence;
2745
2746
0
            presence = purple_buddy_get_presence(buddy);
2747
2748
0
            if(purple_presence_is_online(presence)) {
2749
0
              contact->online--;
2750
0
              if (contact->online == 0)
2751
0
                group->online--;
2752
2753
0
              purple_blist_node_set_int(&buddy->node,
2754
0
                          "last_seen", time(NULL));
2755
0
            }
2756
2757
0
            contact->currentsize--;
2758
0
            if (contact->currentsize == 0)
2759
0
              group->currentsize--;
2760
2761
0
            if (!g_list_find(list, presence))
2762
0
              list = g_list_prepend(list, presence);
2763
2764
0
            if (contact->priority == buddy)
2765
0
              purple_contact_invalidate_priority_buddy(contact);
2766
0
            else
2767
0
              recompute = TRUE;
2768
2769
0
            if (ops && ops->remove) {
2770
0
              ops->remove(purplebuddylist, bnode);
2771
0
            }
2772
0
          }
2773
0
        }
2774
0
        if (recompute) {
2775
0
          purple_contact_invalidate_priority_buddy(contact);
2776
0
          if (ops && ops->update)
2777
0
            ops->update(purplebuddylist, cnode);
2778
0
        }
2779
0
      } else if (PURPLE_BLIST_NODE_IS_CHAT(cnode)) {
2780
0
        chat = (PurpleChat *)cnode;
2781
2782
0
        if(chat->account == account) {
2783
0
          group->currentsize--;
2784
0
          group->online--;
2785
2786
0
          if (ops && ops->remove)
2787
0
            ops->remove(purplebuddylist, cnode);
2788
0
        }
2789
0
      }
2790
0
    }
2791
0
  }
2792
2793
0
  for (iter = list; iter; iter = iter->next)
2794
0
  {
2795
0
    purple_presence_set_status_active(iter->data, "offline", TRUE);
2796
0
  }
2797
0
  g_list_free(list);
2798
0
}
2799
2800
gboolean purple_group_on_account(PurpleGroup *g, PurpleAccount *account)
2801
0
{
2802
0
  PurpleBlistNode *cnode;
2803
0
  for (cnode = ((PurpleBlistNode *)g)->child; cnode; cnode = cnode->next) {
2804
0
    if (PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
2805
0
      if(purple_contact_on_account((PurpleContact *) cnode, account))
2806
0
        return TRUE;
2807
0
    } else if (PURPLE_BLIST_NODE_IS_CHAT(cnode)) {
2808
0
      PurpleChat *chat = (PurpleChat *)cnode;
2809
0
      if ((!account && purple_account_is_connected(chat->account))
2810
0
          || chat->account == account)
2811
0
        return TRUE;
2812
0
    }
2813
0
  }
2814
0
  return FALSE;
2815
0
}
2816
2817
const char *purple_group_get_name(PurpleGroup *group)
2818
0
{
2819
0
  g_return_val_if_fail(group != NULL, NULL);
2820
2821
0
  return group->name;
2822
0
}
2823
2824
void
2825
purple_blist_request_add_buddy(PurpleAccount *account, const char *username,
2826
               const char *group, const char *alias)
2827
0
{
2828
0
  PurpleBlistUiOps *ui_ops;
2829
2830
0
  ui_ops = purple_blist_get_ui_ops();
2831
2832
0
  if (ui_ops != NULL && ui_ops->request_add_buddy != NULL)
2833
0
    ui_ops->request_add_buddy(account, username, group, alias);
2834
0
}
2835
2836
void
2837
purple_blist_request_add_chat(PurpleAccount *account, PurpleGroup *group,
2838
              const char *alias, const char *name)
2839
0
{
2840
0
  PurpleBlistUiOps *ui_ops;
2841
2842
0
  ui_ops = purple_blist_get_ui_ops();
2843
2844
0
  if (ui_ops != NULL && ui_ops->request_add_chat != NULL)
2845
0
    ui_ops->request_add_chat(account, group, alias, name);
2846
0
}
2847
2848
void
2849
purple_blist_request_add_group(void)
2850
0
{
2851
0
  PurpleBlistUiOps *ui_ops;
2852
2853
0
  ui_ops = purple_blist_get_ui_ops();
2854
2855
0
  if (ui_ops != NULL && ui_ops->request_add_group != NULL)
2856
0
    ui_ops->request_add_group();
2857
0
}
2858
2859
static void
2860
purple_blist_node_destroy(PurpleBlistNode *node)
2861
0
{
2862
0
  PurpleBlistUiOps *ui_ops;
2863
0
  PurpleBlistNode *child, *next_child;
2864
2865
0
  ui_ops = purple_blist_get_ui_ops();
2866
0
  child = node->child;
2867
0
  while (child) {
2868
0
    next_child = child->next;
2869
0
    purple_blist_node_destroy(child);
2870
0
    child = next_child;
2871
0
  }
2872
2873
  /* Allow the UI to free data */
2874
0
  node->parent = NULL;
2875
0
  node->child  = NULL;
2876
0
  node->next   = NULL;
2877
0
  node->prev   = NULL;
2878
0
  if (ui_ops && ui_ops->remove)
2879
0
    ui_ops->remove(purplebuddylist, node);
2880
2881
0
  if (PURPLE_BLIST_NODE_IS_BUDDY(node))
2882
0
    purple_buddy_destroy((PurpleBuddy*)node);
2883
0
  else if (PURPLE_BLIST_NODE_IS_CHAT(node))
2884
0
    purple_chat_destroy((PurpleChat*)node);
2885
0
  else if (PURPLE_BLIST_NODE_IS_CONTACT(node))
2886
0
    purple_contact_destroy((PurpleContact*)node);
2887
0
  else if (PURPLE_BLIST_NODE_IS_GROUP(node))
2888
0
    purple_group_destroy((PurpleGroup*)node);
2889
0
}
2890
2891
static void
2892
purple_blist_node_setting_free(gpointer data)
2893
0
{
2894
0
  PurpleValue *value;
2895
2896
0
  value = (PurpleValue *)data;
2897
2898
0
  purple_value_destroy(value);
2899
0
}
2900
2901
static void purple_blist_node_initialize_settings(PurpleBlistNode *node)
2902
0
{
2903
0
  if (node->settings)
2904
0
    return;
2905
2906
0
  node->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
2907
0
      (GDestroyNotify)purple_blist_node_setting_free);
2908
0
}
2909
2910
void purple_blist_node_remove_setting(PurpleBlistNode *node, const char *key)
2911
0
{
2912
0
  PurpleBlistUiOps *ops;
2913
0
  g_return_if_fail(node != NULL);
2914
0
  g_return_if_fail(node->settings != NULL);
2915
0
  g_return_if_fail(key != NULL);
2916
2917
0
  g_hash_table_remove(node->settings, key);
2918
2919
0
  ops = purple_blist_get_ui_ops();
2920
0
  if (ops && ops->save_node)
2921
0
    ops->save_node(node);
2922
0
}
2923
2924
void
2925
purple_blist_node_set_flags(PurpleBlistNode *node, PurpleBlistNodeFlags flags)
2926
0
{
2927
0
  g_return_if_fail(node != NULL);
2928
2929
0
  node->flags = flags;
2930
0
}
2931
2932
PurpleBlistNodeFlags
2933
purple_blist_node_get_flags(PurpleBlistNode *node)
2934
0
{
2935
0
  g_return_val_if_fail(node != NULL, 0);
2936
2937
0
  return node->flags;
2938
0
}
2939
2940
PurpleBlistNodeType
2941
purple_blist_node_get_type(PurpleBlistNode *node)
2942
0
{
2943
0
  g_return_val_if_fail(node != NULL, PURPLE_BLIST_OTHER_NODE);
2944
0
  return node->type;
2945
0
}
2946
2947
void
2948
purple_blist_node_set_bool(PurpleBlistNode* node, const char *key, gboolean data)
2949
0
{
2950
0
  PurpleValue *value;
2951
0
  PurpleBlistUiOps *ops;
2952
2953
0
  g_return_if_fail(node != NULL);
2954
0
  g_return_if_fail(node->settings != NULL);
2955
0
  g_return_if_fail(key != NULL);
2956
2957
0
  value = purple_value_new(PURPLE_TYPE_BOOLEAN);
2958
0
  purple_value_set_boolean(value, data);
2959
2960
0
  g_hash_table_replace(node->settings, g_strdup(key), value);
2961
2962
0
  ops = purple_blist_get_ui_ops();
2963
0
  if (ops && ops->save_node)
2964
0
    ops->save_node(node);
2965
0
}
2966
2967
gboolean
2968
purple_blist_node_get_bool(PurpleBlistNode* node, const char *key)
2969
0
{
2970
0
  PurpleValue *value;
2971
2972
0
  g_return_val_if_fail(node != NULL, FALSE);
2973
0
  g_return_val_if_fail(node->settings != NULL, FALSE);
2974
0
  g_return_val_if_fail(key != NULL, FALSE);
2975
2976
0
  value = g_hash_table_lookup(node->settings, key);
2977
2978
0
  if (value == NULL)
2979
0
    return FALSE;
2980
2981
0
  g_return_val_if_fail(purple_value_get_type(value) == PURPLE_TYPE_BOOLEAN, FALSE);
2982
2983
0
  return purple_value_get_boolean(value);
2984
0
}
2985
2986
void
2987
purple_blist_node_set_int(PurpleBlistNode* node, const char *key, int data)
2988
0
{
2989
0
  PurpleValue *value;
2990
0
  PurpleBlistUiOps *ops;
2991
2992
0
  g_return_if_fail(node != NULL);
2993
0
  g_return_if_fail(node->settings != NULL);
2994
0
  g_return_if_fail(key != NULL);
2995
2996
0
  value = purple_value_new(PURPLE_TYPE_INT);
2997
0
  purple_value_set_int(value, data);
2998
2999
0
  g_hash_table_replace(node->settings, g_strdup(key), value);
3000
3001
0
  ops = purple_blist_get_ui_ops();
3002
0
  if (ops && ops->save_node)
3003
0
    ops->save_node(node);
3004
0
}
3005
3006
int
3007
purple_blist_node_get_int(PurpleBlistNode* node, const char *key)
3008
0
{
3009
0
  PurpleValue *value;
3010
3011
0
  g_return_val_if_fail(node != NULL, 0);
3012
0
  g_return_val_if_fail(node->settings != NULL, 0);
3013
0
  g_return_val_if_fail(key != NULL, 0);
3014
3015
0
  value = g_hash_table_lookup(node->settings, key);
3016
3017
0
  if (value == NULL)
3018
0
    return 0;
3019
3020
0
  g_return_val_if_fail(purple_value_get_type(value) == PURPLE_TYPE_INT, 0);
3021
3022
0
  return purple_value_get_int(value);
3023
0
}
3024
3025
void
3026
purple_blist_node_set_string(PurpleBlistNode* node, const char *key, const char *data)
3027
0
{
3028
0
  PurpleValue *value;
3029
0
  PurpleBlistUiOps *ops;
3030
3031
0
  g_return_if_fail(node != NULL);
3032
0
  g_return_if_fail(node->settings != NULL);
3033
0
  g_return_if_fail(key != NULL);
3034
3035
0
  value = purple_value_new(PURPLE_TYPE_STRING);
3036
0
  purple_value_set_string(value, data);
3037
3038
0
  g_hash_table_replace(node->settings, g_strdup(key), value);
3039
3040
0
  ops = purple_blist_get_ui_ops();
3041
0
  if (ops && ops->save_node)
3042
0
    ops->save_node(node);
3043
0
}
3044
3045
const char *
3046
purple_blist_node_get_string(PurpleBlistNode* node, const char *key)
3047
0
{
3048
0
  PurpleValue *value;
3049
3050
0
  g_return_val_if_fail(node != NULL, NULL);
3051
0
  g_return_val_if_fail(node->settings != NULL, NULL);
3052
0
  g_return_val_if_fail(key != NULL, NULL);
3053
3054
0
  value = g_hash_table_lookup(node->settings, key);
3055
3056
0
  if (value == NULL)
3057
0
    return NULL;
3058
3059
0
  g_return_val_if_fail(purple_value_get_type(value) == PURPLE_TYPE_STRING, NULL);
3060
3061
0
  return purple_value_get_string(value);
3062
0
}
3063
3064
GList *
3065
purple_blist_node_get_extended_menu(PurpleBlistNode *n)
3066
0
{
3067
0
  GList *menu = NULL;
3068
3069
0
  g_return_val_if_fail(n != NULL, NULL);
3070
3071
0
  purple_signal_emit(purple_blist_get_handle(),
3072
0
      "blist-node-extended-menu",
3073
0
      n, &menu);
3074
0
  return menu;
3075
0
}
3076
3077
int purple_blist_get_group_size(PurpleGroup *group, gboolean offline)
3078
0
{
3079
0
  if (!group)
3080
0
    return 0;
3081
3082
0
  return offline ? group->totalsize : group->currentsize;
3083
0
}
3084
3085
int purple_blist_get_group_online_count(PurpleGroup *group)
3086
0
{
3087
0
  if (!group)
3088
0
    return 0;
3089
3090
0
  return group->online;
3091
0
}
3092
3093
void
3094
purple_blist_set_ui_ops(PurpleBlistUiOps *ops)
3095
0
{
3096
0
  gboolean overrode = FALSE;
3097
0
  blist_ui_ops = ops;
3098
3099
0
  if (!ops)
3100
0
    return;
3101
3102
0
  if (!ops->save_node) {
3103
0
    ops->save_node = purple_blist_save_node;
3104
0
    overrode = TRUE;
3105
0
  }
3106
0
  if (!ops->remove_node) {
3107
0
    ops->remove_node = purple_blist_save_node;
3108
0
    overrode = TRUE;
3109
0
  }
3110
0
  if (!ops->save_account) {
3111
0
    ops->save_account = purple_blist_save_account;
3112
0
    overrode = TRUE;
3113
0
  }
3114
3115
0
  if (overrode && (ops->save_node    != purple_blist_save_node ||
3116
0
                   ops->remove_node  != purple_blist_save_node ||
3117
0
                   ops->save_account != purple_blist_save_account)) {
3118
0
    purple_debug_warning("blist", "Only some of the blist saving UI ops "
3119
0
        "were overridden. This probably is not what you want!\n");
3120
0
  }
3121
0
}
3122
3123
PurpleBlistUiOps *
3124
purple_blist_get_ui_ops(void)
3125
0
{
3126
0
  return blist_ui_ops;
3127
0
}
3128
3129
3130
void *
3131
purple_blist_get_handle(void)
3132
0
{
3133
0
  static int handle;
3134
3135
0
  return &handle;
3136
0
}
3137
3138
void
3139
purple_blist_init(void)
3140
0
{
3141
0
  void *handle = purple_blist_get_handle();
3142
3143
0
  purple_signal_register(handle, "buddy-status-changed",
3144
0
                       purple_marshal_VOID__POINTER_POINTER_POINTER, NULL,
3145
0
                       3,
3146
0
                       purple_value_new(PURPLE_TYPE_SUBTYPE,
3147
0
                                      PURPLE_SUBTYPE_BLIST_BUDDY),
3148
0
                       purple_value_new(PURPLE_TYPE_SUBTYPE,
3149
0
                    PURPLE_SUBTYPE_STATUS),
3150
0
                       purple_value_new(PURPLE_TYPE_SUBTYPE,
3151
0
                    PURPLE_SUBTYPE_STATUS));
3152
0
  purple_signal_register(handle, "buddy-privacy-changed",
3153
0
                       purple_marshal_VOID__POINTER, NULL,
3154
0
                       1,
3155
0
                       purple_value_new(PURPLE_TYPE_SUBTYPE,
3156
0
                                      PURPLE_SUBTYPE_BLIST_BUDDY));
3157
3158
0
  purple_signal_register(handle, "buddy-idle-changed",
3159
0
                       purple_marshal_VOID__POINTER_INT_INT, NULL,
3160
0
                       3,
3161
0
                       purple_value_new(PURPLE_TYPE_SUBTYPE,
3162
0
                                      PURPLE_SUBTYPE_BLIST_BUDDY),
3163
0
                       purple_value_new(PURPLE_TYPE_INT),
3164
0
                       purple_value_new(PURPLE_TYPE_INT));
3165
3166
3167
0
  purple_signal_register(handle, "buddy-signed-on",
3168
0
             purple_marshal_VOID__POINTER, NULL, 1,
3169
0
             purple_value_new(PURPLE_TYPE_SUBTYPE,
3170
0
                    PURPLE_SUBTYPE_BLIST_BUDDY));
3171
3172
0
  purple_signal_register(handle, "buddy-signed-off",
3173
0
             purple_marshal_VOID__POINTER, NULL, 1,
3174
0
             purple_value_new(PURPLE_TYPE_SUBTYPE,
3175
0
                    PURPLE_SUBTYPE_BLIST_BUDDY));
3176
3177
0
  purple_signal_register(handle, "buddy-got-login-time",
3178
0
             purple_marshal_VOID__POINTER, NULL, 1,
3179
0
             purple_value_new(PURPLE_TYPE_SUBTYPE,
3180
0
                    PURPLE_SUBTYPE_BLIST_BUDDY));
3181
3182
0
  purple_signal_register(handle, "blist-node-added",
3183
0
             purple_marshal_VOID__POINTER, NULL, 1,
3184
0
             purple_value_new(PURPLE_TYPE_SUBTYPE,
3185
0
                    PURPLE_SUBTYPE_BLIST_NODE));
3186
3187
0
  purple_signal_register(handle, "blist-node-removed",
3188
0
             purple_marshal_VOID__POINTER, NULL, 1,
3189
0
             purple_value_new(PURPLE_TYPE_SUBTYPE,
3190
0
                    PURPLE_SUBTYPE_BLIST_NODE));
3191
3192
0
  purple_signal_register(handle, "buddy-added",
3193
0
             purple_marshal_VOID__POINTER, NULL, 1,
3194
0
             purple_value_new(PURPLE_TYPE_SUBTYPE,
3195
0
                    PURPLE_SUBTYPE_BLIST_BUDDY));
3196
3197
0
  purple_signal_register(handle, "buddy-removed",
3198
0
             purple_marshal_VOID__POINTER, NULL, 1,
3199
0
             purple_value_new(PURPLE_TYPE_SUBTYPE,
3200
0
                    PURPLE_SUBTYPE_BLIST_BUDDY));
3201
3202
0
  purple_signal_register(handle, "buddy-removed-from-group",
3203
0
             purple_marshal_VOID__POINTER, NULL, 1,
3204
0
             purple_value_new(PURPLE_TYPE_SUBTYPE,
3205
0
                    PURPLE_SUBTYPE_BLIST_BUDDY));
3206
3207
0
  purple_signal_register(handle, "buddy-icon-changed",
3208
0
             purple_marshal_VOID__POINTER, NULL, 1,
3209
0
             purple_value_new(PURPLE_TYPE_SUBTYPE,
3210
0
                    PURPLE_SUBTYPE_BLIST_BUDDY));
3211
3212
0
  purple_signal_register(handle, "update-idle", purple_marshal_VOID, NULL, 0);
3213
3214
0
  purple_signal_register(handle, "blist-node-extended-menu",
3215
0
           purple_marshal_VOID__POINTER_POINTER, NULL, 2,
3216
0
           purple_value_new(PURPLE_TYPE_SUBTYPE,
3217
0
              PURPLE_SUBTYPE_BLIST_NODE),
3218
0
           purple_value_new(PURPLE_TYPE_BOXED, "GList **"));
3219
3220
0
  purple_signal_register(handle, "blist-node-aliased",
3221
0
             purple_marshal_VOID__POINTER_POINTER, NULL, 2,
3222
0
             purple_value_new(PURPLE_TYPE_SUBTYPE,
3223
0
                    PURPLE_SUBTYPE_BLIST_NODE),
3224
0
             purple_value_new(PURPLE_TYPE_STRING));
3225
3226
0
  purple_signal_register(handle, "buddy-caps-changed",
3227
0
      purple_marshal_VOID__POINTER_INT_INT, NULL,
3228
0
      3, purple_value_new(PURPLE_TYPE_SUBTYPE,
3229
0
        PURPLE_SUBTYPE_BLIST_BUDDY),
3230
0
      purple_value_new(PURPLE_TYPE_INT),
3231
0
      purple_value_new(PURPLE_TYPE_INT));
3232
3233
0
  purple_signal_connect(purple_accounts_get_handle(), "account-created",
3234
0
      handle,
3235
0
      PURPLE_CALLBACK(purple_blist_buddies_cache_add_account),
3236
0
      NULL);
3237
3238
0
  purple_signal_connect(purple_accounts_get_handle(), "account-destroying",
3239
0
      handle,
3240
0
      PURPLE_CALLBACK(purple_blist_buddies_cache_remove_account),
3241
0
      NULL);
3242
0
}
3243
3244
void
3245
purple_blist_uninit(void)
3246
0
{
3247
0
  PurpleBlistNode *node, *next_node;
3248
3249
  /* This happens if we quit before purple_set_blist is called. */
3250
0
  if (purplebuddylist == NULL)
3251
0
    return;
3252
3253
0
  if (save_timer != 0) {
3254
0
    purple_timeout_remove(save_timer);
3255
0
    save_timer = 0;
3256
0
    purple_blist_sync();
3257
0
  }
3258
3259
0
  purple_blist_destroy();
3260
3261
0
  node = purple_blist_get_root();
3262
0
  while (node) {
3263
0
    next_node = node->next;
3264
0
    purple_blist_node_destroy(node);
3265
0
    node = next_node;
3266
0
  }
3267
0
  purplebuddylist->root = NULL;
3268
3269
0
  g_hash_table_destroy(purplebuddylist->buddies);
3270
0
  g_hash_table_destroy(buddies_cache);
3271
0
  g_hash_table_destroy(groups_cache);
3272
3273
0
  buddies_cache = NULL;
3274
0
  groups_cache = NULL;
3275
3276
0
  PURPLE_DBUS_UNREGISTER_POINTER(purplebuddylist);
3277
0
  g_free(purplebuddylist);
3278
0
  purplebuddylist = NULL;
3279
3280
0
  purple_signals_disconnect_by_handle(purple_blist_get_handle());
3281
0
  purple_signals_unregister_by_instance(purple_blist_get_handle());
3282
0
}