Mercurial > ~mikael > mcabber > hg
diff 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 |
line wrap: on
line diff
--- a/mcabber/src/roster.c Fri Sep 16 21:49:39 2005 +0200 +++ b/mcabber/src/roster.c Mon Sep 19 23:32:42 2005 +0200 @@ -25,14 +25,26 @@ #include "roster.h" +/* Resource structure */ + +typedef struct { + gchar *name; + gchar prio; + enum imstatus status; + gchar *status_msg; + enum imrole role; + gchar *realjid; /* for chatrooms, if buddy's real jid is known */ +} res; + /* This is a private structure type for the roster */ typedef struct { - const gchar *name; - const gchar *jid; - const gchar *status_msg; + gchar *name; + gchar *jid; guint type; - enum imstatus status; + enum subscr subscription; + GSList *resource; + res *cur_res; guint flags; // list: user -> points to his group; group -> points to its users list GSList *list; @@ -49,6 +61,115 @@ GList *alternate_buddy; +/* ### Resources functions ### */ + +static void free_all_resources(GSList **reslist) +{ + GSList *lip; + res *p_res; + + for ( lip = *reslist; lip ; lip = g_slist_next(lip)) { + p_res = (res*)lip->data; + if (p_res->status_msg) { + g_free((gchar*)p_res->status_msg); + } + if (p_res->name) { + g_free((gchar*)p_res->name); + } + if (p_res->realjid) { + g_free((gchar*)p_res->realjid); + } + } + // Free all nodes but the first (which is static) + g_slist_free(*reslist); + *reslist = NULL; +} + +// Resources are sorted in ascending order +static gint resource_compare_prio(res *a, res *b) { + //return (a->prio - b->prio); + if (a->prio < b->prio) return -1; + else return 1; +} + +// get_resource(rost, resname) +// Return a pointer to the resource with name resname, in rost's resources list +// - if rost has no resources, return NULL +// - if resname is defined, return the match or NULL +// - if resname is NULL, the last resource is returned, currently +// This could change in the future, because we should return the best one +// (priority? last used? and fall back to the first resource) +// +static res *get_resource(roster *rost, const char *resname) +{ + GSList *p; + res *r = NULL; + + for (p = rost->resource; p; p = g_slist_next(p)) { + r = p->data; + if (resname && !strcmp(r->name, resname)) + return r; + } + + // The last resource is one of the resources with the highest priority, + // however, we don't know if it is the more-recently-used. + if (!resname) return r; + return NULL; +} + +// get_or_add_resource(rost, resname, priority) +// - if there is a "resname" resource in rost's resources, return a pointer +// on this resource +// - if not, add the resource, set the name, and return a pointer on this +// new resource +static res *get_or_add_resource(roster *rost, const char *resname, gchar prio) +{ + GSList *p; + res *nres; + + if (!resname) return NULL; + + for (p = rost->resource; p; p = g_slist_next(p)) { + res *r = p->data; + if (!strcmp(r->name, resname)) + return r; + } + + // Resource not found + nres = g_new0(res, 1); + nres->name = g_strdup(resname); + nres->prio = prio; + rost->resource = g_slist_insert_sorted(rost->resource, nres, + (GCompareFunc)&resource_compare_prio); + return nres; +} + +static void del_resource(roster *rost, const char *resname) +{ + GSList *p; + GSList *p_res_elt = NULL; + res *p_res; + + if (!resname) return; + + for (p = rost->resource; p; p = g_slist_next(p)) { + res *r = p->data; + if (!strcmp(r->name, resname)) + p_res_elt = p; + } + + if (!p_res_elt) return; // Resource not found + + p_res = p_res_elt->data; + // Free allocations and delete resource node + if (p_res->name) g_free(p_res->name); + if (p_res->status_msg) g_free(p_res->status_msg); + if (p_res->realjid) g_free(p_res->realjid); + rost->resource = g_slist_delete_link(rost->resource, p_res_elt); + return; +} + + /* ### Roster functions ### */ // Comparison function used to search in the roster (compares jids and types) @@ -80,10 +201,10 @@ sample.type = roster_type; if (type == jidsearch) { - sample.jid = jidname; + sample.jid = (gchar*)jidname; comp = (GCompareFunc)&roster_compare_jid_type; } else if (type == namesearch) { - sample.name = jidname; + sample.name = (gchar*)jidname; comp = (GCompareFunc)&roster_compare_name; } else return NULL; // should not happen @@ -183,7 +304,7 @@ // Let's free memory (jid, name, status message) if (roster_usr->jid) g_free((gchar*)roster_usr->jid); if (roster_usr->name) g_free((gchar*)roster_usr->name); - if (roster_usr->status_msg) g_free((gchar*)roster_usr->status_msg); + free_all_resources(&roster_usr->resource); g_free(roster_usr); // That's a little complex, we need to dereference twice @@ -220,7 +341,7 @@ // Free name and jid if (roster_usr->jid) g_free((gchar*)roster_usr->jid); if (roster_usr->name) g_free((gchar*)roster_usr->name); - if (roster_usr->status_msg) g_free((gchar*)roster_usr->status_msg); + free_all_resources(&roster_usr->resource); g_free(roster_usr); sl_usr = g_slist_next(sl_usr); } @@ -243,25 +364,38 @@ } } -void roster_setstatus(const char *jid, enum imstatus bstat, - const char *status_msg) +void roster_setstatus(const char *jid, const char *resname, gchar prio, + enum imstatus bstat, const char *status_msg) { GSList *sl_user; roster *roster_usr; + res *p_res; sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT); // If we can't find it, we add it if (sl_user == NULL) sl_user = roster_add_user(jid, NULL, NULL, ROSTER_TYPE_USER); + // If there is no resource name, we can leave now + if (!resname) return; + roster_usr = (roster*)sl_user->data; - roster_usr->status = bstat; - if (roster_usr->status_msg) { - g_free((gchar*)roster_usr->status_msg); - roster_usr->status_msg = NULL; + + // If bstat is offline, we MUST delete the resource, actually + if (bstat == offline) { + del_resource(roster_usr, resname); + return; + } + + // New or updated resource + p_res = get_or_add_resource(roster_usr, resname, prio); + p_res->status = bstat; + if (p_res->status_msg) { + g_free((gchar*)p_res->status_msg); + p_res->status_msg = NULL; } if (status_msg) - roster_usr->status_msg = g_strdup(status_msg); + p_res->status_msg = g_strdup(status_msg); } // roster_setflags() @@ -347,30 +481,38 @@ roster_usr->type = type; } -enum imstatus roster_getstatus(const char *jid) +enum imstatus roster_getstatus(const char *jid, const char *resname) { GSList *sl_user; roster *roster_usr; + res *p_res; sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT); if (sl_user == NULL) return offline; // Not in the roster, anyway... roster_usr = (roster*)sl_user->data; - return roster_usr->status; + p_res = get_resource(roster_usr, resname); + if (p_res) + return p_res->status; + return offline; } -const char *roster_getstatusmsg(const char *jid) +const char *roster_getstatusmsg(const char *jid, const char *resname) { GSList *sl_user; roster *roster_usr; + res *p_res; sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT); if (sl_user == NULL) return NULL; // Not in the roster, anyway... roster_usr = (roster*)sl_user->data; - return roster_usr->status_msg; + p_res = get_resource(roster_usr, resname); + if (p_res) + return p_res->status_msg; + return NULL; } guint roster_gettype(const char *jid) @@ -466,7 +608,7 @@ // - group isn't hidden (shrunk) // - this is the current_buddy if (!hide_offline_buddies || roster_usrelt == roster_current_buddy || - (buddy_getstatus((gpointer)roster_usrelt) != offline) || + (buddy_getstatus((gpointer)roster_usrelt, NULL) != offline) || (buddy_getflags((gpointer)roster_usrelt) & (ROSTER_FLAG_LOCK | ROSTER_FLAG_MSG))) { // This user should be added. Maybe the group hasn't been added yet? @@ -544,13 +686,14 @@ sl_clone = roster_add_user(roster_usr->jid, roster_usr->name, newgroupname, roster_usr->type); roster_clone = (roster*)sl_clone->data; - roster_clone->status = roster_usr->status; - roster_clone->flags = roster_usr->flags; + roster_clone->flags = roster_usr->flags; + roster_clone->resource = roster_usr->resource; + roster_usr->resource = NULL; // Free old buddy if (roster_usr->jid) g_free((gchar*)roster_usr->jid); if (roster_usr->name) g_free((gchar*)roster_usr->name); - if (roster_usr->status_msg) g_free((gchar*)roster_usr->status_msg); + free_all_resources(&roster_usr->resource); g_free(roster_usr); // If new new group is folded, the curren_buddy will be lost, and the @@ -629,16 +772,22 @@ return roster_usr->type; } -enum imstatus buddy_getstatus(gpointer rosterdata) +enum imstatus buddy_getstatus(gpointer rosterdata, const char *resname) { roster *roster_usr = rosterdata; - return roster_usr->status; + res *p_res = get_resource(roster_usr, resname); + if (p_res) + return p_res->status; + return offline; } -const char *buddy_getstatusmsg(gpointer rosterdata) +const char *buddy_getstatusmsg(gpointer rosterdata, const char *resname) { roster *roster_usr = rosterdata; - return roster_usr->status_msg; + res *p_res = get_resource(roster_usr, resname); + if (p_res) + return p_res->status_msg; + return NULL; } // buddy_setflags()