changeset 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 170f1aa12989
children 63562fd409a1
files mcabber/src/commands.c mcabber/src/hooks.c mcabber/src/hooks.h mcabber/src/jabglue.c mcabber/src/roster.c mcabber/src/roster.h mcabber/src/screen.c
diffstat 7 files changed, 235 insertions(+), 64 deletions(-) [+]
line wrap: on
line diff
--- 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);
 
--- 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);
--- 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);
--- 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;
--- 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()
--- 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);
--- 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;