about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorJames Booth <boothj5@gmail.com>2012-11-18 21:46:58 +0000
committerJames Booth <boothj5@gmail.com>2012-11-18 21:46:58 +0000
commit49676e3fae307b666ca1c11f734ad38ced36dd07 (patch)
tree01090b472b6e9b2feb7484ccd03c55ae97929b7e
parente9d6ba211774e382771df5afcc3170f64a72ca6d (diff)
downloadprofani-tty-49676e3fae307b666ca1c11f734ad38ced36dd07.tar.gz
Handle nick changes from other users
-rw-r--r--src/jabber.c48
-rw-r--r--src/profanity.c12
-rw-r--r--src/profanity.h2
-rw-r--r--src/room_chat.c41
-rw-r--r--src/room_chat.h8
-rw-r--r--src/stanza.c108
-rw-r--r--src/stanza.h9
-rw-r--r--src/ui.h5
-rw-r--r--src/windows.c20
9 files changed, 227 insertions, 26 deletions
diff --git a/src/jabber.c b/src/jabber.c
index a8b7aa44..a94a6693 100644
--- a/src/jabber.c
+++ b/src/jabber.c
@@ -709,7 +709,7 @@ _room_presence_handler(const char * const jid, xmpp_stanza_t * const stanza)
     }
 
     // handle self presence
-    if (strcmp(room_get_nick_for_room(room), nick) == 0) {
+    if (stanza_is_muc_self_presence(stanza)) {
         char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
 
         // left room
@@ -725,32 +725,42 @@ _room_presence_handler(const char * const jid, xmpp_stanza_t * const stanza)
 
     // handle presence from room members
     } else {
-        // roster not yet complete, just add to roster
-        if (!room_get_roster_received(room)) {
-            room_add_to_roster(room, nick);
+        char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
+        char *show_str, *status_str;
 
-        // deal with presence information
+        xmpp_stanza_t *status = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_STATUS);
+        if (status != NULL) {
+            status_str = xmpp_stanza_get_text(status);
         } else {
-            char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE);
-            char *show_str, *status_str;
+            status_str = NULL;
+        }
 
-            xmpp_stanza_t *status = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_STATUS);
-            if (status != NULL) {
-                status_str = xmpp_stanza_get_text(status);
+        if ((type != NULL) && (strcmp(type, STANZA_TYPE_UNAVAILABLE) == 0)) {
+            if (stanza_is_room_nick_change(stanza)) {
+                char *new_nick = stanza_get_new_nick(stanza);
+                room_add_pending_nick_change(room, new_nick, nick);
+                room_remove_from_roster(room, nick);
             } else {
-                status_str = NULL;
-            }
-
-            if ((type != NULL) && (strcmp(type, STANZA_TYPE_UNAVAILABLE) == 0)) {
                 prof_handle_room_member_offline(room, nick, "offline", status_str);
+            }
+        } else {
+            xmpp_stanza_t *show = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_SHOW);
+            if (show != NULL) {
+                show_str = xmpp_stanza_get_text(show);
             } else {
-                xmpp_stanza_t *show = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_SHOW);
-                if (show != NULL) {
-                    show_str = xmpp_stanza_get_text(show);
+                show_str = "online";
+            }
+            if (!room_get_roster_received(room)) {
+                room_add_to_roster(room, nick, show_str, status_str);
+            } else {
+                char *old_nick = room_complete_pending_nick_change(room, nick);
+
+                if (old_nick != NULL) {
+                    room_add_to_roster(room, nick, show_str, status_str);
+                    prof_handle_room_member_nick_change(room, old_nick, nick);
                 } else {
-                    show_str = "online";
+                    prof_handle_room_member_online(room, nick, show_str, status_str);
                 }
-                prof_handle_room_member_online(room, nick, show_str, status_str);
             }
         }
     }
diff --git a/src/profanity.c b/src/profanity.c
index f1b8743e..4ce7b855 100644
--- a/src/profanity.c
+++ b/src/profanity.c
@@ -250,8 +250,8 @@ void
 prof_handle_room_member_online(const char * const room, const char * const nick,
     const char * const show, const char * const status)
 {
-    room_add_to_roster(room, nick);
-    win_show_room_member_online(room, nick);
+    room_add_to_roster(room, nick, show, status);
+    win_show_room_member_online(room, nick, show, status);
     win_page_off();
 }
 
