about summary refs log tree commit diff stats
path: root/src/muc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/muc.c')
-rw-r--r--src/muc.c599
1 files changed, 338 insertions, 261 deletions
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