changeset 987:f47e312560af

Improve JEP22 + JEP85 support
author Mikael Berthe <mikael@lilotux.net>
date Mon, 30 Oct 2006 20:18:00 +0100
parents ed697234bd39
children 6e2bfd1ffded
files mcabber/libjabber/jabber.h mcabber/src/jabglue.c mcabber/src/roster.c mcabber/src/roster.h mcabber/src/screen.c
diffstat 5 files changed, 215 insertions(+), 77 deletions(-) [+]
line wrap: on
line diff
--- a/mcabber/libjabber/jabber.h	Sun Oct 29 11:43:00 2006 +0100
+++ b/mcabber/libjabber/jabber.h	Mon Oct 30 20:18:00 2006 +0100
@@ -281,6 +281,7 @@
 #define NS_DISCO_ITEMS "http://jabber.org/protocol/disco#items"
 #define NS_IQ_AUTH    "http://jabber.org/features/iq-auth"
 #define NS_REGISTER_FEATURE "http://jabber.org/features/iq-register"
+#define NS_CHATSTATES "http://jabber.org/protocol/chatstates"
 
 #define NS_XDBGINSERT "jabber:xdb:ginsert"
 #define NS_XDBNSLIST  "jabber:xdb:nslist"
--- a/mcabber/src/jabglue.c	Sun Oct 29 11:43:00 2006 +0100
+++ b/mcabber/src/jabglue.c	Mon Oct 30 20:18:00 2006 +0100
@@ -416,6 +416,13 @@
 {
   xmlnode x;
   gchar *strtype;
+#if defined JEP0022 || defined JEP0085
+  xmlnode event;
+  char *rname, *barejid;
+  GSList *sl_buddy;
+  guint which_jep = 0; /* 0: none, 1: 85, 2: 22 */
+  struct jep0085 *jep85 = NULL;
+#endif
 
   if (!online) return;
 
@@ -431,20 +438,49 @@
     xmlnode_insert_cdata(y, subject, (unsigned) -1);
   }
 
-  // TODO: insert event notifications request
-#undef USE_JEP_85
-#ifdef USE_JEP_85
-#define NS_CHAT_STATES    "http://jabber.org/features/chatstates"
-  // JEP-85
-  xmlnode event = xmlnode_insert_tag(x, "composing");
-  xmlnode_put_attrib(event, "xmlns", NS_CHAT_STATES);
-#else
-  // JEP-22
-  xmlnode event = xmlnode_insert_tag(x, "x");
-  xmlnode_put_attrib(event, "xmlns", NS_EVENT);
-  xmlnode_insert_tag(event, "composing");
+#if defined JEP0022 || defined JEP0085
+  rname = strchr(jid, JID_RESOURCE_SEPARATOR);
+  barejid = jidtodisp(jid);
+  sl_buddy = roster_find(barejid, jidsearch, ROSTER_TYPE_USER);
+  g_free(barejid);
+
+  // If we can get a resource name, we use it.  Else we use NULL,
+  // which hopefully will give us the most likely resource.
+  if (rname)
+    rname++;
+  if (sl_buddy)
+    jep85 = buddy_resource_jep85(sl_buddy->data, rname);
 #endif
 
+#ifdef JEP0085
+  /* JEP-0085 5.1
+   * "Until receiving a reply to the initial content message (or a standalone
+   * notification) from the Contact, the User MUST NOT send subsequent chat
+   * state notifications to the Contact."
+   * In our implementation support is initially "unknown", they it's "probed"
+   * and can become "ok".
+   */
+  if (jep85 && (jep85->support == CHATSTATES_SUPPORT_OK ||
+                jep85->support == CHATSTATES_SUPPORT_UNKNOWN)) {
+    event = xmlnode_insert_tag(x, "active");
+    xmlnode_put_attrib(event, "xmlns", NS_CHATSTATES);
+    if (jep85->support == CHATSTATES_SUPPORT_UNKNOWN)
+      jep85->support = CHATSTATES_SUPPORT_PROBED;
+    else
+      which_jep = 1;
+  }
+#endif
+#ifdef JEP0022
+  /* JEP-22
+   * If the Contact supports JEP-0085, we do not use JEP-0022.
+   * If not, we try to fall back to JEP-0022.
+   */
+  if (!which_jep) {
+    event = xmlnode_insert_tag(x, "x");
+    xmlnode_put_attrib(event, "xmlns", NS_EVENT);
+    xmlnode_insert_tag(event, "composing");
+  }
+#endif
 
   jab_send(jc, x);
   xmlnode_free(x);
