changeset 1600:c5ee395fbc8c

Updated Entity Capabilities support (XEP-0115)
author franky
date Tue, 23 Sep 2008 10:59:25 +0200
parents dcd5d4c75199
children 3efc92a48945
files mcabber/src/Makefile.am mcabber/src/caps.c mcabber/src/caps.h mcabber/src/main.c mcabber/src/roster.c mcabber/src/roster.h mcabber/src/xmpp.c mcabber/src/xmpp_defines.h mcabber/src/xmpp_helper.c mcabber/src/xmpp_helper.h mcabber/src/xmpp_iq.c
diffstat 11 files changed, 364 insertions(+), 112 deletions(-) [+]
line wrap: on
line diff
--- a/mcabber/src/Makefile.am	Sun Oct 11 15:39:32 2009 +0200
+++ b/mcabber/src/Makefile.am	Tue Sep 23 10:59:25 2008 +0200
@@ -5,7 +5,7 @@
 		  settings.c settings.h hooks.c hooks.h utf8.c utf8.h \
 		  histolog.c histolog.h utils.c utils.h pgp.c pgp.h \
 		  fifo.c fifo.h help.c help.h \
-		  xmpp.c xmpp.h xmpp_helper.h
+		  xmpp.c xmpp.h xmpp_helper.h caps.c caps.h
 
 if OTR
 mcabber_SOURCES += otr.c otr.h nohtml.c nohtml.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/src/caps.c	Tue Sep 23 10:59:25 2008 +0200
