about summary refs log tree commit diff stats
path: root/src/xmpp
diff options
context:
space:
mode:
authorWill Song <incertia9474@gmail.com>2015-05-29 19:53:37 -0500
committerWill Song <incertia9474@gmail.com>2015-05-29 19:53:37 -0500
commit9463c6719036957c8468e1363a48afbfe751fd3e (patch)
treeaed6ccd050fc182a0d4d111bef6f743d28a42a73 /src/xmpp
parent7f436d614b11d72893d856f1c3d817ab34b0b9eb (diff)
parent304e08a9c0bfa27ed84dc1ff0a2e2d32b36529f8 (diff)
downloadprofani-tty-9463c6719036957c8468e1363a48afbfe751fd3e.tar.gz
fix conflicts
Diffstat (limited to 'src/xmpp')
-rw-r--r--src/xmpp/bookmark.c26
-rw-r--r--src/xmpp/bookmark.h2
-rw-r--r--src/xmpp/capabilities.c46
-rw-r--r--src/xmpp/capabilities.h2
-rw-r--r--src/xmpp/connection.c43
-rw-r--r--src/xmpp/connection.h2
-rw-r--r--src/xmpp/form.c4
-rw-r--r--src/xmpp/form.h2
-rw-r--r--src/xmpp/iq.c407
-rw-r--r--src/xmpp/iq.h2
-rw-r--r--src/xmpp/message.c559
-rw-r--r--src/xmpp/message.h2
-rw-r--r--src/xmpp/presence.c107
-rw-r--r--src/xmpp/presence.h2
-rw-r--r--src/xmpp/roster.c109
-rw-r--r--src/xmpp/roster.h2
-rw-r--r--src/xmpp/stanza.c200
-rw-r--r--src/xmpp/stanza.h23
-rw-r--r--src/xmpp/xmpp.h11
19 files changed, 1035 insertions, 516 deletions
diff --git a/src/xmpp/bookmark.c b/src/xmpp/bookmark.c
index 94adabea..68e66569 100644
--- a/src/xmpp/bookmark.c
+++ b/src/xmpp/bookmark.c
@@ -1,7 +1,7 @@
 /*
  * bookmark.c
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -42,7 +42,7 @@
 #include "common.h"
 #include "log.h"
 #include "muc.h"
-#include "server_events.h"
+#include "event/server_events.h"
 #include "xmpp/connection.h"
 #include "xmpp/stanza.h"
 #include "xmpp/xmpp.h"
@@ -74,7 +74,7 @@ bookmark_request(void)
 
     autocomplete_free(bookmark_ac);
     bookmark_ac = autocomplete_new();
-    if (bookmark_list != NULL) {
+    if (bookmark_list) {
         g_list_free_full(bookmark_list, _bookmark_item_destroy);
         bookmark_list = NULL;
     }
@@ -96,12 +96,12 @@ bookmark_add(const char *jid, const char *nick, const char *password, const char
     } else {
         Bookmark *item = malloc(sizeof(*item));
         item->jid = strdup(jid);
-        if (nick != NULL) {
+        if (nick) {
             item->nick = strdup(nick);
         } else {
             item->nick = NULL;
         }
-        if (password != NULL) {
+        if (password) {
             item->password = strdup(password);
         } else {
             item->password = NULL;
@@ -136,15 +136,15 @@ bookmark_update(const char *jid, const char *nick, const char *password, const c
         return FALSE;
     } else {
         Bookmark *bm = found->data;
-        if (nick != NULL) {
+        if (nick) {
             free(bm->nick);
             bm->nick = strdup(nick);
         }
-        if (password != NULL) {
+        if (password) {
             free(bm->password);
             bm->password = strdup(password);
         }
-        if (autojoin_str != NULL) {
+        if (autojoin_str) {
             if (g_strcmp0(autojoin_str, "on") == 0) {
                 bm->autojoin = TRUE;
             } else if (g_strcmp0(autojoin_str, "off") == 0) {
@@ -228,7 +228,7 @@ bookmark_find(const char * const search_str)
 void
 bookmark_autocomplete_reset(void)
 {
-    if (bookmark_ac != NULL) {
+    if (bookmark_ac) {
         autocomplete_reset(bookmark_ac);
     }
 }
@@ -413,14 +413,14 @@ _send_bookmarks(void)
     xmpp_stanza_set_ns(storage, "storage:bookmarks");
 
     GList *curr = bookmark_list;
-    while (curr != NULL) {
+    while (curr) {
         Bookmark *bookmark = curr->data;
         xmpp_stanza_t *conference = xmpp_stanza_new(ctx);
         xmpp_stanza_set_name(conference, STANZA_NAME_CONFERENCE);
         xmpp_stanza_set_attribute(conference, STANZA_ATTR_JID, bookmark->jid);
 
         Jid *jidp = jid_create(bookmark->jid);
-        if (jidp->localpart != NULL) {
+        if (jidp->localpart) {
             xmpp_stanza_set_attribute(conference, STANZA_ATTR_NAME, jidp->localpart);
         }
         jid_destroy(jidp);
@@ -431,7 +431,7 @@ _send_bookmarks(void)
             xmpp_stanza_set_attribute(conference, STANZA_ATTR_AUTOJOIN, "false");
         }
 
-        if (bookmark->nick != NULL) {
+        if (bookmark->nick) {
             xmpp_stanza_t *nick_st = xmpp_stanza_new(ctx);
             xmpp_stanza_set_name(nick_st, STANZA_NAME_NICK);
             xmpp_stanza_t *nick_text = xmpp_stanza_new(ctx);
@@ -443,7 +443,7 @@ _send_bookmarks(void)
             xmpp_stanza_release(nick_st);
         }
 
-        if (bookmark->password != NULL) {
+        if (bookmark->password) {
             xmpp_stanza_t *password_st = xmpp_stanza_new(ctx);
             xmpp_stanza_set_name(password_st, STANZA_NAME_PASSWORD);
             xmpp_stanza_t *password_text = xmpp_stanza_new(ctx);
diff --git a/src/xmpp/bookmark.h b/src/xmpp/bookmark.h
index f9392182..c8de8147 100644
--- a/src/xmpp/bookmark.h
+++ b/src/xmpp/bookmark.h
@@ -1,7 +1,7 @@
 /*
  * bookmark.h
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/xmpp/capabilities.c b/src/xmpp/capabilities.c
index 164313e1..56475da1 100644
--- a/src/xmpp/capabilities.c
+++ b/src/xmpp/capabilities.c
@@ -1,7 +1,7 @@
 /*
  * capabilities.c
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -154,56 +154,56 @@ _caps_by_ver(const char * const ver)
 
         char *category = g_key_file_get_string(cache, ver, "category", NULL);
         if (category) {
-            new_caps->category = strdup(category);
+            new_caps->category = category;
         } else {
             new_caps->category = NULL;
         }
 
         char *type = g_key_file_get_string(cache, ver, "type", NULL);
         if (type) {
-            new_caps->type = strdup(type);
+            new_caps->type = type;
         } else {
             new_caps->type = NULL;
         }
 
         char *name = g_key_file_get_string(cache, ver, "name", NULL);
         if (name) {
-            new_caps->name = strdup(name);
+            new_caps->name = name;
         } else {
             new_caps->name = NULL;
         }
 
         char *software = g_key_file_get_string(cache, ver, "software", NULL);
         if (software) {
-            new_caps->software = strdup(software);
+            new_caps->software = software;
         } else {
             new_caps->software = NULL;
         }
 
         char *software_version = g_key_file_get_string(cache, ver, "software_version", NULL);
         if (software_version) {
-            new_caps->software_version = strdup(software_version);
+            new_caps->software_version = software_version;
         } else {
             new_caps->software_version = NULL;
         }
 
         char *os = g_key_file_get_string(cache, ver, "os", NULL);
         if (os) {
-            new_caps->os = strdup(os);
+            new_caps->os = os;
         } else {
             new_caps->os = NULL;
         }
 
         char *os_version = g_key_file_get_string(cache, ver, "os_version", NULL);
         if (os_version) {
-            new_caps->os_version = strdup(os_version);
+            new_caps->os_version = os_version;
         } else {
             new_caps->os_version = NULL;
         }
 
         gsize features_len = 0;
         gchar **features = g_key_file_get_string_list(cache, ver, "features", &features_len, NULL);
-        if (features != NULL && features_len > 0) {
+        if (features && features_len > 0) {
             GSList *features_list = NULL;
             int i;
             for (i = 0; i < features_len; i++) {
@@ -395,16 +395,16 @@ caps_create(xmpp_stanza_t *query)
     GSList *features = NULL;
 
     xmpp_stanza_t *softwareinfo = xmpp_stanza_get_child_by_ns(query, STANZA_NS_DATA);
-    if (softwareinfo != NULL) {
+    if (softwareinfo) {
         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) {
+            while (field) {
                 formField = field->data;
-                if (formField->values != NULL) {
+                if (formField->values) {
                     if (strcmp(formField->var, "software") == 0) {
                         software = strdup(formField->values->data);
                     } else if (strcmp(formField->var, "software_version") == 0) {
@@ -424,7 +424,7 @@ caps_create(xmpp_stanza_t *query)
 
     xmpp_stanza_t *child = xmpp_stanza_get_children(query);
     GSList *identity_stanzas = NULL;
-    while (child != NULL) {
+    while (child) {
         if (g_strcmp0(xmpp_stanza_get_name(child), "feature") == 0) {
             features = g_slist_append(features, strdup(xmpp_stanza_get_attribute(child, "var")));
         }
@@ -490,42 +490,42 @@ caps_create(xmpp_stanza_t *query)
 
     Capabilities *new_caps = malloc(sizeof(struct capabilities_t));
 
-    if (category != NULL) {
+    if (category) {
         new_caps->category = strdup(category);
     } else {
         new_caps->category = NULL;
     }
-    if (type != NULL) {
+    if (type) {
         new_caps->type = strdup(type);
     } else {
         new_caps->type = NULL;
     }
-    if (name != NULL) {
+    if (name) {
         new_caps->name = strdup(name);
     } else {
         new_caps->name = NULL;
     }
-    if (software != NULL) {
+    if (software) {
         new_caps->software = software;
     } else {
         new_caps->software = NULL;
     }
-    if (software_version != NULL) {
+    if (software_version) {
         new_caps->software_version = software_version;
     } else {
         new_caps->software_version = NULL;
     }
-    if (os != NULL) {
+    if (os) {
         new_caps->os = os;
     } else {
         new_caps->os = NULL;
     }
-    if (os_version != NULL) {
+    if (os_version) {
         new_caps->os_version = os_version;
     } else {
         new_caps->os_version = NULL;
     }
-    if (features != NULL) {
+    if (features) {
         new_caps->features = features;
     } else {
         new_caps->features = NULL;
@@ -635,7 +635,7 @@ caps_close(void)
 void
 caps_destroy(Capabilities *caps)
 {
-    if (caps != NULL) {
+    if (caps) {
         free(caps->category);
         free(caps->type);
         free(caps->name);
@@ -643,7 +643,7 @@ caps_destroy(Capabilities *caps)
         free(caps->software_version);
         free(caps->os);
         free(caps->os_version);
-        if (caps->features != NULL) {
+        if (caps->features) {
             g_slist_free_full(caps->features, free);
         }
         free(caps);
diff --git a/src/xmpp/capabilities.h b/src/xmpp/capabilities.h
index 692ac49d..85f1d989 100644
--- a/src/xmpp/capabilities.h
+++ b/src/xmpp/capabilities.h
@@ -1,7 +1,7 @@
 /*
  * capabilities.h
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/xmpp/connection.c b/src/xmpp/connection.c
index 915525e4..70d49b7c 100644
--- a/src/xmpp/connection.c
+++ b/src/xmpp/connection.c
@@ -1,7 +1,7 @@
 /*
  * connection.c
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -45,7 +45,7 @@
 #include "log.h"
 #include "muc.h"
 #include "profanity.h"
-#include "server_events.h"
+#include "event/server_events.h"
 #include "xmpp/bookmark.h"
 #include "xmpp/capabilities.h"
 #include "xmpp/connection.h"
@@ -129,11 +129,11 @@ jabber_connect_with_account(const ProfAccount * const account)
     log_info("Connecting using account: %s", account->name);
 
     // save account name and password for reconnect
-    if (saved_account.name != NULL) {
+    if (saved_account.name) {
         free(saved_account.name);
     }
     saved_account.name = strdup(account->name);
-    if (saved_account.passwd != NULL) {
+    if (saved_account.passwd) {
         free(saved_account.passwd);
     }
     saved_account.passwd = strdup(account->password);
@@ -157,7 +157,7 @@ jabber_connect_with_details(const char * const jid,
     // save details for reconnect, remember name for account creating on success
     saved_details.name = strdup(jid);
     saved_details.passwd = strdup(passwd);
-    if (altdomain != NULL) {
+    if (altdomain) {
         saved_details.altdomain = strdup(altdomain);
     } else {
         saved_details.altdomain = NULL;
@@ -199,11 +199,11 @@ jabber_disconnect(void)
         _connection_free_saved_account();
         _connection_free_saved_details();
         _connection_free_session_data();
-        if (jabber_conn.conn != NULL) {
+        if (jabber_conn.conn) {
             xmpp_conn_release(jabber_conn.conn);
             jabber_conn.conn = NULL;
         }
-        if (jabber_conn.ctx != NULL) {
+        if (jabber_conn.ctx) {
             xmpp_ctx_free(jabber_conn.ctx);
             jabber_conn.ctx = NULL;
         }
@@ -238,7 +238,7 @@ jabber_process_events(void)
             break;
         case JABBER_DISCONNECTED:
             reconnect_sec = prefs_get_reconnect();
-            if ((reconnect_sec != 0) && (reconnect_timer != NULL)) {
+            if ((reconnect_sec != 0) && reconnect_timer) {
                 int elapsed_sec = g_timer_elapsed(reconnect_timer, NULL);
                 if (elapsed_sec > reconnect_sec) {
                     _jabber_reconnect();
@@ -302,7 +302,7 @@ void
 connection_set_presence_message(const char * const message)
 {
     FREE_SET_NULL(jabber_conn.presence_message);
-    if (message != NULL) {
+    if (message) {
         jabber_conn.presence_message = strdup(message);
     }
 }
@@ -371,15 +371,15 @@ _jabber_connect(const char * const fulljid, const char * const passwd,
     jid_destroy(jid);
 
     log_info("Connecting as %s", fulljid);
-    if (jabber_conn.log != NULL) {
+    if (jabber_conn.log) {
         free(jabber_conn.log);
     }
     jabber_conn.log = _xmpp_get_file_logger();
 
-    if (jabber_conn.conn != NULL) {
+    if (jabber_conn.conn) {
         xmpp_conn_release(jabber_conn.conn);
     }
-    if (jabber_conn.ctx != NULL) {
+    if (jabber_conn.ctx) {
         xmpp_ctx_free(jabber_conn.ctx);
     }
     jabber_conn.ctx = xmpp_ctx_new(NULL, jabber_conn.log);
@@ -436,9 +436,9 @@ _connection_handler(xmpp_conn_t * const conn,
         log_debug("Connection handler: XMPP_CONN_CONNECT");
 
         // logged in with account
-        if (saved_account.name != NULL) {
+        if (saved_account.name) {
             log_debug("Connection handler: logged in with account name: %s", saved_account.name);
-            handle_login_account_success(saved_account.name);
+            sv_ev_login_account_success(saved_account.name);
 
         // logged in without account, use details to create new account
         } else {
@@ -446,7 +446,7 @@ _connection_handler(xmpp_conn_t * const conn,
             accounts_add(saved_details.name, saved_details.altdomain, saved_details.port);
             accounts_set_jid(saved_details.name, saved_details.jid);
 
-            handle_login_account_success(saved_details.name);
+            sv_ev_login_account_success(saved_details.name);
             saved_account.name = strdup(saved_details.name);
             saved_account.passwd = strdup(saved_details.passwd);
 
@@ -466,10 +466,15 @@ _connection_handler(xmpp_conn_t * const conn,
 
         roster_request();
         bookmark_request();
+
+        if (prefs_get_boolean(PREF_CARBONS)){
+            iq_enable_carbons();
+        }
+
         jabber_conn.conn_status = JABBER_CONNECTED;
 
         if (prefs_get_reconnect() != 0) {
-            if (reconnect_timer != NULL) {
+            if (reconnect_timer) {
                 g_timer_destroy(reconnect_timer);
                 reconnect_timer = NULL;
             }
@@ -481,7 +486,7 @@ _connection_handler(xmpp_conn_t * const conn,
         // lost connection for unknown reason
         if (jabber_conn.conn_status == JABBER_CONNECTED) {
             log_debug("Connection handler: Lost connection for unknown reason");
-            handle_lost_connection();
+            sv_ev_lost_connection();
             if (prefs_get_reconnect() != 0) {
                 assert(reconnect_timer == NULL);
                 reconnect_timer = g_timer_new();
@@ -498,7 +503,7 @@ _connection_handler(xmpp_conn_t * const conn,
             log_debug("Connection handler: Login failed");
             if (reconnect_timer == NULL) {
                 log_debug("Connection handler: No reconnect timer");
-                handle_failed_login();
+                sv_ev_failed_login();
                 _connection_free_saved_account();
                 _connection_free_saved_details();
                 _connection_free_session_data();
@@ -558,7 +563,7 @@ _xmpp_file_logger(void * const userdata, const xmpp_log_level_t level,
     log_level_t prof_level = _get_log_level(level);
     log_msg(prof_level, area, msg);
     if ((g_strcmp0(area, "xmpp") == 0) || (g_strcmp0(area, "conn")) == 0) {
-        handle_xmpp_stanza(msg);
+        sv_ev_xmpp_stanza(msg);
     }
 }
 
diff --git a/src/xmpp/connection.h b/src/xmpp/connection.h
index f9e2cf22..63f7cde0 100644
--- a/src/xmpp/connection.h
+++ b/src/xmpp/connection.h
@@ -1,7 +1,7 @@
 /*
  * connection.h
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/xmpp/form.c b/src/xmpp/form.c
index 1facc754..e6213b64 100644
--- a/src/xmpp/form.c
+++ b/src/xmpp/form.c
@@ -1,7 +1,7 @@
 /*
  * form.c
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -211,7 +211,7 @@ form_create(xmpp_stanza_t * const form_stanza)
 
             field->var = _get_attr(field_stanza, "var");
 
-            if (field->type_t != FIELD_HIDDEN && field->var != NULL) {
+            if (field->type_t != FIELD_HIDDEN && field->var) {
                 GString *tag = g_string_new("");
                 g_string_printf(tag, "field%d", tag_num++);
                 g_hash_table_insert(form->var_to_tag, strdup(field->var), strdup(tag->str));
diff --git a/src/xmpp/form.h b/src/xmpp/form.h
index fa14e1c5..86cd4b7b 100644
--- a/src/xmpp/form.h
+++ b/src/xmpp/form.h
@@ -1,7 +1,7 @@
 /*
  * form.h
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c
index 7fedf0ed..18abad36 100644
--- a/src/xmpp/iq.c
+++ b/src/xmpp/iq.c
@@ -1,7 +1,7 @@
 /*
  * iq.c
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -47,8 +47,9 @@
 #include "log.h"
 #include "muc.h"
 #include "profanity.h"
+#include "ui/ui.h"
 #include "config/preferences.h"
-#include "server_events.h"
+#include "event/server_events.h"
 #include "xmpp/capabilities.h"
 #include "xmpp/connection.h"
 #include "xmpp/stanza.h"
@@ -58,48 +59,35 @@
 
 #define HANDLE(ns, type, func) xmpp_handler_add(conn, func, ns, STANZA_NAME_IQ, type, ctx)
 
-static int _error_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-static int _ping_get_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-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_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);
-static int _disco_items_result_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-static int _disco_items_get_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-static int _destroy_room_result_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-static int _room_config_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-static int _room_config_submit_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-static int _room_affiliation_list_result_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-static int _room_affiliation_set_result_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-static int _room_role_set_result_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-static int _room_role_list_result_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-static int _room_kick_result_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-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);
-static int _caps_response_handler_for_jid(xmpp_conn_t *const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-static int _caps_response_handler_legacy(xmpp_conn_t *const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
+typedef struct p_room_info_data_t {
+    char *room;
+    gboolean display;
+} ProfRoomInfoData;
+
+static int _error_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _ping_get_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+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_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _room_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);
+static int _disco_items_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _disco_items_get_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _destroy_room_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _room_config_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _room_config_submit_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _room_affiliation_list_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _room_affiliation_set_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _room_role_set_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _room_role_list_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _room_kick_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _enable_carbons_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _disable_carbons_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+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);
+static int _caps_response_handler_for_jid(xmpp_conn_t *const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _caps_response_handler_legacy(xmpp_conn_t *const conn, xmpp_stanza_t * const stanza, void * const userdata);
 
 void
 iq_add_handlers(void)
@@ -153,6 +141,34 @@ iq_room_list_request(gchar *conferencejid)
 }
 
 void
+iq_enable_carbons()
+{
+    xmpp_conn_t * const conn = connection_get_conn();
+    xmpp_ctx_t * const ctx = connection_get_ctx();
+    xmpp_stanza_t *iq = stanza_enable_carbons(ctx);
+    char *id = xmpp_stanza_get_id(iq);
+
+    xmpp_id_handler_add(conn, _enable_carbons_handler, id, NULL);
+
+    xmpp_send(conn, iq);
+    xmpp_stanza_release(iq);
+}
+
+void
+iq_disable_carbons()
+{
+    xmpp_conn_t * const conn = connection_get_conn();
+    xmpp_ctx_t * const ctx = connection_get_ctx();
+    xmpp_stanza_t *iq = stanza_disable_carbons(ctx);
+    char *id = xmpp_stanza_get_id(iq);
+
+    xmpp_id_handler_add(conn, _disable_carbons_handler, id, NULL);
+
+    xmpp_send(conn, iq);
+    xmpp_stanza_release(iq);
+}
+
+void
 iq_disco_info_request(gchar *jid)
 {
     xmpp_conn_t * const conn = connection_get_conn();
@@ -169,14 +185,18 @@ iq_disco_info_request(gchar *jid)
 }
 
 void
-iq_room_info_request(gchar *room)
+iq_room_info_request(const char * const room, gboolean display_result)
 {
     xmpp_conn_t * const conn = connection_get_conn();
     xmpp_ctx_t * const ctx = connection_get_ctx();
     char *id = create_unique_id("room_disco_info");
     xmpp_stanza_t *iq = stanza_create_disco_info_iq(ctx, id, room, NULL);
 
-    xmpp_id_handler_add(conn, _disco_info_response_handler, id, room);
+    ProfRoomInfoData *cb_data = malloc(sizeof(ProfRoomInfoData));
+    cb_data->room = strdup(room);
+    cb_data->display = display_result;
+
+    xmpp_id_handler_add(conn, _room_info_response_handler, id, cb_data);
 
     free(id);
 
@@ -456,7 +476,7 @@ _error_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     const char *id = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_ID);
     char *error_msg = stanza_get_error_message(stanza);
 
-    if (id != NULL) {
+    if (id) {
         log_debug("IQ error handler fired, id: %s, error: %s", id, error_msg);
         log_error("IQ error received, id: %s, error: %s", id, error_msg);
     } else {
@@ -476,13 +496,13 @@ _pong_handler(xmpp_conn_t *const conn, xmpp_stanza_t * const stanza,
     char *id = xmpp_stanza_get_id(stanza);
     char *type = xmpp_stanza_get_type(stanza);
 
-    if (id != NULL) {
+    if (id) {
         log_debug("IQ pong handler fired, id: %s.", id);
     } else {
         log_debug("IQ pong handler fired.");
     }
 
-    if (id != NULL && type != NULL) {
+    if (id && type) {
         // show warning if error
         if (strcmp(type, STANZA_TYPE_ERROR) == 0) {
             char *error_msg = stanza_get_error_message(stanza);
@@ -491,12 +511,13 @@ _pong_handler(xmpp_conn_t *const conn, xmpp_stanza_t * const stanza,
 
             // turn off autoping if error type is 'cancel'
             xmpp_stanza_t *error = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_ERROR);
-            if (error != NULL) {
+            if (error) {
                 char *errtype = xmpp_stanza_get_type(error);
-                if (errtype != NULL) {
+                if (errtype) {
                     if (strcmp(errtype, "cancel") == 0) {
                         log_warning("Server ping (id=%s) error type 'cancel', disabling autoping.", id);
-                        handle_autoping_cancel();
+                        prefs_set_autoping(0);
+                        cons_show_error("Server ping not supported, autoping disabled.");
                         xmpp_timed_handler_delete(conn, _ping_timed_handler);
                     }
                 }
@@ -586,12 +607,14 @@ static int
 _caps_response_handler_for_jid(xmpp_conn_t *const conn, xmpp_stanza_t * const stanza,
     void * const userdata)
 {
+    char *jid = (char *)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);
 
     char *type = xmpp_stanza_get_type(stanza);
     // ignore non result
     if ((g_strcmp0(type, "get") == 0) || (g_strcmp0(type, "set") == 0)) {
+        free(jid);
         return 1;
     }
 
@@ -604,6 +627,7 @@ _caps_response_handler_for_jid(xmpp_conn_t *const conn, xmpp_stanza_t * const st
     const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
     if (!from) {
         log_info("No from attribute");
+        free(jid);
         return 0;
     }
 
@@ -612,25 +636,29 @@ _caps_response_handler_for_jid(xmpp_conn_t *const conn, xmpp_stanza_t * const st
         char *error_message = stanza_get_error_message(stanza);
         log_warning("Error received for capabilities response from %s: ", from, error_message);
         free(error_message);
+        free(jid);
         return 0;
     }
 
     if (query == NULL) {
         log_warning("No query element found.");
+        free(jid);
         return 0;
     }
 
     char *node = xmpp_stanza_get_attribute(query, STANZA_ATTR_NODE);
     if (node == NULL) {
         log_warning("No node attribute found");
+        free(jid);
         return 0;
     }
 
-    char *jid = (char *)userdata;
     log_info("Associating capabilities with: %s", jid);
     Capabilities *capabilities = caps_create(query);
     caps_add_by_jid(jid, capabilities);
 
+    free(jid);
+
     return 0;
 }
 
@@ -640,10 +668,12 @@ _caps_response_handler_legacy(xmpp_conn_t *const conn, xmpp_stanza_t * const sta
 {
     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 *expected_node = (char *)userdata;
 
     char *type = xmpp_stanza_get_type(stanza);
     // ignore non result
     if ((g_strcmp0(type, "get") == 0) || (g_strcmp0(type, "set") == 0)) {
+        free(expected_node);
         return 1;
     }
 
@@ -656,6 +686,7 @@ _caps_response_handler_legacy(xmpp_conn_t *const conn, xmpp_stanza_t * const sta
     const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
     if (!from) {
         log_info("No from attribute");
+        free(expected_node);
         return 0;
     }
 
@@ -664,22 +695,23 @@ _caps_response_handler_legacy(xmpp_conn_t *const conn, xmpp_stanza_t * const sta
         char *error_message = stanza_get_error_message(stanza);
         log_warning("Error received for capabilities response from %s: ", from, error_message);
         free(error_message);
+        free(expected_node);
         return 0;
     }
 
     if (query == NULL) {
         log_warning("No query element found.");
+        free(expected_node);
         return 0;
     }
 
     char *node = xmpp_stanza_get_attribute(query, STANZA_ATTR_NODE);
     if (node == NULL) {
         log_warning("No node attribute found");
+        free(expected_node);
         return 0;
     }
 
-    char *expected_node = (char *)userdata;
-
     // nodes match
     if (g_strcmp0(expected_node, node) == 0) {
         log_info("Legacy capabilities, nodes match %s", node);
@@ -704,6 +736,36 @@ _caps_response_handler_legacy(xmpp_conn_t *const conn, xmpp_stanza_t * const sta
 }
 
 static int
+_enable_carbons_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
+{
+    char *type = xmpp_stanza_get_type(stanza);
+    if (g_strcmp0(type, "error") == 0) {
+        char *error_message = stanza_get_error_message(stanza);
+        cons_show_error("Server error enabling message carbons: %s", error_message);
+        log_debug("Error enabling carbons: %s", error_message);
+    } else {
+        log_debug("Message carbons enabled.");
+    }
+
+    return 0;
+}
+
+static int
+_disable_carbons_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
+{
+    char *type = xmpp_stanza_get_type(stanza);
+    if (g_strcmp0(type, "error") == 0) {
+        char *error_message = stanza_get_error_message(stanza);
+        cons_show_error("Server error disabling message carbons: %s", error_message);
+        log_debug("Error disabling carbons: %s", error_message);
+    } else {
+        log_debug("Message carbons disabled.");
+    }
+
+    return 0;
+}
+
+static int
 _manual_pong_handler(xmpp_conn_t *const conn, xmpp_stanza_t * const stanza,
     void * const userdata)
 {
@@ -714,7 +776,12 @@ _manual_pong_handler(xmpp_conn_t *const conn, xmpp_stanza_t * const stanza,
     // handle error responses
     if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) {
         char *error_message = stanza_get_error_message(stanza);
-        handle_ping_error_result(from, error_message);
+        if (!error_message) {
+            cons_show_error("Error returned from pinging %s.", from);
+        } else {
+            cons_show_error("Error returned from pinging %s: %s.", from, error_message);
+        }
+
         free(error_message);
         g_date_time_unref(sent);
         return 0;
@@ -728,7 +795,11 @@ _manual_pong_handler(xmpp_conn_t *const conn, xmpp_stanza_t * const stanza,
     g_date_time_unref(sent);
     g_date_time_unref(now);
 
-    handle_ping_result(from, elapsed_millis);
+    if (from == NULL) {
+        cons_show("Ping response from server: %dms.", elapsed_millis);
+    } else {
+        cons_show("Ping response from %s: %dms.", from, elapsed_millis);
+    }
 
     return 0;
 }
@@ -759,7 +830,7 @@ _version_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
 {
     char *id = xmpp_stanza_get_id(stanza);
 
-    if (id != NULL) {
+    if (id) {
         log_debug("IQ version result handler fired, id: %s.", id);
     } else {
         log_debug("IQ version result handler fired.");
@@ -784,13 +855,13 @@ _version_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     xmpp_stanza_t *version = xmpp_stanza_get_child_by_name(query, "version");
     xmpp_stanza_t *os = xmpp_stanza_get_child_by_name(query, "os");
 
-    if (name != NULL) {
+    if (name) {
         name_str = xmpp_stanza_get_text(name);
     }
-    if (version != NULL) {
+    if (version) {
         version_str = xmpp_stanza_get_text(version);
     }
-    if (os != NULL) {
+    if (os) {
         os_str = xmpp_stanza_get_text(os);
     }
 
@@ -805,7 +876,7 @@ _version_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
         presence = string_from_resource_presence(resource->presence);
     }
 
-    handle_software_version_result(jid, presence, name_str, version_str, os_str);
+    cons_show_software_version(jid, presence, name_str, version_str, os_str);
 
     jid_destroy(jidp);
 
@@ -821,7 +892,7 @@ _ping_get_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     const char *to = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TO);
     const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
 
-    if (id != NULL) {
+    if (id) {
         log_debug("IQ ping get handler fired, id: %s.", id);
     } else {
         log_debug("IQ ping get handler fired.");
@@ -837,7 +908,7 @@ _ping_get_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     xmpp_stanza_set_attribute(pong, STANZA_ATTR_FROM, to);
     xmpp_stanza_set_attribute(pong, STANZA_ATTR_TYPE, STANZA_TYPE_RESULT);
 
-    if (id != NULL) {
+    if (id) {
         xmpp_stanza_set_attribute(pong, STANZA_ATTR_ID, id);
     }
 
@@ -855,16 +926,16 @@ _version_get_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     const char *id = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_ID);
     const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
 
-    if (id != NULL) {
+    if (id) {
         log_debug("IQ version get handler fired, id: %s.", id);
     } else {
         log_debug("IQ version get handler fired.");
     }
 
-    if (from != NULL) {
+    if (from) {
         xmpp_stanza_t *response = xmpp_stanza_new(ctx);
         xmpp_stanza_set_name(response, STANZA_NAME_IQ);
-        if (id != NULL) {
+        if (id) {
             xmpp_stanza_set_id(response, id);
         }
         xmpp_stanza_set_attribute(response, STANZA_ATTR_TO, from);
@@ -923,13 +994,13 @@ _disco_items_get_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     const char *id = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_ID);
     const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
 
-    if (id != NULL) {
+    if (id) {
         log_debug("IQ disco items get handler fired, id: %s.", id);
     } else {
         log_debug("IQ disco items get handler fired.");
     }
 
-    if (from != NULL) {
+    if (from) {
         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));
@@ -960,20 +1031,20 @@ _disco_info_get_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
 
     const char *id = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_ID);
 
-    if (id != NULL) {
+    if (id) {
         log_debug("IQ disco info get handler fired, id: %s.", id);
     } else {
         log_debug("IQ disco info get handler fired.");
     }
 
-    if (from != NULL) {
+    if (from) {
         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);
-        if (node_str != NULL) {
+        if (node_str) {
             xmpp_stanza_set_attribute(query, STANZA_ATTR_NODE, node_str);
         }
         xmpp_stanza_add_child(response, query);
@@ -992,7 +1063,7 @@ _destroy_room_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const sta
 {
     const char *id = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_ID);
 
-    if (id != NULL) {
+    if (id) {
         log_debug("IQ destroy room result handler fired, id: %s.", id);
     } else {
         log_debug("IQ destroy room result handler fired.");
@@ -1002,7 +1073,7 @@ _destroy_room_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const sta
     if (from == NULL) {
         log_error("No from attribute for IQ destroy room result");
     } else {
-        handle_room_destroy(from);
+        sv_ev_room_destroy(from);
     }
 
     return 0;
@@ -1016,7 +1087,7 @@ _room_config_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     const char *type = xmpp_stanza_get_type(stanza);
     const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
 
-    if (id != NULL) {
+    if (id) {
         log_debug("IQ room config handler fired, id: %s.", id);
     } else {
         log_debug("IQ room config handler fired.");
@@ -1025,40 +1096,40 @@ _room_config_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     // handle error responses
     if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) {
         char *error_message = stanza_get_error_message(stanza);
-        handle_room_configuration_form_error(from, error_message);
+        ui_handle_room_configuration_form_error(from, error_message);
         free(error_message);
         return 0;
     }
 
     if (from == NULL) {
         log_warning("No from attribute for IQ config request result");
-        handle_room_configuration_form_error(from, "No from attribute for room cofig response.");
+        ui_handle_room_configuration_form_error(from, "No from attribute for room cofig response.");
         return 0;
     }
 
     xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY);
     if (query == NULL) {
         log_warning("No query element found parsing room config response");
-        handle_room_configuration_form_error(from, "No query element found parsing room config response");
+        ui_handle_room_configuration_form_error(from, "No query element found parsing room config response");
         return 0;
     }
 
     xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(query, STANZA_NS_DATA);
     if (x == NULL) {
         log_warning("No x element found with %s namespace parsing room config response", STANZA_NS_DATA);
-        handle_room_configuration_form_error(from, "No form configuration options available");
+        ui_handle_room_configuration_form_error(from, "No form configuration options available");
         return 0;
     }
 
     char *form_type = xmpp_stanza_get_attribute(x, STANZA_ATTR_TYPE);
     if (g_strcmp0(form_type, "form") != 0) {
         log_warning("x element not of type 'form' parsing room config response");
-        handle_room_configuration_form_error(from, "Form not of type 'form' parsing room config response.");
+        ui_handle_room_configuration_form_error(from, "Form not of type 'form' parsing room config response.");
         return 0;
     }
 
     DataForm *form = form_create(x);
-    handle_room_configure(from, form);
+    ui_handle_room_configuration(from, form);
 
     return 0;
 }
@@ -1071,7 +1142,7 @@ static int _room_affiliation_set_result_handler(xmpp_conn_t * const conn, xmpp_s
     const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
     struct privilege_set_t *affiliation_set = (struct privilege_set_t *)userdata;
 
-    if (id != NULL) {
+    if (id) {
         log_debug("IQ affiliation set handler fired, id: %s.", id);
     } else {
         log_debug("IQ affiliation set handler fired.");
@@ -1080,7 +1151,8 @@ static int _room_affiliation_set_result_handler(xmpp_conn_t * const conn, xmpp_s
     // handle error responses
     if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) {
         char *error_message = stanza_get_error_message(stanza);
-        handle_room_affiliation_set_error(from, affiliation_set->item, affiliation_set->privilege, error_message);
+        log_debug("Error setting affiliation %s list for room %s, user %s: %s", affiliation_set->privilege, from, affiliation_set->item, error_message);
+        ui_handle_room_affiliation_set_error(from, affiliation_set->item, affiliation_set->privilege, error_message);
         free(error_message);
     }
 
@@ -1099,7 +1171,7 @@ static int _room_role_set_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t
     const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
     struct privilege_set_t *role_set = (struct privilege_set_t *)userdata;
 
-    if (id != NULL) {
+    if (id) {
         log_debug("IQ role set handler fired, id: %s.", id);
     } else {
         log_debug("IQ role set handler fired.");
@@ -1108,7 +1180,8 @@ static int _room_role_set_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t
     // handle error responses
     if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) {
         char *error_message = stanza_get_error_message(stanza);
-        handle_room_role_set_error(from, role_set->item, role_set->privilege, error_message);
+        log_debug("Error setting role %s list for room %s, user %s: %s", role_set->privilege, from, role_set->item, error_message);
+        ui_handle_room_role_set_error(from, role_set->item, role_set->privilege, error_message);
         free(error_message);
     }
 
@@ -1127,7 +1200,7 @@ _room_affiliation_list_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t *
     const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
     char *affiliation = (char *)userdata;
 
-    if (id != NULL) {
+    if (id) {
         log_debug("IQ affiliation list result handler fired, id: %s.", id);
     } else {
         log_debug("IQ affiliation list result handler fired.");
@@ -1136,7 +1209,8 @@ _room_affiliation_list_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t *
     // handle error responses
     if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) {
         char *error_message = stanza_get_error_message(stanza);
-        handle_room_affiliation_list_result_error(from, affiliation, error_message);
+        log_debug("Error retrieving %s list for room %s: %s", affiliation, from, error_message);
+        ui_handle_room_affiliation_list_error(from, affiliation, error_message);
         free(error_message);
         free(affiliation);
         return 0;
@@ -1158,7 +1232,8 @@ _room_affiliation_list_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t *
         }
     }
 
-    handle_room_affiliation_list(from, affiliation, jids);
+    muc_jid_autocomplete_add_all(from, jids);
+    ui_handle_room_affiliation_list(from, affiliation, jids);
     free(affiliation);
     g_slist_free(jids);
 
@@ -1173,7 +1248,7 @@ _room_role_list_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const s
     const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
     char *role = (char *)userdata;
 
-    if (id != NULL) {
+    if (id) {
         log_debug("IQ role list result handler fired, id: %s.", id);
     } else {
         log_debug("IQ role list result handler fired.");
@@ -1182,7 +1257,8 @@ _room_role_list_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const s
     // handle error responses
     if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) {
         char *error_message = stanza_get_error_message(stanza);
-        handle_room_role_list_result_error(from, role, error_message);
+        log_debug("Error retrieving %s list for room %s: %s", role, from, error_message);
+        ui_handle_room_role_list_error(from, role, error_message);
         free(error_message);
         free(role);
         return 0;
@@ -1204,7 +1280,7 @@ _room_role_list_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const s
         }
     }
 
-    handle_room_role_list(from, role, nicks);
+    ui_handle_room_role_list(from, role, nicks);
     free(role);
     g_slist_free(nicks);
 
@@ -1219,7 +1295,7 @@ _room_config_submit_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stan
     const char *type = xmpp_stanza_get_type(stanza);
     const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
 
-    if (id != NULL) {
+    if (id) {
         log_debug("IQ room config submit handler fired, id: %s.", id);
     } else {
         log_debug("IQ room config submit handler fired.");
@@ -1228,12 +1304,12 @@ _room_config_submit_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stan
     // handle error responses
     if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) {
         char *error_message = stanza_get_error_message(stanza);
-        handle_room_config_submit_result_error(from, error_message);
+        ui_handle_room_config_submit_result_error(from, error_message);
         free(error_message);
         return 0;
     }
 
-    handle_room_config_submit_result(from);
+    ui_handle_room_config_submit_result(from);
 
     return 0;
 }
@@ -1246,7 +1322,7 @@ _room_kick_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza
     const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
     char *nick = (char *)userdata;
 
-    if (id != NULL) {
+    if (id) {
         log_debug("IQ kick result handler fired, id: %s.", id);
     } else {
         log_debug("IQ kick result handler fired.");
@@ -1255,7 +1331,7 @@ _room_kick_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza
     // handle error responses
     if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) {
         char *error_message = stanza_get_error_message(stanza);
-        handle_room_kick_result_error(from, nick, error_message);
+        ui_handle_room_kick_error(from, nick, error_message);
         free(error_message);
         free(nick);
         return 0;
@@ -1269,7 +1345,7 @@ _room_kick_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza
 static void
 _identity_destroy(DiscoIdentity *identity)
 {
-    if (identity != NULL) {
+    if (identity) {
         free(identity->name);
         free(identity->type);
         free(identity->category);
@@ -1280,7 +1356,7 @@ _identity_destroy(DiscoIdentity *identity)
 static void
 _item_destroy(DiscoItem *item)
 {
-    if (item != NULL) {
+    if (item) {
         free(item->jid);
         free(item->name);
         free(item);
@@ -1288,32 +1364,104 @@ _item_destroy(DiscoItem *item)
 }
 
 static int
+_room_info_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
+    void * const userdata)
+{
+    const char *type = xmpp_stanza_get_type(stanza);
+    ProfRoomInfoData *cb_data = (ProfRoomInfoData *)userdata;
+    log_info("Received diso#info response for room: %s", cb_data->room);
+
+    // handle error responses
+    if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) {
+        if (cb_data->display) {
+            char *error_message = stanza_get_error_message(stanza);
+            ui_handle_room_info_error(cb_data->room, error_message);
+            free(error_message);
+        }
+        free(cb_data->room);
+        free(cb_data);
+        return 0;
+    }
+
+    xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY);
+
+    if (query) {
+        xmpp_stanza_t *child = xmpp_stanza_get_children(query);
+        GSList *identities = NULL;
+        GSList *features = NULL;
+        while (child) {
+            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) {
+                    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 || category || type) {
+                    DiscoIdentity *identity = malloc(sizeof(struct disco_identity_t));
+
+                    if (name) {
+                        identity->name = strdup(name);
+                    } else {
+                        identity->name = NULL;
+                    }
+                    if (category) {
+                        identity->category = strdup(category);
+                    } else {
+                        identity->category = NULL;
+                    }
+                    if (type) {
+                        identity->type = strdup(type);
+                    } else {
+                        identity->type = NULL;
+                    }
+
+                    identities = g_slist_append(identities, identity);
+                }
+            }
+
+            child = xmpp_stanza_get_next(child);
+        }
+
+        muc_set_features(cb_data->room, features);
+        if (cb_data->display) {
+            ui_show_room_disco_info(cb_data->room, identities, features);
+        }
+
+        g_slist_free_full(features, free);
+        g_slist_free_full(identities, (GDestroyNotify)_identity_destroy);
+    }
+
+    free(cb_data->room);
+    free(cb_data);
+
+    return 0;
+}
+
+static int
 _disco_info_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     void * const userdata)
 {
     const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
     const char *type = xmpp_stanza_get_type(stanza);
 
-    char *room = NULL;
-    if (userdata) {
-        room = (char *) userdata;
-        log_info("Received diso#info response for room: %s", room);
+    if (from) {
+        log_info("Received diso#info response from: %s", from);
     } else {
-        room = NULL;
-        if (from) {
-            log_info("Received diso#info response from: %s", from);
-        } else {
-            log_info("Received diso#info response");
-        }
+        log_info("Received diso#info response");
     }
 
     // handle error responses
     if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) {
         char *error_message = stanza_get_error_message(stanza);
-        if (room) {
-            handle_room_info_error(room, error_message);
+        if (from) {
+            cons_show_error("Service discovery failed for %s: %s", from, error_message);
         } else {
-            handle_disco_info_error(from, error_message);
+            cons_show_error("Service discovery failed: %s", error_message);
         }
         free(error_message);
         return 0;
@@ -1321,15 +1469,15 @@ _disco_info_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const sta
 
     xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY);
 
-    if (query != NULL) {
+    if (query) {
         xmpp_stanza_t *child = xmpp_stanza_get_children(query);
         GSList *identities = NULL;
         GSList *features = NULL;
-        while (child != NULL) {
+        while (child) {
             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) {
+                if (var) {
                     features = g_slist_append(features, strdup(var));
                 }
             } else if (g_strcmp0(stanza_name, STANZA_NAME_IDENTITY) == 0) {
@@ -1337,20 +1485,20 @@ _disco_info_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const sta
                 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)) {
+                if (name || category || type) {
                     DiscoIdentity *identity = malloc(sizeof(struct disco_identity_t));
 
-                    if (name != NULL) {
+                    if (name) {
                         identity->name = strdup(name);
                     } else {
                         identity->name = NULL;
                     }
-                    if (category != NULL) {
+                    if (category) {
                         identity->category = strdup(category);
                     } else {
                         identity->category = NULL;
                     }
-                    if (type != NULL) {
+                    if (type) {
                         identity->type = strdup(type);
                     } else {
                         identity->type = NULL;
@@ -1363,16 +1511,13 @@ _disco_info_response_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const sta
             child = xmpp_stanza_get_next(child);
         }
 
-        if (room) {
-            handle_room_disco_info(room, identities, features);
-        } else {
-            handle_disco_info(from, identities, features);
-        }
+        cons_show_disco_info(from, identities, features);
 
         g_slist_free_full(features, free);
         g_slist_free_full(identities, (GDestroyNotify)_identity_destroy);
     }
-    return 1;
+
+    return 0;
 }
 
 static int
@@ -1389,17 +1534,17 @@ _disco_items_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stan
         log_debug("Response to query: %s", id);
         xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY);
 
-        if (query != NULL) {
+        if (query) {
             xmpp_stanza_t *child = xmpp_stanza_get_children(query);
-            while (child != NULL) {
+            while (child) {
                 const char *stanza_name = xmpp_stanza_get_name(child);
-                if ((stanza_name != NULL) && (g_strcmp0(stanza_name, STANZA_NAME_ITEM) == 0)) {
+                if (stanza_name && (g_strcmp0(stanza_name, STANZA_NAME_ITEM) == 0)) {
                     const char *item_jid = xmpp_stanza_get_attribute(child, STANZA_ATTR_JID);
-                    if (item_jid != NULL) {
+                    if (item_jid) {
                         DiscoItem *item = malloc(sizeof(struct disco_item_t));
                         item->jid = strdup(item_jid);
                         const char *item_name = xmpp_stanza_get_attribute(child, STANZA_ATTR_NAME);
-                        if (item_name != NULL) {
+                        if (item_name) {
                             item->name = strdup(item_name);
                         } else {
                             item->name = NULL;
@@ -1414,9 +1559,9 @@ _disco_items_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stan
     }
 
     if (g_strcmp0(id, "confreq") == 0) {
-        handle_room_list(items, from);
+        cons_show_room_list(items, from);
     } else if (g_strcmp0(id, "discoitemsreq") == 0) {
-        handle_disco_items(items, from);
+        cons_show_disco_items(items, from);
     }
 
     g_slist_free_full(items, (GDestroyNotify)_item_destroy);
diff --git a/src/xmpp/iq.h b/src/xmpp/iq.h
index 8c803ab5..d3a22fe4 100644
--- a/src/xmpp/iq.h
+++ b/src/xmpp/iq.h
@@ -1,7 +1,7 @@
 /*
  * iq.h
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/xmpp/message.c b/src/xmpp/message.c
index e96c1a74..bc702199 100644
--- a/src/xmpp/message.c
+++ b/src/xmpp/message.c
@@ -1,7 +1,7 @@
 /*
  * message.c
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -42,7 +42,8 @@
 #include "log.h"
 #include "muc.h"
 #include "profanity.h"
-#include "server_events.h"
+#include "ui/ui.h"
+#include "event/server_events.h"
 #include "xmpp/connection.h"
 #include "xmpp/message.h"
 #include "xmpp/roster.h"
@@ -52,18 +53,13 @@
 
 #define HANDLE(ns, type, func) xmpp_handler_add(conn, func, ns, STANZA_NAME_MESSAGE, type, ctx)
 
-static int _groupchat_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-static int _chat_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-static int _muc_user_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-static int _conference_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-static int _captcha_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
-static int _message_error_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
+static int _groupchat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _muc_user_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _conference_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _captcha_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _message_error_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
+static int _receipt_received_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata);
 
 void
 message_add_handlers(void)
@@ -77,34 +73,89 @@ message_add_handlers(void)
     HANDLE(STANZA_NS_MUC_USER,   NULL,                   _muc_user_handler);
     HANDLE(STANZA_NS_CONFERENCE, NULL,                   _conference_handler);
     HANDLE(STANZA_NS_CAPTCHA,    NULL,                   _captcha_handler);
+    HANDLE(STANZA_NS_RECEIPTS,   NULL,                   _receipt_received_handler);
 }
 
-void
+char *
 message_send_chat(const char * const barejid, const char * const msg)
 {
-    xmpp_stanza_t *message;
     xmpp_conn_t * const conn = connection_get_conn();
     xmpp_ctx_t * const ctx = connection_get_ctx();
 
     ChatSession *session = chat_session_get(barejid);
+    char *state = NULL;
+    char *jid = NULL;
+    if (session) {
+        if (prefs_get_boolean(PREF_STATES) && session->send_states) {
+            state = STANZA_NAME_ACTIVE;
+        }
+        Jid *jidp = jid_create_from_bare_and_resource(session->barejid, session->resource);
+        jid = strdup(jidp->fulljid);
+        jid_destroy(jidp);
+
+    } else {
+        if (prefs_get_boolean(PREF_STATES)) {
+            state = STANZA_NAME_ACTIVE;
+        }
+        jid = strdup(barejid);
+    }
+
+    char *id = create_unique_id("msg");
+    xmpp_stanza_t *message = stanza_create_message(ctx, id, jid, STANZA_TYPE_CHAT, msg);
+    free(jid);
+
+    if (state) {
+        stanza_attach_state(ctx, message, state);
+    }
+    if (prefs_get_boolean(PREF_RECEIPTS_REQUEST)) {
+        stanza_attach_receipt_request(ctx, message);
+    }
+
+    xmpp_send(conn, message);
+    xmpp_stanza_release(message);
+
+    return id;
+}
+
+char *
+message_send_chat_encrypted(const char * const barejid, const char * const msg)
+{
+    xmpp_conn_t * const conn = connection_get_conn();
+    xmpp_ctx_t * const ctx = connection_get_ctx();
+
+    ChatSession *session = chat_session_get(barejid);
+    char *state = NULL;
+    char *jid = NULL;
     if (session) {
-        char *state = NULL;
         if (prefs_get_boolean(PREF_STATES) && session->send_states) {
             state = STANZA_NAME_ACTIVE;
         }
         Jid *jidp = jid_create_from_bare_and_resource(session->barejid, session->resource);
-        message = stanza_create_message(ctx, jidp->fulljid, STANZA_TYPE_CHAT, msg, state);
+        jid = strdup(jidp->fulljid);
         jid_destroy(jidp);
     } else {
-        char *state = NULL;
         if (prefs_get_boolean(PREF_STATES)) {
             state = STANZA_NAME_ACTIVE;
         }
-        message = stanza_create_message(ctx, barejid, STANZA_TYPE_CHAT, msg, state);
+        jid = strdup(barejid);
+    }
+
+    char *id = create_unique_id("msg");
+    xmpp_stanza_t *message = stanza_create_message(ctx, id, barejid, STANZA_TYPE_CHAT, msg);
+    free(jid);
+
+    if (state) {
+        stanza_attach_state(ctx, message, state);
+    }
+    stanza_attach_carbons_private(ctx, message);
+    if (prefs_get_boolean(PREF_RECEIPTS_REQUEST)) {
+        stanza_attach_receipt_request(ctx, message);
     }
 
     xmpp_send(conn, message);
     xmpp_stanza_release(message);
+
+    return id;
 }
 
 void
@@ -112,7 +163,9 @@ message_send_private(const char * const fulljid, const char * const msg)
 {
     xmpp_conn_t * const conn = connection_get_conn();
     xmpp_ctx_t * const ctx = connection_get_ctx();
-    xmpp_stanza_t *message = stanza_create_message(ctx, fulljid, STANZA_TYPE_CHAT, msg, NULL);
+    char *id = create_unique_id("prv");
+    xmpp_stanza_t *message = stanza_create_message(ctx, id, fulljid, STANZA_TYPE_CHAT, msg);
+    free(id);
 
     xmpp_send(conn, message);
     xmpp_stanza_release(message);
@@ -123,7 +176,9 @@ message_send_groupchat(const char * const roomjid, const char * const msg)
 {
     xmpp_conn_t * const conn = connection_get_conn();
     xmpp_ctx_t * const ctx = connection_get_ctx();
-    xmpp_stanza_t *message = stanza_create_message(ctx, roomjid, STANZA_TYPE_GROUPCHAT, msg, NULL);
+    char *id = create_unique_id("muc");
+    xmpp_stanza_t *message = stanza_create_message(ctx, id, roomjid, STANZA_TYPE_GROUPCHAT, msg);
+    free(id);
 
     xmpp_send(conn, message);
     xmpp_stanza_release(message);
@@ -146,7 +201,18 @@ message_send_invite(const char * const roomjid, const char * const contact,
 {
     xmpp_conn_t * const conn = connection_get_conn();
     xmpp_ctx_t * const ctx = connection_get_ctx();
-    xmpp_stanza_t *stanza = stanza_create_invite(ctx, roomjid, contact, reason);
+    xmpp_stanza_t *stanza;
+
+
+    muc_member_type_t member_type = muc_member_type(roomjid);
+    if (member_type == MUC_MEMBER_TYPE_PUBLIC) {
+        log_debug("Sending direct invite to %s, for %s", contact, roomjid);
+        char *password = muc_password(roomjid);
+        stanza = stanza_create_invite(ctx, roomjid, contact, reason, password);
+    } else {
+        log_debug("Sending mediated invite to %s, for %s", contact, roomjid);
+        stanza = stanza_create_mediated_invite(ctx, roomjid, contact, reason);
+    }
 
     xmpp_send(conn, stanza);
     xmpp_stanza_release(stanza);
@@ -196,14 +262,13 @@ message_send_gone(const char * const jid)
 }
 
 static int
-_message_error_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
-    void * const userdata)
+_message_error_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
 {
     char *id = xmpp_stanza_get_id(stanza);
     char *jid = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
     xmpp_stanza_t *error_stanza = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_ERROR);
     char *type = NULL;
-    if (error_stanza != NULL) {
+    if (error_stanza) {
         type = xmpp_stanza_get_attribute(error_stanza, STANZA_ATTR_TYPE);
     }
 
@@ -211,15 +276,15 @@ _message_error_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     char *err_msg = stanza_get_error_message(stanza);
 
     GString *log_msg = g_string_new("message stanza error received");
-    if (id != NULL) {
+    if (id) {
         g_string_append(log_msg, " id=");
         g_string_append(log_msg, id);
     }
-    if (jid != NULL) {
+    if (jid) {
         g_string_append(log_msg, " from=");
         g_string_append(log_msg, jid);
     }
-    if (type != NULL) {
+    if (type) {
         g_string_append(log_msg, " type=");
         g_string_append(log_msg, type);
     }
@@ -230,7 +295,16 @@ _message_error_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
 
     g_string_free(log_msg, TRUE);
 
-    handle_message_error(jid, type, err_msg);
+    if (!jid) {
+        ui_handle_error(err_msg);
+    } else if (type && (strcmp(type, "cancel") == 0)) {
+        log_info("Recipient %s not found: %s", jid, err_msg);
+        Jid *jidp = jid_create(jid);
+        chat_session_remove(jidp->barejid);
+        jid_destroy(jidp);
+    } else {
+        ui_handle_recipient_error(jid, err_msg);
+    }
 
     free(err_msg);
 
@@ -238,113 +312,120 @@ _message_error_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
 }
 
 static int
-_muc_user_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
-    void * const userdata)
+_muc_user_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
 {
     xmpp_ctx_t *ctx = connection_get_ctx();
     xmpp_stanza_t *xns_muc_user = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_MUC_USER);
     char *room = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
 
-    if (room == NULL) {
+    if (!room) {
         log_warning("Message received with no from attribute, ignoring");
         return 1;
     }
 
     // XEP-0045
     xmpp_stanza_t *invite = xmpp_stanza_get_child_by_name(xns_muc_user, STANZA_NAME_INVITE);
-    if (invite != NULL) {
-        char *invitor_jid = xmpp_stanza_get_attribute(invite, STANZA_ATTR_FROM);
-        if (invitor_jid == NULL) {
-            log_warning("Chat room invite received with no from attribute");
-            return 1;
-        }
+    if (!invite) {
+        return 1;
+    }
 
-        Jid *jidp = jid_create(invitor_jid);
-        if (jidp == NULL) {
-            return 1;
-        }
-        char *invitor = jidp->barejid;
+    char *invitor_jid = xmpp_stanza_get_attribute(invite, STANZA_ATTR_FROM);
+    if (!invitor_jid) {
+        log_warning("Chat room invite received with no from attribute");
+        return 1;
+    }
 
-        char *reason = NULL;
-        xmpp_stanza_t *reason_st = xmpp_stanza_get_child_by_name(invite, STANZA_NAME_REASON);
-        if (reason_st != NULL) {
-            reason = xmpp_stanza_get_text(reason_st);
-        }
+    Jid *jidp = jid_create(invitor_jid);
+    if (!jidp) {
+        return 1;
+    }
+    char *invitor = jidp->barejid;
 
-        handle_room_invite(INVITE_MEDIATED, invitor, room, reason);
-        jid_destroy(jidp);
-        if (reason != NULL) {
-            xmpp_free(ctx, reason);
-        }
+    char *reason = NULL;
+    xmpp_stanza_t *reason_st = xmpp_stanza_get_child_by_name(invite, STANZA_NAME_REASON);
+    if (reason_st) {
+        reason = xmpp_stanza_get_text(reason_st);
+    }
+
+    char *password = NULL;
+    xmpp_stanza_t *password_st = xmpp_stanza_get_child_by_name(xns_muc_user, STANZA_NAME_PASSWORD);
+    if (password_st) {
+        password = xmpp_stanza_get_text(password_st);
+    }
+
+    sv_ev_room_invite(INVITE_MEDIATED, invitor, room, reason, password);
+    jid_destroy(jidp);
+    if (reason) {
+        xmpp_free(ctx, reason);
+    }
+    if (password) {
+        xmpp_free(ctx, password);
     }
 
     return 1;
 }
 
 static int
-_conference_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
-    void * const userdata)
+_conference_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
 {
     xmpp_stanza_t *xns_conference = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_CONFERENCE);
-    char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
-    char *room = NULL;
-    char *invitor = NULL;
-    char *reason = NULL;
 
-    if (from == NULL) {
+    char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
+    if (!from) {
         log_warning("Message received with no from attribute, ignoring");
         return 1;
     }
 
-    // XEP-0429
-    room = xmpp_stanza_get_attribute(xns_conference, STANZA_ATTR_JID);
-    if (room == NULL) {
+    Jid *jidp = jid_create(from);
+    if (!jidp) {
         return 1;
     }
 
-    Jid *jidp = jid_create(from);
-    if (jidp == NULL) {
+    // XEP-0249
+    char *room = xmpp_stanza_get_attribute(xns_conference, STANZA_ATTR_JID);
+    if (!room) {
         return 1;
     }
-    invitor = jidp->barejid;
 
-    reason = xmpp_stanza_get_attribute(xns_conference, STANZA_ATTR_REASON);
-
-    handle_room_invite(INVITE_DIRECT, invitor, room, reason);
+    char *reason = xmpp_stanza_get_attribute(xns_conference, STANZA_ATTR_REASON);
+    char *password = xmpp_stanza_get_attribute(xns_conference, STANZA_ATTR_PASSWORD);
 
+    sv_ev_room_invite(INVITE_DIRECT, jidp->barejid, room, reason, password);
     jid_destroy(jidp);
 
     return 1;
 }
 
 static int
-_captcha_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
-    void * const userdata)
+_captcha_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
 {
     xmpp_ctx_t *ctx = connection_get_ctx();
     char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
 
-    if (from == NULL) {
+    if (!from) {
         log_warning("Message received with no from attribute, ignoring");
         return 1;
     }
 
     // XEP-0158
     xmpp_stanza_t *body = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_BODY);
-    if (body != NULL) {
-        char *message = xmpp_stanza_get_text(body);
-        if (message != NULL) {
-            handle_room_broadcast(from, message);
-            xmpp_free(ctx, message);
-        }
+    if (!body) {
+        return 1;
     }
 
+    char *message = xmpp_stanza_get_text(body);
+    if (!message) {
+        return 1;
+    }
+
+    sv_ev_room_broadcast(from, message);
+    xmpp_free(ctx, message);
+
     return 1;
 }
 
 static int
-_groupchat_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata)
+_groupchat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
 {
     xmpp_ctx_t *ctx = connection_get_ctx();
     char *message = NULL;
@@ -353,9 +434,9 @@ _groupchat_handler(xmpp_conn_t * const conn,
 
     // handle room subject
     xmpp_stanza_t *subject = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_SUBJECT);
-    if (subject != NULL) {
+    if (subject) {
         message = xmpp_stanza_get_text(subject);
-        handle_room_subject(jid->barejid, jid->resourcepart, message);
+        sv_ev_room_subject(jid->barejid, jid->resourcepart, message);
         xmpp_free(ctx, message);
 
         jid_destroy(jid);
@@ -363,16 +444,22 @@ _groupchat_handler(xmpp_conn_t * const conn,
     }
 
     // handle room broadcasts
-    if (jid->resourcepart == NULL) {
+    if (!jid->resourcepart) {
         xmpp_stanza_t *body = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_BODY);
-        if (body != NULL) {
-            message = xmpp_stanza_get_text(body);
-            if (message != NULL) {
-                handle_room_broadcast(room_jid, message);
-                xmpp_free(ctx, message);
-            }
+        if (!body) {
+            jid_destroy(jid);
+            return 1;
         }
 
+        message = xmpp_stanza_get_text(body);
+        if (!message) {
+            jid_destroy(jid);
+            return 1;
+        }
+
+        sv_ev_room_broadcast(room_jid, message);
+        xmpp_free(ctx, message);
+
         jid_destroy(jid);
         return 1;
     }
@@ -390,32 +477,191 @@ _groupchat_handler(xmpp_conn_t * const conn,
         return 1;
     }
 
+    xmpp_stanza_t *body = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_BODY);
+
+    // check for and deal with message
+    if (!body) {
+        jid_destroy(jid);
+        return 1;
+    }
+
+    message = xmpp_stanza_get_text(body);
+    if (!message) {
+        jid_destroy(jid);
+        return 1;
+    }
+
     // determine if the notifications happened whilst offline
     GTimeVal tv_stamp;
     gboolean delayed = stanza_get_delay(stanza, &tv_stamp);
+    if (delayed) {
+        sv_ev_room_history(jid->barejid, jid->resourcepart, tv_stamp, message);
+    } else {
+        sv_ev_room_message(jid->barejid, jid->resourcepart, message);
+    }
+
+    xmpp_free(ctx, message);
+    jid_destroy(jid);
+
+    return 1;
+}
+
+void
+_message_send_receipt(const char * const fulljid, const char * const message_id)
+{
+    xmpp_conn_t * const conn = connection_get_conn();
+    xmpp_ctx_t * const ctx = connection_get_ctx();
+    xmpp_stanza_t *message = xmpp_stanza_new(ctx);
+    char *id = create_unique_id("receipt");
+    xmpp_stanza_set_name(message, STANZA_NAME_MESSAGE);
+    xmpp_stanza_set_id(message, id);
+    xmpp_stanza_set_attribute(message, STANZA_ATTR_TO, fulljid);
+
+    xmpp_stanza_t *receipt = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(receipt, "received");
+    xmpp_stanza_set_ns(receipt, STANZA_NS_RECEIPTS);
+    xmpp_stanza_set_attribute(receipt, STANZA_ATTR_ID, message_id);
+
+    xmpp_stanza_add_child(message, receipt);
+    xmpp_stanza_release(receipt);
+
+    xmpp_send(conn, message);
+    xmpp_stanza_release(message);
+}
+
+static int
+_receipt_received_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
+{
+    xmpp_stanza_t *receipt = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_RECEIPTS);
+    char *name = xmpp_stanza_get_name(receipt);
+    if (g_strcmp0(name, "received") != 0) {
+        return 1;
+    }
+
+    char *id = xmpp_stanza_get_attribute(receipt, STANZA_ATTR_ID);
+    if (!id) {
+        return 1;
+    }
+
+    char *fulljid = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
+    if (!fulljid) {
+        return 1;
+    }
+
+    Jid *jidp = jid_create(fulljid);
+    sv_ev_message_receipt(jidp->barejid, id);
+    jid_destroy(jidp);
+
+    return 1;
+}
+
+void
+_receipt_request_handler(xmpp_stanza_t * const stanza)
+{
+    if (!prefs_get_boolean(PREF_RECEIPTS_SEND)) {
+        return;
+    }
+
+    char *id = xmpp_stanza_get_id(stanza);
+    if (!id) {
+        return;
+    }
+
+    xmpp_stanza_t *receipts = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_RECEIPTS);
+    if (!receipts) {
+        return;
+    }
+
+    char *receipts_name = xmpp_stanza_get_name(receipts);
+    if (g_strcmp0(receipts_name, "request") != 0) {
+        return;
+    }
+
+    gchar *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
+    Jid *jid = jid_create(from);
+    _message_send_receipt(jid->fulljid, id);
+    jid_destroy(jid);
+}
+
+void
+_private_chat_handler(xmpp_stanza_t * const stanza, const char * const fulljid)
+{
     xmpp_stanza_t *body = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_BODY);
+    if (!body) {
+        return;
+    }
 
-    // check for and deal with message
-    if (body != NULL) {
-        message = xmpp_stanza_get_text(body);
-        if (message != NULL) {
-            if (delayed) {
-                handle_room_history(jid->barejid, jid->resourcepart, tv_stamp, message);
-            } else {
-                handle_room_message(jid->barejid, jid->resourcepart, message);
+    char *message = xmpp_stanza_get_text(body);
+    if (!message) {
+        return;
+    }
+
+    GTimeVal tv_stamp;
+    gboolean delayed = stanza_get_delay(stanza, &tv_stamp);
+    if (delayed) {
+        sv_ev_delayed_private_message(fulljid, message, tv_stamp);
+    } else {
+        sv_ev_incoming_private_message(fulljid, message);
+    }
+
+    xmpp_ctx_t *ctx = connection_get_ctx();
+    xmpp_free(ctx, message);
+}
+
+static gboolean
+_handle_carbons(xmpp_stanza_t * const stanza)
+{
+    xmpp_stanza_t *carbons = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_CARBONS);
+    if (!carbons) {
+        return FALSE;
+    }
+
+    char *name = xmpp_stanza_get_name(carbons);
+    if ((g_strcmp0(name, "received") == 0) || (g_strcmp0(name, "sent")) == 0) {
+        xmpp_stanza_t *forwarded = xmpp_stanza_get_child_by_ns(carbons, STANZA_NS_FORWARD);
+        xmpp_stanza_t *message = xmpp_stanza_get_child_by_name(forwarded, STANZA_NAME_MESSAGE);
+
+        xmpp_ctx_t *ctx = connection_get_ctx();
+
+        gchar *to = xmpp_stanza_get_attribute(message, STANZA_ATTR_TO);
+        gchar *from = xmpp_stanza_get_attribute(message, STANZA_ATTR_FROM);
+
+        // happens when receive a carbon of a self sent message
+        if (!to) to = from;
+
+        Jid *jid_from = jid_create(from);
+        Jid *jid_to = jid_create(to);
+        Jid *my_jid = jid_create(jabber_get_fulljid());
+
+        // check for and deal with message
+        xmpp_stanza_t *body = xmpp_stanza_get_child_by_name(message, STANZA_NAME_BODY);
+        if (body) {
+            char *message = xmpp_stanza_get_text(body);
+            if (message) {
+                // if we are the recipient, treat as standard incoming message
+                if(g_strcmp0(my_jid->barejid, jid_to->barejid) == 0){
+                    sv_ev_incoming_message(jid_from->barejid, jid_from->resourcepart, message);
+                }
+                // else treat as a sent message
+                else{
+                    sv_ev_carbon(jid_to->barejid, message);
+                }
+                xmpp_free(ctx, message);
             }
-            xmpp_free(ctx, message);
         }
-    }
 
-    jid_destroy(jid);
+        jid_destroy(jid_from);
+        jid_destroy(jid_to);
+        jid_destroy(my_jid);
 
-    return 1;
+        return TRUE;
+    }
+
+    return FALSE;
 }
 
 static int
-_chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
-    void * const userdata)
+_chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
 {
     // ignore if type not chat or absent
     char *type = xmpp_stanza_get_type(stanza);
@@ -423,6 +669,12 @@ _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
         return 1;
     }
 
+    // check if carbon message
+    gboolean res = _handle_carbons(stanza);
+    if (res) {
+        return 1;
+    }
+
     // ignore handled namespaces
     xmpp_stanza_t *conf = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_CONFERENCE);
     xmpp_stanza_t *mucuser = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_MUC_USER);
@@ -431,76 +683,57 @@ _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
         return 1;
     }
 
-    xmpp_ctx_t *ctx = connection_get_ctx();
     gchar *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
-
     Jid *jid = jid_create(from);
 
     // private message from chat room use full jid (room/nick)
     if (muc_active(jid->barejid)) {
-        // determine if the notifications happened whilst offline
-        GTimeVal tv_stamp;
-        gboolean delayed = stanza_get_delay(stanza, &tv_stamp);
-
-        // check for and deal with message
-        xmpp_stanza_t *body = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_BODY);
-        if (body != NULL) {
-            char *message = xmpp_stanza_get_text(body);
-            if (message != NULL) {
-                if (delayed) {
-                    handle_delayed_private_message(jid->str, message, tv_stamp);
-                } else {
-                    handle_incoming_private_message(jid->str, message);
-                }
-                xmpp_free(ctx, message);
-            }
-        }
-
-        jid_destroy(jid);
+        _private_chat_handler(stanza, jid->fulljid);
         return 1;
+    }
 
     // standard chat message, use jid without resource
-    } else {
-        // determine if the notifications happened whilst offline
-        GTimeVal tv_stamp;
-        gboolean delayed = stanza_get_delay(stanza, &tv_stamp);
-
-        // check for and deal with message
-        xmpp_stanza_t *body = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_BODY);
-        if (body != NULL) {
-            char *message = xmpp_stanza_get_text(body);
-            if (message != NULL) {
-                if (delayed) {
-                    handle_delayed_message(jid->barejid, message, tv_stamp);
-                } else {
-                    handle_incoming_message(jid->barejid, jid->resourcepart, message);
-                }
-                xmpp_free(ctx, message);
-            }
-        }
+    GTimeVal tv_stamp;
+    gboolean delayed = stanza_get_delay(stanza, &tv_stamp);
 
-        // handle chat sessions and states
-        if (!delayed && jid->resourcepart) {
-            gboolean gone = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_GONE) != NULL;
-            gboolean typing = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_COMPOSING) != NULL;
-            gboolean paused = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_PAUSED) != NULL;
-            gboolean inactive = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_INACTIVE) != NULL;
-            if (gone) {
-                handle_gone(jid->barejid, jid->resourcepart);
-            } else if (typing) {
-                handle_typing(jid->barejid, jid->resourcepart);
-            } else if (paused) {
-                handle_paused(jid->barejid, jid->resourcepart);
-            } else if (inactive) {
-                handle_inactive(jid->barejid, jid->resourcepart);
-            } else if (stanza_contains_chat_state(stanza)) {
-                handle_activity(jid->barejid, jid->resourcepart, TRUE);
+    xmpp_stanza_t *body = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_BODY);
+    if (body) {
+        char *message = xmpp_stanza_get_text(body);
+        if (message) {
+            if (delayed) {
+                sv_ev_delayed_message(jid->barejid, message, tv_stamp);
             } else {
-                handle_activity(jid->barejid, jid->resourcepart, FALSE);
+                sv_ev_incoming_message(jid->barejid, jid->resourcepart, message);
             }
+
+            _receipt_request_handler(stanza);
+
+            xmpp_ctx_t *ctx = connection_get_ctx();
+            xmpp_free(ctx, message);
         }
+    }
 
-        jid_destroy(jid);
-        return 1;
+    // handle chat sessions and states
+    if (!delayed && jid->resourcepart) {
+        gboolean gone = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_GONE) != NULL;
+        gboolean typing = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_COMPOSING) != NULL;
+        gboolean paused = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_PAUSED) != NULL;
+        gboolean inactive = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_INACTIVE) != NULL;
+        if (gone) {
+            sv_ev_gone(jid->barejid, jid->resourcepart);
+        } else if (typing) {
+            sv_ev_typing(jid->barejid, jid->resourcepart);
+        } else if (paused) {
+            sv_ev_paused(jid->barejid, jid->resourcepart);
+        } else if (inactive) {
+            sv_ev_inactive(jid->barejid, jid->resourcepart);
+        } else if (stanza_contains_chat_state(stanza)) {
+            sv_ev_activity(jid->barejid, jid->resourcepart, TRUE);
+        } else {
+            sv_ev_activity(jid->barejid, jid->resourcepart, FALSE);
+        }
     }
-}
\ No newline at end of file
+
+    jid_destroy(jid);
+    return 1;
+}
diff --git a/src/xmpp/message.h b/src/xmpp/message.h
index b3410dc9..6fbc27bd 100644
--- a/src/xmpp/message.h
+++ b/src/xmpp/message.h
@@ -1,7 +1,7 @@
 /*
  * message.h
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/xmpp/presence.c b/src/xmpp/presence.c
index 65384a0d..e46730e3 100644
--- a/src/xmpp/presence.c
+++ b/src/xmpp/presence.c
@@ -1,7 +1,7 @@
 /*
  * presence.c
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -44,7 +44,8 @@
 #include "log.h"
 #include "muc.h"
 #include "profanity.h"
-#include "server_events.h"
+#include "ui/ui.h"
+#include "event/server_events.h"
 #include "xmpp/capabilities.h"
 #include "xmpp/connection.h"
 #include "xmpp/stanza.h"
@@ -170,7 +171,7 @@ presence_sub_request_exists(const char * const bare_jid)
     GSList *requests_p = autocomplete_create_list(sub_requests_ac);
     GSList *requests = requests_p;
 
-    while (requests != NULL) {
+    while (requests) {
         if (strcmp(requests->data, bare_jid) == 0) {
             result = TRUE;
             break;
@@ -178,7 +179,7 @@ presence_sub_request_exists(const char * const bare_jid)
         requests = g_slist_next(requests);
     }
 
-    if (requests_p != NULL) {
+    if (requests_p) {
         g_slist_free_full(requests_p, free);
     }
 
@@ -192,27 +193,22 @@ presence_reset_sub_request_search(void)
 }
 
 void
-presence_update(const resource_presence_t presence_type, const char * const msg,
-    const int idle)
+presence_send(const resource_presence_t presence_type, const char * const msg, const int idle)
 {
     if (jabber_get_connection_status() != JABBER_CONNECTED) {
         log_warning("Error setting presence, not connected.");
         return;
     }
 
-    if (msg != NULL) {
-        log_debug("Updating presence: %s, \"%s\"",
-            string_from_resource_presence(presence_type), msg);
+    if (msg) {
+        log_debug("Updating presence: %s, \"%s\"", string_from_resource_presence(presence_type), msg);
     } else {
-        log_debug("Updating presence: %s",
-            string_from_resource_presence(presence_type));
+        log_debug("Updating presence: %s", string_from_resource_presence(presence_type));
     }
 
     xmpp_ctx_t * const ctx = connection_get_ctx();
     xmpp_conn_t * const conn = connection_get_conn();
-    const int pri =
-        accounts_get_priority_for_presence_type(jabber_get_account_name(),
-                                                presence_type);
+    const int pri = accounts_get_priority_for_presence_type(jabber_get_account_name(), presence_type);
     const char *show = stanza_get_presence_string_from_type(presence_type);
 
     connection_set_presence_message(msg);
@@ -245,11 +241,11 @@ _send_room_presence(xmpp_conn_t *conn, xmpp_stanza_t *presence)
     GList *rooms_p = muc_rooms();
     GList *rooms = rooms_p;
 
-    while (rooms != NULL) {
+    while (rooms) {
         const char *room = rooms->data;
         const char *nick = muc_nick(room);
 
-        if (nick != NULL) {
+        if (nick) {
             char *full_room_jid = create_fulljid(room, nick);
 
             xmpp_stanza_set_attribute(presence, STANZA_ATTR_TO, full_room_jid);
@@ -261,7 +257,7 @@ _send_room_presence(xmpp_conn_t *conn, xmpp_stanza_t *presence)
         rooms = g_list_next(rooms);
     }
 
-    if (rooms_p != NULL) {
+    if (rooms_p) {
         g_list_free(rooms_p);
     }
 }
@@ -333,7 +329,7 @@ presence_leave_chat_room(const char * const room_jid)
     xmpp_conn_t *conn = connection_get_conn();
     char *nick = muc_nick(room_jid);
 
-    if (nick != NULL) {
+    if (nick) {
         xmpp_stanza_t *presence = stanza_create_room_leave_presence(ctx, room_jid,
             nick);
         xmpp_send(conn, presence);
@@ -350,11 +346,11 @@ _presence_error_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     xmpp_stanza_t *error_stanza = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_ERROR);
     xmpp_stanza_t *x = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_X);
     char *xmlns = NULL;
-    if (x != NULL) {
+    if (x) {
         xmlns = xmpp_stanza_get_ns(x);
     }
     char *type = NULL;
-    if (error_stanza != NULL) {
+    if (error_stanza) {
         type = xmpp_stanza_get_attribute(error_stanza, STANZA_ATTR_TYPE);
     }
 
@@ -364,7 +360,7 @@ _presence_error_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
 
         char *error_cond = NULL;
         xmpp_stanza_t *reason_st = xmpp_stanza_get_child_by_ns(error_stanza, STANZA_NS_STANZAS);
-        if (reason_st != NULL) {
+        if (reason_st) {
             error_cond = xmpp_stanza_get_name(reason_st);
         }
         if (error_cond == NULL) {
@@ -372,7 +368,10 @@ _presence_error_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
         }
 
         log_info("Error joining room: %s, reason: %s", fulljid->barejid, error_cond);
-        handle_room_join_error(fulljid->barejid, error_cond);
+        if (muc_active(fulljid->barejid)) {
+            muc_leave(fulljid->barejid);
+        }
+        ui_handle_room_join_error(fulljid->barejid, error_cond);
         jid_destroy(fulljid);
         return 1;
     }
@@ -381,15 +380,15 @@ _presence_error_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     char *err_msg = stanza_get_error_message(stanza);
 
     GString *log_msg = g_string_new("presence stanza error received");
-    if (id != NULL) {
+    if (id) {
         g_string_append(log_msg, " id=");
         g_string_append(log_msg, id);
     }
-    if (from != NULL) {
+    if (from) {
         g_string_append(log_msg, " from=");
         g_string_append(log_msg, from);
     }
-    if (type != NULL) {
+    if (type) {
         g_string_append(log_msg, " type=");
         g_string_append(log_msg, type);
     }
@@ -400,7 +399,11 @@ _presence_error_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
 
     g_string_free(log_msg, TRUE);
 
-    handle_presence_error(from, type, err_msg);
+    if (from) {
+        ui_handle_recipient_error(from, err_msg);
+    } else {
+        ui_handle_error(err_msg);
+    }
 
     free(err_msg);
 
@@ -416,7 +419,7 @@ _unsubscribed_handler(xmpp_conn_t * const conn,
     Jid *from_jid = jid_create(from);
     log_debug("Unsubscribed presence handler fired for %s", from);
 
-    handle_subscription(from_jid->barejid, PRESENCE_UNSUBSCRIBED);
+    sv_ev_subscription(from_jid->barejid, PRESENCE_UNSUBSCRIBED);
     autocomplete_remove(sub_requests_ac, from_jid->barejid);
 
     jid_destroy(from_jid);
@@ -432,7 +435,7 @@ _subscribed_handler(xmpp_conn_t * const conn,
     Jid *from_jid = jid_create(from);
     log_debug("Subscribed presence handler fired for %s", from);
 
-    handle_subscription(from_jid->barejid, PRESENCE_SUBSCRIBED);
+    sv_ev_subscription(from_jid->barejid, PRESENCE_SUBSCRIBED);
     autocomplete_remove(sub_requests_ac, from_jid->barejid);
 
     jid_destroy(from_jid);
@@ -452,7 +455,7 @@ _subscribe_handler(xmpp_conn_t * const conn,
         return 1;
     }
 
-    handle_subscription(from_jid->barejid, PRESENCE_SUBSCRIBE);
+    sv_ev_subscription(from_jid->barejid, PRESENCE_SUBSCRIBE);
     autocomplete_add(sub_requests_ac, from_jid->barejid);
 
     jid_destroy(from_jid);
@@ -464,6 +467,8 @@ static int
 _unavailable_handler(xmpp_conn_t * const conn,
     xmpp_stanza_t * const stanza, void * const userdata)
 {
+    ui_input_nonblocking(TRUE);
+
     const char *jid = xmpp_conn_get_jid(conn);
     char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
     log_debug("Unavailable presence handler fired for %s", from);
@@ -479,15 +484,15 @@ _unavailable_handler(xmpp_conn_t * const conn,
     char *status_str = stanza_get_status(stanza, NULL);
 
     if (strcmp(my_jid->barejid, from_jid->barejid) !=0) {
-        if (from_jid->resourcepart != NULL) {
-            handle_contact_offline(from_jid->barejid, from_jid->resourcepart, status_str);
+        if (from_jid->resourcepart) {
+            sv_ev_contact_offline(from_jid->barejid, from_jid->resourcepart, status_str);
 
         // hack for servers that do not send full jid with unavailable presence
         } else {
-            handle_contact_offline(from_jid->barejid, "__prof_default", status_str);
+            sv_ev_contact_offline(from_jid->barejid, "__prof_default", status_str);
         }
     } else {
-        if (from_jid->resourcepart != NULL) {
+        if (from_jid->resourcepart) {
             connection_remove_available_resource(from_jid->resourcepart);
         }
     }
@@ -517,7 +522,7 @@ _handle_caps(char *jid, XMPPCaps *caps)
             }
         }
 
-    // unsupported hash, xep-0115, assoiciate with JID, no cache
+    // unsupported hash, xep-0115, associate with JID, no cache
     } else if (caps->hash) {
         log_info("Hash %s not supported: %s, sending service discovery request", caps->hash, jid);
         char *id = create_unique_id("caps");
@@ -539,6 +544,8 @@ static int
 _available_handler(xmpp_conn_t * const conn,
     xmpp_stanza_t * const stanza, void * const userdata)
 {
+    ui_input_nonblocking(TRUE);
+
     // handler still fires if error
     if (g_strcmp0(xmpp_stanza_get_type(stanza), STANZA_TYPE_ERROR) == 0) {
         return 1;
@@ -596,7 +603,7 @@ _available_handler(xmpp_conn_t * const conn,
     if (g_strcmp0(xmpp_presence->jid->barejid, my_jid->barejid) == 0) {
         connection_add_available_resource(resource);
     } else {
-        handle_contact_online(xmpp_presence->jid->barejid, resource, xmpp_presence->last_activity);
+        sv_ev_contact_online(xmpp_presence->jid->barejid, resource, xmpp_presence->last_activity);
     }
 
     jid_destroy(my_jid);
@@ -611,7 +618,7 @@ _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) {
+    if (node) {
         log_debug("Node string: %s.", node);
         if (!caps_contains(caps_key)) {
             log_debug("Capabilities not cached for '%s', sending discovery IQ.", from);
@@ -629,6 +636,8 @@ _send_caps_request(char *node, char *caps_key, char *id, char *from)
 static int
 _muc_user_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
 {
+    ui_input_nonblocking(TRUE);
+
     char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
     char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
 
@@ -682,27 +691,27 @@ _muc_user_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void *
                     char *new_jid = stanza_get_muc_destroy_alternative_room(stanza);
                     char *password = stanza_get_muc_destroy_alternative_password(stanza);
                     char *reason = stanza_get_muc_destroy_reason(stanza);
-                    handle_room_destroyed(room, new_jid, password, reason);
+                    sv_ev_room_destroyed(room, new_jid, password, reason);
                     free(password);
                     free(reason);
 
                 // kicked from room
-                } else if (g_slist_find_custom(status_codes, "307", (GCompareFunc)g_strcmp0) != NULL) {
+                } else if (g_slist_find_custom(status_codes, "307", (GCompareFunc)g_strcmp0)) {
                     char *actor = stanza_get_actor(stanza);
                     char *reason = stanza_get_reason(stanza);
-                    handle_room_kicked(room, actor, reason);
+                    sv_ev_room_kicked(room, actor, reason);
                     free(reason);
 
                 // banned from room
-                } else if (g_slist_find_custom(status_codes, "301", (GCompareFunc)g_strcmp0) != NULL) {
+                } else if (g_slist_find_custom(status_codes, "301", (GCompareFunc)g_strcmp0)) {
                     char *actor = stanza_get_actor(stanza);
                     char *reason = stanza_get_reason(stanza);
-                    handle_room_banned(room, actor, reason);
+                    sv_ev_room_banned(room, actor, reason);
                     free(reason);
 
                 // normal exit
                 } else {
-                    handle_leave_room(room);
+                    sv_ev_leave_room(room);
                 }
 
                 g_slist_free_full(status_codes, free);
@@ -713,7 +722,7 @@ _muc_user_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void *
             gboolean config_required = stanza_muc_requires_config(stanza);
             char *actor = stanza_get_actor(stanza);
             char *reason = stanza_get_reason(stanza);
-            handle_muc_self_online(room, nick, config_required, role, affiliation, actor, reason, jid, show_str, status_str);
+            sv_ev_muc_self_online(room, nick, config_required, role, affiliation, actor, reason, jid, show_str, status_str);
         }
 
     // handle presence from room members
@@ -732,22 +741,22 @@ _muc_user_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void *
                 GSList *status_codes = stanza_get_status_codes_by_ns(stanza, STANZA_NS_MUC_USER);
 
                 // kicked from room
-                if (g_slist_find_custom(status_codes, "307", (GCompareFunc)g_strcmp0) != NULL) {
+                if (g_slist_find_custom(status_codes, "307", (GCompareFunc)g_strcmp0)) {
                     char *actor = stanza_get_actor(stanza);
                     char *reason = stanza_get_reason(stanza);
-                    handle_room_occupent_kicked(room, nick, actor, reason);
+                    sv_ev_room_occupent_kicked(room, nick, actor, reason);
                     free(reason);
 
                 // banned from room
-                } else if (g_slist_find_custom(status_codes, "301", (GCompareFunc)g_strcmp0) != NULL) {
+                } else if (g_slist_find_custom(status_codes, "301", (GCompareFunc)g_strcmp0)) {
                     char *actor = stanza_get_actor(stanza);
                     char *reason = stanza_get_reason(stanza);
-                    handle_room_occupent_banned(room, nick, actor, reason);
+                    sv_ev_room_occupent_banned(room, nick, actor, reason);
                     free(reason);
 
                 // normal exit
                 } else {
-                    handle_room_occupant_offline(room, nick, "offline", status_str);
+                    sv_ev_room_occupant_offline(room, nick, "offline", status_str);
                 }
 
                 g_slist_free_full(status_codes, free);
@@ -765,7 +774,7 @@ _muc_user_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void *
 
             char *actor = stanza_get_actor(stanza);
             char *reason = stanza_get_reason(stanza);
-            handle_muc_occupant_online(room, nick, jid, role, affiliation, actor, reason, show_str, status_str);
+            sv_ev_muc_occupant_online(room, nick, jid, role, affiliation, actor, reason, show_str, status_str);
         }
     }
 
diff --git a/src/xmpp/presence.h b/src/xmpp/presence.h
index e704aea0..90b83473 100644
--- a/src/xmpp/presence.h
+++ b/src/xmpp/presence.h
@@ -1,7 +1,7 @@
 /*
  * presence.h
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/xmpp/roster.c b/src/xmpp/roster.c
index 3449c8d5..5c9fa5d4 100644
--- a/src/xmpp/roster.c
+++ b/src/xmpp/roster.c
@@ -1,7 +1,7 @@
 /*
  * roster.c
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -41,7 +41,9 @@
 
 #include "log.h"
 #include "profanity.h"
-#include "server_events.h"
+#include "ui/ui.h"
+#include "event/server_events.h"
+#include "event/client_events.h"
 #include "tools/autocomplete.h"
 #include "xmpp/connection.h"
 #include "xmpp/roster.h"
@@ -131,7 +133,7 @@ roster_send_add_to_group(const char * const group, PContact contact)
 {
     GSList *groups = p_contact_groups(contact);
     GSList *new_groups = NULL;
-    while (groups != NULL) {
+    while (groups) {
         new_groups = g_slist_append(new_groups, strdup(groups->data));
         groups = g_slist_next(groups);
     }
@@ -141,7 +143,7 @@ roster_send_add_to_group(const char * const group, PContact contact)
     char *unique_id = create_unique_id(NULL);
     GroupData *data = malloc(sizeof(GroupData));
     data->group = strdup(group);
-    if (p_contact_name(contact) != NULL) {
+    if (p_contact_name(contact)) {
         data->name = strdup(p_contact_name(contact));
     } else {
         data->name = strdup(p_contact_barejid(contact));
@@ -161,9 +163,9 @@ static int
 _group_add_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     void * const userdata)
 {
-    if (userdata != NULL) {
+    if (userdata) {
         GroupData *data = userdata;
-        handle_group_add(data->name, data->group);
+        ui_group_added(data->name, data->group);
         free(data->name);
         free(data->group);
         free(userdata);
@@ -176,7 +178,7 @@ roster_send_remove_from_group(const char * const group, PContact contact)
 {
     GSList *groups = p_contact_groups(contact);
     GSList *new_groups = NULL;
-    while (groups != NULL) {
+    while (groups) {
         if (strcmp(groups->data, group) != 0) {
             new_groups = g_slist_append(new_groups, strdup(groups->data));
         }
@@ -190,7 +192,7 @@ roster_send_remove_from_group(const char * const group, PContact contact)
     char *unique_id = create_unique_id(NULL);
     GroupData *data = malloc(sizeof(GroupData));
     data->group = strdup(group);
-    if (p_contact_name(contact) != NULL) {
+    if (p_contact_name(contact)) {
         data->name = strdup(p_contact_name(contact));
     } else {
         data->name = strdup(p_contact_barejid(contact));
@@ -208,9 +210,9 @@ static int
 _group_remove_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     void * const userdata)
 {
-    if (userdata != NULL) {
+    if (userdata) {
         GroupData *data = userdata;
-        handle_group_remove(data->name, data->group);
+        ui_group_removed(data->name, data->group);
         free(data->name);
         free(data->group);
         free(userdata);
@@ -234,13 +236,14 @@ _roster_set_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     // if from attribute exists and it is not current users barejid, ignore push
     Jid *my_jid = jid_create(jabber_get_fulljid());
     const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
-    if ((from != NULL) && (strcmp(from, my_jid->barejid) != 0)) {
+    if (from && (strcmp(from, my_jid->barejid) != 0)) {
         jid_destroy(my_jid);
         return 1;
     }
     jid_destroy(my_jid);
 
     const char *barejid = xmpp_stanza_get_attribute(item, STANZA_ATTR_JID);
+    gchar *barejid_lower = g_utf8_strdown(barejid, -1);
     const char *name = xmpp_stanza_get_attribute(item, STANZA_ATTR_NAME);
     const char *sub = xmpp_stanza_get_attribute(item, STANZA_ATTR_SUBSCRIPTION);
     const char *ask = xmpp_stanza_get_attribute(item, STANZA_ATTR_ASK);
@@ -254,83 +257,83 @@ _roster_set_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
     if (g_strcmp0(sub, "remove") == 0) {
         // remove barejid and name
         if (name == NULL) {
-            name = barejid;
+            name = barejid_lower;
         }
-
-        roster_remove(name, barejid);
-
-        handle_roster_remove(barejid);
+        roster_remove(name, barejid_lower);
+        ui_roster_remove(barejid_lower);
 
     // otherwise update local roster
     } else {
 
         // check for pending out subscriptions
         gboolean pending_out = FALSE;
-        if ((ask != NULL) && (strcmp(ask, "subscribe") == 0)) {
+        if (ask && (strcmp(ask, "subscribe") == 0)) {
             pending_out = TRUE;
         }
 
         GSList *groups = _get_groups_from_item(item);
 
         // update the local roster
-        PContact contact = roster_get_contact(barejid);
+        PContact contact = roster_get_contact(barejid_lower);
         if (contact == NULL) {
-            gboolean added = roster_add(barejid, name, groups, sub, pending_out);
+            gboolean added = roster_add(barejid_lower, name, groups, sub, pending_out);
             if (added) {
-                handle_roster_add(barejid, name);
+                ui_roster_add(barejid_lower, name);
             }
         } else {
-            handle_roster_update(barejid, name, groups, sub, pending_out);
+            sv_ev_roster_update(barejid_lower, name, groups, sub, pending_out);
         }
     }
 
+    g_free(barejid_lower);
+
     return 1;
 }
 
 static int
-_roster_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
-    void * const userdata)
+_roster_result_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);
 
+    if (g_strcmp0(id, "roster") != 0) {
+        return 1;
+    }
+
     // handle initial roster response
-    if (g_strcmp0(id, "roster") == 0) {
-        xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY);
-        xmpp_stanza_t *item = xmpp_stanza_get_children(query);
-
-        while (item != NULL) {
-            const char *barejid = 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);
-
-            // do not set nickname to empty string, set to NULL instead
-            if (name && (strlen(name) == 0)) {
-                name = NULL;
-            }
+    xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY);
+    xmpp_stanza_t *item = xmpp_stanza_get_children(query);
 
-            gboolean pending_out = FALSE;
-            const char *ask = xmpp_stanza_get_attribute(item, STANZA_ATTR_ASK);
-            if (g_strcmp0(ask, "subscribe") == 0) {
-                pending_out = TRUE;
-            }
+    while (item) {
+        const char *barejid = xmpp_stanza_get_attribute(item, STANZA_ATTR_JID);
+        gchar *barejid_lower = g_utf8_strdown(barejid, -1);
+        const char *name = xmpp_stanza_get_attribute(item, STANZA_ATTR_NAME);
+        const char *sub = xmpp_stanza_get_attribute(item, STANZA_ATTR_SUBSCRIPTION);
 
-            GSList *groups = _get_groups_from_item(item);
+        // do not set nickname to empty string, set to NULL instead
+        if (name && (strlen(name) == 0)) name = NULL;
 
-            gboolean added = roster_add(barejid, name, groups, sub, pending_out);
+        gboolean pending_out = FALSE;
+        const char *ask = xmpp_stanza_get_attribute(item, STANZA_ATTR_ASK);
+        if (g_strcmp0(ask, "subscribe") == 0) {
+            pending_out = TRUE;
+        }
 
-            if (!added) {
-                log_warning("Attempt to add contact twice: %s", barejid);
-            }
+        GSList *groups = _get_groups_from_item(item);
 
-            item = xmpp_stanza_get_next(item);
+        gboolean added = roster_add(barejid_lower, name, groups, sub, pending_out);
+        if (!added) {
+            log_warning("Attempt to add contact twice: %s", barejid_lower);
         }
 
-        handle_roster_received();
-
-        resource_presence_t conn_presence = accounts_get_login_presence(jabber_get_account_name());
-        presence_update(conn_presence, NULL, 0);
+        g_free(barejid_lower);
+        item = xmpp_stanza_get_next(item);
     }
 
+    sv_ev_roster_received();
+
+    resource_presence_t conn_presence = accounts_get_login_presence(jabber_get_account_name());
+    cl_ev_presence_send(conn_presence, NULL, 0);
+
     return 1;
 }
 
@@ -340,10 +343,10 @@ _get_groups_from_item(xmpp_stanza_t *item)
     GSList *groups = NULL;
     xmpp_stanza_t *group_element = xmpp_stanza_get_children(item);
 
-    while (group_element != NULL) {
+    while (group_element) {
         if (strcmp(xmpp_stanza_get_name(group_element), STANZA_NAME_GROUP) == 0) {
             char *groupname = xmpp_stanza_get_text(group_element);
-            if (groupname != NULL) {
+            if (groupname) {
                 groups = g_slist_append(groups, groupname);
             }
         }
diff --git a/src/xmpp/roster.h b/src/xmpp/roster.h
index c29a674a..68811a5c 100644
--- a/src/xmpp/roster.h
+++ b/src/xmpp/roster.h
@@ -1,7 +1,7 @@
 /*
  * roster.h
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c
index 4f1d412d..1f25239b 100644
--- a/src/xmpp/stanza.c
+++ b/src/xmpp/stanza.c
@@ -1,7 +1,7 @@
 /*
  * stanza.c
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -141,7 +141,7 @@ stanza_create_bookmarks_pubsub_add(xmpp_ctx_t *ctx, const char * const jid,
         xmpp_stanza_set_attribute(conference, STANZA_ATTR_AUTOJOIN, "false");
     }
 
-    if (nick != NULL) {
+    if (nick) {
         xmpp_stanza_t *nick_st = xmpp_stanza_new(ctx);
         xmpp_stanza_set_name(nick_st, STANZA_NAME_NICK);
         xmpp_stanza_set_text(nick_st, nick);
@@ -199,6 +199,44 @@ stanza_create_bookmarks_pubsub_add(xmpp_ctx_t *ctx, const char * const jid,
 #endif
 
 xmpp_stanza_t *
+stanza_enable_carbons(xmpp_ctx_t *ctx){
+    xmpp_stanza_t *iq = xmpp_stanza_new(ctx);
+    char *id = create_unique_id("carbons");
+
+    xmpp_stanza_set_name(iq, STANZA_NAME_IQ);
+    xmpp_stanza_set_type(iq, STANZA_TYPE_SET);
+    xmpp_stanza_set_id(iq, id);
+    free(id);
+
+    xmpp_stanza_t *carbons_enable = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(carbons_enable, STANZA_NAME_ENABLE);
+    xmpp_stanza_set_ns(carbons_enable, STANZA_NS_CARBONS);
+
+    xmpp_stanza_add_child(iq, carbons_enable);
+
+    return iq;
+}
+
+xmpp_stanza_t *
+stanza_disable_carbons(xmpp_ctx_t *ctx){
+    xmpp_stanza_t *iq = xmpp_stanza_new(ctx);
+    char *id = create_unique_id("carbons");
+
+    xmpp_stanza_set_name(iq, STANZA_NAME_IQ);
+    xmpp_stanza_set_type(iq, STANZA_TYPE_SET);
+    xmpp_stanza_set_id(iq, id);
+    free(id);
+
+    xmpp_stanza_t *carbons_disable = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(carbons_disable, STANZA_NAME_DISABLE);
+    xmpp_stanza_set_ns(carbons_disable, STANZA_NS_CARBONS);
+
+    xmpp_stanza_add_child(iq, carbons_disable);
+
+    return iq;
+}
+
+xmpp_stanza_t *
 stanza_create_chat_state(xmpp_ctx_t *ctx, const char * const fulljid, const char * const state)
 {
     xmpp_stanza_t *msg, *chat_state;
@@ -244,9 +282,44 @@ stanza_create_room_subject_message(xmpp_ctx_t *ctx, const char * const room, con
 }
 
 xmpp_stanza_t *
-stanza_create_message(xmpp_ctx_t *ctx, const char * const recipient,
-    const char * const type, const char * const message,
-    const char * const state)
+stanza_attach_state(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char * const state)
+{
+    xmpp_stanza_t *chat_state = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(chat_state, state);
+    xmpp_stanza_set_ns(chat_state, STANZA_NS_CHATSTATES);
+    xmpp_stanza_add_child(stanza, chat_state);
+    xmpp_stanza_release(chat_state);
+
+    return stanza;
+}
+
+xmpp_stanza_t *
+stanza_attach_carbons_private(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza)
+{
+    xmpp_stanza_t *private_carbon = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(private_carbon, "private");
+    xmpp_stanza_set_ns(private_carbon, STANZA_NS_CARBONS);
+    xmpp_stanza_add_child(stanza, private_carbon);
+    xmpp_stanza_release(private_carbon);
+
+    return stanza;
+}
+
+xmpp_stanza_t *
+stanza_attach_receipt_request(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza)
+{
+    xmpp_stanza_t *receipet_request = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(receipet_request, "request");
+    xmpp_stanza_set_ns(receipet_request, STANZA_NS_RECEIPTS);
+    xmpp_stanza_add_child(stanza, receipet_request);
+    xmpp_stanza_release(receipet_request);
+
+    return stanza;
+}
+
+xmpp_stanza_t *
+stanza_create_message(xmpp_ctx_t *ctx, char *id, const char * const recipient,
+    const char * const type, const char * const message)
 {
     xmpp_stanza_t *msg, *body, *text;
 
@@ -254,9 +327,7 @@ stanza_create_message(xmpp_ctx_t *ctx, const char * const recipient,
     xmpp_stanza_set_name(msg, STANZA_NAME_MESSAGE);
     xmpp_stanza_set_type(msg, type);
     xmpp_stanza_set_attribute(msg, STANZA_ATTR_TO, recipient);
-    char *id = create_unique_id(NULL);
     xmpp_stanza_set_id(msg, id);
-    free(id);
 
     body = xmpp_stanza_new(ctx);
     xmpp_stanza_set_name(body, STANZA_NAME_BODY);
@@ -268,14 +339,6 @@ stanza_create_message(xmpp_ctx_t *ctx, const char * const recipient,
     xmpp_stanza_add_child(msg, body);
     xmpp_stanza_release(body);
 
-    if (state != NULL) {
-        xmpp_stanza_t *chat_state = xmpp_stanza_new(ctx);
-        xmpp_stanza_set_name(chat_state, state);
-        xmpp_stanza_set_ns(chat_state, STANZA_NS_CHATSTATES);
-        xmpp_stanza_add_child(msg, chat_state);
-        xmpp_stanza_release(chat_state);
-    }
-
     return msg;
 }
 
@@ -311,7 +374,7 @@ stanza_create_roster_set(xmpp_ctx_t *ctx, const char * const id,
     xmpp_stanza_t *iq = xmpp_stanza_new(ctx);
     xmpp_stanza_set_name(iq, STANZA_NAME_IQ);
     xmpp_stanza_set_type(iq, STANZA_TYPE_SET);
-    if (id != NULL) {
+    if (id) {
         xmpp_stanza_set_id(iq, id);
     }
 
@@ -323,13 +386,13 @@ stanza_create_roster_set(xmpp_ctx_t *ctx, const char * const id,
     xmpp_stanza_set_name(item, STANZA_NAME_ITEM);
     xmpp_stanza_set_attribute(item, STANZA_ATTR_JID, jid);
 
-    if (handle != NULL) {
+    if (handle) {
         xmpp_stanza_set_attribute(item, STANZA_ATTR_NAME, handle);
     } else {
         xmpp_stanza_set_attribute(item, STANZA_ATTR_NAME, "");
     }
 
-    while (groups != NULL) {
+    while (groups) {
         xmpp_stanza_t *group = xmpp_stanza_new(ctx);
         xmpp_stanza_t *groupname = xmpp_stanza_new(ctx);
         xmpp_stanza_set_name(group, STANZA_NAME_GROUP);
@@ -351,7 +414,7 @@ stanza_create_roster_set(xmpp_ctx_t *ctx, const char * const id,
 
 xmpp_stanza_t *
 stanza_create_invite(xmpp_ctx_t *ctx, const char * const room,
-    const char * const contact, const char * const reason)
+    const char * const contact, const char * const reason, const char * const password)
 {
     xmpp_stanza_t *message, *x;
 
@@ -367,10 +430,53 @@ stanza_create_invite(xmpp_ctx_t *ctx, const char * const room,
     xmpp_stanza_set_ns(x, STANZA_NS_CONFERENCE);
 
     xmpp_stanza_set_attribute(x, STANZA_ATTR_JID, room);
-    if (reason != NULL) {
+    if (reason) {
         xmpp_stanza_set_attribute(x, STANZA_ATTR_REASON, reason);
     }
+    if (password) {
+        xmpp_stanza_set_attribute(x, STANZA_ATTR_PASSWORD, password);
+    }
+
+    xmpp_stanza_add_child(message, x);
+    xmpp_stanza_release(x);
+
+    return message;
+}
+
+xmpp_stanza_t *
+stanza_create_mediated_invite(xmpp_ctx_t *ctx, const char * const room,
+    const char * const contact, const char * const reason)
+{
+    xmpp_stanza_t *message, *x, *invite;
+
+    message = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(message, STANZA_NAME_MESSAGE);
+    xmpp_stanza_set_attribute(message, STANZA_ATTR_TO, room);
+    char *id = create_unique_id(NULL);
+    xmpp_stanza_set_id(message, id);
+    free(id);
+
+    x = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(x, STANZA_NAME_X);
+    xmpp_stanza_set_ns(x, STANZA_NS_MUC_USER);
+
+    invite = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(invite, STANZA_NAME_INVITE);
+    xmpp_stanza_set_attribute(invite, STANZA_ATTR_TO, contact);
+
+    if (reason) {
+        xmpp_stanza_t *reason_st = xmpp_stanza_new(ctx);
+        xmpp_stanza_set_name(reason_st, STANZA_NAME_REASON);
+        xmpp_stanza_t *reason_txt = xmpp_stanza_new(ctx);
+        xmpp_stanza_set_text(reason_txt, reason);
+        xmpp_stanza_add_child(reason_st, reason_txt);
+        xmpp_stanza_release(reason_txt);
+        xmpp_stanza_add_child(invite, reason_st);
+        xmpp_stanza_release(reason_st);
+    }
 
+    xmpp_stanza_add_child(x, invite);
+    xmpp_stanza_release(invite);
     xmpp_stanza_add_child(message, x);
     xmpp_stanza_release(x);
 
@@ -393,7 +499,7 @@ stanza_create_room_join_presence(xmpp_ctx_t * const ctx,
     xmpp_stanza_set_ns(x, STANZA_NS_MUC);
 
     // if a password was given
-    if (passwd != NULL) {
+    if (passwd) {
         xmpp_stanza_t *pass = xmpp_stanza_new(ctx);
         xmpp_stanza_set_name(pass, "password");
         xmpp_stanza_t *text = xmpp_stanza_new(ctx);
@@ -787,7 +893,7 @@ stanza_create_disco_info_iq(xmpp_ctx_t *ctx, const char * const id, const char *
     xmpp_stanza_t *query = xmpp_stanza_new(ctx);
     xmpp_stanza_set_name(query, STANZA_NAME_QUERY);
     xmpp_stanza_set_ns(query, XMPP_NS_DISCO_INFO);
-    if (node != NULL) {
+    if (node) {
         xmpp_stanza_set_attribute(query, STANZA_ATTR_NODE, node);
     }
 
@@ -858,7 +964,7 @@ stanza_create_ping_iq(xmpp_ctx_t *ctx, const char * const target)
     xmpp_stanza_t *iq = xmpp_stanza_new(ctx);
     xmpp_stanza_set_name(iq, STANZA_NAME_IQ);
     xmpp_stanza_set_type(iq, STANZA_TYPE_GET);
-    if (target != NULL) {
+    if (target) {
         xmpp_stanza_set_attribute(iq, STANZA_ATTR_TO, target);
     }
     char *id = create_unique_id("ping");
@@ -881,11 +987,11 @@ stanza_get_delay(xmpp_stanza_t * const stanza, GTimeVal *tv_stamp)
 {
     // first check for XEP-0203 delayed delivery
     xmpp_stanza_t *delay = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_DELAY);
-    if (delay != NULL) {
+    if (delay) {
         char *xmlns = xmpp_stanza_get_attribute(delay, STANZA_ATTR_XMLNS);
-        if ((xmlns != NULL) && (strcmp(xmlns, "urn:xmpp:delay") == 0)) {
+        if (xmlns && (strcmp(xmlns, "urn:xmpp:delay") == 0)) {
             char *stamp = xmpp_stanza_get_attribute(delay, STANZA_ATTR_STAMP);
-            if ((stamp != NULL) && (g_time_val_from_iso8601(stamp, tv_stamp))) {
+            if (stamp && (g_time_val_from_iso8601(stamp, tv_stamp))) {
                 return TRUE;
             }
         }
@@ -894,11 +1000,11 @@ stanza_get_delay(xmpp_stanza_t * const stanza, GTimeVal *tv_stamp)
     // otherwise check for XEP-0091 legacy delayed delivery
     // stanp format : CCYYMMDDThh:mm:ss
     xmpp_stanza_t *x = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_X);
-    if (x != NULL) {
+    if (x) {
         char *xmlns = xmpp_stanza_get_attribute(x, STANZA_ATTR_XMLNS);
-        if ((xmlns != NULL) && (strcmp(xmlns, "jabber:x:delay") == 0)) {
+        if (xmlns && (strcmp(xmlns, "jabber:x:delay") == 0)) {
             char *stamp = xmpp_stanza_get_attribute(x, STANZA_ATTR_STAMP);
-            if ((stamp != NULL) && (g_time_val_from_iso8601(stamp, tv_stamp))) {
+            if (stamp && (g_time_val_from_iso8601(stamp, tv_stamp))) {
                 return TRUE;
             }
         }
@@ -914,17 +1020,17 @@ stanza_get_status(xmpp_stanza_t *stanza, char *def)
     xmpp_stanza_t *status =
         xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_STATUS);
 
-    if (status != NULL) {
+    if (status) {
         // xmpp_free and free may be different functions so convert all to
         // libc malloc
         char *s1, *s2 = NULL;
         s1 = xmpp_stanza_get_text(status);
-        if (s1 != NULL) {
+        if (s1) {
             s2 = strdup(s1);
             xmpp_free(ctx, s1);
         }
         return s2;
-    } else if (def != NULL) {
+    } else if (def) {
         return strdup(def);
     } else {
         return NULL;
@@ -938,17 +1044,17 @@ stanza_get_show(xmpp_stanza_t *stanza, char *def)
     xmpp_stanza_t *show =
         xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_SHOW);
 
-    if (show != NULL) {
+    if (show) {
         // xmpp_free and free may be different functions so convert all to
         // libc malloc
         char *s1, *s2 = NULL;
         s1 = xmpp_stanza_get_text(show);
-        if (s1 != NULL) {
+        if (s1) {
             s2 = strdup(s1);
             xmpp_free(ctx, s1);
         }
         return s2;
-    } else if (def != NULL) {
+    } else if (def) {
         return strdup(def);
     } else {
         return NULL;
@@ -984,7 +1090,7 @@ stanza_muc_requires_config(xmpp_stanza_t * const stanza)
 
     // muc user namespaced x element
     xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_MUC_USER);
-    if (x != NULL) {
+    if (x) {
 
         // check for item element with owner affiliation
         xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(x, "item");
@@ -998,7 +1104,7 @@ stanza_muc_requires_config(xmpp_stanza_t * const stanza)
 
         // check for status code 201
         xmpp_stanza_t *x_children = xmpp_stanza_get_children(x);
-        while (x_children != NULL) {
+        while (x_children) {
             if (g_strcmp0(xmpp_stanza_get_name(x_children), STANZA_NAME_STATUS) == 0) {
                 char *code = xmpp_stanza_get_attribute(x_children, STANZA_ATTR_CODE);
                 if (g_strcmp0(code, "201") == 0) {
@@ -1240,11 +1346,11 @@ stanza_is_room_nick_change(xmpp_stanza_t * const stanza)
 
     // muc user namespaced x element
     xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_MUC_USER);
-    if (x != NULL) {
+    if (x) {
 
         // check for status child element with 303 code
         xmpp_stanza_t *x_children = xmpp_stanza_get_children(x);
-        while (x_children != NULL) {
+        while (x_children) {
             if (g_strcmp0(xmpp_stanza_get_name(x_children), STANZA_NAME_STATUS) == 0) {
                 char *code = xmpp_stanza_get_attribute(x_children, STANZA_ATTR_CODE);
                 if (g_strcmp0(code, "303") == 0) {
@@ -1268,7 +1374,7 @@ stanza_get_new_nick(xmpp_stanza_t * const stanza)
     xmpp_stanza_t *x = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_X);
     xmpp_stanza_t *x_children = xmpp_stanza_get_children(x);
 
-    while (x_children != NULL) {
+    while (x_children) {
         if (strcmp(xmpp_stanza_get_name(x_children), STANZA_NAME_ITEM) == 0) {
             char *nick = xmpp_stanza_get_attribute(x_children, STANZA_ATTR_NICK);
             if (nick) {
@@ -1382,9 +1488,9 @@ stanza_get_error_message(xmpp_stanza_t *stanza)
         xmpp_stanza_t *text_stanza = xmpp_stanza_get_child_by_name(error_stanza, STANZA_NAME_TEXT);
 
         // check for text
-        if (text_stanza != NULL) {
+        if (text_stanza) {
             gchar *err_msg = xmpp_stanza_get_text(text_stanza);
-            if (err_msg != NULL) {
+            if (err_msg) {
                 char *result =  strdup(err_msg);
                 xmpp_free(ctx, err_msg);
                 return result;
@@ -1419,7 +1525,7 @@ stanza_get_error_message(xmpp_stanza_t *stanza)
             int i;
             for (i = 0; i < ARRAY_SIZE(defined_conditions); i++) {
                 xmpp_stanza_t *cond_stanza = xmpp_stanza_get_child_by_name(error_stanza, defined_conditions[i]);
-                if (cond_stanza != NULL) {
+                if (cond_stanza) {
                     char *result = strdup(xmpp_stanza_get_name(cond_stanza));
                     return result;
                 }
@@ -1456,7 +1562,7 @@ void
 stanza_attach_show(xmpp_ctx_t * const ctx, xmpp_stanza_t * const presence,
     const char * const show)
 {
-    if (show != NULL) {
+    if (show) {
         xmpp_stanza_t *show_stanza = xmpp_stanza_new(ctx);
         xmpp_stanza_set_name(show_stanza, STANZA_NAME_SHOW);
         xmpp_stanza_t *text = xmpp_stanza_new(ctx);
@@ -1472,7 +1578,7 @@ void
 stanza_attach_status(xmpp_ctx_t * const ctx, xmpp_stanza_t * const presence,
     const char * const status)
 {
-    if (status != NULL) {
+    if (status) {
         xmpp_stanza_t *status_stanza = xmpp_stanza_new(ctx);
         xmpp_stanza_set_name(status_stanza, STANZA_NAME_STATUS);
         xmpp_stanza_t *text = xmpp_stanza_new(ctx);
@@ -1619,9 +1725,9 @@ stanza_parse_presence(xmpp_stanza_t *stanza, int *err)
 
     result->priority = 0;
     xmpp_stanza_t *priority_stanza = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_PRIORITY);
-    if (priority_stanza != NULL) {
+    if (priority_stanza) {
         char *priority_str = xmpp_stanza_get_text(priority_stanza);
-        if (priority_str != NULL) {
+        if (priority_str) {
             result->priority = atoi(priority_str);
         }
         free(priority_str);
diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h
index 84282401..89dbda57 100644
--- a/src/xmpp/stanza.h
+++ b/src/xmpp/stanza.h
@@ -1,7 +1,7 @@
 /*
  * stanza.h
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -77,6 +77,8 @@
 #define STANZA_NAME_VALUE "value"
 #define STANZA_NAME_DESTROY "destroy"
 #define STANZA_NAME_ACTOR "actor"
+#define STANZA_NAME_ENABLE "enable"
+#define STANZA_NAME_DISABLE "disable"
 
 // error conditions
 #define STANZA_NAME_BAD_REQUEST "bad-request"
@@ -133,6 +135,7 @@
 #define STANZA_ATTR_CATEGORY "category"
 #define STANZA_ATTR_REASON "reason"
 #define STANZA_ATTR_AUTOJOIN "autojoin"
+#define STANZA_ATTR_PASSWORD "password"
 
 #define STANZA_TEXT_AWAY "away"
 #define STANZA_TEXT_DND "dnd"
@@ -154,6 +157,9 @@
 #define STANZA_NS_CONFERENCE "jabber:x:conference"
 #define STANZA_NS_CAPTCHA "urn:xmpp:captcha"
 #define STANZA_NS_PUBSUB "http://jabber.org/protocol/pubsub"
+#define STANZA_NS_CARBONS "urn:xmpp:carbons:2"
+#define STANZA_NS_FORWARD "urn:xmpp:forward:0"
+#define STANZA_NS_RECEIPTS "urn:xmpp:receipts"
 
 #define STANZA_DATAFORM_SOFTWARE "urn:xmpp:dataforms:softwareinfo"
 
@@ -178,12 +184,19 @@ typedef enum {
 
 xmpp_stanza_t* stanza_create_bookmarks_storage_request(xmpp_ctx_t *ctx);
 
+xmpp_stanza_t * stanza_enable_carbons(xmpp_ctx_t *ctx);
+
+xmpp_stanza_t * stanza_disable_carbons(xmpp_ctx_t *ctx);
+
 xmpp_stanza_t* stanza_create_chat_state(xmpp_ctx_t *ctx,
     const char * const fulljid, const char * const state);
 
-xmpp_stanza_t* stanza_create_message(xmpp_ctx_t *ctx,
-    const char * const recipient, const char * const type,
-    const char * const message, const char * const state);
+xmpp_stanza_t * stanza_attach_state(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char * const state);
+xmpp_stanza_t * stanza_attach_carbons_private(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza);
+xmpp_stanza_t * stanza_attach_receipt_request(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza);
+
+xmpp_stanza_t* stanza_create_message(xmpp_ctx_t *ctx, char *id,
+    const char * const recipient, const char * const type, const char * const message);
 
 xmpp_stanza_t* stanza_create_room_join_presence(xmpp_ctx_t * const ctx,
     const char * const full_room_jid, const char * const passwd);
@@ -202,6 +215,8 @@ xmpp_stanza_t* stanza_create_disco_info_iq(xmpp_ctx_t *ctx, const char * const i
     const char * const to, const char * const node);
 
 xmpp_stanza_t* stanza_create_invite(xmpp_ctx_t *ctx, const char * const room,
+    const char * const contact, const char * const reason, const char * const password);
+xmpp_stanza_t* stanza_create_mediated_invite(xmpp_ctx_t *ctx, const char * const room,
     const char * const contact, const char * const reason);
 
 gboolean stanza_contains_chat_state(xmpp_stanza_t *stanza);
diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h
index a004a4bf..398c9f46 100644
--- a/src/xmpp/xmpp.h
+++ b/src/xmpp/xmpp.h
@@ -1,7 +1,7 @@
 /*
  * xmpp.h
  *
- * Copyright (C) 2012 - 2014 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2015 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -145,7 +145,8 @@ char* jabber_get_account_name(void);
 GList * jabber_get_available_resources(void);
 
 // message functions
-void message_send_chat(const char * const barejid, const char * const msg);
+char* message_send_chat(const char * const barejid, const char * const msg);
+char* message_send_chat_encrypted(const char * const barejid, const char * const msg);
 void message_send_private(const char * const fulljid, const char * const msg);
 void message_send_groupchat(const char * const roomjid, const char * const msg);
 void message_send_groupchat_subject(const char * const roomjid, const char * const subject);
@@ -167,11 +168,13 @@ char * presence_sub_request_find(const char * const search_str);
 void presence_join_room(char *room, char *nick, char * passwd);
 void presence_change_room_nick(const char * const room, const char * const nick);
 void presence_leave_chat_room(const char * const room_jid);
-void presence_update(resource_presence_t status, const char * const msg,
+void presence_send(resource_presence_t status, const char * const msg,
     int idle);
 gboolean presence_sub_request_exists(const char * const bare_jid);
 
 // iq functions
+void iq_enable_carbons();
+void iq_disable_carbons();
 void iq_send_software_version(const char * const fulljid);
 void iq_room_list_request(gchar *conferencejid);
 void iq_disco_info_request(gchar *jid);
@@ -189,7 +192,7 @@ 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_send_caps_request_legacy(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_info_request(const char * const room, gboolean display_result);
 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,
     const char * const reason);