changeset 1215:80c095886fb5

Entity Capabilities support (XEP-0115)
author Mikael Berthe <mikael@lilotux.net>
date Tue, 08 May 2007 10:00:32 +0200
parents 9f5c5f176953
children 8645b5166040
files mcabber/libjabber/jabber.h mcabber/src/jab_iq.c mcabber/src/jab_priv.h mcabber/src/jabglue.c
diffstat 4 files changed, 131 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/mcabber/libjabber/jabber.h	Tue May 01 18:19:12 2007 +0200
+++ b/mcabber/libjabber/jabber.h	Tue May 08 10:00:32 2007 +0200
@@ -281,9 +281,11 @@
 #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_MUC       "http://jabber.org/protocol/muc"
+
+#define NS_CAPS       "http://jabber.org/protocol/caps"
 #define NS_CHATSTATES "http://jabber.org/protocol/chatstates"
 #define NS_COMMANDS   "http://jabber.org/protocol/commands"
+#define NS_MUC        "http://jabber.org/protocol/muc"
 
 #define NS_XDBGINSERT "jabber:xdb:ginsert"
 #define NS_XDBNSLIST  "jabber:xdb:nslist"
--- a/mcabber/src/jab_iq.c	Tue May 01 18:19:12 2007 +0200
+++ b/mcabber/src/jab_iq.c	Tue May 08 10:00:32 2007 +0200
@@ -34,6 +34,10 @@
 #include "hbuf.h"
 #include "commands.h"
 
+#ifdef ENABLE_HGCSET
+# include "hgcset.h"
+#endif
+
 
 // Bookmarks for IQ:private storage
 xmlnode bookmarks;
@@ -98,6 +102,41 @@
   {NULL, NULL, NULL},
 };
 
+//  entity_version()
+// Return a static version string for Entity Capabilities.
+// It should be specific to the client version, so we'll append
+// a random string if it's a dev version and there's no changeset, or
+// if it's been modified locally.
+const char *entity_version(void)
+{
+  static char *ver;
+  char *tver;
+
+  if (ver)
+    return ver;
+
+#ifdef HGCSET
+  tver = g_strdup_printf("%s-%s", PACKAGE_VERSION, HGCSET);
+  if (strlen(HGCSET) == 12)
+    ver = tver;
+#else
+  tver = g_strdup(PACKAGE_VERSION);
+  if (!strcasestr(PACKAGE_VERSION, "-dev"))
+    ver = tver; // Official release
+#endif
+
+  // If this isn't an official release, or if the changeset shows the source
+  // code has been modified locally, we alter the release id.
+  if (!ver) {
+    unsigned int n;
+    srand(time(NULL));
+    n = (1U + (unsigned int)rand()) >> 8;
+    ver = g_strdup_printf("%s~%x", tver, n);
+    g_free(tver);
+  }
+  return ver;
+}
+
 //  iqs_new(type, namespace, prefix, timeout)
 // Create a query (GET, SET) IQ structure.  This function should not be used
 // for RESULT packets.
@@ -1258,36 +1297,83 @@
   }
 }
 
