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