1: -module(gdpr_SUITE).
    2: 
    3: %% Tests for features related to GDPR compliance.
    4: 
    5: -include_lib("common_test/include/ct.hrl").
    6: -include_lib("escalus/include/escalus.hrl").
    7: -include_lib("exml/include/exml.hrl").
    8: -include_lib("eunit/include/eunit.hrl").
    9: -include("muc_light.hrl").
   10: 
   11: -export([suite/0, all/0, groups/0]).
   12: -export([init_per_suite/1, end_per_suite/1]).
   13: -export([init_per_group/2, end_per_group/2]).
   14: -export([init_per_testcase/2, end_per_testcase/2]).
   15: -export([
   16:          retrieve_vcard/1,
   17:          remove_vcard/1,
   18:          remove_private/1,
   19:          remove_multiple_private_xmls/1,
   20:          dont_remove_other_user_private_xml/1,
   21:          retrieve_roster/1,
   22:          retrieve_mam_pm/1,
   23:          retrieve_mam_muc/1,
   24:          retrieve_mam_muc_private_msg/1,
   25:          retrieve_mam_muc_store_pm/1,
   26:          remove_mam_pm/1,
   27:          retrieve_mam_muc_light/1,
   28:          retrieve_mam_pm_and_muc_light_interfere/1,
   29:          retrieve_mam_pm_and_muc_light_dont_interfere/1,
   30:          remove_roster/1,
   31:          retrieve_offline/1,
   32:          remove_offline/1,
   33:          retrieve_pubsub_payloads/1,
   34:          retrieve_created_pubsub_nodes/1,
   35:          retrieve_all_pubsub_data/1,
   36:          dont_retrieve_other_user_pubsub_payload/1,
   37:          retrieve_pubsub_subscriptions/1,
   38:          retrieve_private_xml/1,
   39:          dont_retrieve_other_user_private_xml/1,
   40:          retrieve_multiple_private_xmls/1,
   41:          retrieve_inbox/1,
   42:          remove_inbox/1,
   43:          retrieve_inbox_for_multiple_messages/1,
   44:          retrieve_inbox_muclight/1,
   45:          retrieve_inbox_muc/1,
   46:          remove_inbox_muclight/1,
   47:          remove_inbox_muc/1,
   48:          retrieve_logs/1,
   49:          remove_pubsub_all_data/1,
   50:          remove_pubsub_dont_remove_node_when_only_publisher/1,
   51:          remove_pubsub_subscriptions/1,
   52:          remove_pubsub_dont_remove_flat_pubsub_node/1,
   53:          remove_pubsub_push_node/1,
   54:          remove_pubsub_pep_node/1
   55:         ]).
   56: 
   57: -import(mongooseimctl_helper, [mongooseimctl/3]).
   58: -import(distributed_helper, [mim/0, subhost_pattern/1, rpc/4]).
   59: -import(muc_light_helper, [room_bin_jid/1]).
   60: -import(domain_helper, [host_type/0]).
   61: -import(config_parser_helper, [default_mod_config/1, mod_config/2]).
   62: -import(graphql_helper, [execute_command/4, get_ok_value/2]).
   63: 
   64: -define(ROOM, <<"tt1">>).
   65: 
   66: %% -------------------------------------------------------------
   67: %% Common Test stuff
   68: %% -------------------------------------------------------------
   69: 
   70: suite() ->
   71:     escalus:suite().
   72: 
   73: all() ->
   74:     [
   75:      {group, retrieve_personal_data},
   76:      {group, remove_personal_data}
   77:     ].
   78: 
   79: groups() ->
   80:     %% **DON'T** make any of these groups parallel, because calling mongooseimctl
   81:     %% in parallel is broken!
   82:     [
   83:      {retrieve_personal_data, [], [
   84:                                    retrieve_vcard,
   85:                                    retrieve_roster,
   86:                                    retrieve_offline,
   87:                                    retrieve_inbox,
   88:                                    retrieve_logs,
   89:                                    {group, retrieve_personal_data_pubsub},
   90:                                    {group, retrieve_personal_data_private_xml},
   91:                                    {group, retrieve_personal_data_mam},
   92:                                    {group, retrieve_personal_data_inbox}
   93:                                   ]},
   94:     {retrieve_personal_data_inbox, [],[
   95:         retrieve_inbox,
   96:         retrieve_inbox_for_multiple_messages,
   97:         retrieve_inbox_muclight,
   98:         retrieve_inbox_muc
   99:     ]},
  100:      {retrieve_personal_data_pubsub, [], [
  101:                                           retrieve_pubsub_payloads,
  102:                                           dont_retrieve_other_user_pubsub_payload,
  103:                                           retrieve_pubsub_subscriptions,
  104:                                           retrieve_created_pubsub_nodes,
  105:                                           retrieve_all_pubsub_data
  106:                                          ]},
  107:      {retrieve_personal_data_private_xml, [], [
  108:                                                retrieve_private_xml,
  109:                                                dont_retrieve_other_user_private_xml,
  110:                                                retrieve_multiple_private_xmls
  111:                                               ]},
  112:      {retrieve_personal_data_mam, [], [
  113:                                        {group, retrieve_personal_data_mam_rdbms},
  114:                                        {group, retrieve_personal_data_mam_cassandra},
  115:                                        {group, retrieve_personal_data_mam_elasticsearch}
  116:                                       ]},
  117:      {retrieve_personal_data_mam_rdbms, [], all_mam_testcases()},
  118:      {retrieve_personal_data_mam_cassandra, [], all_mam_testcases()},
  119:      {retrieve_personal_data_mam_elasticsearch, [], all_mam_testcases()},
  120:      {remove_personal_data, [], removal_testcases()},
  121:      {remove_personal_data_inbox, [], [remove_inbox, remove_inbox_muclight, remove_inbox_muc]},
  122:      {remove_personal_data_mam, [], [
  123:                                      {group, remove_personal_data_mam_rdbms},
  124:                                      {group, remove_personal_data_mam_cassandra},
  125:                                      {group, remove_personal_data_mam_elasticsearch}
  126:                                     ]},
  127:      {remove_personal_data_mam_rdbms, [], mam_removal_testcases()},
  128:      {remove_personal_data_mam_cassandra, [], mam_removal_testcases()},
  129:      {remove_personal_data_mam_elasticsearch, [], mam_removal_testcases()},
  130:      {remove_personal_data_pubsub, [], [
  131:                                         remove_pubsub_subscriptions,
  132:                                         remove_pubsub_dont_remove_node_when_only_publisher,
  133:                                         remove_pubsub_dont_remove_flat_pubsub_node,
  134:                                         remove_pubsub_push_node,
  135:                                         remove_pubsub_pep_node,
  136:                                         remove_pubsub_all_data
  137:                                        ]}
  138:     ].
  139: 
  140: removal_testcases() ->
  141:     [
  142:         remove_vcard,
  143:         remove_roster,
  144:         remove_offline,
  145:         remove_private,
  146:         remove_multiple_private_xmls,
  147:         dont_remove_other_user_private_xml,
  148:         {group, remove_personal_data_inbox},
  149:         {group, remove_personal_data_pubsub},
  150:         {group, remove_personal_data_mam}
  151:     ].
  152: 
  153: mam_removal_testcases() ->
  154:     [
  155:      remove_mam_pm
  156:     ].
  157: 
  158: 
  159: mam_testcases() ->
  160:     [
  161:         retrieve_mam_pm,
  162:         retrieve_mam_muc_light,
  163:         retrieve_mam_pm_and_muc_light_interfere,
  164:         retrieve_mam_pm_and_muc_light_dont_interfere
  165:     ].
  166: 
  167: all_mam_testcases() ->
  168:     [
  169:         retrieve_mam_muc,
  170:         retrieve_mam_muc_private_msg,
  171:         retrieve_mam_muc_store_pm
  172:         | mam_testcases()
  173:     ].
  174: 
  175: init_per_suite(Config) ->
  176:     #{node := MimNode} = distributed_helper:mim(),
  177:     Config1 = [{{ejabberd_cwd, MimNode}, get_mim_cwd()} | dynamic_modules:save_modules(host_type(), Config)],
  178:     muc_helper:load_muc(),
  179:     Config2 = graphql_helper:init_admin_cli(Config1),
  180:     escalus:init_per_suite(Config2).
  181: 
  182: end_per_suite(Config) ->
  183:     delete_files(),
  184:     escalus_fresh:clean(),
  185:     graphql_helper:clean(),
  186:     dynamic_modules:restore_modules(Config),
  187:     escalus:end_per_suite(Config).
  188: 
  189: init_per_group(GN, Config) when GN =:= remove_personal_data_mam_rdbms;
  190:                                 GN =:= retrieve_personal_data_mam_rdbms ->
  191:     try_backend_for_mam(Config, rdbms);
  192: init_per_group(GN, Config) when GN =:= retrieve_personal_data_pubsub;
  193:                                 GN =:= remove_personal_data_pubsub ->
  194:     [{group, GN} | Config];
  195: init_per_group(GN, Config) when GN =:= retrieve_personal_data_mam_cassandra;
  196:                                 GN =:= remove_personal_data_mam_cassandra->
  197:     try_backend_for_mam(Config, cassandra);
  198: init_per_group(GN, Config) when GN =:= retrieve_personal_data_mam_elasticsearch;
  199:                                 GN =:= remove_personal_data_mam_elasticsearch ->
  200:     try_backend_for_mam(Config, elasticsearch);
  201: init_per_group(retrieve_personal_data_inbox = GN, Config) ->
  202:     init_inbox(GN, Config, muclight);
  203: init_per_group(remove_personal_data_inbox = GN, Config) ->
  204:     init_inbox(GN, Config, muclight);
  205: init_per_group(retrieve_personal_data_private_xml, Config) ->
  206:     private_started(),
  207:     Config;
  208: init_per_group(_GN, Config) ->
  209:     Config.
  210: 
  211: end_per_group(_GN, Config) ->
  212:     Config.
  213: 
  214: try_backend_for_mam(Config, Backend) ->
  215:     case is_backend_enabled(Backend) of
  216:         true -> [{mam_backend, Backend} | Config];
  217:         false -> {skip, backend_is_not_configured}
  218:     end.
  219: 
  220: is_backend_enabled(rdbms)         -> mongoose_helper:is_rdbms_enabled(host_type());
  221: is_backend_enabled(cassandra)     -> mam_helper:is_cassandra_enabled(host_type());
  222: is_backend_enabled(elasticsearch) -> mam_helper:is_elasticsearch_enabled(host_type()).
  223: 
  224: 
  225: init_per_testcase(retrieve_logs = CN, Config) ->
  226:     case is_mim2_started() of
  227:         false -> {skip, not_running_in_distributed};
  228:         _ -> escalus:init_per_testcase(CN, Config)
  229:     end;
  230: init_per_testcase(CN, Config) when CN =:= remove_offline;
  231:                                    CN =:= retrieve_offline ->
  232:     offline_started(),
  233:     escalus:init_per_testcase(CN, Config);
  234: init_per_testcase(CN, Config) when
  235:       CN =:= remove_inbox;
  236:       CN =:= retrieve_inbox;
  237:       CN =:= remove_inbox_muclight;
  238:       CN =:= retrieve_inbox_muclight ->
  239:     Config1 = init_inbox(CN, Config, muclight),
  240:     Config1;
  241: init_per_testcase(CN, Config) when CN =:= retrieve_inbox_muc;
  242:                                    CN =:= remove_inbox_muc ->
  243:     muc_helper:load_muc(),
  244:     Config0 = init_inbox(CN, Config, muc),
  245:     Config0;
  246: 
  247: init_per_testcase(retrieve_vcard = CN, Config) ->
  248:     case vcard_helper:is_vcard_ldap() of
  249:         true ->
  250:             {skip, skipped_for_simplicity_for_now}; % TODO: Fix the case for LDAP as well
  251:         _ ->
  252:             escalus:init_per_testcase(CN, Config)
  253:     end;
  254: init_per_testcase(remove_vcard = CN, Config) ->
  255:     case vcard_helper:is_vcard_ldap() of
  256:         true ->
  257:             {skip, skipped_for_simplicity_for_now}; % TODO: Fix the case for LDAP as well
  258:         _ ->
  259:             vcard_started(),
  260:             escalus:init_per_testcase(CN, Config)
  261:     end;
  262: init_per_testcase(CN, Config) when CN =:= remove_private;
  263:                                    CN =:= dont_remove_other_user_private_xml;
  264:                                    CN =:= remove_multiple_private_xmls ->
  265:     private_started(),
  266:     escalus:init_per_testcase(CN, Config);
  267: 
  268: init_per_testcase(CN, Config) when CN =:= retrieve_mam_muc;
  269:                                    CN =:= retrieve_mam_muc_private_msg;
  270:                                    CN =:= retrieve_mam_muc_store_pm;
  271:                                    CN =:= retrieve_mam_muc_light;
  272:                                    CN =:= retrieve_mam_pm_and_muc_light_interfere;
  273:                                    CN =:= retrieve_mam_pm_and_muc_light_dont_interfere;
  274:                                    CN =:= retrieve_mam_pm;
  275:                                    CN =:= remove_mam_pm ->
  276:     case proplists:get_value(mam_backend, Config, skip) of
  277:         skip ->
  278:             {skip, no_mam_backend_configured};
  279:         Backend ->
  280:             dynamic_modules:restore_modules(Config),
  281:             RequiredModules = mam_required_modules(CN, Backend),
  282:             dynamic_modules:ensure_modules(host_type(), RequiredModules),
  283:             ct:log("required modules: ~p~n", [RequiredModules]),
  284:             escalus:init_per_testcase(CN, [{mam_modules, RequiredModules} | Config])
  285:     end;
  286: init_per_testcase(remove_roster = CN, Config) ->
  287:     roster_started(),
  288:     escalus:init_per_testcase(CN, Config);
  289: init_per_testcase(CN, Config) ->
  290:     GN = proplists:get_value(group, Config),
  291:     IsPubSub = lists:member(GN, [retrieve_personal_data_pubsub, remove_personal_data_pubsub]),
  292:     case IsPubSub of
  293:         true ->
  294:             dynamic_modules:ensure_modules(host_type(), pubsub_required_modules());
  295:         _ ->
  296:             ok
  297:     end,
  298:     escalus:init_per_testcase(CN, Config).
  299: 
  300: 
  301: end_per_testcase(CN, Config) when CN =:= retrieve_mam_muc_light;
  302:                                   CN =:= retrieve_mam_pm_and_muc_light_interfere;
  303:                                   CN =:= retrieve_mam_pm_and_muc_light_dont_interfere ->
  304:     muc_light_helper:clear_db(host_type()),
  305:     escalus:end_per_testcase(CN, Config);
  306: %% mod_inbox
  307: end_per_testcase(CN, Config) when
  308:       CN =:= remove_inbox;
  309:       CN =:= retrieve_inbox;
  310:       CN =:= remove_inbox_muclight;
  311:       CN =:= retrieve_inbox_muclight ->
  312:     muc_light_helper:clear_db(host_type()),
  313:     escalus:end_per_testcase(CN, Config);
  314: end_per_testcase(CN, Config) when CN =:= retrieve_inbox_muc;
  315:                                   CN =:= remove_inbox_muc ->
  316:     muc_helper:unload_muc(),
  317:     escalus:end_per_testcase(CN, Config);
  318: end_per_testcase(CN, Config) ->
  319:     escalus_fresh:clean(),
  320:     escalus:end_per_testcase(CN, Config).
  321: 
  322: init_inbox(CN, Config, GroupChatType) ->
  323:     case (not ct_helper:is_ct_running())
  324:          orelse mongoose_helper:is_rdbms_enabled(host_type()) of
  325:         true ->
  326:             dynamic_modules:ensure_modules(host_type(), inbox_required_modules(GroupChatType)),
  327:             escalus:init_per_testcase(CN, Config);
  328:         false ->
  329:             {skip, require_rdbms}
  330:     end.
  331: inbox_required_modules(Type) ->
  332:     GroupChatModules = groupchat_module(Type),
  333:     InboxOpts = (inbox_helper:inbox_opts())#{groupchat => [Type]},
  334:     Inbox = {mod_inbox, InboxOpts},
  335:     GroupChatModules ++ [Inbox] .
  336: 
  337: groupchat_module(muc) ->
  338:     [];
  339: groupchat_module(muclight) ->
  340:     [{mod_muc_light, mod_config(mod_muc_light,
  341:                                 #{backend => mongoose_helper:mnesia_or_rdbms_backend(),
  342:                                   rooms_in_rosters => true})}].
  343: 
  344: mam_required_modules(CN, Backend)
  345:         when CN =:= remove_mam_pm;
  346:              CN =:= retrieve_mam_pm ->
  347:     [{mod_mam, mam_helper:config_opts(#{backend => Backend, pm => #{}})}];
  348: mam_required_modules(CN, Backend)
  349:         when CN =:= retrieve_mam_pm_and_muc_light_dont_interfere;
  350:              CN =:= retrieve_mam_muc_light ->
  351:     HostPattern = subhost_pattern(muc_light_helper:muc_host_pattern()),
  352:     MucLightOpts = #{backend => mongoose_helper:mnesia_or_rdbms_backend()},
  353:     [{mod_mam, mam_helper:config_opts(#{backend => Backend,
  354:                                         pm => #{},
  355:                                         muc => #{host => HostPattern}})},
  356:      {mod_muc_light, mod_config(mod_muc_light, MucLightOpts)}];
  357: mam_required_modules(retrieve_mam_pm_and_muc_light_interfere, Backend) ->
  358:     HostPattern = subhost_pattern(muc_light_helper:muc_host_pattern()),
  359:     MucLightOpts = #{backend => mongoose_helper:mnesia_or_rdbms_backend()},
  360:     [{mod_mam, mam_helper:config_opts(#{backend => Backend,
  361:                                         db_message_format => mam_message_xml,
  362:                                         pm => #{archive_groupchats => true},
  363:                                         muc => #{host => HostPattern}})},
  364:      {mod_muc_light, mod_config(mod_muc_light, MucLightOpts)}];
  365: mam_required_modules(CN, Backend) when CN =:= retrieve_mam_muc_private_msg;
  366:                                                CN =:= retrieve_mam_muc ->
  367:     HostPattern = subhost_pattern(muc_helper:muc_host_pattern()),
  368:     MucOpts = #{host => HostPattern,
  369:                 online_backend => ct_helper:get_internal_database(),
  370:                 backend => mongoose_helper:mnesia_or_rdbms_backend()},
  371:     [{mod_mam, mam_helper:config_opts(#{backend => Backend,
  372:                                         pm => #{},
  373:                                         muc => #{host => HostPattern}})},
  374:      {mod_muc, muc_helper:make_opts(MucOpts)}];
  375: mam_required_modules(retrieve_mam_muc_store_pm, Backend) ->
  376:     HostPattern = subhost_pattern(muc_helper:muc_host_pattern()),
  377:     MucOpts = #{host => HostPattern,
  378:                 online_backend => ct_helper:get_internal_database(),
  379:                 backend => mongoose_helper:mnesia_or_rdbms_backend()},
  380:     [{mod_mam, mam_helper:config_opts(#{backend => Backend,
  381:                                         pm => #{archive_groupchats => true},
  382:                                         muc => #{host => HostPattern}})},
  383:      {mod_muc, muc_helper:make_opts(MucOpts)}].
  384: 
  385: pick_enabled_backend() ->
  386:     BackendsList = [
  387:         {mongoose_helper:is_rdbms_enabled(host_type()), rdbms}
  388:     ],
  389:     proplists:get_value(true, BackendsList, mnesia).
  390: 
  391: roster_required_modules() ->
  392:     Backend = pick_enabled_backend(),
  393:     [{mod_roster, roster_backend_opts(Backend)}].
  394: 
  395: roster_backend_opts(Backend) ->
  396:     mod_config(mod_roster, #{backend => Backend}).
  397: 
  398: vcard_required_modules() ->
  399:     Backend = pick_enabled_backend(),
  400:     [{mod_vcard, mod_config(mod_vcard, vcard_backend_opts(Backend))}].
  401: 
  402: vcard_backend_opts(Backend) ->
  403:     #{backend => Backend}.
  404: 
  405: offline_required_modules() ->
  406:     [{mod_offline, mod_offline_config(pick_enabled_backend())}].
  407: 
  408: mod_offline_config(Backend) ->
  409:     config_parser_helper:mod_config(mod_offline, #{backend => Backend}).
  410: 
  411: pubsub_required_modules() ->
  412:     pubsub_required_modules([<<"flat">>, <<"pep">>, <<"push">>]).
  413: pubsub_required_modules(Plugins) ->
  414:     HostPattern = subhost_pattern("pubsub.@HOST@"),
  415:     PubsubConfig = mod_config(mod_pubsub, #{backend => mongoose_helper:mnesia_or_rdbms_backend(),
  416:                                             host => HostPattern,
  417:                                             nodetree => nodetree_tree,
  418:                                             plugins => Plugins}),
  419:     HostType = domain_helper:host_type(),
  420:     Backend = mongoose_helper:get_backend_mnesia_rdbms(HostType),
  421:     [{mod_caps, config_parser_helper:mod_config(mod_caps, #{backend => Backend})},
  422:      {mod_pubsub, PubsubConfig}].
  423: 
  424: is_mim2_started() ->
  425:     #{node := Node} = distributed_helper:mim2(),
  426:     case net_adm:ping(Node) of
  427:         pong -> true;
  428:         _ -> false
  429:     end.
  430: 
  431: roster_started() ->
  432:     dynamic_modules:ensure_modules(host_type(), roster_required_modules()).
  433: 
  434: vcard_started() ->
  435:     dynamic_modules:ensure_modules(host_type(), vcard_required_modules()).
  436: 
  437: offline_started() ->
  438:     dynamic_modules:ensure_modules(host_type(), offline_required_modules()).
  439: 
  440: private_required_modules() ->
  441:     [{mod_private, create_private_config(pick_enabled_backend())}].
  442: 
  443: create_private_config(Backend) ->
  444:     mod_config(mod_private, #{backend => Backend}).
  445: 
  446: private_started() ->
  447:     dynamic_modules:ensure_modules(host_type(), private_required_modules()).
  448: 
  449: %% -------------------------------------------------------------
  450: %% Test cases
  451: %% -------------------------------------------------------------
  452: 
  453: %% ------------------------- Data retrieval - per type verification -------------------------
  454: 
  455: retrieve_vcard(Config) ->
  456:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  457:             AliceFields = [{<<"FN">>, <<"Alice">>}, {<<"LN">>, <<"Ecila">>}],
  458:             AliceSetResultStanza
  459:             = escalus:send_and_wait(Alice, escalus_stanza:vcard_update(AliceFields)),
  460:             escalus:assert(is_iq_result, AliceSetResultStanza),
  461:             AliceU = escalus_utils:jid_to_lower(escalus_client:username(Alice)),
  462:             AliceS = escalus_utils:jid_to_lower(escalus_client:server(Alice)),
  463:             ExpectedHeader = ["jid", "vcard"],
  464:             ExpectedItems = [
  465:                              #{ "jid" => [{contains, AliceU},
  466:                                           {contains, AliceS}],
  467:                                 "vcard" => [{contains, "Alice"},
  468:                                             {contains, "Ecila"}] }
  469:                             ],
  470:             retrieve_and_validate_personal_data(
  471:               Alice, Config, "vcard", ExpectedHeader, ExpectedItems)
  472:         end).
  473: 
  474: remove_vcard(Config) ->
  475:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  476:         AliceFields = [{<<"FN">>, <<"Alice">>}, {<<"LN">>, <<"Ecila">>}],
  477:         AliceSetResultStanza
  478:             = escalus:send_and_wait(Alice, escalus_stanza:vcard_update(AliceFields)),
  479:         escalus:assert(is_iq_result, AliceSetResultStanza),
  480: 
  481:         unregister(Alice, Config),
  482: 
  483:         assert_personal_data_via_rpc(Alice, [{vcard,["jid","vcard"],[]}])
  484: 
  485:         end).
  486: 
  487: remove_private(Config) ->
  488:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  489:         %% Add some private data for Alice
  490:         Element = #xmlel{name = <<"item">>,
  491:                          attrs = [{<<"xmlns">>, <<"alice:private_remove:ns">>}],
  492:                          children = [#xmlcdata{ content = <<"Something to declare">> }]},
  493:         SetPrivateResult = escalus:send_and_wait(Alice,
  494:                              escalus_stanza:private_set(Element)),
  495:         escalus:assert(is_iq_result, SetPrivateResult),
  496: 
  497:         %% Verify the data is stored
  498:         assert_personal_data_via_rpc(Alice, [{private, ["ns","xml"],
  499:                    [{<<"alice:private_remove:ns">>,
  500:                      <<"<item xmlns='alice:private_remove:ns'>Something to declare</item>">>}]}]),
  501: 
  502:         %% Remove Alice
  503:         unregister(Alice, Config),
  504: 
  505:         %% Expect Alice's data to be gone
  506:         assert_personal_data_via_rpc(Alice, [{private, ["ns","xml"], []}])
  507: 
  508:         end).
  509: 
  510: dont_remove_other_user_private_xml(Config) ->
  511:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  512:         %% Add some private data for Alice and Bob
  513:         AliceNS = <<"alice:private:ns">>,
  514:         AliceContent = <<"To be or not to be">>,
  515:         BobNS = <<"bob:private:ns">>,
  516:         BobContent = <<"This is the winter of our discontent">>,
  517:         send_and_assert_private_stanza(Alice, AliceNS, AliceContent),
  518:         send_and_assert_private_stanza(Bob, BobNS, BobContent),
  519: 
  520:         %% Remove Alice
  521:         unregister(Alice, Config),
  522: 
  523:         %% Expect Alice's data to be gone
  524:         assert_personal_data_via_rpc(Alice, [{private, ["ns","xml"], []}]),
  525: 
  526:         %% Verify that Bob's data is left intact
  527:         ExpectedHeader = ["ns", "xml"],
  528:         ExpectedItems = [#{ "ns" => binary_to_list(BobNS),
  529:                             "xml" => [{contains, binary_to_list(BobNS)},
  530:                                       {contains, binary_to_list(BobContent)}] }
  531:                         ],
  532:         retrieve_and_validate_personal_data(
  533:             Bob, Config, "private", ExpectedHeader, ExpectedItems)
  534: 
  535:     end).
  536: 
  537: remove_multiple_private_xmls(Config) ->
  538:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  539:         %% Add some private data for Alice for multiple keys
  540:         NSsAndContents = [
  541:                           {<<"alice:private:ns1">>, <<"Some text">>},
  542:                           {<<"alice:private:ns2">>, <<"Other text for another key">>},
  543:                           {<<"alice:private:ns3">>, <<"Even more of text">>}
  544:                          ],
  545:         lists:foreach(
  546:             fun({NS, Content}) ->
  547:                 send_and_assert_private_stanza(Alice, NS, Content)
  548:             end, NSsAndContents),
  549:         ExpectedHeader = ["ns", "xml"],
  550:         ExpectedItems = lists:map(
  551:             fun({NS, Content}) ->
  552:                 #{ "ns" => binary_to_list(NS),
  553:                    "xml" => [{contains, binary_to_list(NS)},
  554:                          {contains, binary_to_list(Content)}]}
  555:             end, NSsAndContents),
  556: 
  557:         %% Verify the data is stored
  558:         retrieve_and_validate_personal_data(
  559:           Alice, Config, "private", ExpectedHeader, ExpectedItems),
  560: 
  561:         %% Remove Alice
  562:         unregister(Alice, Config),
  563: 
  564:         %% Expect all of Alice's data to be gone
  565:         assert_personal_data_via_rpc(Alice, [{private, ["ns","xml"], []}])
  566: 
  567:      end).
  568: 
  569: retrieve_roster(Config) ->
  570:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  571:             escalus_story:make_all_clients_friends([Alice, Bob]),
  572:             BobU = escalus_utils:jid_to_lower(escalus_client:username(Bob)),
  573:             BobS = escalus_utils:jid_to_lower(escalus_client:server(Bob)),
  574:             ExpectedItems = [
  575:                              #{ "jid" => [{contains,  BobU}, {contains, BobS}] }
  576:                             ],
  577:             retrieve_and_validate_personal_data(
  578:                 Alice, Config, "roster", expected_header(mod_roster), ExpectedItems)
  579:         end).
  580: 
  581: remove_roster(Config) ->
  582:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  583:         escalus_story:make_all_clients_friends([Alice, Bob]),
  584:         AliceU = escalus_utils:jid_to_lower(escalus_client:username(Alice)),
  585:         AliceS = escalus_utils:jid_to_lower(escalus_client:server(Alice)),
  586:         ExpectedItems = [
  587:                          #{ "jid" => [{contains,  AliceU}, {contains, AliceS}] }
  588:                         ],
  589: 
  590:         unregister(Alice, Config),
  591: 
  592:         assert_personal_data_via_rpc(Alice, [{roster, expected_header(mod_roster), []}]),
  593:         retrieve_and_validate_personal_data(
  594:                 Bob, Config, "roster", expected_header(mod_roster), ExpectedItems)
  595: 
  596:         end).
  597: 
  598: retrieve_mam_pm(Config) ->
  599:     F = fun(Alice, Bob) ->
  600:             Msg1 = <<"1some simple pm message">>,
  601:             Msg2 = <<"2another simple pm message">>,
  602:             Msg3 = <<"3third simple pm message">>,
  603:             escalus:send(Alice, escalus_stanza:chat_to(Bob, Msg1)),
  604:             escalus:send(Bob, escalus_stanza:chat_to(Alice, Msg2)),
  605:             escalus:send(Alice, escalus_stanza:chat_to(Bob, Msg3)),
  606:             [mam_helper:wait_for_archive_size(User, 3) || User <- [Alice, Bob]],
  607:             AliceJID = escalus_client:full_jid(Alice),
  608:             BobJID = escalus_client:full_jid(Bob),
  609: 
  610:             ExpectedHeader = ["id", "from", "message"],
  611:             ExpectedItems = [
  612:                                 #{"message" => [{contains, Msg1}], "from" => [{jid, AliceJID}]},
  613:                                 #{"message" => [{contains, Msg3}], "from" => [{jid, AliceJID}]},
  614:                                 #{"message" => [{contains, Msg2}], "from" => [{jid, BobJID}]}
  615:                             ],
  616: 
  617:             retrieve_and_validate_personal_data(
  618:                 Alice, Config, "mam_pm", ExpectedHeader, ExpectedItems, ["from", "message"]),
  619:             retrieve_and_validate_personal_data(
  620:                 Bob, Config, "mam_pm", ExpectedHeader, ExpectedItems, ["from", "message"])
  621:         end,
  622:     escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F).
  623: 
  624: retrieve_mam_muc(Config) ->
  625:     F = fun(Alice, Bob, Kate) ->
  626:         AliceUserCfg = escalus_users:get_user_by_name(alice),
  627:         RoomCfg = muc_helper:start_fresh_room([], AliceUserCfg, <<"someroom">>, []),
  628:         [Room, Domain] = [proplists:get_value(Key, RoomCfg) || Key <- [room, muc_host]],
  629:         AllRoomMembers = [Alice, Bob, Kate],
  630: 
  631:         muc_helper:enter_room(RoomCfg, [{Alice, <<"Nancy">>},
  632:                                         {Bob, <<"Sid">>},
  633:                                         {Kate, <<"Johnny">>}]),
  634: 
  635:         Body1 = <<"1some simple muc message">>,
  636:         Body2 = <<"2another one">>,
  637:         Body3 = <<"3third message">>,
  638:         muc_helper:send_to_room(RoomCfg, Alice, Body1),
  639:         muc_helper:verify_message_received(RoomCfg, AllRoomMembers, <<"Nancy">>, Body1),
  640:         muc_helper:send_to_room(RoomCfg, Alice, Body2),
  641:         muc_helper:verify_message_received(RoomCfg, AllRoomMembers, <<"Nancy">>, Body2),
  642:         muc_helper:send_to_room(RoomCfg, Bob, Body3),
  643:         muc_helper:verify_message_received(RoomCfg, AllRoomMembers, <<"Sid">>, Body3),
  644: 
  645:         mam_helper:wait_for_room_archive_size(Domain, Room, 3),
  646: 
  647:         ExpectedItemsAlice = [#{"message" => [{contains, binary_to_list(Body1)}]},
  648:                               #{"message" => [{contains, binary_to_list(Body2)}]}],
  649: 
  650:         ExpectedItemsBob = [#{"message" => [{contains, binary_to_list(Body3)}]}],
  651: 
  652:         AliceDir = retrieve_all_personal_data(Alice, Config),
  653:         BobDir = retrieve_all_personal_data(Bob, Config),
  654:         KateDir = retrieve_all_personal_data(Kate, Config),
  655: 
  656:         validate_personal_data(
  657:             AliceDir, "mam_muc", ["id", "message"], ExpectedItemsAlice, ["message"]),
  658:         validate_personal_data(
  659:             BobDir, "mam_muc", ["id", "message"], ExpectedItemsBob, ["message"]),
  660:         refute_personal_data(KateDir, "mam_muc"),
  661: 
  662:         [refute_personal_data(Dir, "mam_pm") || Dir <- [AliceDir, BobDir, KateDir]],
  663: 
  664:         muc_helper:destroy_room(RoomCfg)
  665:         end,
  666:     escalus_fresh:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], F).
  667: 
  668: retrieve_mam_muc_private_msg(Config) ->
  669:     F = fun(Alice, Bob) ->
  670:             AliceUserCfg = escalus_users:get_user_by_name(alice),
  671:             RoomCfg = muc_helper:start_fresh_room([], AliceUserCfg, <<"someroom">>, []),
  672:             [Room, Domain] = [proplists:get_value(Key, RoomCfg) || Key <- [room, muc_host]],
  673: 
  674:             muc_helper:enter_room(RoomCfg, [{Alice, <<"Nancy">>}, {Bob, <<"Sid">>}]),
  675: 
  676:             PMBody = <<"Hi, Bob!">>,
  677:             {PrivAddrAlice, _} = send_receive_muc_private_message(
  678:                 Room, Domain, {Alice, <<"Nancy">>}, {Bob, <<"Sid">>}, PMBody),
  679: 
  680:             [mam_helper:wait_for_archive_size(User, 1) || User <- [Alice, Bob]],
  681: 
  682:             PMExpectedItemsAlice = [#{"message" => [{contains, binary_to_list(PMBody)}],
  683:                                       "from" => [{jid, escalus_client:full_jid(Alice)}]}],
  684:             PMExpectedItemsBob = [#{"message" => [{contains, binary_to_list(PMBody)}],
  685:                                     "from" => [{jid, PrivAddrAlice}]}],
  686: 
  687:             AliceDir = retrieve_all_personal_data(Alice, Config),
  688:             BobDir = retrieve_all_personal_data(Bob, Config),
  689: 
  690:             validate_personal_data(
  691:                 AliceDir, "mam_pm", ["id", "from", "message"], PMExpectedItemsAlice, []),
  692:             validate_personal_data(
  693:                 BobDir, "mam_pm", ["id", "from", "message"], PMExpectedItemsBob, []),
  694: 
  695:             refute_personal_data(AliceDir, "mam_muc"),
  696:             refute_personal_data(BobDir, "mam_muc"),
  697: 
  698:             muc_helper:destroy_room(RoomCfg)
  699:         end,
  700:     escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F).
  701: 
  702: 
  703: 
  704: retrieve_mam_muc_store_pm(Config) ->
  705:     F = fun(Alice, Bob, Kate) ->
  706:             AliceUserCfg = escalus_users:get_user_by_name(alice),
  707:             RoomCfg = muc_helper:start_fresh_room([], AliceUserCfg, <<"someroom">>, []),
  708:             [Room, Domain] = [proplists:get_value(Key, RoomCfg) || Key <- [room, muc_host]],
  709:             AllRoomMembers = [Alice, Bob, Kate],
  710: 
  711:             muc_helper:enter_room(RoomCfg, [{Alice, <<"Nancy">>},
  712:                                             {Bob, <<"Sid">>},
  713:                                             {Kate, <<"Johnny">>}]),
  714: 
  715:             Body1 = <<"1some simple muc message">>,
  716:             Body2 = <<"2another one">>,
  717:             Body3 = <<"3third message">>,
  718:             muc_helper:send_to_room(RoomCfg, Alice, Body1),
  719:             muc_helper:verify_message_received(RoomCfg, AllRoomMembers, <<"Nancy">>, Body1),
  720:             muc_helper:send_to_room(RoomCfg, Alice, Body2),
  721:             muc_helper:verify_message_received(RoomCfg, AllRoomMembers, <<"Nancy">>, Body2),
  722:             muc_helper:send_to_room(RoomCfg, Bob, Body3),
  723:             muc_helper:verify_message_received(RoomCfg, AllRoomMembers, <<"Sid">>, Body3),
  724: 
  725:             PMBody = <<"4Hi, Bob!">>,
  726:             {PrivAddrAlice, PrivAddrBob} = send_receive_muc_private_message(
  727:                 Room, Domain, {Alice, <<"Nancy">>}, {Bob, <<"Sid">>}, PMBody),
  728: 
  729:             mam_helper:wait_for_room_archive_size(Domain, Room, 3),
  730:             mam_helper:wait_for_archive_size(Kate, 4),
  731:             [mam_helper:wait_for_archive_size(User, 5) || User <- [Alice, Bob]],
  732: 
  733:             AliceDir = retrieve_all_personal_data(Alice, Config),
  734:             BobDir = retrieve_all_personal_data(Bob, Config),
  735:             KateDir = retrieve_all_personal_data(Kate, Config),
  736: 
  737:             ExpectedItemsAlice = [#{"message" => [{contains, binary_to_list(Body1)}]},
  738:                                   #{"message" => [{contains, binary_to_list(Body2)}]}],
  739:             ExpectedItemsBob = [#{"message" => [{contains, binary_to_list(Body3)}]}],
  740: 
  741:             validate_personal_data(
  742:                 AliceDir, "mam_muc", ["id", "message"], ExpectedItemsAlice, ["message"]),
  743:             validate_personal_data(
  744:                 BobDir, "mam_muc", ["id", "message"], ExpectedItemsBob, ["message"]),
  745:             refute_personal_data(KateDir, "mam_muc"),
  746: 
  747:             RoomJID = <<Room/binary, "@", Domain/binary>>,
  748:             MsgFromAliceToRoom = #{"message" => [{contains, "<body>[1,2]"}],
  749:                                    "from" => [{jid, PrivAddrAlice}]},
  750:             PMExpectedItemsKate = [#{"message" => [{contains, "<body/>"}],
  751:                                      "from" => [{jid, RoomJID}]},
  752:                                    MsgFromAliceToRoom, MsgFromAliceToRoom,
  753:                                    #{"message" => [{contains, binary_to_list(Body3)}],
  754:                                      "from" => [{jid, PrivAddrBob}]}
  755:                                   ],
  756:             PMExpectedItemsAlice = PMExpectedItemsKate ++
  757:                                    [#{"message" => [{contains, binary_to_list(PMBody)}],
  758:                                       "from" => [{jid, escalus_client:full_jid(Alice)}]}],
  759:             MsgFromAlice = #{"message" => [{contains, "<body>[1,2,4]"}],
  760:                              "from" => [{jid, PrivAddrAlice}]},
  761:             PMExpectedItemsBob = [#{"message" => [{contains, "<body/>"}],
  762:                                     "from" => [{jid, RoomJID}]},
  763:                                   MsgFromAlice, MsgFromAlice, MsgFromAlice,
  764:                                   #{"message" => [{contains, binary_to_list(Body3)}],
  765:                                     "from" => [{jid, PrivAddrBob}]}
  766:                                  ],
  767:             SortFn = muc_msg_first(RoomJID),
  768:             validate_personal_data(
  769:                 KateDir, "mam_pm", ["id", "from", "message"], PMExpectedItemsKate, SortFn),
  770:             validate_personal_data(
  771:                 AliceDir, "mam_pm", ["id", "from", "message"], PMExpectedItemsAlice, SortFn),
  772:             validate_personal_data(
  773:                 BobDir, "mam_pm", ["id", "from", "message"], PMExpectedItemsBob, SortFn),
  774: 
  775:             muc_helper:destroy_room(RoomCfg)
  776:         end,
  777:     escalus_fresh:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], F).
  778: 
  779: remove_mam_pm(Config) ->
  780:     F = fun(Alice, Bob) ->
  781:             Msg1 = <<"1remove_mam_pm message">>,
  782:             Msg2 = <<"2remove_mam_pm message message">>,
  783:             Msg3 = <<"3remove_mam_pm message message">>,
  784:             escalus:send(Alice, escalus_stanza:chat_to(Bob, Msg1)),
  785:             escalus:send(Bob, escalus_stanza:chat_to(Alice, Msg2)),
  786:             escalus:send(Alice, escalus_stanza:chat_to(Bob, Msg3)),
  787:             [mam_helper:wait_for_archive_size(User, 3) || User <- [Alice, Bob]],
  788:             AliceJID = escalus_client:full_jid(Alice),
  789:             BobJID = escalus_client:full_jid(Bob),
  790: 
  791:             ExpectedHeader = ["id", "from", "message"],
  792:             ExpectedItems = [
  793:                                 #{"message" => [{contains, Msg1}], "from" => [{jid, AliceJID}]},
  794:                                 #{"message" => [{contains, Msg3}], "from" => [{jid, AliceJID}]},
  795:                                 #{"message" => [{contains, Msg2}], "from" => [{jid, BobJID}]}
  796:                             ],
  797: 
  798:             unregister(Alice, Config),
  799: 
  800:             assert_personal_data_via_rpc(Alice, [{mam_pm, ExpectedHeader, []}]),
  801: 
  802:             retrieve_and_validate_personal_data(
  803:                 Bob, Config, "mam_pm", ExpectedHeader, ExpectedItems, ["from", "message"])
  804:         end,
  805:     escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F).
  806: 
  807: retrieve_mam_muc_light(Config) ->
  808:     F = fun(Alice, Bob, Kate) ->
  809:             RoomJid = muc_light_helper:given_muc_light_room(undefined, Alice, [{Bob, member}, {Kate, member}]),
  810:             [Room, Domain] = binary:split(RoomJid, <<"@">>),
  811:             Body1 = <<"1some simple muc message">>,
  812:             Body2 = <<"2another one">>,
  813:             Body3 = <<"3third message">>,
  814: 
  815:             M1 = muc_light_helper:when_muc_light_message_is_sent(Alice, Room, Body1, <<"Id1">>),
  816:             muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob, Kate], M1),
  817:             M2 = muc_light_helper:when_muc_light_message_is_sent(Alice, Room, Body2, <<"Id2">>),
  818:             muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob, Kate], M2),
  819:             M3 = muc_light_helper:when_muc_light_message_is_sent(Bob, Room, Body3, <<"Id3">>),
  820:             muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob, Kate], M3),
  821: 
  822:             mam_helper:wait_for_room_archive_size(Domain, Room, 4),
  823: 
  824:             ExpectedItemsAlice = [#{"message" => [{contains, binary_to_list(Body1)}]},
  825:                                   #{"message" => [{contains, binary_to_list(Body2)}]}],
  826: 
  827:             ExpectedItemsBob = [#{"message" => [{contains, binary_to_list(Body3)}]}],
  828: 
  829:             retrieve_and_validate_personal_data(
  830:                 Alice, Config, "mam_muc", ["id", "message"], ExpectedItemsAlice, ["message"]),
  831:             retrieve_and_validate_personal_data(
  832:                 Bob, Config, "mam_muc", ["id", "message"], ExpectedItemsBob, ["message"]),
  833:             refute_personal_data(Kate, Config, "mam_muc")
  834:         end,
  835:     escalus_fresh:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], F).
  836: 
  837: retrieve_mam_pm_and_muc_light_dont_interfere(Config) ->
  838:     F = fun(Alice, Bob, Kate) ->
  839:             RoomJid = muc_light_helper:given_muc_light_room(undefined, Alice,
  840:                                                             [{Bob, member}, {Kate, member}]),
  841:             [Room, Domain] = binary:split(RoomJid, <<"@">>),
  842:             BodyMucAlice = <<"some simple muc message from Alice">>,
  843:             BodyMucBob = <<"some simple muc message from Bob">>,
  844:             BodyPmAlice = <<"some simple pm message from Alice">>,
  845:             BodyPmBob = <<"some simple pm message from Bob">>,
  846: 
  847:             M1 = muc_light_helper:when_muc_light_message_is_sent(Alice, Room, BodyMucAlice, <<"Id1">>),
  848:             muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob, Kate], M1),
  849:             M2 = muc_light_helper:when_muc_light_message_is_sent(Bob, Room, BodyMucBob, <<"Id2">>),
  850:             muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob, Kate], M2),
  851: 
  852:             mam_helper:wait_for_room_archive_size(Domain, Room, 3),
  853: 
  854:             escalus:send(Alice, escalus_stanza:chat_to(Bob, BodyPmAlice)),
  855:             escalus:send(Bob, escalus_stanza:chat_to(Alice, BodyPmBob)),
  856: 
  857:             [mam_helper:wait_for_archive_size(User, 2) || User <- [Alice, Bob]],
  858: 
  859:             false = mongoose_helper:successful_rpc(gen_mod, get_module_opt,
  860:                                                    [host_type(), mod_mam_pm, archive_groupchats, undefined]),
  861: 
  862:             AliceDir = retrieve_all_personal_data(Alice, Config),
  863:             BobDir = retrieve_all_personal_data(Bob, Config),
  864:             KateDir = retrieve_all_personal_data(Kate, Config),
  865: 
  866:             validate_personal_data(
  867:                 AliceDir, "mam_muc", ["id", "message"],
  868:                 [#{"message" => [{contains, binary_to_list(BodyMucAlice)}]}], []),
  869:             validate_personal_data(
  870:                 BobDir, "mam_muc", ["id", "message"],
  871:                 [#{"message" => [{contains, binary_to_list(BodyMucBob)}]}], []),
  872: 
  873:             PM = [#{"message" => [{contains, BodyPmAlice}],
  874:                     "from" => [{jid, escalus_client:full_jid(Alice)}]},
  875:                   #{"message" => [{contains, BodyPmBob}],
  876:                     "from" => [{jid, escalus_client:full_jid(Bob)}]}],
  877:             validate_personal_data(AliceDir, "mam_pm", ["id", "from", "message"], PM, ["from", "message"]),
  878:             validate_personal_data(BobDir, "mam_pm", ["id", "from", "message"], PM, ["from", "message"]),
  879:             refute_personal_data(KateDir, "mam_pm"),
  880:             refute_personal_data(KateDir, "mam_muc")
  881:         end,
  882:     escalus_fresh:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], F).
  883: 
  884: retrieve_mam_pm_and_muc_light_interfere(Config) ->
  885:     F = fun(Alice, Bob, Kate) ->
  886:             RoomJid = muc_light_helper:given_muc_light_room(undefined, Alice,
  887:                                                             [{Bob, member}, {Kate, member}]),
  888:             [Room, Domain] = binary:split(RoomJid, <<"@">>),
  889:             BodyMucAlice = <<"some simple muc message from Alice">>,
  890:             BodyMucBob = <<"some simple muc message from Bob">>,
  891:             BodyPmAlice = <<"some simple pm message from Alice">>,
  892:             BodyPmBob = <<"some simple pm message from Bob">>,
  893: 
  894:             M1 = muc_light_helper:when_muc_light_message_is_sent(Alice, Room, BodyMucAlice, <<"Id1">>),
  895:             muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob, Kate], M1),
  896:             M2 = muc_light_helper:when_muc_light_message_is_sent(Bob, Room, BodyMucBob, <<"Id2">>),
  897:             muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob, Kate], M2),
  898: 
  899:             mam_helper:wait_for_room_archive_size(Domain, Room, 3),
  900: 
  901:             escalus:send(Alice, escalus_stanza:chat_to(Bob, BodyPmAlice)),
  902:             escalus:send(Bob, escalus_stanza:chat_to(Alice, BodyPmBob)),
  903: 
  904:             [mam_helper:wait_for_archive_size(User, 5) || User <- [Alice, Bob]],
  905:             mam_helper:wait_for_archive_size(Kate, 3),
  906: 
  907:             true = mongoose_helper:successful_rpc(gen_mod, get_module_opt,
  908:                                                    [host_type(), mod_mam_pm, archive_groupchats, undefined]),
  909: 
  910:             AliceDir = retrieve_all_personal_data(Alice, Config),
  911:             BobDir = retrieve_all_personal_data(Bob, Config),
  912:             KateDir = retrieve_all_personal_data(Kate, Config),
  913: 
  914:             validate_personal_data(
  915:                 AliceDir, "mam_muc", ["id", "message"],
  916:                 [#{"message" => [{contains, binary_to_list(BodyMucAlice)}]}], []),
  917:             validate_personal_data(
  918:                 BobDir, "mam_muc", ["id", "message"],
  919:                 [#{"message" => [{contains, binary_to_list(BodyMucBob)}]}], []),
  920: 
  921:             AliceRoomJid = <<RoomJid/binary, "/", (escalus_client:short_jid(Alice))/binary>>,
  922:             BobRoomJid = <<RoomJid/binary, "/", (escalus_client:short_jid(Bob))/binary>>,
  923: 
  924:             MucPM = [#{"message" => [{contains, "urn:xmpp:muclight:0#affiliations"}],
  925:                        "from" => [{jid, RoomJid}]},
  926:                      #{"message" => [{contains, BodyMucAlice}], "from" => [{jid, AliceRoomJid}]},
  927:                      #{"message" => [{contains, BodyMucBob}], "from" => [{jid, BobRoomJid}]}],
  928:             AllPM = MucPM ++ [#{"message" => [{contains, BodyPmAlice}],
  929:                                 "from" => [{jid, escalus_client:full_jid(Alice)}]},
  930:                               #{"message" => [{contains, BodyPmBob}],
  931:                                     "from" => [{jid, escalus_client:full_jid(Bob)}]}],
  932:             SortFn = muc_msg_first(RoomJid),
  933:             validate_personal_data(AliceDir, "mam_pm", ["id", "from", "message"], AllPM, SortFn),
  934:             validate_personal_data(BobDir, "mam_pm", ["id", "from", "message"], AllPM, SortFn),
  935:             validate_personal_data(KateDir, "mam_pm", ["id", "from", "message"], MucPM, SortFn),
  936:             refute_personal_data(KateDir, "mam_muc")
  937:         end,
  938:     escalus_fresh:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], F).
  939: 
  940: retrieve_offline(Config) ->
  941:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  942:             mongoose_helper:logout_user(Config, Alice),
  943:             Body1 = <<"1Hey!">>,
  944:             Body2 = <<"2Here is Johnny!">>,
  945:             Body3 = <<"3Where is Johnny ?">>,
  946:             escalus:send(Bob, escalus_stanza:chat_to(Alice, Body1)),
  947:             escalus:send(Bob, escalus_stanza:chat_to(Alice, Body2)),
  948:             escalus:send(Kate, escalus_stanza:chat_to(Alice, Body3)),
  949:             %% Well, jid_to_lower works for any binary :)
  950:             AliceU = escalus_utils:jid_to_lower(escalus_client:username(Alice)),
  951:             AliceS = escalus_utils:jid_to_lower(escalus_client:server(Alice)),
  952:             mongoose_helper:wait_until(
  953:               fun() ->
  954:                       mongoose_helper:successful_rpc(mod_offline_backend, count_offline_messages,
  955:                                                      [host_type(), AliceU, AliceS, 10])
  956:               end, 3),
  957: 
  958:             BobJid = escalus_client:full_jid(Bob),
  959:             AliceJid = escalus_client:short_jid(Alice),
  960:             KateJid = escalus_client:full_jid(Kate),
  961:             ExpectedHeader = ["timestamp", "from", "to", "packet"],
  962:             Expected = [{Body1, BobJid, AliceJid}, {Body2, BobJid, AliceJid}, {Body3, KateJid, AliceJid}],
  963: 
  964:             ExpectedItems = lists:map(fun({Body, From ,To}) ->
  965:                 #{ "packet" => [{contains, Body}],
  966:                     "from" => binary_to_list(From),
  967:                     "to" => binary_to_list(To),
  968:                     "timestamp" => [{validate, fun validate_datetime/1}]}
  969:             end, Expected),
  970: 
  971:             retrieve_and_validate_personal_data(
  972:               Alice, Config, "offline", ExpectedHeader, ExpectedItems, ["packet"])
  973:         end).
  974: 
  975: remove_offline(Config) ->
  976:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) ->
  977:             mongoose_helper:logout_user(Config, Alice),
  978:             Body1 = <<"Hey!">>,
  979:             Body2 = <<"Here is Johnny!">>,
  980:             Body3 = <<"Where is Johnny ?">>,
  981:             escalus:send(Bob, escalus_stanza:chat_to(Alice, Body1)),
  982:             escalus:send(Bob, escalus_stanza:chat_to(Alice, Body2)),
  983:             escalus:send(Kate, escalus_stanza:chat_to(Alice, Body3)),
  984:             %% Well, jid_to_lower works for any binary :)
  985:             AliceU = escalus_utils:jid_to_lower(escalus_client:username(Alice)),
  986:             AliceS = escalus_utils:jid_to_lower(escalus_client:server(Alice)),
  987:             mongoose_helper:wait_until(
  988:               fun() ->
  989:                       mongoose_helper:successful_rpc(mod_offline_backend, count_offline_messages,
  990:                                                      [host_type(), AliceU, AliceS, 10])
  991:               end, 3),
  992: 
  993:             unregister(Alice, Config),
  994: 
  995:             assert_personal_data_via_rpc(
  996:               Alice, [{offline, ["timestamp","from", "to", "packet"],[]}])
  997:         end).
  998: 
  999: retrieve_pubsub_payloads(Config) ->
 1000:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
 1001:         [Node1={_,NodeName1}, Node2={_,NodeName2}] = pubsub_tools:create_node_names(2),
 1002:         {BinItem1, StringItem1} = item_content(<<"Item1Data">>),
 1003:         {BinItem2, StringItem2} = item_content(<<"Item2Data">>),
 1004:         {BinItem3, StringItem3} = item_content(<<"Item3Data">>),
 1005:         {BinOther, StringOther} = item_content(<<"OtherItemData">>),
 1006: 
 1007:         pubsub_tools:publish(Alice, <<"Item1">>, Node1, [{with_payload, BinItem1}]),
 1008:         pubsub_tools:publish(Alice, <<"Item2">>, Node1, [{with_payload, BinItem2}]),
 1009:         pubsub_tools:publish(Alice, <<"Item3">>, Node1, [{with_payload, BinItem3}]),
 1010:         pubsub_tools:publish(Alice, <<"OtherItem">>, Node2, [{with_payload, BinOther}]),
 1011: 
 1012:         ExpectedItems = [pubsub_payloads_row_map(NodeName1, "Item1", StringItem1),
 1013:                          pubsub_payloads_row_map(NodeName1, "Item2", StringItem2),
 1014:                          pubsub_payloads_row_map(NodeName1, "Item3", StringItem3),
 1015:                          pubsub_payloads_row_map(NodeName2, "OtherItem", StringOther)],
 1016: 
 1017:         retrieve_and_validate_personal_data(Alice, Config, "pubsub_payloads",
 1018:                                             ["node_name", "item_id", "payload"],
 1019:                                             ExpectedItems, ["item_id"])
 1020:                                               end).
 1021: 
 1022: dont_retrieve_other_user_pubsub_payload(Config) ->
 1023:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1024:         [Node1={_,NodeName1}] = pubsub_tools:create_node_names(1),
 1025:         pubsub_tools:create_nodes([{Alice, Node1, []}]),
 1026: 
 1027:         {BinItem1, StringItem1} = item_content(<<"Item1Data">>),
 1028:         {BinItem2, StringItem2} = item_content(<<"Item2Data">>),
 1029: 
 1030:         AffChange = [{Bob, <<"publish-only">>}],
 1031:         pubsub_tools:set_affiliations(Alice, Node1, AffChange, []),
 1032:         pubsub_tools:publish(Alice, <<"Item1">>, Node1, [{with_payload, {true, BinItem1}}]),
 1033:         pubsub_tools:publish(Bob, <<"Item2">>, Node1, [{with_payload, {true, BinItem2}}]),
 1034: 
 1035:         retrieve_and_validate_personal_data(
 1036:             Alice, Config, "pubsub_payloads", ["node_name", "item_id", "payload"],
 1037:             [pubsub_payloads_row_map(NodeName1, "Item1", StringItem1)]),
 1038: 
 1039:         retrieve_and_validate_personal_data(
 1040:             Bob, Config, "pubsub_payloads", ["node_name","item_id", "payload"],
 1041:             [pubsub_payloads_row_map(NodeName1, "Item2", StringItem2)]),
 1042: 
 1043:         pubsub_tools:delete_node(Alice, Node1, [])
 1044:                                               end).
 1045: 
 1046: retrieve_created_pubsub_nodes(Config) ->
 1047:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1048:         [Node1={_,NodeName1}, Node2={_,NodeName2}, Node3={_,NodeName3}] =
 1049:         pubsub_tools:create_node_names(3),
 1050: 
 1051:         NodeNS = <<"myns">>,
 1052:         PepNode = make_pep_node_info(Alice, NodeNS),
 1053:         AccessModel = {<<"pubsub#access_model">>, <<"authorize">>},
 1054: 
 1055:         pubsub_tools:create_nodes([
 1056:                       {Alice, Node1, []},
 1057:                       {Alice, Node2, []},
 1058:                       {Alice, PepNode, [{config, [AccessModel]}]},
 1059:                       {Bob, Node3, [{type, <<"push">>}]}
 1060:                      ]),
 1061: 
 1062:         ExpectedHeader = ["node_name", "type"],
 1063: 
 1064:         retrieve_and_validate_personal_data(
 1065:             Alice, Config, "pubsub_nodes", ExpectedHeader,
 1066:             [pubsub_nodes_row_map(NodeNS, "pep"),
 1067:              pubsub_nodes_row_map(NodeName1, "flat"),
 1068:              pubsub_nodes_row_map(NodeName2, "flat")]),
 1069: 
 1070:         retrieve_and_validate_personal_data(
 1071:             Bob, Config, "pubsub_nodes", ExpectedHeader,
 1072:             [pubsub_nodes_row_map(NodeName3, "push")]),
 1073: 
 1074: 
 1075:         Nodes = [{Alice, PepNode}, {Alice, Node1}, {Alice, Node2}, {Bob, Node3}],
 1076:         [pubsub_tools:delete_node(User, Node, []) || {User, Node} <- Nodes]
 1077:                                                         end).
 1078: 
 1079: remove_pubsub_subscriptions(Config) ->
 1080:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1081:             Node = pubsub_tools:pubsub_node(),
 1082:             pubsub_tools:create_node(Alice, Node, []),
 1083:             pubsub_tools:subscribe(Bob, Node, []),
 1084: 
 1085:             unregister(Bob, Config),
 1086: 
 1087:             assert_personal_data_via_rpc(Bob,
 1088:                                          [{pubsub_payloads,["node_name","item_id","payload"],[]},
 1089:                                           {pubsub_nodes,["node_name","type"],[]},
 1090:                                           {pubsub_subscriptions,["node_name"],[]}])
 1091:         end).
 1092: 
 1093: retrieve_pubsub_subscriptions(Config) ->
 1094:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1095:             Node = {_Domain, NodeName} = pubsub_tools:pubsub_node(),
 1096:             pubsub_tools:create_node(Alice, Node, []),
 1097:             pubsub_tools:subscribe(Bob, Node, []),
 1098:             retrieve_and_validate_personal_data(Bob, Config, "pubsub_subscriptions", ["node_name"],
 1099:                 [pubsub_subscription_row_map(NodeName)]),
 1100: 
 1101:             pubsub_tools:delete_node(Alice, Node, [])
 1102:         end).
 1103: 
 1104: remove_pubsub_dont_remove_flat_pubsub_node(Config) ->
 1105:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
 1106:         Node1 = {_,NodeName} = pubsub_tools:pubsub_node_with_num(1),
 1107:         pubsub_tools:create_nodes([{Alice, Node1, []}]),
 1108: 
 1109:         unregister(Alice, Config),
 1110: 
 1111:         assert_personal_data_via_rpc(Alice,
 1112:                                      [{pubsub_payloads,["node_name","item_id","payload"],[]},
 1113:                                       {pubsub_nodes,["node_name","type"],[[NodeName, <<"flat">>]]},
 1114:                                       {pubsub_subscriptions,["node_name"],[]}])
 1115:         end).
 1116: 
 1117: remove_pubsub_push_node(Config) ->
 1118:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1119:         [Node] = pubsub_tools:create_node_names(1),
 1120:         pubsub_tools:create_nodes([{Alice, Node, [{type, <<"push">>}]}]),
 1121: 
 1122:         Content = [
 1123:                    {<<"message-count">>, <<"1">>},
 1124:                    {<<"last-message-sender">>, <<"senderId">>},
 1125:                    {<<"last-message-body">>, <<"message body">>}
 1126:                   ],
 1127:         Options = [
 1128:                    {<<"device_id">>, <<"sometoken">>},
 1129:                    {<<"service">>, <<"apns">>}
 1130:                   ],
 1131: 
 1132:         PublishIQ = push_pubsub_SUITE:publish_iq(Bob, Node, Content, Options),
 1133:         escalus:send(Bob, PublishIQ),
 1134:         escalus:assert(is_iq_result, escalus:wait_for_stanza(Bob)),
 1135: 
 1136:         unregister(Alice, Config),
 1137: 
 1138:         assert_personal_data_via_rpc(Alice, [{pubsub_payloads,["node_name","item_id","payload"],[]},
 1139:                                              {pubsub_nodes,["node_name","type"],[]},
 1140:                                              {pubsub_subscriptions,["node_name"],[]}])
 1141:         end).
 1142: 
 1143: remove_pubsub_pep_node(Config) ->
 1144:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
 1145:         NodeName = <<"myns">>,
 1146:         PepNode = make_pep_node_info(Alice, NodeName),
 1147: 
 1148:         pubsub_tools:create_nodes([
 1149:                       {Alice, PepNode, []}
 1150:                      ]),
 1151: 
 1152:         unregister(Alice, Config),
 1153: 
 1154:         assert_personal_data_via_rpc(Alice, [{pubsub_payloads,["node_name","item_id","payload"],[]},
 1155:                                              {pubsub_nodes,["node_name","type"],[]},
 1156:                                              {pubsub_subscriptions,["node_name"],[]}])
 1157:         end).
 1158: 
 1159: remove_pubsub_dont_remove_node_when_only_publisher(Config) ->
 1160:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1161:         Node1 = {_,NodeName} = pubsub_tools:pubsub_node_with_num(1),
 1162:         pubsub_tools:create_nodes([{Alice, Node1, []}]),
 1163: 
 1164:         AffChange = [{Bob, <<"publish-only">>}],
 1165:         pubsub_tools:set_affiliations(Alice, Node1, AffChange, []),
 1166: 
 1167:         unregister(Bob, Config),
 1168: 
 1169:         assert_personal_data_via_rpc(Alice,
 1170:                                      [{pubsub_payloads,["node_name","item_id","payload"],[]},
 1171:                                       {pubsub_nodes,["node_name","type"],[[NodeName, <<"flat">>]]},
 1172:                                       {pubsub_subscriptions,["node_name"],[]}])
 1173:         end).
 1174: 
 1175: remove_pubsub_all_data(Config) ->
 1176:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1177:         [Node1={_,Name1}, Node2={_,Name2}, Node3={_,Name3}, Node4={_,Name4}]
 1178:             = pubsub_tools:create_node_names(4),
 1179:         PepNode = make_pep_node_info(Alice, <<"myns">>),
 1180:         pubsub_tools:create_nodes([
 1181:                       {Alice, Node1, []},
 1182:                       {Alice, Node2, []},
 1183:                       {Alice, PepNode, []},
 1184:                       {Bob, Node3, []},
 1185:                       {Bob, Node4, [{type, <<"push">>}]}
 1186:                      ]),
 1187: 
 1188:         AffChange = [{Bob, <<"publish-only">>}],
 1189:         pubsub_tools:set_affiliations(Alice, Node1, AffChange, []),
 1190:         pubsub_tools:subscribe(Bob, Node2, []),
 1191:         pubsub_tools:subscribe(Alice, Node3, []),
 1192: 
 1193:         {BinItem1, _} = item_content(<<"Item1Data">>),
 1194:         {BinItem2, _} = item_content(<<"Item2Data">>),
 1195:         {BinItem3, _} = item_content(<<"Item3Data">>),
 1196:         {BinItem4, _} = item_content(<<"Item4Data">>),
 1197:         AliceToNode1 = <<"Alice publishes to Node1, but nobody is subscribed">>,
 1198:         AliceToNode2 = <<"Alice published to Node2, so Bob receives it">>,
 1199:         BobToNode1 = <<"Bob publishes to Node1, but nobody is subscribed">>,
 1200:         BobToNode3 = <<"Bob publishes to Node3, so Alice receives it">>,
 1201: 
 1202:         pubsub_tools:publish(Alice, AliceToNode1, Node1, [{with_payload, {true, BinItem1}}]),
 1203: 
 1204:         pubsub_tools:publish(Alice, AliceToNode2, Node2, [{with_payload, {true, BinItem2}}]),
 1205:         pubsub_tools:receive_item_notification(Bob, AliceToNode2, Node2, []),
 1206: 
 1207:         pubsub_tools:publish(Bob, BobToNode1, Node1, [{with_payload, {true, BinItem3}}]),
 1208: 
 1209:         pubsub_tools:publish(Bob, BobToNode3, Node3, [{with_payload, {true, BinItem4}}]),
 1210:         pubsub_tools:receive_item_notification(Alice, BobToNode3, Node3, []),
 1211: 
 1212:         unregister(Alice, Config),
 1213: 
 1214:         [{pubsub_payloads,["node_name","item_id","payload"], AlicePayloads},
 1215:          {pubsub_nodes,["node_name","type"], AliceNodes},
 1216:          {pubsub_subscriptions, ["node_name"], []}]
 1217:             = get_personal_data_via_rpc(
 1218:                 Alice, [pubsub_payloads, pubsub_nodes, pubsub_subscriptions]),
 1219:         XmlBinItem1 = exml:to_binary(BinItem1),
 1220:         XmlBinItem2 = exml:to_binary(BinItem2),
 1221:         [[Name1, AliceToNode1, XmlBinItem1],
 1222:          [Name2, AliceToNode2, XmlBinItem2]] = lists:sort(AlicePayloads),
 1223:         [[Name1, <<"flat">>], [Name2, <<"flat">>]] = lists:sort(AliceNodes),
 1224: 
 1225:         [{pubsub_payloads,["node_name","item_id","payload"], Payloads},
 1226:          {pubsub_nodes,["node_name","type"], Nodes},
 1227:          {pubsub_subscriptions, ["node_name"], Subs}]
 1228:             = get_personal_data_via_rpc(
 1229:                 Bob, [pubsub_payloads, pubsub_nodes, pubsub_subscriptions]),
 1230:         XmlBinItem3 = exml:to_binary(BinItem3),
 1231:         XmlBinItem4 = exml:to_binary(BinItem4),
 1232:         [[Name1, BobToNode1, XmlBinItem3],
 1233:          [Name3, BobToNode3, XmlBinItem4]] = lists:sort(Payloads),
 1234:         [[Name3, <<"flat">>], [Name4, <<"push">>]] = lists:sort(Nodes),
 1235:         [[Name2]] = Subs
 1236:       end).
 1237: 
 1238: retrieve_all_pubsub_data(Config) ->
 1239:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1240:         [Node1={_,NodeName1}, Node2={_,NodeName2}, Node3={_,NodeName3}] =
 1241:         pubsub_tools:create_node_names(3),
 1242:         pubsub_tools:create_nodes([{Alice, Node1, []}, {Alice, Node2, []}, {Bob, Node3, []}]),
 1243: 
 1244:         AffChange = [{Bob, <<"publish-only">>}],
 1245:         pubsub_tools:set_affiliations(Alice, Node1, AffChange, []),
 1246:         pubsub_tools:subscribe(Bob, Node2, []),
 1247: 
 1248:         {BinItem1, StringItem1} = item_content(<<"Item1Data">>),
 1249:         {BinItem2, StringItem2} = item_content(<<"Item2Data">>),
 1250:         {BinItem3, StringItem3} = item_content(<<"Item3Data">>),
 1251: 
 1252:         pubsub_tools:publish(Alice, <<"Item1">>, Node1, [{with_payload, {true, BinItem1}}]),
 1253:         pubsub_tools:publish(Alice, <<"Item2">>, Node2, [{with_payload, {true, BinItem2}}]),
 1254:         pubsub_tools:receive_item_notification(Bob, <<"Item2">>, Node2, []),
 1255:         pubsub_tools:publish(Bob, <<"Item3">>, Node1, [{with_payload, {true, BinItem3}}]),
 1256: 
 1257:         %% Bob has one subscription, one node created and one payload sent
 1258:         retrieve_and_validate_personal_data(
 1259:             Bob, Config, "pubsub_subscriptions", ["node_name"],
 1260:             [pubsub_subscription_row_map(NodeName2)]),
 1261: 
 1262:         retrieve_and_validate_personal_data(
 1263:             Bob, Config, "pubsub_nodes", ["node_name", "type"],
 1264:             [pubsub_nodes_row_map(NodeName3, "flat")]),
 1265: 
 1266:         retrieve_and_validate_personal_data(
 1267:             Bob, Config, "pubsub_payloads", ["node_name", "item_id", "payload"],
 1268:             [pubsub_payloads_row_map(NodeName1, "Item3", StringItem3)]),
 1269: 
 1270:         %% Alice has two nodes created and two payloads sent
 1271:         retrieve_and_validate_personal_data(
 1272:             Alice, Config, "pubsub_nodes", ["node_name", "type"],
 1273:             [pubsub_nodes_row_map(NodeName1, "flat"),
 1274:              pubsub_nodes_row_map(NodeName2, "flat")]),
 1275:         retrieve_and_validate_personal_data(
 1276:             Alice, Config, "pubsub_payloads", ["node_name", "item_id","payload"],
 1277:             [pubsub_payloads_row_map(NodeName1, "Item1", StringItem1),
 1278:              pubsub_payloads_row_map(NodeName2, "Item2", StringItem2)]),
 1279: 
 1280:         dynamic_modules:ensure_modules(host_type(), pubsub_required_modules()),
 1281:         Nodes = [{Alice, Node1}, {Alice, Node2}, {Bob, Node3}],
 1282:         [pubsub_tools:delete_node(User, Node, []) || {User, Node} <- Nodes]
 1283:       end).
 1284: 
 1285: 
 1286: retrieve_private_xml(Config) ->
 1287:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
 1288:             NS = <<"alice:gdpr:ns">>,
 1289:             Content = <<"dGhlcmUgYmUgZHJhZ29ucw==">>,
 1290:             send_and_assert_private_stanza(Alice, NS, Content),
 1291:             ExpectedHeader = ["ns", "xml"],
 1292:             ExpectedItems = [#{ "ns" => binary_to_list(NS),
 1293:                                 "xml" => [{contains, binary_to_list(NS)},
 1294:                                           {contains, binary_to_list(Content)}] }
 1295:                             ],
 1296:             retrieve_and_validate_personal_data(
 1297:               Alice, Config, "private", ExpectedHeader, ExpectedItems)
 1298:         end).
 1299: 
 1300: dont_retrieve_other_user_private_xml(Config) ->
 1301:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1302:             AliceNS = <<"alice:gdpr:ns">>,
 1303:             AliceContent = <<"To be or not to be">>,
 1304:             BobNS = <<"bob:gdpr:ns">>,
 1305:             BobContent = <<"This is the winter of our discontent">>,
 1306:             send_and_assert_private_stanza(Alice, AliceNS, AliceContent),
 1307:             send_and_assert_private_stanza(Bob, BobNS, BobContent),
 1308:             ExpectedHeader = ["ns", "xml"],
 1309:             ExpectedItems = [#{ "ns" => binary_to_list(AliceNS),
 1310:                                 "xml" => [{contains, binary_to_list(AliceNS)},
 1311:                                           {contains, binary_to_list(AliceContent)}] }
 1312:                             ],
 1313:             retrieve_and_validate_personal_data(
 1314:               Alice, Config, "private", ExpectedHeader, ExpectedItems)
 1315:         end).
 1316: 
 1317: retrieve_multiple_private_xmls(Config) ->
 1318:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
 1319:             NSsAndContents = [
 1320:                               {<<"alice:gdpr:ns1">>, <<"You do not talk about FIGHT CLUB.">>},
 1321:                               {<<"alice:gdpr:ns2">>, <<"You do not talk about FIGHT CLUB.">>},
 1322:                               {<<"alice:gdpr:ns3">>, <<"If someone says stop or goes limp,"
 1323:                                                        " taps out the fight is over.">>},
 1324:                               {<<"alice:gdpr:ns4">>, <<"Only two guys to a fight.">>},
 1325:                               {<<"alice:gdpr:ns5">>, <<"One fight at a time.">>}
 1326:                              ],
 1327:             lists:foreach(
 1328:                 fun({NS, Content}) ->
 1329:                     send_and_assert_private_stanza(Alice, NS, Content)
 1330:                 end, NSsAndContents),
 1331:             ExpectedHeader = ["ns", "xml"],
 1332:             ExpectedItems = lists:map(
 1333:                 fun({NS, Content}) ->
 1334:                     #{ "ns" => binary_to_list(NS),
 1335:                        "xml" => [{contains, binary_to_list(NS)},
 1336:                                  {contains, binary_to_list(Content)}]}
 1337:                 end, NSsAndContents),
 1338: 
 1339:             retrieve_and_validate_personal_data(
 1340:               Alice, Config, "private", ExpectedHeader, ExpectedItems)
 1341:         end).
 1342: 
 1343: retrieve_inbox_muclight(Config) ->
 1344:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1345:         muc_light_helper:given_muc_light_room(?ROOM, Alice, [{Bob, member}]),
 1346:         Domain = muc_light_helper:muc_host(),
 1347: 
 1348:         Body = <<"Are you sure?">>,
 1349:         Res = muc_light_helper:when_muc_light_message_is_sent(Alice, ?ROOM, Body, <<"9128">>),
 1350:         muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob], Res),
 1351:         ExpectedHeader = ["jid", "content", "unread_count", "timestamp"],
 1352:         ExpectedAliceItems = [#{ "jid" => [{contains, <<?ROOM/binary, $@, Domain/binary>>}],
 1353:                                  "unread_count" => "0" }],
 1354:         %% MUC Light affiliations are also stored in inbox
 1355:         ExpectedBobItems = [#{ "jid" => [{contains, <<?ROOM/binary, $@, Domain/binary>>}],
 1356:                                "unread_count" => "2" }],
 1357: 
 1358:         retrieve_and_validate_personal_data(
 1359:           Alice, Config, "inbox", ExpectedHeader, ExpectedAliceItems),
 1360:         retrieve_and_validate_personal_data(
 1361:           Bob, Config, "inbox", ExpectedHeader, ExpectedBobItems),
 1362: 
 1363:         StanzaDestroy = escalus_stanza:to(escalus_stanza:iq_set(?NS_MUC_LIGHT_DESTROY, []), room_bin_jid(?ROOM)),
 1364:         escalus:send(Alice, StanzaDestroy),
 1365:         ok
 1366:         end).
 1367: 
 1368: retrieve_inbox_muc(Config) ->
 1369:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1370:         {ok, Room} = given_fresh_muc_room(Alice#client.props, []),
 1371:         Users = [Alice, Bob],
 1372:         Msg = <<"Hi Room!">>,
 1373:         Id = <<"MyID">>,
 1374:         RoomAddr = muc_helper:room_address(Room),
 1375: 
 1376:         inbox_helper:enter_room(Room, Users),
 1377:         inbox_helper:make_members(Room, Alice, [Bob]),
 1378:         Stanza = escalus_stanza:set_id(
 1379:           escalus_stanza:groupchat_to(RoomAddr, Msg), Id),
 1380:         escalus:send(Bob, Stanza),
 1381:         inbox_helper:wait_for_groupchat_msg(Users),
 1382: 
 1383:         ExpectedHeader = ["jid", "content", "unread_count", "timestamp"],
 1384: 
 1385:         ExpectedBobItems = [#{
 1386:                                 "content" => [{contains, Msg}],
 1387:                                 "jid" => [{contains, RoomAddr}],
 1388:                                 "unread_count" => "0" }],
 1389: 
 1390:          retrieve_and_validate_personal_data(
 1391:            Bob, Config, "inbox", ExpectedHeader, ExpectedBobItems),
 1392:         ExpectedAliceItems = [#{
 1393:                                 "content" => [{contains, Msg}],
 1394:                                 "jid" => [{contains, RoomAddr}],
 1395:                                 "unread_count" => "1" }],
 1396: 
 1397:          retrieve_and_validate_personal_data(
 1398:            Alice, Config, "inbox", ExpectedHeader, ExpectedAliceItems),
 1399:         ok
 1400:       end).
 1401: 
 1402: retrieve_inbox(Config) ->
 1403:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1404:             BobU = escalus_utils:jid_to_lower(escalus_client:username(Bob)),
 1405:             BobS = escalus_utils:jid_to_lower(escalus_client:server(Bob)),
 1406:             AliceU = escalus_utils:jid_to_lower(escalus_client:username(Alice)),
 1407:             AliceS = escalus_utils:jid_to_lower(escalus_client:server(Alice)),
 1408:             Body = <<"With spam?">>,
 1409:             send_and_assert_is_chat_message(Bob, Alice, Body),
 1410:             ExpectedHeader = ["jid", "content", "unread_count", "timestamp"],
 1411:             ExpectedAliceItems = [#{ "content" => [{contains, Body}],
 1412:                                      "jid" => [{contains, BobS},
 1413:                                                {contains, BobU}],
 1414:                                      "unread_count" => "1" }],
 1415:             ExpectedBobItems = [#{ "content" => [{contains, Body}],
 1416:                                     "jid" => [{contains, AliceS},
 1417:                                               {contains, AliceU}],
 1418:                                     "unread_count" => "0" }],
 1419:             retrieve_and_validate_personal_data(
 1420:               Alice, Config, "inbox", ExpectedHeader, ExpectedAliceItems),
 1421:             retrieve_and_validate_personal_data(
 1422:               Bob, Config, "inbox", ExpectedHeader, ExpectedBobItems)
 1423:         end).
 1424: 
 1425: remove_inbox(Config) ->
 1426:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1427:             AliceU = escalus_utils:jid_to_lower(escalus_client:username(Alice)),
 1428:             AliceS = escalus_utils:jid_to_lower(escalus_client:server(Alice)),
 1429:             Body = <<"With spam?">>,
 1430:             send_and_assert_is_chat_message(Bob, Alice, Body),
 1431: 
 1432:             ExpectedHeader = ["jid", "content", "unread_count", "timestamp"],
 1433: 
 1434:             unregister(Alice, Config),
 1435: 
 1436:             assert_personal_data_via_rpc(Alice, [{inbox, ExpectedHeader, []}]),
 1437: 
 1438:             ExpectedBobItems = [
 1439:                              #{ "content" => [{contains, Body}],
 1440:                                 "jid" => [{contains, AliceS},
 1441:                                           {contains, AliceU}],
 1442:                                 "unread_count" => "0" }
 1443:                             ],
 1444:             retrieve_and_validate_personal_data(
 1445:               Bob, Config, "inbox", ExpectedHeader, ExpectedBobItems)
 1446:         end).
 1447: 
 1448: remove_inbox_muclight(Config) ->
 1449:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1450:         Domain = muc_light_helper:muc_host(),
 1451:         Room = <<"ttt2">>,
 1452:         muc_light_helper:given_muc_light_room(Room, Alice, [{Bob, member}]),
 1453: 
 1454:         Body = <<"Are you sure?">>,
 1455:         Res = muc_light_helper:when_muc_light_message_is_sent(Alice, Room , Body, <<"9128">>),
 1456:         muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob], Res),
 1457: 
 1458:         ExpectedHeader = ["jid", "content", "unread_count", "timestamp"],
 1459: 
 1460:         unregister(Alice, Config),
 1461: 
 1462:         %% MUC Light affiliations are also stored in inbox
 1463:         %% 1. Added to the room
 1464:         %% 2. Message
 1465:         %% 3. Aff change: Alice -> none, Bob -> owner
 1466:         %% Writing aff changes to inbox is enabled by default
 1467:         ExpectedBobItems = [#{
 1468:                                 "jid" => [{contains, <<Room/binary, $@, Domain/binary>>}],
 1469:                                 "unread_count" => "3" }
 1470:                            ],
 1471: 
 1472:         retrieve_and_validate_personal_data(
 1473:            Bob, Config, "inbox", ExpectedHeader, ExpectedBobItems),
 1474: 
 1475:         assert_personal_data_via_rpc(Alice, [{inbox, ExpectedHeader, []}]),
 1476: 
 1477:         timer:sleep(5000),
 1478:         StanzaDestroy = escalus_stanza:to(escalus_stanza:iq_set(?NS_MUC_LIGHT_DESTROY, []),
 1479:                                       room_bin_jid(Room)),
 1480:         escalus:send(Alice, StanzaDestroy),
 1481:         ok
 1482:         end).
 1483: 
 1484: remove_inbox_muc(Config) ->
 1485:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1486:         {ok, Room} = given_fresh_muc_room(Alice#client.props, []),
 1487: 
 1488:         Users = [Alice, Bob],
 1489:         Msg = <<"Hi Room!">>,
 1490:         Id = <<"MyID">>,
 1491:         RoomAddr = muc_helper:room_address(Room),
 1492: 
 1493:         inbox_helper:enter_room(Room, Users),
 1494:         inbox_helper:make_members(Room, Alice, [Bob]),
 1495:         Stanza = escalus_stanza:set_id(
 1496:           escalus_stanza:groupchat_to(RoomAddr, Msg), Id),
 1497:         escalus:send(Bob, Stanza),
 1498:         inbox_helper:wait_for_groupchat_msg(Users),
 1499: 
 1500:         ExpectedHeader = ["jid", "content", "unread_count", "timestamp"],
 1501: 
 1502:         unregister(Alice, Config),
 1503: 
 1504:         escalus:wait_for_stanza(Bob),
 1505:         assert_personal_data_via_rpc(Alice, [{inbox, ExpectedHeader, []}]),
 1506: 
 1507:         ExpectedBobItems = [#{
 1508:                                 "content" => [{contains, Msg}],
 1509:                                 "jid" => [{contains, RoomAddr}],
 1510:                                 "unread_count" => "0" }],
 1511: 
 1512:          retrieve_and_validate_personal_data(
 1513:            Bob, Config, "inbox", ExpectedHeader, ExpectedBobItems),
 1514:         ok
 1515:       end).
 1516: 
 1517: retrieve_inbox_for_multiple_messages(Config) ->
 1518:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
 1519:             Bodies = [ <<"Nobody exists on purpose.">>,
 1520:                        <<"Nobody belongs anywhere.">>,
 1521:                        <<"We're all going to die.">>,
 1522:                        <<"Come watch TV.">>],
 1523:             lists:foreach(fun(Body) -> send_and_assert_is_chat_message(Bob, Alice, Body) end, Bodies),
 1524:             BobU = escalus_utils:jid_to_lower(escalus_client:username(Bob)),
 1525:             BobS = escalus_utils:jid_to_lower(escalus_client:server(Bob)),
 1526: 
 1527:             ExpectedHeader = ["jid", "content", "unread_count", "timestamp"],
 1528:             ExpectedAliceItems = [#{ "content" => [{contains, lists:last(Bodies)}],
 1529:                                      "jid" => [{contains, BobS},
 1530:                                                {contains, BobU}],
 1531:                                      "unread_count" => integer_to_list(length(Bodies)) }],
 1532:             retrieve_and_validate_personal_data(
 1533:               Alice, Config, "inbox", ExpectedHeader, ExpectedAliceItems)
 1534:         end).
 1535: 
 1536: retrieve_logs(Config) ->
 1537:     escalus:fresh_story(Config, [{alice, 1}],
 1538:         fun(Alice) ->
 1539:             User = string:to_lower(binary_to_list(escalus_client:username(Alice))),
 1540:             Domain = string:to_lower(binary_to_list(escalus_client:server(Alice))),
 1541:             JID = string:to_upper(binary_to_list(escalus_client:short_jid(Alice))),
 1542:             #{node := MIM2NodeName} = MIM2Node = distributed_helper:mim2(),
 1543:             mongoose_helper:successful_rpc(net_kernel, connect_node, [MIM2NodeName]),
 1544:             mongoose_helper:successful_rpc(MIM2Node, error_logger, error_msg,
 1545:                                            ["event=disturbance_in_the_force, jid=~s", [JID]]),
 1546:             Dir = request_and_unzip_personal_data(list_to_binary(User), list_to_binary(Domain),
 1547:                                                   Config),
 1548:             Filename = filename:join(Dir, "logs-" ++ atom_to_list(MIM2NodeName) ++ ".txt"),
 1549:             {ok, Content} = file:read_file(Filename),
 1550:             {match, _} = re:run(Content, "disturbance_in_the_force")
 1551:         end).
 1552: 
 1553: %% -------------------------------------------------------------
 1554: %% Internal functions
 1555: %% -------------------------------------------------------------
 1556: 
 1557: assert_personal_data_via_rpc(Client, ExpectedPersonalDataEntries) ->
 1558:     ExpectedKeys = [ Key || {Key, _, _} <- ExpectedPersonalDataEntries ],
 1559: 
 1560:     %% We use wait_until here, because e.g. the deletion in ElasticSearch
 1561:     %% sometimes is applied with a delay (i.e. immediately after successful deletion
 1562:     %% the data retrieval still returned valid entries)
 1563:     mongoose_helper:wait_until(
 1564:             fun() ->
 1565:                 get_personal_data_via_rpc(Client, ExpectedKeys)
 1566:             end, ExpectedPersonalDataEntries).
 1567: 
 1568: get_personal_data_via_rpc(Client, ExpectedKeys) ->
 1569:     ClientU = escalus_utils:jid_to_lower(escalus_client:username(Client)),
 1570:     ClientS = escalus_utils:jid_to_lower(escalus_client:server(Client)),
 1571:     AllPersonalData = mongoose_helper:successful_rpc(
 1572:                         gdpr_api, get_data_from_modules, [ClientU, ClientS]),
 1573:     %% We don't use lists:filter/2 because this line also ensures order
 1574:     [ lists:keyfind(Key, 1, AllPersonalData) || Key <- ExpectedKeys ].
 1575: 
 1576: retrieve_and_validate_personal_data(User, Config, FilePrefix, ExpectedHeader, ExpectedItems) ->
 1577:     Dir = retrieve_all_personal_data(User, Config),
 1578:     validate_personal_data(Dir, FilePrefix, ExpectedHeader, ExpectedItems, ExpectedHeader).
 1579: 
 1580: retrieve_and_validate_personal_data(User, Config, FilePrefix, ExpectedHeader, ExpectedItems, SortBy) ->
 1581:     Dir = retrieve_all_personal_data(User, Config),
 1582:     validate_personal_data(Dir, FilePrefix, ExpectedHeader, ExpectedItems, SortBy).
 1583: 
 1584: validate_personal_data(Dir, FilePrefix, ExpectedHeader, ExpectedItems, SortBy) ->
 1585:     PersonalCSV = decode_personal_data(Dir, FilePrefix),
 1586:     UnsortedMaps = csv_to_maps(ExpectedHeader, PersonalCSV),
 1587:     PersonalMaps = lists:sort(get_sort_fn(SortBy), UnsortedMaps),
 1588:     try validate_personal_maps(PersonalMaps, ExpectedItems) of
 1589:         _ -> ok
 1590:     catch
 1591:         C:R:S ->
 1592:             ct:fail(#{
 1593:                         class => C,
 1594:                         reason => R,
 1595:                         stacktrace => S,
 1596:                         sorted_by => SortBy,
 1597:                         personal_maps => PersonalMaps,
 1598:                         expected_items => ExpectedItems
 1599:                     })
 1600:     end.
 1601: 
 1602: 
 1603: get_sort_fn(SortBy) when is_list(SortBy) ->
 1604:     %% if SortBy is [], than original list remains unsorted.
 1605:     fun(Map1, Map2) -> compare_maps(SortBy, Map1, Map2) end;
 1606: get_sort_fn(SortFn) when is_function(SortFn, 2) ->
 1607:     SortFn.
 1608: 
 1609: compare_maps([], _, _) -> true;
 1610: compare_maps([Key | T], Map1, Map2) ->
 1611:     #{Key:=Val1} = Map1,
 1612:     #{Key:=Val2} = Map2,
 1613:     if
 1614:         Val1 =:= Val2 -> compare_maps(T, Map1, Map2);
 1615:         Val1 > Val2 -> false;
 1616:         Val1 < Val2 -> true
 1617:     end.
 1618: 
 1619: muc_msg_first(MucJid) ->
 1620:     MucJidNormalized = escalus_utils:jid_to_lower(to_binary(MucJid)),
 1621:     N = erlang:byte_size(MucJidNormalized),
 1622:     fun(#{"from" := JID1}, #{"from" := JID2}) ->
 1623:         Jid1Normalized = escalus_utils:jid_to_lower(to_binary(JID1)),
 1624:         Jid2Normalized = escalus_utils:jid_to_lower(to_binary(JID2)),
 1625:         case {Jid1Normalized, Jid2Normalized} of
 1626:             {<<MucJidNormalized:N/binary, _/binary>>, <<MucJidNormalized:N/binary, _/binary>>} ->
 1627:                 Jid1Normalized =< Jid2Normalized;
 1628:             {<<MucJidNormalized:N/binary, _/binary>>, _} ->
 1629:                 true;
 1630:             {_, <<MucJidNormalized:N/binary, _/binary>>} ->
 1631:                 false;
 1632:             {_, _} ->
 1633:                 Jid1Normalized =< Jid2Normalized
 1634:         end
 1635:     end.
 1636: 
 1637: csv_to_maps(ExpectedHeader, [ExpectedHeader | Rows]) ->
 1638:     lists:foldl(fun(Row, Maps) -> [ csv_row_to_map(ExpectedHeader, Row) | Maps ] end, [], Rows).
 1639: 
 1640: csv_row_to_map(Header, Row) ->
 1641:     maps:from_list(lists:zip(Header, Row)).
 1642: 
 1643: validate_personal_maps(PersonalMaps, ExpectedItems) ->
 1644:     validate_sorted_personal_maps(PersonalMaps, ExpectedItems).
 1645: 
 1646: validate_sorted_personal_maps([], []) -> ok;
 1647: validate_sorted_personal_maps(UnexpectedRecords, []) ->
 1648:     erlang:error("Unexpected records left ~p", [UnexpectedRecords]);
 1649: validate_sorted_personal_maps([Map | RMaps], [Checks | RChecks]) ->
 1650:     maps:fold(fun(K, Conditions, _) ->
 1651:                       validate_personal_item(maps:get(K, Map), Conditions)
 1652:               end, ok, Checks),
 1653:     validate_sorted_personal_maps(RMaps, RChecks).
 1654: 
 1655: validate_personal_item(_Value, []) ->
 1656:     ok;
 1657: validate_personal_item(ExactValue, ExactValue) ->
 1658:     ok;
 1659: validate_personal_item(Value, [{jid, ExpectedValue} | RConditions]) ->
 1660:     JID = escalus_utils:jid_to_lower(to_binary(Value)),
 1661:     JID = escalus_utils:jid_to_lower(to_binary(ExpectedValue)),
 1662:     validate_personal_item(Value, RConditions);
 1663: validate_personal_item(Value, [{contains, String} | RConditions]) ->
 1664:     {match, _} = re:run(Value, String),
 1665:     validate_personal_item(Value, RConditions);
 1666: validate_personal_item(Value, [{validate, Validator} | RConditions]) when is_function(Validator) ->
 1667:     true = Validator(Value),
 1668:     validate_personal_item(Value, RConditions).
 1669: 
 1670: to_binary(List) when is_list(List)       -> list_to_binary(List);
 1671: to_binary(Binary) when is_binary(Binary) -> Binary.
 1672: 
 1673: decode_personal_data(Dir, FilePrefix) ->
 1674:     CSVPath = filename:join(Dir, FilePrefix ++ ".csv"),
 1675:     {ok, Content} = file:read_file(CSVPath),
 1676:     % We expect non-empty list because it must contain at least header with columns names
 1677:     [_ | _] = csv:decode_binary(Content).
 1678: 
 1679: refute_personal_data(Client, Config, FilePrefix) ->
 1680:     Dir = retrieve_all_personal_data(Client, Config),
 1681:     refute_personal_data(Dir, FilePrefix).
 1682: 
 1683: refute_personal_data(Dir, FilePrefix) ->
 1684:     CSVPath = filename:join(Dir, FilePrefix ++ ".csv"),
 1685:     false = filelib:is_regular(CSVPath).
 1686: 
 1687: retrieve_all_personal_data(Client, Config) ->
 1688:     User = escalus_client:username(Client),
 1689:     Domain = escalus_client:server(Client),
 1690:     request_and_unzip_personal_data(User, Domain, Config).
 1691: 
 1692: request_and_unzip_personal_data(User, Domain, Config) ->
 1693:     {Filename, Res} = retrieve_personal_data(User, Domain, Config),
 1694:     ParsedResult = get_ok_value([data, gdpr, retrievePersonalData], Res),
 1695:     ?assertEqual(<<"Data retrieved">>, ParsedResult),
 1696:     FullPath = get_mim_cwd() ++ "/" ++ Filename,
 1697:     Dir = make_dir_name(Filename, User),
 1698:     ct:log("extracting logs ~s", [Dir]),
 1699:     {ok, _} = zip:extract(FullPath, [{cwd, Dir}]),
 1700:     Dir.
 1701: 
 1702: make_dir_name(Filename, User) when is_binary(User) ->
 1703:     make_dir_name(Filename, binary_to_list(User));
 1704: make_dir_name(Filename, User) when is_list(User) ->
 1705:     Filename ++ "." ++ User ++ ".unzipped".
 1706: 
 1707: retrieve_personal_data(User, Domain, Config) ->
 1708:     Filename = random_filename(Config),
 1709:     Vars = #{<<"username">> => User, <<"domain">> => Domain,
 1710:              <<"resultFilepath">> => list_to_binary(Filename)},
 1711:     Result = execute_command(<<"gdpr">>, <<"retrievePersonalData">>, Vars, Config),
 1712:     {Filename, Result}.
 1713: 
 1714: unregister(Client, Config) ->
 1715:     User = escalus_client:full_jid(Client),
 1716:     Path = [data, account, removeUser, message],
 1717:     Vars = #{<<"user">> => User},
 1718:     Resp = execute_command(<<"account">>, <<"removeUser">>, Vars, Config),
 1719:     ?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Resp),
 1720:                                           <<"successfully unregister">>)).
 1721: 
 1722: random_filename(Config) ->
 1723:     TCName = atom_to_list(?config(tc_name, Config)),
 1724:     TCName ++ "." ++ integer_to_list(erlang:system_time()) ++ ".zip".
 1725: 
 1726: get_mim_cwd() ->
 1727:     {ok, Cwd} = rpc(mim(), file, get_cwd, []),
 1728:     Cwd.
 1729: 
 1730: delete_files() ->
 1731:     Cwd = get_mim_cwd(),
 1732:     {ok, Filenames} = rpc(mim(), file, list_dir, [Cwd]),
 1733:     FilteredFilenames = lists:filter(
 1734:         fun is_file_to_be_deleted/1,
 1735:         Filenames),
 1736:     lists:foreach(
 1737:         fun(Filename) -> rpc(mim(), file, delete, [Cwd ++ "/" ++ Filename]) end,
 1738:         FilteredFilenames),
 1739:     ok.
 1740: 
 1741: is_file_to_be_deleted(Filename) ->
 1742:     DeletableRegexes = ["\.csv", "\.zip"],
 1743:     lists:any(
 1744:         fun(Regex) ->
 1745:             re:run(Filename, Regex) =/= nomatch
 1746:         end,
 1747:     DeletableRegexes).
 1748: 
 1749: pubsub_payloads_row_map(Node, ItemId, Payload) ->
 1750:     #{"node_name" => binary_to_list(Node), "item_id" => ItemId, "payload" => Payload}.
 1751: 
 1752: pubsub_nodes_row_map(Node, Type) ->
 1753:     #{"node_name" => binary_to_list(Node), "type" => Type}.
 1754: 
 1755: pubsub_subscription_row_map(Node) ->
 1756:     #{"node_name" => binary_to_list(Node)}.
 1757: 
 1758: make_pep_node_info(Client, NodeName) ->
 1759:     {escalus_utils:jid_to_lower(escalus_utils:get_short_jid(Client)), NodeName}.
 1760: 
 1761: item_content(Data) ->
 1762:     Bin = item_content_xml(Data),
 1763:     {Bin, binary_to_list(exml:to_binary(Bin))}.
 1764: 
 1765: item_content_xml(Data) ->
 1766:     #xmlel{name = <<"entry">>,
 1767:            attrs = [{<<"xmlns">>, <<"http://www.w3.org/2005/Atom">>}],
 1768:            children = [#xmlcdata{content = Data}]}.
 1769: 
 1770: send_and_assert_private_stanza(User, NS, Content) ->
 1771:     XML = #xmlel{ name = <<"fingerprint">>,
 1772:                   attrs = [{<<"xmlns">>, NS}],
 1773:                   children = [#xmlcdata{ content = Content }]},
 1774:     PrivateStanza = escalus_stanza:private_set(XML),
 1775:     escalus_client:send(User, PrivateStanza),
 1776:     escalus:assert(is_iq_result, [PrivateStanza], escalus_client:wait_for_stanza(User)).
 1777: 
 1778: send_and_assert_is_chat_message(UserFrom, UserTo, Body) ->
 1779:     escalus:send(UserFrom, escalus_stanza:chat_to(UserTo, Body)),
 1780:     Msg = escalus:wait_for_stanza(UserTo),
 1781:     escalus:assert(is_chat_message, [Body], Msg).
 1782: 
 1783: validate_datetime(TimeStr) ->
 1784:     [Date, Time] = string:tokens(TimeStr, "T"),
 1785:     validate_date(Date),
 1786:     validate_time(Time).
 1787: 
 1788: validate_date(Date) ->
 1789:     [Y, M, D] = string:tokens(Date, "-"),
 1790:     Date1 = {list_to_integer(Y), list_to_integer(M), list_to_integer(D)},
 1791:     calendar:valid_date(Date1).
 1792: 
 1793: validate_time(Time) ->
 1794:   [T | _] = string:tokens(Time, "Z"),
 1795:   validate_time1(T).
 1796: 
 1797: 
 1798: validate_time1(Time) ->
 1799:     [H, M, S] = string:tokens(Time, ":"),
 1800:     check_list([{H, 24}, {M, 60}, {S, 60}]).
 1801: 
 1802: check_list(List) ->
 1803:     lists:all(fun({V, L}) -> I = list_to_integer(V), I >= 0 andalso I < L end, List).
 1804: 
 1805: expected_header(mod_roster) -> ["jid", "name", "subscription",
 1806:                               "ask", "groups", "askmessage", "xs"].
 1807: 
 1808: given_fresh_muc_room(UserSpec, RoomOpts) ->
 1809:     Username = proplists:get_value(username, UserSpec),
 1810:     RoomName = muc_helper:fresh_room_name(Username),
 1811:     From = muc_helper:generate_rpc_jid({user, UserSpec}),
 1812:     muc_helper:create_instant_room(RoomName, From, Username, RoomOpts),
 1813:     {ok, RoomName}.
 1814: 
 1815: send_receive_muc_private_message(Room, Domain, {User1, Nickname1}, {User2, Nickname2}, Text) ->
 1816:     RoomPrivAddrUser1 = <<Room/binary, "@", Domain/binary, "/", Nickname1/binary>>,
 1817:     RoomPrivAddrUser2 = <<Room/binary, "@", Domain/binary, "/", Nickname2/binary>>,
 1818:     Msg = escalus_stanza:chat_to(RoomPrivAddrUser2, Text),
 1819:     escalus:send(User1, Msg),
 1820:     PMStanza = escalus:wait_for_stanza(User2),
 1821:     escalus:assert(is_chat_message_from_to,
 1822:                    [RoomPrivAddrUser1, escalus_client:full_jid(User2), Text], PMStanza),
 1823:     {RoomPrivAddrUser1, RoomPrivAddrUser2}.