about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorJames Booth <boothj5@gmail.com>2013-01-28 22:24:47 +0000
committerJames Booth <boothj5@gmail.com>2013-01-28 22:24:47 +0000
commit7255ef5e1c03aa09ebffc8636a0b49a76ff3f07a (patch)
treef2608b7828e95ca193dd5ee9218f8dd9c5940b2f
parentcb24ba8be175e0ddde93a0e01c286ee7cab2b241 (diff)
downloadprofani-tty-7255ef5e1c03aa09ebffc8636a0b49a76ff3f07a.tar.gz
Added xmpp_presence.c
-rw-r--r--Makefile.am2
-rw-r--r--src/command.c8
-rw-r--r--src/muc.h2
-rw-r--r--src/xmpp.h13
-rw-r--r--src/xmpp_conn.c301
-rw-r--r--src/xmpp_presence.c324
6 files changed, 360 insertions, 290 deletions
diff --git a/Makefile.am b/Makefile.am
index 846f9bd0..66e599f2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5,7 +5,7 @@ profanity_SOURCES = src/command.c src/contact.c src/command_history.c src/xmpp.h
 	src/autocomplete.h src/ui_titlebar.c src/ui_windows.c src/common.c \
 	src/contact_list.c src/ui_inputwin.c src/log.h src/profanity.c \
 	src/history.c src/ui.h src/common.h src/ contact_list.h src/xmpp_conn.c \
-	src/main.c src/profanity.h src/history.h \
+	src/main.c src/profanity.h src/history.h  src/xmpp_presence.c \
 	src/tinyurl.c src/tinyurl.h src/chat_session.c \
 	src/chat_session.h src/muc.c \
 	src/muc.h src/xmpp_stanza.c src/command_parser.c \
