comparison mcabber/src/roster.c @ 438:b44be19d6229

Handle multiple resources for the same buddy
author Mikael Berthe <mikael@lilotux.net>
date Mon, 19 Sep 2005 23:32:42 +0200
parents 03f1e37759a6
children 63562fd409a1
comparison
equal deleted inserted replaced
437:170f1aa12989 438:b44be19d6229
23 #include <string.h> 23 #include <string.h>
24 24
25 #include "roster.h" 25 #include "roster.h"
26 26
27 27
28 /* Resource structure */
29
30 typedef struct {
31 gchar *name;
32 gchar prio;
33 enum imstatus status;
34 gchar *status_msg;
35 enum imrole role;
36 gchar *realjid; /* for chatrooms, if buddy's real jid is known */
37 } res;
38
28 /* This is a private structure type for the roster */ 39 /* This is a private structure type for the roster */
29 40
30 typedef struct { 41 typedef struct {
31 const gchar *name; 42 gchar *name;
32 const gchar *jid; 43 gchar *jid;
33 const gchar *status_msg;
34 guint type; 44 guint type;
35 enum imstatus status; 45 enum subscr subscription;
46 GSList *resource;
47 res *cur_res;
36 guint flags; 48 guint flags;
37 // list: user -> points to his group; group -> points to its users list 49 // list: user -> points to his group; group -> points to its users list
38 GSList *list; 50 GSList *list;
39 } roster; 51 } roster;
40 52
47 GList *buddylist; 59 GList *buddylist;
48 GList *current_buddy; 60 GList *current_buddy;
49 GList *alternate_buddy; 61 GList *alternate_buddy;
50 62
51 63
64 /* ### Resources functions ### */
65
66 static void free_all_resources(GSList **reslist)
67 {
68 GSList *lip;
69 res *p_res;
70
71 for ( lip = *reslist; lip ; lip = g_slist_next(lip)) {
72 p_res = (res*)lip->data;
73 if (p_res->status_msg) {
74 g_free((gchar*)p_res->status_msg);
75 }
76 if (p_res->name) {
77 g_free((gchar*)p_res->name);
78 }
79 if (p_res->realjid) {
80 g_free((gchar*)p_res->realjid);
81 }
82 }
83 // Free all nodes but the first (which is static)
84 g_slist_free(*reslist);
85 *reslist = NULL;
86 }
87
88 // Resources are sorted in ascending order
89 static gint resource_compare_prio(res *a, res *b) {
90 //return (a->prio - b->prio);
91 if (a->prio < b->prio) return -1;
92 else return 1;
93 }
94
95 // get_resource(rost, resname)
96 // Return a pointer to the resource with name resname, in rost's resources list
97 // - if rost has no resources, return NULL
98 // - if resname is defined, return the match or NULL
99 // - if resname is NULL, the last resource is returned, currently
100 // This could change in the future, because we should return the best one
101 // (priority? last used? and fall back to the first resource)
102 //
103 static res *get_resource(roster *rost, const char *resname)
104 {
105 GSList *p;
106 res *r = NULL;
107
108 for (p = rost->resource; p; p = g_slist_next(p)) {
109 r = p->data;
110 if (resname && !strcmp(r->name, resname))
111 return r;
112 }
113
114 // The last resource is one of the resources with the highest priority,
115 // however, we don't know if it is the more-recently-used.
116 if (!resname) return r;
117 return NULL;
118 }
119
120 // get_or_add_resource(rost, resname, priority)
121 // - if there is a "resname" resource in rost's resources, return a pointer
122 // on this resource
123 // - if not, add the resource, set the name, and return a pointer on this
124 // new resource
125 static res *get_or_add_resource(roster *rost, const char *resname, gchar prio)
126 {
127 GSList *p;
128 res *nres;
129
130 if (!resname) return NULL;
131
132 for (p = rost->resource; p; p = g_slist_next(p)) {
133 res *r = p->data;
134 if (!strcmp(r->name, resname))
135 return r;
136 }
137
138 // Resource not found
139 nres = g_new0(res, 1);
140 nres->name = g_strdup(resname);
141 nres->prio = prio;
142 rost->resource = g_slist_insert_sorted(rost->resource, nres,
143 (GCompareFunc)&resource_compare_prio);
144 return nres;
145 }
146
147 static void del_resource(roster *rost, const char *resname)
148 {
149 GSList *p;
150 GSList *p_res_elt = NULL;
151 res *p_res;
152
153 if (!resname) return;
154
155 for (p = rost->resource; p; p = g_slist_next(p)) {
156 res *r = p->data;
157 if (!strcmp(r->name, resname))
158 p_res_elt = p;
159 }
160
161 if (!p_res_elt) return; // Resource not found
162
163 p_res = p_res_elt->data;
164 // Free allocations and delete resource node
165 if (p_res->name) g_free(p_res->name);
166 if (p_res->status_msg) g_free(p_res->status_msg);
167 if (p_res->realjid) g_free(p_res->realjid);
168 rost->resource = g_slist_delete_link(rost->resource, p_res_elt);
169 return;
170 }
171
172
52 /* ### Roster functions ### */ 173 /* ### Roster functions ### */
53 174
54 // Comparison function used to search in the roster (compares jids and types) 175 // Comparison function used to search in the roster (compares jids and types)
55 static gint roster_compare_jid_type(roster *a, roster *b) { 176 static gint roster_compare_jid_type(roster *a, roster *b) {
56 if (! (a->type & b->type)) 177 if (! (a->type & b->type))
78 if (!roster_type) 199 if (!roster_type)
79 roster_type = ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_GROUP; 200 roster_type = ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_GROUP;
80 201
81 sample.type = roster_type; 202 sample.type = roster_type;
82 if (type == jidsearch) { 203 if (type == jidsearch) {
83 sample.jid = jidname; 204 sample.jid = (gchar*)jidname;
84 comp = (GCompareFunc)&roster_compare_jid_type; 205 comp = (GCompareFunc)&roster_compare_jid_type;
85 } else if (type == namesearch) { 206 } else if (type == namesearch) {
86 sample.name = jidname; 207 sample.name = (gchar*)jidname;
87 comp = (GCompareFunc)&roster_compare_name; 208 comp = (GCompareFunc)&roster_compare_name;
88 } else 209 } else
89 return NULL; // should not happen 210 return NULL; // should not happen
90 211
91 while (sl_roster_elt) { 212 while (sl_roster_elt) {
181 if (node) unread_list = g_slist_delete_link(unread_list, node); 302 if (node) unread_list = g_slist_delete_link(unread_list, node);
182 303
183 // Let's free memory (jid, name, status message) 304 // Let's free memory (jid, name, status message)
184 if (roster_usr->jid) g_free((gchar*)roster_usr->jid); 305 if (roster_usr->jid) g_free((gchar*)roster_usr->jid);
185 if (roster_usr->name) g_free((gchar*)roster_usr->name); 306 if (roster_usr->name) g_free((gchar*)roster_usr->name);
186 if (roster_usr->status_msg) g_free((gchar*)roster_usr->status_msg); 307 free_all_resources(&roster_usr->resource);
187 g_free(roster_usr); 308 g_free(roster_usr);
188 309
189 // That's a little complex, we need to dereference twice 310 // That's a little complex, we need to dereference twice
190 sl_group = ((roster*)sl_user->data)->list; 311 sl_group = ((roster*)sl_user->data)->list;
191 sl_group_listptr = &((roster*)(sl_group->data))->list; 312 sl_group_listptr = &((roster*)(sl_group->data))->list;
218 while (sl_usr) { 339 while (sl_usr) {
219 roster *roster_usr = (roster*)sl_usr->data; 340 roster *roster_usr = (roster*)sl_usr->data;
220 // Free name and jid 341 // Free name and jid
221 if (roster_usr->jid) g_free((gchar*)roster_usr->jid); 342 if (roster_usr->jid) g_free((gchar*)roster_usr->jid);
222 if (roster_usr->name) g_free((gchar*)roster_usr->name); 343 if (roster_usr->name) g_free((gchar*)roster_usr->name);
223 if (roster_usr->status_msg) g_free((gchar*)roster_usr->status_msg); 344 free_all_resources(&roster_usr->resource);
224 g_free(roster_usr); 345 g_free(roster_usr);
225 sl_usr = g_slist_next(sl_usr); 346 sl_usr = g_slist_next(sl_usr);
226 } 347 }
227 // Free group's users list 348 // Free group's users list
228 if (roster_grp->list) 349 if (roster_grp->list)
241 if (buddylist) 362 if (buddylist)
242 buddylist_build(); 363 buddylist_build();
243 } 364 }
244 } 365 }
245 366
246 void roster_setstatus(const char *jid, enum imstatus bstat, 367 void roster_setstatus(const char *jid, const char *resname, gchar prio,
247 const char *status_msg) 368 enum imstatus bstat, const char *status_msg)
248 { 369 {
249 GSList *sl_user; 370 GSList *sl_user;
250 roster *roster_usr; 371 roster *roster_usr;
372 res *p_res;
251 373
252 sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT); 374 sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
253 // If we can't find it, we add it 375 // If we can't find it, we add it
254 if (sl_user == NULL) 376 if (sl_user == NULL)
255 sl_user = roster_add_user(jid, NULL, NULL, ROSTER_TYPE_USER); 377 sl_user = roster_add_user(jid, NULL, NULL, ROSTER_TYPE_USER);
256 378
379 // If there is no resource name, we can leave now
380 if (!resname) return;
381
257 roster_usr = (roster*)sl_user->data; 382 roster_usr = (roster*)sl_user->data;
258 roster_usr->status = bstat; 383
259 if (roster_usr->status_msg) { 384 // If bstat is offline, we MUST delete the resource, actually
260 g_free((gchar*)roster_usr->status_msg); 385 if (bstat == offline) {
261 roster_usr->status_msg = NULL; 386 del_resource(roster_usr, resname);
387 return;
388 }
389
390 // New or updated resource
391 p_res = get_or_add_resource(roster_usr, resname, prio);
392 p_res->status = bstat;
393 if (p_res->status_msg) {
394 g_free((gchar*)p_res->status_msg);
395 p_res->status_msg = NULL;
262 } 396 }
263 if (status_msg) 397 if (status_msg)
264 roster_usr->status_msg = g_strdup(status_msg); 398 p_res->status_msg = g_strdup(status_msg);
265 } 399 }
266 400
267 // roster_setflags() 401 // roster_setflags()
268 // Set one or several flags to value (TRUE/FALSE) 402 // Set one or several flags to value (TRUE/FALSE)
269 void roster_setflags(const char *jid, guint flags, guint value) 403 void roster_setflags(const char *jid, guint flags, guint value)
345 479
346 roster_usr = (roster*)sl_user->data; 480 roster_usr = (roster*)sl_user->data;
347 roster_usr->type = type; 481 roster_usr->type = type;
348 } 482 }
349 483
350 enum imstatus roster_getstatus(const char *jid) 484 enum imstatus roster_getstatus(const char *jid, const char *resname)
351 { 485 {
352 GSList *sl_user; 486 GSList *sl_user;
353 roster *roster_usr; 487 roster *roster_usr;
488 res *p_res;
354 489
355 sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT); 490 sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
356 if (sl_user == NULL) 491 if (sl_user == NULL)
357 return offline; // Not in the roster, anyway... 492 return offline; // Not in the roster, anyway...
358 493
359 roster_usr = (roster*)sl_user->data; 494 roster_usr = (roster*)sl_user->data;
360 return roster_usr->status; 495 p_res = get_resource(roster_usr, resname);
361 } 496 if (p_res)
362 497 return p_res->status;
363 const char *roster_getstatusmsg(const char *jid) 498 return offline;
499 }
500
501 const char *roster_getstatusmsg(const char *jid, const char *resname)
364 { 502 {
365 GSList *sl_user; 503 GSList *sl_user;
366 roster *roster_usr; 504 roster *roster_usr;
505 res *p_res;
367 506
368 sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT); 507 sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
369 if (sl_user == NULL) 508 if (sl_user == NULL)
370 return NULL; // Not in the roster, anyway... 509 return NULL; // Not in the roster, anyway...
371 510
372 roster_usr = (roster*)sl_user->data; 511 roster_usr = (roster*)sl_user->data;
373 return roster_usr->status_msg; 512 p_res = get_resource(roster_usr, resname);
513 if (p_res)
514 return p_res->status_msg;
515 return NULL;
374 } 516 }
375 517
376 guint roster_gettype(const char *jid) 518 guint roster_gettype(const char *jid)
377 { 519 {
378 GSList *sl_user; 520 GSList *sl_user;
464 // - buddy has a lock (for example the buddy window is currently open) 606 // - buddy has a lock (for example the buddy window is currently open)
465 // - buddy has a pending (non-read) message 607 // - buddy has a pending (non-read) message
466 // - group isn't hidden (shrunk) 608 // - group isn't hidden (shrunk)
467 // - this is the current_buddy 609 // - this is the current_buddy
468 if (!hide_offline_buddies || roster_usrelt == roster_current_buddy || 610 if (!hide_offline_buddies || roster_usrelt == roster_current_buddy ||
469 (buddy_getstatus((gpointer)roster_usrelt) != offline) || 611 (buddy_getstatus((gpointer)roster_usrelt, NULL) != offline) ||
470 (buddy_getflags((gpointer)roster_usrelt) & 612 (buddy_getflags((gpointer)roster_usrelt) &
471 (ROSTER_FLAG_LOCK | ROSTER_FLAG_MSG))) { 613 (ROSTER_FLAG_LOCK | ROSTER_FLAG_MSG))) {
472 // This user should be added. Maybe the group hasn't been added yet? 614 // This user should be added. Maybe the group hasn't been added yet?
473 if (pending_group && 615 if (pending_group &&
474 (hide_offline_buddies || roster_usrelt == roster_current_buddy)) { 616 (hide_offline_buddies || roster_usrelt == roster_current_buddy)) {
542 684
543 // Add the buddy to its new group; actually we "clone" this buddy... 685 // Add the buddy to its new group; actually we "clone" this buddy...
544 sl_clone = roster_add_user(roster_usr->jid, roster_usr->name, 686 sl_clone = roster_add_user(roster_usr->jid, roster_usr->name,
545 newgroupname, roster_usr->type); 687 newgroupname, roster_usr->type);
546 roster_clone = (roster*)sl_clone->data; 688 roster_clone = (roster*)sl_clone->data;
547 roster_clone->status = roster_usr->status; 689 roster_clone->flags = roster_usr->flags;
548 roster_clone->flags = roster_usr->flags; 690 roster_clone->resource = roster_usr->resource;
691 roster_usr->resource = NULL;
549 692
550 // Free old buddy 693 // Free old buddy
551 if (roster_usr->jid) g_free((gchar*)roster_usr->jid); 694 if (roster_usr->jid) g_free((gchar*)roster_usr->jid);
552 if (roster_usr->name) g_free((gchar*)roster_usr->name); 695 if (roster_usr->name) g_free((gchar*)roster_usr->name);
553 if (roster_usr->status_msg) g_free((gchar*)roster_usr->status_msg); 696 free_all_resources(&roster_usr->resource);
554 g_free(roster_usr); 697 g_free(roster_usr);
555 698
556 // If new new group is folded, the curren_buddy will be lost, and the 699 // If new new group is folded, the curren_buddy will be lost, and the
557 // chat window won't be correctly refreshed. So we make sure it isn't... 700 // chat window won't be correctly refreshed. So we make sure it isn't...
558 ((roster*)((GSList*)roster_clone->list)->data)->flags &= ~ROSTER_FLAG_HIDE; 701 ((roster*)((GSList*)roster_clone->list)->data)->flags &= ~ROSTER_FLAG_HIDE;
627 { 770 {
628 roster *roster_usr = rosterdata; 771 roster *roster_usr = rosterdata;
629 return roster_usr->type; 772 return roster_usr->type;
630 } 773 }
631 774
632 enum imstatus buddy_getstatus(gpointer rosterdata) 775 enum imstatus buddy_getstatus(gpointer rosterdata, const char *resname)
633 { 776 {
634 roster *roster_usr = rosterdata; 777 roster *roster_usr = rosterdata;
635 return roster_usr->status; 778 res *p_res = get_resource(roster_usr, resname);
636 } 779 if (p_res)
637 780 return p_res->status;
638 const char *buddy_getstatusmsg(gpointer rosterdata) 781 return offline;
639 { 782 }
640 roster *roster_usr = rosterdata; 783
641 return roster_usr->status_msg; 784 const char *buddy_getstatusmsg(gpointer rosterdata, const char *resname)
785 {
786 roster *roster_usr = rosterdata;
787 res *p_res = get_resource(roster_usr, resname);
788 if (p_res)
789 return p_res->status_msg;
790 return NULL;
642 } 791 }
643 792
644 // buddy_setflags() 793 // buddy_setflags()
645 // Set one or several flags to value (TRUE/FALSE) 794 // Set one or several flags to value (TRUE/FALSE)
646 void buddy_setflags(gpointer rosterdata, guint flags, guint value) 795 void buddy_setflags(gpointer rosterdata, guint flags, guint value)