@@ -0,0 +1,176 @@
+/*
+ * caps.c       -- Entity Capabilities Cache for mcabber
+ *
+ * Copyright (C) 2008 Frank Zschockelt <mcabber@freakysoft.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <glib.h>
+
+typedef struct {
+  char *category;
+  char *name;
+  char *type;
+  GHashTable *features;
+} caps;
+
+static GHashTable *caps_cache = NULL;
+
+void caps_destroy(gpointer data)
+{
+  caps *c = data;
+  g_free(c->category);
+  g_free(c->name);
+  g_free(c->type);
+  g_hash_table_destroy(c->features);
+  g_free(c);
+}
+
+void caps_init(void)
+{
+  if (!caps_cache)
+    caps_cache = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                       g_free, caps_destroy);
+}
+
+void caps_free(void)
+{
+  if (caps_cache) {
+    g_hash_table_destroy(caps_cache);
+    caps_cache = NULL;
+  }
+}
+
+void caps_add(char *hash)
+{
+  if (!hash)
+    return;
+  caps *c = g_new0(caps, 1);
+  c->features = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+  g_hash_table_insert(caps_cache, g_strdup(hash), c);
+}
+
+int caps_has_hash(const char *hash)
+{
+  return (hash != NULL && (g_hash_table_lookup(caps_cache, hash) != NULL));
+}
+
+void caps_set_identity(char *hash,
+                       const char *category,
+                       const char *name,
+                       const char *type)
+{
+  caps *c;
+  if (!hash)
+    return;
+
+  c = g_hash_table_lookup(caps_cache, hash);
+  if (c) {
+    c->category = g_strdup(category);
+    c->name = g_strdup(name);
+    c->type = g_strdup(type);
+  }
+}
+
+void caps_add_feature(char *hash, const char *feature)
+{
+  caps *c;
+  if (!hash)
+    return;
+  c = g_hash_table_lookup(caps_cache, hash);
+  if (c) {
+    char *f = g_strdup(feature);
+    g_hash_table_insert(c->features, f, f);
+  }
+}
+
+int caps_has_feature(char *hash, char *feature)
+{
+  caps *c;
+  if (!hash)
+    return 0;
+  c = g_hash_table_lookup(caps_cache, hash);
+  if (c)
+    return (g_hash_table_lookup(c->features, feature) != NULL);
+  return 0;
+}
+
+static GFunc _foreach_function;
+
+void _caps_foreach_helper(gpointer key, gpointer value, gpointer user_data)
+{
+  // GFunc func = (GFunc)user_data;
+  _foreach_function(value, user_data);
+}
+
+void caps_foreach_feature(const char *hash, GFunc func, gpointer user_data)
+{
+  caps *c;
+  if (!hash)
+    return;
+  c = g_hash_table_lookup(caps_cache, hash);
+  if (!c)
+    return;
+  _foreach_function = func;
+  g_hash_table_foreach(c->features, _caps_foreach_helper, user_data);
+}
+
+gint _strcmp_sort(gconstpointer a, gconstpointer b)
+{
+  return g_strcmp0(a, b);
+}
+
+//generates the sha1 hash for the special capability "" and returns it
+const char *caps_generate(void)
+{
+  char *identity;
+  GList *features;
+  GChecksum *sha1;
+  guint8 digest[20];
+  gsize digest_size = 20;
+  gchar *hash, *old_hash = NULL;
+  caps *old_caps;
+  unsigned int i;
+  caps *c = g_hash_table_lookup(caps_cache, "");
+
+  g_hash_table_steal(caps_cache, "");
+  sha1 = g_checksum_new(G_CHECKSUM_SHA1);
+  identity = g_strdup_printf("%s/%s/%s<", c->category, c->type, c->name);
+  g_checksum_update(sha1, (guchar*)identity, -1);
+  g_free(identity);
+
+  features = g_list_copy(g_hash_table_get_values(c->features));
+  features = g_list_sort(features, _strcmp_sort);
+  for (i=0; i < g_list_length(features); i++) {
+    g_checksum_update(sha1, g_list_nth_data(features, i), -1);
+    g_checksum_update(sha1, (guchar *)"<", -1);
+  }
+  g_list_free(features);
+
+  g_checksum_get_digest(sha1, digest, &digest_size);
+  hash = g_base64_encode(digest, digest_size);
+  g_checksum_free(sha1);
+  g_hash_table_lookup_extended(caps_cache, hash,
+                               (gpointer *)&old_hash, (gpointer *)&old_caps);
+  g_hash_table_insert(caps_cache, hash, c);
+  if (old_hash)
+    return old_hash;
+  else
+    return hash;
+}
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0:  For Vim users... */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/src/caps.h	Tue Sep 23 10:59:25 2008 +0200
@@ -0,0 +1,22 @@
+#ifndef __CAPS_H__
+#define __CAPS_H__ 1
+
+#include <glib.h>
+
+void  caps_init(void);
+void  caps_free(void);
+void  caps_add(char *hash);
+int   caps_has_hash(const char *hash);
+void  caps_set_identity(char *hash,
+                        const char *category,
+                        const char *name,
+                        const char *type);
+void  caps_add_feature(char *hash, const char *feature);
+int   caps_has_feature(char *hash, char *feature);
+void  caps_foreach_feature(const char *hash, GFunc func, gpointer user_data);
+
+char *caps_generate(void);
+
+#endif /* __CAPS_H__ */
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0:  For Vim users... */
--- a/mcabber/src/main.c	Sun Oct 11 15:39:32 2009 +0200
+++ b/mcabber/src/main.c	Tue Sep 23 10:59:25 2008 +0200
@@ -31,6 +31,7 @@
 #include <glib.h>
 #include <config.h>
 
+#include "caps.h"
 #include "screen.h"
 #include "settings.h"
 #include "roster.h"
@@ -318,6 +319,7 @@
   roster_init();
   settings_init();
   scr_init_bindings();
+  caps_init();
   /* Initialize charset */
   scr_InitLocaleCharSet();
 
@@ -429,6 +431,7 @@
 #endif
   /* Save pending message state */
   hlog_save_state();
+  caps_free();
 
   printf("\n\nThanks for using mcabber!\n");
 
--- a/mcabber/src/roster.c	Sun Oct 11 15:39:32 2009 +0200
+++ b/mcabber/src/roster.c	Tue Sep 23 10:59:25 2008 +0200
@@ -67,6 +67,7 @@
   enum imaffiliation affil;
   gchar *realjid;       /* for chatrooms, if buddy's real jid is known */
   guint events;
+  char *caps;
 #ifdef JEP0022
   struct jep0022 jep22;
 #endif
