about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/command/command.c20
-rw-r--r--src/command/commands.c117
-rw-r--r--src/muc.c599
-rw-r--r--src/muc.h116
-rw-r--r--src/roster_list.c2
-rw-r--r--src/server_events.c54
-rw-r--r--src/server_events.h9
-rw-r--r--src/tools/autocomplete.c48
-rw-r--r--src/tools/autocomplete.h2
-rw-r--r--src/ui/console.c9
-rw-r--r--src/ui/core.c53
-rw-r--r--src/ui/notifier.c2
-rw-r--r--src/ui/ui.h4
-rw-r--r--src/ui/window.c81
-rw-r--r--src/ui/window.h3
-rw-r--r--src/xmpp/bookmark.c14
-rw-r--r--src/xmpp/iq.c13
-rw-r--r--src/xmpp/message.c4
-rw-r--r--src/xmpp/presence.c76
-rw-r--r--src/xmpp/stanza.c22
20 files changed, 729 insertions, 519 deletions
diff --git a/src/command/command.c b/src/command/command.c
index b579fdcf..bea6cbcd 100644
--- a/src/command/command.c
+++ b/src/command/command.c
@@ -307,12 +307,13 @@ static struct cmd_t command_defs[] =
 
     { "/room",
         cmd_room, parse_args, 1, 1, NULL,
-        { "/room accept|destroy|config", "Room configuration.",
-        { "/room accept|destroy|config",
-          "---------------------------",
+        { "/room accept|destroy|config|info", "Room configuration.",
+        { "/room accept|destroy|config|info",
+          "--------------------------------",
           "accept  - Accept default room configuration.",
           "destroy - Reject default room configuration.",
           "config  - Edit room configuration.",
+          "info    - Show room details.",
           NULL } } },
 
     { "/form",
@@ -1226,6 +1227,7 @@ cmd_init(void)
     autocomplete_add(room_ac, "accept");
     autocomplete_add(room_ac, "destroy");
     autocomplete_add(room_ac, "config");
+    autocomplete_add(room_ac, "info");
 
     form_ac = autocomplete_new();
     autocomplete_add(form_ac, "submit");
@@ -1257,9 +1259,7 @@ cmd_uninit(void)
     autocomplete_free(autoaway_mode_ac);
     autocomplete_free(autoconnect_ac);
     autocomplete_free(theme_ac);
-    if (theme_load_ac != NULL) {
-        autocomplete_free(theme_load_ac);
-    }
+    autocomplete_free(theme_load_ac);
     autocomplete_free(account_ac);
     autocomplete_free(account_set_ac);
     autocomplete_free(account_clear_ac);
@@ -1356,7 +1356,7 @@ void
 cmd_reset_autocomplete()
 {
     roster_reset_search_attempts();
-    muc_reset_invites_ac();
+    muc_invites_reset_ac();
     accounts_reset_all_search();
     accounts_reset_enabled_search();
     prefs_reset_boolean_choice();
@@ -1370,7 +1370,7 @@ cmd_reset_autocomplete()
 
     if (ui_current_win_type() == WIN_MUC) {
         char *recipient = ui_current_recipient();
-        muc_reset_autocomplete(recipient);
+        muc_autocomplete_reset(recipient);
     }
 
     autocomplete_reset(who_ac);
@@ -1605,7 +1605,7 @@ _cmd_complete_parameters(char *input, int *size)
     // autocomplete nickname in chat rooms
     if (ui_current_win_type() == WIN_MUC) {
         char *recipient = ui_current_recipient();
-        Autocomplete nick_ac = muc_get_roster_ac(recipient);
+        Autocomplete nick_ac = muc_roster_ac(recipient);
         if (nick_ac != NULL) {
             gchar *nick_choices[] = { "/msg", "/info", "/caps", "/status", "/software" } ;
 
@@ -1655,7 +1655,7 @@ _cmd_complete_parameters(char *input, int *size)
     gchar *invite_choices[] = { "/decline", "/join" };
     for (i = 0; i < ARRAY_SIZE(invite_choices); i++) {
         result = autocomplete_param_with_func(input, size, invite_choices[i],
-            muc_find_invite);
+            muc_invites_find);
         if (result != NULL) {
             ui_replace_input(input, result, size);
             g_free(result);
diff --git a/src/command/commands.c b/src/command/commands.c
index 045ad00a..2b8a0d2c 100644
--- a/src/command/commands.c
+++ b/src/command/commands.c
@@ -476,7 +476,7 @@ cmd_disconnect(gchar **args, struct cmd_help_t help)
         cons_show("%s logged out successfully.", jid);
         jabber_disconnect();
         roster_clear();
-        muc_clear_invites();
+        muc_invites_clear();
         chat_sessions_clear();
         ui_disconnected();
         free(jid);
@@ -726,7 +726,7 @@ static void
 _who_room(const char * const presence)
 {
     char *room = ui_current_recipient();
-    GList *list = muc_get_roster(room);
+    GList *list = muc_roster(room);
 
     // no arg, show all contacts
     if ((presence == NULL) || (g_strcmp0(presence, "any") == 0)) {
@@ -737,9 +737,9 @@ _who_room(const char * const presence)
         GList *filtered = NULL;
 
         while (list != NULL) {
-            PContact contact = list->data;
-            if (p_contact_is_available(contact)) {
-                filtered = g_list_append(filtered, contact);
+            Occupant *occupant = list->data;
+            if (muc_occupant_available(occupant)) {
+                filtered = g_list_append(filtered, occupant);
             }
             list = g_list_next(list);
         }
@@ -751,51 +751,24 @@ _who_room(const char * const presence)
         GList *filtered = NULL;
 
         while (list != NULL) {
-            PContact contact = list->data;
-            if (!p_contact_is_available(contact)) {
-                filtered = g_list_append(filtered, contact);
+            Occupant *occupant = list->data;
+            if (!muc_occupant_available(occupant)) {
+                filtered = g_list_append(filtered, occupant);
             }
             list = g_list_next(list);
         }
 
         ui_room_roster(room, filtered, "unavailable");
 
-    // online, available resources
-    } else if (strcmp("online", presence) == 0) {
-        GList *filtered = NULL;
-
-        while (list != NULL) {
-            PContact contact = list->data;
-            if (p_contact_has_available_resource(contact)) {
-                filtered = g_list_append(filtered, contact);
-            }
-            list = g_list_next(list);
-        }
-
-        ui_room_roster(room, filtered, "online");
-
-    // offline, no available resources
-    } else if (strcmp("offline", presence) == 0) {
-        GList *filtered = NULL;
-
-        while (list != NULL) {
-            PContact contact = list->data;
-            if (!p_contact_has_available_resource(contact)) {
-                filtered = g_list_append(filtered, contact);
-            }
-            list = g_list_next(list);
-        }
-
-        ui_room_roster(room, filtered, "offline");
-
     // show specific status
     } else {
         GList *filtered = NULL;
 
         while (list != NULL) {
-            PContact contact = list->data;
-            if (strcmp(p_contact_presence(contact), presence) == 0) {
-                filtered = g_list_append(filtered, contact);
+            Occupant *occupant = list->data;
+            const char *presence_str = string_from_resource_presence(occupant->presence);
+            if (strcmp(presence_str, presence) == 0) {
+                filtered = g_list_append(filtered, occupant);
             }
             list = g_list_next(list);
         }
@@ -1048,7 +1021,7 @@ cmd_msg(gchar **args, struct cmd_help_t help)
 
     if (win_type == WIN_MUC) {
         char *room_name = ui_current_recipient();
-        if (muc_nick_in_roster(room_name, usr)) {
+        if (muc_roster_contains_nick(room_name, usr)) {
             GString *full_jid = g_string_new(room_name);
             g_string_append(full_jid, "/");
             g_string_append(full_jid, usr);
@@ -1464,6 +1437,7 @@ cmd_info(gchar **args, struct cmd_help_t help)
     jabber_conn_status_t conn_status = jabber_get_connection_status();
     win_type_t win_type = ui_current_win_type();
     PContact pcontact = NULL;
+    Occupant *occupant = NULL;
 
     if (conn_status != JABBER_CONNECTED) {
         cons_show("You are not currently connected.");
@@ -1473,28 +1447,34 @@ cmd_info(gchar **args, struct cmd_help_t help)
     switch (win_type)
     {
         case WIN_MUC:
-            if (usr != NULL) {
-                ui_info_room(usr);
+            if (usr) {
+                char *room = ui_current_recipient();
+                occupant = muc_roster_item(room, usr);
+                if (occupant) {
+                    ui_info_room(room, occupant);
+                } else {
+                    ui_current_print_line("No such occupant \"%s\" in room.", usr);
+                }
             } else {
                 ui_current_print_line("You must specify a nickname.");
             }
             break;
         case WIN_CHAT:
-            if (usr != NULL) {
+            if (usr) {
                 ui_current_print_line("No parameter required when in chat.");
             } else {
                 ui_info();
             }
             break;
         case WIN_PRIVATE:
-            if (usr != NULL) {
+            if (usr) {
                 ui_current_print_line("No parameter required when in chat.");
             } else {
                 ui_info_private();
             }
             break;
         case WIN_CONSOLE:
-            if (usr != NULL) {
+            if (usr) {
                 char *usr_jid = roster_barejid_from_name(usr);
                 if (usr_jid == NULL) {
                     usr_jid = usr;
@@ -1522,6 +1502,7 @@ cmd_caps(gchar **args, struct cmd_help_t help)
     jabber_conn_status_t conn_status = jabber_get_connection_status();
     win_type_t win_type = ui_current_win_type();
     PContact pcontact = NULL;
+    Occupant *occupant = NULL;
 
     if (conn_status != JABBER_CONNECTED) {
         cons_show("You are not currently connected.");
@@ -1533,11 +1514,10 @@ cmd_caps(gchar **args, struct cmd_help_t help)
         case WIN_MUC:
             if (args[0] != NULL) {
                 char *room = ui_current_recipient();
-                pcontact = muc_get_participant(room, args[0]);
-                if (pcontact != NULL) {
+                occupant = muc_roster_item(room, args[0]);
+                if (occupant) {
                     Jid *jidp = jid_create_from_bare_and_resource(room, args[0]);
-                    Resource *resource = p_contact_get_resource(pcontact, args[0]);
-                    cons_show_caps(jidp->fulljid, resource);
+                    cons_show_caps(jidp->fulljid, occupant->presence);
                     jid_destroy(jidp);
                 } else {
                     cons_show("No such participant \"%s\" in room.", args[0]);
@@ -1562,7 +1542,7 @@ cmd_caps(gchar **args, struct cmd_help_t help)
                         if (resource == NULL) {
                             cons_show("Could not find resource %s, for contact %s", jid->barejid, jid->resourcepart);
                         } else {
-                            cons_show_caps(jid->fulljid, resource);
+                            cons_show_caps(jid->fulljid, resource->presence);
                         }
                     }
                 }
@@ -1578,9 +1558,8 @@ cmd_caps(gchar **args, struct cmd_help_t help)
                 char *recipient = ui_current_recipient();
                 Jid *jid = jid_create(recipient);
                 if (jid) {
-                    pcontact = muc_get_participant(jid->barejid, jid->resourcepart);
-                    Resource *resource = p_contact_get_resource(pcontact, jid->resourcepart);
-                    cons_show_caps(jid->resourcepart, resource);
+                    occupant = muc_roster_item(jid->barejid, jid->resourcepart);
+                    cons_show_caps(jid->resourcepart, occupant->presence);
                     jid_destroy(jid);
                 }
             }
@@ -1598,7 +1577,7 @@ cmd_software(gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
     win_type_t win_type = ui_current_win_type();
-    PContact pcontact = NULL;
+    Occupant *occupant = NULL;
     char *recipient;
 
     if (conn_status != JABBER_CONNECTED) {
@@ -1611,8 +1590,8 @@ cmd_software(gchar **args, struct cmd_help_t help)
         case WIN_MUC:
             if (args[0] != NULL) {
                 recipient = ui_current_recipient();
-                pcontact = muc_get_participant(recipient, args[0]);
-                if (pcontact != NULL) {
+                occupant = muc_roster_item(recipient, args[0]);
+                if (occupant) {
                     Jid *jid = jid_create_from_bare_and_resource(recipient, args[0]);
                     iq_send_software_version(jid->fulljid);
                     jid_destroy(jid);
@@ -1715,10 +1694,10 @@ cmd_join(gchar **args, struct cmd_help_t help)
         nick = account->muc_nick;
     }
 
-    if (!muc_room_is_active(room)) {
+    if (!muc_active(room)) {
         presence_join_room(room, nick, passwd);
-        muc_join_room(room, nick, passwd, FALSE);
-    } else if (muc_get_roster_received(room)) {
+        muc_join(room, nick, passwd, FALSE);
+    } else if (muc_roster_complete(room)) {
         ui_room_join(room, TRUE);
     }
 
@@ -1767,7 +1746,7 @@ cmd_invite(gchar **args, struct cmd_help_t help)
 gboolean
 cmd_invites(gchar **args, struct cmd_help_t help)
 {
-    GSList *invites = muc_get_invites();
+    GSList *invites = muc_invites();
     cons_show_room_invites(invites);
     g_slist_free_full(invites, g_free);
     return TRUE;
@@ -1776,10 +1755,10 @@ cmd_invites(gchar **args, struct cmd_help_t help)
 gboolean
 cmd_decline(gchar **args, struct cmd_help_t help)
 {
-    if (!muc_invites_include(args[0])) {
+    if (!muc_invites_contain(args[0])) {
         cons_show("No such invite exists.");
     } else {
-        muc_remove_invite(args[0]);
+        muc_invites_remove(args[0]);
         cons_show("Declined invite to %s.", args[0]);
     }
 
@@ -2083,7 +2062,8 @@ cmd_room(gchar **args, struct cmd_help_t help)
 
     if ((g_strcmp0(args[0], "accept") != 0) &&
             (g_strcmp0(args[0], "destroy") != 0) &&
-            (g_strcmp0(args[0], "config") != 0)) {
+            (g_strcmp0(args[0], "config") != 0) &&
+            (g_strcmp0(args[0], "info") != 0)) {
         cons_show("Usage: %s", help.usage);
         return TRUE;
     }
@@ -2097,6 +2077,13 @@ cmd_room(gchar **args, struct cmd_help_t help)
         ui_index = 0;
     }
 
+    if (g_strcmp0(args[0], "info") == 0) {
+        char *role = muc_role_str(room);
+        char *affiliation = muc_affiliation_str(room);
+        ui_current_print_line("Affiliation: %s, Role: %s", affiliation, role);
+        return TRUE;
+    }
+
     if (g_strcmp0(args[0], "accept") == 0) {
         gboolean requires_config = muc_requires_config(room);
         if (!requires_config) {
@@ -2171,8 +2158,8 @@ cmd_bookmark(gchar **args, struct cmd_help_t help)
         // default to current nickname, password, and autojoin "on"
         if (cmd == NULL) {
             char *jid = ui_current_recipient();
-            char *nick = muc_get_room_nick(jid);
-            char *password = muc_get_room_password(jid);
+            char *nick = muc_nick(jid);
+            char *password = muc_password(jid);
             gboolean added = bookmark_add(jid, nick, password, "on");
             if (added) {
                 ui_current_print_formatted_line('!', 0, "Bookmark added for %s.", jid);
diff --git a/src/muc.c b/src/muc.c
index 3c937670..6c48f342 100644
--- a/src/muc.c
+++ b/src/muc.c
@@ -42,10 +42,13 @@
 #include "jid.h"
 #include "tools/autocomplete.h"
 #include "ui/ui.h"
+#include "muc.h"
 
 typedef struct _muc_room_t {
     char *room; // e.g. test@conference.server
     char *nick; // e.g. Some User
+    muc_role_t role;
+    muc_affiliation_t affiliation;
     char *password;
     char *subject;
     char *autocomplete_prefix;
@@ -63,54 +66,60 @@ GHashTable *rooms = NULL;
 Autocomplete invite_ac;
 
 static void _free_room(ChatRoom *room);
-static gint _compare_participants(PContact a, PContact b);
+static gint _compare_occupants(Occupant *a, Occupant *b);
+static muc_role_t _role_from_string(const char * const role);
+static muc_affiliation_t _affiliation_from_string(const char * const affiliation);
+static char* _role_to_string(muc_role_t role);
+static char* _affiliation_to_string(muc_affiliation_t affiliation);
+static Occupant* _muc_occupant_new(const char *const nick, muc_role_t role, muc_affiliation_t affiliation,
+    resource_presence_t presence, const char * const status);
+static void _occupant_free(Occupant *occupant);
 
 void
 muc_init(void)
 {
     invite_ac = autocomplete_new();
+    rooms = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)_free_room);
 }
 
 void
 muc_close(void)
 {
     autocomplete_free(invite_ac);
-    if (rooms != NULL) {
-        g_hash_table_destroy(rooms);
-        rooms = NULL;
-    }
+    g_hash_table_destroy(rooms);
+    rooms = NULL;
 }
 
 void
-muc_add_invite(char *room)
+muc_invites_add(const char * const room)
 {
     autocomplete_add(invite_ac, room);
 }
 
 void
-muc_remove_invite(char *room)
+muc_invites_remove(const char * const room)
 {
     autocomplete_remove(invite_ac, room);
 }
 
 gint
-muc_invite_count(void)
+muc_invites_count(void)
 {
     return autocomplete_length(invite_ac);
 }
 
 GSList *
-muc_get_invites(void)
+muc_invites(void)
 {
-    return autocomplete_get_list(invite_ac);
+    return autocomplete_create_list(invite_ac);
 }
 
 gboolean
-muc_invites_include(const char * const room)
+muc_invites_contain(const char * const room)
 {
-    GSList *invites = autocomplete_get_list(invite_ac);
+    GSList *invites = autocomplete_create_list(invite_ac);
     GSList *curr = invites;
-    while (curr != NULL) {
+    while (curr) {
         if (strcmp(curr->data, room) == 0) {
             g_slist_free_full(invites, g_free);
             return TRUE;
@@ -119,44 +128,39 @@ muc_invites_include(const char * const room)
         }
     }
     g_slist_free_full(invites, g_free);
+
     return FALSE;
 }
 
 void
-muc_reset_invites_ac(void)
+muc_invites_reset_ac(void)
 {
     autocomplete_reset(invite_ac);
 }
 
 char *
-muc_find_invite(char *search_str)
+muc_invites_find(char *search_str)
 {
     return autocomplete_complete(invite_ac, search_str, TRUE);
 }
 
 void
-muc_clear_invites(void)
+muc_invites_clear(void)
 {
     autocomplete_clear(invite_ac);
 }
 
-/*
- * Join the chat room with the specified nickname
- */
 void
-muc_join_room(const char * const room, const char * const nick,
+muc_join(const char * const room, const char * const nick,
     const char * const password, gboolean autojoin)
 {
-    if (rooms == NULL) {
-        rooms = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
-            (GDestroyNotify)_free_room);
-    }
-
     ChatRoom *new_room = malloc(sizeof(ChatRoom));
     new_room->room = strdup(room);
     new_room->nick = strdup(nick);
+    new_room->role = MUC_ROLE_NONE;
+    new_room->affiliation = MUC_AFFILIATION_NONE;
     new_room->autocomplete_prefix = NULL;
-    if (password != NULL) {
+    if (password) {
         new_room->password = strdup(password);
     } else {
         new_room->password = NULL;
@@ -164,11 +168,9 @@ muc_join_room(const char * const room, const char * const nick,
     new_room->subject = NULL;
     new_room->pending_broadcasts = NULL;
     new_room->pending_config = FALSE;
-    new_room->roster = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
-        (GDestroyNotify)p_contact_free);
+    new_room->roster = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)_occupant_free);
     new_room->nick_ac = autocomplete_new();
-    new_room->nick_changes = g_hash_table_new_full(g_str_hash, g_str_equal,
-        g_free, g_free);
+    new_room->nick_changes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
     new_room->roster_received = FALSE;
     new_room->pending_nick_change = FALSE;
     new_room->autojoin = autojoin;
@@ -176,80 +178,49 @@ muc_join_room(const char * const room, const char * const nick,
     g_hash_table_insert(rooms, strdup(room), new_room);
 }
 
-/*
- * Leave the room
- */
 void
-muc_leave_room(const char * const room)
+muc_leave(const char * const room)
 {
-    if (rooms != NULL) {
-        g_hash_table_remove(rooms, room);
-    }
+    g_hash_table_remove(rooms, room);
 }
 
 gboolean
 muc_requires_config(const char * const room)
 {
-    if (rooms == NULL) {
-        return FALSE;
-    }
-
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-    if (chat_room == NULL) {
+    if (chat_room) {
+        return chat_room->pending_config;
+    } else {
         return FALSE;
     }
 
-    return chat_room->pending_config;
 }
 
 void
 muc_set_requires_config(const char * const room, gboolean val)
 {
-    if (rooms == NULL) {
-        return;
-    }
-
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-    if (chat_room == NULL) {
-        return;
+    if (chat_room) {
+        chat_room->pending_config = val;
     }
-
-    chat_room->pending_config = val;
-    return;
 }
 
 /*
  * Returns TRUE if the user is currently in the room
  */
 gboolean
-muc_room_is_active(const char * const room)
+muc_active(const char * const room)
 {
-    if (rooms != NULL) {
-        ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-        if (chat_room != NULL) {
-            return TRUE;
-        } else {
-            return FALSE;
-        }
-    } else {
-        return FALSE;
-    }
+    ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
+    return (chat_room != NULL);
 }
 
 gboolean
-muc_room_is_autojoin(const char * const room)
+muc_autojoin(const char * const room)
 {
-    if (rooms != NULL) {
-        ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-        if (chat_room != NULL) {
-            return chat_room->autojoin;
-        } else {
-            return FALSE;
-        }
+    ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
+    if (chat_room) {
+        return chat_room->autojoin;
     } else {
         return FALSE;
     }
@@ -258,72 +229,53 @@ muc_room_is_autojoin(const char * const room)
 void
 muc_set_subject(const char * const room, const char * const subject)
 {
-    if (rooms != NULL) {
-        ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-        if (chat_room != NULL) {
-            if (chat_room->subject != NULL) {
-                free(chat_room->subject);
-            }
-            chat_room->subject = strdup(subject);
-        }
+    ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
+    if (chat_room) {
+        free(chat_room->subject);
+        chat_room->subject = strdup(subject);
     }
 }
 
 char *
-muc_get_subject(const char * const room)
+muc_subject(const char * const room)
 {
-    if (rooms != NULL) {
-        ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-        if (chat_room != NULL) {
-            return chat_room->subject;
-        } else {
-            return NULL;
-        }
+    ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
+    if (chat_room) {
+        return chat_room->subject;
     } else {
         return NULL;
     }
 }
 
 void
-muc_add_pending_broadcast(const char * const room, const char * const message)
+muc_pending_broadcasts_add(const char * const room, const char * const message)
 {
-    if (rooms != NULL) {
-        ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-        if (chat_room != NULL) {
-            chat_room->pending_broadcasts = g_list_append(chat_room->pending_broadcasts, strdup(message));
-        }
+    ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
+    if (chat_room) {
+        chat_room->pending_broadcasts = g_list_append(chat_room->pending_broadcasts, strdup(message));
     }
 }
 
 GList *
-muc_get_pending_broadcasts(const char * const room)
+muc_pending_broadcasts(const char * const room)
 {
-    if (rooms != NULL) {
-        ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-        if (chat_room != NULL) {
-            return chat_room->pending_broadcasts;
-        } else {
-            return NULL;
-        }
+    ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
+    if (chat_room) {
+        return chat_room->pending_broadcasts;
     } else {
         return NULL;
     }
 }
 
 char *
-muc_get_old_nick(const char * const room, const char * const new_nick)
+muc_old_nick(const char * const room, const char * const new_nick)
 {
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-    if ((chat_room != NULL) && (chat_room->pending_nick_change)) {
+    if (chat_room && chat_room->pending_nick_change) {
         return g_hash_table_lookup(chat_room->nick_changes, new_nick);
+    } else {
+        return NULL;
     }
-
-    return NULL;
 }
 
 /*
@@ -331,11 +283,10 @@ muc_get_old_nick(const char * const room, const char * const new_nick)
  * and is awaiting the response
  */
 void
-muc_set_room_pending_nick_change(const char * const room, const char * const new_nick)
+muc_nick_change_start(const char * const room, const char * const new_nick)
 {
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-    if (chat_room != NULL) {
+    if (chat_room) {
         chat_room->pending_nick_change = TRUE;
         g_hash_table_insert(chat_room->nick_changes, strdup(new_nick), strdup(chat_room->nick));
     }
@@ -346,11 +297,10 @@ muc_set_room_pending_nick_change(const char * const room, const char * const new
  * nick change
  */
 gboolean
-muc_is_room_pending_nick_change(const char * const room)
+muc_nick_change_pending(const char * const room)
 {
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-    if (chat_room != NULL) {
+    if (chat_room) {
         return chat_room->pending_nick_change;
     } else {
         return FALSE;
@@ -362,11 +312,10 @@ muc_is_room_pending_nick_change(const char * const room)
  * the service has responded
  */
 void
-muc_complete_room_nick_change(const char * const room, const char * const nick)
+muc_nick_change_complete(const char * const room, const char * const nick)
 {
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-    if (chat_room != NULL) {
+    if (chat_room) {
         free(chat_room->nick);
         chat_room->nick = strdup(nick);
         chat_room->pending_nick_change = FALSE;
@@ -380,13 +329,9 @@ muc_complete_room_nick_change(const char * const room, const char * const nick)
  * modified or freed.
  */
 GList *
-muc_get_active_room_list(void)
+muc_rooms(void)
 {
-    if (rooms != NULL) {
-        return g_hash_table_get_keys(rooms);
-    } else {
-        return NULL;
-    }
+    return g_hash_table_get_keys(rooms);
 }
 
 /*
@@ -394,16 +339,11 @@ muc_get_active_room_list(void)
  * The nickname is owned by the chat room and should not be modified or freed
  */
 char *
-muc_get_room_nick(const char * const room)
+muc_nick(const char * const room)
 {
-    if (rooms != NULL) {
-        ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-        if (chat_room != NULL) {
-            return chat_room->nick;
-        } else {
-            return NULL;
-        }
+    ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
+    if (chat_room) {
+        return chat_room->nick;
     } else {
         return NULL;
     }
@@ -414,16 +354,11 @@ muc_get_room_nick(const char * const room)
  * The password is owned by the chat room and should not be modified or freed
  */
 char *
-muc_get_room_password(const char * const room)
+muc_password(const char * const room)
 {
-    if (rooms != NULL) {
-        ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-        if (chat_room != NULL) {
-            return chat_room->password;
-        } else {
-            return NULL;
-        }
+    ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
+    if (chat_room) {
+        return chat_room->password;
     } else {
         return NULL;
     }
@@ -433,47 +368,44 @@ muc_get_room_password(const char * const room)
  * Returns TRUE if the specified nick exists in the room's roster
  */
 gboolean
-muc_nick_in_roster(const char * const room, const char * const nick)
+muc_roster_contains_nick(const char * const room, const char * const nick)
 {
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-    if (chat_room != NULL) {
-        PContact contact = g_hash_table_lookup(chat_room->roster, nick);
-        if (contact != NULL) {
-            return TRUE;
-        } else {
-            return FALSE;
-        }
+    if (chat_room) {
+        Occupant *occupant = g_hash_table_lookup(chat_room->roster, nick);
+        return (occupant != NULL);
+    } else {
+        return FALSE;
     }
-
-    return FALSE;
 }
 
 /*
  * Add a new chat room member to the room's roster
  */
 gboolean
-muc_add_to_roster(const char * const room, const char * const nick,
-    const char * const show, const char * const status)
+muc_roster_add(const char * const room, const char * const nick, const char * const role,
+    const char * const affiliation, const char * const show, const char * const status)
 {
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
     gboolean updated = FALSE;
+    resource_presence_t new_presence = resource_presence_from_string(show);
 
-    if (chat_room != NULL) {
-        PContact old = g_hash_table_lookup(chat_room->roster, nick);
+    if (chat_room) {
+        Occupant *old = g_hash_table_lookup(chat_room->roster, nick);
 
-        if (old == NULL) {
+        if (!old) {
             updated = TRUE;
             autocomplete_add(chat_room->nick_ac, nick);
-        } else if ((g_strcmp0(p_contact_presence(old), show) != 0) ||
-                    (g_strcmp0(p_contact_status(old), status) != 0)) {
+        } else if (old->presence != new_presence ||
+                    (g_strcmp0(old->status, status) != 0)) {
             updated = TRUE;
         }
-        PContact contact = p_contact_new(nick, NULL, NULL, NULL, NULL, FALSE);
-        resource_presence_t resource_presence = resource_presence_from_string(show);
-        Resource *resource = resource_new(nick, resource_presence, status, 0);
-        p_contact_set_presence(contact, resource);
-        g_hash_table_replace(chat_room->roster, strdup(nick), contact);
+
+        resource_presence_t presence = resource_presence_from_string(show);
+        muc_role_t role_t = _role_from_string(role);
+        muc_affiliation_t affiliation_t = _affiliation_from_string(affiliation);
+        Occupant *occupant = _muc_occupant_new(nick, role_t, affiliation_t, presence, status);
+        g_hash_table_replace(chat_room->roster, strdup(nick), occupant);
     }
 
     return updated;
@@ -483,27 +415,25 @@ muc_add_to_roster(const char * const room, const char * const nick,
  * Remove a room member from the room's roster
  */
 void
-muc_remove_from_roster(const char * const room, const char * const nick)
+muc_roster_remove(const char * const room, const char * const nick)
 {
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-    if (chat_room != NULL) {
+    if (chat_room) {
         g_hash_table_remove(chat_room->roster, nick);
         autocomplete_remove(chat_room->nick_ac, nick);
     }
 }
 
-PContact
-muc_get_participant(const char * const room, const char * const nick)
+Occupant *
+muc_roster_item(const char * const room, const char * const nick)
 {
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-    if (chat_room != NULL) {
-        PContact participant = g_hash_table_lookup(chat_room->roster, nick);
-        return participant;
+    if (chat_room) {
+        Occupant *occupant = g_hash_table_lookup(chat_room->roster, nick);
+        return occupant;
+    } else {
+        return NULL;
     }
-
-    return NULL;
 }
 
 /*
@@ -511,11 +441,10 @@ muc_get_participant(const char * const room, const char * const nick)
  * The list is owned by the room and must not be mofified or freed
  */
 GList *
-muc_get_roster(const char * const room)
+muc_roster(const char * const room)
 {
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-    if (chat_room != NULL) {
+    if (chat_room) {
         GList *result = NULL;
         GHashTableIter iter;
         gpointer key;
@@ -523,7 +452,7 @@ muc_get_roster(const char * const room)
 
         g_hash_table_iter_init(&iter, chat_room->roster);
         while (g_hash_table_iter_next(&iter, &key, &value)) {
-            result = g_list_insert_sorted(result, value, (GCompareFunc)_compare_participants);
+            result = g_list_insert_sorted(result, value, (GCompareFunc)_compare_occupants);
         }
 
         return result;
@@ -536,11 +465,10 @@ muc_get_roster(const char * const room)
  * Return a Autocomplete representing the room member's in the roster
  */
 Autocomplete
-muc_get_roster_ac(const char * const room)
+muc_roster_ac(const char * const room)
 {
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-    if (chat_room != NULL) {
+    if (chat_room) {
         return chat_room->nick_ac;
     } else {
         return NULL;
@@ -551,11 +479,10 @@ muc_get_roster_ac(const char * const room)
  * Set to TRUE when the rooms roster has been fully received
  */
 void
-muc_set_roster_received(const char * const room)
+muc_roster_set_complete(const char * const room)
 {
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-    if (chat_room != NULL) {
+    if (chat_room) {
         chat_room->roster_received = TRUE;
     }
 }
@@ -564,30 +491,34 @@ muc_set_roster_received(const char * const room)
  * Returns TRUE id the rooms roster has been fully received
  */
 gboolean
-muc_get_roster_received(const char * const room)
+muc_roster_complete(const char * const room)
 {
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-    if (chat_room != NULL) {
+    if (chat_room) {
         return chat_room->roster_received;
     } else {
         return FALSE;
     }
 }
 
+gboolean
+muc_occupant_available(Occupant *occupant)
+{
+    return (occupant->presence == RESOURCE_ONLINE || occupant->presence == RESOURCE_CHAT);
+}
+
 /*
  * Remove the old_nick from the roster, and flag that a pending nickname change
  * is in progress
  */
 void
-muc_set_roster_pending_nick_change(const char * const room,
+muc_roster_nick_change_start(const char * const room,
     const char * const new_nick, const char * const old_nick)
 {
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-    if (chat_room != NULL) {
+    if (chat_room) {
         g_hash_table_insert(chat_room->nick_changes, strdup(new_nick), strdup(old_nick));
-        muc_remove_from_roster(room, old_nick);
+        muc_roster_remove(room, old_nick);
     }
 }
 
@@ -598,14 +529,13 @@ muc_set_roster_pending_nick_change(const char * const room,
  * the caller
  */
 char *
-muc_complete_roster_nick_change(const char * const room,
+muc_roster_nick_change_complete(const char * const room,
     const char * const nick)
 {
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
-
-    if (chat_room != NULL) {
+    if (chat_room) {
         char *old_nick = g_hash_table_lookup(chat_room->nick_changes, nick);
-        if (old_nick != NULL) {
+        if (old_nick) {
             char *old_nick_cpy = strdup(old_nick);
             g_hash_table_remove(chat_room->nick_changes, nick);
 
@@ -619,96 +549,113 @@ muc_complete_roster_nick_change(const char * const room,
 void
 muc_autocomplete(char *input, int *size)
 {
-    if (rooms == NULL) {
-        return;
-    }
-
     char *recipient = ui_current_recipient();
     ChatRoom *chat_room = g_hash_table_lookup(rooms, recipient);
 
-    if (chat_room == NULL) {
-        return;
-    }
+    if (chat_room && chat_room->nick_ac) {
+        input[*size] = '\0';
+        char *search_str = NULL;
 
-    if (chat_room->nick_ac == NULL) {
-        return;
-    }
-
-    input[*size] = '\0';
-    char *search_str = NULL;
-
-    gchar *last_space = g_strrstr(input, " ");
-    if (last_space == NULL) {
-        search_str = input;
-        if (chat_room->autocomplete_prefix == NULL) {
-            chat_room->autocomplete_prefix = strdup("");
-        }
-    } else {
-        search_str = last_space+1;
-        if (chat_room->autocomplete_prefix == NULL) {
-            chat_room->autocomplete_prefix = g_strndup(input, search_str - input);
+        gchar *last_space = g_strrstr(input, " ");
+        if (!last_space) {
+            search_str = input;
+            if (!chat_room->autocomplete_prefix) {
+                chat_room->autocomplete_prefix = strdup("");
+            }
+        } else {
+            search_str = last_space+1;
+            if (!chat_room->autocomplete_prefix) {
+                chat_room->autocomplete_prefix = g_strndup(input, search_str - input);
+            }
         }
-    }
 
-    char *result = autocomplete_complete(chat_room->nick_ac, search_str, FALSE);
-    if (result != NULL) {
-        GString *replace_with = g_string_new(chat_room->autocomplete_prefix);
-        g_string_append(replace_with, result);
-        if (last_space == NULL || (*(last_space+1) == '\0')) {
-            g_string_append(replace_with, ": ");
+        char *result = autocomplete_complete(chat_room->nick_ac, search_str, FALSE);
+        if (result) {
+            GString *replace_with = g_string_new(chat_room->autocomplete_prefix);
+            g_string_append(replace_with, result);
+            if (!last_space || (*(last_space+1) == '\0')) {
+                g_string_append(replace_with, ": ");
+            }
+            ui_replace_input(input, replace_with->str, size);
+            g_string_free(replace_with, TRUE);
+            g_free(result);
         }
-        ui_replace_input(input, replace_with->str, size);
-        g_string_free(replace_with, TRUE);
-        g_free(result);
     }
-
-    return;
 }
 
 void
-muc_reset_autocomplete(const char * const room)
+muc_autocomplete_reset(const char * const room)
 {
-    if (rooms == NULL) {
-        return;
+    ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
+    if (chat_room) {
+        if (chat_room->nick_ac) {
+            autocomplete_reset(chat_room->nick_ac);
+        }
+
+        if (chat_room->autocomplete_prefix) {
+            free(chat_room->autocomplete_prefix);
+            chat_room->autocomplete_prefix = NULL;
+        }
     }
+}
 
+char *
+muc_role_str(const char * const room)
+{
     ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
+    if (chat_room) {
+        return _role_to_string(chat_room->role);
+    } else {
+        return "none";
+    }
+}
 
-    if (chat_room == NULL) {
-        return;
+void
+muc_set_role(const char * const room, const char * const role)
+{
+    ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
+    if (chat_room) {
+        chat_room->role = _role_from_string(role);
     }
+}
 
-    if (chat_room->nick_ac != NULL) {
-        autocomplete_reset(chat_room->nick_ac);
+char *
+muc_affiliation_str(const char * const room)
+{
+    ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
+    if (chat_room) {
+        return _affiliation_to_string(chat_room->affiliation);
+    } else {
+        return "none";
     }
+}
 
-    if (chat_room->autocomplete_prefix != NULL) {
-        free(chat_room->autocomplete_prefix);
-        chat_room->autocomplete_prefix = NULL;
+void
+muc_set_affiliation(const char * const room, const char * const affiliation)
+{
+    ChatRoom *chat_room = g_hash_table_lookup(rooms, room);
+    if (chat_room) {
+        chat_room->affiliation = _affiliation_from_string(affiliation);
     }
 }
 
 static void
 _free_room(ChatRoom *room)
 {
-    if (room != NULL) {
+    if (room) {
         free(room->room);
         free(room->nick);
         free(room->subject);
         free(room->password);
-        if (room->autocomplete_prefix != NULL) {
-            free(room->autocomplete_prefix);
-        }
-        if (room->roster != NULL) {
+        free(room->autocomplete_prefix);
+        if (room->roster) {
             g_hash_table_destroy(room->roster);
         }
-        if (room->nick_ac != NULL) {
-            autocomplete_free(room->nick_ac);
-        }
-        if (room->nick_changes != NULL) {
+        autocomplete_free(room->nick_ac);
+        if (room->nick_changes) {
             g_hash_table_destroy(room->nick_changes);
         }
-        if (room->pending_broadcasts != NULL) {
+        if (room->pending_broadcasts) {
             g_list_free_full(room->pending_broadcasts, free);
         }
         free(room);
@@ -716,10 +663,10 @@ _free_room(ChatRoom *room)
 }
 
 static
-gint _compare_participants(PContact a, PContact b)
+gint _compare_occupants(Occupant *a, Occupant *b)
 {
-    const char * utf8_str_a = p_contact_barejid(a);
-    const char * utf8_str_b = p_contact_barejid(b);
+    const char * utf8_str_a = a->nick;
+    const char * utf8_str_b = b->nick;
 
     gchar *key_a = g_utf8_collate_key(utf8_str_a, -1);
     gchar *key_b = g_utf8_collate_key(utf8_str_b, -1);
@@ -731,3 +678,133 @@ gint _compare_participants(PContact a, PContact b)
 
     return result;
 }
+
+static muc_role_t
+_role_from_string(const char * const role)
+{
+    if (role) {
+        if (g_strcmp0(role, "visitor") == 0) {
+            return MUC_ROLE_VISITOR;
+        } else if (g_strcmp0(role, "participant") == 0) {
+            return MUC_ROLE_PARTICIPANT;
+        } else if (g_strcmp0(role, "moderator") == 0) {
+            return MUC_ROLE_MODERATOR;
+        } else {
+            return MUC_ROLE_NONE;
+        }
+    } else {
+        return MUC_ROLE_NONE;
+    }
+}
+
+static char *
+_role_to_string(muc_role_t role)
+{
+    char *result = NULL;
+
+    switch (role) {
+    case MUC_ROLE_NONE:
+        result = "none";
+        break;
+    case MUC_ROLE_VISITOR:
+        result = "visitor";
+        break;
+    case MUC_ROLE_PARTICIPANT:
+        result = "participant";
+        break;
+    case MUC_ROLE_MODERATOR:
+        result = "moderator";
+        break;
+    default:
+        result = "none";
+        break;
+    }
+
+    return result;
+}
+
+static muc_affiliation_t
+_affiliation_from_string(const char * const affiliation)
+{
+    if (affiliation) {
+        if (g_strcmp0(affiliation, "outcast") == 0) {
+            return MUC_AFFILIATION_OUTCAST;
+        } else if (g_strcmp0(affiliation, "member") == 0) {
+            return MUC_AFFILIATION_MEMBER;
+        } else if (g_strcmp0(affiliation, "admin") == 0) {
+            return MUC_AFFILIATION_ADMIN;
+        } else if (g_strcmp0(affiliation, "owner") == 0) {
+            return MUC_AFFILIATION_OWNER;
+        } else {
+            return MUC_AFFILIATION_NONE;
+        }
+    } else {
+        return MUC_AFFILIATION_NONE;
+    }
+}
+
+static char *
+_affiliation_to_string(muc_affiliation_t affiliation)
+{
+    char *result = NULL;
+
+    switch (affiliation) {
+    case MUC_AFFILIATION_NONE:
+        result = "none";
+        break;
+    case MUC_AFFILIATION_OUTCAST:
+        result = "outcast";
+        break;
+    case MUC_AFFILIATION_MEMBER:
+        result = "member";
+        break;
+    case MUC_AFFILIATION_ADMIN:
+        result = "admin";
+        break;
+    case MUC_AFFILIATION_OWNER:
+        result = "owner";
+        break;
+    default:
+        result = "none";
+        break;
+    }
+
+    return result;
+}
+
+static Occupant*
+_muc_occupant_new(const char *const nick, muc_role_t role, muc_affiliation_t affiliation, resource_presence_t presence,
+    const char * const status)
+{
+    Occupant *occupant = malloc(sizeof(Occupant));
+
+    if (nick) {
+        occupant->nick = strdup(nick);
+    } else {
+        occupant->nick = NULL;
+    }
+
+    occupant->presence = presence;
+
+    if (status) {
+        occupant->status = strdup(status);
+    } else {
+        occupant->status = NULL;
+    }
+
+    occupant->role = role;
+    occupant->affiliation = affiliation;
+
+    return occupant;
+}
+
+static void
+_occupant_free(Occupant *occupant)
+{
+    if (occupant) {
+        free(occupant->nick);
+        free(occupant->status);
+        free(occupant);
+        occupant = NULL;
+    }
+}
\ No newline at end of file
diff --git a/src/muc.h b/src/muc.h
index 722e4a17..3bf836ee 100644
--- a/src/muc.h
+++ b/src/muc.h
@@ -41,56 +41,88 @@
 #include "jid.h"
 #include "tools/autocomplete.h"
 
+typedef enum {
+    MUC_ROLE_NONE,
+    MUC_ROLE_VISITOR,
+    MUC_ROLE_PARTICIPANT,
+    MUC_ROLE_MODERATOR
+} muc_role_t;
+
+typedef enum {
+    MUC_AFFILIATION_NONE,
+    MUC_AFFILIATION_OUTCAST,
+    MUC_AFFILIATION_MEMBER,
+    MUC_AFFILIATION_ADMIN,
+    MUC_AFFILIATION_OWNER
+} muc_affiliation_t;
+
+typedef struct _muc_occupant_t {
+    char *nick;
+    muc_role_t role;
+    muc_affiliation_t affiliation;
+    resource_presence_t presence;
+    char *status;
+} Occupant;
+
 void muc_init(void);
 void muc_close(void);
-void muc_join_room(const char * const room, const char * const nick,
-    const char * const password, gboolean autojoin);
-void muc_leave_room(const char * const room);
-gboolean muc_room_is_active(const char * const room);
-gboolean muc_room_is_autojoin(const char * const room);
-GList* muc_get_active_room_list(void);
-char* muc_get_room_nick(const char * const room);
-char* muc_get_room_password(const char * const room);
-
-void muc_set_room_pending_nick_change(const char * const room, const char * const new_nick);
-gboolean muc_is_room_pending_nick_change(const char * const room);
-void muc_complete_room_nick_change(const char * const room,
-    const char * const nick);
-char * muc_get_old_nick(const char * const room, const char * const new_nick);
-
-gboolean muc_add_to_roster(const char * const room, const char * const nick,
-    const char * const show, const char * const status);
-void muc_remove_from_roster(const char * const room, const char * const nick);
-GList * muc_get_roster(const char * const room);
-Autocomplete muc_get_roster_ac(const char * const room);
-gboolean muc_nick_in_roster(const char * const room, const char * const nick);
-PContact muc_get_participant(const char * const room, const char * const nick);
-void muc_set_roster_received(const char * const room);
-gboolean muc_get_roster_received(const char * const room);
-
-void muc_set_roster_pending_nick_change(const char * const room,
-    const char * const new_nick, const char * const old_nick);
-char* muc_complete_roster_nick_change(const char * const room,
-    const char * const nick);
-
-void muc_add_invite(const char *room);
-void muc_remove_invite(const char * const room);
-gint muc_invite_count(void);
-GSList* muc_get_invites(void);
-gboolean muc_invites_include(const char * const room);
-void muc_reset_invites_ac(void);
-char* muc_find_invite(char *search_str);
-void muc_clear_invites(void);
+
+void muc_join(const char * const room, const char * const nick, const char * const password, gboolean autojoin);
+void muc_leave(const char * const room);
+
+gboolean muc_active(const char * const room);
+gboolean muc_autojoin(const char * const room);
+
+GList* muc_rooms(void);
+
+char* muc_nick(const char * const room);
+char* muc_password(const char * const room);
+
+void muc_nick_change_start(const char * const room, const char * const new_nick);
+void muc_nick_change_complete(const char * const room, const char * const nick);
+gboolean muc_nick_change_pending(const char * const room);
+char* muc_old_nick(const char * const room, const char * const new_nick);
+
+gboolean muc_roster_contains_nick(const char * const room, const char * const nick);
+gboolean muc_roster_complete(const char * const room);
+gboolean muc_roster_add(const char * const room, const char * const nick, const char * const role, 
+    const char * const affiliation, const char * const show,
+    const char * const status);
+void muc_roster_remove(const char * const room, const char * const nick);
+void muc_roster_set_complete(const char * const room);
+GList * muc_roster(const char * const room);
+Autocomplete muc_roster_ac(const char * const room);
+Occupant* muc_roster_item(const char * const room, const char * const nick);
+
+gboolean muc_occupant_available(Occupant *occupant);
+
+void muc_roster_nick_change_start(const char * const room, const char * const new_nick, const char * const old_nick);
+char* muc_roster_nick_change_complete(const char * const room, const char * const nick);
+
+void muc_invites_add(const char * const room);
+void muc_invites_remove(const char * const room);
+gint muc_invites_count(void);
+GSList* muc_invites(void);
+gboolean muc_invites_contain(const char * const room);
+void muc_invites_reset_ac(void);
+char* muc_invites_find(char *search_str);
+void muc_invites_clear(void);
 
 void muc_set_subject(const char * const room, const char * const subject);
-char * muc_get_subject(const char * const room);
-void muc_add_pending_broadcast(const char * const room, const char * const message);
-GList * muc_get_pending_broadcasts(const char * const room);
+char* muc_subject(const char * const room);
+
+void muc_pending_broadcasts_add(const char * const room, const char * const message);
+GList * muc_pending_broadcasts(const char * const room);
 
 void muc_autocomplete(char *input, int *size);
-void muc_reset_autocomplete(const char * const room);
+void muc_autocomplete_reset(const char * const room);
 
 gboolean muc_requires_config(const char * const room);
 void muc_set_requires_config(const char * const room, gboolean val);
 
+void muc_set_role(const char * const room, const char * const role);
+void muc_set_affiliation(const char * const room, const char * const affiliation);
+char *muc_role_str(const char * const room);
+char *muc_affiliation_str(const char * const room);
+
 #endif
diff --git a/src/roster_list.c b/src/roster_list.c
index aba90d34..f7c865f9 100644
--- a/src/roster_list.c
+++ b/src/roster_list.c
@@ -339,7 +339,7 @@ roster_get_group(const char * const group)
 GSList *
 roster_get_groups(void)
 {
-    return autocomplete_get_list(groups_ac);
+    return autocomplete_create_list(groups_ac);
 }
 
 char *
diff --git a/src/server_events.c b/src/server_events.c
index f89385c6..4755f3fa 100644
--- a/src/server_events.c
+++ b/src/server_events.c
@@ -54,8 +54,8 @@
 void
 handle_room_join_error(const char * const room, const char * const err)
 {
-    if (muc_room_is_active(room)) {
-        muc_leave_room(room);
+    if (muc_active(room)) {
+        muc_leave(room);
     }
     ui_handle_room_join_error(room, err);
 }
@@ -109,11 +109,11 @@ handle_login_account_success(char *account_name)
     ui_handle_login_account_success(account);
 
     // attempt to rejoin rooms with passwords
-    GList *curr = muc_get_active_room_list();
+    GList *curr = muc_rooms();
     while (curr != NULL) {
-        char *password = muc_get_room_password(curr->data);
+        char *password = muc_password(curr->data);
         if (password != NULL) {
-            char *nick = muc_get_room_nick(curr->data);
+            char *nick = muc_nick(curr->data);
             presence_join_room(curr->data, nick, password);
         }
         curr = g_list_next(curr);
@@ -129,7 +129,7 @@ handle_lost_connection(void)
 {
     cons_show_error("Lost connection.");
     roster_clear();
-    muc_clear_invites();
+    muc_invites_clear();
     chat_sessions_clear();
     ui_disconnected();
 }
@@ -181,9 +181,9 @@ handle_room_invite(jabber_invite_t invite_type,
     const char * const invitor, const char * const room,
     const char * const reason)
 {
-    if (!muc_room_is_active(room) && !muc_invites_include(room)) {
+    if (!muc_active(room) && !muc_invites_contain(room)) {
         cons_show_room_invite(invitor, room, reason);
-        muc_add_invite(room);
+        muc_invites_add(room);
     }
 }
 
@@ -191,10 +191,10 @@ void
 handle_room_broadcast(const char *const room_jid,
     const char * const message)
 {
-    if (muc_get_roster_received(room_jid)) {
+    if (muc_roster_complete(room_jid)) {
         ui_room_broadcast(room_jid, message);
     } else {
-        muc_add_pending_broadcast(room_jid, message);
+        muc_pending_broadcasts_add(room_jid, message);
     }
 }
 
@@ -202,7 +202,7 @@ void
 handle_room_subject(const char * const room_jid, const char * const subject)
 {
     muc_set_subject(room_jid, subject);
-    if (muc_get_roster_received(room_jid)) {
+    if (muc_roster_complete(room_jid)) {
         ui_room_subject(room_jid, subject);
     }
 }
@@ -449,13 +449,14 @@ handle_contact_online(char *barejid, Resource *resource,
 void
 handle_leave_room(const char * const room)
 {
-    muc_leave_room(room);
+    muc_leave(room);
 }
 
 void
 handle_room_nick_change(const char * const room,
     const char * const nick)
 {
+    muc_nick_change_complete(room, nick);
     ui_room_nick_change(room, nick);
 }
 
@@ -469,7 +470,7 @@ handle_room_requires_config(const char * const room)
 void
 handle_room_destroy(const char * const room)
 {
-    muc_leave_room(room);
+    muc_leave(room);
     ui_room_destroyed(room);
 }
 
@@ -500,22 +501,22 @@ handle_room_config_submit_result_error(const char * const room, const char * con
 void
 handle_room_roster_complete(const char * const room)
 {
-    if (muc_room_is_autojoin(room)) {
+    if (muc_autojoin(room)) {
         ui_room_join(room, FALSE);
     } else {
         ui_room_join(room, TRUE);
     }
-    muc_remove_invite(room);
-    muc_set_roster_received(room);
-    GList *roster = muc_get_roster(room);
+    muc_invites_remove(room);
+    muc_roster_set_complete(room);
+    GList *roster = muc_roster(room);
     ui_room_roster(room, roster, NULL);
 
-    char *subject = muc_get_subject(room);
+    char *subject = muc_subject(room);
     if (subject != NULL) {
         ui_room_subject(room, subject);
     }
 
-    GList *pending_broadcasts = muc_get_pending_broadcasts(room);
+    GList *pending_broadcasts = muc_pending_broadcasts(room);
     if (pending_broadcasts != NULL) {
         GList *curr = pending_broadcasts;
         while (curr != NULL) {
@@ -526,11 +527,10 @@ handle_room_roster_complete(const char * const room)
 }
 
 void
-handle_room_member_presence(const char * const room,
-    const char * const nick, const char * const show,
-    const char * const status)
+handle_room_member_presence(const char * const room, const char * const nick, const char * const role, 
+    const char *  const affiliation, const char * const show, const char * const status)
 {
-    gboolean updated = muc_add_to_roster(room, nick, show, status);
+    gboolean updated = muc_roster_add(room, nick, role, affiliation, show, status);
 
     if (updated) {
         char *muc_status_pref = prefs_get_string(PREF_STATUSES_MUC);
@@ -542,10 +542,10 @@ handle_room_member_presence(const char * const room,
 }
 
 void
-handle_room_member_online(const char * const room, const char * const nick,
-    const char * const show, const char * const status)
+handle_room_member_online(const char * const room, const char * const nick, const char * const role,
+    const char * const affiliation, const char * const show, const char * const status)
 {
-    muc_add_to_roster(room, nick, show, status);
+    muc_roster_add(room, nick, role, affiliation, show, status);
 
     char *muc_status_pref = prefs_get_string(PREF_STATUSES_MUC);
     if (g_strcmp0(muc_status_pref, "none") != 0) {
@@ -558,7 +558,7 @@ void
 handle_room_member_offline(const char * const room, const char * const nick,
     const char * const show, const char * const status)
 {
-    muc_remove_from_roster(room, nick);
+    muc_roster_remove(room, nick);
 
     char *muc_status_pref = prefs_get_string(PREF_STATUSES_MUC);
     if (g_strcmp0(muc_status_pref, "none") != 0) {
diff --git a/src/server_events.h b/src/server_events.h
index 41506cdb..6990e8bf 100644
--- a/src/server_events.h
+++ b/src/server_events.h
@@ -73,11 +73,10 @@ void handle_room_nick_change(const char * const room,
 void handle_room_requires_config(const char * const room);
 void handle_room_destroy(const char * const room);
 void handle_room_roster_complete(const char * const room);
-void handle_room_member_presence(const char * const room,
-    const char * const nick, const char * const show,
-    const char * const status);
-void handle_room_member_online(const char * const room, const char * const nick,
-    const char * const show, const char * const status);
+void handle_room_member_presence(const char * const room, const char * const nick, const char * const role,
+    const char * const affiliation, const char * const show, const char * const status);
+void handle_room_member_online(const char * const room, const char * const nick, const char * const role,
+    const char * const affiliation, const char * const show, const char * const status);
 void handle_room_member_offline(const char * const room, const char * const nick,
     const char * const show, const char * const status);
 void handle_room_member_nick_change(const char * const room,
diff --git a/src/tools/autocomplete.c b/src/tools/autocomplete.c
index 0fe8f166..486fd2ba 100644
--- a/src/tools/autocomplete.c
+++ b/src/tools/autocomplete.c
@@ -62,7 +62,7 @@ autocomplete_new(void)
 void
 autocomplete_clear(Autocomplete ac)
 {
-    if (ac != NULL) {
+    if (ac) {
         g_slist_free_full(ac->items, free);
         ac->items = NULL;
 
@@ -80,16 +80,18 @@ autocomplete_reset(Autocomplete ac)
 void
 autocomplete_free(Autocomplete ac)
 {
-    autocomplete_clear(ac);
-    free(ac);
+    if (ac) {
+        autocomplete_clear(ac);
+        free(ac);
+    }
 }
 
 gint
 autocomplete_length(Autocomplete ac)
 {
-    if (ac == NULL) {
+    if (!ac) {
         return 0;
-    } else if (ac->items == NULL) {
+    } else if (!ac->items) {
         return 0;
     } else {
         return g_slist_length(ac->items);
@@ -99,25 +101,26 @@ autocomplete_length(Autocomplete ac)
 void
 autocomplete_add(Autocomplete ac, const char *item)
 {
-    if (ac != NULL) {
+    if (ac) {
         char *item_cpy;
         GSList *curr = g_slist_find_custom(ac->items, item, (GCompareFunc)strcmp);
 
         // if item already exists
-        if (curr != NULL) {
+        if (curr) {
             return;
         }
 
         item_cpy = strdup(item);
         ac->items = g_slist_insert_sorted(ac->items, item_cpy, (GCompareFunc)strcmp);
     }
+
     return;
 }
 
 void
 autocomplete_remove(Autocomplete ac, const char * const item)
 {
-    if (ac != NULL) {
+    if (ac) {
         GSList *curr = g_slist_find_custom(ac->items, item, (GCompareFunc)strcmp);
 
         if (!curr) {
@@ -137,7 +140,7 @@ autocomplete_remove(Autocomplete ac, const char * const item)
 }
 
 GSList *
-autocomplete_get_list(Autocomplete ac)
+autocomplete_create_list(Autocomplete ac)
 {
     GSList *copy = NULL;
     GSList *curr = ac->items;
@@ -171,36 +174,43 @@ autocomplete_complete(Autocomplete ac, gchar *search_str, gboolean quote)
     gchar *found = NULL;
 
     // no autocomplete to search
-    if (ac == NULL)
+    if (!ac) {
         return NULL;
+    }
 
     // no items to search
-    if (!ac->items)
+    if (!ac->items) {
         return NULL;
+    }
 
     // first search attempt
-    if (ac->last_found == NULL) {
-        if (ac->search_str != NULL) {
+    if (!ac->last_found) {
+        if (ac->search_str) {
             FREE_SET_NULL(ac->search_str);
         }
+
         ac->search_str = strdup(search_str);
         found = _search_from(ac, ac->items, quote);
+
         return found;
 
     // subsequent search attempt
     } else {
         // search from here+1 to end
         found = _search_from(ac, g_slist_next(ac->last_found), quote);
-        if (found != NULL)
+        if (found) {
             return found;
+        }
 
         // search from beginning
         found = _search_from(ac, ac->items, quote);
-        if (found != NULL)
+        if (found) {
             return found;
+        }
 
         // we found nothing, reset search
         autocomplete_reset(ac);
+
         return NULL;
     }
 }
@@ -224,7 +234,7 @@ autocomplete_param_with_func(char *input, int *size, char *command,
         inp_cpy[(*size) - len] = '\0';
 
         char *found = func(inp_cpy);
-        if (found != NULL) {
+        if (found) {
             auto_msg = g_string_new(command_cpy);
             g_string_append(auto_msg, found);
             free(found);
@@ -254,7 +264,7 @@ autocomplete_param_with_ac(char *input, int *size, char *command,
         inp_cpy[(*size) - len] = '\0';
 
         char *found = autocomplete_complete(ac, inp_cpy, quote);
-        if (found != NULL) {
+        if (found) {
             auto_msg = g_string_new(command_cpy);
             g_string_append(auto_msg, found);
             free(found);
@@ -292,9 +302,9 @@ autocomplete_param_no_with_func(char *input, int *size, char *command,
             gchar *comp_str = g_strdup(&inp_cpy[strlen(start_str)]);
 
             // autocomplete param
-            if (comp_str != NULL) {
+            if (comp_str) {
                 char *found = func(comp_str);
-                if (found != NULL) {
+                if (found) {
                     result_str = g_string_new("");
                     g_string_append(result_str, start_str);
                     g_string_append(result_str, found);
diff --git a/src/tools/autocomplete.h b/src/tools/autocomplete.h
index 9e2be30c..a029b7ef 100644
--- a/src/tools/autocomplete.h
+++ b/src/tools/autocomplete.h
@@ -55,7 +55,7 @@ void autocomplete_remove(Autocomplete ac, const char * const item);
 // find the next item prefixed with search string
 gchar * autocomplete_complete(Autocomplete ac, gchar *search_str, gboolean quote);
 
-GSList * autocomplete_get_list(Autocomplete ac);
+GSList * autocomplete_create_list(Autocomplete ac);
 gint autocomplete_length(Autocomplete ac);
 
 char * autocomplete_param_with_func(char *input, int *size, char *command,
diff --git a/src/ui/console.c b/src/ui/console.c
index 5c0a4e53..a34ff71d 100644
--- a/src/ui/console.c
+++ b/src/ui/console.c
@@ -32,6 +32,7 @@
  *
  */
 
+
 #include <string.h>
 #include <stdlib.h>
 
@@ -282,14 +283,14 @@ _cons_show_info(PContact pcontact)
 }
 
 static void
-_cons_show_caps(const char * const fulljid, Resource *resource)
+_cons_show_caps(const char * const fulljid, resource_presence_t presence)
 {
     ProfWin *console = wins_get_console();
     cons_show("");
 
     Capabilities *caps = caps_lookup(fulljid);
     if (caps) {
-        const char *resource_presence = string_from_resource_presence(resource->presence);
+        const char *resource_presence = string_from_resource_presence(presence);
 
         int presence_colour = win_presence_colour(resource_presence);
         win_save_vprint(console, '-', NULL, NO_EOL, presence_colour, "", "%s", fulljid);
@@ -454,7 +455,7 @@ _cons_show_bookmarks(const GList *list)
 
             int presence_colour = 0;
 
-            if (muc_room_is_active(item->jid)) {
+            if (muc_active(item->jid)) {
                 presence_colour = COLOUR_ONLINE;
             }
             win_save_vprint(console, '-', NULL, NO_EOL, presence_colour, "", "  %s", item->jid);
@@ -467,7 +468,7 @@ _cons_show_bookmarks(const GList *list)
             if (item->password != NULL) {
                 win_save_print(console, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " (private)");
             }
-            if (muc_room_is_active(item->jid)) {
+            if (muc_active(item->jid)) {
                 ProfWin *roomwin = wins_get_by_recipient(item->jid);
                 if (roomwin != NULL) {
                     int num = wins_get_num(roomwin);
diff --git a/src/ui/core.c b/src/ui/core.c
index b57527c2..dc4ab885 100644
--- a/src/ui/core.c
+++ b/src/ui/core.c
@@ -1207,7 +1207,7 @@ _ui_new_chat_win(const char * const to)
     if (window == NULL) {
         Jid *jid = jid_create(to);
 
-        if (muc_room_is_active(jid->barejid)) {
+        if (muc_active(jid->barejid)) {
             window = wins_new(to, WIN_PRIVATE);
         } else {
             window = wins_new(to, WIN_CHAT);
@@ -1324,7 +1324,7 @@ _ui_outgoing_msg(const char * const from, const char * const to,
     if (window == NULL) {
         Jid *jid = jid_create(to);
 
-        if (muc_room_is_active(jid->barejid)) {
+        if (muc_active(jid->barejid)) {
             window = wins_new(to, WIN_PRIVATE);
         } else {
             window = wins_new(to, WIN_CHAT);
@@ -1377,7 +1377,7 @@ _ui_room_join(const char * const room, gboolean focus)
     } else {
         status_bar_active(num);
         ProfWin *console = wins_get_console();
-        char *nick = muc_get_room_nick(room);
+        char *nick = muc_nick(room);
         win_save_vprint(console, '!', NULL, 0, COLOUR_TYPING, "", "-> Autojoined %s as %s (%d).", room, nick, num);
     }
 }
@@ -1393,26 +1393,25 @@ _ui_room_roster(const char * const room, GList *roster, const char * const prese
             if (presence == NULL) {
                 win_save_print(window, '!', NULL, 0, COLOUR_ROOMINFO, "", "Room is empty.");
             } else {
-                win_save_vprint(window, '!', NULL, 0, COLOUR_ROOMINFO, "", "No participants %s.", presence);
+                win_save_vprint(window, '!', NULL, 0, COLOUR_ROOMINFO, "", "No occupants %s.", presence);
             }
         } else {
             int length = g_list_length(roster);
             if (presence == NULL) {
                 length++;
-                win_save_vprint(window, '!', NULL, NO_EOL, COLOUR_ROOMINFO, "", "%d participants: ", length);
-                win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, COLOUR_ONLINE, "", "%s", muc_get_room_nick(room));
+                win_save_vprint(window, '!', NULL, NO_EOL, COLOUR_ROOMINFO, "", "%d occupants: ", length);
+                win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, COLOUR_ONLINE, "", "%s", muc_nick(room));
                 win_save_print(window, '!', NULL, NO_DATE | NO_EOL, 0, "", ", ");
             } else {
                 win_save_vprint(window, '!', NULL, NO_EOL, COLOUR_ROOMINFO, "", "%d %s: ", length, presence);
             }
 
             while (roster != NULL) {
-                PContact member = roster->data;
-                const char *nick = p_contact_barejid(member);
-                const char *show = p_contact_presence(member);
+                Occupant *occupant = roster->data;
+                const char *presence_str = string_from_resource_presence(occupant->presence);
 
-                int presence_colour = win_presence_colour(show);
-                win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, presence_colour, "", "%s", nick);
+                int presence_colour = win_presence_colour(presence_str);
+                win_save_vprint(window, '!', NULL, NO_DATE | NO_EOL, presence_colour, "", "%s", occupant->nick);
 
                 if (roster->next != NULL) {
                     win_save_print(window, '!', NULL, NO_DATE | NO_EOL, 0, "", ", ");
@@ -1531,7 +1530,7 @@ _ui_room_message(const char * const room_jid, const char * const nick,
         log_error("Room message received from %s, but no window open for %s", nick, room_jid);
     } else {
         int num = wins_get_num(window);
-        char *my_nick = muc_get_room_nick(room_jid);
+        char *my_nick = muc_nick(room_jid);
 
         if (strcmp(nick, my_nick) != 0) {
             if (g_strrstr(message, my_nick) != NULL) {
@@ -1566,7 +1565,7 @@ _ui_room_message(const char * const room_jid, const char * const nick,
             ui_index = 0;
         }
 
-        if (strcmp(nick, muc_get_room_nick(room_jid)) != 0) {
+        if (strcmp(nick, muc_nick(room_jid)) != 0) {
             if (prefs_get_boolean(PREF_BEEP)) {
                 beep();
             }
@@ -1731,11 +1730,11 @@ static void
 _ui_status_private(void)
 {
     Jid *jid = jid_create(ui_current_recipient());
-    PContact pcontact = muc_get_participant(jid->barejid, jid->resourcepart);
+    Occupant *occupant = muc_roster_item(jid->barejid, jid->resourcepart);
     ProfWin *window = wins_get_current();
 
-    if (pcontact != NULL) {
-        win_show_contact(window, pcontact);
+    if (occupant) {
+        win_show_occupant(window, occupant);
     } else {
         win_save_println(window, "Error getting contact info.");
     }
@@ -1747,11 +1746,11 @@ static void
 _ui_info_private(void)
 {
     Jid *jid = jid_create(ui_current_recipient());
-    PContact pcontact = muc_get_participant(jid->barejid, jid->resourcepart);
+    Occupant *occupant = muc_roster_item(jid->barejid, jid->resourcepart);
     ProfWin *window = wins_get_current();
 
-    if (pcontact != NULL) {
-        win_show_info(window, pcontact);
+    if (occupant) {
+        win_show_occupant_info(window, jid->barejid, occupant);
     } else {
         win_save_println(window, "Error getting contact info.");
     }
@@ -1762,27 +1761,21 @@ _ui_info_private(void)
 static void
 _ui_status_room(const char * const contact)
 {
-    PContact pcontact = muc_get_participant(ui_current_recipient(), contact);
+    Occupant *occupant = muc_roster_item(ui_current_recipient(), contact);
     ProfWin *current = wins_get_current();
 
-    if (pcontact != NULL) {
-        win_show_contact(current, pcontact);
+    if (occupant) {
+        win_show_occupant(current, occupant);
     } else {
         win_save_vprint(current, '-', NULL, 0, 0, "", "No such participant \"%s\" in room.", contact);
     }
 }
 
 static void
-_ui_info_room(const char * const contact)
+_ui_info_room(const char * const room, Occupant *occupant)
 {
-    PContact pcontact = muc_get_participant(ui_current_recipient(), contact);
     ProfWin *current = wins_get_current();
-
-    if (pcontact != NULL) {
-        win_show_info(current, pcontact);
-    } else {
-        win_save_vprint(current, '-', NULL, 0, 0, "", "No such participant \"%s\" in room.", contact);
-    }
+    win_show_occupant_info(current, room, occupant);
 }
 
 static gint
diff --git a/src/ui/notifier.c b/src/ui/notifier.c
index 6673edb0..81320383 100644
--- a/src/ui/notifier.c
+++ b/src/ui/notifier.c
@@ -129,7 +129,7 @@ static void
 _notify_remind(void)
 {
     gint unread = ui_unread();
-    gint open = muc_invite_count();
+    gint open = muc_invites_count();
     gint subs = presence_sub_request_count();
 
     GString *text = g_string_new("");
diff --git a/src/ui/ui.h b/src/ui/ui.h
index 82327d03..d71a78c1 100644
--- a/src/ui/ui.h
+++ b/src/ui/ui.h
@@ -172,7 +172,7 @@ void (*ui_show_lines)(ProfWin *window, const gchar** lines);
 
 // contact status functions
 void (*ui_status_room)(const char * const contact);
-void (*ui_info_room)(const char * const contact);
+void (*ui_info_room)(const char * const room, Occupant *occupant);
 void (*ui_status)(void);
 void (*ui_info)(void);
 void (*ui_status_private)(void);
@@ -234,7 +234,7 @@ void (*cons_show_roster_group)(const char * const group, GSList * list);
 void (*cons_show_wins)(void);
 void (*cons_show_status)(const char * const barejid);
 void (*cons_show_info)(PContact pcontact);
-void (*cons_show_caps)(const char * const fulljid, Resource *resource);
+void (*cons_show_caps)(const char * const fulljid, resource_presence_t presence);
 void (*cons_show_themes)(GSList *themes);
 void (*cons_show_aliases)(GList *aliases);
 void (*cons_show_login_success)(ProfAccount *account);
diff --git a/src/ui/window.c b/src/ui/window.c
index b9d2bee4..4a922c59 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -126,6 +126,23 @@ win_presence_colour(const char * const presence)
 }
 
 void
+win_show_occupant(ProfWin *window, Occupant *occupant)
+{
+    const char *presence_str = string_from_resource_presence(occupant->presence);
+
+    int presence_colour = win_presence_colour(presence_str);
+
+    win_save_print(window, '-', NULL, NO_EOL, presence_colour, "", occupant->nick);
+    win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " is %s", presence_str);
+
+    if (occupant->status) {
+        win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", \"%s\"", occupant->status);
+    }
+
+    win_save_print(window, '-', NULL, NO_DATE, presence_colour, "", "");
+}
+
+void
 win_show_contact(ProfWin *window, PContact contact)
 {
     const char *barejid = p_contact_barejid(contact);
@@ -170,6 +187,70 @@ win_show_contact(ProfWin *window, PContact contact)
 }
 
 void
+win_show_occupant_info(ProfWin *window, const char * const room, Occupant *occupant)
+{
+    const char *presence_str = string_from_resource_presence(occupant->presence);
+
+    int presence_colour = win_presence_colour(presence_str);
+
+    win_save_print(window, '-', NULL, NO_EOL, presence_colour, "", occupant->nick);
+    win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", " is %s", presence_str);
+
+    if (occupant->status) {
+        win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, presence_colour, "", ", \"%s\"", occupant->status);
+    }
+
+    win_save_newline(window);
+
+    win_save_vprint(window, '-', NULL, NO_DATE, 0, "", "Role: %s, Affiliation: %s", occupant->role, occupant->affiliation);
+
+    Jid *jidp = jid_create_from_bare_and_resource(room, occupant->nick);
+    Capabilities *caps = caps_lookup(jidp->fulljid);
+
+    if (caps) {
+        // show identity
+        if ((caps->category != NULL) || (caps->type != NULL) || (caps->name != NULL)) {
+            win_save_print(window, '-', NULL, NO_EOL, 0, "", "    Identity: ");
+            if (caps->name != NULL) {
+                win_save_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->name);
+                if ((caps->category != NULL) || (caps->type != NULL)) {
+                    win_save_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", " ");
+                }
+            }
+            if (caps->type != NULL) {
+                win_save_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->type);
+                if (caps->category != NULL) {
+                    win_save_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", " ");
+                }
+            }
+            if (caps->category != NULL) {
+                win_save_print(window, '-', NULL, NO_DATE | NO_EOL, 0, "", caps->category);
+            }
+            win_save_newline(window);
+        }
+        if (caps->software != NULL) {
+            win_save_vprint(window, '-', NULL, NO_EOL, 0, "", "    Software: %s", caps->software);
+        }
+        if (caps->software_version != NULL) {
+            win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->software_version);
+        }
+        if ((caps->software != NULL) || (caps->software_version != NULL)) {
+            win_save_newline(window);
+        }
+        if (caps->os != NULL) {
+            win_save_vprint(window, '-', NULL, NO_EOL, 0, "", "    OS: %s", caps->os);
+        }
+        if (caps->os_version != NULL) {
+            win_save_vprint(window, '-', NULL, NO_DATE | NO_EOL, 0, "", ", %s", caps->os_version);
+        }
+        if ((caps->os != NULL) || (caps->os_version != NULL)) {
+            win_save_newline(window);
+        }
+        caps_destroy(caps);
+    }
+}
+
+void
 win_show_info(ProfWin *window, PContact contact)
 {
     const char *barejid = p_contact_barejid(contact);
diff --git a/src/ui/window.h b/src/ui/window.h
index 8150fac9..804ea300 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -44,6 +44,7 @@
 #endif
 
 #include "contact.h"
+#include "muc.h"
 #include "ui/buffer.h"
 #include "xmpp/xmpp.h"
 
@@ -85,6 +86,7 @@ void win_update_virtual(ProfWin *window);
 void win_move_to_end(ProfWin *window);
 int  win_presence_colour(const char * const presence);
 void win_show_contact(ProfWin *window, PContact contact);
+void win_show_occupant(ProfWin *window, Occupant *occupant);
 void win_show_status_string(ProfWin *window, const char * const from,
     const char * const show, const char * const status,
     GDateTime *last_activity, const char * const pre,
@@ -92,6 +94,7 @@ void win_show_status_string(ProfWin *window, const char * const from,
 void win_print_incoming_message(ProfWin *window, GTimeVal *tv_stamp,
     const char * const from, const char * const message);
 void win_show_info(ProfWin *window, PContact contact);
+void win_show_occupant_info(ProfWin *window, const char * const room, Occupant *occupant);
 void win_save_vprint(ProfWin *window, const char show_char, GTimeVal *tstamp, int flags, int attrs, const char * const from, const char * const message, ...);
 void win_save_print(ProfWin *window, const char show_char, GTimeVal *tstamp, int flags, int attrs, const char * const from, const char * const message);
 void win_save_println(ProfWin *window, const char * const message);
diff --git a/src/xmpp/bookmark.c b/src/xmpp/bookmark.c
index b4931b8f..b5d495a6 100644
--- a/src/xmpp/bookmark.c
+++ b/src/xmpp/bookmark.c
@@ -77,9 +77,7 @@ bookmark_request(void)
     id = strdup("bookmark_init_request");
 
     autojoin_count = 0;
-    if (bookmark_ac != NULL) {
-        autocomplete_free(bookmark_ac);
-    }
+    autocomplete_free(bookmark_ac);
     bookmark_ac = autocomplete_new();
     if (bookmark_list != NULL) {
         g_list_free_full(bookmark_list, _bookmark_item_destroy);
@@ -180,15 +178,15 @@ _bookmark_join(const char *jid)
         char *account_name = jabber_get_account_name();
         ProfAccount *account = accounts_get_account(account_name);
         Bookmark *item = found->data;
-        if (!muc_room_is_active(item->jid)) {
+        if (!muc_active(item->jid)) {
             char *nick = item->nick;
             if (nick == NULL) {
                 nick = account->muc_nick;
             }
             presence_join_room(item->jid, nick, item->password);
-            muc_join_room(item->jid, nick, item->password, FALSE);
+            muc_join(item->jid, nick, item->password, FALSE);
             account_free(account);
-        } else if (muc_get_roster_received(item->jid)) {
+        } else if (muc_roster_complete(item->jid)) {
             ui_room_join(item->jid, TRUE);
         }
         return TRUE;
@@ -345,9 +343,9 @@ _bookmark_handle_result(xmpp_conn_t * const conn,
 
                 log_debug("Autojoin %s with nick=%s", jid, name);
                 room_jid = jid_create_from_bare_and_resource(jid, name);
-                if (!muc_room_is_active(room_jid->barejid)) {
+                if (!muc_active(room_jid->barejid)) {
                     presence_join_room(jid, name, password);
-                    muc_join_room(jid, name, password, TRUE);
+                    muc_join(jid, name, password, TRUE);
                 }
                 jid_destroy(room_jid);
                 account_free(account);
diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c
index 4a354a23..10355332 100644
--- a/src/xmpp/iq.c
+++ b/src/xmpp/iq.c
@@ -493,16 +493,17 @@ _version_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
         os_str = xmpp_stanza_get_text(os);
     }
 
-    PContact contact;
     Jid *jidp = jid_create(jid);
-    if (muc_room_is_active(jidp->barejid)) {
-        contact = muc_get_participant(jidp->barejid, jidp->resourcepart);
+    const char *presence = NULL;
+    if (muc_active(jidp->barejid)) {
+        Occupant *occupant = muc_roster_item(jidp->barejid, jidp->resourcepart);
+        presence = string_from_resource_presence(occupant->presence);
     } else {
-        contact = roster_get_contact(jidp->barejid);
+        PContact contact = roster_get_contact(jidp->barejid);
+        Resource *resource = p_contact_get_resource(contact, jidp->resourcepart);
+        presence = string_from_resource_presence(resource->presence);
     }
 
-    Resource *resource = p_contact_get_resource(contact, jidp->resourcepart);
-    const char *presence = string_from_resource_presence(resource->presence);
     handle_software_version_result(jid, presence, name_str, version_str, os_str);
 
     jid_destroy(jidp);
diff --git a/src/xmpp/message.c b/src/xmpp/message.c
index 0e80f9b5..f3940067 100644
--- a/src/xmpp/message.c
+++ b/src/xmpp/message.c
@@ -392,7 +392,7 @@ _groupchat_handler(xmpp_conn_t * const conn,
     }
 
     // room not active in profanity
-    if (!muc_room_is_active(jid->barejid)) {
+    if (!muc_active(jid->barejid)) {
         log_error("Message received for inactive chat room: %s", jid->str);
         jid_destroy(jid);
         return 1;
@@ -444,7 +444,7 @@ _chat_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
         return 1;
 
     // private message from chat room use full jid (room/nick)
-    } else if (muc_room_is_active(jid->barejid)) {
+    } else if (muc_active(jid->barejid)) {
         // determine if the notifications happened whilst offline
         GTimeVal tv_stamp;
         gboolean delayed = stanza_get_delay(stanza, &tv_stamp);
diff --git a/src/xmpp/presence.c b/src/xmpp/presence.c
index 54c9d68c..cfbf18fe 100644
--- a/src/xmpp/presence.c
+++ b/src/xmpp/presence.c
@@ -142,7 +142,7 @@ _presence_subscription(const char * const jid, const jabber_subscr_t action)
 static GSList *
 _presence_get_subscription_requests(void)
 {
-    return autocomplete_get_list(sub_requests_ac);
+    return autocomplete_create_list(sub_requests_ac);
 }
 
 static gint
@@ -167,7 +167,7 @@ static gboolean
 _presence_sub_request_exists(const char * const bare_jid)
 {
     gboolean result = FALSE;
-    GSList *requests_p = autocomplete_get_list(sub_requests_ac);
+    GSList *requests_p = autocomplete_create_list(sub_requests_ac);
     GSList *requests = requests_p;
 
     while (requests != NULL) {
@@ -242,12 +242,12 @@ _presence_update(const resource_presence_t presence_type, const char * const msg
 static void
 _send_room_presence(xmpp_conn_t *conn, xmpp_stanza_t *presence)
 {
-    GList *rooms_p = muc_get_active_room_list();
+    GList *rooms_p = muc_rooms();
     GList *rooms = rooms_p;
 
     while (rooms != NULL) {
         const char *room = rooms->data;
-        const char *nick = muc_get_room_nick(room);
+        const char *nick = muc_nick(room);
 
         if (nick != NULL) {
             char *full_room_jid = create_fulljid(room, nick);
@@ -331,7 +331,7 @@ _presence_leave_chat_room(const char * const room_jid)
     log_debug("Sending room leave presence to: %s", room_jid);
     xmpp_ctx_t *ctx = connection_get_ctx();
     xmpp_conn_t *conn = connection_get_conn();
-    char *nick = muc_get_room_nick(room_jid);
+    char *nick = muc_nick(room_jid);
 
     if (nick != NULL) {
         xmpp_stanza_t *presence = stanza_create_room_leave_presence(ctx, room_jid,
@@ -697,27 +697,43 @@ _muc_user_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
         char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
         char *new_nick = stanza_get_new_nick(stanza);
 
+        // self unavailable
         if ((type != NULL) && (strcmp(type, STANZA_TYPE_UNAVAILABLE) == 0)) {
 
             // leave room if not self nick change
             if (new_nick != NULL) {
-                muc_set_room_pending_nick_change(from_room, new_nick);
+                muc_nick_change_start(from_room, new_nick);
             } else {
                 handle_leave_room(from_room);
             }
 
-        // handle self nick change
-        } else if (muc_is_room_pending_nick_change(from_room)) {
-            muc_complete_room_nick_change(from_room, from_nick);
-            handle_room_nick_change(from_room, from_nick);
+        // self online
+        } else {
+
+            // handle self nick change
+            if (muc_nick_change_pending(from_room)) {
+                handle_room_nick_change(from_room, from_nick);
 
-        // handle roster complete
-        } else if (!muc_get_roster_received(from_room)) {
-            handle_room_roster_complete(from_room);
+            // handle roster complete
+            } else if (!muc_roster_complete(from_room)) {
+                handle_room_roster_complete(from_room);
 
-            // room configuration required
-            if (stanza_muc_requires_config(stanza)) {
-                handle_room_requires_config(from_room);
+                // room configuration required
+                if (stanza_muc_requires_config(stanza)) {
+                    handle_room_requires_config(from_room);
+                }
+            }
+
+            // set own affiliation and role
+            xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_MUC_USER);
+            if (x) {
+                xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(x, STANZA_NAME_ITEM);
+                if (item) {
+                    char *role = xmpp_stanza_get_attribute(item, "role");
+                    muc_set_role(from_room, role);
+                    char *affiliation = xmpp_stanza_get_attribute(item, "affiliation");
+                    muc_set_affiliation(from_room, affiliation);
+                }
             }
         }
 
@@ -736,7 +752,7 @@ _muc_user_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
             if (stanza_is_room_nick_change(stanza)) {
                 char *new_nick = stanza_get_new_nick(stanza);
                 if (new_nick != NULL) {
-                    muc_set_roster_pending_nick_change(from_room, new_nick, from_nick);
+                    muc_roster_nick_change_start(from_room, new_nick, from_nick);
                     free(new_nick);
                 }
             } else {
@@ -750,20 +766,32 @@ _muc_user_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza,
             }
 
             char *show_str = stanza_get_show(stanza, "online");
-            if (!muc_get_roster_received(from_room)) {
-                muc_add_to_roster(from_room, from_nick, show_str, status_str);
+            char *role = NULL;
+            char *affiliation = NULL;
+
+            xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_MUC_USER);
+            if (x) {
+                xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(x, STANZA_NAME_ITEM);
+                if (item) {
+                    role = xmpp_stanza_get_attribute(item, "role");
+                    affiliation = xmpp_stanza_get_attribute(item, "affiliation");
+                }
+            }
+
+            if (!muc_roster_complete(from_room)) {
+                muc_roster_add(from_room, from_nick, role, affiliation, show_str, status_str);
             } else {
-                char *old_nick = muc_complete_roster_nick_change(from_room, from_nick);
+                char *old_nick = muc_roster_nick_change_complete(from_room, from_nick);
 
                 if (old_nick != NULL) {
-                    muc_add_to_roster(from_room, from_nick, show_str, status_str);
+                    muc_roster_add(from_room, from_nick, role, affiliation, show_str, status_str);
                     handle_room_member_nick_change(from_room, old_nick, from_nick);
                     free(old_nick);
                 } else {
-                    if (!muc_nick_in_roster(from_room, from_nick)) {
-                        handle_room_member_online(from_room, from_nick, show_str, status_str);
+                    if (!muc_roster_contains_nick(from_room, from_nick)) {
+                        handle_room_member_online(from_room, from_nick, role, affiliation, show_str, status_str);
                     } else {
-                        handle_room_member_presence(from_room, from_nick, show_str, status_str);
+                        handle_room_member_presence(from_room, from_nick, role, affiliation, show_str, status_str);
                     }
                 }
             }
diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c
index 2d99c0f0..ff31bc65 100644
--- a/src/xmpp/stanza.c
+++ b/src/xmpp/stanza.c
@@ -822,11 +822,11 @@ stanza_is_muc_self_presence(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 110 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, "110") == 0) {
@@ -838,9 +838,9 @@ stanza_is_muc_self_presence(xmpp_stanza_t * const stanza,
 
         // check for item child element with jid property
         xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(x, STANZA_NAME_ITEM);
-        if (item != NULL) {
+        if (item) {
             char *jid = xmpp_stanza_get_attribute(item, STANZA_ATTR_JID);
-            if (jid != NULL) {
+            if (jid) {
                 if (g_str_has_prefix(self_jid, jid)) {
                     return TRUE;
                 }
@@ -849,21 +849,21 @@ stanza_is_muc_self_presence(xmpp_stanza_t * const stanza,
 
         // check if 'from' attribute identifies this user
         char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
-        if (from != NULL) {
+        if (from) {
             Jid *from_jid = jid_create(from);
-            if (muc_room_is_active(from_jid->barejid)) {
-                char *nick = muc_get_room_nick(from_jid->barejid);
+            if (muc_active(from_jid->barejid)) {
+                char *nick = muc_nick(from_jid->barejid);
                 if (g_strcmp0(from_jid->resourcepart, nick) == 0) {
                     return TRUE;
                 }
             }
 
             // check if a new nickname maps to a pending nick change for this user
-            if (muc_is_room_pending_nick_change(from_jid->barejid)) {
+            if (muc_nick_change_pending(from_jid->barejid)) {
                 char *new_nick = from_jid->resourcepart;
-                if (new_nick != NULL) {
-                    char *nick = muc_get_room_nick(from_jid->barejid);
-                    char *old_nick = muc_get_old_nick(from_jid->barejid, new_nick);
+                if (new_nick) {
+                    char *nick = muc_nick(from_jid->barejid);
+                    char *old_nick = muc_old_nick(from_jid->barejid, new_nick);
                     if (g_strcmp0(old_nick, nick) == 0) {
                         return TRUE;
                     }