changeset 990:35e7913affb7

Send events/chatstates notifications (JEP-22/JEP-85)
author Mikael Berthe <mikael@lilotux.net>
date Wed, 01 Nov 2006 00:57:56 +0100
parents 859ab76e5093
children ef10906691bb
files mcabber/src/jabglue.c mcabber/src/jabglue.h mcabber/src/screen.c
diffstat 3 files changed, 162 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/mcabber/src/jabglue.c	Tue Oct 31 20:30:29 2006 +0100
+++ b/mcabber/src/jabglue.c	Wed Nov 01 00:57:56 2006 +0100
@@ -481,6 +481,7 @@
       jep85->support = CHATSTATES_SUPPORT_PROBED;
     else
       which_jep = 1;
+    jep85->last_state_sent = ROSTER_EVENT_ACTIVE;
   }
 #endif
 #ifdef JEP0022
@@ -489,19 +490,22 @@
    * If not, we try to fall back to JEP-0022.
    */
   if (!which_jep) {
+    struct jep0022 *jep22 = NULL;
     event = xmlnode_insert_tag(x, "x");
     xmlnode_put_attrib(event, "xmlns", NS_EVENT);
     xmlnode_insert_tag(event, "composing");
 
+    if (sl_buddy)
+      jep22 = buddy_resource_jep22(sl_buddy->data, rname);
+    if (jep22)
+      jep22->last_state_sent = ROSTER_EVENT_ACTIVE;
+
     // An id is mandatory when using JEP-0022.
     if (!msgid && (text || subject)) {
-      struct jep0022 *jep22;
       msgid = new_msgid();
       // Let's update last_msgid_sent
       // (We do not update it when the msgid is provided by the caller,
       // because this is probably a special message...)
-      if (sl_buddy)
-        jep22 = buddy_resource_jep22(sl_buddy->data, rname);
       if (jep22) {
         g_free(jep22->last_msgid_sent);
         jep22->last_msgid_sent = g_strdup(msgid);
@@ -518,10 +522,68 @@
   jb_reset_keepalive();
 }
 
+
+#ifdef JEP0085
+//  jb_send_jep85_chatstate()
+// Send a JEP-85 chatstate.
+static void jb_send_jep85_chatstate(const char *jid, guint state)
+{
+  xmlnode x;
+  xmlnode event;
+  char *rname, *barejid;
+  GSList *sl_buddy;
+  const char *chattag;
+  struct jep0085 *jep85 = NULL;
+
+  if (!online) return;
+
+  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);
+
+  if (!jep85 || (jep85->support != CHATSTATES_SUPPORT_OK))
+    return;
+
+  if (state == jep85->last_state_sent)
+    return;
+
+  if (state == ROSTER_EVENT_ACTIVE)
+    chattag = "active";
+  else if (state == ROSTER_EVENT_COMPOSING)
+    chattag = "composing";
+  else if (state == ROSTER_EVENT_PAUSED)
+    chattag = "paused";
+  else {
+    scr_LogPrint(LPRINT_LOGNORM, "Error: unsupported JEP-85 state (%d)", state);
+    return;
+  }
+
+  jep85->last_state_sent = state;
+
+  x = jutil_msgnew(TMSG_CHAT, (char*)jid, NULL, NULL);
+
+  event = xmlnode_insert_tag(x, chattag);
+  xmlnode_put_attrib(event, "xmlns", NS_CHATSTATES);
+
+  jab_send(jc, x);
+  xmlnode_free(x);
+
+  jb_reset_keepalive();
+}
+#endif
+
 #ifdef JEP0022
 //  jb_send_jep22_event()
 // Send a JEP-22 message event (delivered, composing...).
-void jb_send_jep22_event(const char *jid, guint type)
+static void jb_send_jep22_event(const char *jid, guint type)
 {
   xmlnode x;
   xmlnode event;
@@ -529,6 +591,7 @@
   char *rname, *barejid;
   GSList *sl_buddy;
   struct jep0022 *jep22 = NULL;
+  guint jep22_state;
 
   if (!online) return;
 
@@ -544,8 +607,27 @@
   if (sl_buddy)
     jep22 = buddy_resource_jep22(sl_buddy->data, rname);
 
-  if (jep22)
-    msgid = jep22->last_msgid_rcvd;
+  if (!jep22)
+    return; // XXX Maybe we could try harder (other resources?)
+
+  msgid = jep22->last_msgid_rcvd;
+
+  // For composing events (composing, active, inactive, paused...),
+  // JEP22 only has 2 states; we'll use composing and active.
+  if (type == ROSTER_EVENT_COMPOSING)
+    jep22_state = ROSTER_EVENT_COMPOSING;
+  else if (type == ROSTER_EVENT_ACTIVE ||
+           type == ROSTER_EVENT_PAUSED)
+    jep22_state = ROSTER_EVENT_ACTIVE;
+  else
+    jep22_state = 0; // ROSTER_EVENT_NONE
+
+  if (jep22_state) {
+    // Do not re-send a same event
+    if (jep22_state == jep22->last_state_sent)
+      return;
+    jep22->last_state_sent = jep22_state;
+  }
 
   x = jutil_msgnew(TMSG_CHAT, (char*)jid, NULL, NULL);
 
@@ -555,7 +637,7 @@
     xmlnode_insert_tag(event, "delivered");
   else if (type == ROSTER_EVENT_COMPOSING)
     xmlnode_insert_tag(event, "composing");
-  xmlnode_put_attrib(event, "id", (msgid ? msgid : ""));
+  xmlnode_put_attrib(event, "id", msgid);
 
   jab_send(jc, x);
   xmlnode_free(x);
@@ -564,6 +646,34 @@
 }
 #endif
 
+#if defined JEP0022 || defined JEP0085
+void jb_send_chatstate(gpointer buddy, guint chatstate)
+{
+  const char *jid;
+  struct jep0085 *jep85 = NULL;
+  struct jep0022 *jep22 = NULL;
+
+  jid = buddy_getjid(buddy);
+  if (!jid) return;
+
+#ifdef JEP0085
+  jep85 = buddy_resource_jep85(buddy, NULL);
+  if (jep85 && jep85->support == CHATSTATES_SUPPORT_OK) {
+    // FIXME: compare w/ last state sent...
+    jb_send_jep85_chatstate(jid, chatstate);
+    return;
+  }
+#endif
+#ifdef JEP0022
+  jep22 = buddy_resource_jep22(buddy, NULL);
+  if (jep22 && jep22->support == CHATSTATES_SUPPORT_OK) {
+    // FIXME: compare w/ last state sent...
+    jb_send_jep22_event(jid, chatstate);
+  }
+#endif
+}
+#endif
+
 //  jb_subscr_send_auth(jid)
 // Allow jid to receive our presence updates
 void jb_subscr_send_auth(const char *jid)
@@ -1572,14 +1682,14 @@
 void handle_state_events(char *from, xmlnode xmldata)
 {
 #if defined JEP0022 || defined JEP0085
-  xmlnode state_ns;
+  xmlnode state_ns = NULL;
   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;
+  struct jep0022 *jep22 = NULL;
+  struct jep0085 *jep85 = NULL;
 
   rname = strchr(from, JID_RESOURCE_SEPARATOR);
   jid   = jidtodisp(from);
--- a/mcabber/src/jabglue.h	Tue Oct 31 20:30:29 2006 +0100
+++ b/mcabber/src/jabglue.h	Wed Nov 01 00:57:56 2006 +0100
@@ -52,6 +52,7 @@
 void jb_send_msg(const char *jid, const char *text, int type,
                  const char *subject, const char *id);
 void jb_send_raw(const char *str);
+void jb_send_chatstate(gpointer buddy, guint chatstate);
 void jb_keepalive(void);
 inline void jb_reset_keepalive(void);
 void jb_set_keepalive_delay(unsigned int delay);
--- a/mcabber/src/screen.c	Tue Oct 31 20:30:29 2006 +0100
+++ b/mcabber/src/screen.c	Wed Nov 01 00:57:56 2006 +0100
@@ -95,6 +95,9 @@
 static GList *cmdhisto_cur;
 static char   cmdhisto_backup[INPUTLINE_LENGTH+1];
 
+static int    chatstate;
+static bool   lock_chatstate;
+
 #define MAX_KEYSEQ_LENGTH 8
 
 typedef struct {
@@ -1408,6 +1411,30 @@
   }
 }
 
+//  set_chatstate(state)
+// Set the current chat state (0=active, 1=composing, 2=paused)
+static inline void set_chatstate(int state)
+{
+#if defined JEP0022 || defined JEP0085
+  if (!chatmode)
+    state = 0;
+  if (state != chatstate) {
+    chatstate = state;
+    if (current_buddy &&
+        buddy_gettype(BUDDATA(current_buddy)) == ROSTER_TYPE_USER) {
+      guint jep_state;
+      if (chatstate == 1)
+        jep_state = ROSTER_EVENT_COMPOSING;
+      else if (chatstate == 2)
+        jep_state = ROSTER_EVENT_PAUSED;
+      else
+        jep_state = ROSTER_EVENT_ACTIVE;
+      jb_send_chatstate(BUDDATA(current_buddy), jep_state);
+    }
+  }
+#endif
+}
+
 //  set_current_buddy(newbuddy)
 // Set the current_buddy to newbuddy (if not NULL)
 // Lock the newbuddy, and unlock the previous current_buddy
@@ -1422,6 +1449,11 @@
   if (!current_buddy || !newbuddy)  return;
   if (newbuddy == current_buddy)    return;
 
+  // We're moving to another buddy.  We're thus inactive wrt current_buddy.
+  set_chatstate(0);
+  // We don't want the chatstate to be changed again right now.
+  lock_chatstate = true;
+
   prev_st = buddy_getstatus(BUDDATA(current_buddy), NULL);
   buddy_setflags(BUDDATA(current_buddy), ROSTER_FLAG_LOCK, FALSE);
   if (chatmode)
@@ -2530,6 +2562,8 @@
   int key = kcode.value;
   int display_char = FALSE;
 
+  lock_chatstate = false;
+
   switch (kcode.mcode) {
     case 0:
         break;
@@ -2753,6 +2787,13 @@
   if (completion_started && key != 9 && key != KEY_RESIZE)
     scr_end_current_completion();
   refresh_inputline();
+
+  if (!lock_chatstate) {
+    if (inputLine[0] == 0 || inputLine[0] == COMMAND_CHAR)
+      set_chatstate(0);
+    else
+      set_chatstate(1);
+  }
   return 0;
 }