@@ -150,6 +151,7 @@
 #ifdef HAVE_GPGME
   g_free(p_res->pgpdata.sign_keyid);
 #endif
+  g_free(p_res->caps);
   g_free(p_res);
 }
 
@@ -1196,6 +1198,26 @@
     p_res->events = events;
 }
 
+char *buddy_resource_getcaps(gpointer rosterdata, const char *resname)
+{
+  roster *roster_usr = rosterdata;
+  res *p_res = get_resource(roster_usr, resname);
+  if (p_res)
+    return p_res->caps;
+  return NULL;
+}
+
+void buddy_resource_setcaps(gpointer rosterdata, const char *resname,
+                            const char *caps)
+{
+  roster *roster_usr = rosterdata;
+  res *p_res = get_resource(roster_usr, resname);
+  if (p_res) {
+    g_free(p_res->caps);
+    p_res->caps = g_strdup(caps);
+  }
+}
+
 struct jep0022 *buddy_resource_jep22(gpointer rosterdata, const char *resname)
 {
 #ifdef JEP0022
--- a/mcabber/src/roster.h	Sun Oct 11 15:39:32 2009 +0200
+++ b/mcabber/src/roster.h	Tue Sep 23 10:59:25 2008 +0200
@@ -212,6 +212,9 @@
 void    buddy_resource_setevents(gpointer rosterdata, const char *resname,
                                  guint event);
 guint   buddy_resource_getevents(gpointer rosterdata, const char *resname);
+void    buddy_resource_setcaps(gpointer rosterdata, const char *resname,
+                               const char *caps);
+char   *buddy_resource_getcaps(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);
 struct pgp_data *buddy_resource_pgp(gpointer rosterdata, const char *resname);
--- a/mcabber/src/xmpp.c	Sun Oct 11 15:39:32 2009 +0200
+++ b/mcabber/src/xmpp.c	Tue Sep 23 10:59:25 2008 +0200
@@ -25,6 +25,7 @@
 #include <string.h>
 #include <sys/utsname.h>
 
+#include "caps.h"
 #include "commands.h"
 #include "events.h"
 #include "histolog.h"
@@ -1253,6 +1254,34 @@
   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
 }
 
+static LmHandlerResult cb_caps(LmMessageHandler *h, LmConnection *c,
+                               LmMessage *m, gpointer user_data)
+{
+  char *ver = user_data;
+
+  if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_ERROR) {
+    display_server_error(lm_message_node_get_child(m->node, "error"));
+  } else if (lm_message_get_sub_type(m) == LM_MESSAGE_SUB_TYPE_RESULT) {
+    LmMessageNode *info;
+    LmMessageNode *query = lm_message_node_get_child(m->node, "query");
+
+    caps_add(ver);
+    info = lm_message_node_get_child(query, "identity");
+    if (info)
+      caps_set_identity(ver, lm_message_node_get_attribute(info, "category"),
+                        lm_message_node_get_attribute(info, "name"),
+                        lm_message_node_get_attribute(info, "type"));
+    info = lm_message_node_get_child(query, "feature");
+    while (info) {
+      if (!g_strcmp0(info->name, "feature"))
+        caps_add_feature(ver, lm_message_node_get_attribute(info, "var"));
+      info = info->next;
+    }
+  }
+  g_free(ver);
+  return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
 static LmHandlerResult handle_presence(LmMessageHandler *handler,
                                        LmConnection *connection,
                                        LmMessage *m, gpointer user_data)
@@ -1262,7 +1291,7 @@
   enum imstatus ust;
   char bpprio;
   time_t timestamp = 0L;
-  LmMessageNode *muc_packet;
+  LmMessageNode *muc_packet, *caps;
 
   //Check for MUC presence packet
   muc_packet = lm_message_node_find_xmlns
@@ -1345,6 +1374,39 @@
                     ustmsg);
   }
 
