Mercurial > ~mikael > mcabber > hg
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) |