@@ -1039,10 +1075,8 @@
 
   x = xmlnode_get_firstchild(xmldata);
   for ( ; x; x = xmlnode_get_nextsibling(x)) {
-    if ((p = xmlnode_get_name(x)) && !strcmp(p, "x"))
-      if ((p = xmlnode_get_attrib(x, "xmlns")) && !strcmp(p, xmlns)) {
-        break;
-    }
+    if ((p = xmlnode_get_attrib(x, "xmlns")) && !strcmp(p, xmlns))
+      break;
   }
   return x;
 }
@@ -1392,7 +1426,7 @@
 {
   char *p, *r, *s;
   xmlnode x;
-  char *body=NULL;
+  char *body = NULL;
   char *enc = NULL;
   char *tmp = NULL;
   time_t timestamp = 0;
@@ -1459,50 +1493,111 @@
   g_free(tmp);
 }
 
-void handle_state_events(char* from, xmlnode xmldata)
+void handle_state_events(char *from, xmlnode xmldata)
 {
-  xmlnode x   = NULL;
-  char *rname = strchr(from, JID_RESOURCE_SEPARATOR) + 1;
-  char *jid   = jidtodisp(from);
-  GSList *slist = roster_find(jid, jidsearch, ROSTER_TYPE_USER);
-  if (slist == NULL) return;
-  int jep85 = 0;
+#if defined JEP0022 || defined JEP0085
+  xmlnode state_ns;
+  const char *body;
+  char *rname, *jid;
+  GSList *sl_buddy;
+  guint events;
+  guint which_jep = 0; /* 0: none, 1: 85, 2: 22 */
+  struct jep0022 *jep22;
+  struct jep0085 *jep85;
+
+  rname = strchr(from, JID_RESOURCE_SEPARATOR);
+  jid   = jidtodisp(from);
+  sl_buddy = roster_find(jid, jidsearch, ROSTER_TYPE_USER);
+
+  /* XXX Actually that's wrong, since it filters out server "offline"
+     messages (for JEP-0022) */
+  if (!sl_buddy || !rname++) {
+    g_free(jid);
+    return;
+  }
 
-  guint events  = buddy_resource_getevents(slist->data, rname);
+  events = buddy_resource_getevents(sl_buddy->data, rname);
+
+  jep85 = buddy_resource_jep85(sl_buddy->data, rname);
+  if (jep85) {
+    state_ns = xml_get_xmlns(xmldata, NS_CHATSTATES);
+    if (state_ns)
+      which_jep = 1;
+  }
 
-  x = xml_get_xmlns(xmldata, NS_EVENT);
-  if (x == NULL) {
-      x = xmldata;
-      jep85 = 1;
+  if (which_jep != 1) { /* Fall back to JEP-0022 */
+    jep22 = buddy_resource_jep22(sl_buddy->data, rname);
+    if (jep22) {
+      state_ns = xml_get_xmlns(xmldata, NS_EVENT);
+      if (state_ns)
+        which_jep = 2;
+    }
+  }
+
+  if (!which_jep) { /* Sender does not use chat states */
+    g_free(jid);
+    return;
   }
 
-  xmlnode tag = xmlnode_get_tag(x, "composing");
-  if (tag != NULL) {
-    events |= ROSTER_EVENT_COMPOSING;
-  } else if (!jep85) {
-    events &= ~ROSTER_EVENT_COMPOSING;
-  }
+  body = xmlnode_get_tag_data(xmldata, "body");
+
+  if (which_jep == 1) { /* JEP-0085 */
+    const char *p;
+    jep85->support = CHATSTATES_SUPPORT_OK;
+
+    p = xmlnode_get_name(state_ns);
+    if (!strcmp(p, "composing")) {
+      jep85->last_state_rcvd = ROSTER_EVENT_COMPOSING;
+    } else if (!strcmp(p, "active")) {
+      jep85->last_state_rcvd = ROSTER_EVENT_ACTIVE;
+    } else if (!strcmp(p, "paused")) {
+      jep85->last_state_rcvd = ROSTER_EVENT_PAUSED;
+    } else if (!strcmp(p, "inactive")) {
+      jep85->last_state_rcvd = ROSTER_EVENT_INACTIVE;
+    } else if (!strcmp(p, "gone")) {
+      jep85->last_state_rcvd = ROSTER_EVENT_GONE;
+    }
 
-  if (jep85) {
-      tag = xmlnode_get_tag(x, "paused");
-      if (tag != NULL) {
+    if (jep85->last_state_rcvd == ROSTER_EVENT_COMPOSING)
+      events = ROSTER_EVENT_COMPOSING;
+    else
+      events = ROSTER_EVENT_NONE;
+  } else {              /* JEP-0022 */
+    const char *msgid;
+    jep22->support = CHATSTATES_SUPPORT_OK;
+    jep22->last_state_rcvd = ROSTER_EVENT_NONE;
+
+    msgid = xmlnode_get_attrib(xmldata, "id");
+
+    if (xmlnode_get_tag(state_ns, "composing")) {
+      // Clear composing if the message contains a body
+      if (body)
         events &= ~ROSTER_EVENT_COMPOSING;
-      }
+      else
+        events |= ROSTER_EVENT_COMPOSING;
+      jep22->last_state_rcvd |= ROSTER_EVENT_COMPOSING;
+
+    } else {
+      events &= ~ROSTER_EVENT_COMPOSING;
+    }
+
+    if (xmlnode_get_tag(state_ns, "delivered"))
+        jep22->last_state_rcvd |= ROSTER_EVENT_DELIVERED;
+
+    // Cache the message id
+    g_free(jep22->last_msgid_rcvd);
+    if (msgid)
+      jep22->last_msgid_rcvd = g_strdup(msgid);
+    else
+      jep22->last_msgid_rcvd = NULL;
   }
 
-  // clear composing and set new message event
-  // if message contains message body
-  if (xmlnode_get_tag_data(xmldata, "body") != NULL) {
-    events |= ROSTER_EVENT_MSG;
-    events &= ~ROSTER_EVENT_COMPOSING;
-  }
+  buddy_resource_setevents(sl_buddy->data, rname, events);
 
-  buddy_resource_setevents(slist->data, rname, events);
-
-  scr_UpdateBuddyWindow();
-  scr_DrawRoster();
+  update_roster = TRUE;
 
   g_free(jid);
+#endif
 }
 
 static void evscallback_subscription(eviqs *evp, guint evcontext)
@@ -1643,10 +1738,6 @@
 static void packethandler(jconn conn, jpacket packet)
 {
   char *p;
-  /*
-  char *r, *s;
-  const char *m;
-  */
   char *from=NULL, *type=NULL;
 
   jb_reset_keepalive(); // reset keepalive timeout
@@ -1664,7 +1755,7 @@
   if (p) from = p;
 
   if (!from && packet->type != JPACKET_IQ) {
-    scr_LogPrint(LPRINT_LOGNORM, "Error in packet (could be UTF8-related)");
+    scr_LogPrint(LPRINT_LOGNORM, "Error in stream packet");
     return;
   }
 
--- a/mcabber/src/roster.c	Sun Oct 29 11:43:00 2006 +0100
+++ b/mcabber/src/roster.c	Mon Oct 30 20:18:00 2006 +0100
@@ -52,6 +52,12 @@
   enum imaffiliation affil;
   gchar *realjid;       /* for chatrooms, if buddy's real jid is known */
   guint events;
+#ifdef JEP0022
+  struct jep0022 jep22;
+#endif
+#ifdef JEP0085
+  struct jep0085 jep85;
+#endif
 } res;
 
 /* This is a private structure type for the roster */
@@ -115,6 +121,10 @@
     g_free((gchar*)p_res->status_msg);
     g_free((gchar*)p_res->name);
     g_free((gchar*)p_res->realjid);
+#ifdef JEP0022
+    g_free(p_res->jep22.last_msgid_sent);
+    g_free(p_res->jep22.last_msgid_rcvd);
+#endif
   }
   // Free all nodes but the first (which is static)
   g_slist_free(*reslist);
@@ -209,6 +219,10 @@
   g_free(p_res->name);
   g_free(p_res->status_msg);
   g_free(p_res->realjid);
+#ifdef JEP0022
+  g_free(p_res->jep22.last_msgid_sent);
+  g_free(p_res->jep22.last_msgid_rcvd);
+#endif
   rost->resource = g_slist_delete_link(rost->resource, p_res_elt);
   return;
 }
