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