diff --git a/src/server_events.c b/src/server_events.c index f920e1a9..0bb017b8 100644 --- a/src/server_events.c +++ b/src/server_events.c @@ -490,6 +490,7 @@ void handle_leave_room(const char * const room) { muc_leave(room); + ui_leave_room(room); } void @@ -511,7 +512,22 @@ void handle_room_destroy(const char * const room) { muc_leave(room); - ui_room_destroyed(room); + ui_room_destroy(room); +} + +void +handle_room_destroyed(const char * const room, const char * const new_jid, const char * const password, + const char * const reason) +{ + muc_leave(room); + ui_room_destroyed(room, reason, new_jid, password); +} + +void +handle_room_kicked(const char * const room, const char * const actor, const char * const reason) +{ + muc_leave(room); + ui_room_kicked(room, actor, reason); } void @@ -538,12 +554,6 @@ handle_room_config_submit_result_error(const char * const room, const char * con ui_handle_room_config_submit_result_error(room, message); } -void -handle_room_kick(const char * const room, const char * const nick) -{ - ui_handle_room_kick(room, nick); -} - void handle_room_kick_result_error(const char * const room, const char * const nick, const char * const error) { @@ -619,6 +629,15 @@ handle_room_member_offline(const char * const room, const char * const nick, prefs_free_string(muc_status_pref); } +void +handle_room_occupent_kicked(const char * const room, const char * const nick, const char * const actor, + const char * const reason) +{ + muc_roster_remove(room, nick); + ui_room_member_kicked(room, nick, actor, reason); +} + + void handle_room_member_nick_change(const char * const room, const char * const old_nick, const char * const nick) diff --git a/src/server_events.h b/src/server_events.h index 7e70e833..92988bd9 100644 --- a/src/server_events.h +++ b/src/server_events.h @@ -65,7 +65,6 @@ void handle_room_affiliation_list(const char * const room, const char * const af void handle_room_affiliation_set_error(const char * const room, const char * const jid, const char * const affiliation, const char * const error); void handle_room_affiliation_set(const char * const room, const char * const jid, const char * const affiliation); -void handle_room_kick(const char * const room, const char * const nick); void handle_room_kick_result_error(const char * const room, const char * const nick, const char * const error); void handle_duck_result(const char * const result); void handle_incoming_message(char *from, char *message, gboolean priv); @@ -91,6 +90,11 @@ 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, const char * const old_nick, const char * const nick); +void handle_room_destroyed(const char * const room, const char * const new_jid, const char * const password, + const char * const reason); +void handle_room_kicked(const char * const room, const char * const actor, const char * const reason); +void handle_room_occupent_kicked(const char * const room, const char * const nick, const char * const actor, + const char * const reason); void handle_group_add(const char * const contact, const char * const group); void handle_group_remove(const char * const contact, diff --git a/src/ui/core.c b/src/ui/core.c index 620d08d8..a0712142 100644 --- a/src/ui/core.c +++ b/src/ui/core.c @@ -1493,6 +1493,31 @@ _ui_room_member_offline(const char * const room, const char * const nick) } } +static void +_ui_room_member_kicked(const char * const room, const char * const nick, const char * const actor, + const char * const reason) +{ + ProfWin *window = wins_get_by_recipient(room); + if (window == NULL) { + log_error("Received kick for room participant %s, but no window open for %s.", nick, room); + } else { + GString *message = g_string_new(nick); + g_string_append(message, " has been kicked from the room"); + if (actor) { + g_string_append(message, " by "); + g_string_append(message, actor); + } + if (reason) { + g_string_append(message, ", reason: "); + g_string_append(message, reason); + } + + win_save_vprint(window, '!', NULL, 0, COLOUR_OFFLINE, "", "<- %s", message->str); + g_string_free(message, TRUE); + } +} + + static void _ui_room_member_online(const char * const room, const char * const nick, const char * const show, const char * const status) @@ -1691,7 +1716,7 @@ _ui_room_requires_config(const char * const room_jid) } static void -_ui_room_destroyed(const char * const room_jid) +_ui_room_destroy(const char * const room_jid) { ProfWin *window = wins_get_by_recipient(room_jid); if (window == NULL) { @@ -1703,6 +1728,71 @@ _ui_room_destroyed(const char * const room_jid) } } +static void +_ui_leave_room(const char * const room) +{ + ProfWin *window = wins_get_by_recipient(room); + if (window) { + int num = wins_get_num(window); + ui_close_win(num); + } +} + +static void +_ui_room_destroyed(const char * const room, const char * const reason, const char * const new_jid, + const char * const password) +{ + ProfWin *window = wins_get_by_recipient(room); + if (window == NULL) { + log_error("Received room destroy, but no window open for %s.", room); + } else { + int num = wins_get_num(window); + ui_close_win(num); + ProfWin *console = wins_get_console(); + + if (reason) { + win_save_vprint(console, '!', NULL, 0, COLOUR_TYPING, "", "<- Room destroyed: %s, reason: %s", room, reason); + } else { + win_save_vprint(console, '!', NULL, 0, COLOUR_TYPING, "", "<- Room destroyed: %s", room); + } + + if (new_jid) { + if (password) { + win_save_vprint(console, '!', NULL, 0, COLOUR_TYPING, "", "Replacement room: %s, password: %s", new_jid, password); + } else { + win_save_vprint(console, '!', NULL, 0, COLOUR_TYPING, "", "Replacement room: %s", new_jid); + } + } + } +} + +static void +_ui_room_kicked(const char * const room, const char * const actor, const char * const reason) +{ + ProfWin *window = wins_get_by_recipient(room); + if (window == NULL) { + log_error("Received kick, but no window open for %s.", room); + } else { + int num = wins_get_num(window); + ui_close_win(num); + + GString *message = g_string_new("Kicked from "); + g_string_append(message, room); + if (actor) { + g_string_append(message, " by "); + g_string_append(message, actor); + } + if (reason) { + g_string_append(message, ", reason: "); + g_string_append(message, reason); + } + + ProfWin *console = wins_get_console(); + win_save_vprint(console, '!', NULL, 0, COLOUR_TYPING, "", "<- %s", message->str); + g_string_free(message, TRUE); + } +} + static void _ui_room_subject(const char * const room, const char * const nick, const char * const subject) { @@ -1750,17 +1840,6 @@ _ui_handle_room_kick_error(const char * const room, const char * const nick, con } } -static void -_ui_handle_room_kick(const char * const room, const char * const nick) -{ - ProfWin *window = wins_get_by_recipient(room); - if (window == NULL) { - log_error("Kick result received for %s, but no window open for %s.", nick, room); - } else { - win_save_vprint(window, '!', NULL, 0, 0, "", "%s has been kicked from the room", nick); - } -} - static void _ui_room_broadcast(const char * const room_jid, const char * const message) { @@ -2766,7 +2845,7 @@ ui_init_module(void) ui_swap_wins = _ui_swap_wins; ui_update = _ui_update; ui_room_requires_config = _ui_room_requires_config; - ui_room_destroyed = _ui_room_destroyed; + ui_room_destroy = _ui_room_destroy; ui_handle_room_configuration = _ui_handle_room_configuration; ui_handle_room_config_submit_result = _ui_handle_room_config_submit_result; ui_handle_room_config_submit_result_error = _ui_handle_room_config_submit_result_error; @@ -2783,9 +2862,13 @@ ui_init_module(void) ui_handle_room_info_error = _ui_handle_room_info_error; ui_show_room_disco_info = _ui_show_room_disco_info; ui_handle_room_affiliation_list_error = _ui_handle_room_affiliation_list_error; - ui_handle_room_affiliation_list =_ui_handle_room_affiliation_list; - ui_handle_room_affiliation_set_error =_ui_handle_room_affiliation_set_error; + ui_handle_room_affiliation_list = _ui_handle_room_affiliation_list; + ui_handle_room_affiliation_set_error = _ui_handle_room_affiliation_set_error; ui_handle_room_affiliation_set = _ui_handle_room_affiliation_set; - ui_handle_room_kick_error =_ui_handle_room_kick_error; - ui_handle_room_kick = _ui_handle_room_kick; + ui_handle_room_kick_error = _ui_handle_room_kick_error; + ui_room_destroyed = _ui_room_destroyed; + ui_room_kicked = _ui_room_kicked; + ui_leave_room = _ui_leave_room; + ui_room_member_kicked = _ui_room_member_kicked; } + diff --git a/src/ui/ui.h b/src/ui/ui.h index 7f958b11..dafcceac 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -135,13 +135,18 @@ void (*ui_room_message)(const char * const room_jid, const char * const nick, const char * const message); void (*ui_room_subject)(const char * const room, const char * const nick, const char * const subject); void (*ui_room_requires_config)(const char * const room_jid); -void (*ui_room_destroyed)(const char * const room_jid); +void (*ui_room_destroy)(const char * const room_jid); void (*ui_show_room_info)(ProfWin *window, const char * const room); void (*ui_show_room_role_list)(ProfWin *window, const char * const room, muc_role_t role); void (*ui_show_room_affiliation_list)(ProfWin *window, const char * const room, muc_affiliation_t affiliation); void (*ui_handle_room_info_error)(const char * const room, const char * const error); void (*ui_show_room_disco_info)(const char * const room, GSList *identities, GSList *features); - +void (*ui_room_destroyed)(const char * const room, const char * const reason, const char * const new_jid, + const char * const password); +void (*ui_room_kicked)(const char * const room, const char * const actor, const char * const reason); +void (*ui_room_member_kicked)(const char * const room, const char * const nick, const char * const actor, + const char * const reason); +void (*ui_leave_room)(const char * const room); void (*ui_room_broadcast)(const char * const room_jid, const char * const message); void (*ui_room_member_offline)(const char * const room, const char * const nick); @@ -176,7 +181,6 @@ void (*ui_handle_room_affiliation_set_error)(const char * const room, const char const char * const affiliation, const char * const error); void (*ui_handle_room_affiliation_set)(const char * const room, const char * const jid, const char * const affiliation); void (*ui_handle_room_kick_error)(const char * const room, const char * const nick, const char * const error); -void (*ui_handle_room_kick)(const char * const room, const char * const nick); void (*ui_show_form)(ProfWin *window, const char * const room, DataForm *form); void (*ui_show_form_field)(ProfWin *window, DataForm *form, char *tag); void (*ui_show_form_help)(ProfWin *window, DataForm *form); diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c index 4be3d97f..c24e4ecc 100644 --- a/src/xmpp/iq.c +++ b/src/xmpp/iq.c @@ -960,7 +960,6 @@ _room_kick_result_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza return 0; } - handle_room_kick(from, nick); free(nick); return 0; diff --git a/src/xmpp/presence.c b/src/xmpp/presence.c index be2d4a01..adc6fc06 100644 --- a/src/xmpp/presence.c +++ b/src/xmpp/presence.c @@ -704,7 +704,30 @@ _muc_user_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, if (new_nick != NULL) { muc_nick_change_start(from_room, new_nick); } else { - handle_leave_room(from_room); + GSList *status_codes = stanza_get_status_codes_by_ns(stanza, STANZA_NS_MUC_USER); + + // room destroyed + if (stanza_room_destroyed(stanza)) { + char *new_jid = stanza_get_muc_destroy_alternative_room(stanza); + char *password = stanza_get_muc_destroy_alternative_password(stanza); + char *reason = stanza_get_muc_destroy_reason(stanza); + handle_room_destroyed(from_room, new_jid, password, reason); + free(password); + free(reason); + + // kicked from room + } else if (g_slist_find_custom(status_codes, "307", (GCompareFunc)g_strcmp0) != NULL) { + char *actor = stanza_get_kick_actor(stanza); + char *reason = stanza_get_kick_reason(stanza); + handle_room_kicked(from_room, actor, reason); + free(reason); + + // normal exit + } else { + handle_leave_room(from_room); + } + + g_slist_free(status_codes); } // self online @@ -756,7 +779,19 @@ _muc_user_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, free(new_nick); } } else { - handle_room_member_offline(from_room, from_nick, "offline", status_str); + GSList *status_codes = stanza_get_status_codes_by_ns(stanza, STANZA_NS_MUC_USER); + + // kicked from room + if (g_slist_find_custom(status_codes, "307", (GCompareFunc)g_strcmp0) != NULL) { + char *actor = stanza_get_kick_actor(stanza); + char *reason = stanza_get_kick_reason(stanza); + handle_room_occupent_kicked(from_room, from_nick, actor, reason); + free(reason); + + // normal exit + } else { + handle_room_member_offline(from_room, from_nick, "offline", status_str); + } } } else { // send disco info for capabilities, if not cached diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index eb5f857c..07e96659 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -1010,6 +1010,156 @@ stanza_is_muc_self_presence(xmpp_stanza_t * const stanza, return FALSE; } +GSList * +stanza_get_status_codes_by_ns(xmpp_stanza_t * const stanza, char *ns) +{ + GSList *codes = NULL; + xmpp_stanza_t *ns_child = xmpp_stanza_get_child_by_ns(stanza, ns); + if (ns_child) { + xmpp_stanza_t *child = xmpp_stanza_get_children(ns_child); + while (child) { + char *name = xmpp_stanza_get_name(child); + if (g_strcmp0(name, STANZA_NAME_STATUS) == 0) { + char *code = xmpp_stanza_get_attribute(child, STANZA_ATTR_CODE); + if (code) { + codes = g_slist_append(codes, code); + } + } + child = xmpp_stanza_get_next(child); + } + } + return codes; +} + +gboolean +stanza_room_destroyed(xmpp_stanza_t *stanza) +{ + char *stanza_name = xmpp_stanza_get_name(stanza); + if (g_strcmp0(stanza_name, STANZA_NAME_PRESENCE) == 0) { + xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_MUC_USER); + if (x) { + xmpp_stanza_t *destroy = xmpp_stanza_get_child_by_name(x, STANZA_NAME_DESTROY); + if (destroy) { + return TRUE; + } + } + } + + return FALSE; +} + +char * +stanza_get_muc_destroy_alternative_room(xmpp_stanza_t *stanza) +{ + char *stanza_name = xmpp_stanza_get_name(stanza); + if (g_strcmp0(stanza_name, STANZA_NAME_PRESENCE) == 0) { + xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_MUC_USER); + if (x) { + xmpp_stanza_t *destroy = xmpp_stanza_get_child_by_name(x, STANZA_NAME_DESTROY); + if (destroy) { + char *jid = xmpp_stanza_get_attribute(destroy, STANZA_ATTR_JID); + if (jid) { + return jid; + } + } + } + } + + return NULL; +} + +char * +stanza_get_muc_destroy_alternative_password(xmpp_stanza_t *stanza) +{ + char *stanza_name = xmpp_stanza_get_name(stanza); + if (g_strcmp0(stanza_name, STANZA_NAME_PRESENCE) == 0) { + xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_MUC_USER); + if (x) { + xmpp_stanza_t *destroy = xmpp_stanza_get_child_by_name(x, STANZA_NAME_DESTROY); + if (destroy) { + xmpp_stanza_t *password_st = xmpp_stanza_get_child_by_name(destroy, STANZA_NAME_PASSWORD); + if (password_st) { + char *password = xmpp_stanza_get_text(password_st); + if (password) { + return password; + } + } + } + } + } + return NULL; +} + +char * +stanza_get_muc_destroy_reason(xmpp_stanza_t *stanza) +{ + char *stanza_name = xmpp_stanza_get_name(stanza); + if (g_strcmp0(stanza_name, STANZA_NAME_PRESENCE) == 0) { + xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_MUC_USER); + if (x) { + xmpp_stanza_t *destroy = xmpp_stanza_get_child_by_name(x, STANZA_NAME_DESTROY); + if (destroy) { + xmpp_stanza_t *reason_st = xmpp_stanza_get_child_by_name(destroy, STANZA_NAME_REASON); + if (reason_st) { + char *reason = xmpp_stanza_get_text(reason_st); + if (reason) { + return reason; + } + } + } + } + } + return NULL; +} + +char * +stanza_get_kick_actor(xmpp_stanza_t *stanza) +{ + char *stanza_name = xmpp_stanza_get_name(stanza); + if (g_strcmp0(stanza_name, STANZA_NAME_PRESENCE) == 0) { + 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) { + xmpp_stanza_t *actor = xmpp_stanza_get_child_by_name(item, STANZA_NAME_ACTOR); + if (actor) { + char *nick = xmpp_stanza_get_attribute(actor, STANZA_ATTR_NICK); + if (nick) { + return nick; + } + char *jid = xmpp_stanza_get_attribute(actor, STANZA_ATTR_JID); + if (jid) { + return jid; + } + } + } + } + } + return NULL; +} + +char * +stanza_get_kick_reason(xmpp_stanza_t *stanza) +{ + char *stanza_name = xmpp_stanza_get_name(stanza); + if (g_strcmp0(stanza_name, STANZA_NAME_PRESENCE) == 0) { + 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) { + xmpp_stanza_t *reason_st = xmpp_stanza_get_child_by_name(item, STANZA_NAME_REASON); + if (reason_st) { + char *reason = xmpp_stanza_get_text(reason_st); + if (reason) { + return reason; + } + } + } + } + } + return NULL; +} + gboolean stanza_is_room_nick_change(xmpp_stanza_t * const stanza) { diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index fefa47b6..ae673848 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -76,6 +76,7 @@ #define STANZA_NAME_CONFERENCE "conference" #define STANZA_NAME_VALUE "value" #define STANZA_NAME_DESTROY "destroy" +#define STANZA_NAME_ACTOR "actor" // error conditions #define STANZA_NAME_BAD_REQUEST "bad-request" @@ -248,4 +249,12 @@ xmpp_stanza_t * stanza_create_roster_remove_set(xmpp_ctx_t *ctx, char * stanza_get_error_message(xmpp_stanza_t * const stanza); +GSList* stanza_get_status_codes_by_ns(xmpp_stanza_t * const stanza, char *ns); +gboolean stanza_room_destroyed(xmpp_stanza_t *stanza); +char* stanza_get_muc_destroy_alternative_room(xmpp_stanza_t *stanza); +char* stanza_get_muc_destroy_alternative_password(xmpp_stanza_t *stanza); +char* stanza_get_muc_destroy_reason(xmpp_stanza_t *stanza); +char* stanza_get_kick_actor(xmpp_stanza_t *stanza); +char* stanza_get_kick_reason(xmpp_stanza_t *stanza); + #endif