@@ -1079,14 +1093,28 @@
   res *p_res = get_resource(roster_usr, resname);
   if (p_res)
     p_res->events = events;
+}
 
-  /*
-  // update group
-  roster_usr = roster_usr->list->data;
-  p_res = get_resource(roster_usr, "");
+struct jep0022 *buddy_resource_jep22(gpointer rosterdata, const char *resname)
+{
+#ifdef JEP0022
+  roster *roster_usr = rosterdata;
+  res *p_res = get_resource(roster_usr, resname);
   if (p_res)
-    p_res->events = events;
-  */
+    return &p_res->jep22;
+#endif
+  return NULL;
+}
+
+struct jep0085 *buddy_resource_jep85(gpointer rosterdata, const char *resname)
+{
+#ifdef JEP0085
+  roster *roster_usr = rosterdata;
+  res *p_res = get_resource(roster_usr, resname);
+  if (p_res)
+    return &p_res->jep85;
+#endif
+  return NULL;
 }
 
 enum imrole buddy_getrole(gpointer rosterdata, const char *resname)
--- a/mcabber/src/roster.h	Sun Oct 29 11:43:00 2006 +0100
+++ b/mcabber/src/roster.h	Mon Oct 30 20:18:00 2006 +0100
@@ -74,21 +74,42 @@
 #define ROSTER_FLAG_USRLOCK (1U<<3) // Node should not be removed from buddylist
 // ROSTER_FLAG_LOCAL   (1U<<4) // Buddy not on server's roster  (??)
 