+  //XEP-0115 Entity Capabilities
+  caps = lm_message_node_find_xmlns(m->node, NS_CAPS);
+  if (caps) {
+    const char *ver = lm_message_node_get_attribute(caps, "ver");
+    GSList *sl_buddy = NULL;
+    if (rname)
+      sl_buddy = roster_find(r, jidsearch, ROSTER_TYPE_USER);
+    //only cache the caps if the user is on the roster
+    if (sl_buddy && buddy_getonserverflag(sl_buddy->data)) {
+      buddy_resource_setcaps(sl_buddy->data, rname, ver);
+
+      if (!caps_has_hash(ver)) {
+        char *node;
+        LmMessageHandler *handler;
+        LmMessage *iq = lm_message_new_with_sub_type(from, LM_MESSAGE_TYPE_IQ,
+                                                     LM_MESSAGE_SUB_TYPE_GET);
+        node = g_strdup_printf("%s#%s",
+                               lm_message_node_get_attribute(caps, "node"),
+                               ver);
+        lm_message_node_set_attributes
+                (lm_message_node_add_child(iq->node, "query", NULL),
+                 "xmlns", NS_DISCO_INFO,
+                 "node", node,
+                 NULL);
+        g_free(node);
+        handler = lm_message_handler_new(cb_caps, g_strdup(ver), NULL);
+        lm_connection_send_with_reply(connection, iq, handler, NULL);
+        lm_message_unref(iq);
+        lm_message_handler_unref(handler);
+      }
+    }
+  }
+
   g_free(r);
   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
 }
@@ -1693,34 +1755,16 @@
 
 //  insert_entity_capabilities(presence_stanza)
 // Entity Capabilities (XEP-0115)
-static void insert_entity_capabilities(LmMessageNode * x)
+static void insert_entity_capabilities(LmMessageNode *x, enum imstatus status)
 {
   LmMessageNode *y;
-  const char *ver = entity_version();
-  char *exts, *exts2;
-
-  exts = NULL;
+  const char *ver = entity_version(status);
 
   y = lm_message_node_add_child(x, "c", NULL);
   lm_message_node_set_attribute(y, "xmlns", NS_CAPS);
+  lm_message_node_set_attribute(y, "hash", "sha-1");
   lm_message_node_set_attribute(y, "node", MCABBER_CAPS_NODE);
   lm_message_node_set_attribute(y, "ver", ver);
-#ifdef JEP0085
-  if (!chatstates_disabled) {
-    exts2 = g_strjoin(" ", "csn", exts, NULL);
-    g_free(exts);
-    exts = exts2;
-  }
-#endif
-  if (!settings_opt_get_int("iq_last_disable")) {
-    exts2 = g_strjoin(" ", "iql", exts, NULL);
-    g_free(exts);
-    exts = exts2;
-  }
-  if (exts) {
-    lm_message_node_set_attribute(y, "ext", exts);
-    g_free(exts);
-  }
 }
 
 void xmpp_disconnect(void)
