diff options
-rw-r--r-- | src/xmpp/capabilities.c | 158 | ||||
-rw-r--r-- | src/xmpp/capabilities.h | 8 | ||||
-rw-r--r-- | src/xmpp/iq.c | 271 | ||||
-rw-r--r-- | src/xmpp/presence.c | 42 | ||||
-rw-r--r-- | src/xmpp/stanza.c | 45 | ||||
-rw-r--r-- | src/xmpp/stanza.h | 2 | ||||
-rw-r--r-- | src/xmpp/xmpp.h | 2 |
7 files changed, 309 insertions, 219 deletions
diff --git a/src/xmpp/capabilities.c b/src/xmpp/capabilities.c index 606bd64b..30f5aef8 100644 --- a/src/xmpp/capabilities.c +++ b/src/xmpp/capabilities.c @@ -61,62 +61,15 @@ caps_init(void) } void -caps_add(const char * const caps_str, const char * const category, - const char * const type, const char * const name, - const char * const software, const char * const software_version, - const char * const os, const char * const os_version, - GSList *features) +caps_add(const char * const ver, Capabilities *caps) { - Capabilities *new_caps = malloc(sizeof(struct capabilities_t)); - - if (category != NULL) { - new_caps->category = strdup(category); - } else { - new_caps->category = NULL; - } - if (type != NULL) { - new_caps->type = strdup(type); - } else { - new_caps->type = NULL; - } - if (name != NULL) { - new_caps->name = strdup(name); - } else { - new_caps->name = NULL; - } - if (software != NULL) { - new_caps->software = strdup(software); - } else { - new_caps->software = NULL; - } - if (software_version != NULL) { - new_caps->software_version = strdup(software_version); - } else { - new_caps->software_version = NULL; - } - if (os != NULL) { - new_caps->os = strdup(os); - } else { - new_caps->os = NULL; - } - if (os_version != NULL) { - new_caps->os_version = strdup(os_version); - } else { - new_caps->os_version = NULL; - } - if (features != NULL) { - new_caps->features = features; - } else { - new_caps->features = NULL; - } - - g_hash_table_insert(capabilities, strdup(caps_str), new_caps); + g_hash_table_insert(capabilities, strdup(ver), caps); } gboolean -caps_contains(const char * const caps_str) +caps_contains(const char * const caps_ver) { - return (g_hash_table_lookup(capabilities, caps_str) != NULL); + return (g_hash_table_lookup(capabilities, caps_ver) != NULL); } static Capabilities * @@ -228,6 +181,109 @@ caps_create_sha1_str(xmpp_stanza_t * const query) return result; } +Capabilities * +caps_create(xmpp_stanza_t *query) +{ + const char *category = NULL; + const char *type = NULL; + const char *name = NULL; + const char *software = NULL; + const char *software_version = NULL; + const char *os = NULL; + const char *os_version = NULL; + GSList *features = NULL; + + xmpp_stanza_t *identity = xmpp_stanza_get_child_by_name(query, "identity"); + if (identity != NULL) { + category = xmpp_stanza_get_attribute(identity, "category"); + type = xmpp_stanza_get_attribute(identity, "type"); + name = xmpp_stanza_get_attribute(identity, "name"); + } + + xmpp_stanza_t *softwareinfo = xmpp_stanza_get_child_by_ns(query, STANZA_NS_DATA); + if (softwareinfo != NULL) { + DataForm *form = form_create(softwareinfo); + FormField *formField = NULL; + + char *form_type = form_get_form_type_field(form); + if (g_strcmp0(form_type, STANZA_DATAFORM_SOFTWARE) == 0) { + GSList *field = form->fields; + while (field != NULL) { + formField = field->data; + if (formField->values != NULL) { + if (strcmp(formField->var, "software") == 0) { + software = formField->values->data; + } else if (strcmp(formField->var, "software_version") == 0) { + software_version = formField->values->data; + } else if (strcmp(formField->var, "os") == 0) { + os = formField->values->data; + } else if (strcmp(formField->var, "os_version") == 0) { + os_version = formField->values->data; + } + } + field = g_slist_next(field); + } + } + + form_destroy(form); + } + + xmpp_stanza_t *child = xmpp_stanza_get_children(query); + while (child != NULL) { + if (g_strcmp0(xmpp_stanza_get_name(child), "feature") == 0) { + features = g_slist_append(features, strdup(xmpp_stanza_get_attribute(child, "var"))); + } + + child = xmpp_stanza_get_next(child); + } + + Capabilities *new_caps = malloc(sizeof(struct capabilities_t)); + + if (category != NULL) { + new_caps->category = strdup(category); + } else { + new_caps->category = NULL; + } + if (type != NULL) { + new_caps->type = strdup(type); + } else { + new_caps->type = NULL; + } + if (name != NULL) { + new_caps->name = strdup(name); + } else { + new_caps->name = NULL; + } + if (software != NULL) { + new_caps->software = strdup(software); + } else { + new_caps->software = NULL; + } + if (software_version != NULL) { + new_caps->software_version = strdup(software_version); + } else { + new_caps->software_version = NULL; + } + if (os != NULL) { + new_caps->os = strdup(os); + } else { + new_caps->os = NULL; + } + if (os_version != NULL) { + new_caps->os_version = strdup(os_version); + } else { + new_caps->os_version = NULL; + } + if (features != NULL) { + new_caps->features = features; + } else { + new_caps->features = NULL; + } + + return new_caps; +} + + xmpp_stanza_t * caps_create_query_response_stanza(xmpp_ctx_t * const ctx) { diff --git a/src/xmpp/capabilities.h b/src/xmpp/capabilities.h index 39b87eb0..71a1edb5 100644 --- a/src/xmpp/capabilities.h +++ b/src/xmpp/capabilities.h @@ -40,12 +40,10 @@ #include "xmpp/xmpp.h" void caps_init(void); -void caps_add(const char * const caps_str, const char * const category, - const char * const type, const char * const name, - const char * const software, const char * const software_version, - const char * const os, const char * const os_version, GSList *features); -gboolean caps_contains(const char * const caps_str); +void caps_add(const char * const ver, Capabilities *caps); +gboolean caps_contains(const char * const caps_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); #endif diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c index 822e552d..2a228d74 100644 --- a/src/xmpp/iq.c +++ b/src/xmpp/iq.c @@ -66,7 +66,7 @@ static int _version_get_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); static int _disco_info_get_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); -static int _disco_info_result_handler(xmpp_conn_t * const conn, +static int _disco_info_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); static int _version_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); @@ -84,6 +84,8 @@ static int _manual_pong_handler(xmpp_conn_t *const conn, xmpp_stanza_t * const stanza, void * const userdata); static int _ping_timed_handler(xmpp_conn_t * const conn, void * const userdata); +static int _caps_response_handler(xmpp_conn_t *const conn, + xmpp_stanza_t * const stanza, void * const userdata); void iq_add_handlers(void) @@ -94,7 +96,6 @@ iq_add_handlers(void) HANDLE(NULL, STANZA_TYPE_ERROR, _error_handler); HANDLE(XMPP_NS_DISCO_INFO, STANZA_TYPE_GET, _disco_info_get_handler); - HANDLE(XMPP_NS_DISCO_INFO, STANZA_TYPE_RESULT, _disco_info_result_handler); HANDLE(XMPP_NS_DISCO_ITEMS, STANZA_TYPE_GET, _disco_items_get_handler); HANDLE(XMPP_NS_DISCO_ITEMS, STANZA_TYPE_RESULT, _disco_items_result_handler); @@ -142,7 +143,38 @@ _iq_disco_info_request(gchar *jid) { xmpp_conn_t * const conn = connection_get_conn(); xmpp_ctx_t * const ctx = connection_get_ctx(); - xmpp_stanza_t *iq = stanza_create_disco_info_iq(ctx, "discoinforeq", jid, NULL); + char *id = create_unique_id("disco_info"); + xmpp_stanza_t *iq = stanza_create_disco_info_iq(ctx, id, jid, NULL); + + xmpp_id_handler_add(conn, _disco_info_response_handler, id, NULL); + + 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) +{ + 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, NULL); + xmpp_send(conn, iq); xmpp_stanza_release(iq); } @@ -304,6 +336,51 @@ _pong_handler(xmpp_conn_t *const conn, xmpp_stanza_t * const stanza, } static int +_caps_response_handler(xmpp_conn_t *const conn, xmpp_stanza_t * const stanza, + void * const userdata) +{ + 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); + + if (id) { + log_info("Capabilities response handler fired for id %s", id); + } else { + log_info("Capabilities response handler fired"); + } + + char *node = xmpp_stanza_get_attribute(query, STANZA_ATTR_NODE); + if (node == NULL) { + log_warning("No node attribute found"); + 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); + } else { + log_info("Valid SHA-1 hash found: %s", given_sha1); + + if (caps_contains(given_sha1)) { + log_info("Capabilties cached"); + } else { + log_info("Capabilities not cached, storing"); + Capabilities *capabilities = caps_create(query); + caps_add(given_sha1, capabilities); + } + } + + g_free(generated_sha1); + g_strfreev(split); + return 0; +} + +static int _manual_pong_handler(xmpp_conn_t *const conn, xmpp_stanza_t * const stanza, void * const userdata) { @@ -710,171 +787,60 @@ _item_destroy(DiscoItem *item) } static int -_disco_info_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, +_disco_info_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { log_debug("Received diso#info response"); - const char *id = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_ID); const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); - if (g_strcmp0(id, "discoinforeq") == 0) { - - xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY); + xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY); - if (query != NULL) { - xmpp_stanza_t *child = xmpp_stanza_get_children(query); - GSList *identities = NULL; - GSList *features = NULL; - while (child != NULL) { - const char *stanza_name = xmpp_stanza_get_name(child); - if (g_strcmp0(stanza_name, STANZA_NAME_FEATURE) == 0) { - const char *var = xmpp_stanza_get_attribute(child, STANZA_ATTR_VAR); - if (var != NULL) { - features = g_slist_append(features, strdup(var)); + if (query != NULL) { + xmpp_stanza_t *child = xmpp_stanza_get_children(query); + GSList *identities = NULL; + GSList *features = NULL; + while (child != NULL) { + const char *stanza_name = xmpp_stanza_get_name(child); + if (g_strcmp0(stanza_name, STANZA_NAME_FEATURE) == 0) { + const char *var = xmpp_stanza_get_attribute(child, STANZA_ATTR_VAR); + if (var != NULL) { + features = g_slist_append(features, strdup(var)); + } + } else if (g_strcmp0(stanza_name, STANZA_NAME_IDENTITY) == 0) { + const char *name = xmpp_stanza_get_attribute(child, STANZA_ATTR_NAME); + const char *type = xmpp_stanza_get_attribute(child, STANZA_ATTR_TYPE); + const char *category = xmpp_stanza_get_attribute(child, STANZA_ATTR_CATEGORY); + + if ((name != NULL) || (category != NULL) || (type != NULL)) { + DiscoIdentity *identity = malloc(sizeof(struct disco_identity_t)); + + if (name != NULL) { + identity->name = strdup(name); + } else { + identity->name = NULL; } - } else if (g_strcmp0(stanza_name, STANZA_NAME_IDENTITY) == 0) { - const char *name = xmpp_stanza_get_attribute(child, STANZA_ATTR_NAME); - const char *type = xmpp_stanza_get_attribute(child, STANZA_ATTR_TYPE); - const char *category = xmpp_stanza_get_attribute(child, STANZA_ATTR_CATEGORY); - - if ((name != NULL) || (category != NULL) || (type != NULL)) { - DiscoIdentity *identity = malloc(sizeof(struct disco_identity_t)); - - if (name != NULL) { - identity->name = strdup(name); - } else { - identity->name = NULL; - } - if (category != NULL) { - identity->category = strdup(category); - } else { - identity->category = NULL; - } - if (type != NULL) { - identity->type = strdup(type); - } else { - identity->type = NULL; - } - - identities = g_slist_append(identities, identity); + if (category != NULL) { + identity->category = strdup(category); + } else { + identity->category = NULL; } - } - - child = xmpp_stanza_get_next(child); - } - - handle_disco_info(from, identities, features); - g_slist_free_full(features, free); - g_slist_free_full(identities, (GDestroyNotify)_identity_destroy); - } - } else if ((id != NULL) && (g_str_has_prefix(id, "capsreq"))) { - log_debug("Response to query: %s", id); - xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY); - char *node = xmpp_stanza_get_attribute(query, STANZA_ATTR_NODE); - if (node == NULL) { - return 1; - } - - char *caps_key = NULL; - - // xep-0115 - if (g_strcmp0(id, "capsreq") == 0) { - log_debug("xep-0115 supported capabilities"); - caps_key = strdup(node); - - // 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_info("Generated sha-1 does not match given:"); - log_info("Generated : %s", generated_sha1); - log_info("Given : %s", given_sha1); - g_free(generated_sha1); - g_strfreev(split); - free(caps_key); - - return 1; - } - g_free(generated_sha1); - g_strfreev(split); - - // non supported hash, or legacy caps - } else { - log_debug("Unsupported hash, or legacy capabilities"); - caps_key = strdup(id + 8); - log_debug("Caps key: %s", caps_key); - } - - // already cached - if (caps_contains(caps_key)) { - log_info("Client info already cached."); - free(caps_key); - return 1; - } - - log_debug("Client info not cached"); - - const char *category = NULL; - const char *type = NULL; - const char *name = NULL; - const char *software = NULL; - const char *software_version = NULL; - const char *os = NULL; - const char *os_version = NULL; - GSList *features = NULL; - - xmpp_stanza_t *identity = xmpp_stanza_get_child_by_name(query, "identity"); - if (identity != NULL) { - category = xmpp_stanza_get_attribute(identity, "category"); - type = xmpp_stanza_get_attribute(identity, "type"); - name = xmpp_stanza_get_attribute(identity, "name"); - } - - xmpp_stanza_t *softwareinfo = xmpp_stanza_get_child_by_ns(query, STANZA_NS_DATA); - if (softwareinfo != NULL) { - DataForm *form = form_create(softwareinfo); - FormField *formField = NULL; - - char *form_type = form_get_form_type_field(form); - if (g_strcmp0(form_type, STANZA_DATAFORM_SOFTWARE) == 0) { - GSList *field = form->fields; - while (field != NULL) { - formField = field->data; - if (formField->values != NULL) { - if (strcmp(formField->var, "software") == 0) { - software = formField->values->data; - } else if (strcmp(formField->var, "software_version") == 0) { - software_version = formField->values->data; - } else if (strcmp(formField->var, "os") == 0) { - os = formField->values->data; - } else if (strcmp(formField->var, "os_version") == 0) { - os_version = formField->values->data; - } + if (type != NULL) { + identity->type = strdup(type); + } else { + identity->type = NULL; } - field = g_slist_next(field); - } - } - - form_destroy(form); - } - xmpp_stanza_t *child = xmpp_stanza_get_children(query); - while (child != NULL) { - if (g_strcmp0(xmpp_stanza_get_name(child), "feature") == 0) { - features = g_slist_append(features, strdup(xmpp_stanza_get_attribute(child, "var"))); + identities = g_slist_append(identities, identity); + } } child = xmpp_stanza_get_next(child); } - caps_add(caps_key, category, type, name, software, software_version, - os, os_version, features); - - free(caps_key); + handle_disco_info(from, identities, features); + g_slist_free_full(features, free); + g_slist_free_full(identities, (GDestroyNotify)_identity_destroy); } - return 1; } @@ -941,4 +907,5 @@ iq_init_module(void) iq_request_room_config_form = _iq_request_room_config_form; iq_room_config_cancel = _iq_room_config_cancel; iq_submit_room_config = _iq_submit_room_config; + iq_send_caps_request = _iq_send_caps_request; } diff --git a/src/xmpp/presence.c b/src/xmpp/presence.c index cd83cf5d..5e97b963 100644 --- a/src/xmpp/presence.c +++ b/src/xmpp/presence.c @@ -581,12 +581,46 @@ _available_handler(xmpp_conn_t * const conn, free(priority_str); } - // get capabilities key - char *caps_key = NULL; + // send disco info for capabilities, if not cached if (stanza_contains_caps(stanza)) { - caps_key = _get_caps_key(stanza); + log_info("Presence contains capabilities."); + + char *hash = stanza_caps_get_hash(stanza); + + // hash supported xep-0115 + if (g_strcmp0(hash, "sha-1") == 0) { + log_info("Hash %s supported"); + + char *ver = stanza_get_caps_ver(stanza); + if (caps_contains(ver)) { + log_info("Capabilities cached"); + } else { + log_info("Capabilities not cached, sending service discovery request"); + char *node = stanza_caps_get_node(stanza); + char *id = create_unique_id("caps"); + + iq_send_caps_request(from, id, node, ver); + + // send service discovery request + // with id handler to validate response, + // generate hash, + // if match, cache against hash + } + + // no hash, or not supported + } else { + if (hash) { + log_info("Hash %s not supported, not sending service discovery request"); + // send service discovery request, cache against from full jid + } else { + log_info("No hash specified, not sending service discovery request"); + // do legacy + } + } } + char *caps_key = strdup("hello"); + // create Resource Resource *resource = NULL; resource_presence_t presence = resource_presence_from_string(show_str); @@ -648,8 +682,6 @@ _get_caps_key(xmpp_stanza_t * const stanza) char *caps_key = NULL; char *id = NULL; - log_debug("Presence contains capabilities."); - if (node == NULL) { return NULL; } diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index 118bffb7..cca07539 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -973,11 +973,12 @@ stanza_contains_caps(xmpp_stanza_t * const stanza) { xmpp_stanza_t *caps = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_C); - if (caps == NULL) { + if (!caps) { return FALSE; } - if (strcmp(xmpp_stanza_get_ns(caps), STANZA_NS_CAPS) != 0) { + char *ns = xmpp_stanza_get_ns(caps); + if (g_strcmp0(ns, STANZA_NS_CAPS) != 0) { return FALSE; } @@ -989,18 +990,50 @@ stanza_caps_get_hash(xmpp_stanza_t * const stanza) { xmpp_stanza_t *caps = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_C); - if (caps == NULL) { + if (!caps) { return NULL; } - if (strcmp(xmpp_stanza_get_ns(caps), STANZA_NS_CAPS) != 0) { + char *ns = xmpp_stanza_get_ns(caps); + if (g_strcmp0(ns, STANZA_NS_CAPS) != 0) { + return NULL; + } + + return xmpp_stanza_get_attribute(caps, STANZA_ATTR_HASH); +} + +char * +stanza_caps_get_node(xmpp_stanza_t * const stanza) +{ + xmpp_stanza_t *caps = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_C); + + if (!caps) { return NULL; } - char *result = xmpp_stanza_get_attribute(caps, STANZA_ATTR_HASH); + char *ns = xmpp_stanza_get_ns(caps); + if (g_strcmp0(ns, STANZA_NS_CAPS) != 0) { + return NULL; + } + + return xmpp_stanza_get_attribute(caps, STANZA_ATTR_NODE); +} - return result; +char * +stanza_get_caps_ver(xmpp_stanza_t * const stanza) +{ + xmpp_stanza_t *caps = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_C); + + if (!caps) { + return NULL; + } + + char *ns = xmpp_stanza_get_ns(caps); + if (g_strcmp0(ns, STANZA_NS_CAPS) != 0) { + return NULL; + } + return xmpp_stanza_get_attribute(caps, STANZA_ATTR_VER); } char * diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index 155044f2..a98c8c22 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -209,6 +209,8 @@ int stanza_get_idle_time(xmpp_stanza_t * const stanza); char * stanza_get_caps_str(xmpp_stanza_t * const stanza); gboolean stanza_contains_caps(xmpp_stanza_t * const stanza); char * stanza_caps_get_hash(xmpp_stanza_t * const stanza); +char * stanza_get_caps_ver(xmpp_stanza_t * const stanza); +char * stanza_caps_get_node(xmpp_stanza_t * const stanza); DataForm * stanza_create_form(xmpp_stanza_t * const stanza); void stanza_destroy_form(DataForm *form); diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index 3b0b8156..c05a3b21 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -189,6 +189,8 @@ void (*iq_request_room_config_form)(const char * const room_jid); void (*iq_submit_room_config)(const char * const room, DataForm *form); 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); // caps functions Capabilities* (*caps_get)(const char * const caps_str); |