diff --git a/big_tests/tests/domain_removal_SUITE.erl b/big_tests/tests/domain_removal_SUITE.erl index 033d13bda3..7b0d937f9e 100644 --- a/big_tests/tests/domain_removal_SUITE.erl +++ b/big_tests/tests/domain_removal_SUITE.erl @@ -9,6 +9,7 @@ -include_lib("eunit/include/eunit.hrl"). -include_lib("exml/include/exml_stream.hrl"). -include_lib("jid/include/jid.hrl"). +-include_lib("common_test/include/ct.hrl"). all() -> [{group, auth_removal}, @@ -16,6 +17,7 @@ all() -> {group, mam_removal}, {group, inbox_removal}, {group, muc_light_removal}, + {group, muc_removal}, {group, private_removal}, {group, roster_removal}, {group, offline_removal}, @@ -30,6 +32,7 @@ groups() -> {inbox_removal, [], [inbox_removal]}, {muc_light_removal, [], [muc_light_removal, muc_light_blocking_removal]}, + {muc_removal, [], [muc_removal]}, {private_removal, [], [private_removal]}, {roster_removal, [], [roster_removal]}, {offline_removal, [], [offline_removal]}, @@ -82,6 +85,9 @@ group_to_modules(mam_removal) -> group_to_modules(muc_light_removal) -> MucHost = subhost_pattern(muc_light_helper:muc_host_pattern()), [{mod_muc_light, [{backend, rdbms}, {host, MucHost}]}]; +group_to_modules(muc_removal) -> + MucHost = subhost_pattern(muc_helper:muc_host_pattern()), + [{mod_muc, [{backend, rdbms}, {host, MucHost}]}]; group_to_modules(inbox_removal) -> [{mod_inbox, []}]; group_to_modules(private_removal) -> @@ -97,12 +103,20 @@ group_to_modules(vcard_removal) -> %%% Testcase specific setup/teardown %%%=================================================================== +init_per_testcase(muc_removal, Config) -> + muc_helper:load_muc(), + mongoose_helper:ensure_muc_clean(), + escalus:init_per_testcase(muc_removal, Config); init_per_testcase(roster_removal, ConfigIn) -> Config = roster_helper:set_versioning(true, true, ConfigIn), escalus:init_per_testcase(roster_removal, Config); init_per_testcase(TestCase, Config) -> escalus:init_per_testcase(TestCase, Config). +end_per_testcase(muc_removal, Config) -> + mongoose_helper:ensure_muc_clean(), + muc_helper:unload_muc(), + escalus:end_per_testcase(muc_removal, Config); end_per_testcase(roster_removal, Config) -> roster_helper:restore_versioning(Config), escalus:end_per_testcase(roster_removal, Config); @@ -180,6 +194,27 @@ inbox_removal(Config) -> inbox_helper:get_inbox(Bob, #{count => 0, unread_messages => 0, active_conversations => 0}) end). +muc_removal(Config0) -> + muc_helper:story_with_room(Config0, [{persistent, true}], [{alice, 1}], fun(Config, Alice) -> + AliceJid= jid:from_binary(escalus_client:full_jid(Alice)), + {_, Domain} = jid:to_lus(AliceJid), + MucHost = muc_helper:muc_host(), + % Alice joins room and registers nick + EnterRoom = muc_helper:stanza_muc_enter_room(?config(room, Config), <<"alice">>), + escalus:send(Alice, EnterRoom), + escalus:wait_for_stanzas(Alice, 2), + muc_helper:set_nick(Alice, <<"alice2">>), + % check muc tables + ?assertMatch([_], get_muc_rooms(MucHost)), + ?assertMatch([_], get_muc_room_aff(Domain)), + ?assertMatch({ok, _}, get_muc_registered(MucHost, AliceJid)), + % remove domain and check muc tables + run_remove_domain(), + ?assertMatch([], get_muc_rooms(MucHost)), + ?assertMatch([], get_muc_room_aff(Domain)), + ?assertMatch({error, not_registered}, get_muc_registered(MucHost, AliceJid)) + end). + muc_light_removal(Config0) -> F = fun(Config, Alice) -> %% GIVEN a room @@ -341,6 +376,18 @@ get_vcard_search_query_count(Element) -> {element, <<"count">>}, cdata]). +get_muc_registered(MucHost, UserJid) -> + rpc(mim(), mod_muc_db_rdbms, get_nick, [host_type(), MucHost, UserJid]). + +get_muc_rooms(MucHost) -> + {ok, Rooms} = rpc(mim(), mod_muc_db_rdbms, get_rooms, [host_type(), MucHost]), + Rooms. + +get_muc_room_aff(Domain) -> + Query = "SELECT * FROM muc_room_aff WHERE lserver = '" ++ binary_to_list(Domain) ++ "'", + {selected, Affs} = rpc(mim(), mongoose_rdbms, sql_query, [host_type(), Query]), + Affs. + select_from_roster(Table) -> Query = "SELECT * FROM " ++ Table ++ " WHERE server='" ++ binary_to_list(domain()) ++ "'", {selected, Res} = rpc(mim(), mongoose_rdbms, sql_query, [host_type(), Query]), diff --git a/big_tests/tests/muc_SUITE.erl b/big_tests/tests/muc_SUITE.erl index 42a158eb1a..dcd2549410 100644 --- a/big_tests/tests/muc_SUITE.erl +++ b/big_tests/tests/muc_SUITE.erl @@ -48,7 +48,11 @@ room_address/2, room_address/1, disco_service_story/1, - story_with_room/4 + story_with_room/4, + stanza_form/2, + form_field/1, + change_nick_form_iq/1, + set_nick/2 ]). -import(domain_helper, [host_type/0, domain/0]). @@ -4870,27 +4874,6 @@ stanza_cancel(Room) -> stanza_to_room(escalus_stanza:iq_set( ?NS_MUC_OWNER, Payload), Room). -stanza_form(Payload, Type) -> - #xmlel{ - name = <<"x">>, - attrs = [{<<"xmlns">>,<<"jabber:x:data">>}, {<<"type">>,<<"submit">>}], - children = [form_field({<<"FORM_TYPE">>, Type, <<"hidden">>}) | Payload] - }. - -form_field_item(Value) -> - #xmlel{ name = <<"value">>, - children = [#xmlcdata{content = Value}]}. - -form_field({Var, Value, Type}) when is_list(Value) -> - #xmlel{ name = <<"field">>, - attrs = [{<<"var">>, Var},{<<"type">>, Type}], - children = [form_field_item(V) || V <- Value]}; -form_field({Var, Value, Type}) -> - #xmlel{ name = <<"field">>, - attrs = [{<<"type">>, Type},{<<"var">>, Var}], - children = [#xmlel{name = <<"value">>, - children = [#xmlcdata{content = Value}] }] }. - stanza_instant_room(Room) -> X = #xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_DATA_FORMS}, {<<"type">>, <<"submit">>}]}, @@ -4931,22 +4914,12 @@ get_nick_form_iq() -> GetIQ = escalus_stanza:iq_get(<<"jabber:iq:register">>, []), escalus_stanza:to(GetIQ, muc_host()). -change_nick_form_iq(Nick) -> - NS = <<"jabber:iq:register">>, - NickField = form_field({<<"nick">>, Nick, <<"text-single">>}), - Form = stanza_form([NickField], NS), - SetIQ = escalus_stanza:iq_set(NS, [Form]), - escalus_stanza:to(SetIQ, muc_host()). - remove_nick_form_iq() -> NS = <<"jabber:iq:register">>, RemoveEl = #xmlel{name = <<"remove">>}, SetIQ = escalus_stanza:iq_set(NS, [RemoveEl]), escalus_stanza:to(SetIQ, muc_host()). -set_nick(User, Nick) -> - escalus:send_iq_and_wait_for_result(User, change_nick_form_iq(Nick)). - unset_nick(User) -> escalus:send_iq_and_wait_for_result(User, remove_nick_form_iq()). diff --git a/big_tests/tests/muc_helper.erl b/big_tests/tests/muc_helper.erl index 2a16dae56f..82d70b81db 100644 --- a/big_tests/tests/muc_helper.erl +++ b/big_tests/tests/muc_helper.erl @@ -296,3 +296,38 @@ story_with_room(Config, RoomOpts, [{Owner, _}|_] = UserSpecs, StoryFun) -> after destroy_room(Config2) end. + +%%-------------------------------------------------------------------- +%% Helpers (stanzas) +%%-------------------------------------------------------------------- + +stanza_form(Payload, Type) -> + #xmlel{ + name = <<"x">>, + attrs = [{<<"xmlns">>,<<"jabber:x:data">>}, {<<"type">>,<<"submit">>}], + children = [form_field({<<"FORM_TYPE">>, Type, <<"hidden">>}) | Payload] + }. + +form_field_item(Value) -> + #xmlel{ name = <<"value">>, + children = [#xmlcdata{content = Value}]}. + +form_field({Var, Value, Type}) when is_list(Value) -> + #xmlel{ name = <<"field">>, + attrs = [{<<"var">>, Var},{<<"type">>, Type}], + children = [form_field_item(V) || V <- Value]}; +form_field({Var, Value, Type}) -> + #xmlel{ name = <<"field">>, + attrs = [{<<"type">>, Type},{<<"var">>, Var}], + children = [#xmlel{name = <<"value">>, + children = [#xmlcdata{content = Value}] }] }. + +change_nick_form_iq(Nick) -> + NS = <<"jabber:iq:register">>, + NickField = form_field({<<"nick">>, Nick, <<"text-single">>}), + Form = stanza_form([NickField], NS), + SetIQ = escalus_stanza:iq_set(NS, [Form]), + escalus_stanza:to(SetIQ, muc_helper:muc_host()). + +set_nick(User, Nick) -> + escalus:send_iq_and_wait_for_result(User, change_nick_form_iq(Nick)). diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 65414a7c63..2f5b7c3e45 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -62,6 +62,7 @@ %% Hooks handlers -export([is_muc_room_owner/4, can_access_room/4, + remove_domain/3, get_room_affiliations/2, can_access_identity/4, disco_local_items/1]). @@ -83,8 +84,9 @@ {?MOD_MUC_DB_BACKEND, set_nick, 4}, {?MOD_MUC_DB_BACKEND, store_room, 4}, {?MOD_MUC_DB_BACKEND, unset_nick, 3}, + {?MOD_MUC_DB_BACKEND, remove_domain, 3}, can_access_identity/4, can_access_room/4, get_room_affiliations/2, create_instant_room/6, - disco_local_items/1, hibernated_rooms_number/0, is_muc_room_owner/4, + disco_local_items/1, hibernated_rooms_number/0, is_muc_room_owner/4, remove_domain/3, online_rooms_number/0, register_room/4, restore_room/3, start_link/2 ]). @@ -1252,6 +1254,19 @@ can_access_room(_, _HostType, Room, User) -> {ok, CanAccess} -> CanAccess end. +-spec remove_domain(mongoose_hooks:simple_acc(), + mongooseim:host_type(), jid:lserver()) -> + mongoose_hooks:simple_acc(). +remove_domain(Acc, HostType, Domain) -> + case backend_module:is_exported(mod_muc_db_backend, remove_domain, 3) of + true -> + MUCHost = server_host_to_muc_host(HostType, Domain), + mod_muc_db_backend:remove_domain(HostType, MUCHost, Domain); + false -> + ok + end, + Acc. + -spec get_room_affiliations(mongoose_acc:t(), jid:jid()) -> {mongoose_acc:t(), any()}. get_room_affiliations(Acc1, Room) -> @@ -1340,6 +1355,7 @@ config_metrics(HostType) -> hooks(HostType) -> [{is_muc_room_owner, HostType, ?MODULE, is_muc_room_owner, 50}, {can_access_room, HostType, ?MODULE, can_access_room, 50}, + {remove_domain, HostType, ?MODULE, remove_domain, 50}, {get_room_affiliations, HostType, ?MODULE, get_room_affiliations, 50}, {can_access_identity, HostType, ?MODULE, can_access_identity, 50}, {disco_local_items, HostType, ?MODULE, disco_local_items, 250}]. diff --git a/src/mod_muc_db.erl b/src/mod_muc_db.erl index 0a38b758c7..675a5fa4d8 100644 --- a/src/mod_muc_db.erl +++ b/src/mod_muc_db.erl @@ -49,3 +49,7 @@ %% Unregistered nicks can be used by someone else -callback unset_nick(server_host(), muc_host(), client_jid()) -> ok | {error, term()}. + +-callback remove_domain(mongooseim:host_type(), muc_host(), jid:lserver()) -> ok. + +-optional_callbacks([remove_domain/3]). diff --git a/src/mod_muc_db_rdbms.erl b/src/mod_muc_db_rdbms.erl index d231b99a46..4a628e4707 100644 --- a/src/mod_muc_db_rdbms.erl +++ b/src/mod_muc_db_rdbms.erl @@ -10,10 +10,11 @@ can_use_nick/4, get_nick/3, set_nick/4, - unset_nick/3 + unset_nick/3, + remove_domain/3 ]). --ignore_xref([can_use_nick/4, forget_room/3, get_nick/3, get_rooms/2, init/2, +-ignore_xref([can_use_nick/4, forget_room/3, get_nick/3, get_rooms/2, remove_domain/3, init/2, restore_room/3, set_nick/4, store_room/4, unset_nick/3]). -import(mongoose_rdbms, [prepare/4, execute_successfully/3]). @@ -56,6 +57,9 @@ prepare_queries(ServerHost) -> prepare(muc_delete_room, muc_rooms, [muc_host, room_name], <<"DELETE FROM muc_rooms WHERE muc_host = ? AND room_name = ?">>), + prepare(muc_rooms_remove_domain, muc_rooms, + [muc_host], + <<"DELETE FROM muc_rooms WHERE muc_host = ?">>), prepare(muc_select_rooms, muc_rooms, [muc_host], <<"SELECT id, room_name, options FROM muc_rooms WHERE muc_host = ?">>), %% Queries to muc_room_aff table @@ -70,6 +74,13 @@ prepare_queries(ServerHost) -> "FROM muc_room_aff WHERE room_id = ?">>), prepare(muc_delete_aff, muc_room_aff, [room_id], <<"DELETE FROM muc_room_aff WHERE room_id = ?">>), + prepare(muc_room_aff_remove_room_domain, muc_room_aff, + ['muc_rooms.muc_host'], + <<"DELETE FROM muc_room_aff WHERE room_id IN " + "(SELECT id FROM muc_rooms WHERE muc_host = ?)">>), + prepare(muc_room_aff_remove_user_domain, muc_room_aff, + [lserver], + <<"DELETE FROM muc_room_aff WHERE lserver = ?">>), %% Queries to muc_registered table prepare(muc_select_nick_user, muc_registered, [muc_host, lserver, nick], @@ -83,6 +94,12 @@ prepare_queries(ServerHost) -> [muc_host, lserver, luser], <<"DELETE FROM muc_registered WHERE muc_host = ?" " AND lserver = ? AND luser = ?">>), + prepare(muc_registered_remove_room_domain, muc_registered, + [muc_host], + <<"DELETE FROM muc_registered WHERE muc_host = ?">>), + prepare(muc_registered_remove_user_domain, muc_registered, + [lserver], + <<"DELETE FROM muc_room_aff WHERE lserver = ?">>), rdbms_queries:prepare_upsert(ServerHost, muc_nick_upsert, muc_registered, [<<"muc_host">>, <<"luser">>, <<"lserver">>, <<"nick">>], [<<"nick">>], @@ -91,6 +108,24 @@ prepare_queries(ServerHost) -> %% Room API functions +-spec remove_domain(mongooseim:host_type(), muc_host(), jid:lserver()) -> ok. +remove_domain(HostType, MucHost, Domain) -> + F = fun() -> + mongoose_rdbms:execute_successfully( + HostType, muc_registered_remove_room_domain, [MucHost]), + mongoose_rdbms:execute_successfully( + HostType, muc_registered_remove_user_domain, [Domain]), + mongoose_rdbms:execute_successfully( + HostType, muc_room_aff_remove_room_domain, [MucHost]), + mongoose_rdbms:execute_successfully( + HostType, muc_room_aff_remove_user_domain, [Domain]), + mongoose_rdbms:execute_successfully( + HostType, muc_rooms_remove_domain, [MucHost]), + ok + end, + {atomic, ok} = mongoose_rdbms:sql_transaction(HostType, F), + ok. + -spec store_room(server_host(), muc_host(), mod_muc:room(), room_opts()) -> ok | {error, term()}. store_room(ServerHost, MucHost, RoomName, Opts) ->