@@ -1765,7 +1809,7 @@
   if (lm_connection_is_authenticated(lconnection)) {
     const char *s_msg = (st != invisible ? msg : NULL);
     m = lm_message_new_presence(st, recipient, s_msg);
-    insert_entity_capabilities(m->node); // Entity Capabilities (XEP-0115)
+    insert_entity_capabilities(m->node, st); // Entity Capabilities (XEP-0115)
 #ifdef HAVE_GPGME
     if (!do_not_sign && gpg_enabled()) {
       char *signature;
--- a/mcabber/src/xmpp_defines.h	Sun Oct 11 15:39:32 2009 +0200
+++ b/mcabber/src/xmpp_defines.h	Tue Sep 23 10:59:25 2008 +0200
@@ -1,6 +1,8 @@
 #ifndef __XMPP_DEFINES_H__
 #define __XMPP_DEFINES_H__ 1
 
+#define MCABBER_CAPS_NODE "http://mcabber.com/caps"
+
 #define NS_CLIENT    "jabber:client"
 #define NS_SERVER    "jabber:server"
 #define NS_DIALBACK  "jabber:server:dialback"
--- a/mcabber/src/xmpp_helper.c	Sun Oct 11 15:39:32 2009 +0200
+++ b/mcabber/src/xmpp_helper.c	Tue Sep 23 10:59:25 2008 +0200
@@ -125,25 +125,43 @@
   return new;
 }
 
-//  entity_version()
+//  entity_version(enum imstatus status)
 // Return a static version string for Entity Capabilities.
 // It should be specific to the client version, please change the id
 // if you alter mcabber's disco support (or add something to the version
 // number) so that it doesn't conflict with the official client.
-const char *entity_version(void)
+const char *entity_version(enum imstatus status)
 {
-  static char *ver;
-  const char *PVERSION = PACKAGE_VERSION; // "+xxx";
+  static char *ver, *ver_notavail;
 
-  if (ver)
+  if (ver && (status != notavail))
     return ver;
+  if (ver_notavail)
+    return ver_notavail;
 
-#ifdef HGCSET
-  ver = g_strdup_printf("%s-%s", PVERSION, HGCSET);
-#else
-  ver = g_strdup(PVERSION);
-#endif
+  caps_add("");
+  caps_set_identity("", "client", PACKAGE_STRING, "pc");
+  caps_add_feature("", NS_DISCO_INFO);
+  caps_add_feature("", NS_MUC);
+  // advertise ChatStates only if they aren't disabled
+  if (!settings_opt_get_int("disable_chatstates"))
+   caps_add_feature("", NS_CHATSTATES);
+  caps_add_feature("", NS_TIME);
+  caps_add_feature("", NS_XMPP_TIME);
+  caps_add_feature("", NS_VERSION);
+  caps_add_feature("", NS_PING);
+  caps_add_feature("", NS_COMMANDS);
+  if (!settings_opt_get_int("iq_last_disable") &&
+      (!settings_opt_get_int("iq_last_disable_when_notavail") ||
+       status != notavail))
+   caps_add_feature("", NS_LAST);
 
+  if (status == notavail) {
+    ver_notavail = caps_generate();
+    return ver_notavail;
+  }
+
+  ver = caps_generate();
   return ver;
 }
 
--- a/mcabber/src/xmpp_helper.h	Sun Oct 11 15:39:32 2009 +0200
+++ b/mcabber/src/xmpp_helper.h	Tue Sep 23 10:59:25 2008 +0200
@@ -17,8 +17,7 @@
 void lm_message_node_deep_ref(LmMessageNode * node);
 
 /* XEP-0115 (Entity Capabilities) node */
-#define MCABBER_CAPS_NODE "http://mcabber.com/caps"
-const char *entity_version(void);
+const char *entity_version(enum imstatus status);
 
 #endif
 
--- a/mcabber/src/xmpp_iq.c	Sun Oct 11 15:39:32 2009 +0200
+++ b/mcabber/src/xmpp_iq.c	Tue Sep 23 10:59:25 2008 +0200
@@ -461,82 +461,47 @@
 }
 
 
-//  disco_info_set_ext(ansquery, ext)
-// Add features attributes to ansquery for extension ext.
-static void disco_info_set_ext(LmMessageNode *ansquery, const char *ext)
+void _disco_add_feature_helper(gpointer data, gpointer user_data)
 {
-  char *nodename = g_strdup_printf("%s#%s", MCABBER_CAPS_NODE, ext);
-  lm_message_node_set_attribute(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.
-    lm_message_node_set_attribute(lm_message_node_add_child(ansquery,
-                                                            "feature", NULL),
-                                  "var", NS_CHATSTATES);
-  }
-  if (!strcasecmp(ext, "iql")) {
-    // I guess it's ok to send this even if it's not compiled in.
-    lm_message_node_set_attribute(lm_message_node_add_child(ansquery,
-                                                            "feature", NULL),
-                                  "var", NS_LAST);
-  }
+  LmMessageNode *node = user_data;
+  lm_message_node_set_attribute
+          (lm_message_node_add_child(node, "feature", NULL), "var", data);
 }
 
-//  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).
+//  disco_info_set_caps(ansquery, entitycaps)
+// Add features attributes to ansquery.  entitycaps should either be a
+// valid capabilities hash or NULL. If it is NULL, the node attribute won't
+// be added to the query child and Entity Capabilities will be announced
+// as a feature.
 // Please change the entity version string if you modify mcabber disco
 // source code, so that it doesn't conflict with the upstream client.
