1: -module(domain_removal_SUITE).
    2: 
    3: -compile([export_all, nowarn_export_all]).
    4: 
    5: -import(distributed_helper, [mim/0, rpc/4, subhost_pattern/1]).
    6: -import(domain_helper, [host_type/0, domain_to_host_type/2, domain/0]).
    7: -import(config_parser_helper, [mod_config/2]).
    8: 
    9: -include("mam_helper.hrl").
   10: -include_lib("eunit/include/eunit.hrl").
   11: -include_lib("exml/include/exml_stream.hrl").
   12: -include_lib("jid/include/jid.hrl").
   13: -include_lib("common_test/include/ct.hrl").
   14: 
   15: all() ->
   16:     [
   17:      {group, auth_removal},
   18:      {group, cache_removal},
   19:      {group, mam_removal},
   20:      {group, inbox_removal},
   21:      {group, muc_light_removal},
   22:      {group, muc_removal},
   23:      {group, private_removal},
   24:      {group, roster_removal},
   25:      {group, offline_removal},
   26:      {group, markers_removal},
   27:      {group, vcard_removal},
   28:      {group, last_removal}
   29:     ].
   30: 
   31: groups() ->
   32:     [
   33:      {auth_removal, [], [auth_removal]},
   34:      {cache_removal, [], [cache_removal]},
   35:      {mam_removal, [], [mam_pm_removal,
   36:                         mam_muc_removal]},
   37:      {inbox_removal, [], [inbox_removal]},
   38:      {muc_light_removal, [], [muc_light_removal,
   39:                               muc_light_blocking_removal]},
   40:      {muc_removal, [], [muc_removal]},
   41:      {private_removal, [], [private_removal]},
   42:      {roster_removal, [], [roster_removal]},
   43:      {offline_removal, [], [offline_removal]},
   44:      {markers_removal, [], [markers_removal]},
   45:      {vcard_removal, [], [vcard_removal]},
   46:      {last_removal, [], [last_removal]}
   47:     ].
   48: 
   49: %%%===================================================================
   50: %%% Overall setup/teardown
   51: %%%===================================================================
   52: init_per_suite(Config) ->
   53:     escalus:init_per_suite(Config).
   54: 
   55: end_per_suite(Config) ->
   56:     escalus_fresh:clean(),
   57:     escalus:end_per_suite(Config).
   58: 
   59: %%%===================================================================
   60: %%% Group specific setup/teardown
   61: %%%===================================================================
   62: init_per_group(Group, Config) ->
   63:     case mongoose_helper:is_rdbms_enabled(host_type()) of
   64:         true ->
   65:             HostTypes = domain_helper:host_types(),
   66:             Config2 = dynamic_modules:save_modules(HostTypes, Config),
   67:             [dynamic_modules:ensure_modules(HostType, group_to_modules(Group)) ||
   68:                 HostType <- HostTypes],
   69:             Config2;
   70:         false ->
   71:             {skip, require_rdbms}
   72:     end.
   73: 
   74: end_per_group(_Groupname, Config) ->
   75:     case mongoose_helper:is_rdbms_enabled(host_type()) of
   76:         true ->
   77:             dynamic_modules:restore_modules(Config);
   78:         false ->
   79:             ok
   80:     end,
   81:     ok.
   82: 
   83: group_to_modules(auth_removal) ->
   84:     [];
   85: group_to_modules(cache_removal) ->
   86:     [{mod_cache_users, config_parser_helper:default_mod_config(mod_cache_users)},
   87:      {mod_mam_meta, mam_helper:config_opts(#{pm => #{}})}];
   88: group_to_modules(mam_removal) ->
   89:     MucHost = subhost_pattern(muc_light_helper:muc_host_pattern()),
   90:     [{mod_mam_meta, mam_helper:config_opts(#{pm => #{}, muc => #{host => MucHost}})},
   91:      {mod_muc_light, mod_config(mod_muc_light, #{backend => rdbms})}];
   92: group_to_modules(muc_light_removal) ->
   93:     [{mod_muc_light, mod_config(mod_muc_light, #{backend => rdbms})}];
   94: group_to_modules(muc_removal) ->
   95:     MucHost = subhost_pattern(muc_helper:muc_host_pattern()),
   96:     Opts = #{backend => rdbms, host => MucHost},
   97:     [{mod_muc, muc_helper:make_opts(Opts)}];
   98: group_to_modules(inbox_removal) ->
   99:     [{mod_inbox, inbox_helper:inbox_opts()}];
  100: group_to_modules(private_removal) ->
  101:     [{mod_private, #{iqdisc => one_queue, backend => rdbms}}];
  102: group_to_modules(roster_removal) ->
  103:     [{mod_roster, mod_config(mod_roster, #{backend => rdbms})}];
  104: group_to_modules(offline_removal) ->
  105:     [{mod_offline, [{backend, rdbms}]}];
  106: group_to_modules(markers_removal) ->
  107:     [{mod_smart_markers, config_parser_helper:default_mod_config(mod_smart_markers)}];
  108: group_to_modules(vcard_removal) ->
  109:     [{mod_vcard, mod_config(mod_vcard, #{backend => rdbms})}];
  110: group_to_modules(last_removal) ->
  111:     [{mod_last, mod_config(mod_last, #{backend => rdbms})}].
  112: 
  113: %%%===================================================================
  114: %%% Testcase specific setup/teardown
  115: %%%===================================================================
  116: 
  117: init_per_testcase(muc_removal, Config) ->
  118:     muc_helper:load_muc(),
  119:     mongoose_helper:ensure_muc_clean(),
  120:     escalus:init_per_testcase(muc_removal, Config);
  121: init_per_testcase(roster_removal, ConfigIn) ->
  122:     Config = roster_helper:set_versioning(true, true, ConfigIn),
  123:     escalus:init_per_testcase(roster_removal, Config);
  124: init_per_testcase(TestCase, Config) ->
  125:     escalus:init_per_testcase(TestCase, Config).
  126: 
  127: end_per_testcase(muc_removal, Config) ->
  128:     mongoose_helper:ensure_muc_clean(),
  129:     muc_helper:unload_muc(),
  130:     escalus:end_per_testcase(muc_removal, Config);
  131: end_per_testcase(TestCase, Config) ->
  132:     escalus:end_per_testcase(TestCase, Config).
  133: 
  134: %%%===================================================================
  135: %%% Test Cases
  136: %%%===================================================================
  137: 
  138: auth_removal(Config) ->
  139:     FreshConfig = escalus_fresh:create_users(Config, [{alice, 1}, {alice_bis, 1}]),
  140:     AliceSpec = escalus_users:get_userspec(FreshConfig, alice),
  141:     AliceBisSpec = escalus_users:get_userspec(FreshConfig, alice_bis),
  142:     connect_and_disconnect(AliceSpec),
  143:     connect_and_disconnect(AliceBisSpec),
  144:     ?assertMatch([_Alice], rpc(mim(), ejabberd_auth, get_vh_registered_users, [domain()])),
  145:     run_remove_domain(),
  146:     ?assertMatch({error, {connection_step_failed, _, _}}, escalus_connection:start(AliceSpec)),
  147:     connect_and_disconnect(AliceBisSpec), % different domain - not removed
  148:     ?assertEqual([], rpc(mim(), ejabberd_auth, get_vh_registered_users, [domain()])).
  149: 
  150: cache_removal(Config) ->
  151:     FreshConfig = escalus_fresh:create_users(Config, [{alice, 1}, {alice_bis, 1}]),
  152:     F = fun(Alice, AliceBis) ->
  153:                 escalus:send(Alice, escalus_stanza:chat_to(AliceBis, <<"Hi!">>)),
  154:                 escalus:wait_for_stanza(AliceBis),
  155:                 mam_helper:wait_for_archive_size(Alice, 1),
  156:                 mam_helper:wait_for_archive_size(AliceBis, 1)
  157:         end,
  158:     escalus:story(FreshConfig, [{alice, 1}, {alice_bis, 1}], F),
  159:     %% Storing the message in MAM should have populated the cache for both users
  160:     ?assertEqual({stop, true}, does_cached_user_exist(FreshConfig, alice)),
  161:     ?assertEqual({stop, true}, does_cached_user_exist(FreshConfig, alice_bis)),
  162:     run_remove_domain(),
  163:     %% Cache removed only for Alice's domain
  164:     ?assertEqual(false, does_cached_user_exist(FreshConfig, alice)),
  165:     ?assertEqual({stop, true}, does_cached_user_exist(FreshConfig, alice_bis)).
  166: 
  167: mam_pm_removal(Config) ->
  168:     F = fun(Alice, Bob) ->
  169:         escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)),
  170:         escalus:wait_for_stanza(Bob),
  171:         mam_helper:wait_for_archive_size(Alice, 1),
  172:         mam_helper:wait_for_archive_size(Bob, 1),
  173:         run_remove_domain(),
  174:         mam_helper:wait_for_archive_size(Alice, 0),
  175:         mam_helper:wait_for_archive_size(Bob, 0)
  176:         end,
  177:     escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F).
  178: 
  179: mam_muc_removal(Config0) ->
  180:     F = fun(Config, Alice) ->
  181:         Room = muc_helper:fresh_room_name(),
  182:         MucHost = muc_light_helper:muc_host(),
  183:         muc_light_helper:create_room(Room, MucHost, alice,
  184:                                      [], Config, muc_light_helper:ver(1)),
  185:         RoomAddr = <<Room/binary, "@", MucHost/binary>>,
  186:         escalus:send(Alice, escalus_stanza:groupchat_to(RoomAddr, <<"text">>)),
  187:         escalus:wait_for_stanza(Alice),
  188:         mam_helper:wait_for_room_archive_size(MucHost, Room, 1),
  189:         run_remove_domain(),
  190:         mam_helper:wait_for_room_archive_size(MucHost, Room, 0)
  191:         end,
  192:     escalus_fresh:story_with_config(Config0, [{alice, 1}], F).
  193: 
  194: inbox_removal(Config) ->
  195:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  196:         escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)),
  197:         escalus:wait_for_stanza(Bob),
  198:         inbox_helper:get_inbox(Alice, #{count => 1}),
  199:         inbox_helper:get_inbox(Bob, #{count => 1}),
  200:         run_remove_domain(),
  201:         inbox_helper:get_inbox(Alice, #{count => 0, unread_messages => 0, active_conversations => 0}),
  202:         inbox_helper:get_inbox(Bob, #{count => 0, unread_messages => 0, active_conversations => 0})
  203:       end).
  204: 
  205: muc_removal(Config0) ->
  206:     muc_helper:story_with_room(Config0, [{persistent, true}], [{alice, 1}], fun(Config, Alice) ->
  207:         AliceJid= jid:from_binary(escalus_client:full_jid(Alice)),
  208:         {_, Domain} = jid:to_lus(AliceJid),
  209:         MucHost = muc_helper:muc_host(),
  210:         % Alice joins room and registers nick
  211:         EnterRoom = muc_helper:stanza_muc_enter_room(?config(room, Config), <<"alice">>),
  212:         escalus:send(Alice, EnterRoom),
  213:         escalus:wait_for_stanzas(Alice, 2),
  214:         muc_helper:set_nick(Alice, <<"alice2">>),
  215:         % check muc tables
  216:         ?assertMatch([_], get_muc_rooms(MucHost)),
  217:         ?assertMatch([_], get_muc_room_aff(Domain)),
  218:         ?assertMatch({ok, _}, get_muc_registered(MucHost, AliceJid)),
  219:         % remove domain and check muc tables
  220:         run_remove_domain(),
  221:         ?assertMatch([], get_muc_rooms(MucHost)),
  222:         ?assertMatch([], get_muc_room_aff(Domain)),
  223:         ?assertMatch({error, not_registered}, get_muc_registered(MucHost, AliceJid))
  224:     end).
  225: 
  226: muc_light_removal(Config0) ->
  227:     F = fun(Config, Alice) ->
  228:         %% GIVEN a room
  229:         Room = muc_helper:fresh_room_name(),
  230:         MucHost = muc_light_helper:muc_host(),
  231:         RoomAddr = <<Room/binary, "@", MucHost/binary>>,
  232:         muc_light_helper:create_room(Room, MucHost, alice,
  233:                                      [], Config, muc_light_helper:ver(1)),
  234:         escalus:send(Alice, escalus_stanza:groupchat_to(RoomAddr, <<"text">>)),
  235:         escalus:wait_for_stanza(Alice),
  236:         RoomID = select_room_id(host_type(), Room, MucHost),
  237:         {selected, [_]} = select_affs_by_room_id(host_type(), RoomID),
  238:         {selected, [_|_]} = select_config_by_room_id(host_type(), RoomID),
  239:         {ok, _RoomConfig, _AffUsers, _Version} = get_room_info(host_type(), Room, MucHost),
  240:         %% WHEN domain hook called
  241:         run_remove_domain(),
  242:         %% THEN Room info not available
  243:         {error, not_exists} = get_room_info(host_type(), Room, MucHost),
  244:         %% THEN Tables are empty
  245:         {selected, []} = select_affs_by_room_id(host_type(), RoomID),
  246:         {selected, []} = select_config_by_room_id(host_type(), RoomID)
  247:         end,
  248:     escalus_fresh:story_with_config(Config0, [{alice, 1}], F).
  249: 
  250: muc_light_blocking_removal(Config0) ->
  251:     F = fun(Config, Alice, Bob) ->
  252:         %% GIVEN a room
  253:         Room = muc_helper:fresh_room_name(),
  254:         MucHost = muc_light_helper:muc_host(),
  255:         muc_light_helper:create_room(Room, MucHost, alice,
  256:                                      [], Config, muc_light_helper:ver(1)),
  257:         block_muclight_user(Bob, Alice),
  258:         [_] = get_blocking(host_type(), Bob, MucHost),
  259:         %% WHEN domain hook called
  260:         run_remove_domain(),
  261:         [] = get_blocking(host_type(), Bob, MucHost)
  262:         end,
  263:     escalus_fresh:story_with_config(Config0, [{alice, 1}, {bob, 1}], F).
  264: 
  265: private_removal(Config) ->
  266:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  267:         NS = <<"alice:private:ns">>,
  268:         Tag = <<"my_element">>,
  269:         %% Alice stores some data in her private storage
  270:         IqSet = escalus_stanza:private_set(my_banana(NS)),
  271:         IqGet = escalus_stanza:private_get(NS, Tag),
  272:         escalus:send_iq_and_wait_for_result(Alice, IqSet),
  273:         %% Compare results before and after removal
  274:         Res1 = escalus_client:send_iq_and_wait_for_result(Alice, IqGet),
  275:         run_remove_domain(),
  276:         Res2 = escalus_client:send_iq_and_wait_for_result(Alice, IqGet),
  277:         escalus:assert(is_private_result, Res1),
  278:         escalus:assert(is_private_result, Res2),
  279:         Val1 = get_private_data(Res1, Tag, NS),
  280:         Val2 = get_private_data(Res2, Tag, NS),
  281:         ?assert_equal_extra(<<"banana">>, Val1, #{stanza => Res1}),
  282:         ?assert_equal_extra(<<>>, Val2, #{stanza => Res2})
  283:       end).
  284: 
  285: offline_removal(Config) ->
  286:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}], fun(FreshConfig, Alice, Bob) ->
  287:         mongoose_helper:logout_user(FreshConfig, Bob),
  288:         escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"msgtxt">>)),
  289:         % wait until message is stored
  290:         BobJid = jid:from_binary(escalus_client:full_jid(Bob)),
  291:         {LUser, LServer} = jid:to_lus(BobJid),
  292:         mongoose_helper:wait_until(
  293:           fun() -> mongoose_helper:total_offline_messages({LUser, LServer}) end, 1),
  294:         % check messages in DB
  295:         ?assertMatch({ok, [_]}, rpc(mim(), mod_offline_rdbms, fetch_messages, [host_type(), BobJid])),
  296:         run_remove_domain(),
  297:         ?assertMatch({ok, []}, rpc(mim(), mod_offline_rdbms, fetch_messages, [host_type(), BobJid]))
  298:     end).
  299: 
  300: markers_removal(Config) ->
  301:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  302:         Body = <<"Hello Bob!">>,
  303:         MsgId = escalus_stanza:id(),
  304:         Msg = escalus_stanza:set_id(escalus_stanza:chat_to(Bob, Body), MsgId),
  305:         escalus:send(Alice, Msg),
  306:         escalus:wait_for_stanza(Bob),
  307:         ChatMarker = escalus_stanza:chat_marker(Alice, <<"displayed">>, MsgId),
  308:         escalus:send(Bob, ChatMarker),
  309:         escalus:wait_for_stanza(Alice),
  310:         mongoose_helper:wait_until(
  311:           fun() -> 1 =< mongoose_helper:generic_count(mod_smart_markers) end, true),
  312:         % check messages in DB
  313:         AliceJid = jid:from_binary(escalus_client:full_jid(Alice)),
  314:         ?assertMatch([_], rpc(mim(), mod_smart_markers_backend, get_chat_markers,
  315:                                     [host_type(), AliceJid, undefined, 0])),
  316:         run_remove_domain(),
  317:         ?assertMatch([], rpc(mim(), mod_smart_markers_backend, get_chat_markers,
  318:                                    [host_type(), AliceJid, undefined, 0]))
  319:     end).
  320: 
  321: roster_removal(Config) ->
  322:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  323:         %% add contact
  324:         Stanza = escalus_stanza:roster_add_contact(Bob, [<<"friends">>], <<"Bobby">>),
  325:         escalus:send(Alice, Stanza),
  326:         Received = escalus:wait_for_stanzas(Alice, 2),
  327:         escalus:assert_many([is_roster_set, is_iq_result], Received),
  328: 
  329:         %% check roster
  330:         BobJid = escalus_client:short_jid(Bob),
  331:         Received2 = escalus:send_iq_and_wait_for_result(Alice, escalus_stanza:roster_get()),
  332:         escalus:assert(is_roster_result, Received2),
  333:         escalus:assert(roster_contains, [BobJid], Received2),
  334:         escalus:assert(count_roster_items, [1], Received2),
  335:         ?assertMatch([_], select_from_roster("rosterusers")),
  336:         ?assertMatch([_], select_from_roster("rostergroups")),
  337:         ?assertMatch([_], select_from_roster("roster_version")),
  338: 
  339:         %% remove domain and check roster
  340:         run_remove_domain(),
  341:         Received3 = escalus:send_iq_and_wait_for_result(Alice, escalus_stanza:roster_get()),
  342:         escalus:assert(is_roster_result, Received3),
  343:         escalus:assert(count_roster_items, [0], Received3),
  344:         ?assertMatch([], select_from_roster("rosterusers")),
  345:         ?assertMatch([], select_from_roster("rostergroups")),
  346:         ?assertMatch([], select_from_roster("roster_version"))
  347:     end).
  348: 
  349: vcard_removal(Config) ->
  350:     escalus:fresh_story(Config, [{alice, 1}], fun(Client) ->
  351:         DirJID = <<"vjud.", (domain())/binary>>,
  352:         {LUser, LServer} = jid:to_lus(jid:from_binary(escalus_client:full_jid(Client))),
  353:         VCardFields = [{<<"FN">>, <<"Old name">>}],
  354:         FilterFields = [{<<"fn">>, <<"Old name">>}],
  355:         DbFilterFields = [{<<"fn">>, [<<"Old name">>]}],
  356:         %create vcard for alice
  357:         UpdateResult = escalus:send_and_wait(Client,
  358:                                              escalus_stanza:vcard_update(VCardFields)),
  359:         escalus:assert(is_iq_result, UpdateResult),
  360:         %check before domain removal
  361:         RequestResult = escalus:send_and_wait(Client, escalus_stanza:vcard_request()),
  362:         ?assertMatch(<<"Old name">>, get_vcard_fn(RequestResult)),
  363:         SearchResult = escalus:send_and_wait(Client,
  364:                                              search_vcard_fields(DirJID, FilterFields)),
  365:         ?assertMatch(<<"1">>, get_vcard_search_query_count(SearchResult)),
  366:         ?assertMatch({ok, _}, rpc(mim(), mod_vcard_rdbms, get_vcard,
  367:                                   [host_type(), LUser, LServer])),
  368:         ?assertMatch([_], rpc(mim(), mod_vcard_rdbms, search,
  369:                               [host_type(), LServer, DbFilterFields])),
  370:         %check after domain removal
  371:         run_remove_domain(),
  372:         RequestResult2 = escalus:send_and_wait(Client, escalus_stanza:vcard_request()),
  373:         escalus:assert(is_iq_error, RequestResult2),
  374:         SearchResult2 = escalus:send_and_wait(Client,
  375:                                               search_vcard_fields(DirJID, FilterFields)),
  376:         ?assertMatch(<<"0">>, get_vcard_search_query_count(SearchResult2)),
  377:         ?assertMatch({error, _}, rpc(mim(), mod_vcard_rdbms, get_vcard,
  378:                                      [host_type(), LUser, LServer])),
  379:         ?assertMatch([], rpc(mim(), mod_vcard_rdbms, search,
  380:                              [host_type(), LServer, DbFilterFields]))
  381:     end).
  382: 
  383: last_removal(Config0) ->
  384:     F = fun(Config2, Alice, Bob) ->
  385:             escalus_story:make_all_clients_friends([Alice, Bob]),
  386: 
  387:             %% Bob logs out with a status
  388:             Status = escalus_stanza:tags([{<<"status">>, <<"I am a banana!">>}]),
  389:             Presence = escalus_stanza:presence(<<"unavailable">>, Status),
  390:             escalus_client:send(Bob, Presence),
  391: 
  392:             escalus_client:stop(Config2, Bob),
  393:             timer:sleep(1024), % more than a second
  394: 
  395:             PresUn = escalus_client:wait_for_stanza(Alice),
  396:             escalus:assert(is_presence_with_type, [<<"unavailable">>], PresUn),
  397:     
  398:             %% Alice asks for Bob's last availability
  399:             BobShortJID = escalus_client:short_jid(Bob),
  400:             GetLast = escalus_stanza:last_activity(BobShortJID),
  401:             Stanza = escalus_client:send_iq_and_wait_for_result(Alice, GetLast),
  402:     
  403:             %% Alice receives Bob's status and last online time > 0
  404:             escalus:assert(is_last_result, Stanza),
  405:             true = (1 =< get_last_activity(Stanza)),
  406:             <<"I am a banana!">> = get_last_status(Stanza),
  407:     
  408:             run_remove_domain(),                                         
  409:             escalus_client:send(Alice, GetLast),
  410:             Error = escalus_client:wait_for_stanza(Alice),
  411:             escalus:assert(is_error, [<<"auth">>, <<"forbidden">>], Error)
  412:         end,
  413:     escalus:fresh_story_with_config(Config0, [{alice, 1}, {bob, 1}], F).
  414: 
  415: %% Helpers
  416: 
  417: connect_and_disconnect(Spec) ->
  418:     {ok, Client, _} = escalus_connection:start(Spec),
  419:     escalus_connection:stop(Client).
  420: 
  421: does_cached_user_exist(Config, User) ->
  422:     Jid = #jid{server = Domain} = jid:from_binary(escalus_users:get_jid(Config, User)),
  423:     HostType = domain_to_host_type(mim(), Domain),
  424:     rpc(mim(), mod_cache_users, does_cached_user_exist, [false, HostType, Jid, stored]).
  425: 
  426: search_vcard_fields(DirJID, Filters) ->
  427:     escalus_stanza:search_iq(DirJID, escalus_stanza:search_fields(Filters)).
  428: 
  429: get_vcard_fn(Element) ->
  430:     exml_query:path(Element, [{element, <<"vCard">>},
  431:                               {element, <<"FN">>},
  432:                               cdata]).
  433: 
  434: get_vcard_search_query_count(Element) ->
  435:     exml_query:path(Element, [{element, <<"query">>},
  436:                               {element, <<"set">>},
  437:                               {element, <<"count">>},
  438:                               cdata]).
  439: 
  440: get_muc_registered(MucHost, UserJid) ->
  441:     rpc(mim(), mod_muc_rdbms, get_nick, [host_type(), MucHost, UserJid]).
  442: 
  443: get_muc_rooms(MucHost) ->
  444:     {ok, Rooms} = rpc(mim(), mod_muc_rdbms, get_rooms, [host_type(), MucHost]),
  445:     Rooms.
  446: 
  447: get_muc_room_aff(Domain) ->
  448:     Query = "SELECT * FROM muc_room_aff WHERE lserver = '" ++ binary_to_list(Domain) ++ "'",
  449:     {selected, Affs} = rpc(mim(), mongoose_rdbms, sql_query, [host_type(), Query]),
  450:     Affs.
  451: 
  452: select_from_roster(Table) ->
  453:     Query = "SELECT * FROM " ++ Table ++ " WHERE server='" ++ binary_to_list(domain()) ++ "'",
  454:     {selected, Res} = rpc(mim(), mongoose_rdbms, sql_query, [host_type(), Query]),
  455:     Res.
  456: 
  457: run_remove_domain() ->
  458:     rpc(mim(), mongoose_hooks, remove_domain, [host_type(), domain()]).
  459: 
  460: get_room_info(HostType, RoomU, RoomS) ->
  461:     rpc(mim(), mod_muc_light_db_backend, get_info, [HostType, {RoomU, RoomS}]).
  462: 
  463: select_room_id(MainHost, RoomU, RoomS) ->
  464:     {selected, [{DbRoomID}]} =
  465:         rpc(mim(), mod_muc_light_db_rdbms, select_room_id, [MainHost, RoomU, RoomS]),
  466:     rpc(mim(), mongoose_rdbms, result_to_integer, [DbRoomID]).
  467: 
  468: select_affs_by_room_id(MainHost, RoomID) ->
  469:     rpc(mim(), mod_muc_light_db_rdbms, select_affs_by_room_id, [MainHost, RoomID]).
  470: 
  471: select_config_by_room_id(MainHost, RoomID) ->
  472:     rpc(mim(), mod_muc_light_db_rdbms, select_config_by_room_id, [MainHost, RoomID]).
  473: 
  474: get_blocking(HostType, User, MUCServer) ->
  475:     Jid = jid:from_binary(escalus_client:short_jid(User)),
  476:     {LUser, LServer, _} = jid:to_lower(Jid),
  477:     rpc(mim(), mod_muc_light_db_rdbms, get_blocking, [HostType, {LUser, LServer}, MUCServer]).
  478: 
  479: block_muclight_user(Bob, Alice) ->
  480:     %% Bob blocks Alice
  481:     AliceJIDBin = escalus_client:short_jid(Alice),
  482:     BlocklistChange = [{user, deny, AliceJIDBin}],
  483:     escalus:send(Bob, muc_light_helper:stanza_blocking_set(BlocklistChange)),
  484:     escalus:assert(is_iq_result, escalus:wait_for_stanza(Bob)).
  485: 
  486: my_banana(NS) ->
  487:     #xmlel{
  488:         name = <<"my_element">>,
  489:         attrs = [{<<"xmlns">>, NS}],
  490:         children = [#xmlcdata{content = <<"banana">>}]}.
  491: 
  492: get_private_data(Elem, Tag, NS) ->
  493:     Path = [{element, <<"query">>}, {element_with_ns, Tag, NS}, cdata],
  494:     exml_query:path(Elem, Path).
  495: 
  496: get_last_activity(Stanza) ->
  497:     S = exml_query:path(Stanza, [{element, <<"query">>}, {attr, <<"seconds">>}]),
  498:     list_to_integer(binary_to_list(S)).
  499: 
  500: get_last_status(Stanza) ->
  501:     exml_query:path(Stanza, [{element, <<"query">>}, cdata]).