# HG changeset patch # User Mikael Berthe # Date 1127165562 -7200 # Node ID b44be19d62293f96d2cb41e9baec9d8782e0c0f6 # Parent 170f1aa129899cbcee032724f3a18082c9c7db71 Handle multiple resources for the same buddy diff -r 170f1aa12989 -r b44be19d6229 mcabber/src/commands.c --- a/mcabber/src/commands.c Fri Sep 16 21:49:39 2005 +0200 +++ b/mcabber/src/commands.c Mon Sep 19 23:32:42 2005 +0200 @@ -627,8 +627,8 @@ jid = buddy_getjid(bud); name = buddy_getname(bud); type = buddy_gettype(bud); - status = buddy_getstatus(bud); - st_msg = buddy_getstatusmsg(bud); + status = buddy_getstatus(bud, NULL); + st_msg = buddy_getstatusmsg(bud, NULL); buffer = g_new(char, 128); diff -r 170f1aa12989 -r b44be19d6229 mcabber/src/hooks.c --- a/mcabber/src/hooks.c Fri Sep 16 21:49:39 2005 +0200 +++ b/mcabber/src/hooks.c Mon Sep 19 23:32:42 2005 +0200 @@ -60,7 +60,8 @@ // We need to rebuild the list if the sender is unknown or // if the sender is offline/invisible and hide_offline_buddies is set if (new_guy || - (roster_getstatus(jid) == offline && buddylist_get_hide_offline_buddies())) + (roster_getstatus(jid, NULL) == offline && + buddylist_get_hide_offline_buddies())) { buddylist_build(); update_roster = TRUE; @@ -75,13 +76,16 @@ hk_ext_cmd(jid, 'M', 'S', NULL); } -inline void hk_statuschange(const char *jid, time_t timestamp, - enum imstatus status, const char *status_msg) +inline void hk_statuschange(const char *jid, const char *resname, gchar prio, + time_t timestamp, enum imstatus status, + const char *status_msg) { - scr_LogPrint(LPRINT_LOGNORM, "Buddy status has changed: [%c>%c] <%s> %s", - imstatus2char[roster_getstatus(jid)], imstatus2char[status], jid, - ((status_msg) ? status_msg : "")); - roster_setstatus(jid, status, status_msg); + const char *rn = (resname ? resname : "default"); + scr_LogPrint(LPRINT_LOGNORM, "Buddy status has changed: [%c>%c] <%s/%s> %s", + imstatus2char[roster_getstatus(jid, resname)], + imstatus2char[status], jid, rn, + ((status_msg) ? status_msg : "")); + roster_setstatus(jid, rn, prio, status, status_msg); buddylist_build(); scr_DrawRoster(); hlog_write_status(jid, 0, status, status_msg); diff -r 170f1aa12989 -r b44be19d6229 mcabber/src/hooks.h --- a/mcabber/src/hooks.h Fri Sep 16 21:49:39 2005 +0200 +++ b/mcabber/src/hooks.h Mon Sep 19 23:32:42 2005 +0200 @@ -8,10 +8,12 @@ inline void hk_message_in(const char *jid, time_t timestamp, const char *msg, const char *type); inline void hk_message_out(const char *jid, time_t timestamp, const char *msg); -inline void hk_statuschange(const char *jid, time_t timestamp, - enum imstatus status, char const *status_msg); +inline void hk_statuschange(const char *jid, const char *resname, gchar prio, + time_t timestamp, enum imstatus status, + char const *status_msg); inline void hk_mystatuschange(time_t timestamp, - enum imstatus old_status, enum imstatus new_status, const char *msg); + enum imstatus old_status, + enum imstatus new_status, const char *msg); void hk_ext_cmd_init(const char *command); void hk_ext_cmd(const char *jid, guchar type, guchar info, const char *data); diff -r 170f1aa12989 -r b44be19d6229 mcabber/src/jabglue.c --- a/mcabber/src/jabglue.c Fri Sep 16 21:49:39 2005 +0200 +++ b/mcabber/src/jabglue.c Mon Sep 19 23:32:42 2005 +0200 @@ -99,8 +99,7 @@ char *ptr; char *alias; - while ((alias = g_strdup(jid)) == NULL) - safe_usleep(100); + alias = g_strdup(jid); if ((ptr = strchr(alias, '/')) != NULL) { *ptr = 0; @@ -710,12 +709,13 @@ void packethandler(jconn conn, jpacket packet) { char *p, *r; - const char *m; + const char *m, *rname; xmlnode x, y; char *from=NULL, *type=NULL, *body=NULL, *enc=NULL; char *ns=NULL; char *id=NULL; enum imstatus ust; + char bpprio; jb_reset_keepalive(); // reset keepalive delay jpacket_reset(packet); @@ -903,18 +903,18 @@ g_free(r); break; } - x = xmlnode_get_tag(packet->x, "show"); - ust = available; - if (x) { - p = xmlnode_get_data(x); if (p) ns = p; + p = xmlnode_get_tag_data(packet->x, "priority"); + if (p && *p) bpprio = (gchar)atoi(p); + else bpprio = 0; - if (ns) { - if (!strcmp(ns, "away")) ust = away; - else if (!strcmp(ns, "dnd")) ust = dontdisturb; - else if (!strcmp(ns, "xa")) ust = notavail; - else if (!strcmp(ns, "chat")) ust = freeforchat; - } + ust = available; + p = xmlnode_get_tag_data(packet->x, "show"); + if (p) { + if (!strcmp(p, "away")) ust = away; + else if (!strcmp(p, "dnd")) ust = dontdisturb; + else if (!strcmp(p, "xa")) ust = notavail; + else if (!strcmp(p, "chat")) ust = freeforchat; } if (type && !strcmp(type, "unavailable")) @@ -927,9 +927,11 @@ // Call hk_statuschange() if status has changed or if the // status message is different - m = roster_getstatusmsg(r); - if ((ust != roster_getstatus(r)) || (p && (!m || strcmp(p, m)))) - hk_statuschange(r, 0, ust, p); + rname = strchr(from, '/'); + if (rname) rname++; + m = roster_getstatusmsg(r, rname); + if ((ust != roster_getstatus(r, rname)) || (p && (!m || strcmp(p, m)))) + hk_statuschange(r, rname, bpprio, 0, ust, p); g_free(r); if (p) g_free(p); break; diff -r 170f1aa12989 -r b44be19d6229 mcabber/src/roster.c --- 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() diff -r 170f1aa12989 -r b44be19d6229 mcabber/src/roster.h --- a/mcabber/src/roster.h Fri Sep 16 21:49:39 2005 +0200 +++ b/mcabber/src/roster.h Mon Sep 19 23:32:42 2005 +0200 @@ -5,6 +5,20 @@ # include "jabglue.h" +enum imrole { + role_none, + role_moderator, + role_participant, + role_visitor +}; + +enum subscr { + sub_none, + sub_to, + sub_from, + sub_both +}; + enum findwhat { jidsearch, namesearch @@ -36,16 +50,16 @@ guint type); void roster_del_user(const char *jid); void roster_free(void); -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); void roster_setflags(const char *jid, guint flags, guint value); void roster_msg_setflag(const char *jid, guint value); void roster_settype(const char *jid, guint type); -enum imstatus roster_getstatus(const char *jid); -const char *roster_getstatusmsg(const char *jid); +enum imstatus roster_getstatus(const char *jid, const char *resname); +const char *roster_getstatusmsg(const char *jid, const char *resname); guint roster_gettype(const char *jid); inline guint roster_exists(const char *jidname, enum findwhat type, - guint roster_type); + guint roster_type); void buddylist_build(void); void buddy_hide_group(gpointer rosterdata, int hide); @@ -58,8 +72,8 @@ void buddy_setgroup(gpointer rosterdata, char *newgroupname); const char *buddy_getgroupname(gpointer rosterdata); gpointer buddy_getgroup(gpointer rosterdata); -enum imstatus buddy_getstatus(gpointer rosterdata); -const char *buddy_getstatusmsg(gpointer rosterdata); +enum imstatus buddy_getstatus(gpointer rosterdata, const char *resname); +const char *buddy_getstatusmsg(gpointer rosterdata, const char *resname); void buddy_setflags(gpointer rosterdata, guint flags, guint value); guint buddy_getflags(gpointer rosterdata); GList *buddy_search(char *string); diff -r 170f1aa12989 -r b44be19d6229 mcabber/src/screen.c --- a/mcabber/src/screen.c Fri Sep 16 21:49:39 2005 +0200 +++ b/mcabber/src/screen.c Mon Sep 19 23:32:42 2005 +0200 @@ -733,7 +733,7 @@ pending = '#'; } - budstate = buddy_getstatus(BUDDATA(buddy)); + budstate = buddy_getstatus(BUDDATA(buddy), NULL); if (budstate >= 0 && budstate < imstatus_size && currentstatus != offline) status = imstatus2char[budstate]; if (buddy == current_buddy) { @@ -849,7 +849,7 @@ if (!current_buddy || !newbuddy) return; if (newbuddy == current_buddy) return; - prev_st = buddy_getstatus(BUDDATA(current_buddy)); + prev_st = buddy_getstatus(BUDDATA(current_buddy), NULL); buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE); if (chatmode) alternate_buddy = current_buddy;