about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--src/xmpp/capabilities.c93
-rw-r--r--src/xmpp/capabilities.h9
-rw-r--r--src/xmpp/iq.c83
-rw-r--r--src/xmpp/presence.c18
-rw-r--r--src/xmpp/xmpp.h2
5 files changed, 153 insertions, 52 deletions
diff --git a/src/xmpp/capabilities.c b/src/xmpp/capabilities.c
index 1d6907b8..cbacae9b 100644
--- a/src/xmpp/capabilities.c
+++ b/src/xmpp/capabilities.c
@@ -55,14 +55,17 @@
 static gchar *cache_loc;
 static GKeyFile *cache;
 
-static GHashTable *jid_lookup;
+static GHashTable *jid_to_ver;
+static GHashTable *jid_to_caps;
 
 static char *my_sha1;
 
 static void _caps_destroy(Capabilities *caps);
 static gchar* _get_cache_file(void);
 static void _save_cache(void);
-static Capabilities * _caps_get(const char * const caps_str);
+static Capabilities * _caps_by_ver(const char * const ver);
+static Capabilities * _caps_by_jid(const char * const jid);
+Capabilities * _caps_copy(Capabilities *caps);
 
 void
 caps_init(void)
@@ -78,13 +81,14 @@ caps_init(void)
     g_key_file_load_from_file(cache, cache_loc, G_KEY_FILE_KEEP_COMMENTS,
         NULL);
 
-    jid_lookup = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+    jid_to_ver = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+    jid_to_caps = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)_caps_destroy);
 
     my_sha1 = NULL;
 }
 
 void
