From a37b5ac823cc5daefac6ab33a16d6f44c747ba9c Mon Sep 17 00:00:00 2001 From: fiaxh Date: Tue, 11 Apr 2017 18:06:01 +0200 Subject: [PATCH] MUC improvements --- libdino/src/service/conversation_manager.vala | 2 +- libdino/src/service/muc_manager.vala | 59 +++++------ libdino/src/service/presence_manager.vala | 4 +- .../conversation_selector/groupchat_row.vala | 25 +++-- main/src/ui/conversation_titlebar.vala | 18 ++-- xmpp-vala/CMakeLists.txt | 1 + xmpp-vala/src/module/presence/module.vala | 4 +- xmpp-vala/src/module/xep/0045_muc/flag.vala | 21 ++-- xmpp-vala/src/module/xep/0045_muc/module.vala | 100 +++++++----------- .../src/module/xep/0045_muc/status_code.vala | 58 ++++++++++ 10 files changed, 169 insertions(+), 123 deletions(-) create mode 100644 xmpp-vala/src/module/xep/0045_muc/status_code.vala diff --git a/libdino/src/service/conversation_manager.vala b/libdino/src/service/conversation_manager.vala index db9cff91..abc9e502 100644 --- a/libdino/src/service/conversation_manager.vala +++ b/libdino/src/service/conversation_manager.vala @@ -26,7 +26,7 @@ public class ConversationManager : StreamInteractionModule, Object { this.stream_interactor = stream_interactor; stream_interactor.add_module(this); stream_interactor.account_added.connect(on_account_added); - stream_interactor.get_module(MucManager.IDENTITY).groupchat_joined.connect(on_groupchat_joined); + stream_interactor.get_module(MucManager.IDENTITY).joined.connect(on_groupchat_joined); stream_interactor.get_module(MessageProcessor.IDENTITY).pre_message_received.connect(on_message_received); stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent.connect(on_message_sent); } diff --git a/libdino/src/service/muc_manager.vala b/libdino/src/service/muc_manager.vala index f63a557c..b9d6dce2 100644 --- a/libdino/src/service/muc_manager.vala +++ b/libdino/src/service/muc_manager.vala @@ -8,8 +8,9 @@ public class MucManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("muc_manager"); public string id { get { return IDENTITY.id; } } - public signal void groupchat_joined(Account account, Jid jid, string nick); - public signal void groupchat_subject_set(Account account, Jid jid, string subject); + public signal void joined(Account account, Jid jid, string nick); + public signal void left(Account account, Jid jid); + public signal void subject_set(Account account, Jid jid, string? subject); public signal void bookmarks_updated(Account account, ArrayList conferences); private StreamInteractor stream_interactor; @@ -29,7 +30,8 @@ public class MucManager : StreamInteractionModule, Object { public void join(Account account, Jid jid, string nick, string? password = null) { Core.XmppStream stream = stream_interactor.get_stream(account); - if (stream != null) stream.get_module(Xep.Muc.Module.IDENTITY).enter(stream, jid.bare_jid.to_string(), nick, password, on_groupchat_joined, () => {}, Quadruple.create(this, jid, nick, account)); + if (stream == null) return; + stream.get_module(Xep.Muc.Module.IDENTITY).enter(stream, jid.bare_jid.to_string(), nick, password); } public void part(Account account, Jid jid) { @@ -140,22 +142,39 @@ public class MucManager : StreamInteractionModule, Object { return null; } + public bool is_joined(Jid jid, Account account) { + return get_nick(jid, account) != null; + } + private void on_account_added(Account account) { + stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).room_entered.connect( (stream, jid, nick) => { + joined(account, new Jid(jid), nick); + }); + stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).self_removed_from_room.connect( (stream, jid, code) => { + left(account, new Jid(jid)); + }); stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).subject_set.connect( (stream, subject, jid) => { - on_subject_set(account, new Jid(jid), subject); + subject_set(account, new Jid(jid), subject); }); stream_interactor.module_manager.get_module(account, Xep.Bookmarks.Module.IDENTITY).conferences_updated.connect( (stream, conferences) => { bookmarks_updated(account, conferences); }); } - private void on_subject_set(Account account, Jid sender_jid, string subject) { - groupchat_subject_set(account, sender_jid, subject); - } - private void on_stream_negotiated(Account account) { Core.XmppStream stream = stream_interactor.get_stream(account); - if (stream != null) stream.get_module(Xep.Bookmarks.Module.IDENTITY).get_conferences(stream, join_autojoin_conferences, Tuple.create(this, account)); + if (stream != null) stream.get_module(Xep.Bookmarks.Module.IDENTITY).get_conferences(stream, (stream, conferences, o) => { + Tuple tuple = o as Tuple; + MucManager outer_ = tuple.a; + Account account_ = tuple.b; + foreach (Xep.Bookmarks.Conference bookmark in conferences) { + Jid jid = new Jid(bookmark.jid); + outer_.conference_bookmarks[jid] = bookmark; + if (bookmark.autojoin) { + outer_.join(account_, jid, bookmark.nick); + } + } + }, Tuple.create(this, account)); } private void on_pre_message_received(Entities.Message message, Xmpp.Message.Stanza message_stanza, Conversation conversation) { @@ -178,28 +197,6 @@ public class MucManager : StreamInteractionModule, Object { } } } - - private static void on_groupchat_joined(Core.XmppStream stream, Object? store) { - Quadruple quadruple = store as Quadruple; - MucManager outer = quadruple.a; - Jid jid = quadruple.b; - string nick = quadruple.c; - Account account = quadruple.d; - outer.groupchat_joined(account, jid, nick); - } - - private static void join_autojoin_conferences(Core.XmppStream stream, ArrayList conferences, Object? o) { - Tuple tuple = o as Tuple; - MucManager outer = tuple.a; - Account account = tuple.b; - foreach (Xep.Bookmarks.Conference bookmark in conferences) { - Jid jid = new Jid(bookmark.jid); - outer.conference_bookmarks[jid] = bookmark; - if (bookmark.autojoin) { - outer.join(account, jid, bookmark.nick); - } - } - } } } \ No newline at end of file diff --git a/libdino/src/service/presence_manager.vala b/libdino/src/service/presence_manager.vala index 5dfb2cf6..ad2db89f 100644 --- a/libdino/src/service/presence_manager.vala +++ b/libdino/src/service/presence_manager.vala @@ -83,8 +83,8 @@ public class PresenceManager : StreamInteractionModule, Object { stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_available_show.connect((stream, jid, show) => on_received_available_show(account, new Jid(jid), show) ); - stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_unavailable.connect((stream, jid) => - on_received_unavailable(account, new Jid(jid)) + stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_unavailable.connect((stream, presence) => + on_received_unavailable(account, new Jid(presence.from)) ); stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_subscription_request.connect((stream, jid) => received_subscription_request(new Jid(jid), account) diff --git a/main/src/ui/conversation_selector/groupchat_row.vala b/main/src/ui/conversation_selector/groupchat_row.vala index 598902cc..07dabf06 100644 --- a/main/src/ui/conversation_selector/groupchat_row.vala +++ b/main/src/ui/conversation_selector/groupchat_row.vala @@ -8,10 +8,15 @@ public class GroupchatRow : ConversationRow { base(stream_interactor, conversation); has_tooltip = true; set_tooltip_text(conversation.counterpart.bare_jid.to_string()); - set_avatar((new AvatarGenerator(AVATAR_SIZE, AVATAR_SIZE, image.scale_factor)) - .set_greyscale(true) - .draw_conversation(stream_interactor, conversation), image.scale_factor); + update_avatar(); + x_button.clicked.connect(on_x_button_clicked); + stream_interactor.get_module(MucManager.IDENTITY).left.connect(() => { + Idle.add(() => { + update_avatar(); + return false; + }); + }); } @@ -21,15 +26,21 @@ public class GroupchatRow : ConversationRow { } public override void network_connection(bool connected) { - set_avatar((new AvatarGenerator(AVATAR_SIZE, AVATAR_SIZE, image.scale_factor)) - .set_greyscale(!connected || - stream_interactor.get_module(MucManager.IDENTITY).get_nick(conversation.counterpart, conversation.account) == null) // TODO better currently joined - .draw_conversation(stream_interactor, conversation), image.scale_factor); + update_avatar(); } private void on_x_button_clicked() { stream_interactor.get_module(MucManager.IDENTITY).part(conversation.account, conversation.counterpart); } + + private void update_avatar() { + ConnectionManager.ConnectionState connection_state = stream_interactor.connection_manager.get_state(conversation.account); + bool is_joined = stream_interactor.get_module(MucManager.IDENTITY).is_joined(conversation.counterpart, conversation.account); + + set_avatar((new AvatarGenerator(AVATAR_SIZE, AVATAR_SIZE, image.scale_factor)) + .set_greyscale(connection_state != ConnectionManager.ConnectionState.CONNECTED || !is_joined) + .draw_conversation(stream_interactor, conversation), image.scale_factor); + } } } \ No newline at end of file diff --git a/main/src/ui/conversation_titlebar.vala b/main/src/ui/conversation_titlebar.vala index 70a376f1..b1db3604 100644 --- a/main/src/ui/conversation_titlebar.vala +++ b/main/src/ui/conversation_titlebar.vala @@ -20,11 +20,17 @@ public class ConversationTitlebar : Gtk.HeaderBar { public ConversationTitlebar(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; - stream_interactor.get_module(MucManager.IDENTITY).groupchat_subject_set.connect((account, jid, subject) => { - Idle.add(() => { on_groupchat_subject_set(account, jid, subject); return false; }); - }); create_conversation_menu(); create_encryption_menu(); + + stream_interactor.get_module(MucManager.IDENTITY).subject_set.connect((account, jid, subject) => { + Idle.add(() => { + if (conversation != null && conversation.counterpart.equals_bare(jid) && conversation.account.equals(account)) { + update_subtitle(subject); + } + return false; + }); + }); } public void initialize_for_conversation(Conversation conversation) { @@ -118,12 +124,6 @@ public class ConversationTitlebar : Gtk.HeaderBar { encryption_button.set_popover(menu); encryption_button.set_image(new Image.from_icon_name("changes-allow-symbolic", IconSize.BUTTON)); } - - private void on_groupchat_subject_set(Account account, Jid jid, string subject) { - if (conversation != null && conversation.counterpart.equals_bare(jid) && conversation.account.equals(account)) { - update_subtitle(subject); - } - } } } \ No newline at end of file diff --git a/xmpp-vala/CMakeLists.txt b/xmpp-vala/CMakeLists.txt index 7c8ce6e6..240ee02e 100644 --- a/xmpp-vala/CMakeLists.txt +++ b/xmpp-vala/CMakeLists.txt @@ -41,6 +41,7 @@ SOURCES "src/module/xep/0030_service_discovery/module.vala" "src/module/xep/0045_muc/flag.vala" "src/module/xep/0045_muc/module.vala" + "src/module/xep/0045_muc/status_code.vala" "src/module/xep/0048_bookmarks/module.vala" "src/module/xep/0048_bookmarks/conference.vala" "src/module/xep/0049_private_xml_storage.vala" diff --git a/xmpp-vala/src/module/presence/module.vala b/xmpp-vala/src/module/presence/module.vala index 3c078453..a2cdf1d7 100644 --- a/xmpp-vala/src/module/presence/module.vala +++ b/xmpp-vala/src/module/presence/module.vala @@ -11,7 +11,7 @@ namespace Xmpp.Presence { public signal void initial_presence_sent(XmppStream stream, Presence.Stanza presence); public signal void received_available(XmppStream stream, Presence.Stanza presence); public signal void received_available_show(XmppStream stream, string jid, string show); - public signal void received_unavailable(XmppStream stream, string jid); + public signal void received_unavailable(XmppStream stream, Presence.Stanza presence); public signal void received_subscription_request(XmppStream stream, string jid); public signal void received_unsubscription(XmppStream stream, string jid); @@ -76,7 +76,7 @@ namespace Xmpp.Presence { break; case Presence.Stanza.TYPE_UNAVAILABLE: stream.get_flag(Flag.IDENTITY).remove_presence(presence.from); - received_unavailable(stream, presence.from); + received_unavailable(stream, presence); break; case Presence.Stanza.TYPE_SUBSCRIBE: received_subscription_request(stream, presence.from); diff --git a/xmpp-vala/src/module/xep/0045_muc/flag.vala b/xmpp-vala/src/module/xep/0045_muc/flag.vala index f6340a5c..e5e0af6e 100644 --- a/xmpp-vala/src/module/xep/0045_muc/flag.vala +++ b/xmpp-vala/src/module/xep/0045_muc/flag.vala @@ -7,7 +7,6 @@ namespace Xmpp.Xep.Muc { public class Flag : XmppStreamFlag { public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, "muc"); - private HashMap enter_listeners = new HashMap(); private HashMap enter_ids = new HashMap(); private HashMap own_nicks = new HashMap(); private HashMap subjects = new HashMap(); @@ -32,8 +31,6 @@ public class Flag : XmppStreamFlag { public string? get_enter_id(string bare_jid) { return enter_ids[bare_jid]; } - public ListenerHolder? get_enter_listener(string bare_jid) { return enter_listeners[bare_jid]; } - public bool is_muc(string jid) { return own_nicks[jid] != null; } public bool is_occupant(string jid) { @@ -45,23 +42,33 @@ public class Flag : XmppStreamFlag { public string? get_muc_subject(string bare_jid) { return subjects[bare_jid]; } - public void set_muc_subject(string full_jid, string subject) { + public void set_muc_subject(string full_jid, string? subject) { string bare_jid = get_bare_jid(full_jid); subjects[bare_jid] = subject; subjects_by[bare_jid] = full_jid; } - public void start_muc_enter(string bare_jid, string presence_id, ListenerHolder listener) { - enter_listeners[bare_jid] = listener; + public void start_muc_enter(string bare_jid, string presence_id) { enter_ids[bare_jid] = presence_id; } public void finish_muc_enter(string bare_jid, string? nick = null) { if (nick != null) own_nicks[bare_jid] = nick; - enter_listeners.unset(bare_jid); enter_ids.unset(bare_jid); } + public void left_muc(XmppStream stream, string muc) { + own_nicks.unset(muc); + subjects.unset(muc); + subjects_by.unset(muc); + Gee.List? occupants = stream.get_flag(Presence.Flag.IDENTITY).get_resources(muc); + if (occupants != null) { + foreach (string occupant in occupants) { + remove_occupant_info(occupant); + } + } + } + public void remove_occupant_info(string full_jid) { occupant_real_jids.unset(full_jid); occupant_affiliation.unset(full_jid); diff --git a/xmpp-vala/src/module/xep/0045_muc/module.vala b/xmpp-vala/src/module/xep/0045_muc/module.vala index 486c342a..714bb2ca 100644 --- a/xmpp-vala/src/module/xep/0045_muc/module.vala +++ b/xmpp-vala/src/module/xep/0045_muc/module.vala @@ -35,8 +35,14 @@ public class Module : XmppStreamModule { public signal void received_occupant_jid(XmppStream stream, string jid, string? real_jid); public signal void received_occupant_role(XmppStream stream, string jid, string? role); public signal void subject_set(XmppStream stream, string subject, string jid); + public signal void room_configuration_changed(XmppStream stream, string jid, StatusCode code); - public void enter(XmppStream stream, string bare_jid, string nick, string? password, ListenerHolder.OnSuccess success_listener, ListenerHolder.OnError error_listener, Object? store) { + public signal void room_entered(XmppStream stream, string jid, string nick); + public signal void room_enter_error(XmppStream stream, string jid, MucEnterError error); + public signal void self_removed_from_room(XmppStream stream, string jid, StatusCode code); + public signal void removed_from_room(XmppStream stream, string jid, StatusCode? code); + + public void enter(XmppStream stream, string bare_jid, string nick, string? password) { Presence.Stanza presence = new Presence.Stanza(); presence.to = bare_jid + "/" + nick; StanzaNode x_node = new StanzaNode.build("x", NS_URI).add_self_xmlns(); @@ -45,8 +51,7 @@ public class Module : XmppStreamModule { } presence.stanza.put_node(x_node); - stream.get_flag(Flag.IDENTITY).start_muc_enter(bare_jid, presence.id, new ListenerHolder(success_listener, error_listener, store)); - + stream.get_flag(Flag.IDENTITY).start_muc_enter(bare_jid, presence.id); stream.get_module(Presence.Module.IDENTITY).send_presence(stream, presence); } @@ -129,7 +134,6 @@ public class Module : XmppStreamModule { string bare_jid = get_bare_jid(presence.from); ErrorStanza? error_stanza = presence.get_error(); if (flag.get_enter_id(bare_jid) == error_stanza.original_id) { - ListenerHolder? listener = flag.get_enter_listener(bare_jid); MucEnterError? error = null; if (error_stanza.condition == ErrorStanza.CONDITION_NOT_AUTHORIZED && ErrorStanza.TYPE_AUTH == error_stanza.type_) { error = MucEnterError.PASSWORD_REQUIRED; @@ -144,7 +148,7 @@ public class Module : XmppStreamModule { } else if (ErrorStanza.CONDITION_ITEM_NOT_FOUND == error_stanza.condition && ErrorStanza.TYPE_CANCEL == error_stanza.type_) { error = MucEnterError.ROOM_DOESNT_EXIST; } - if (error != null && listener != null) listener.on_error(stream, error, listener.reference); + if (error != null) room_enter_error(stream, bare_jid, error); flag.finish_muc_enter(bare_jid); } } @@ -158,9 +162,10 @@ public class Module : XmppStreamModule { ArrayList status_codes = get_status_codes(x_node); if (status_codes.contains(StatusCode.SELF_PRESENCE)) { string bare_jid = get_bare_jid(presence.from); - ListenerHolder listener = flag.get_enter_listener(bare_jid); - listener.on_success(stream, listener.reference); - flag.finish_muc_enter(bare_jid, get_resource_part(presence.from)); + if (flag.get_enter_id(bare_jid) != null) { + room_entered(stream, bare_jid, get_resource_part(presence.from)); + flag.finish_muc_enter(bare_jid, get_resource_part(presence.from)); + } } string? affiliation = x_node["item", "affiliation"].val; if (affiliation != null) { @@ -179,10 +184,30 @@ public class Module : XmppStreamModule { } } - private void on_received_unavailable(XmppStream stream, string jid) { + private void on_received_unavailable(XmppStream stream, Presence.Stanza presence) { Flag flag = stream.get_flag(Flag.IDENTITY); - if (flag.is_occupant(jid)) { - flag.remove_occupant_info(jid); + if (!flag.is_occupant(presence.from)) return; + + StanzaNode? x_node = presence.stanza.get_subnode("x", NS_URI_USER); + if (x_node == null) return; + + ArrayList status_codes = get_status_codes(x_node); + + if (StatusCode.SELF_PRESENCE in status_codes) { + flag.remove_occupant_info(presence.from); + } + + foreach (StatusCode code in USER_REMOVED_CODES) { + if (code in status_codes) { + if (StatusCode.SELF_PRESENCE in status_codes) { + flag.left_muc(stream, get_bare_jid(presence.from)); + self_removed_from_room(stream, presence.from, code); + Presence.Flag presence_flag = stream.get_flag(Presence.Flag.IDENTITY); + presence_flag.remove_presence(get_bare_jid(presence.from)); + } else { + removed_from_room(stream, presence.from, code); + } + } } } @@ -195,57 +220,4 @@ public class Module : XmppStreamModule { } } -public enum StatusCode { - /** Inform user that any occupant is allowed to see the user's full JID */ - JID_VISIBLE = 100, - /** Inform user that his or her affiliation changed while not in the room */ - AFFILIATION_CHANGED = 101, - /** Inform occupants that room now shows unavailable members */ - SHOWS_UNAVIABLE_MEMBERS = 102, - /** Inform occupants that room now does not show unavailable members */ - SHOWS_UNAVIABLE_MEMBERS_NOT = 103, - /** Inform occupants that a non-privacy-related room configuration change has occurred */ - CONFIG_CHANGE_NON_PRIVACY = 104, - /** Inform user that presence refers to itself */ - SELF_PRESENCE = 110, - /** Inform occupants that room logging is now enabled */ - LOGGING_ENABLED = 170, - /** Inform occupants that room logging is now disabled */ - LOGGING_DISABLED = 171, - /** Inform occupants that the room is now non-anonymous */ - NON_ANONYMOUS = 172, - /** Inform occupants that the room is now semi-anonymous */ - SEMI_ANONYMOUS = 173, - /** Inform user that a new room has been created */ - NEW_ROOM_CREATED = 201, - /** Inform user that service has assigned or modified occupant's roomnick */ - MODIFIED_NICK = 210, - /** Inform user that he or she has been banned from the room */ - BANNED = 301, - /** Inform all occupants of new room nickname */ - ROOM_NICKNAME = 303, - /** Inform user that he or she has been kicked from the room */ - KICKED = 307, - /** Inform user that he or she is being removed from the room */ - REMOVED_AFFILIATION_CHANGE = 321, - /** Inform user that he or she is being removed from the room because the room has been changed to members-only - and the user is not a member */ - REMOVED_MEMBERS_ONLY = 322, - /** Inform user that he or she is being removed from the room because the MUC service is being shut down */ - REMOVED_SHUTDOWN = 332 -} - -public class ListenerHolder { - [CCode (has_target = false)] public delegate void OnSuccess(XmppStream stream, Object? store); - public OnSuccess on_success { get; private set; } - [CCode (has_target = false)] public delegate void OnError(XmppStream stream, MucEnterError error, Object? store); - public OnError on_error { get; private set; } - public Object? reference { get; private set; } - - public ListenerHolder(OnSuccess on_success, OnError on_error, Object? reference = null) { - this.on_success = on_success; - this.reference = reference; - } -} - } diff --git a/xmpp-vala/src/module/xep/0045_muc/status_code.vala b/xmpp-vala/src/module/xep/0045_muc/status_code.vala new file mode 100644 index 00000000..7cb9421f --- /dev/null +++ b/xmpp-vala/src/module/xep/0045_muc/status_code.vala @@ -0,0 +1,58 @@ +namespace Xmpp.Xep.Muc { + +public const StatusCode[] ROOM_CONFIGURATION_CODES = { + StatusCode.LOGGING_ENABLED, + StatusCode.LOGGING_DISABLED, + StatusCode.NON_ANONYMOUS, + StatusCode.SEMI_ANONYMOUS +}; + +public const StatusCode[] USER_REMOVED_CODES = { + StatusCode.BANNED, + StatusCode.KICKED, + StatusCode.REMOVED_AFFILIATION_CHANGE, + StatusCode.REMOVED_MEMBERS_ONLY, + StatusCode.REMOVED_SHUTDOWN +}; + +public enum StatusCode { + /** Inform user that any occupant is allowed to see the user's full JID */ + JID_VISIBLE = 100, + /** Inform user that his or her affiliation changed while not in the room */ + AFFILIATION_CHANGED = 101, + /** Inform occupants that room now shows unavailable members */ + SHOWS_UNAVIABLE_MEMBERS = 102, + /** Inform occupants that room now does not show unavailable members */ + SHOWS_UNAVIABLE_MEMBERS_NOT = 103, + /** Inform occupants that a non-privacy-related room configuration change has occurred */ + CONFIG_CHANGE_NON_PRIVACY = 104, + /** Inform user that presence refers to itself */ + SELF_PRESENCE = 110, + /** Inform occupants that room logging is now enabled */ + LOGGING_ENABLED = 170, + /** Inform occupants that room logging is now disabled */ + LOGGING_DISABLED = 171, + /** Inform occupants that the room is now non-anonymous */ + NON_ANONYMOUS = 172, + /** Inform occupants that the room is now semi-anonymous */ + SEMI_ANONYMOUS = 173, + /** Inform user that a new room has been created */ + NEW_ROOM_CREATED = 201, + /** Inform user that service has assigned or modified occupant's roomnick */ + MODIFIED_NICK = 210, + /** Inform user that he or she has been banned from the room */ + BANNED = 301, + /** Inform all occupants of new room nickname */ + ROOM_NICKNAME = 303, + /** Inform user that he or she has been kicked from the room */ + KICKED = 307, + /** Inform user that he or she is being removed from the room */ + REMOVED_AFFILIATION_CHANGE = 321, + /** Inform user that he or she is being removed from the room because the room has been changed to members-only + and the user is not a member */ + REMOVED_MEMBERS_ONLY = 322, + /** Inform user that he or she is being removed from the room because the MUC service is being shut down */ + REMOVED_SHUTDOWN = 332 +} + +} \ No newline at end of file