about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorJames Booth <boothj5@gmail.com>2013-01-24 00:26:53 +0000
committerJames Booth <boothj5@gmail.com>2013-01-24 00:26:53 +0000
commit37666528e5f35480f5bb5bce62764048d1096dca (patch)
treef3df38b63bdf1a5eae54a4905f3c2185a1da1731
parent27b6842f194c02d2f7f15ba8fb79f6a57f70360c (diff)
downloadprofani-tty-37666528e5f35480f5bb5bce62764048d1096dca.tar.gz
Split jabber module
-rw-r--r--Makefile.am4
-rw-r--r--src/connection.c (renamed from src/jabber.c)344
-rw-r--r--src/iq.c374
-rw-r--r--src/iq.h31
4 files changed, 410 insertions, 343 deletions
diff --git a/Makefile.am b/Makefile.am
index f3959f5f..97910ec6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,14 +4,14 @@ profanity_SOURCES = src/command.c src/contact.c src/history.c src/jabber.h \
 	src/command.h src/contact.h src/history.h src/log.c src/preferences.h \
 	src/prof_autocomplete.h src/title_bar.c src/windows.c src/common.c \
 	src/contact_list.c src/input_win.c src/log.h src/profanity.c \
-	src/prof_history.c src/ui.h src/common.h src/ contact_list.h src/jabber.c \
+	src/prof_history.c src/ui.h src/common.h src/ contact_list.h src/connection.c \
 	src/main.c src/profanity.h src/prof_history.h src/chat_log.c \
 	src/chat_log.h src/tinyurl.c src/tinyurl.h src/chat_session.c \
 	src/chat_session.h src/release.c src/release.h src/muc.c \
 	src/muc.h src/stanza.c src/stanza.h src/parser.c src/parser.h \
 	src/theme.c src/theme.h src/window.c src/window.h src/xdg_base.c \
 	src/xdg_base.h src/files.c src/files.h src/accounts.c src/accounts.h \
-	src/jid.h src/jid.c src/capabilities.h src/capabilities.c
+	src/jid.h src/jid.c src/capabilities.h src/capabilities.c src/iq.h src/iq.c
 
 TESTS = tests/testsuite
 check_PROGRAMS = tests/testsuite
