void _send_caps_request(char *node, char *caps_key, char *id, char *from) { xmpp_ctx_t *ctx = connection_get_ctx(); xmpp_conn_t *conn = connection_get_conn(); if (node != NULL) { log_debug("Node string: %s.", node); if (!caps_contains(caps_key)) { log_debug("Capabilities not cached for '%s', sending discovery IQ.", from); xmpp_stanza_t *iq = stanza_create_disco_info_iq(ctx, id, from, node); xmpp_send(conn, iq); xmpp_stanza_release(iq); } else { log_debug("Capabilities already cached, for %s", caps_key); } } else { log_debug("No node string, not sending discovery IQ."); } }
static void _handle_caps(xmpp_stanza_t *const stanza) { char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); if (from) { 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 (ver) { if (caps_contains(ver)) { log_info("Capabilities cached: %s", ver); caps_map(from, ver); } else { log_info("Capabilities not cached: %s, sending service discovery request", ver); char *node = stanza_caps_get_node(stanza); char *id = create_unique_id("caps"); iq_send_caps_request(from, id, node, ver); free(id); } } // 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 } } } }
static int _iq_handle_discoinfo_result(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { log_debug("Recieved 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) { GSList *identities = NULL; GSList *features = NULL; 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); 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; } 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); } } 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 = stanza_create_form(softwareinfo); FormField *formField = NULL; if (g_strcmp0(form->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); } } stanza_destroy_form(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); } caps_add(caps_key, category, type, name, software, software_version, os, os_version, features); free(caps_key); } return 1; }
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"); } const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); if (!from) { log_info("No from attribute"); 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); log_warning("Error received for capabilities response from %s: ", from, error_message); free(error_message); return 0; } if (query == NULL) { log_warning("No query element found."); return 0; } 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: %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); } caps_map(from, given_sha1); } g_free(generated_sha1); g_strfreev(split); return 0; }