-caps_add(const char * const ver, Capabilities *caps)
+caps_add_by_ver(const char * const ver, Capabilities *caps)
 {
     gboolean cached = g_key_file_has_group(cache, ver);
     if (!cached) {
@@ -126,66 +130,72 @@ caps_add(const char * const ver, Capabilities *caps)
 }
 
 void
-caps_map(const char * const jid, const char * const ver)
+caps_add_by_jid(const char * const jid, Capabilities *caps)
 {
-    g_hash_table_insert(jid_lookup, strdup(jid), strdup(ver));
+    g_hash_table_insert(jid_to_caps, strdup(jid), caps);
+}
+
+void
+caps_map_jid_to_ver(const char * const jid, const char * const ver)
+{
+    g_hash_table_insert(jid_to_ver, strdup(jid), strdup(ver));
 }
 
 gboolean
-caps_contains(const char * const caps_ver)
+caps_contains(const char * const ver)
 {
-    return (g_key_file_has_group(cache, caps_ver));
+    return (g_key_file_has_group(cache, ver));
 }
 
 static Capabilities *
-_caps_get(const char * const caps_str)
+_caps_by_ver(const char * const ver)
 {
-    if (g_key_file_has_group(cache, caps_str)) {
+    if (g_key_file_has_group(cache, ver)) {
         Capabilities *new_caps = malloc(sizeof(struct capabilities_t));
 
-        char *category = g_key_file_get_string(cache, caps_str, "category", NULL);
+        char *category = g_key_file_get_string(cache, ver, "category", NULL);
         if (category) {
             new_caps->category = strdup(category);
         } else {
             new_caps->category = NULL;
         }
 
-        char *type = g_key_file_get_string(cache, caps_str, "type", NULL);
+        char *type = g_key_file_get_string(cache, ver, "type", NULL);
         if (type) {
             new_caps->type = strdup(type);
         } else {
             new_caps->type = NULL;
         }
 
-        char *name = g_key_file_get_string(cache, caps_str, "name", NULL);
+        char *name = g_key_file_get_string(cache, ver, "name", NULL);
         if (name) {
             new_caps->name = strdup(name);
         } else {
             new_caps->name = NULL;
         }
 
-        char *software = g_key_file_get_string(cache, caps_str, "software", NULL);
+        char *software = g_key_file_get_string(cache, ver, "software", NULL);
         if (software) {
             new_caps->software = strdup(software);
         } else {
             new_caps->software = NULL;
         }
 
-        char *software_version = g_key_file_get_string(cache, caps_str, "software_version", NULL);
+        char *software_version = g_key_file_get_string(cache, ver, "software_version", NULL);
         if (software_version) {
             new_caps->software_version = strdup(software_version);
         } else {
             new_caps->software_version = NULL;
         }
 
-        char *os = g_key_file_get_string(cache, caps_str, "os", NULL);
+        char *os = g_key_file_get_string(cache, ver, "os", NULL);
         if (os) {
             new_caps->os = strdup(os);
         } else {
             new_caps->os = NULL;
         }
 
-        char *os_version = g_key_file_get_string(cache, caps_str, "os_version", NULL);
+        char *os_version = g_key_file_get_string(cache, ver, "os_version", NULL);
         if (os_version) {
             new_caps->os_version = strdup(os_version);
         } else {
@@ -193,7 +203,7 @@ _caps_get(const char * const caps_str)
         }
 
         gsize features_len = 0;
-        gchar **features = g_key_file_get_string_list(cache, caps_str, "features", &features_len, NULL);
+        gchar **features = g_key_file_get_string_list(cache, ver, "features", &features_len, NULL);
         if (features != NULL && features_len > 0) {
             GSList *features_list = NULL;
             int i;
@@ -212,19 +222,59 @@ _caps_get(const char * const caps_str)
 }
 
 static Capabilities *
+_caps_by_jid(const char * const jid)
+{
+    return g_hash_table_lookup(jid_to_caps, jid);
+}
+
+static Capabilities *
 _caps_lookup(const char * const jid)
 {
-    char *ver = g_hash_table_lookup(jid_lookup, jid);
+    char *ver = g_hash_table_lookup(jid_to_ver, jid);
     if (ver) {
-        Capabilities *caps = _caps_get(ver);
+        Capabilities *caps = _caps_by_ver(ver);
         if (caps) {
+            log_debug("Capabilities lookup %s, found by verification string %s.", jid, ver);
             return caps;
         }
+    } else {
+        Capabilities *caps = _caps_by_jid(jid);
+        if (caps) {
+            log_debug("Capabilities lookup %s, found by JID.", jid);
+            return _caps_copy(caps);
+        }
     }
 
+    log_debug("Capabilities lookup %s, none found.", jid);
     return NULL;
 }
 
+Capabilities *
+_caps_copy(Capabilities *caps)
+{
+    if (!caps) {
+        return NULL;
+    } else {
+        Capabilities *result = (Capabilities *)malloc(sizeof(Capabilities));
+        result->category = caps->category ? strdup(caps->category) : NULL;
+        result->type = caps->type ? strdup(caps->type) : NULL;
+        result->name = caps->name ? strdup(caps->name) : NULL;
+        result->software = caps->software ? strdup(caps->software) : NULL;
+        result->software_version = caps->software_version ? strdup(caps->software_version) : NULL;
+        result->os = caps->os ? strdup(caps->os) : NULL;
+        result->os_version = caps->os_version ? strdup(caps->os_version) : NULL;
+
+        result->features = NULL;
+        GSList *curr = caps->features;
+        while (curr) {
+            result->features = g_slist_append(result->features, strdup(curr->data));
+            curr = g_slist_next(curr);
+        }
+
+        return result;
+    }
+}
+
 char *
 caps_create_sha1_str(xmpp_stanza_t * const query)
 {
@@ -579,7 +629,8 @@ _caps_close(void)
 {
     g_key_file_free(cache);
     cache = NULL;
-    g_hash_table_destroy(jid_lookup);
+    g_hash_table_destroy(jid_to_ver);
+    g_hash_table_destroy(jid_to_caps);
 }
 
 static void
diff --git a/src/xmpp/capabilities.h b/src/xmpp/capabilities.h
index a43b301d..692ac49d 100644
--- a/src/xmpp/capabilities.h
+++ b/src/xmpp/capabilities.h
@@ -40,9 +40,12 @@
 #include "xmpp/xmpp.h"
 
 void caps_init(void);
-void caps_add(const char * const ver, Capabilities *caps);
-void caps_map(const char * const jid, const char * const ver);
-gboolean caps_contains(const char * const caps_ver);
+
+void caps_add_by_ver(const char * const ver, Capabilities *caps);
+void caps_add_by_jid(const char * const jid, Capabilities *caps);
+void caps_map_jid_to_ver(const char * const jid, const char * const ver);
+gboolean caps_contains(const char * const ver);
+
 char* caps_create_sha1_str(xmpp_stanza_t * const query);
 xmpp_stanza_t* caps_create_query_response_stanza(xmpp_ctx_t * const ctx);
 Capabilities* caps_create(xmpp_stanza_t *query);
diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c
index 353d771b..1138620d 100644
--- a/src/xmpp/iq.c
+++ b/src/xmpp/iq.c
@@ -181,6 +181,33 @@ _iq_room_info_request(gchar *room)
 }
 
 static void
+_iq_send_caps_request_for_jid(const char * const to, const char * const id,
+    const char * const node, const char * const ver)
+{
+    xmpp_conn_t * const conn = connection_get_conn();
+    xmpp_ctx_t * const ctx = connection_get_ctx();
+
+    if (!node) {
+        log_error("Could not create caps request, no node");
+        return;
+    }
+    if (!ver) {
+        log_error("Could not create caps request, no ver");
+        return;
+    }
+
+    GString *node_str = g_string_new("");
+    g_string_printf(node_str, "%s#%s", node, ver);
+    xmpp_stanza_t *iq = stanza_create_disco_info_iq(ctx, id, to, node_str->str);
+    g_string_free(node_str, TRUE);
+
+    xmpp_id_handler_add(conn, _caps_response_handler, id, strdup(to));
+
+    xmpp_send(conn, iq);
+    xmpp_stanza_release(iq);
+}
+
+static void
 _iq_send_caps_request(const char * const to, const char * const id,
     const char * const node, const char * const ver)
 {
@@ -457,6 +484,12 @@ _caps_response_handler(xmpp_conn_t *const conn, xmpp_stanza_t * const stanza,
     const char *id = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_ID);
     xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY);
 
+    char *type = xmpp_stanza_get_type(stanza);
+    // ignore non result
+    if ((g_strcmp0(type, "get") == 0) || (g_strcmp0(type, "set") == 0)) {
+        return 1;
+    }
+
     if (id) {
         log_info("Capabilities response handler fired for id %s", id);
     } else {
@@ -469,7 +502,6 @@ _caps_response_handler(xmpp_conn_t *const conn, xmpp_stanza_t * const stanza,
         return 0;
     }
 
-    char *type = xmpp_stanza_get_type(stanza);
     // handle error responses
     if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) {
         char *error_message = stanza_get_error_message(stanza);
@@ -489,32 +521,40 @@ _caps_response_handler(xmpp_conn_t *const conn, xmpp_stanza_t * const stanza,
         return 0;
     }
 
-    // validate sha1
-    gchar **split = g_strsplit(node, "#", -1);
-    char *given_sha1 = split[1];
-    char *generated_sha1 = caps_create_sha1_str(query);
-
-    if (g_strcmp0(given_sha1, generated_sha1) != 0) {
-        log_warning("Generated sha-1 does not match given:");
-        log_warning("Generated : %s", generated_sha1);
-        log_warning("Given     : %s", given_sha1);
+    if (userdata) {
+        char *jid = (char *)userdata;
+        log_info("Associating capabilities with: %s", jid);
+        Capabilities *capabilities = caps_create(query);
+        caps_add_by_jid(jid, capabilities);
     } else {
-        log_info("Valid SHA-1 hash found: %s", given_sha1);
-
-        if (caps_contains(given_sha1)) {
-            log_info("Capabilties cached: %s", given_sha1);
+        // validate sha1
+        gchar **split = g_strsplit(node, "#", -1);
+        char *given_sha1 = split[1];
+        char *generated_sha1 = caps_create_sha1_str(query);
+
+        if (g_strcmp0(given_sha1, generated_sha1) != 0) {
+            log_warning("Generated sha-1 does not match given:");
+            log_warning("Generated : %s", generated_sha1);
+            log_warning("Given     : %s", given_sha1);
         } else {
-            log_info("Capabilities not cached: %s, storing", given_sha1);
-            Capabilities *capabilities = caps_create(query);
-            caps_add(given_sha1, capabilities);
-            caps_destroy(capabilities);
+            log_info("Valid SHA-1 hash found: %s", given_sha1);
+
+            if (caps_contains(given_sha1)) {
+                log_info("Capabilties already cached: %s", given_sha1);
+            } else {
+                log_info("Capabilities not cached: %s, storing", given_sha1);
+                Capabilities *capabilities = caps_create(query);
+                caps_add_by_ver(given_sha1, capabilities);
+                caps_destroy(capabilities);
+            }
+
+            caps_map_jid_to_ver(from, given_sha1);
         }
 
-        caps_map(from, given_sha1);
+        g_free(generated_sha1);
+        g_strfreev(split);
     }
 
-    g_free(generated_sha1);
-    g_strfreev(split);
     return 0;
 }
 
@@ -1254,6 +1294,7 @@ iq_init_module(void)
     iq_room_config_cancel = _iq_room_config_cancel;
     iq_submit_room_config = _iq_submit_room_config;
     iq_send_caps_request = _iq_send_caps_request;
+    iq_send_caps_request_for_jid = _iq_send_caps_request_for_jid;
     iq_room_info_request = _iq_room_info_request;
     iq_room_affiliation_set = _iq_room_affiliation_set;
     iq_room_affiliation_list = _iq_room_affiliation_list;
diff --git a/src/xmpp/presence.c b/src/xmpp/presence.c
index 963815a8..5acec9da 100644
--- a/src/xmpp/presence.c
+++ b/src/xmpp/presence.c
@@ -507,22 +507,26 @@ _handle_caps(char *jid, XMPPCaps *caps)
         log_info("Hash %s supported", caps->hash);
         if (caps->ver) {
             if (caps_contains(caps->ver)) {
-                log_info("Capabilities cached: %s", caps->ver);
-                caps_map(jid, caps->ver);
+                log_info("Capabilities cache hit: %s, for %s.", caps->ver, jid);
+                caps_map_jid_to_ver(jid, caps->ver);
             } else {
-                log_info("Capabilities not cached: %s, sending service discovery request", caps->ver);
+                log_info("Capabilities cache miss: %s, for %s, sending service discovery request", caps->ver, jid);
                 char *id = create_unique_id("caps");
                 iq_send_caps_request(jid, id, caps->node, caps->ver);
                 free(id);
             }
         }
 
-    // no hash, or not supported
+    // unsupported hash
     } else if (caps->hash) {
-        log_info("Hash %s not supported, not sending service discovery request", caps->hash);
-        // send service discovery request, cache against from full jid
+        log_info("Hash %s not supported: %s, sending service discovery request", caps->hash, jid);
+        char *id = create_unique_id("caps");
+        iq_send_caps_request_for_jid(jid, id, caps->node, caps->ver);
+        free(id);
+
+    // no hash
     } else {
-        log_info("No hash specified, not sending service discovery request");
+        log_info("No hash specified: %s, not sending service discovery request", jid);
         // do legacy
     }
 }
diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h
index 15f0a7a5..349f8638 100644
--- a/src/xmpp/xmpp.h
+++ b/src/xmpp/xmpp.h
@@ -192,6 +192,8 @@ void (*iq_room_config_cancel)(const char * const room_jid);
 void (*iq_send_ping)(const char * const target);
 void (*iq_send_caps_request)(const char * const to, const char * const id,
     const char * const node, const char * const ver);
+void (*iq_send_caps_request_for_jid)(const char * const to, const char * const id,
+    const char * const node, const char * const ver);
 void (*iq_room_info_request)(gchar *room);
 void (*iq_room_affiliation_list)(const char * const room, char *affiliation);
 void (*iq_room_affiliation_set)(const char * const room, const char * const jid, char *affiliation,