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]).