+//  disco_info_set_ext(ansquery, ext)
+// Add features attributes to ansquery for extension ext.
+static void disco_info_set_ext(xmlnode ansquery, const char *ext)
+{
+  char *nodename;
+  nodename = g_strdup_printf("%s#%s", MCABBER_CAPS_NODE, ext);
+  xmlnode_put_attrib(ansquery, "node", nodename);
+  g_free(nodename);
+  if (!strcasecmp(ext, "csn")) {
+    // I guess it's ok to send this even if it's not compiled in.
+    xmlnode_put_attrib(xmlnode_insert_tag(ansquery, "feature"),
+                       "var", NS_CHATSTATES);
+  }
+}
+
+//  disco_info_set_default(ansquery, entitycaps)
+// Add features attributes to ansquery.  If entitycaps is TRUE, assume
+// that we're answering an Entity Caps request (if not, the request was
+// a basic discovery query).
+static void disco_info_set_default(xmlnode ansquery, guint entitycaps)
+{
+  xmlnode y;
+  char *eversion;
+
+  eversion = g_strdup_printf("%s#%s", MCABBER_CAPS_NODE, entity_version());
+  xmlnode_put_attrib(ansquery, "node", eversion);
+  g_free(eversion);
+
+  y = xmlnode_insert_tag(ansquery, "identity");
+  xmlnode_put_attrib(y, "category", "client");
+  xmlnode_put_attrib(y, "type", "pc");
+  xmlnode_put_attrib(y, "name", PACKAGE_NAME);
+
+  xmlnode_put_attrib(xmlnode_insert_tag(ansquery, "feature"),
+                     "var", NS_DISCO_INFO);
+  xmlnode_put_attrib(xmlnode_insert_tag(ansquery, "feature"),
+                     "var", NS_MUC);
+#ifdef JEP0085
+  // Advertise ChatStates only if we're not using Entity Capabilities
+  if (!entitycaps)
+    xmlnode_put_attrib(xmlnode_insert_tag(ansquery, "feature"),
+                       "var", NS_CHATSTATES);
+#endif
+  xmlnode_put_attrib(xmlnode_insert_tag(ansquery, "feature"),
+                     "var", NS_TIME);
+  xmlnode_put_attrib(xmlnode_insert_tag(ansquery, "feature"),
+                     "var", NS_VERSION);
+  xmlnode_put_attrib(xmlnode_insert_tag(ansquery, "feature"),
+                     "var", NS_PING);
+  xmlnode_put_attrib(xmlnode_insert_tag(ansquery, "feature"),
+                     "var", NS_COMMANDS);
+}
+
 static void handle_iq_disco_info(jconn conn, char *from, const char *id,
                                  xmlnode xmldata)
 {
-  xmlnode x, y;
+  xmlnode x;
   xmlnode myquery;
+  char *node;
 
   x = jutil_iqnew(JPACKET__RESULT, NS_DISCO_INFO);
   xmlnode_put_attrib(x, "id", id);
   xmlnode_put_attrib(x, "to", xmlnode_get_attrib(xmldata, "from"));
   myquery = xmlnode_get_tag(x, "query");
 
-  y = xmlnode_insert_tag(myquery, "identity");
-  xmlnode_put_attrib(y, "category", "client");
-  xmlnode_put_attrib(y, "type", "pc");
-  xmlnode_put_attrib(y, "name", PACKAGE_NAME);
+  node = xmlnode_get_attrib(xmlnode_get_tag(xmldata, "query"), "node");
+  if (node && startswith(node, MCABBER_CAPS_NODE "#", FALSE)) {
+    const char *param = node+strlen(MCABBER_CAPS_NODE)+1;
+    if (!strcmp(param, entity_version()))
+      disco_info_set_default(myquery, TRUE);  // client#version
+    else
+      disco_info_set_ext(myquery, param);     // client#extension
+  } else {
+    // Basic discovery request
+    disco_info_set_default(myquery, FALSE);
+  }
 
-  xmlnode_put_attrib(xmlnode_insert_tag(myquery, "feature"),
-                     "var", NS_DISCO_INFO);
-  xmlnode_put_attrib(xmlnode_insert_tag(myquery, "feature"),
-                     "var", NS_MUC);
-  xmlnode_put_attrib(xmlnode_insert_tag(myquery, "feature"),
-                     "var", NS_CHATSTATES);
-  xmlnode_put_attrib(xmlnode_insert_tag(myquery, "feature"),
-                     "var", NS_TIME);
-  xmlnode_put_attrib(xmlnode_insert_tag(myquery, "feature"),
-                     "var", NS_VERSION);
-  xmlnode_put_attrib(xmlnode_insert_tag(myquery, "feature"),
-                     "var", NS_PING);
-  xmlnode_put_attrib(xmlnode_insert_tag(myquery, "feature"),
-                     "var", NS_COMMANDS);
   jab_send(jc, x);
   xmlnode_free(x);
 }
--- a/mcabber/src/jab_priv.h	Tue May 01 18:19:12 2007 +0200
+++ b/mcabber/src/jab_priv.h	Tue May 08 10:00:32 2007 +0200
@@ -6,7 +6,10 @@
 #include "jabglue.h"
 #include "events.h"
 
-#define JABBER_AGENT_GROUP "Jabber Agents"
+/* XEP-0115 (Entity Capabilities) node */
+#define MCABBER_CAPS_NODE   "http://mcabber.lilotux.net/caps"
+
+#define JABBER_AGENT_GROUP  "Jabber Agents"
 
 enum enum_jstate {
   STATE_CONNECTING,
@@ -32,6 +35,7 @@
 extern xmlnode bookmarks, rosternotes;
 
 extern char *mcabber_version(void);
+const char *entity_version(void);
 
 
 char *jidtodisp(const char *fjid);
--- a/mcabber/src/jabglue.c	Tue May 01 18:19:12 2007 +0200
+++ b/mcabber/src/jabglue.c	Tue May 08 10:00:32 2007 +0200
@@ -323,6 +323,23 @@
   return mystatusmsg;
 }
 
+//  insert_entity_capabilities(presence_stanza)
+// Entity Capabilities (XEP-0115)
+static void insert_entity_capabilities(xmlnode x)
+{
+  xmlnode y;
+  const char *ver = entity_version();
+
+  y = xmlnode_insert_tag(x, "c");
+  xmlnode_put_attrib(y, "xmlns", NS_CAPS);
+  xmlnode_put_attrib(y, "node", MCABBER_CAPS_NODE);
+  xmlnode_put_attrib(y, "ver", ver);
+#ifdef JEP0085
+  if (!chatstates_disabled)
+    xmlnode_put_attrib(y, "ext", "csn");
+#endif
+}
+
 static void roompresence(gpointer room, void *presencedata)
 {
   const char *bjid;
@@ -435,6 +452,7 @@
   if (online) {
     const char *s_msg = (st != invisible ? msg : NULL);
     x = presnew(st, recipient, s_msg);
+    insert_entity_capabilities(x); // Entity Capabilities (XEP-0115)
 #ifdef HAVE_GPGME
     if (!do_not_sign && gpg_enabled()) {
       char *signature;