+#define JEP0022
+#define JEP0085
+
+struct jep0022 {
+  guint support;
+  guint last_state_sent;
+  gchar *last_msgid_sent;
+  guint last_state_rcvd;
+  gchar *last_msgid_rcvd;
+};
+struct jep0085 {
+  guint support;
+  guint last_state_sent;
+  guint last_state_rcvd;
+};
+
+enum chatstate_support {
+  CHATSTATES_SUPPORT_UNKNOWN = 0,
+  CHATSTATES_SUPPORT_PROBED,
+  CHATSTATES_SUPPORT_NONE,
+  CHATSTATES_SUPPORT_OK
+};
 
 /* Message event and chat state flags */
 #define ROSTER_EVENT_NONE      0U
-#define ROSTER_EVENT_MSG       1U
 /* JEP-22 Message Events */
-#define ROSTER_EVENT_OFFLINE   (1U<<1)
-#define ROSTER_EVENT_DELIVERED (1U<<2)
-#define ROSTER_EVENT_DISPLAYED (1U<<3)
+#define ROSTER_EVENT_OFFLINE   (1U<<0)
+#define ROSTER_EVENT_DELIVERED (1U<<1)
+#define ROSTER_EVENT_DISPLAYED (1U<<2)
 /* JEP-22 & JEP-85 */
-#define ROSTER_EVENT_COMPOSING (1U<<4)
+#define ROSTER_EVENT_COMPOSING (1U<<3)
 /* JEP-85 Chat State Notifications */
-#define ROSTER_EVENT_ACTIVE    (1U<<5)
-#define ROSTER_EVENT_PAUSED    (1U<<6)
-#define ROSTER_EVENT_INACTIVE  (1U<<7)
-#define ROSTER_EVENT_GONE      (1U<<8)
+#define ROSTER_EVENT_ACTIVE    (1U<<4)
+#define ROSTER_EVENT_PAUSED    (1U<<5)
+#define ROSTER_EVENT_INACTIVE  (1U<<6)
+#define ROSTER_EVENT_GONE      (1U<<7)
 
 extern GList *buddylist;
 extern GList *current_buddy;
@@ -154,6 +175,8 @@
 void    buddy_resource_setevents(gpointer rosterdata, const char *resname,
                                  guint event);
 guint   buddy_resource_getevents(gpointer rosterdata, const char *resname);
+struct jep0022 *buddy_resource_jep22(gpointer rosterdata, const char *resname);
+struct jep0085 *buddy_resource_jep85(gpointer rosterdata, const char *resname);
 enum imrole buddy_getrole(gpointer rosterdata, const char *resname);
 enum imaffiliation buddy_getaffil(gpointer rosterdata, const char *resname);
 const char *buddy_getrjid(gpointer rosterdata, const char *resname);
--- a/mcabber/src/screen.c	Sun Oct 29 11:43:00 2006 +0100
+++ b/mcabber/src/screen.c	Mon Oct 30 20:18:00 2006 +0100
@@ -1191,14 +1191,9 @@
     for ( ; resources ; resources = g_slist_next(resources) ) {
       guint events = buddy_resource_getevents(BUDDATA(buddy),
                                               resources ? resources->data : "");
-      if (events) {
-        if (events & ROSTER_EVENT_MSG && false) { // FIXME: not yet.
-          pending = '#';
-          break;
-        } else if (events & ROSTER_EVENT_COMPOSING) {
-          pending = '+';
-          break;
-        }
+      if (events & ROSTER_EVENT_COMPOSING) {
+        pending = '+';
+        break;
       }
     }