# HG changeset patch # User Mikael Berthe # Date 1178611232 -7200 # Node ID 80c095886fb59b6544cce54bd04273f781cb3c0e # Parent 9f5c5f1769538f14c9650bece358f993d0e30444 Entity Capabilities support (XEP-0115) diff -r 9f5c5f176953 -r 80c095886fb5 mcabber/libjabber/jabber.h --- 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" diff -r 9f5c5f176953 -r 80c095886fb5 mcabber/src/jab_iq.c --- 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); } diff -r 9f5c5f176953 -r 80c095886fb5 mcabber/src/jab_priv.h --- 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); diff -r 9f5c5f176953 -r 80c095886fb5 mcabber/src/jabglue.c --- 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;