diff --git a/src/command.c b/src/command.c
index 0d590b1f..c3701d3f 100644
--- a/src/command.c
+++ b/src/command.c
@@ -1206,7 +1206,7 @@ _cmd_sub(gchar **args, struct cmd_help_t help)
     }
 
     if (strcmp(subcmd, "received") == 0) {
-        GList *received = jabber_get_subscription_requests();
+        GList *received = presence_get_subscription_requests();
 
         if (received == NULL) {
             cons_show("No outstanding subscription requests.");
@@ -1236,15 +1236,15 @@ _cmd_sub(gchar **args, struct cmd_help_t help)
     bare_jid = strtok(jid, "/");
 
     if (strcmp(subcmd, "allow") == 0) {
-        jabber_subscription(bare_jid, PRESENCE_SUBSCRIBED);
+        presence_subscription(bare_jid, PRESENCE_SUBSCRIBED);
         cons_show("Accepted subscription for %s", bare_jid);
         log_info("Accepted subscription for %s", bare_jid);
     } else if (strcmp(subcmd, "deny") == 0) {
-        jabber_subscription(bare_jid, PRESENCE_UNSUBSCRIBED);
+        presence_subscription(bare_jid, PRESENCE_UNSUBSCRIBED);
         cons_show("Deleted/denied subscription for %s", bare_jid);
         log_info("Deleted/denied subscription for %s", bare_jid);
     } else if (strcmp(subcmd, "request") == 0) {
-        jabber_subscription(bare_jid, PRESENCE_SUBSCRIBE);
+        presence_subscription(bare_jid, PRESENCE_SUBSCRIBE);
         cons_show("Sent subscription request to %s.", bare_jid);
         log_info("Sent subscription request to %s.", bare_jid);
     } else if (strcmp(subcmd, "show") == 0) {
diff --git a/src/muc.h b/src/muc.h
index efd637fd..f2685b96 100644
--- a/src/muc.h
+++ b/src/muc.h
@@ -26,6 +26,8 @@
 #include <glib.h>
 
 #include "autocomplete.h"
+#include "contact.h"
+#include "jid.h"
 
 void muc_join_room(const char * const room, const char * const nick);
 void muc_leave_room(const char * const room);
diff --git a/src/xmpp.h b/src/xmpp.h
index 4d8d9898..b8356729 100644
--- a/src/xmpp.h
+++ b/src/xmpp.h
@@ -149,8 +149,6 @@ void jabber_process_events(void);
 void jabber_join(Jid *jid);
 void jabber_change_room_nick(const char * const room, const char * const nick);
 void jabber_leave_chat_room(const char * const room_jid);
-void jabber_subscription(const char * const jid, jabber_subscr_t action);
-GList* jabber_get_subscription_requests(void);
 void jabber_send(const char * const msg, const char * const recipient);
 void jabber_send_groupchat(const char * const msg, const char * const recipient);
 void jabber_send_inactive(const char * const recipient);
@@ -167,10 +165,21 @@ char * jabber_get_status(void);
 void jabber_free_resources(void);
 void jabber_restart(void);
 void jabber_set_autoping(int seconds);
+xmpp_conn_t *jabber_get_conn(void);
+xmpp_ctx_t *jabber_get_ctx(void);
+int error_handler(xmpp_stanza_t * const stanza);
 
 // iq functions
 void iq_add_handlers(xmpp_conn_t * const conn, xmpp_ctx_t * const ctx);
 
+// presence functions
+void presence_init(void);
+void presence_subscription(const char * const jid, const jabber_subscr_t action);
+GList* presence_get_subscription_requests(void);
+void presence_free_sub_requests(void);
+int presence_handler(xmpp_conn_t * const conn,
+    xmpp_stanza_t * const stanza, void * const userdata);
+
 // caps functions
 void caps_init(void);
 void caps_add(const char * const caps_str, const char * const client);
diff --git a/src/xmpp_conn.c b/src/xmpp_conn.c
index 07e592e7..52ded632 100644
--- a/src/xmpp_conn.c
+++ b/src/xmpp_conn.c
@@ -47,8 +47,6 @@ static struct _jabber_conn_t {
     int priority;
 } jabber_conn;
 
-static GHashTable *sub_requests;
-
 // for auto reconnect
 static struct {
     char *name;
@@ -85,13 +83,9 @@ static void _connection_handler(xmpp_conn_t * const conn,
 static int _message_handler(xmpp_conn_t * const conn,
     xmpp_stanza_t * const stanza, void * const userdata);
 static int _groupchat_message_handler(xmpp_stanza_t * const stanza);
-static int _error_handler(xmpp_stanza_t * const stanza);
 static int _chat_message_handler(xmpp_stanza_t * const stanza);
 
-static int _presence_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata);
 static int _ping_timed_handler(xmpp_conn_t * const conn, void * const userdata);
-static char * _handle_presence_caps(xmpp_stanza_t * const stanza);
 
 void
 jabber_init(const int disable_tls)
@@ -101,7 +95,7 @@ jabber_init(const int disable_tls)
     jabber_conn.presence = PRESENCE_OFFLINE;
     jabber_conn.status = NULL;
     jabber_conn.tls_disabled = disable_tls;
-    sub_requests = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+    presence_init();
 }
 
 void
@@ -325,43 +319,6 @@ jabber_send_gone(const char * const recipient)
 }
 
 void
-jabber_subscription(const char * const jid, jabber_subscr_t action)
-{
-    xmpp_stanza_t *presence;
-    char *type, *jid_cpy, *bare_jid;
-
-    // jid must be a bare JID
-    jid_cpy = strdup(jid);
-    bare_jid = strtok(jid_cpy, "/");
-    g_hash_table_remove(sub_requests, bare_jid);
-
-    if (action == PRESENCE_SUBSCRIBE)
-        type = STANZA_TYPE_SUBSCRIBE;
-    else if (action == PRESENCE_SUBSCRIBED)
-        type = STANZA_TYPE_SUBSCRIBED;
-    else if (action == PRESENCE_UNSUBSCRIBED)
-        type = STANZA_TYPE_UNSUBSCRIBED;
-    else { // unknown action
-        free(jid_cpy);
-        return;
-    }
-
-    presence = xmpp_stanza_new(jabber_conn.ctx);
-    xmpp_stanza_set_name(presence, STANZA_NAME_PRESENCE);
-    xmpp_stanza_set_type(presence, type);
-    xmpp_stanza_set_attribute(presence, STANZA_ATTR_TO, bare_jid);
-    xmpp_send(jabber_conn.conn, presence);
-    xmpp_stanza_release(presence);
-    free(jid_cpy);
-}
-
-GList *
-jabber_get_subscription_requests(void)
-{
-    return g_hash_table_get_keys(sub_requests);
-}
-
-void
 jabber_join(Jid *jid)
 {
     xmpp_stanza_t *presence = stanza_create_room_join_presence(jabber_conn.ctx,
@@ -524,6 +481,18 @@ jabber_get_connection_status(void)
     return (jabber_conn.conn_status);
 }
 
+xmpp_conn_t *
+jabber_get_conn(void)
+{
+    return jabber_conn.conn;
+}
+
+xmpp_ctx_t *
+jabber_get_ctx(void)
+{
+    return jabber_conn.ctx;
+}
+
 const char *
 jabber_get_jid(void)
 {
@@ -559,8 +528,7 @@ jabber_free_resources(void)
     FREE_SET_NULL(saved_account.name);
     FREE_SET_NULL(saved_account.passwd);
     chat_sessions_clear();
-    if (sub_requests != NULL)
-        g_hash_table_remove_all(sub_requests);
+    presence_free_sub_requests();
     xmpp_conn_release(jabber_conn.conn);
     xmpp_ctx_free(jabber_conn.ctx);
     xmpp_shutdown();
@@ -584,7 +552,7 @@ _message_handler(xmpp_conn_t * const conn,
         log_error("Message stanza received with no type attribute");
         return 1;
     } else if (strcmp(type, STANZA_TYPE_ERROR) == 0) {
-        return _error_handler(stanza);
+        return error_handler(stanza);
     } else if (strcmp(type, STANZA_TYPE_GROUPCHAT) == 0) {
         return _groupchat_message_handler(stanza);
     } else if (strcmp(type, STANZA_TYPE_CHAT) == 0) {
@@ -661,8 +629,8 @@ _groupchat_message_handler(xmpp_stanza_t * const stanza)
     return 1;
 }
 
-static int
-_error_handler(xmpp_stanza_t * const stanza)
+int
+error_handler(xmpp_stanza_t * const stanza)
 {
     gchar *err_msg = NULL;
     gchar *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
@@ -811,7 +779,7 @@ _connection_handler(xmpp_conn_t * const conn,
         chat_sessions_init();
 
         xmpp_handler_add(conn, _message_handler, NULL, STANZA_NAME_MESSAGE, NULL, ctx);
-        xmpp_handler_add(conn, _presence_handler, NULL, STANZA_NAME_PRESENCE, NULL, ctx);
+        xmpp_handler_add(conn, presence_handler, NULL, STANZA_NAME_PRESENCE, NULL, ctx);
 
         iq_add_handlers(conn, ctx);
 
@@ -877,239 +845,6 @@ _ping_timed_handler(xmpp_conn_t * const conn, void * const userdata)
     return 1;
 }
 
-static int
-_room_presence_handler(const char * const jid, xmpp_stanza_t * const stanza)
-{
-    char *room = NULL;
-    char *nick = NULL;
-
-    if (!parse_room_jid(jid, &room, &nick)) {
-        log_error("Could not parse room jid: %s", room);
-        return 1;
-    }
-
-    // handle self presence
-    if (stanza_is_muc_self_presence(stanza, jabber_get_jid())) {
-        char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
-        gboolean nick_change = stanza_is_room_nick_change(stanza);
-
-        if ((type != NULL) && (strcmp(type, STANZA_TYPE_UNAVAILABLE) == 0)) {
-
-            // leave room if not self nick change
-            if (nick_change) {
-                muc_set_room_pending_nick_change(room);
-            } else {
-                prof_handle_leave_room(room);
-            }
-
-        // handle self nick change
-        } else if (muc_is_room_pending_nick_change(room)) {
-            muc_complete_room_nick_change(room, nick);
-            prof_handle_room_nick_change(room, nick);
-
-        // handle roster complete
-        } else if (!muc_get_roster_received(room)) {
-            prof_handle_room_roster_complete(room);
-
-        }
-
-    // handle presence from room members
-    } else {
-        char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
-        char *show_str, *status_str;
-        char *caps_key = _handle_presence_caps(stanza);
-
-        xmpp_stanza_t *status = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_STATUS);
-        if (status != NULL) {
-            status_str = xmpp_stanza_get_text(status);
-        } else {
-            status_str = NULL;
-        }
-
-        if ((type != NULL) && (strcmp(type, STANZA_TYPE_UNAVAILABLE) == 0)) {
-
-            // handle nickname change
-            if (stanza_is_room_nick_change(stanza)) {
-                char *new_nick = stanza_get_new_nick(stanza);
-                muc_set_roster_pending_nick_change(room, new_nick, nick);
-            } else {
-                prof_handle_room_member_offline(room, nick, "offline", status_str);
-            }
-        } else {
-            xmpp_stanza_t *show = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_SHOW);
-            if (show != NULL) {
-                show_str = xmpp_stanza_get_text(show);
-            } else {
-                show_str = "online";
-            }
-            if (!muc_get_roster_received(room)) {
-                muc_add_to_roster(room, nick, show_str, status_str, caps_key);
-            } else {
-                char *old_nick = muc_complete_roster_nick_change(room, nick);
-
-                if (old_nick != NULL) {
-                    muc_add_to_roster(room, nick, show_str, status_str, caps_key);
-                    prof_handle_room_member_nick_change(room, old_nick, nick);
-                } else {
-                    if (!muc_nick_in_roster(room, nick)) {
-                        prof_handle_room_member_online(room, nick, show_str, status_str, caps_key);
-                    } else {
-                        prof_handle_room_member_presence(room, nick, show_str, status_str, caps_key);
-                    }
-                }
-            }
-        }
-    }
-
-    free(room);
-    free(nick);
-
-    return 1;
-}
-
-static char *
-_handle_presence_caps(xmpp_stanza_t * const stanza)
-{
-    char *caps_key = NULL;
-    char *node = NULL;
-    char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
-    if (stanza_contains_caps(stanza)) {
-        char *hash_type = stanza_caps_get_hash(stanza);
-
-        // xep-0115
-        if (hash_type != NULL) {
-
-            // supported hash
-            if (strcmp(hash_type, "sha-1") == 0) {
-                node = stanza_get_caps_str(stanza);
-                caps_key = node;
-
-                if (node != NULL) {
-                    if (!caps_contains(caps_key)) {
-                        xmpp_stanza_t *iq = stanza_create_disco_iq(jabber_conn.ctx, "disco", from, node);
-                        xmpp_send(jabber_conn.conn, iq);
-                        xmpp_stanza_release(iq);
-                    }
-                }
-
-            // unsupported hash
-            } else {
-                node = stanza_get_caps_str(stanza);
-                caps_key = from;
-
-                if (node != NULL) {
-                    if (!caps_contains(caps_key)) {
-                        GString *id = g_string_new("disco_");
-                        g_string_append(id, from);
-                        xmpp_stanza_t *iq = stanza_create_disco_iq(jabber_conn.ctx, id->str, from, node);
-                        xmpp_send(jabber_conn.conn, iq);
-                        xmpp_stanza_release(iq);
-                        g_string_free(id, TRUE);
-                    }
-                }
-            }
-
-            return strdup(caps_key);
-
-        //ignore or handle legacy caps
-        } else {
-            node = stanza_get_caps_str(stanza);
-            caps_key = from;
-
-            if (node != NULL) {
-                if (!caps_contains(caps_key)) {
-                    GString *id = g_string_new("disco_");
-                    g_string_append(id, from);
-                    xmpp_stanza_t *iq = stanza_create_disco_iq(jabber_conn.ctx, id->str, from, node);
-                    xmpp_send(jabber_conn.conn, iq);
-                    xmpp_stanza_release(iq);
-                    g_string_free(id, TRUE);
-                }
-            }
-
-            return caps_key;
-        }
-    }
-    return NULL;
-}
-
-static int
-_presence_handler(xmpp_conn_t * const conn,
-    xmpp_stanza_t * const stanza, void * const userdata)
-{
-    const char *jid = xmpp_conn_get_jid(jabber_conn.conn);
-    char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
-    char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
-
-    Jid *my_jid = jid_create(jid);
-    Jid *from_jid = jid_create(from);
-
-    if ((type != NULL) && (strcmp(type, STANZA_TYPE_ERROR) == 0)) {
-        return _error_handler(stanza);
-    }
-
-    // handle chat room presence
-    if (muc_room_is_active(from_jid)) {
-        return _room_presence_handler(from_jid->str, stanza);
-
-    // handle regular presence
-    } else {
-        char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
-        char *show_str, *status_str;
-        int idle_seconds = stanza_get_idle_time(stanza);
-        GDateTime *last_activity = NULL;
-
-        if (idle_seconds > 0) {
-            GDateTime *now = g_date_time_new_now_local();
-            last_activity = g_date_time_add_seconds(now, 0 - idle_seconds);
-            g_date_time_unref(now);
-        }
-
-        char *caps_key = _handle_presence_caps(stanza);
-
-        xmpp_stanza_t *status = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_STATUS);
-        if (status != NULL)
-            status_str = xmpp_stanza_get_text(status);
-        else
-            status_str = NULL;
-
-        if (type == NULL) { // available
-            xmpp_stanza_t *show = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_SHOW);
-            if (show != NULL)
-                show_str = xmpp_stanza_get_text(show);
-            else
-                show_str = "online";
-
-            if (strcmp(my_jid->barejid, from_jid->barejid) !=0) {
-                prof_handle_contact_online(from_jid->barejid, show_str, status_str, last_activity, caps_key);
-            }
-        } else if (strcmp(type, STANZA_TYPE_UNAVAILABLE) == 0) {
-            if (strcmp(my_jid->barejid, from_jid->barejid) !=0) {
-                prof_handle_contact_offline(from_jid->barejid, "offline", status_str);
-            }
-
-        if (last_activity != NULL) {
-            g_date_time_unref(last_activity);
-        }
-
-        // subscriptions
-        } else if (strcmp(type, STANZA_TYPE_SUBSCRIBE) == 0) {
-            prof_handle_subscription(from_jid->barejid, PRESENCE_SUBSCRIBE);
-            g_hash_table_insert(sub_requests, strdup(from_jid->barejid), strdup(from_jid->barejid));
-        } else if (strcmp(type, STANZA_TYPE_SUBSCRIBED) == 0) {
-            prof_handle_subscription(from_jid->barejid, PRESENCE_SUBSCRIBED);
-            g_hash_table_remove(sub_requests, from_jid->barejid);
-        } else if (strcmp(type, STANZA_TYPE_UNSUBSCRIBED) == 0) {
-            prof_handle_subscription(from_jid->barejid, PRESENCE_UNSUBSCRIBED);
-            g_hash_table_remove(sub_requests, from_jid->barejid);
-        } else { /* unknown type */
-            log_debug("Received presence with unknown type '%s'", type);
-        }
-    }
-
-    return 1;
-}
-
 static log_level_t
 _get_log_level(xmpp_log_level_t xmpp_level)
 {
diff --git a/src/xmpp_presence.c b/src/xmpp_presence.c
new file mode 100644
index 00000000..aa5dcad3
--- /dev/null
+++ b/src/xmpp_presence.c
@@ -0,0 +1,324 @@
+/*
+ * xmpp_presence.c
+ *
+ * Copyright (C) 2012, 2013 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "log.h"
+#include "muc.h"
+#include "profanity.h"
+#include "xmpp.h"
+
+static GHashTable *sub_requests;
+static char* _handle_presence_caps(xmpp_stanza_t * const stanza);
+static int _room_presence_handler(const char * const jid,
+    xmpp_stanza_t * const stanza);
+
+void
+presence_init(void)
+{
+    sub_requests = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+}
+
+void
+presence_subscription(const char * const jid, const jabber_subscr_t action)
+{
+    xmpp_ctx_t *ctx = jabber_get_ctx();
+    xmpp_conn_t *conn = jabber_get_conn();
+    xmpp_stanza_t *presence;
+    char *type, *jid_cpy, *bare_jid;
+
+    // jid must be a bare JID
+    jid_cpy = strdup(jid);
+    bare_jid = strtok(jid_cpy, "/");
+    g_hash_table_remove(sub_requests, bare_jid);
+
+    if (action == PRESENCE_SUBSCRIBE)
+        type = STANZA_TYPE_SUBSCRIBE;
+    else if (action == PRESENCE_SUBSCRIBED)
+        type = STANZA_TYPE_SUBSCRIBED;
+    else if (action == PRESENCE_UNSUBSCRIBED)
+        type = STANZA_TYPE_UNSUBSCRIBED;
+    else { // unknown action
+        free(jid_cpy);
+        return;
+    }
+
+    presence = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(presence, STANZA_NAME_PRESENCE);
+    xmpp_stanza_set_type(presence, type);
+    xmpp_stanza_set_attribute(presence, STANZA_ATTR_TO, bare_jid);
+    xmpp_send(conn, presence);
+    xmpp_stanza_release(presence);
+    free(jid_cpy);
+}
+
+GList *
+presence_get_subscription_requests(void)
+{
+    return g_hash_table_get_keys(sub_requests);
+}
+
+void
+presence_free_sub_requests(void)
+{
+    if (sub_requests != NULL)
+        g_hash_table_remove_all(sub_requests);
+}
+
+int
+presence_handler(xmpp_conn_t * const conn,
+    xmpp_stanza_t * const stanza, void * const userdata)
+{
+    const char *jid = xmpp_conn_get_jid(conn);
+    char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
+    char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
+
+    Jid *my_jid = jid_create(jid);
+    Jid *from_jid = jid_create(from);
+
+    if ((type != NULL) && (strcmp(type, STANZA_TYPE_ERROR) == 0)) {
+        return error_handler(stanza);
+    }
+
+    // handle chat room presence
+    if (muc_room_is_active(from_jid)) {
+        return _room_presence_handler(from_jid->str, stanza);
+
+    // handle regular presence
+    } else {
+        char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
+        char *show_str, *status_str;
+        int idle_seconds = stanza_get_idle_time(stanza);
+        GDateTime *last_activity = NULL;
+
+        if (idle_seconds > 0) {
+            GDateTime *now = g_date_time_new_now_local();
+            last_activity = g_date_time_add_seconds(now, 0 - idle_seconds);
+            g_date_time_unref(now);
+        }
+
+        char *caps_key = _handle_presence_caps(stanza);
+
+        xmpp_stanza_t *status = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_STATUS);
+        if (status != NULL)
+            status_str = xmpp_stanza_get_text(status);
+        else
+            status_str = NULL;
+
+        if (type == NULL) { // available
+            xmpp_stanza_t *show = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_SHOW);
+            if (show != NULL)
+                show_str = xmpp_stanza_get_text(show);
+            else
+                show_str = "online";
+
+            if (strcmp(my_jid->barejid, from_jid->barejid) !=0) {
+                prof_handle_contact_online(from_jid->barejid, show_str, status_str, last_activity, caps_key);
+            }
+        } else if (strcmp(type, STANZA_TYPE_UNAVAILABLE) == 0) {
+            if (strcmp(my_jid->barejid, from_jid->barejid) !=0) {
+                prof_handle_contact_offline(from_jid->barejid, "offline", status_str);
+            }
+
+        if (last_activity != NULL) {
+            g_date_time_unref(last_activity);
+        }
+
+        // subscriptions
+        } else if (strcmp(type, STANZA_TYPE_SUBSCRIBE) == 0) {
+            prof_handle_subscription(from_jid->barejid, PRESENCE_SUBSCRIBE);
+            g_hash_table_insert(sub_requests, strdup(from_jid->barejid), strdup(from_jid->barejid));
+        } else if (strcmp(type, STANZA_TYPE_SUBSCRIBED) == 0) {
+            prof_handle_subscription(from_jid->barejid, PRESENCE_SUBSCRIBED);
+            g_hash_table_remove(sub_requests, from_jid->barejid);
+        } else if (strcmp(type, STANZA_TYPE_UNSUBSCRIBED) == 0) {
+            prof_handle_subscription(from_jid->barejid, PRESENCE_UNSUBSCRIBED);
+            g_hash_table_remove(sub_requests, from_jid->barejid);
+        } else { /* unknown type */
+            log_debug("Received presence with unknown type '%s'", type);
+        }
+    }
+
+    return 1;
+}
+
+static char *
+_handle_presence_caps(xmpp_stanza_t * const stanza)
+{
+    xmpp_ctx_t *ctx = jabber_get_ctx();
+    xmpp_conn_t *conn = jabber_get_conn();
+    char *caps_key = NULL;
+    char *node = NULL;
+    char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
+    if (stanza_contains_caps(stanza)) {
+        char *hash_type = stanza_caps_get_hash(stanza);
+
+        // xep-0115
+        if (hash_type != NULL) {
+
+            // supported hash
+            if (strcmp(hash_type, "sha-1") == 0) {
+                node = stanza_get_caps_str(stanza);
+                caps_key = node;
+
+                if (node != NULL) {
+                    if (!caps_contains(caps_key)) {
+                        xmpp_stanza_t *iq = stanza_create_disco_iq(ctx, "disco", from, node);
+                        xmpp_send(conn, iq);
+                        xmpp_stanza_release(iq);
+                    }
+                }
+
+            // unsupported hash
+            } else {
+                node = stanza_get_caps_str(stanza);
+                caps_key = from;
+
+                if (node != NULL) {
+                    if (!caps_contains(caps_key)) {
+                        GString *id = g_string_new("disco_");
+                        g_string_append(id, from);
+                        xmpp_stanza_t *iq = stanza_create_disco_iq(ctx, id->str, from, node);
+                        xmpp_send(conn, iq);
+                        xmpp_stanza_release(iq);
+                        g_string_free(id, TRUE);
+                    }
+                }
+            }
+
+            return strdup(caps_key);
+
+        //ignore or handle legacy caps
+        } else {
+            node = stanza_get_caps_str(stanza);
+            caps_key = from;
+
+            if (node != NULL) {
+                if (!caps_contains(caps_key)) {
+                    GString *id = g_string_new("disco_");
+                    g_string_append(id, from);
+                    xmpp_stanza_t *iq = stanza_create_disco_iq(ctx, id->str, from, node);
+                    xmpp_send(conn, iq);
+                    xmpp_stanza_release(iq);
+                    g_string_free(id, TRUE);
+                }
+            }
+
+            return caps_key;
+        }
+    }
+    return NULL;
+}
+
+static int
+_room_presence_handler(const char * const jid, xmpp_stanza_t * const stanza)
+{
+    char *room = NULL;
+    char *nick = NULL;
+
+    if (!parse_room_jid(jid, &room, &nick)) {
+        log_error("Could not parse room jid: %s", room);
+        return 1;
+    }
+
+    // handle self presence
+    if (stanza_is_muc_self_presence(stanza, jabber_get_jid())) {
+        char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
+        gboolean nick_change = stanza_is_room_nick_change(stanza);
+
+        if ((type != NULL) && (strcmp(type, STANZA_TYPE_UNAVAILABLE) == 0)) {
+
+            // leave room if not self nick change
+            if (nick_change) {
+                muc_set_room_pending_nick_change(room);
+            } else {
+                prof_handle_leave_room(room);
+            }
+
+        // handle self nick change
+        } else if (muc_is_room_pending_nick_change(room)) {
+            muc_complete_room_nick_change(room, nick);
+            prof_handle_room_nick_change(room, nick);
+
+        // handle roster complete
+        } else if (!muc_get_roster_received(room)) {
+            prof_handle_room_roster_complete(room);
+
+        }
+
+    // handle presence from room members
+    } else {
+        char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
+        char *show_str, *status_str;
+        char *caps_key = _handle_presence_caps(stanza);
+
+        xmpp_stanza_t *status = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_STATUS);
+        if (status != NULL) {
+            status_str = xmpp_stanza_get_text(status);
+        } else {
+            status_str = NULL;
+        }
+
+        if ((type != NULL) && (strcmp(type, STANZA_TYPE_UNAVAILABLE) == 0)) {
+
+            // handle nickname change
+            if (stanza_is_room_nick_change(stanza)) {
+                char *new_nick = stanza_get_new_nick(stanza);
+                muc_set_roster_pending_nick_change(room, new_nick, nick);
+            } else {
+                prof_handle_room_member_offline(room, nick, "offline", status_str);
+            }
+        } else {
+            xmpp_stanza_t *show = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_SHOW);
+            if (show != NULL) {
+                show_str = xmpp_stanza_get_text(show);
+            } else {
+                show_str = "online";
+            }
+            if (!muc_get_roster_received(room)) {
+                muc_add_to_roster(room, nick, show_str, status_str, caps_key);
+            } else {
+                char *old_nick = muc_complete_roster_nick_change(room, nick);
+
+                if (old_nick != NULL) {
+                    muc_add_to_roster(room, nick, show_str, status_str, caps_key);
+                    prof_handle_room_member_nick_change(room, old_nick, nick);
+                } else {
+                    if (!muc_nick_in_roster(room, nick)) {
+                        prof_handle_room_member_online(room, nick, show_str, status_str, caps_key);
+                    } else {
+                        prof_handle_room_member_presence(room, nick, show_str, status_str, caps_key);
+                    }
+                }
+            }
+        }
+    }
+
+    free(room);
+    free(nick);
+
+    return 1;
+}
+