diff --git a/src/jabber.c b/src/connection.c
index e4873b32..cf289314 100644
--- a/src/jabber.c
+++ b/src/connection.c
@@ -1,5 +1,5 @@
 /*
- * jabber.c
+ * connection.c
  *
  * Copyright (C) 2012, 2013 James Booth <boothj5@gmail.com>
  *
@@ -30,6 +30,7 @@
 #include "chat_session.h"
 #include "common.h"
 #include "contact_list.h"
+#include "iq.h"
 #include "jabber.h"
 #include "jid.h"
 #include "log.h"
@@ -81,22 +82,6 @@ static int _groupchat_message_handler(xmpp_stanza_t * const stanza);
 static int _error_handler(xmpp_stanza_t * const stanza);
 static int _chat_message_handler(xmpp_stanza_t * const stanza);
 
-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, xmpp_ctx_t * const ctx,
-    const char * const id, const char * const type, const char * const from);
-static int _caps_response_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, xmpp_ctx_t * const ctx,
-    const char * const id, const char * const type, const char * const from);
-static int _caps_request_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, xmpp_ctx_t * const ctx,
-    const char * const id, const char * const type, const char * const from);
-static int _version_request_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, xmpp_ctx_t * const ctx,
-    const char * const id, const char * const type, const char * const from);
-
 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);
@@ -756,7 +741,7 @@ _connection_handler(xmpp_conn_t * const conn,
 
         xmpp_handler_add(conn, _message_handler, NULL, STANZA_NAME_MESSAGE, NULL, ctx);
         xmpp_handler_add(conn, _presence_handler, NULL, STANZA_NAME_PRESENCE, NULL, ctx);
-        xmpp_handler_add(conn, _iq_handler, NULL, STANZA_NAME_IQ, NULL, ctx);
+        xmpp_handler_add(conn, iq_handler, NULL, STANZA_NAME_IQ, NULL, ctx);
 
         if (prefs_get_autoping() != 0) {
             int millis = prefs_get_autoping() * 1000;
@@ -807,329 +792,6 @@ _connection_handler(xmpp_conn_t * const conn,
 }
 
 static int
-_iq_error_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
-    xmpp_ctx_t * const ctx, const char * const id, const char * const from)
-{
-    if (id != NULL) {
-        log_error("IQ error received, id: %s.", id);
-    } else {
-        log_error("IQ error recieved.");
-    }
-
-    return 1;
-}
-
-static int
-_iq_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata)
-{
-    xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
-    char *id = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_ID);
-    char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
-    char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
-
-    if (g_strcmp0(type, "error") == 0) {
-        return _iq_error_handler(conn, stanza, ctx, id, from);
-    }
-/*
-    if (g_strcmp0(type, "get") == 0) {
-        return _iq_get_handler(conn, stanza, ctx, id, from);
-    }
-
-    if (g_strcmp0(type, "set") == 0) {
-        return _iq_set_handler(conn, stanza, ctx, id, from);
-    }
-
-    if (g_strcmp0(type, "result") == 0) {
-        return _iq_result_handler(conn, stanza, ctx, id, from);
-    }
-*/
-    // handle the initial roster request
-    if (g_strcmp0(id, "roster") == 0) {
-        return _roster_handler(conn, stanza, ctx, id, type, from);
-
-    // handle caps responses
-    } else if ((id != NULL) && (g_str_has_prefix(id, "disco")) &&
-            (g_strcmp0(type, "result") == 0)) {
-        return _caps_response_handler(conn, stanza, ctx, id, type, from);
-
-    // handle caps requests
-    } else if (stanza_is_caps_request(stanza)) {
-        return _caps_request_handler(conn, stanza, ctx, id, type, from);
-
-    } else if (stanza_is_version_request(stanza)) {
-        return _version_request_handler(conn, stanza, ctx, id, type, from);
-
-    // handle iq
-    } else {
-        char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
-        if (type == NULL) {
-            return TRUE;
-        }
-
-        // handle roster update
-        if (strcmp(type, STANZA_TYPE_SET) == 0) {
-
-            xmpp_stanza_t *query =
-                xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY);
-            if (query == NULL) {
-                return TRUE;
-            }
-
-            char *xmlns = xmpp_stanza_get_attribute(query, STANZA_ATTR_XMLNS);
-            if (xmlns == NULL) {
-                return TRUE;
-            }
-            if (strcmp(xmlns, XMPP_NS_ROSTER) != 0) {
-                return TRUE;
-            }
-
-            xmpp_stanza_t *item =
-                xmpp_stanza_get_child_by_name(query, STANZA_NAME_ITEM);
-            if (item == NULL) {
-                return TRUE;
-            }
-
-            const char *jid = xmpp_stanza_get_attribute(item, STANZA_ATTR_JID);
-            const char *sub = xmpp_stanza_get_attribute(item, STANZA_ATTR_SUBSCRIPTION);
-            if (g_strcmp0(sub, "remove") == 0) {
-                contact_list_remove(jid);
-                return TRUE;
-            }
-
-            gboolean pending_out = FALSE;
-            const char *ask = xmpp_stanza_get_attribute(item, STANZA_ATTR_ASK);
-            if ((ask != NULL) && (strcmp(ask, "subscribe") == 0)) {
-                pending_out = TRUE;
-            }
-
-            contact_list_update_subscription(jid, sub, pending_out);
-
-            return TRUE;
-
-        // handle server ping
-        } else if (strcmp(type, STANZA_TYPE_GET) == 0) {
-            xmpp_stanza_t *ping = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_PING);
-            if (ping == NULL) {
-                return TRUE;
-            }
-
-            char *xmlns = xmpp_stanza_get_attribute(ping, STANZA_ATTR_XMLNS);
-            if (xmlns == NULL) {
-                return TRUE;
-            }
-
-            if (strcmp(xmlns, STANZA_NS_PING) != 0) {
-                return TRUE;
-            }
-
-            char *to = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TO);
-            char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
-            if ((from == NULL) || (to == NULL)) {
-                return TRUE;
-            }
-
-            xmpp_stanza_t *pong = xmpp_stanza_new(jabber_conn.ctx);
-            xmpp_stanza_set_name(pong, STANZA_NAME_IQ);
-            xmpp_stanza_set_attribute(pong, STANZA_ATTR_TO, from);
-            xmpp_stanza_set_attribute(pong, STANZA_ATTR_FROM, to);
-            xmpp_stanza_set_attribute(pong, STANZA_ATTR_TYPE, STANZA_TYPE_RESULT);
-            if (id != NULL) {
-                xmpp_stanza_set_attribute(pong, STANZA_ATTR_ID, id);
-            }
-
-            xmpp_send(jabber_conn.conn, pong);
-            xmpp_stanza_release(pong);
-
-            return TRUE;
-        } else {
-            return TRUE;
-        }
-    }
-}
-
-static int
-_roster_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
-    xmpp_ctx_t * const ctx, const char * const id, const char * const type,
-    const char * const from)
-{
-    xmpp_stanza_t *query, *item;
-
-    query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY);
-    item = xmpp_stanza_get_children(query);
-
-    while (item != NULL) {
-        const char *jid = xmpp_stanza_get_attribute(item, STANZA_ATTR_JID);
-        const char *name = xmpp_stanza_get_attribute(item, STANZA_ATTR_NAME);
-        const char *sub = xmpp_stanza_get_attribute(item, STANZA_ATTR_SUBSCRIPTION);
-
-        gboolean pending_out = FALSE;
-        const char *ask = xmpp_stanza_get_attribute(item, STANZA_ATTR_ASK);
-        if (g_strcmp0(ask, "subscribe") == 0) {
-            pending_out = TRUE;
-        }
-
-        gboolean added = contact_list_add(jid, name, "offline", NULL, sub,
-            pending_out);
-
-        if (!added) {
-            log_warning("Attempt to add contact twice: %s", jid);
-        }
-
-        item = xmpp_stanza_get_next(item);
-    }
-
-    /* TODO: Save somehow last presence show and use it for initial
-     *       presence rather than PRESENCE_ONLINE. It will be helpful
-     *       when I set dnd status and reconnect for some reason */
-    // send initial presence
-    jabber_update_presence(PRESENCE_ONLINE, NULL, 0);
-
-    return 1;
-}
-
-static int
-_version_request_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
-    xmpp_ctx_t * const ctx, const char * const id, const char * const type,
-    const char * const from)
-{
-    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);
-
-        xmpp_stanza_release(response);
-    }
-
-    return 1;
-}
-
-static int
-_caps_request_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
-    xmpp_ctx_t * ctx, const char * const id, const char * const type,
-    const char * const from)
-{
-    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);
-
-    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_create_query_response_stanza(ctx);
-        xmpp_stanza_set_attribute(query, STANZA_ATTR_NODE, node_str);
-        xmpp_stanza_add_child(response, query);
-        xmpp_send(conn, response);
-
-        xmpp_stanza_release(response);
-    }
-
-    return 1;
-}
-
-static int
-_caps_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
-    xmpp_ctx_t * ctx, const char * const id, const char * const type,
-    const char * const from)
-{
-    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 = 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("Invalid SHA1 recieved for caps.");
-            FREE_SET_NULL(generated_sha1);
-            g_strfreev(split);
-
-            return 1;
-        }
-        FREE_SET_NULL(generated_sha1);
-        g_strfreev(split);
-
-    // non supported hash, or legacy caps
-    } else {
-        caps_key = strdup(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);
-
-    free(caps_key);
-
-    return 1;
-}
-
-static int
 _ping_timed_handler(xmpp_conn_t * const conn, void * const userdata)
 {
     if (jabber_conn.conn_status == JABBER_CONNECTED) {
diff --git a/src/iq.c b/src/iq.c
new file mode 100644
index 00000000..15c2f801
--- /dev/null
+++ b/src/iq.c
@@ -0,0 +1,374 @@
+/*
+ * iq.c
+ *
+ * Copyright (C) 2012, 2013 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <strophe.h>
+
+#include "capabilities.h"
+#include "common.h"
+#include "config.h"
+#include "contact_list.h"
+#include "jabber.h"
+#include "log.h"
+#include "stanza.h"
+
+static int _iq_error_handler(xmpp_conn_t * const conn,
+    xmpp_stanza_t * const stanza, xmpp_ctx_t * const ctx, const char * const id,
+    const char * const from);
+
+static int _roster_handler(xmpp_conn_t * const conn,
+    xmpp_stanza_t * const stanza, xmpp_ctx_t * const ctx,
+    const char * const id, const char * const type, const char * const from);
+static int _caps_response_handler(xmpp_conn_t * const conn,
+    xmpp_stanza_t * const stanza, xmpp_ctx_t * const ctx,
+    const char * const id, const char * const type, const char * const from);
+static int _caps_request_handler(xmpp_conn_t * const conn,
+    xmpp_stanza_t * const stanza, xmpp_ctx_t * const ctx,
+    const char * const id, const char * const type, const char * const from);
+static int _version_request_handler(xmpp_conn_t * const conn,
+    xmpp_stanza_t * const stanza, xmpp_ctx_t * const ctx,
+    const char * const id, const char * const type, const char * const from);
+
+int
+iq_handler(xmpp_conn_t * const conn,
+    xmpp_stanza_t * const stanza, void * const userdata)
+{
+    xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
+    char *id = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_ID);
+    char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
+    char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
+
+    if (g_strcmp0(type, "error") == 0) {
+        return _iq_error_handler(conn, stanza, ctx, id, from);
+    }
+/*
+    if (g_strcmp0(type, "get") == 0) {
+        return _iq_get_handler(conn, stanza, ctx, id, from);
+    }
+
+    if (g_strcmp0(type, "set") == 0) {
+        return _iq_set_handler(conn, stanza, ctx, id, from);
+    }
+
+    if (g_strcmp0(type, "result") == 0) {
+        return _iq_result_handler(conn, stanza, ctx, id, from);
+    }
+*/
+    // handle the initial roster request
+    if (g_strcmp0(id, "roster") == 0) {
+        return _roster_handler(conn, stanza, ctx, id, type, from);
+
+    // handle caps responses
+    } else if ((id != NULL) && (g_str_has_prefix(id, "disco")) &&
+            (g_strcmp0(type, "result") == 0)) {
+        return _caps_response_handler(conn, stanza, ctx, id, type, from);
+
+    // handle caps requests
+    } else if (stanza_is_caps_request(stanza)) {
+        return _caps_request_handler(conn, stanza, ctx, id, type, from);
+
+    } else if (stanza_is_version_request(stanza)) {
+        return _version_request_handler(conn, stanza, ctx, id, type, from);
+
+    // handle iq
+    } else {
+        char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
+        if (type == NULL) {
+            return TRUE;
+        }
+
+        // handle roster update
+        if (strcmp(type, STANZA_TYPE_SET) == 0) {
+
+            xmpp_stanza_t *query =
+                xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY);
+            if (query == NULL) {
+                return TRUE;
+            }
+
+            char *xmlns = xmpp_stanza_get_attribute(query, STANZA_ATTR_XMLNS);
+            if (xmlns == NULL) {
+                return TRUE;
+            }
+            if (strcmp(xmlns, XMPP_NS_ROSTER) != 0) {
+                return TRUE;
+            }
+
+            xmpp_stanza_t *item =
+                xmpp_stanza_get_child_by_name(query, STANZA_NAME_ITEM);
+            if (item == NULL) {
+                return TRUE;
+            }
+
+            const char *jid = xmpp_stanza_get_attribute(item, STANZA_ATTR_JID);
+            const char *sub = xmpp_stanza_get_attribute(item, STANZA_ATTR_SUBSCRIPTION);
+            if (g_strcmp0(sub, "remove") == 0) {
+                contact_list_remove(jid);
+                return TRUE;
+            }
+
+            gboolean pending_out = FALSE;
+            const char *ask = xmpp_stanza_get_attribute(item, STANZA_ATTR_ASK);
+            if ((ask != NULL) && (strcmp(ask, "subscribe") == 0)) {
+                pending_out = TRUE;
+            }
+
+            contact_list_update_subscription(jid, sub, pending_out);
+
+            return TRUE;
+
+        // handle server ping
+        } else if (strcmp(type, STANZA_TYPE_GET) == 0) {
+            xmpp_stanza_t *ping = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_PING);
+            if (ping == NULL) {
+                return TRUE;
+            }
+
+            char *xmlns = xmpp_stanza_get_attribute(ping, STANZA_ATTR_XMLNS);
+            if (xmlns == NULL) {
+                return TRUE;
+            }
+
+            if (strcmp(xmlns, STANZA_NS_PING) != 0) {
+                return TRUE;
+            }
+
+            char *to = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TO);
+            char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
+            if ((from == NULL) || (to == NULL)) {
+                return TRUE;
+            }
+
+            xmpp_stanza_t *pong = xmpp_stanza_new(ctx);
+            xmpp_stanza_set_name(pong, STANZA_NAME_IQ);
+            xmpp_stanza_set_attribute(pong, STANZA_ATTR_TO, from);
+            xmpp_stanza_set_attribute(pong, STANZA_ATTR_FROM, to);
+            xmpp_stanza_set_attribute(pong, STANZA_ATTR_TYPE, STANZA_TYPE_RESULT);
+            if (id != NULL) {
+                xmpp_stanza_set_attribute(pong, STANZA_ATTR_ID, id);
+            }
+
+            xmpp_send(conn, pong);
+            xmpp_stanza_release(pong);
+
+            return TRUE;
+        } else {
+            return TRUE;
+        }
+    }
+}
+
+static int
+_iq_error_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
+    xmpp_ctx_t * const ctx, const char * const id, const char * const from)
+{
+    if (id != NULL) {
+        log_error("IQ error received, id: %s.", id);
+    } else {
+        log_error("IQ error recieved.");
+    }
+
+    return 1;
+}
+
+static int
+_roster_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
+    xmpp_ctx_t * const ctx, const char * const id, const char * const type,
+    const char * const from)
+{
+    xmpp_stanza_t *query, *item;
+
+    query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY);
+    item = xmpp_stanza_get_children(query);
+
+    while (item != NULL) {
+        const char *jid = xmpp_stanza_get_attribute(item, STANZA_ATTR_JID);
+        const char *name = xmpp_stanza_get_attribute(item, STANZA_ATTR_NAME);
+        const char *sub = xmpp_stanza_get_attribute(item, STANZA_ATTR_SUBSCRIPTION);
+
+        gboolean pending_out = FALSE;
+        const char *ask = xmpp_stanza_get_attribute(item, STANZA_ATTR_ASK);
+        if (g_strcmp0(ask, "subscribe") == 0) {
+            pending_out = TRUE;
+        }
+
+        gboolean added = contact_list_add(jid, name, "offline", NULL, sub,
+            pending_out);
+
+        if (!added) {
+            log_warning("Attempt to add contact twice: %s", jid);
+        }
+
+        item = xmpp_stanza_get_next(item);
+    }
+
+    /* TODO: Save somehow last presence show and use it for initial
+     *       presence rather than PRESENCE_ONLINE. It will be helpful
+     *       when I set dnd status and reconnect for some reason */
+    // send initial presence
+    jabber_update_presence(PRESENCE_ONLINE, NULL, 0);
+
+    return 1;
+}
+
+static int
+_version_request_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
+    xmpp_ctx_t * const ctx, const char * const id, const char * const type,
+    const char * const from)
+{
+    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);
+
+        xmpp_stanza_release(response);
+    }
+
+    return 1;
+}
+
+static int
+_caps_request_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
+    xmpp_ctx_t * ctx, const char * const id, const char * const type,
+    const char * const from)
+{
+    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);
+
+    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_create_query_response_stanza(ctx);
+        xmpp_stanza_set_attribute(query, STANZA_ATTR_NODE, node_str);
+        xmpp_stanza_add_child(response, query);
+        xmpp_send(conn, response);
+
+        xmpp_stanza_release(response);
+    }
+
+    return 1;
+}
+
+static int
+_caps_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
+    xmpp_ctx_t * ctx, const char * const id, const char * const type,
+    const char * const from)
+{
+    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 = 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("Invalid SHA1 recieved for caps.");
+            FREE_SET_NULL(generated_sha1);
+            g_strfreev(split);
+
+            return 1;
+        }
+        FREE_SET_NULL(generated_sha1);
+        g_strfreev(split);
+
+    // non supported hash, or legacy caps
+    } else {
+        caps_key = strdup(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);
+
+    free(caps_key);
+
+    return 1;
+}
diff --git a/src/iq.h b/src/iq.h
new file mode 100644
index 00000000..f24bf023
--- /dev/null
+++ b/src/iq.h
@@ -0,0 +1,31 @@
+/*
+ * iq.h
+ *
+ * Copyright (C) 2012, 2013 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef IQ_H
+#define IQ_H
+
+#include <strophe.h>
+
+int iq_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
+    void * const userdata);
+
+#endif