-static void disco_info_set_default(LmMessageNode *ansquery, guint entitycaps)
+static void disco_info_set_caps(LmMessageNode *ansquery,
+                                const char *entitycaps)
 {
-  LmMessageNode *y;
-  char *eversion;
-
-  eversion = g_strdup_printf("%s#%s", MCABBER_CAPS_NODE, entity_version());
-  lm_message_node_set_attribute(ansquery, "node", eversion);
-  g_free(eversion);
-
-  y = lm_message_node_add_child(ansquery, "identity", NULL);
+  if (entitycaps) {
+    char *eversion;
+    eversion = g_strdup_printf("%s#%s", MCABBER_CAPS_NODE, entitycaps);
+    lm_message_node_set_attribute(ansquery, "node", eversion);
+    g_free(eversion);
+  }
 
-  lm_message_node_set_attributes(y,
-                                 "category", "client",
-                                 "type", "pc",
-                                 "name", PACKAGE_NAME,
-                                 NULL);
+  lm_message_node_set_attributes
+          (lm_message_node_add_child(ansquery, "identity", NULL),
+           "category", "client",
+           "name", PACKAGE_STRING,
+           "type", "pc",
+           NULL);
 
-  lm_message_node_set_attribute
-          (lm_message_node_add_child(ansquery, "feature", NULL),
-           "var", NS_DISCO_INFO);
-  lm_message_node_set_attribute
-          (lm_message_node_add_child(ansquery, "feature", NULL),
-           "var", NS_MUC);
-#ifdef JEP0085
-  // Advertise ChatStates only if we're not using Entity Capabilities
-  if (!entitycaps)
+  if (entitycaps)
+    caps_foreach_feature(entitycaps, _disco_add_feature_helper, ansquery);
+  else {
+    caps_foreach_feature(entity_version(xmpp_getstatus()),
+                         _disco_add_feature_helper,
+                         ansquery);
     lm_message_node_set_attribute
             (lm_message_node_add_child(ansquery, "feature", NULL),
-             "var", NS_CHATSTATES);
-#endif
-  lm_message_node_set_attribute
-          (lm_message_node_add_child(ansquery, "feature", NULL),
-           "var", NS_TIME);
-  lm_message_node_set_attribute
-          (lm_message_node_add_child(ansquery, "feature", NULL),
-           "var", NS_XMPP_TIME);
-  lm_message_node_set_attribute
-          (lm_message_node_add_child(ansquery, "feature", NULL),
-           "var", NS_VERSION);
-  lm_message_node_set_attribute
-          (lm_message_node_add_child(ansquery, "feature", NULL),
-           "var", NS_PING);
-  lm_message_node_set_attribute
-          (lm_message_node_add_child(ansquery, "feature", NULL),
-           "var", NS_COMMANDS);
-  if (!entitycaps)
-    lm_message_node_set_attribute
-            (lm_message_node_add_child(ansquery, "feature", NULL),
-             "var", NS_LAST);
+             "var", NS_CAPS);
+  }
 }
 
 static LmHandlerResult handle_iq_disco_info(LmMessageHandler *h,
@@ -546,23 +511,21 @@
   LmMessage *r;
   LmMessageNode *query, *tmp;
   const char *node = NULL;
+  const char *param = NULL;
 
   r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
   query = lm_message_node_add_child(r->node, "query", NULL);
   lm_message_node_set_attribute(query, "xmlns", NS_DISCO_INFO);
   tmp = lm_message_node_find_child(m->node, "query");
-  if (tmp)
+  if (tmp) {
     node = lm_message_node_get_attribute(tmp, "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(query, TRUE);  // client#version
-    else
-      disco_info_set_ext(query, param);     // client#extension
-  } else {
+    param = node+strlen(MCABBER_CAPS_NODE)+1;
+  }
+  if (node && startswith(node, MCABBER_CAPS_NODE "#", FALSE))
+    disco_info_set_caps(query, param);  // client#version
+  else
     // Basic discovery request
-    disco_info_set_default(query, FALSE);
-  }
+    disco_info_set_caps(query, NULL);
 
   lm_connection_send(c, r, NULL);
   lm_message_unref(r);