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