diff options
Diffstat (limited to 'src/jabber.c')
-rw-r--r-- | src/jabber.c | 258 |
1 files changed, 250 insertions, 8 deletions
diff --git a/src/jabber.c b/src/jabber.c index 2d69ebec..244921bf 100644 --- a/src/jabber.c +++ b/src/jabber.c @@ -26,6 +26,7 @@ #include <strophe.h> +#include "capabilities.h" #include "chat_session.h" #include "common.h" #include "contact_list.h" @@ -84,9 +85,16 @@ static int _iq_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); static int _roster_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); +static int _disco_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, + void * const userdata); +static int _disco_request_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, + void * const userdata); +static int _version_request_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, + void * const userdata); static int _presence_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 char * _handle_presence_caps(xmpp_stanza_t * const stanza); void jabber_init(const int disable_tls) @@ -414,6 +422,16 @@ jabber_update_presence(jabber_presence_t status, const char * const msg, xmpp_stanza_add_child(presence, query); } + // add caps + xmpp_stanza_t *caps = xmpp_stanza_new(jabber_conn.ctx); + xmpp_stanza_set_name(caps, STANZA_NAME_C); + xmpp_stanza_set_ns(caps, STANZA_NS_CAPS); + xmpp_stanza_set_attribute(caps, STANZA_ATTR_HASH, "sha-1"); + xmpp_stanza_set_attribute(caps, STANZA_ATTR_NODE, "http://www.profanity.im"); + xmpp_stanza_t *query = caps_get_query_response_stanza(jabber_conn.ctx); + char *sha1 = caps_get_sha1_str(query); + xmpp_stanza_set_attribute(caps, STANZA_ATTR_VER, sha1); + xmpp_stanza_add_child(presence, caps); xmpp_send(jabber_conn.conn, presence); // send presence for each room @@ -783,11 +801,24 @@ _iq_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { char *id = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_ID); + char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE); // handle the initial roster request - if ((id != NULL) && (strcmp(id, "roster") == 0)) { + if (g_strcmp0(id, "roster") == 0) { return _roster_handler(conn, stanza, userdata); + // handle disco responses + } else if ((id != NULL) && (g_str_has_prefix(id, "disco")) && + (g_strcmp0(type, "result") == 0)) { + return _disco_response_handler(conn, stanza, userdata); + + // handle disco requests + } else if (stanza_is_caps_request(stanza)) { + return _disco_request_handler(conn, stanza, userdata); + + } else if (stanza_is_version_request(stanza)) { + return _version_request_handler(conn, stanza, userdata); + // handle iq } else { char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE); @@ -877,8 +908,8 @@ _iq_handler(xmpp_conn_t * const conn, } static int -_roster_handler(xmpp_conn_t * const conn, - xmpp_stanza_t * const stanza, void * const userdata) +_roster_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, + void * const userdata) { xmpp_stanza_t *query, *item; char *type = xmpp_stanza_get_type(stanza); @@ -921,6 +952,148 @@ _roster_handler(xmpp_conn_t * const conn, } static int +_version_request_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, + void * const userdata) +{ + xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata; + + char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); + char *id = xmpp_stanza_get_id(stanza); + + if (from != NULL) { + xmpp_stanza_t *response = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(response, STANZA_NAME_IQ); + if (id != NULL) { + xmpp_stanza_set_id(response, id); + } + xmpp_stanza_set_attribute(response, STANZA_ATTR_TO, from); + xmpp_stanza_set_type(response, STANZA_TYPE_RESULT); + + xmpp_stanza_t *query = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(query, STANZA_NAME_QUERY); + xmpp_stanza_set_ns(query, STANZA_NS_VERSION); + + xmpp_stanza_t *name = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(name, "name"); + xmpp_stanza_t *name_txt = xmpp_stanza_new(ctx); + xmpp_stanza_set_text(name_txt, "Profanity"); + xmpp_stanza_add_child(name, name_txt); + + xmpp_stanza_t *version = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(version, "version"); + xmpp_stanza_t *version_txt = xmpp_stanza_new(ctx); + GString *version_str = g_string_new(PACKAGE_VERSION); + if (strcmp(PACKAGE_STATUS, "development") == 0) { + g_string_append(version_str, "dev"); + } + xmpp_stanza_set_text(version_txt, version_str->str); + xmpp_stanza_add_child(version, version_txt); + + xmpp_stanza_add_child(query, name); + xmpp_stanza_add_child(query, version); + xmpp_stanza_add_child(response, query); + + xmpp_send(conn, response); + } + + return 1; +} + +static int +_disco_request_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, + void * const userdata) +{ + xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata; + + xmpp_stanza_t *incoming_query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY); + char *node_str = xmpp_stanza_get_attribute(incoming_query, STANZA_ATTR_NODE); + char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); + + if (from != NULL && node_str != NULL) { + xmpp_stanza_t *response = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(response, STANZA_NAME_IQ); + xmpp_stanza_set_id(response, xmpp_stanza_get_id(stanza)); + xmpp_stanza_set_attribute(response, STANZA_ATTR_TO, from); + xmpp_stanza_set_type(response, STANZA_TYPE_RESULT); + xmpp_stanza_t *query = caps_get_query_response_stanza(ctx); + xmpp_stanza_set_attribute(query, STANZA_ATTR_NODE, node_str); + xmpp_stanza_add_child(response, query); + xmpp_send(conn, response); + } + + return 1; +} + +static int +_disco_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, + void * const userdata) +{ + char *type = xmpp_stanza_get_type(stanza); + char *id = xmpp_stanza_get_id(stanza); + + if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) { + log_error("Roster query failed"); + return 1; + } else { + 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, "disco") == 0) { + caps_key = node; + + // validate sha1 + gchar **split = g_strsplit(node, "#", -1); + char *given_sha1 = split[1]; + char *generated_sha1 = caps_get_sha1_str(query); + + if (g_strcmp0(given_sha1, generated_sha1) != 0) { + log_info("Invalid SHA1 recieved for caps."); + return 1; + } + // non supported hash, or legacy caps + } else { + caps_key = id + 6; + } + + // already cached + if (caps_contains(caps_key)) { + log_info("Client info already cached."); + return 1; + } + + xmpp_stanza_t *identity = xmpp_stanza_get_child_by_name(query, "identity"); + + if (identity == NULL) { + return 1; + } + + const char *category = xmpp_stanza_get_attribute(identity, "category"); + if (category == NULL) { + return 1; + } + + if (strcmp(category, "client") != 0) { + return 1; + } + + const char *name = xmpp_stanza_get_attribute(identity, "name"); + if (name == 0) { + return 1; + } + + caps_add(caps_key, name); + + return 1; + } +} + +static int _ping_timed_handler(xmpp_conn_t * const conn, void * const userdata) { if (jabber_conn.conn_status == JABBER_CONNECTED) { @@ -974,6 +1147,7 @@ _room_presence_handler(const char * const jid, xmpp_stanza_t * const stanza) } else { char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE); char *show_str, *status_str; + char *caps_key = _handle_presence_caps(stanza); xmpp_stanza_t *status = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_STATUS); if (status != NULL) { @@ -999,18 +1173,18 @@ _room_presence_handler(const char * const jid, xmpp_stanza_t * const stanza) show_str = "online"; } if (!muc_get_roster_received(room)) { - muc_add_to_roster(room, nick, show_str, status_str); + muc_add_to_roster(room, nick, show_str, status_str, caps_key); } else { char *old_nick = muc_complete_roster_nick_change(room, nick); if (old_nick != NULL) { - muc_add_to_roster(room, nick, show_str, status_str); + muc_add_to_roster(room, nick, show_str, status_str, caps_key); prof_handle_room_member_nick_change(room, old_nick, nick); } else { if (!muc_nick_in_roster(room, nick)) { - prof_handle_room_member_online(room, nick, show_str, status_str); + prof_handle_room_member_online(room, nick, show_str, status_str, caps_key); } else { - prof_handle_room_member_presence(room, nick, show_str, status_str); + prof_handle_room_member_presence(room, nick, show_str, status_str, caps_key); } } } @@ -1023,6 +1197,72 @@ _room_presence_handler(const char * const jid, xmpp_stanza_t * const stanza) return 1; } +static char * +_handle_presence_caps(xmpp_stanza_t * const stanza) +{ + char *caps_key = NULL; + char *node = NULL; + char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); + if (stanza_contains_caps(stanza)) { + char *hash_type = stanza_caps_get_hash(stanza); + + // xep-0115 + if (hash_type != NULL) { + + // supported hash + if (strcmp(hash_type, "sha-1") == 0) { + node = stanza_get_caps_str(stanza); + caps_key = node; + + if (node != NULL) { + if (!caps_contains(caps_key)) { + xmpp_stanza_t *iq = stanza_create_disco_iq(jabber_conn.ctx, "disco", from, node); + xmpp_send(jabber_conn.conn, iq); + xmpp_stanza_release(iq); + } + } + + // unsupported hash + } else { + node = stanza_get_caps_str(stanza); + caps_key = from; + + if (node != NULL) { + if (!caps_contains(caps_key)) { + GString *id = g_string_new("disco_"); + g_string_append(id, from); + xmpp_stanza_t *iq = stanza_create_disco_iq(jabber_conn.ctx, id->str, from, node); + xmpp_send(jabber_conn.conn, iq); + xmpp_stanza_release(iq); + g_string_free(id, TRUE); + } + } + } + + return strdup(caps_key); + + //ignore or handle legacy caps + } else { + node = stanza_get_caps_str(stanza); + caps_key = from; + + if (node != NULL) { + if (!caps_contains(caps_key)) { + GString *id = g_string_new("disco_"); + g_string_append(id, from); + xmpp_stanza_t *iq = stanza_create_disco_iq(jabber_conn.ctx, id->str, from, node); + xmpp_send(jabber_conn.conn, iq); + xmpp_stanza_release(iq); + g_string_free(id, TRUE); + } + } + + return caps_key; + } + } + return NULL; +} + static int _presence_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) @@ -1055,6 +1295,8 @@ _presence_handler(xmpp_conn_t * const conn, g_date_time_unref(now); } + char *caps_key = _handle_presence_caps(stanza); + xmpp_stanza_t *status = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_STATUS); if (status != NULL) status_str = xmpp_stanza_get_text(status); @@ -1069,7 +1311,7 @@ _presence_handler(xmpp_conn_t * const conn, show_str = "online"; if (strcmp(my_jid->barejid, from_jid->barejid) !=0) { - prof_handle_contact_online(from_jid->barejid, show_str, status_str, last_activity); + prof_handle_contact_online(from_jid->barejid, show_str, status_str, last_activity, caps_key); } } else if (strcmp(type, STANZA_TYPE_UNAVAILABLE) == 0) { if (strcmp(my_jid->barejid, from_jid->barejid) !=0) { |