@@ -302,6 +302,14 @@ prof_handle_contact_offline(char *contact, char *show, char *status)
     }
 }
 
+void
+prof_handle_room_member_nick_change(const char * const room,
+    const char * const old_nick, const char * const nick)
+{
+    win_show_room_member_nick_change(room, old_nick, nick);
+    win_page_off();
+}
+
 static void
 _create_config_directory(void)
 {
diff --git a/src/profanity.h b/src/profanity.h
index ab1ea878..670a0f20 100644
--- a/src/profanity.h
+++ b/src/profanity.h
@@ -52,5 +52,7 @@ void prof_handle_room_member_online(const char * const room,
 void prof_handle_room_member_offline(const char * const room,
     const char * const nick, const char * const show, const char * const status);
 void prof_handle_leave_room(const char * const room);
+void prof_handle_room_member_nick_change(const char * const room,
+    const char * const old_nick, const char * const nick);
 
 #endif
diff --git a/src/room_chat.c b/src/room_chat.c
index cdadc529..22a3d555 100644
--- a/src/room_chat.c
+++ b/src/room_chat.c
@@ -31,6 +31,7 @@ typedef struct _muc_room_t {
     char *room;
     char *nick;
     GHashTable *roster;
+    GHashTable *nick_changes;
     gboolean roster_received;
 } muc_room;
 
@@ -51,6 +52,8 @@ room_join(const char * const room, const char * const nick)
     new_room->nick = strdup(nick);
     new_room->roster = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
         (GDestroyNotify)p_contact_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;
 
     g_hash_table_insert(rooms, strdup(room), new_room);
@@ -174,12 +177,13 @@ room_parse_room_jid(const char * const full_room_jid, char **room, char **nick)
 }
 
 void
-room_add_to_roster(const char * const room, const char * const nick)
+room_add_to_roster(const char * const room, const char * const nick,
+    const char * const show, const char * const status)
 {
     muc_room *chat_room = g_hash_table_lookup(rooms, room);
 
     if (chat_room != NULL) {
-        PContact contact = p_contact_new(nick, NULL, "online", NULL, NULL);
+        PContact contact = p_contact_new(nick, NULL, show, status, NULL);
         g_hash_table_replace(chat_room->roster, strdup(nick), contact);
     }
 }
@@ -228,6 +232,35 @@ room_get_roster_received(const char * const room)
     }
 }
 
+void
+room_add_pending_nick_change(const char * const room,
+    const char * const new_nick, const char * const old_nick)
+{
+    muc_room *chat_room = g_hash_table_lookup(rooms, room);
+
+    if (chat_room != NULL) {
+        g_hash_table_insert(chat_room->nick_changes, strdup(new_nick), strdup(old_nick));
+    }
+}
+
+char *
+room_complete_pending_nick_change(const char * const room,
+    const char * const nick)
+{
+    muc_room *chat_room = g_hash_table_lookup(rooms, room);
+
+    if (chat_room != NULL) {
+        char *old_nick =
+            strdup(g_hash_table_lookup(chat_room->nick_changes, nick));
+
+        if (old_nick != NULL) {
+            return old_nick;
+        }
+    }
+
+    return NULL;
+}
+
 static void
 _room_free(muc_room *room)
 {
@@ -244,6 +277,10 @@ _room_free(muc_room *room)
             g_hash_table_remove_all(room->roster);
             room->roster = NULL;
         }
+        if (room->nick_changes != NULL) {
+            g_hash_table_remove_all(room->nick_changes);
+            room->nick_changes = NULL;
+        }
         g_free(room);
     }
     room = NULL;
diff --git a/src/room_chat.h b/src/room_chat.h
index d36c67b7..da68e1da 100644
--- a/src/room_chat.h
+++ b/src/room_chat.h
@@ -34,7 +34,13 @@ char * room_get_room_from_full_jid(const char * const full_room_jid);
 char * room_get_nick_from_full_jid(const char * const full_room_jid);
 gboolean room_parse_room_jid(const char * const full_room_jid, char **room,
     char **nick);
