From 94bd9dbdfe7e7188e20bddf68565277630197822 Mon Sep 17 00:00:00 2001 From: James Booth Date: Thu, 27 Nov 2014 01:08:02 +0000 Subject: Store capabilities against fulljid when unsupported hash --- src/xmpp/capabilities.c | 93 ++++++++++++++++++++++++++++++++++++++----------- src/xmpp/capabilities.h | 9 +++-- src/xmpp/iq.c | 83 ++++++++++++++++++++++++++++++++----------- src/xmpp/presence.c | 18 ++++++---- src/xmpp/xmpp.h | 2 ++ 5 files changed, 153 insertions(+), 52 deletions(-) (limited to 'src/xmpp') 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; @@ -211,20 +221,60 @@ _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 @@ -180,6 +180,33 @@ _iq_room_info_request(gchar *room) xmpp_stanza_release(iq); } +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, -- cgit 1.4.1-2-gfad0