1 /*
2 * roster.c -- Local roster implementation
3 *
4 * Copyright (C) 2005-2010 Mikael Berthe <mikael@lilotux.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <string.h>
21
22 #include "roster.h"
23 #include "utils.h"
24 #include "hooks.h"
25
26 extern void hlog_save_state(void);
27
28 char *strrole[] = { /* Should match enum in roster.h */
29 "none",
30 "moderator",
31 "participant",
32 "visitor"
33 };
34
35 char *straffil[] = { /* Should match enum in roster.h */
36 "none",
37 "owner",
38 "admin",
39 "member",
40 "outcast"
41 };
42
43 char *strprintstatus[] = { /* Should match enum in roster.h */
44 "default",
45 "none",
46 "in_and_out",
47 "all"
48 };
49
50 char *strautowhois[] = { /* Should match enum in roster.h */
51 "default",
52 "off",
53 "on",
54 };
55
56 char *strflagjoins[] = { /* Should match enum in roster.h */
57 "default",
58 "none",
59 "joins",
60 "all"
61 };
62
63 /* Resource structure */
64
65 typedef struct {
66 gchar *name;
67 gchar prio;
68 enum imstatus status;
69 gchar *status_msg;
70 time_t status_timestamp;
71 enum imrole role;
72 enum imaffiliation affil;
73 gchar *realjid; /* for chatrooms, if buddy's real jid is known */
74 guint events;
75 char *caps;
76 #ifdef XEP0085
77 struct xep0085 xep85;
78 #endif
79 #ifdef HAVE_GPGME
80 struct pgp_data pgpdata;
81 #endif
82 } res_t;
83
84 /* This is a private structure type for the roster_t */
85
86 typedef struct {
87 gchar *name;
88 gchar *jid;
89 guint type;
90 enum subscr subscription;
91 GSList *resource;
92 res_t *active_resource;
93
94 /* For groupchats */
95 gchar *nickname;
96 gchar *topic;
97 guint inside_room;
98 guint print_status;
99 guint auto_whois;
100 guint flag_joins;
101
102 /* on_server is TRUE if the item is present on the server roster */
103 guint on_server;
104
105 /* To keep track of last status message */
106 gchar *offline_status_message;
107
108 /* Flag used for the UI */
109 guint flags;
110 guint ui_prio; // Boolean, positive if "attention" is requested
111 guint unread;
112
113 // list: user -> points to his group; group -> points to its users list
114 GSList *list;
115 } roster_t;
116
117
118 /* ### Variables ### */
119
120 static guchar display_filter;
121 static GSList *groups;
122 static GSList *unread_list;
123 static GHashTable *unread_jids;
124 GList *buddylist;
125 static gboolean _rebuild_buddylist = FALSE;
126 GList *current_buddy;
127 GList *alternate_buddy;
128 GList *last_activity_buddy;
129
130 static roster_t roster_special;
131
132 static int unread_jid_del(const char *jid);
133
134 #define DFILTER_ALL 63
135 #define DFILTER_ONLINE 62
136
137
138 /* ### Initialization ### */
139
roster_init(void)140 void roster_init(void)
141 {
142 roster_special.name = SPECIAL_BUFFER_STATUS_ID;
143 roster_special.type = ROSTER_TYPE_SPECIAL;
144 }
145
146 /* ### Resources functions ### */
147
free_resource_data(res_t * p_res)148 static inline void free_resource_data(res_t *p_res)
149 {
150 if (!p_res)
151 return;
152 g_free((gchar*)p_res->status_msg);
153 g_free((gchar*)p_res->name);
154 g_free((gchar*)p_res->realjid);
155 #ifdef HAVE_GPGME
156 g_free(p_res->pgpdata.sign_keyid);
157 #endif
158 g_free(p_res->caps);
159 g_free(p_res);
160 }
161
free_all_resources(GSList ** reslist)162 static void free_all_resources(GSList **reslist)
163 {
164 GSList *lip;
165
166 for (lip = *reslist; lip ; lip = g_slist_next(lip))
167 free_resource_data((res_t *)lip->data);
168 // Free all nodes but the first (which is static)
169 g_slist_free(*reslist);
170 *reslist = NULL;
171 }
172
173 // Resources are sorted in ascending order
resource_compare_prio(res_t * a,res_t * b)174 static gint resource_compare_prio(res_t *a, res_t *b) {
175 if (a->prio < b->prio) return -1;
176 else return 1;
177 }
178
179 // get_resource(rost, resname)
180 // Return a pointer to the resource with name resname, in rost's resources list
181 // - if rost has no resources, return NULL
182 // - if resname is defined, return the match or NULL
183 // - if resname is NULL, the last resource is returned, currently
184 // This could change in the future, because we should return the best one
185 // (priority? last used? and fall back to the first resource)
186 //
get_resource(roster_t * rost,const char * resname)187 static res_t *get_resource(roster_t *rost, const char *resname)
188 {
189 GSList *p;
190 res_t *r = NULL;
191
192 for (p = rost->resource; p; p = g_slist_next(p)) {
193 r = p->data;
194 if (resname && !strcmp(r->name, resname))
195 return r;
196 }
197
198 // The last resource is one of the resources with the highest priority,
199 // however, we don't know if it is the more-recently-used.
200 if (!resname) return r;
201 return NULL;
202 }
203
204 // get_or_add_resource(rost, resname, priority)
205 // - if there is a "resname" resource in rost's resources, return a pointer
206 // on this resource
207 // - if not, add the resource, set the name, and return a pointer on this
208 // new resource
get_or_add_resource(roster_t * rost,const char * resname,gchar prio)209 static res_t *get_or_add_resource(roster_t *rost, const char *resname, gchar prio)
210 {
211 GSList *p;
212 res_t *nres;
213
214 if (!resname) return NULL;
215
216 for (p = rost->resource; p; p = g_slist_next(p)) {
217 res_t *r = p->data;
218 if (!strcmp(r->name, resname)) {
219 if (prio != r->prio) {
220 r->prio = prio;
221 rost->resource = g_slist_sort(rost->resource,
222 (GCompareFunc)&resource_compare_prio);
223 }
224 return r;
225 }
226 }
227
228 // Resource not found
229 nres = g_new0(res_t, 1);
230 nres->name = g_strdup(resname);
231 nres->prio = prio;
232 rost->resource = g_slist_insert_sorted(rost->resource, nres,
233 (GCompareFunc)&resource_compare_prio);
234 return nres;
235 }
236
del_resource(roster_t * rost,const char * resname)237 static void del_resource(roster_t *rost, const char *resname)
238 {
239 GSList *p;
240 GSList *p_res_elt = NULL;
241 res_t *p_res;
242
243 if (!resname) return;
244
245 for (p = rost->resource; p; p = g_slist_next(p)) {
246 res_t *r = p->data;
247 if (!strcmp(r->name, resname))
248 p_res_elt = p;
249 }
250
251 if (!p_res_elt) return; // Resource not found
252
253 p_res = p_res_elt->data;
254
255 // Keep a copy of the status message when a buddy goes offline
256 if (g_slist_length(rost->resource) == 1) {
257 g_free(rost->offline_status_message);
258 rost->offline_status_message = p_res->status_msg;
259 p_res->status_msg = NULL;
260 }
261
262 if (rost->active_resource == p_res)
263 rost->active_resource = NULL;
264
265 // Free allocations and delete resource node
266 free_resource_data(p_res);
267 rost->resource = g_slist_delete_link(rost->resource, p_res_elt);
268 return;
269 }
270
271
272 /* ### Roster functions ### */
273
free_roster_user_data(roster_t * roster_usr)274 static inline void free_roster_user_data(roster_t *roster_usr)
275 {
276 if (!roster_usr)
277 return;
278 g_free((gchar*)roster_usr->jid);
279 //g_free((gchar*)roster_usr->active_resource);
280 g_free((gchar*)roster_usr->name);
281 g_free((gchar*)roster_usr->nickname);
282 g_free((gchar*)roster_usr->topic);
283 g_free((gchar*)roster_usr->offline_status_message);
284 free_all_resources(&roster_usr->resource);
285 g_free(roster_usr);
286 }
287
288 // Comparison function used to search in the roster (compares jids and types)
roster_compare_jid_type(roster_t * a,roster_t * b)289 static gint roster_compare_jid_type(roster_t *a, roster_t *b) {
290 if (! (a->type & b->type))
291 return -1; // arbitrary (but should be != 0, of course)
292 return strcasecmp(a->jid, b->jid);
293 }
294
295 // Comparison function used to search in the roster (compares names and types)
roster_compare_name_type(roster_t * a,roster_t * b)296 static gint roster_compare_name_type(roster_t *a, roster_t *b) {
297 if (! (a->type & b->type))
298 return -1; // arbitrary (but should be != 0, of course)
299 return strcmp(a->name, b->name);
300 }
301
302 // Comparison function used to sort the roster (by name)
roster_compare_name(roster_t * a,roster_t * b)303 static gint roster_compare_name(roster_t *a, roster_t *b) {
304 return strcmp(a->name, b->name);
305 }
306
307 // Finds a roster element (user, group, agent...), by jid or name
308 // If roster_type is 0, returns match of any type.
309 // Returns the roster GSList element, or NULL if jid/name not found
roster_find(const char * jidname,enum findwhat type,guint roster_type)310 GSList *roster_find(const char *jidname, enum findwhat type, guint roster_type)
311 {
312 GSList *sl_roster_elt = groups;
313 GSList *resource;
314 roster_t sample;
315 GCompareFunc comp;
316
317 if (!jidname) return NULL;
318
319 if (!roster_type)
320 roster_type = ROSTER_TYPE_USER | ROSTER_TYPE_ROOM |
321 ROSTER_TYPE_AGENT | ROSTER_TYPE_GROUP;
322
323 sample.type = roster_type;
324 if (type == jidsearch) {
325 sample.jid = (gchar*)jidname;
326 comp = (GCompareFunc)&roster_compare_jid_type;
327 } else if (type == namesearch) {
328 sample.name = (gchar*)jidname;
329 comp = (GCompareFunc)&roster_compare_name_type;
330 } else
331 return NULL; // Should not happen...
332
333 while (sl_roster_elt) {
334 roster_t *roster_elt = (roster_t *)sl_roster_elt->data;
335 if (roster_type & ROSTER_TYPE_GROUP) {
336 if ((type == namesearch) && !strcmp(jidname, roster_elt->name))
337 return sl_roster_elt;
338 }
339 resource = g_slist_find_custom(roster_elt->list, &sample, comp);
340 if (resource) return resource;
341 sl_roster_elt = g_slist_next(sl_roster_elt);
342 }
343 return NULL;
344 }
345
346 // Returns pointer to new group, or existing group with that name
roster_add_group(const char * name)347 GSList *roster_add_group(const char *name)
348 {
349 roster_t *roster_grp;
350 GSList *p_group;
351
352 // #1 Check name doesn't already exist
353 p_group = roster_find(name, namesearch, ROSTER_TYPE_GROUP);
354 if (!p_group) {
355 // #2 Create the group node
356 roster_grp = g_new0(roster_t, 1);
357 roster_grp->name = g_strdup(name);
358 roster_grp->type = ROSTER_TYPE_GROUP;
359 // #3 Insert (sorted)
360 groups = g_slist_insert_sorted(groups, roster_grp,
361 (GCompareFunc)&roster_compare_name);
362 p_group = roster_find(name, namesearch, ROSTER_TYPE_GROUP);
363 }
364 return p_group;
365 }
366
367 // Comparison function used to sort the unread list by ui (attn) priority
_roster_compare_uiprio(roster_t * a,roster_t * b)368 static gint _roster_compare_uiprio(roster_t *a, roster_t *b) {
369 return (b->ui_prio - a->ui_prio);
370 }
371
372 // Returns a pointer to the new user, or existing user with that name
373 // Note: if onserver is -1, the flag won't be changed.
roster_add_user(const char * jid,const char * name,const char * group,guint type,enum subscr esub,gint onserver)374 GSList *roster_add_user(const char *jid, const char *name, const char *group,
375 guint type, enum subscr esub, gint onserver)
376 {
377 roster_t *roster_usr;
378 roster_t *my_group;
379 GSList *slist;
380
381 if ((type != ROSTER_TYPE_USER) &&
382 (type != ROSTER_TYPE_ROOM) &&
383 (type != ROSTER_TYPE_AGENT)) {
384 // XXX Error message?
385 return NULL;
386 }
387
388 // Let's be arbitrary: default group has an empty name ("").
389 if (!group) group = "";
390
391 // #1 Check this user doesn't already exist
392 slist = roster_find(jid, jidsearch, 0);
393 if (slist) {
394 char *oldgroupname;
395 // That's an update
396 roster_usr = slist->data;
397 roster_usr->subscription = esub;
398 if (onserver >= 0)
399 buddy_setonserverflag(slist->data, onserver);
400 if (name)
401 buddy_setname(slist->data, (char*)name);
402 // Let's check if the group name has changed
403 oldgroupname = ((roster_t *)((GSList*)roster_usr->list)->data)->name;
404 if (group && strcmp(oldgroupname, group)) {
405 buddy_setgroup(slist->data, (char*)group);
406 // Note: buddy_setgroup() updates the user lists so we cannot
407 // use slist anymore.
408 return roster_find(jid, jidsearch, 0);
409 }
410 return slist;
411 }
412 // #2 add group if necessary
413 slist = roster_add_group(group);
414 if (!slist) return NULL;
415 my_group = (roster_t *)slist->data;
416 // #3 Create user node
417 roster_usr = g_new0(roster_t, 1);
418 roster_usr->jid = g_strdup(jid);
419 if (name) {
420 roster_usr->name = g_strdup(name);
421 } else {
422 roster_usr->name = jidtodisp(jid);
423 }
424 if (unread_jid_del(jid)) {
425 roster_usr->flags |= ROSTER_FLAG_MSG;
426 // Append the roster_usr to unread_list
427 unread_list = g_slist_insert_sorted(unread_list, roster_usr,
428 (GCompareFunc)&_roster_compare_uiprio);
429 }
430 roster_usr->type = type;
431 roster_usr->subscription = esub;
432 roster_usr->list = slist; // (my_group SList element)
433 if (onserver == 1)
434 roster_usr->on_server = TRUE;
435 // #4 Insert node (sorted)
436 my_group->list = g_slist_insert_sorted(my_group->list, roster_usr,
437 (GCompareFunc)&roster_compare_name);
438 buddylist_defer_build();
439 return roster_find(jid, jidsearch, type);
440 }
441
442 // Removes user (jid) from roster, frees allocated memory
roster_del_user(const char * jid)443 void roster_del_user(const char *jid)
444 {
445 GSList *sl_user, *sl_group;
446 GSList **sl_group_listptr;
447 roster_t *roster_usr;
448 GSList *node;
449
450 sl_user = roster_find(jid, jidsearch,
451 ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM);
452 if (sl_user == NULL)
453 return;
454 roster_usr = (roster_t *)sl_user->data;
455
456 // Remove (if present) from unread messages list
457 node = g_slist_find(unread_list, roster_usr);
458 if (node) unread_list = g_slist_delete_link(unread_list, node);
459 // If there is a pending unread message, keep track of it
460 if (roster_usr->flags & ROSTER_FLAG_MSG)
461 unread_jid_add(roster_usr->jid);
462
463 sl_group = roster_usr->list;
464
465 // Let's free roster_usr memory (jid, name, status message...)
466 free_roster_user_data(roster_usr);
467
468 // That's a little complex, we need to dereference twice
469 sl_group_listptr = &((roster_t *)(sl_group->data))->list;
470 *sl_group_listptr = g_slist_delete_link(*sl_group_listptr, sl_user);
471
472 // We need to rebuild the list
473 if (current_buddy)
474 buddylist_defer_build();
475 // TODO What we could do, too, is to check if the deleted node is
476 // current_buddy, in which case we could move current_buddy to the
477 // previous (or next) node.
478 }
479
480 // Free all roster data and call buddylist_build() to free the buddylist.
roster_free(void)481 void roster_free(void)
482 {
483 GSList *sl_grp = groups;
484
485 // Free unread_list
486 if (unread_list) {
487 g_slist_free(unread_list);
488 unread_list = NULL;
489 }
490
491 // Walk through groups
492 while (sl_grp) {
493 roster_t *roster_grp = (roster_t *)sl_grp->data;
494 GSList *sl_usr = roster_grp->list;
495 // Walk through this group users
496 while (sl_usr) {
497 roster_t *roster_usr = (roster_t *)sl_usr->data;
498 // If there is a pending unread message, keep track of it
499 if (roster_usr->flags & ROSTER_FLAG_MSG)
500 unread_jid_add(roster_usr->jid);
501 // Free roster_usr data (jid, name, status message...)
502 free_roster_user_data(roster_usr);
503 sl_usr = g_slist_next(sl_usr);
504 }
505 // Free group's users list
506 if (roster_grp->list)
507 g_slist_free(roster_grp->list);
508 // Free group's name and jid
509 g_free((gchar*)roster_grp->jid);
510 g_free((gchar*)roster_grp->name);
511 g_free(roster_grp);
512 sl_grp = g_slist_next(sl_grp);
513 }
514 // Free groups list
515 if (groups) {
516 g_slist_free(groups);
517 groups = NULL;
518 // Update (i.e. free) buddylist
519 if (buddylist)
520 buddylist_defer_build();
521 }
522 }
523
524 // roster_setstatus()
525 // Note: resname, role, affil and realjid are for room members only
roster_setstatus(const char * jid,const char * resname,gchar prio,enum imstatus bstat,const char * status_msg,time_t status_time,enum imrole role,enum imaffiliation affil,const char * realjid)526 void roster_setstatus(const char *jid, const char *resname, gchar prio,
527 enum imstatus bstat, const char *status_msg,
528 time_t status_time,
529 enum imrole role, enum imaffiliation affil,
530 const char *realjid)
531 {
532 GSList *sl_user;
533 roster_t *roster_usr;
534 res_t *p_res;
535
536 sl_user = roster_find(jid, jidsearch,
537 ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
538 // If we can't find it, we add it
539 if (sl_user == NULL)
540 sl_user = roster_add_user(jid, NULL, NULL, ROSTER_TYPE_USER,
541 sub_none, -1);
542
543 // If there is no resource name, we can leave now
544 if (!resname) return;
545
546 roster_usr = (roster_t *)sl_user->data;
547
548 // New or updated resource
549 p_res = get_or_add_resource(roster_usr, resname, prio);
550 p_res->status = bstat;
551 if (p_res->status_msg) {
552 g_free((gchar*)p_res->status_msg);
553 p_res->status_msg = NULL;
554 }
555 if (status_msg)
556 p_res->status_msg = g_strdup(status_msg);
557 if (!status_time)
558 time(&status_time);
559 p_res->status_timestamp = status_time;
560
561 p_res->role = role;
562 p_res->affil = affil;
563
564 if (p_res->realjid) {
565 g_free((gchar*)p_res->realjid);
566 p_res->realjid = NULL;
567 }
568 if (realjid)
569 p_res->realjid = g_strdup(realjid);
570
571 // If bstat is offline, we MUST delete the resource, actually
572 if (bstat == offline) {
573 del_resource(roster_usr, resname);
574 return;
575 }
576 }
577
578 // roster_setflags()
579 // Set one or several flags to value (TRUE/FALSE)
roster_setflags(const char * jid,guint flags,guint value)580 void roster_setflags(const char *jid, guint flags, guint value)
581 {
582 GSList *sl_user;
583 roster_t *roster_usr;
584
585 sl_user = roster_find(jid, jidsearch,
586 ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
587 if (sl_user == NULL)
588 return;
589
590 roster_usr = (roster_t *)sl_user->data;
591 if (value)
592 roster_usr->flags |= flags;
593 else
594 roster_usr->flags &= ~flags;
595 }
596
597 // roster_unread_check()
roster_unread_check(void)598 static void roster_unread_check(void)
599 {
600 guint unread_count = 0;
601 gpointer unread_ptr, first_unread;
602 guint muc_unread = 0, muc_attention = 0;
603 guint attention_count = 0;
604
605 unread_ptr = first_unread = unread_msg(NULL);
606 if (first_unread) {
607 do {
608 guint type = buddy_gettype(unread_ptr);
609 unread_count++;
610
611 if (type & ROSTER_TYPE_ROOM) {
612 muc_unread++;
613 if (buddy_getuiprio(unread_ptr) >= ROSTER_UI_PRIO_MUC_HL_MESSAGE)
614 muc_attention++;
615 } else {
616 if (buddy_getuiprio(unread_ptr) >= ROSTER_UI_PRIO_ATTENTION_MESSAGE)
617 attention_count++;
618 }
619 unread_ptr = unread_msg(unread_ptr);
620 } while (unread_ptr && unread_ptr != first_unread);
621 }
622
623 hk_unread_list_change(unread_count, attention_count,
624 muc_unread, muc_attention);
625 }
626
627 // roster_msg_setflag()
628 // Set the ROSTER_FLAG_MSG to the given value for the given jid.
629 // It will update the buddy's group message flag.
630 // Update the unread messages list too.
roster_msg_setflag(const char * jid,guint special,guint value)631 void roster_msg_setflag(const char *jid, guint special, guint value)
632 {
633 GSList *sl_user;
634 roster_t *roster_usr, *roster_grp;
635 int new_roster_item = FALSE;
636 guint unread_list_modified = FALSE;
637
638 if (special) {
639 //sl_user = roster_find(jid, namesearch, ROSTER_TYPE_SPECIAL);
640 //if (!sl_user) return;
641 //roster_usr = (roster_t *)sl_user->data;
642 roster_usr = &roster_special;
643 if (value) {
644 if (!(roster_usr->flags & ROSTER_FLAG_MSG))
645 unread_list_modified = TRUE;
646 roster_usr->flags |= ROSTER_FLAG_MSG;
647 // Append the roster_usr to unread_list, but avoid duplicates
648 if (!g_slist_find(unread_list, roster_usr))
649 unread_list = g_slist_insert_sorted(unread_list, roster_usr,
650 (GCompareFunc)&_roster_compare_uiprio);
651 } else {
652 if (roster_usr->flags & ROSTER_FLAG_MSG)
653 unread_list_modified = TRUE;
654 roster_usr->flags &= ~ROSTER_FLAG_MSG;
655 roster_usr->ui_prio = 0;
656 if (unread_list) {
657 GSList *node = g_slist_find(unread_list, roster_usr);
658 if (node)
659 unread_list = g_slist_delete_link(unread_list, node);
660 }
661 }
662 goto roster_msg_setflag_return;
663 }
664
665 sl_user = roster_find(jid, jidsearch,
666 ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
667 // If we can't find it, we add it
668 if (sl_user == NULL) {
669 sl_user = roster_add_user(jid, NULL, NULL, ROSTER_TYPE_USER, sub_none, -1);
670 new_roster_item = TRUE;
671 }
672
673 roster_usr = (roster_t *)sl_user->data;
674 roster_grp = (roster_t *)roster_usr->list->data;
675 if (value) {
676 if (!(roster_usr->flags & ROSTER_FLAG_MSG))
677 unread_list_modified = TRUE;
678 // Message flag is TRUE. This is easy, we just have to set both flags
679 // to TRUE...
680 roster_usr->flags |= ROSTER_FLAG_MSG;
681 roster_grp->flags |= ROSTER_FLAG_MSG; // group
682 // Append the roster_usr to unread_list, but avoid duplicates
683 if (!g_slist_find(unread_list, roster_usr))
684 unread_list = g_slist_insert_sorted(unread_list, roster_usr,
685 (GCompareFunc)&_roster_compare_uiprio);
686 } else {
687 // Message flag is FALSE.
688 guint msg = FALSE;
689 if (roster_usr->flags & ROSTER_FLAG_MSG)
690 unread_list_modified = TRUE;
691 roster_usr->flags &= ~ROSTER_FLAG_MSG;
692 roster_usr->ui_prio = 0;
693 if (unread_list) {
694 GSList *node = g_slist_find(unread_list, roster_usr);
695 if (node)
696 unread_list = g_slist_delete_link(unread_list, node);
697 }
698 // For the group value we need to watch all buddies in this group;
699 // if one is flagged, then the group will be flagged.
700 // I will re-use sl_user and roster_usr here, as they aren't used
701 // anymore.
702 sl_user = roster_grp->list;
703 while (sl_user) {
704 roster_usr = (roster_t *)sl_user->data;
705 if (roster_usr->flags & ROSTER_FLAG_MSG) {
706 msg = TRUE;
707 break;
708 }
709 sl_user = g_slist_next(sl_user);
710 }
711 if (!msg)
712 roster_grp->flags &= ~ROSTER_FLAG_MSG;
713 else
714 roster_grp->flags |= ROSTER_FLAG_MSG;
715 // Actually the "else" part is useless, because the group
716 // ROSTER_FLAG_MSG should already be set...
717 }
718
719 if (buddylist && (new_roster_item || !g_list_find(buddylist, roster_usr)))
720 buddylist_defer_build();
721
722 roster_msg_setflag_return:
723 if (unread_list_modified) {
724 hlog_save_state();
725 roster_unread_check();
726 }
727 }
728
729 // roster_msg_update_unread()
730 // If increment is true, increment the unread messages count for jid by 1.
731 // If increment is false, reset the unread messages count for jid to 0.
roster_msg_update_unread(const char * jid,gboolean increment)732 void roster_msg_update_unread(const char *jid, gboolean increment)
733 {
734 GSList *sl_user;
735 roster_t *roster_usr;
736
737 sl_user = roster_find(jid, jidsearch,
738 ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
739 if (!sl_user)
740 return;
741
742 roster_usr = (roster_t *)sl_user->data;
743
744 if (increment)
745 roster_usr->unread++;
746 else
747 roster_usr->unread = 0;
748 }
749
750 // roster_setuiprio(jid, special, prio_value, action)
751 // Set the "attention" priority value for the given roster item.
752 // Note that this function doesn't create the roster item if it doesn't exist.
roster_setuiprio(const char * jid,guint special,guint value,enum setuiprio_ops action)753 void roster_setuiprio(const char *jid, guint special, guint value,
754 enum setuiprio_ops action)
755 {
756 guint oldval, newval;
757 roster_t *roster_usr;
758
759 if (special) {
760 roster_usr = &roster_special;
761 } else {
762 GSList *sl_user = roster_find(jid, jidsearch,
763 ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
764 if (!sl_user)
765 return;
766
767 roster_usr = (roster_t *)sl_user->data;
768 }
769 oldval = roster_usr->ui_prio;
770
771 if (action == prio_max)
772 newval = MAX(oldval, value);
773 else if (action == prio_inc)
774 newval = oldval + value;
775 else // prio_set
776 newval = value;
777
778 roster_usr->ui_prio = newval;
779 unread_list = g_slist_sort(unread_list,
780 (GCompareFunc)&_roster_compare_uiprio);
781 roster_unread_check();
782 }
783
roster_getuiprio(const char * jid,guint special)784 guint roster_getuiprio(const char *jid, guint special)
785 {
786 roster_t *roster_usr;
787 GSList *sl_user;
788
789 if (special) {
790 roster_usr = &roster_special;
791 return roster_usr->ui_prio;
792 }
793
794 sl_user = roster_find(jid, jidsearch,
795 ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
796 if (!sl_user)
797 return 0;
798 roster_usr = (roster_t *)sl_user->data;
799 return roster_usr->ui_prio;
800 }
801
roster_getname(const char * jid)802 const char *roster_getname(const char *jid)
803 {
804 GSList *sl_user;
805 roster_t *roster_usr;
806
807 sl_user = roster_find(jid, jidsearch,
808 ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
809 if (sl_user == NULL)
810 return NULL; // Not in the roster...
811
812 roster_usr = (roster_t *)sl_user->data;
813 return roster_usr->name;
814 }
815
roster_getnickname(const char * jid)816 const char *roster_getnickname(const char *jid)
817 {
818 GSList *sl_user;
819 roster_t *roster_usr;
820
821 sl_user = roster_find(jid, jidsearch,
822 ROSTER_TYPE_USER|ROSTER_TYPE_ROOM|ROSTER_TYPE_AGENT);
823 if (sl_user == NULL)
824 return NULL; // Not in the roster...
825
826 roster_usr = (roster_t *)sl_user->data;
827 return roster_usr->nickname;
828 }
829
roster_settype(const char * jid,guint type)830 void roster_settype(const char *jid, guint type)
831 {
832 GSList *sl_user;
833 roster_t *roster_usr;
834
835 if ((sl_user = roster_find(jid, jidsearch, 0)) == NULL)
836 return;
837
838 roster_usr = (roster_t *)sl_user->data;
839 roster_usr->type = type;
840 }
841
roster_getstatus(const char * jid,const char * resname)842 enum imstatus roster_getstatus(const char *jid, const char *resname)
843 {
844 GSList *sl_user;
845 roster_t *roster_usr;
846 res_t *p_res;
847
848 sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
849 if (sl_user == NULL)
850 return offline; // Not in the roster, anyway...
851
852 roster_usr = (roster_t *)sl_user->data;
853 p_res = get_resource(roster_usr, resname);
854 if (p_res)
855 return p_res->status;
856 return offline;
857 }
858
roster_getstatusmsg(const char * jid,const char * resname)859 const char *roster_getstatusmsg(const char *jid, const char *resname)
860 {
861 GSList *sl_user;
862 roster_t *roster_usr;
863 res_t *p_res;
864
865 sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
866 if (sl_user == NULL)
867 return NULL; // Not in the roster, anyway...
868
869 roster_usr = (roster_t *)sl_user->data;
870 p_res = get_resource(roster_usr, resname);
871 if (p_res)
872 return p_res->status_msg;
873 return roster_usr->offline_status_message;
874 }
875
roster_getprio(const char * jid,const char * resname)876 char roster_getprio(const char *jid, const char *resname)
877 {
878 GSList *sl_user;
879 roster_t *roster_usr;
880 res_t *p_res;
881
882 sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
883 if (sl_user == NULL)
884 return offline; // Not in the roster, anyway...
885
886 roster_usr = (roster_t *)sl_user->data;
887 p_res = get_resource(roster_usr, resname);
888 if (p_res)
889 return p_res->prio;
890 return 0;
891 }
892
roster_gettype(const char * jid)893 guint roster_gettype(const char *jid)
894 {
895 GSList *sl_user;
896 roster_t *roster_usr;
897
898 if ((sl_user = roster_find(jid, jidsearch, 0)) == NULL)
899 return 0;
900
901 roster_usr = (roster_t *)sl_user->data;
902 return roster_usr->type;
903 }
904
roster_getsubscription(const char * jid)905 guint roster_getsubscription(const char *jid)
906 {
907 GSList *sl_user;
908 roster_t *roster_usr;
909
910 if ((sl_user = roster_find(jid, jidsearch, 0)) == NULL)
911 return 0;
912
913 roster_usr = (roster_t *)sl_user->data;
914 return roster_usr->subscription;
915 }
916
917 // roster_unsubscribed()
918 // We have lost buddy's presence updates; this function clears the status
919 // message, sets the buddy offline and frees the resources
roster_unsubscribed(const char * jid)920 void roster_unsubscribed(const char *jid)
921 {
922 GSList *sl_user;
923 roster_t *roster_usr;
924
925 sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
926 if (sl_user == NULL)
927 return;
928
929 roster_usr = (roster_t *)sl_user->data;
930 free_all_resources(&roster_usr->resource);
931 }
932
933
934 /* ### BuddyList functions ### */
935
936 // buddylist_set_hide_offline_buddies(hide)
937 // "hide" values: 1=hide 0=show_all -1=invert
buddylist_set_hide_offline_buddies(int hide)938 void buddylist_set_hide_offline_buddies(int hide)
939 {
940 if (hide < 0) { // NEG (invert)
941 if (display_filter == DFILTER_ALL)
942 display_filter = DFILTER_ONLINE;
943 else
944 display_filter = DFILTER_ALL;
945 } else if (hide == 0) { // FALSE (don't hide -- andfo_)
946 display_filter = DFILTER_ALL;
947 } else { // TRUE (hide -- andfo)
948 display_filter = DFILTER_ONLINE;
949 }
950 buddylist_defer_build();
951 }
952
buddylist_isset_filter(void)953 int buddylist_isset_filter(void)
954 {
955 return (display_filter != DFILTER_ALL);
956 }
957
buddylist_is_status_filtered(enum imstatus status)958 int buddylist_is_status_filtered(enum imstatus status)
959 {
960 return display_filter & (1 << status);
961 }
962
buddylist_set_filter(guchar filter)963 void buddylist_set_filter(guchar filter)
964 {
965 display_filter = filter;
966 }
967
buddylist_get_filter(void)968 guchar buddylist_get_filter(void)
969 {
970 return display_filter;
971 }
972
buddylist_defer_build(void)973 void buddylist_defer_build(void)
974 {
975 _rebuild_buddylist = TRUE;
976 }
977
978 // buddylist_build()
979 // Creates the buddylist from the roster entries.
buddylist_build(void)980 void buddylist_build(void)
981 {
982 GSList *sl_roster_elt = groups;
983 roster_t *roster_elt;
984 roster_t *roster_current_buddy = NULL;
985 roster_t *roster_alternate_buddy = NULL;
986 roster_t *roster_last_activity_buddy = NULL;
987 int shrunk_group;
988
989 if (_rebuild_buddylist == FALSE)
990 return;
991 _rebuild_buddylist = FALSE;
992
993 // We need to remember which buddy is selected.
994 if (current_buddy)
995 roster_current_buddy = BUDDATA(current_buddy);
996 current_buddy = NULL;
997 if (alternate_buddy)
998 roster_alternate_buddy = BUDDATA(alternate_buddy);
999 alternate_buddy = NULL;
1000 if (last_activity_buddy)
1001 roster_last_activity_buddy = BUDDATA(last_activity_buddy);
1002 last_activity_buddy = NULL;
1003
1004 // Destroy old buddylist
1005 if (buddylist) {
1006 g_list_free(buddylist);
1007 buddylist = NULL;
1008 }
1009
1010 buddylist = g_list_append(buddylist, &roster_special);
1011
1012 // Create the new list
1013 while (sl_roster_elt) {
1014 GSList *sl_roster_usrelt;
1015 roster_t *roster_usrelt;
1016 guint pending_group = TRUE;
1017 roster_elt = (roster_t *) sl_roster_elt->data;
1018
1019 shrunk_group = roster_elt->flags & ROSTER_FLAG_HIDE;
1020
1021 sl_roster_usrelt = roster_elt->list;
1022 while (sl_roster_usrelt) {
1023 roster_usrelt = (roster_t *) sl_roster_usrelt->data;
1024
1025 // Buddy will be added if either:
1026 // - buddy's status matches the display_filter
1027 // - buddy has a lock (for example the buddy window is currently open)
1028 // - buddy has a pending (non-read) message
1029 // - group isn't hidden (shrunk)
1030 // - this is the current_buddy
1031 if (roster_usrelt == roster_current_buddy ||
1032 buddylist_is_status_filtered(buddy_getstatus((gpointer)roster_usrelt,
1033 NULL)) ||
1034 (buddy_getflags((gpointer)roster_usrelt) &
1035 (ROSTER_FLAG_LOCK | ROSTER_FLAG_USRLOCK | ROSTER_FLAG_MSG))) {
1036 // This user should be added. Maybe the group hasn't been added yet?
1037 if (pending_group) {
1038 // It hasn't been done yet
1039 buddylist = g_list_append(buddylist, roster_elt);
1040 pending_group = FALSE;
1041 }
1042 // Add user
1043 // XXX Should we add the user if there is a message and
1044 // the group is shrunk? If so, we'd need to check LOCK flag too,
1045 // perhaps...
1046 if (!shrunk_group)
1047 buddylist = g_list_append(buddylist, roster_usrelt);
1048 }
1049
1050 sl_roster_usrelt = g_slist_next(sl_roster_usrelt);
1051 }
1052 sl_roster_elt = g_slist_next(sl_roster_elt);
1053 }
1054
1055 // Check if we can find our saved current_buddy...
1056 if (roster_current_buddy)
1057 current_buddy = g_list_find(buddylist, roster_current_buddy);
1058 if (roster_alternate_buddy)
1059 alternate_buddy = g_list_find(buddylist, roster_alternate_buddy);
1060 if (roster_last_activity_buddy)
1061 last_activity_buddy = g_list_find(buddylist, roster_last_activity_buddy);
1062 // current_buddy initialization
1063 if (!current_buddy || (g_list_position(buddylist, current_buddy) == -1))
1064 current_buddy = g_list_first(buddylist);
1065 }
1066
1067 // buddy_hide_group(roster, hide)
1068 // "hide" values: 1=hide 0=show_all -1=invert
buddy_hide_group(gpointer rosterdata,int hide)1069 void buddy_hide_group(gpointer rosterdata, int hide)
1070 {
1071 roster_t *roster_usr = rosterdata;
1072 if (hide > 0) // TRUE (hide)
1073 roster_usr->flags |= ROSTER_FLAG_HIDE;
1074 else if (hide < 0) // NEG (invert)
1075 roster_usr->flags ^= ROSTER_FLAG_HIDE;
1076 else // FALSE (don't hide)
1077 roster_usr->flags &= ~ROSTER_FLAG_HIDE;
1078 }
1079
buddy_getjid(gpointer rosterdata)1080 const char *buddy_getjid(gpointer rosterdata)
1081 {
1082 roster_t *roster_usr = rosterdata;
1083 if (!rosterdata)
1084 return NULL;
1085 return roster_usr->jid;
1086 }
1087
1088 // buddy_setgroup()
1089 // Change the group of current buddy
1090 //
1091 // Note: buddy_setgroup() updates the user lists.
1092 //
buddy_setgroup(gpointer rosterdata,char * newgroupname)1093 void buddy_setgroup(gpointer rosterdata, char *newgroupname)
1094 {
1095 roster_t *roster_usr = rosterdata;
1096 GSList **sl_group;
1097 GSList *sl_newgroup;
1098 roster_t *my_newgroup;
1099
1100 // A group has no group :)
1101 if (roster_usr->type & ROSTER_TYPE_GROUP) return;
1102
1103 // Add newgroup if necessary
1104 if (!newgroupname) newgroupname = "";
1105 sl_newgroup = roster_add_group(newgroupname);
1106 if (!sl_newgroup) return;
1107 my_newgroup = (roster_t *)sl_newgroup->data;
1108
1109 // Remove the buddy from current group
1110 sl_group = &((roster_t *)((GSList*)roster_usr->list)->data)->list;
1111 *sl_group = g_slist_remove(*sl_group, rosterdata);
1112
1113 // Remove old group if it is empty
1114 if (!*sl_group) {
1115 roster_t *roster_grp = (roster_t *)((GSList*)roster_usr->list)->data;
1116 g_free((gchar*)roster_grp->jid);
1117 g_free((gchar*)roster_grp->name);
1118 g_free(roster_grp);
1119 groups = g_slist_remove(groups, roster_grp);
1120 }
1121
1122 // Add the buddy to its new group
1123 roster_usr->list = sl_newgroup; // (my_newgroup SList element)
1124 my_newgroup->list = g_slist_insert_sorted(my_newgroup->list, roster_usr,
1125 (GCompareFunc)&roster_compare_name);
1126
1127 buddylist_defer_build();
1128 }
1129
buddy_setname(gpointer rosterdata,char * newname)1130 void buddy_setname(gpointer rosterdata, char *newname)
1131 {
1132 roster_t *roster_usr = rosterdata;
1133 GSList **sl_group;
1134
1135 // TODO For groups, we need to check for unicity
1136 // However, renaming a group boils down to moving all its buddies to
1137 // another group, so calling this function is not really necessary...
1138 if (roster_usr->type & ROSTER_TYPE_GROUP) return;
1139
1140 if (roster_usr->name) {
1141 g_free((gchar*)roster_usr->name);
1142 roster_usr->name = NULL;
1143 }
1144 if (newname)
1145 roster_usr->name = g_strdup(newname);
1146
1147 // We need to resort the group list
1148 sl_group = &((roster_t *)((GSList*)roster_usr->list)->data)->list;
1149 *sl_group = g_slist_sort(*sl_group, (GCompareFunc)&roster_compare_name);
1150
1151 buddylist_defer_build();
1152 }
1153
buddy_getname(gpointer rosterdata)1154 const char *buddy_getname(gpointer rosterdata)
1155 {
1156 roster_t *roster_usr = rosterdata;
1157 return roster_usr->name;
1158 }
1159
1160 // buddy_setnickname(buddy, newnickname)
1161 // Only for chatrooms
buddy_setnickname(gpointer rosterdata,const char * newname)1162 void buddy_setnickname(gpointer rosterdata, const char *newname)
1163 {
1164 roster_t *roster_usr = rosterdata;
1165
1166 if (!(roster_usr->type & ROSTER_TYPE_ROOM)) return; // XXX Error message?
1167
1168 if (roster_usr->nickname) {
1169 g_free((gchar*)roster_usr->nickname);
1170 roster_usr->nickname = NULL;
1171 }
1172 if (newname)
1173 roster_usr->nickname = g_strdup(newname);
1174 }
1175
buddy_getnickname(gpointer rosterdata)1176 const char *buddy_getnickname(gpointer rosterdata)
1177 {
1178 roster_t *roster_usr = rosterdata;
1179 return roster_usr->nickname;
1180 }
1181
1182 // buddy_setinsideroom(buddy, inside)
1183 // Only for chatrooms
buddy_setinsideroom(gpointer rosterdata,guint inside)1184 void buddy_setinsideroom(gpointer rosterdata, guint inside)
1185 {
1186 roster_t *roster_usr = rosterdata;
1187
1188 if (!(roster_usr->type & ROSTER_TYPE_ROOM)) return;
1189
1190 roster_usr->inside_room = inside;
1191 }
1192
buddy_getinsideroom(gpointer rosterdata)1193 guint buddy_getinsideroom(gpointer rosterdata)
1194 {
1195 roster_t *roster_usr = rosterdata;
1196 return roster_usr->inside_room;
1197 }
1198
1199 // buddy_settopic(buddy, newtopic)
1200 // Only for chatrooms
buddy_settopic(gpointer rosterdata,const char * newtopic)1201 void buddy_settopic(gpointer rosterdata, const char *newtopic)
1202 {
1203 roster_t *roster_usr = rosterdata;
1204
1205 if (!(roster_usr->type & ROSTER_TYPE_ROOM)) return;
1206
1207 if (roster_usr->topic) {
1208 g_free((gchar*)roster_usr->topic);
1209 roster_usr->topic = NULL;
1210 }
1211 if (newtopic)
1212 roster_usr->topic = g_strdup(newtopic);
1213 }
1214
buddy_gettopic(gpointer rosterdata)1215 const char *buddy_gettopic(gpointer rosterdata)
1216 {
1217 roster_t *roster_usr = rosterdata;
1218 return roster_usr->topic;
1219 }
1220
buddy_setprintstatus(gpointer rosterdata,enum room_printstatus pstatus)1221 void buddy_setprintstatus(gpointer rosterdata, enum room_printstatus pstatus)
1222 {
1223 roster_t *roster_usr = rosterdata;
1224 roster_usr->print_status = pstatus;
1225 }
1226
buddy_getprintstatus(gpointer rosterdata)1227 enum room_printstatus buddy_getprintstatus(gpointer rosterdata)
1228 {
1229 roster_t *roster_usr = rosterdata;
1230 return roster_usr->print_status;
1231 }
1232
buddy_setautowhois(gpointer rosterdata,enum room_autowhois awhois)1233 void buddy_setautowhois(gpointer rosterdata, enum room_autowhois awhois)
1234 {
1235 roster_t *roster_usr = rosterdata;
1236 roster_usr->auto_whois = awhois;
1237 }
1238
buddy_getautowhois(gpointer rosterdata)1239 enum room_autowhois buddy_getautowhois(gpointer rosterdata)
1240 {
1241 roster_t *roster_usr = rosterdata;
1242 return roster_usr->auto_whois;
1243 }
1244
buddy_setflagjoins(gpointer rosterdata,enum room_flagjoins fjoins)1245 void buddy_setflagjoins(gpointer rosterdata, enum room_flagjoins fjoins)
1246 {
1247 roster_t *roster_usr = rosterdata;
1248 roster_usr->flag_joins = fjoins;
1249 }
1250
buddy_getflagjoins(gpointer rosterdata)1251 enum room_flagjoins buddy_getflagjoins(gpointer rosterdata)
1252 {
1253 roster_t *roster_usr = rosterdata;
1254 return roster_usr->flag_joins;
1255 }
1256
1257 // buddy_getgroupname()
1258 // Returns a pointer on buddy's group name.
buddy_getgroupname(gpointer rosterdata)1259 const char *buddy_getgroupname(gpointer rosterdata)
1260 {
1261 roster_t *roster_usr = rosterdata;
1262
1263 if (roster_usr->type & ROSTER_TYPE_GROUP)
1264 return roster_usr->name;
1265
1266 if (roster_usr->type & ROSTER_TYPE_SPECIAL)
1267 return NULL;
1268
1269 // This is a user
1270 return ((roster_t *)((GSList*)roster_usr->list)->data)->name;
1271 }
1272
1273 // buddy_getgroup()
1274 // Returns a pointer on buddy's group.
buddy_getgroup(gpointer rosterdata)1275 gpointer buddy_getgroup(gpointer rosterdata)
1276 {
1277 roster_t *roster_usr = rosterdata;
1278
1279 if (roster_usr->type & ROSTER_TYPE_GROUP)
1280 return rosterdata;
1281
1282 if (roster_usr->type & ROSTER_TYPE_SPECIAL)
1283 return NULL;
1284
1285 // This is a user
1286 return (gpointer)((GSList*)roster_usr->list)->data;
1287 }
1288
buddy_settype(gpointer rosterdata,guint type)1289 void buddy_settype(gpointer rosterdata, guint type)
1290 {
1291 roster_t *roster_usr = rosterdata;
1292 roster_usr->type = type;
1293 }
1294
buddy_gettype(gpointer rosterdata)1295 guint buddy_gettype(gpointer rosterdata)
1296 {
1297 roster_t *roster_usr = rosterdata;
1298 return roster_usr->type;
1299 }
1300
buddy_getsubscription(gpointer rosterdata)1301 guint buddy_getsubscription(gpointer rosterdata)
1302 {
1303 roster_t *roster_usr = rosterdata;
1304 return roster_usr->subscription;
1305 }
1306
buddy_getstatus(gpointer rosterdata,const char * resname)1307 enum imstatus buddy_getstatus(gpointer rosterdata, const char *resname)
1308 {
1309 roster_t *roster_usr = rosterdata;
1310 res_t *p_res = get_resource(roster_usr, resname);
1311 if (p_res)
1312 return p_res->status;
1313 return offline;
1314 }
1315
buddy_getstatusmsg(gpointer rosterdata,const char * resname)1316 const char *buddy_getstatusmsg(gpointer rosterdata, const char *resname)
1317 {
1318 roster_t *roster_usr = rosterdata;
1319 res_t *p_res = get_resource(roster_usr, resname);
1320 if (p_res)
1321 return p_res->status_msg;
1322 return roster_usr->offline_status_message;
1323 }
1324
buddy_getstatustime(gpointer rosterdata,const char * resname)1325 time_t buddy_getstatustime(gpointer rosterdata, const char *resname)
1326 {
1327 roster_t *roster_usr = rosterdata;
1328 res_t *p_res = get_resource(roster_usr, resname);
1329 if (p_res)
1330 return p_res->status_timestamp;
1331 return 0;
1332 }
1333
buddy_getresourceprio(gpointer rosterdata,const char * resname)1334 gchar buddy_getresourceprio(gpointer rosterdata, const char *resname)
1335 {
1336 roster_t *roster_usr = rosterdata;
1337 res_t *p_res = get_resource(roster_usr, resname);
1338 if (p_res)
1339 return p_res->prio;
1340 return 0;
1341 }
1342
buddy_resource_getevents(gpointer rosterdata,const char * resname)1343 guint buddy_resource_getevents(gpointer rosterdata, const char *resname)
1344 {
1345 roster_t *roster_usr = rosterdata;
1346 res_t *p_res = get_resource(roster_usr, resname);
1347 if (p_res)
1348 return p_res->events;
1349 return ROSTER_EVENT_NONE;
1350 }
1351
buddy_resource_setevents(gpointer rosterdata,const char * resname,guint events)1352 void buddy_resource_setevents(gpointer rosterdata, const char *resname,
1353 guint events)
1354 {
1355 roster_t *roster_usr = rosterdata;
1356 res_t *p_res = get_resource(roster_usr, resname);
1357 if (p_res)
1358 p_res->events = events;
1359 }
1360
buddy_resource_getcaps(gpointer rosterdata,const char * resname)1361 char *buddy_resource_getcaps(gpointer rosterdata, const char *resname)
1362 {
1363 roster_t *roster_usr = rosterdata;
1364 res_t *p_res = get_resource(roster_usr, resname);
1365 if (p_res)
1366 return p_res->caps;
1367 return NULL;
1368 }
1369
buddy_resource_setcaps(gpointer rosterdata,const char * resname,const char * caps)1370 void buddy_resource_setcaps(gpointer rosterdata, const char *resname,
1371 const char *caps)
1372 {
1373 roster_t *roster_usr = rosterdata;
1374 res_t *p_res = get_resource(roster_usr, resname);
1375 if (p_res) {
1376 g_free(p_res->caps);
1377 p_res->caps = g_strdup(caps);
1378 }
1379 }
1380
buddy_resource_xep85(gpointer rosterdata,const char * resname)1381 struct xep0085 *buddy_resource_xep85(gpointer rosterdata, const char *resname)
1382 {
1383 #ifdef XEP0085
1384 roster_t *roster_usr = rosterdata;
1385 res_t *p_res = get_resource(roster_usr, resname);
1386 if (p_res)
1387 return &p_res->xep85;
1388 #endif
1389 return NULL;
1390 }
1391
buddy_resource_pgp(gpointer rosterdata,const char * resname)1392 struct pgp_data *buddy_resource_pgp(gpointer rosterdata, const char *resname)
1393 {
1394 #ifdef HAVE_GPGME
1395 roster_t *roster_usr = rosterdata;
1396 res_t *p_res = get_resource(roster_usr, resname);
1397 if (p_res)
1398 return &p_res->pgpdata;
1399 #endif
1400 return NULL;
1401 }
1402
buddy_getrole(gpointer rosterdata,const char * resname)1403 enum imrole buddy_getrole(gpointer rosterdata, const char *resname)
1404 {
1405 roster_t *roster_usr = rosterdata;
1406 res_t *p_res = get_resource(roster_usr, resname);
1407 if (p_res)
1408 return p_res->role;
1409 return role_none;
1410 }
1411
buddy_getaffil(gpointer rosterdata,const char * resname)1412 enum imaffiliation buddy_getaffil(gpointer rosterdata, const char *resname)
1413 {
1414 roster_t *roster_usr = rosterdata;
1415 res_t *p_res = get_resource(roster_usr, resname);
1416 if (p_res)
1417 return p_res->affil;
1418 return affil_none;
1419 }
1420
buddy_getrjid(gpointer rosterdata,const char * resname)1421 const char *buddy_getrjid(gpointer rosterdata, const char *resname)
1422 {
1423 roster_t *roster_usr = rosterdata;
1424 res_t *p_res = get_resource(roster_usr, resname);
1425 if (p_res)
1426 return p_res->realjid;
1427 return NULL;
1428 }
1429
1430 // buddy_getresources(roster_data)
1431 // Return a singly-linked-list of resource names
1432 // Note: the caller should free the list (and data) after use
1433 // If roster_data is null, the current buddy is selected
buddy_getresources(gpointer rosterdata)1434 GSList *buddy_getresources(gpointer rosterdata)
1435 {
1436 roster_t *roster_usr = rosterdata;
1437 GSList *reslist = NULL, *lp;
1438
1439 if (!roster_usr) {
1440 if (!current_buddy) return NULL;
1441 roster_usr = BUDDATA(current_buddy);
1442 }
1443 for (lp = roster_usr->resource; lp; lp = g_slist_next(lp))
1444 reslist = g_slist_append(reslist, g_strdup(((res_t *)lp->data)->name));
1445
1446 return reslist;
1447 }
1448
1449 // buddy_getresources_locale(roster_data)
1450 // Same as buddy_getresources() but names are converted to user's locale
1451 // Note: the caller should free the list (and data) after use
buddy_getresources_locale(gpointer rosterdata)1452 GSList *buddy_getresources_locale(gpointer rosterdata)
1453 {
1454 GSList *reslist, *lp;
1455
1456 reslist = buddy_getresources(rosterdata);
1457 // Convert each item to UI's locale
1458 for (lp = reslist; lp; lp = g_slist_next(lp)) {
1459 gchar *oldname = lp->data;
1460 lp->data = from_utf8(oldname);
1461 if (lp->data)
1462 g_free(oldname);
1463 else
1464 lp->data = oldname;
1465 }
1466 return reslist;
1467 }
1468
1469 // buddy_getactiveresource(roster_data)
1470 // Returns name of active (selected for chat) resource
buddy_getactiveresource(gpointer rosterdata)1471 const char *buddy_getactiveresource(gpointer rosterdata)
1472 {
1473 roster_t *roster_usr = rosterdata;
1474 res_t *resource;
1475
1476 if (!roster_usr) {
1477 if (!current_buddy) return NULL;
1478 roster_usr = BUDDATA(current_buddy);
1479 }
1480
1481 resource = roster_usr->active_resource;
1482 if (!resource) return NULL;
1483 return resource->name;
1484 }
1485
buddy_setactiveresource(gpointer rosterdata,const char * resname)1486 void buddy_setactiveresource(gpointer rosterdata, const char *resname)
1487 {
1488 roster_t *roster_usr = rosterdata;
1489 res_t *p_res = NULL;
1490 if (resname)
1491 p_res = get_resource(roster_usr, resname);
1492 roster_usr->active_resource = p_res;
1493 }
1494
1495 /*
1496 // buddy_isresource(roster_data)
1497 // Return true if there is at least one resource
1498 // (which means, for a room, that it isn't empty)
1499 int buddy_isresource(gpointer rosterdata)
1500 {
1501 roster_t *roster_usr = rosterdata;
1502 if (!roster_usr)
1503 return FALSE;
1504 if (roster_usr->resource)
1505 return TRUE;
1506 return FALSE;
1507 }
1508 */
1509
1510 // buddy_resource_setname(roster_data, oldname, newname)
1511 // Useful for nickname change in a MUC room
buddy_resource_setname(gpointer rosterdata,const char * resname,const char * newname)1512 void buddy_resource_setname(gpointer rosterdata, const char *resname,
1513 const char *newname)
1514 {
1515 roster_t *roster_usr = rosterdata;
1516 res_t *p_res = get_resource(roster_usr, resname);
1517 if (p_res) {
1518 if (p_res->name) {
1519 g_free((gchar*)p_res->name);
1520 p_res->name = NULL;
1521 }
1522 if (newname)
1523 p_res->name = g_strdup(newname);
1524 }
1525 }
1526
1527 // buddy_del_all_resources()
1528 // Remove all resources from the specified buddy
buddy_del_all_resources(gpointer rosterdata)1529 void buddy_del_all_resources(gpointer rosterdata)
1530 {
1531 roster_t *roster_usr = rosterdata;
1532
1533 while (roster_usr->resource) {
1534 res_t *r = roster_usr->resource->data;
1535 del_resource(roster_usr, r->name);
1536 }
1537 }
1538
1539 // buddy_setflags()
1540 // Set one or several flags to value (TRUE/FALSE)
buddy_setflags(gpointer rosterdata,guint flags,guint value)1541 void buddy_setflags(gpointer rosterdata, guint flags, guint value)
1542 {
1543 roster_t *roster_usr = rosterdata;
1544 if (value)
1545 roster_usr->flags |= flags;
1546 else
1547 roster_usr->flags &= ~flags;
1548 }
1549
buddy_getflags(gpointer rosterdata)1550 guint buddy_getflags(gpointer rosterdata)
1551 {
1552 roster_t *roster_usr = rosterdata;
1553 return roster_usr->flags;
1554 }
1555
buddy_getuiprio(gpointer rosterdata)1556 guint buddy_getuiprio(gpointer rosterdata)
1557 {
1558 roster_t *roster_usr = rosterdata;
1559 return roster_usr->ui_prio;
1560 }
1561
buddy_getunread(gpointer rosterdata)1562 guint buddy_getunread(gpointer rosterdata)
1563 {
1564 roster_t *roster_usr = rosterdata;
1565 return roster_usr->unread;
1566 }
1567
1568 // buddy_setonserverflag()
1569 // Set the on_server flag
buddy_setonserverflag(gpointer rosterdata,guint onserver)1570 void buddy_setonserverflag(gpointer rosterdata, guint onserver)
1571 {
1572 roster_t *roster_usr = rosterdata;
1573 roster_usr->on_server = onserver;
1574 }
1575
buddy_getonserverflag(gpointer rosterdata)1576 guint buddy_getonserverflag(gpointer rosterdata)
1577 {
1578 roster_t *roster_usr = rosterdata;
1579 return roster_usr->on_server;
1580 }
1581
1582 // buddy_search_jid(jid)
1583 // Look for a buddy with specified jid.
1584 // Search begins at buddylist; if no match is found in the the buddylist,
1585 // return NULL;
buddy_search_jid(const char * jid)1586 GList *buddy_search_jid(const char *jid)
1587 {
1588 GList *buddy;
1589 roster_t *roster_usr;
1590
1591 buddylist_build();
1592 if (!buddylist) return NULL;
1593
1594 for (buddy = buddylist; buddy; buddy = g_list_next(buddy)) {
1595 roster_usr = (roster_t *)buddy->data;
1596 if (roster_usr->jid && !strcasecmp(roster_usr->jid, jid))
1597 return buddy;
1598 }
1599 return NULL;
1600 }
1601
1602 // buddy_search(string)
1603 // Look for a buddy whose name or jid contains string.
1604 // Search begins at current_buddy; if no match is found in the the buddylist,
1605 // return NULL;
buddy_search(char * string)1606 GList *buddy_search(char *string)
1607 {
1608 GList *buddy = current_buddy;
1609 roster_t *roster_usr;
1610 buddylist_build();
1611 if (!buddylist || !current_buddy) return NULL;
1612 for (;;) {
1613 gchar *jid_locale, *name_locale;
1614 char *found = NULL;
1615
1616 buddy = g_list_next(buddy);
1617 if (!buddy)
1618 buddy = buddylist;
1619
1620 roster_usr = (roster_t *)buddy->data;
1621
1622 jid_locale = from_utf8(roster_usr->jid);
1623 if (jid_locale) {
1624 found = strcasestr(jid_locale, string);
1625 g_free(jid_locale);
1626 if (found)
1627 return buddy;
1628 }
1629 name_locale = from_utf8(roster_usr->name);
1630 if (name_locale) {
1631 found = strcasestr(name_locale, string);
1632 g_free(name_locale);
1633 if (found)
1634 return buddy;
1635 }
1636
1637 if (buddy == current_buddy)
1638 return NULL; // Back to the beginning, and no match found
1639 }
1640 }
1641
1642 // foreach_buddy(roster_type, pfunction, param)
1643 // Call pfunction(buddy, param) for each buddy from the roster with
1644 // type matching roster_type.
foreach_buddy(guint roster_type,void (* pfunc)(gpointer rosterdata,void * param),void * param)1645 void foreach_buddy(guint roster_type,
1646 void (*pfunc)(gpointer rosterdata, void *param),
1647 void *param)
1648 {
1649 GSList *sl_roster_elt = groups;
1650 roster_t *roster_elt;
1651 GSList *sl_roster_usrelt;
1652 roster_t *roster_usrelt;
1653
1654 while (sl_roster_elt) { // group list loop
1655 roster_elt = (roster_t *) sl_roster_elt->data;
1656 if (roster_elt->type & ROSTER_TYPE_SPECIAL)
1657 continue; // Skip special items
1658 sl_roster_usrelt = roster_elt->list;
1659 while (sl_roster_usrelt) { // user list loop
1660 roster_usrelt = (roster_t *) sl_roster_usrelt->data;
1661
1662 if (roster_usrelt->type & roster_type)
1663 pfunc(roster_usrelt, param);
1664
1665 sl_roster_usrelt = g_slist_next(sl_roster_usrelt);
1666 }
1667 sl_roster_elt = g_slist_next(sl_roster_elt);
1668 }
1669 }
1670
1671 // foreach_group_member(group, pfunction, param)
1672 // Call pfunction(buddy, param) for each buddy in the specified group.
foreach_group_member(gpointer groupdata,void (* pfunc)(gpointer rosterdata,void * param),void * param)1673 void foreach_group_member(gpointer groupdata,
1674 void (*pfunc)(gpointer rosterdata, void *param),
1675 void *param)
1676 {
1677 roster_t *roster_elt;
1678 GSList *sl_roster_usrelt;
1679 roster_t *roster_usrelt;
1680
1681 roster_elt = groupdata;
1682
1683 if (!(roster_elt->type & ROSTER_TYPE_GROUP))
1684 return;
1685
1686 sl_roster_usrelt = roster_elt->list;
1687 while (sl_roster_usrelt) { // user list loop
1688 GSList *next_sl_usrelt;
1689 roster_usrelt = (roster_t *) sl_roster_usrelt->data;
1690
1691 next_sl_usrelt = g_slist_next(sl_roster_usrelt);
1692 pfunc(roster_usrelt, param);
1693 sl_roster_usrelt = next_sl_usrelt;
1694 }
1695 }
1696
1697 // compl_list(type)
1698 // Returns a list of jid's or groups. (For commands completion)
1699 // type: ROSTER_TYPE_USER (jid's) or ROSTER_TYPE_GROUP (group names)
1700 // The list should be freed by the caller after use.
compl_list(guint type)1701 GSList *compl_list(guint type)
1702 {
1703 GSList *list = NULL;
1704 GSList *sl_roster_elt = groups;
1705 roster_t *roster_elt;
1706 GSList *sl_roster_usrelt;
1707 roster_t *roster_usrelt;
1708
1709 while (sl_roster_elt) { // group list loop
1710 roster_elt = (roster_t *) sl_roster_elt->data;
1711
1712 if (roster_elt->type & ROSTER_TYPE_SPECIAL)
1713 continue; // Skip special items
1714
1715 if (type == ROSTER_TYPE_GROUP) { // (group names)
1716 if (roster_elt->name && *(roster_elt->name))
1717 list = g_slist_append(list, from_utf8(roster_elt->name));
1718 } else { // ROSTER_TYPE_USER (jid) (or agent, or chatroom...)
1719 sl_roster_usrelt = roster_elt->list;
1720 while (sl_roster_usrelt) { // user list loop
1721 roster_usrelt = (roster_t *) sl_roster_usrelt->data;
1722
1723 if (roster_usrelt->jid)
1724 list = g_slist_append(list, from_utf8(roster_usrelt->jid));
1725
1726 sl_roster_usrelt = g_slist_next(sl_roster_usrelt);
1727 }
1728 }
1729 sl_roster_elt = g_slist_next(sl_roster_elt);
1730 }
1731
1732 return list;
1733 }
1734
1735 // unread_msg(rosterdata)
1736 // Return the next buddy with an unread message. If the parameter is NULL,
1737 // return the first buddy with an unread message.
unread_msg(gpointer rosterdata)1738 gpointer unread_msg(gpointer rosterdata)
1739 {
1740 GSList *unread, *next_unread;
1741
1742 if (!unread_list)
1743 return NULL;
1744
1745 // First unread message
1746 if (!rosterdata)
1747 return unread_list->data;
1748
1749 unread = g_slist_find(unread_list, rosterdata);
1750 if (!unread)
1751 return unread_list->data;
1752
1753 next_unread = g_slist_next(unread);
1754 if (next_unread)
1755 return next_unread->data;
1756 return unread_list->data;
1757 }
1758
1759
1760 /* ### "unread_jids" functions ###
1761 *
1762 * The unread_jids hash table is used to keep track of the buddies with
1763 * unread messages when a disconnection occurs.
1764 * When removing a buddy with an unread message from the roster, the
1765 * jid should be added to the unread_jids table. When adding a buddy to
1766 * the roster, we check if (s)he had a pending unread message.
1767 */
1768
1769 // unread_jid_add(jid)
1770 // Add jid to the unread_jids hash table
unread_jid_add(const char * jid)1771 void unread_jid_add(const char *jid)
1772 {
1773 if (!unread_jids) {
1774 // Initialize unread_jids hash table
1775 unread_jids = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1776 }
1777 // The 2nd unread_jids is an arbitrary non-null pointer:
1778 g_hash_table_insert(unread_jids, g_strdup(jid), unread_jids);
1779 }
1780
1781 // unread_jid_del(jid)
1782 // Return TRUE if jid is found in the table (and remove it), FALSE if not
unread_jid_del(const char * jid)1783 static int unread_jid_del(const char *jid)
1784 {
1785 if (!unread_jids)
1786 return FALSE;
1787 return g_hash_table_remove(unread_jids, jid);
1788 }
1789
1790 // unread_jid_get_list()
1791 // Return the JID list.
1792 // The content of the list should not be modified or freed.
1793 // The caller should call g_list_free() after use.
unread_jid_get_list(void)1794 GList *unread_jid_get_list(void)
1795 {
1796 if (!unread_jids)
1797 return NULL;
1798
1799 return g_hash_table_get_keys(unread_jids);
1800 }
1801
1802 /* vim: set et cindent cinoptions=>2\:2(0 ts=2 sw=2: For Vim users... */
1803