-void room_add_to_roster(const char * const room, const char * const nick);
+void room_add_to_roster(const char * const room, const char * const nick,
+    const char * const show, const char * const status);
+void room_add_pending_nick_change(const char * const room,
+    const char * const new_nick, const char * const old_nick);
+char* room_complete_pending_nick_change(const char * const room,
+    const char * const nick);
+
 GList * room_get_roster(const char * const room);
 void room_set_roster_received(const char * const room);
 gboolean room_get_roster_received(const char * const room);
diff --git a/src/stanza.c b/src/stanza.c
index 07e49d36..282adc99 100644
--- a/src/stanza.c
+++ b/src/stanza.c
@@ -236,3 +236,111 @@ stanza_get_delay(xmpp_stanza_t * const stanza, GTimeVal *tv_stamp)
 
     return FALSE;
 }
+
+gboolean
+stanza_is_muc_self_presence(xmpp_stanza_t * const stanza)
+{
+    if (stanza == NULL) {
+        return FALSE;
+    }
+    if (strcmp(xmpp_stanza_get_name(stanza), STANZA_NAME_PRESENCE) != 0) {
+        return FALSE;
+    }
+
+    xmpp_stanza_t *x = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_X);
+
+    if (x == NULL) {
+        return FALSE;
+    }
+
+    char *ns = xmpp_stanza_get_ns(x);
+    if (ns == NULL) {
+        return FALSE;
+    }
+    if (strcmp(ns, STANZA_NS_MUC_USER) != 0) {
+        return FALSE;
+    }
+
+    xmpp_stanza_t *x_children = xmpp_stanza_get_children(x);
+    if (x_children == NULL) {
+        return FALSE;
+    }
+
+    while (x_children != NULL) {
+        if (strcmp(xmpp_stanza_get_name(x_children), STANZA_NAME_STATUS) == 0) {
+            char *code = xmpp_stanza_get_attribute(x_children, STANZA_ATTR_CODE);
+            if (strcmp(code, "110") == 0) {
+                return TRUE;
+            }
+        }
+        x_children = xmpp_stanza_get_next(x_children);
+    }
+
+    return FALSE;
+}
+
+gboolean
+stanza_is_room_nick_change(xmpp_stanza_t * const stanza)
+{
+    if (stanza == NULL) {
+        return FALSE;
+    }
+    if (strcmp(xmpp_stanza_get_name(stanza), STANZA_NAME_PRESENCE) != 0) {
+        return FALSE;
+    }
+
+    xmpp_stanza_t *x = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_X);
+
+    if (x == NULL) {
+        return FALSE;
+    }
+
+    char *ns = xmpp_stanza_get_ns(x);
+    if (ns == NULL) {
+        return FALSE;
+    }
+    if (strcmp(ns, STANZA_NS_MUC_USER) != 0) {
+        return FALSE;
+    }
+
+    xmpp_stanza_t *x_children = xmpp_stanza_get_children(x);
+    if (x_children == NULL) {
+        return FALSE;
+    }
+
+    while (x_children != NULL) {
+        if (strcmp(xmpp_stanza_get_name(x_children), STANZA_NAME_STATUS) == 0) {
+            char *code = xmpp_stanza_get_attribute(x_children, STANZA_ATTR_CODE);
+            if (strcmp(code, "303") == 0) {
+                return TRUE;
+            }
+        }
+        x_children = xmpp_stanza_get_next(x_children);
+    }
+
+    return FALSE;
+
+}
+
+char *
+stanza_get_new_nick(xmpp_stanza_t * const stanza)
+{
+    if (!stanza_is_room_nick_change(stanza)) {
+        return NULL;
+    } else {
+        xmpp_stanza_t *x = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_X);
+        xmpp_stanza_t *x_children = xmpp_stanza_get_children(x);
+
+        while (x_children != NULL) {
+            if (strcmp(xmpp_stanza_get_name(x_children), STANZA_NAME_ITEM) == 0) {
+                char *nick = xmpp_stanza_get_attribute(x_children, STANZA_ATTR_NICK);
+                if (nick != NULL) {
+                    return strdup(nick);
+                }
+            }
+            x_children = xmpp_stanza_get_next(x_children);
+        }
+
+        return NULL;
+    }
+}
diff --git a/src/stanza.h b/src/stanza.h
index 310fca38..faea0fdd 100644
--- a/src/stanza.h
+++ b/src/stanza.h
@@ -46,6 +46,7 @@
 #define STANZA_NAME_PING "ping"
 #define STANZA_NAME_TEXT "text"
 #define STANZA_NAME_SUBJECT "subject"
