Improve MUC support (Myhailo Danylenko)
authorMikael Berthe <mikael@lilotux.net>
Mon, 14 Mar 2011 13:17:17 +0100
changeset 1953 9f443617e96b
parent 1952 975f6f26b052
child 1954 256cfc706ae5
Improve MUC support (Myhailo Danylenko) This patch should allow handling of multiple statuses in MUC rooms. Patch merged from isbear's mcabber-experimental repository.
mcabber/mcabber/xmpp.c
mcabber/mcabber/xmpp_muc.c
mcabber/mcabber/xmpp_muc.h
--- a/mcabber/mcabber/xmpp.c	Mon Mar 14 13:07:55 2011 +0100
+++ b/mcabber/mcabber/xmpp.c	Mon Mar 14 13:17:17 2011 +0100
@@ -1294,7 +1294,7 @@
   if (from) {
     x = lm_message_node_find_xmlns(m->node, NS_MUC_USER);
     if (x && !strcmp(x->name, "x"))
-      got_muc_message(from, x);
+      got_muc_message(from, x, timestamp);
 
     x = lm_message_node_find_xmlns(m->node, NS_X_CONFERENCE);
 
--- a/mcabber/mcabber/xmpp_muc.c	Mon Mar 14 13:07:55 2011 +0100
+++ b/mcabber/mcabber/xmpp_muc.c	Mon Mar 14 13:17:17 2011 +0100
@@ -401,8 +401,6 @@
                          enum imstatus ust, const char *ustmsg,
                          time_t usttime, char bpprio)
 {
-  LmMessageNode *y;
-  const char *p;
   char *mbuf;
   const char *ournick;
   enum imrole mbrole = role_none;
@@ -412,6 +410,8 @@
   const char *mbjid = NULL, *mbnick = NULL;
   const char *actorjid = NULL, *reason = NULL;
   bool new_member = FALSE; // True if somebody else joins the room (not us)
+  bool our_presence = FALSE; // True if this presence is from us (i.e. bears
+                             // code 110)
   guint statuscode = 0;
   guint nickchange = 0;
   GSList *room_elt;
@@ -442,30 +442,95 @@
 
   if (!ournick) {
     // It shouldn't happen, probably a server issue
-    mbuf = g_strdup_printf("Unexpected groupchat packet!");
-
-    scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf);
-    scr_WriteIncomingMessage(roomjid, mbuf, 0, HBB_PREFIX_INFO, 0);
-    g_free(mbuf);
+    const gchar msg[] = "Unexpected groupchat packet!";
+    scr_LogPrint(LPRINT_LOGNORM, msg);
+    scr_WriteIncomingMessage(roomjid, msg, 0, HBB_PREFIX_INFO, 0);
     // Send back an unavailable packet
     xmpp_setstatus(offline, roomjid, "", TRUE);
     scr_draw_roster();
     return;
   }
 