+#define STANZA_NAME_ITEM "item"
 
 #define STANZA_TYPE_CHAT "chat"
 #define STANZA_TYPE_GROUPCHAT "groupchat"
@@ -60,10 +61,12 @@
 #define STANZA_ATTR_FROM "from"
 #define STANZA_ATTR_STAMP "stamp"
 #define STANZA_ATTR_TYPE "type"
+#define STANZA_ATTR_CODE "code"
 #define STANZA_ATTR_JID "jid"
 #define STANZA_ATTR_NAME "name"
 #define STANZA_ATTR_SUBSCRIPTION "subscription"
 #define STANZA_ATTR_XMLNS "xmlns"
+#define STANZA_ATTR_NICK "nick"
 
 #define STANZA_TEXT_AWAY "away"
 #define STANZA_TEXT_DND "dnd"
@@ -73,6 +76,7 @@
 
 #define STANZA_NS_CHATSTATES "http://jabber.org/protocol/chatstates"
 #define STANZA_NS_MUC "http://jabber.org/protocol/muc"
+#define STANZA_NS_MUC_USER "http://jabber.org/protocol/muc#user"
 #define STANZA_NS_PING "urn:xmpp:ping"
 
 xmpp_stanza_t* stanza_create_chat_state(xmpp_ctx_t *ctx,
@@ -101,4 +105,9 @@ gboolean stanza_contains_chat_state(xmpp_stanza_t *stanza);
 
 gboolean stanza_get_delay(xmpp_stanza_t * const stanza, GTimeVal *tv_stamp);
 
+gboolean stanza_is_muc_self_presence(xmpp_stanza_t * const stanza);
+gboolean stanza_is_room_nick_change(xmpp_stanza_t * const stanza);
+
+char* stanza_get_new_nick(xmpp_stanza_t * const stanza);
+
 #endif
diff --git a/src/ui.h b/src/ui.h
index bb2fb8cb..f397debb 100644
--- a/src/ui.h
+++ b/src/ui.h
@@ -126,10 +126,13 @@ void win_show_room_message(const char * const room_jid, const char * const nick,
 void win_show_room_subject(const char * const room_jid,
     const char * const subject);
 void win_show_room_member_offline(const char * const room, const char * const nick);
-void win_show_room_member_online(const char * const room, const char * const nick);
+void win_show_room_member_online(const char * const room,
+    const char * const nick, const char * const show, const char * const status);
 void win_show_status(const char * const contact);
 void win_show_wins(void);
 int win_in_private_chat(void);
+void win_show_room_member_nick_change(const char * const room,
+    const char * const old_nick, const char * const nick);
 
 // console window actions
 void cons_about(void);
diff --git a/src/windows.c b/src/windows.c
index ebf57d6d..5f05305b 100644
--- a/src/windows.c
+++ b/src/windows.c
@@ -706,7 +706,8 @@ win_show_room_member_offline(const char * const room, const char * const nick)
 }
 
 void
-win_show_room_member_online(const char * const room, const char * const nick)
+win_show_room_member_online(const char * const room, const char * const nick,
+    const char * const show, const char * const status)
 {
     int win_index = _find_prof_win_index(room);
     WINDOW *win = _wins[win_index].win;
@@ -721,6 +722,23 @@ win_show_room_member_online(const char * const room, const char * const nick)
 }
 
 void
+win_show_room_member_nick_change(const char * const room,
+    const char * const old_nick, const char * const nick)
+{
+    int win_index = _find_prof_win_index(room);
+    WINDOW *win = _wins[win_index].win;
+
+    _win_show_time(win);
+    wattron(win, COLOUR_ONLINE);
+    wprintw(win, "** %s is now known as %s\n", old_nick, nick);
+    wattroff(win, COLOUR_ONLINE);
+
+    if (win_index == _curr_prof_win)
+        dirty = TRUE;
+
+}
+
+void
 win_show_room_history(const char * const room_jid, const char * const nick,
     GTimeVal tv_stamp, const char * const message)
 {