-  // Get the status code
-  // 201: a room has been created
-  // 301: the user has been banned from the room
-  // 303: new room nickname
-  // 307: the user has been kicked from the room
-  // 321,322,332: the user has been removed from the room
-  y = lm_message_node_find_child(xmldata, "status");
-  if (y) {
-    p = lm_message_node_get_attribute(y, "code");
-    if (p)
-      statuscode = atoi(p);
+#define SETSTATUSCODE(VALUE)                                              \
+{                                                                         \
+  if (G_UNLIKELY(statuscode))                                             \
+    scr_LogPrint(LPRINT_DEBUG, "handle_muc_presence: WARNING: "           \
+                 "replacing status code %u with %u.", statuscode, VALUE); \
+  statuscode = VALUE;                                                     \
+}
+
+  { // Get the status code
+    LmMessageNode *node;
+    for (node = xmldata -> children; node; node = node -> next) {
+      if (!g_strcmp0(node -> name, "status")) {
+        const char *codestr = lm_message_node_get_attribute(node, "code");
+        if (codestr) {
+          const char *mesg = NULL;
+          switch (atoi(codestr)) {
+            // initial
+            case 100:
+                    mesg = "The room is not anonymous.";
+                    break;
+            case 110: // It is our presence
+                    our_presence = TRUE;
+                    break;
+            // initial
+            case 170:
+                    mesg = "The room is logged.";
+                    break;
+            // initial
+            case 201: // Room created
+                    SETSTATUSCODE(201);
+                    break;
+            // initial
+            case 210: // Your nick change (on join)
+                    // FIXME: print nick
+                    mesg = "The room has changed your nick!";
+                    buddy_setnickname(room_elt->data, rname);
+                    ournick = rname;
+                    break;
+            case 301: // User banned
+                    SETSTATUSCODE(301);
+                    break;
+            case 303: // Nick change
+                    SETSTATUSCODE(303);
+                    break;
+            case 307: // User kicked
+                    SETSTATUSCODE(307);
+                    break;
+                    // XXX (next three)
+            case 321:
+                    mesg = "User leaves room due to affilation change.";
+                    break;
+            case 322:
+                    mesg = "User leaves room, as room is only for members now.";
+                    break;
+            case 332:
+                    mesg = "User leaves room due to system shutdown.";
+                    break;
+            default:
+                    scr_LogPrint(LPRINT_DEBUG,
+                           "handle_muc_presence: Unknown MUC status code: %s.",
+                           codestr);
+                    break;
+          }
+          if (mesg) {
+            scr_WriteIncomingMessage(roomjid, mesg, usttime,
+                                     HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
+            if (log_muc_conf)
+              hlog_write_message(roomjid, 0, -1, mesg);
+          }
+        }
+      }
+    }
   }
 
+#undef SETSTATUSCODE
+
+  if (!our_presence)
+    if (ournick && !strcmp(ournick, rname))
+      our_presence = TRUE;
+
   // Get the room's "print_status" settings
   printstatus = buddy_getprintstatus(room_elt->data);
   if (printstatus == status_default) {
@@ -488,7 +553,7 @@
     g_free(mbuf);
     buddy_resource_setname(room_elt->data, rname, mbnick);
     // Maybe it's _our_ nickname...
-    if (ournick && !strcmp(rname, ournick))
+    if (our_presence)
       buddy_setnickname(room_elt->data, mbnick);
     nickchange = TRUE;
   }
@@ -497,7 +562,6 @@
   if (statuscode != 303 && ust == offline) {
     // Somebody is leaving
     enum { leave=0, kick, ban } how = leave;
-    bool we_left = FALSE;
 
     if (statuscode == 307)
       how = kick;
@@ -505,8 +569,7 @@
       how = ban;
 
     // If this is a leave, check if it is ourself
-    if (ournick && !strcmp(rname, ournick)) {
-      we_left = TRUE; // _We_ have left! (kicked, banned, etc.)
+    if (our_presence) {
       buddy_setinsideroom(room_elt->data, FALSE);
       buddy_setnickname(room_elt->data, NULL);
       buddy_del_all_resources(room_elt->data);
@@ -531,7 +594,7 @@
       }
       if (reason)
         reason_msg = g_strdup_printf("\nReason: %s", reason);
-      if (we_left)
+      if (our_presence)
         mbuf = g_strdup_printf("You have been %s%s", mbuf_end,
                                reason_msg ? reason_msg : "");
       else
@@ -542,7 +605,7 @@
       g_free(mbuf_end);
     } else {
       // Natural leave
-      if (we_left) {
+      if (our_presence) {
         LmMessageNode *destroynode = lm_message_node_find_child(xmldata,
                                                                 "destroy");
         if (destroynode) {
@@ -575,9 +638,9 @@
 
     // Display the mbuf message if we're concerned
     // or if the print_status isn't set to none.
-    if (we_left || printstatus != status_none) {
+    if (our_presence || printstatus != status_none) {
       msgflags = HBB_PREFIX_INFO;
-      if (!we_left && settings_opt_get_int("muc_flag_joins") != 2)
+      if (!our_presence && settings_opt_get_int("muc_flag_joins") != 2)
         msgflags |= HBB_PREFIX_NOFLAG;
       scr_WriteIncomingMessage(roomjid, mbuf, usttime, msgflags, 0);
     }
@@ -585,26 +648,31 @@
     if (log_muc_conf)
       hlog_write_message(roomjid, 0, -1, mbuf);
 
-    if (we_left) {
+    if (our_presence) {
       scr_LogPrint(LPRINT_LOGNORM, "%s", mbuf);
       g_free(mbuf);
       return;
     }
     g_free(mbuf);
-  } else if (buddy_getstatus(room_elt->data, rname) == offline &&
-             ust != offline) {
-    // Somebody is joining
-    new_member = muc_handle_join(room_elt, rname, roomjid, ournick,
-                                 printstatus, usttime, log_muc_conf);
   } else {
-    // This is a simple member status change
+    enum imstatus old_ust = buddy_getstatus(room_elt->data, rname);
+    if (old_ust == offline && ust != offline) {
+      // Somebody is joining
+      new_member = muc_handle_join(room_elt, rname, roomjid, ournick,
+                                   printstatus, usttime, log_muc_conf);
+    } else {
+      // This is a simple member status change
 
-    if (printstatus == status_all && !nickchange) {
-      mbuf = g_strdup_printf("Member status has changed: %s [%c] %s", rname,
-                             imstatus2char[ust], ((ustmsg) ? ustmsg : ""));
-      scr_WriteIncomingMessage(roomjid, mbuf, usttime,
-                               HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
-      g_free(mbuf);
+      if (printstatus == status_all && !nickchange) {
+        const char *old_ustmsg = buddy_getstatusmsg(room_elt->data, rname);
+        if (old_ust != ust || g_strcmp0(old_ustmsg, ustmsg)) {
+          mbuf = g_strdup_printf("Member status has changed: %s [%c] %s", rname,
+                                 imstatus2char[ust], ((ustmsg) ? ustmsg : ""));
+          scr_WriteIncomingMessage(roomjid, mbuf, usttime,
+                                 HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
+          g_free(mbuf);
+        }
+      }
     }
   }
 
@@ -727,26 +795,90 @@
 
 
 // Specific MUC message handling (for example invitation processing)
-void got_muc_message(const char *from, LmMessageNode *x)
+void got_muc_message(const char *from, LmMessageNode *x, time_t timestamp)
 {
-  LmMessageNode *invite = lm_message_node_get_child(x, "invite");
-  if (invite)
-  {
+  LmMessageNode *node;
+  // invitation
+  node = lm_message_node_get_child(x, "invite");
+  if (node) {
     const char *invite_from;
     const char *reason = NULL;
     const char *password = NULL;
 
-    invite_from = lm_message_node_get_attribute(invite, "from");
-    reason = lm_message_node_get_child_value(invite, "reason");
-    password = lm_message_node_get_child_value(invite, "password");
+    invite_from = lm_message_node_get_attribute(node, "from");
+    reason = lm_message_node_get_child_value(node, "reason");
+    password = lm_message_node_get_child_value(node, "password");
     if (invite_from)
       got_invite(invite_from, from, reason, password, TRUE);
   }
-  // TODO
-  // handle status code = 100 ( not anonymous )
-  // handle status code = 170 ( changement de config )
-  // 10.2.1 Notification of Configuration Changes
+
   // declined invitation
+  node = lm_message_node_get_child(x, "decline");
+  if (node) {
+    const char *decline_from = lm_message_node_get_attribute(node, "from");
+    const char *reason = lm_message_node_get_child_value(node, "reason");
+    if (decline_from) {
+      if (reason)
+        scr_LogPrint(LPRINT_LOGNORM, "<%s> declines your invitation: %s.",
+                     from, reason);
+      else
+        scr_LogPrint(LPRINT_LOGNORM, "<%s> declines your invitation.", from);
+    }
+  }
+
+  // status codes
+  for (node = x -> children; node; node = node -> next) {
+    if (!g_strcmp0(node -> name, "status")) {
+      const char *codestr = lm_message_node_get_attribute(node, "code");
+      if (codestr) {
+        const char *mesg = NULL;
+        switch (atoi(codestr)) {
+          // initial
+          case 100:
+                  mesg = "The room is not anonymous.";
+                  break;
+          case 101:
+                  mesg = "Your affilation has changed while absent.";
+                  break;
+          case 102:
+                  mesg = "The room shows unavailable members.";
+                  break;
+          case 103:
+                  mesg = "The room does not show unavailable members.";
+                  break;
+          case 104:
+                  mesg = "The room configuration has changed.";
+                  break;
+          case 170:
+                  mesg = "The room is logged.";
+                  break;
+          case 171:
+                  mesg = "The room is not logged.";
+                  break;
+          case 172:
+                  mesg = "The room is not anonymous.";
+                  break;
+          case 173:
+                  mesg = "The room is semi-anonymous.";
+                  break;
+          case 174:
+                  mesg = "The room is anonymous.";
+                  break;
+          default:
+                  scr_LogPrint(LPRINT_DEBUG,
+                               "got_muc_message: Unknown MUC status code: %s.",
+                               codestr);
+                  break;
+        }
+        if (mesg) {
+          scr_WriteIncomingMessage(from, mesg, timestamp,
+                                   HBB_PREFIX_INFO|HBB_PREFIX_NOFLAG, 0);
+        if (settings_opt_get_int("log_muc_conf"))
+            hlog_write_message(from, 0, -1, mesg);
+        }
+      }
+    }
+  }
 }
 
 /* vim: set et cindent cinoptions=>2\:2(0 ts=2 sw=2:  For Vim users... */
--- a/mcabber/mcabber/xmpp_muc.h	Mon Mar 14 13:07:55 2011 +0100
+++ b/mcabber/mcabber/xmpp_muc.h	Mon Mar 14 13:17:17 2011 +0100
@@ -14,7 +14,8 @@
 void roompresence(gpointer room, void *presencedata);
 void got_invite(const char* from, const char *to, const char* reason,
                 const char* passwd, gboolean reply);
-void got_muc_message(const char *from, LmMessageNode *x);
+void got_muc_message(const char *from, LmMessageNode *x,
+                     time_t timestamp);
 void handle_muc_presence(const char *from, LmMessageNode * xmldata,
                          const char *roomjid, const char *rname,
                          enum imstatus ust, const char *ustmsg,