1: %%==============================================================================
    2: %% Copyright 2010 Erlang Solutions Ltd.
    3: %%
    4: %% Licensed under the Apache License, Version 2.0 (the "License");
    5: %% you may not use this file except in compliance with the License.
    6: %% You may obtain a copy of the License at
    7: %%
    8: %% http://www.apache.org/licenses/LICENSE-2.0
    9: %%
   10: %% Unless required by applicable law or agreed to in writing, software
   11: %% distributed under the License is distributed on an "AS IS" BASIS,
   12: %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13: %% See the License for the specific language governing permissions and
   14: %% limitations under the License.
   15: %%==============================================================================
   16: 
   17: -module(mod_blocking_SUITE).
   18: -compile([export_all, nowarn_export_all]).
   19: 
   20: -include_lib("exml/include/exml.hrl").
   21: -include_lib("escalus/include/escalus.hrl").
   22: -include_lib("common_test/include/ct.hrl").
   23: -include_lib("eunit/include/eunit.hrl").
   24: -include_lib("escalus/include/escalus_xmlns.hrl").
   25: 
   26: -define(SLEEP_TIME, 50).
   27: 
   28: %%--------------------------------------------------------------------
   29: %% Suite configuration
   30: %%--------------------------------------------------------------------
   31: 
   32: all() ->
   33:     [
   34:         {group, manage},
   35:         {group, effect},
   36:         {group, offline},
   37:         {group, errors},
   38:         {group, pushes}
   39:     ].
   40: 
   41: groups() ->
   42:     [
   43:          {manage, [parallel], manage_test_cases()},
   44:          {effect, [parallel], effect_test_cases()},
   45:          {offline, [sequence], offline_test_cases()},
   46:          {errors, [parallel], error_test_cases()},
   47:          {pushes, [parallel], push_test_cases()},
   48:          {notify, [parallel], notify_test_cases()}
   49:     ].
   50: 
   51: manage_test_cases() ->
   52:     [
   53:         discovering_support,
   54:         get_block_list,
   55:         add_user_to_blocklist,
   56:         add_user_to_blocklist_with_white_spaces,
   57:         add_another_user_to_blocklist,
   58:         add_many_users_to_blocklist,
   59:         remove_user_from_blocklist,
   60:         remove_many_user_from_blocklist,
   61:         clear_blocklist,
   62:         invalid_block_request
   63:     ].
   64: 
   65: effect_test_cases() ->
   66:     [
   67:         messages_from_blocked_user_dont_arrive,
   68:         messages_from_unblocked_user_arrive_again,
   69:         messages_from_any_blocked_resource_dont_arrive,
   70:         blocking_doesnt_interfere,
   71:         blocking_propagates_to_resources,
   72:         iq_reply_doesnt_crash_user_process
   73:     ].
   74: 
   75: offline_test_cases() ->
   76:     [
   77:         messages_after_relogin,
   78:         messages_arrive_after_unblock_and_relogin,
   79:         blocking_and_relogin_many,
   80:         clear_list_relogin
   81:     ].
   82: 
   83: error_test_cases() ->
   84:     [blocker_cant_send_to_blockee].
   85: 
   86: push_test_cases() ->
   87:     [block_push_sent].
   88: 
   89: notify_test_cases() ->
   90:     [notify_blockee].
   91: 
   92: suite() ->
   93:     escalus:suite().
   94: 
   95: %%--------------------------------------------------------------------
   96: %% Init & teardown
   97: %%--------------------------------------------------------------------
   98: 
   99: init_per_suite(Config0) ->
  100:     HostType = domain_helper:host_type(),
  101:     Config1 = dynamic_modules:save_modules(HostType, Config0),
  102:     Backend = mongoose_helper:get_backend_mnesia_rdbms(HostType),
  103:     ModConfig = [{mod_blocking, set_opts(Backend)}],
  104:     dynamic_modules:ensure_modules(HostType, ModConfig),
  105:     [{backend, Backend} |
  106:      escalus:init_per_suite(Config1)].
  107: 
  108: set_opts(Backend) ->
  109:     #{backend => Backend}.
  110: 
  111: end_per_suite(Config) ->
  112:     escalus_fresh:clean(),
  113:     dynamic_modules:restore_modules(Config),
  114:     escalus:end_per_suite(Config).
  115: 
  116: init_per_group(_GroupName, Config) ->
  117:     escalus_fresh:create_users(Config, escalus:get_users([alice, bob, kate, mike, john])).
  118: 
  119: end_per_group(_GroupName, Config) ->
  120:     Config.
  121: 
  122: init_per_testcase(CaseName, Config) ->
  123:     escalus:init_per_testcase(CaseName, Config).
  124: 
  125: end_per_testcase(CaseName, Config) ->
  126:     escalus:end_per_testcase(CaseName, Config).
  127: 
  128: %%--------------------------------------------------------------------
  129: %% Tests
  130: %%--------------------------------------------------------------------
  131: 
  132: discovering_support(Config) ->
  133:     escalus:fresh_story(
  134:         Config, [{alice, 1}],
  135:         fun(User1) ->
  136:             Server = escalus_client:server(User1),
  137:             IqGet = escalus_stanza:disco_info(Server),
  138:             escalus_client:send(User1, IqGet),
  139:             Result = escalus_client:wait_for_stanza(User1),
  140:             escalus:assert(is_iq_result, [IqGet], Result),
  141:             escalus:assert(has_feature, [?NS_BLOCKING], Result)
  142:         end).
  143: 
  144: 
  145: get_block_list(Config) ->
  146:     escalus:fresh_story(
  147:         Config, [{alice, 1}],
  148:         fun(User1) ->
  149:             Result = get_blocklist(User1),
  150:             escalus:assert(is_iq_result, Result),
  151:             escalus:assert(fun is_blocklist_result_empty/1, Result)
  152:         end).
  153: 
  154: 
  155: add_user_to_blocklist(Config) ->
  156:     escalus:fresh_story(
  157:         Config, [{alice, 1}, {bob, 1}],
  158:         fun(User1, User2) ->
  159:             user_blocks(User1, [User2]),
  160:             BlockList = get_blocklist(User1),
  161:             blocklist_contains_jid(BlockList, User2)
  162:         end).
  163: 
  164: add_user_to_blocklist_with_white_spaces(Config) ->
  165:     escalus:fresh_story(
  166:         Config, [{alice, 1}, {bob, 1}],
  167:         fun(User1, User2) ->
  168:             BlockeeJIDs = [escalus_utils:jid_to_lower(escalus_client:short_jid(B)) || B <- [User2]],
  169:             AddStanza = block_users_stanza_with_white_spaces(BlockeeJIDs),
  170:             escalus_client:send(User1, AddStanza),
  171:             Res = escalus:wait_for_stanza(User1),
  172:             escalus:assert(is_iq_result, Res)
  173:         end).
  174: 
  175: add_another_user_to_blocklist(Config) ->
  176:     escalus:fresh_story(
  177:         Config, [{alice, 1}, {mike, 1}],
  178:         fun(User1, User2) ->
  179:             user_blocks(User1, [User2]),
  180:             BlockList = get_blocklist(User1),
  181:             blocklist_contains_jid(BlockList, User2)
  182:         end).
  183: 
  184: add_many_users_to_blocklist(Config) ->
  185:     escalus:fresh_story(
  186:         Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}],
  187:         fun(User1, User2, User3, User4) ->
  188:             user_blocks(User1, [User2, User3, User4]),
  189:             BlockList = get_blocklist(User1),
  190:             blocklist_contains_jid(BlockList, User2),
  191:             blocklist_contains_jid(BlockList, User3),
  192:             blocklist_contains_jid(BlockList, User4)
  193:         end).
  194: 
  195: remove_user_from_blocklist(Config) ->
  196:     escalus:fresh_story(
  197:         Config, [{alice, 1}, {bob, 1}],
  198:         fun(User1, User2) ->
  199:             user_blocks(User1, [User2]),
  200:             user_unblocks(User1, User2),
  201:             NewList = get_blocklist(User1),
  202:             blocklist_doesnt_contain_jid(NewList, User2)
  203:         end).
  204: 
  205: remove_many_user_from_blocklist(Config) ->
  206:     escalus:fresh_story(
  207:         Config, [{alice, 1}, {bob, 1}, {kate, 1}],
  208:         fun(User1, User2, User3) ->
  209:             user_blocks(User1, [User2, User3]),
  210:             user_unblocks(User1, [User2, User3]),
  211:             NewList = get_blocklist(User1),
  212:             blocklist_doesnt_contain_jid(NewList, User2),
  213:             blocklist_doesnt_contain_jid(NewList, User3)
  214:         end).
  215: 
  216: clear_blocklist(Config) ->
  217:     escalus:fresh_story(
  218:         Config, [{alice, 1}, {bob, 1}, {kate, 1}],
  219:         fun(User1, User2, User3) ->
  220:             user_blocks(User1, [User2, User3]),
  221:             user_unblocks_all(User1),
  222:             NewList = get_blocklist(User1),
  223:             blocklist_is_empty(NewList)
  224:         end).
  225: 
  226: invalid_block_request(Config) ->
  227:     escalus:fresh_story(
  228:         Config, [{alice, 1}],
  229:         fun(User1) ->
  230:             St = block_users_stanza([]),
  231:             escalus_client:send(User1, St),
  232:             privacy_helper:gets_error(User1, <<"modify">>, <<"bad-request">>)
  233:         end).
  234: 
  235: messages_from_blocked_user_dont_arrive(Config) ->
  236:     escalus:fresh_story(
  237:         Config, [{alice, 1}, {bob, 1}],
  238:         fun(User1, User2) ->
  239:             user_blocks(User1, [User2]),
  240:             message(User2, User1, <<"Hi!">>),
  241:             ct:sleep(100),
  242:             escalus_assert:has_no_stanzas(User1),
  243:             privacy_helper:gets_error(User2, <<"cancel">>, <<"service-unavailable">>)
  244:         end).
  245: 
  246: messages_from_unblocked_user_arrive_again(Config) ->
  247:     escalus:fresh_story(
  248:         Config, [{alice, 1}, {bob, 1}],
  249:         fun(User1, User2) ->
  250:             %% given
  251:             user_blocks(User1, [User2]),
  252:             %% when
  253:             user_unblocks(User1, User2),
  254:             %% then
  255:             message_is_delivered(User2, User1, <<"Hello again!">>)
  256:         end).
  257: 
  258: messages_from_any_blocked_resource_dont_arrive(Config) ->
  259:     escalus:fresh_story(
  260:         Config, [{alice, 3}, {bob, 1}],
  261:         fun(User1a, User1b, User1c, User2) ->
  262:             %% given
  263:             user_blocks(User2, [User1a]),
  264:             %% then
  265:             message_is_not_delivered(User1a, [User2], <<"roar!">>),
  266:             privacy_helper:gets_error(User1a, <<"cancel">>, <<"service-unavailable">>),
  267:             message_is_not_delivered(User1b, [User2], <<"woof!">>),
  268:             privacy_helper:gets_error(User1b, <<"cancel">>, <<"service-unavailable">>),
  269:             message_is_not_delivered(User1c, [User2], <<"grrr!">>),
  270:             privacy_helper:gets_error(User1c, <<"cancel">>, <<"service-unavailable">>),
  271:             ct:sleep(100),
  272:             escalus_assert:has_no_stanzas(User2)
  273:         end).
  274: 
  275: blocking_doesnt_interfere(Config) ->
  276:     escalus:fresh_story(
  277:         Config, [{alice, 1}, {bob, 1}, {kate, 1}],
  278:         fun(User1, User2, User3) ->
  279:             %% given
  280:             user_blocks(User1, [User2]),
  281:             %% then
  282:             message_is_not_delivered(User2, [User1], <<"!@#@$@#$%">>),
  283:             privacy_helper:gets_error(User2, <<"cancel">>, <<"service-unavailable">>),
  284:             message_is_delivered(User3, [User1], <<"Ni hao.">>)
  285:         end).
  286: 
  287: blocking_propagates_to_resources(Config) ->
  288:     escalus:fresh_story(
  289:         Config, [{alice, 2}, {bob, 1}],
  290:         fun(User1a, User1b, User2) ->
  291:             %% given
  292:             user_blocks(User1a, [User2]),
  293:             %% then
  294:             client_gets_block_iq(User1b),
  295:             % Alice can't send from any of her resources
  296:             message_is_not_delivered(User1a, [User2], <<"roar!">>),
  297:             client_gets_blocking_error(User1a),
  298:             message_is_not_delivered(User1b, [User2], <<"woof!">>),
  299:             client_gets_blocking_error(User1b),
  300:             % Bob can't send to any of Alice's resources
  301:             message_is_not_delivered(User2, [User1a], <<"hau!">>),
  302:             privacy_helper:gets_error(User2, <<"cancel">>, <<"service-unavailable">>),
  303:             message_is_not_delivered(User2, [User1b], <<"miau!">>),
  304:             privacy_helper:gets_error(User2, <<"cancel">>, <<"service-unavailable">>)
  305:         end).
  306: 
  307: iq_reply_doesnt_crash_user_process(Config) ->
  308:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  309: 
  310:         QueryWithBlockingNS = escalus_stanza:query_el(?NS_BLOCKING, []),
  311:         %% Send IQ reply with blocking ns
  312:         %% Send message to check user process still alive
  313:         privacy_helper:does_user_process_crash(Alice,
  314:             Bob,
  315:             <<"error">>,
  316:             QueryWithBlockingNS,
  317:             <<"Hello, Bob">>),
  318: 
  319:         privacy_helper:does_user_process_crash(Bob,
  320:             Alice,
  321:             <<"result">>,
  322:             QueryWithBlockingNS,
  323:             <<"Hello, Alice">>)
  324:     end).
  325: 
  326: messages_after_relogin(Config) ->
  327:     %% given
  328:     escalus:story(
  329:         Config, [{alice, 1}, {bob, 1}],
  330:         fun(User1, User2) ->
  331:             user_blocks(User1, [User2])
  332:         end),
  333:     %% XXX Because alice can receive presence unavalable from alice
  334:     %% XXX It's a potential bug, please test it.
  335:     %% XXX has_stanzas_but_shouldnt
  336:     %% XXX reported as https://github.com/esl/MongooseIM/issues/1799
  337:     mongoose_helper:kick_everyone(),
  338:     escalus:story(
  339:         Config, [{alice, 1}, {bob, 1}],
  340:         fun(User1, User2) ->
  341:             message_is_not_delivered(User2, [User1], <<"Hey alice, are you there?">>),
  342:             message_is_delivered(User1, [User1], <<"Hey bob, carpe diem!">>)
  343:         end).
  344: 
  345: messages_arrive_after_unblock_and_relogin(Config) ->
  346:     %% given when
  347:     escalus:story(
  348:         Config, [{alice, 1}, {bob, 1}],
  349:         fun(User1, User2) ->
  350:             user_unblocks(User1, [User2])
  351:         end),
  352:     %% then
  353:     escalus:story(
  354:         Config, [{alice, 1}, {bob, 1}],
  355:         fun(User1, User2) ->
  356:             message_is_delivered(User2, [User1], <<"Hey bob, are you there?">>)
  357:         end).
  358: 
  359: blocking_and_relogin_many(Config) ->
  360:     %% given
  361:     simple_story(Config,
  362:         fun(User1, User2, User3, User4, _) ->
  363:             user_blocks(User1, [User2, User3]),
  364:             user_blocks(User1, [User3, User4])
  365:         end),
  366:     %% when
  367:     simple_story(Config,
  368:         fun(User1, User2, _, User4, User5) ->
  369:             user_unblocks(User1,  [User4]),
  370:             user_unblocks(User1,  [User4, User5]),
  371:             user_unblocks(User1,  [User2, User5])
  372:         end),
  373:     %% then
  374:     simple_story(Config,
  375:         fun(User1, User2, User3, User4, _) ->
  376:             message_is_delivered(User1, [User4], <<"Under the bridge!">>),
  377:             message_is_not_delivered(User1, [User3], <<"Cant stop">>),
  378:             client_gets_blocking_error(User1),
  379:             message_is_delivered(User1, [User2], <<"House of th rising sun">>),
  380:             BlockList = get_blocklist(User1),
  381:             blocklist_contains_jid(BlockList, User3)
  382:         end).
  383: 
  384: simple_story(Config, Fun) ->
  385:     escalus:story(
  386:         Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}, {john, 1}],
  387:         Fun
  388:     ).
  389: 
  390: clear_list_relogin(Config) ->
  391:     %% unexpected presence unavailable
  392:     mongoose_helper:kick_everyone(),
  393:     escalus:story(
  394:         Config, [{alice, 1}, {bob, 1}],
  395:         fun(User1, User2) ->
  396:             user_blocks(User1, [User2])
  397:         end),
  398:     escalus:story(
  399:         Config, [{alice, 1}],
  400:         fun(User1) ->
  401:             user_unblocks_all(User1)
  402:         end),
  403:     escalus:story(
  404:         Config, [{alice, 1}, {bob, 1}],
  405:         fun(User1, User2) ->
  406:             message_is_delivered(User1, [User2], <<"Doom and gloom!">>)
  407:         end).
  408: 
  409: blocker_cant_send_to_blockee(Config) ->
  410:     escalus:fresh_story(
  411:         Config, [{alice, 1}, {bob, 1}],
  412:         fun(User1, User2) ->
  413:             user_blocks(User1, [User2]),
  414:             message(User1, User2, <<"I'm not talking to you!">>),
  415:             client_gets_blocking_error(User1)
  416:         end).
  417: 
  418: block_push_sent(Config) ->
  419:     %% make sure privacy list push arrives to all the user's resources
  420:     escalus:fresh_story(
  421:         Config, [{alice, 2}, {bob, 2}],
  422:         fun(User1a, User1b, User2a, _User2b) ->
  423:             user_blocks(User1a, [User2a]),
  424:             client_gets_block_iq(User1b)
  425:         end).
  426: 
  427: notify_blockee(Config) ->
  428:     %% as XEP-0191 says, when we block a user he should receive 'unavailable', and a contrario.
  429:     escalus:fresh_story(
  430:         Config, [{alice, 1}, {bob, 1}],
  431:         fun(Alice, Bob) ->
  432:             %% make sure they're friends and Bob receives Alice's presences
  433:             subscribe(Bob, Alice),
  434:             escalus:send(Alice, escalus_stanza:presence(<<"available">>)),
  435:             escalus:assert(is_presence_with_type, [<<"available">>],
  436:                            escalus:wait_for_stanza(Alice)),
  437:             escalus:assert(is_presence_with_type, [<<"available">>],
  438:                            escalus:wait_for_stanza(Bob)),
  439:             user_blocks(Alice, [Bob]),
  440:             escalus:assert(is_presence_with_type, [<<"unavailable">>],
  441:                            escalus:wait_for_stanza(Bob)),
  442:             user_unblocks(Alice, [Bob]),
  443:             escalus:assert(is_presence_with_type, [<<"available">>],
  444:                            escalus:wait_for_stanza(Bob))
  445:         end).
  446: 
  447: %% common
  448: 
  449: %%
  450: get_blocklist(User) ->
  451:     IQGet = get_blocklist_stanza(),
  452:     escalus_client:send(User, IQGet),
  453:     escalus_client:wait_for_stanza(User).
  454: 
  455: %%
  456: %% stanza generators
  457: %%
  458: 
  459: get_blocklist_stanza() ->
  460:     Payload = #xmlel{name = <<"blocklist">>,
  461:         attrs=[{<<"xmlns">>, ?NS_BLOCKING}]},
  462:     #xmlel{name = <<"iq">>,
  463:         attrs = [{<<"type">>, <<"get">>}],
  464:         children = [Payload]}.
  465: 
  466: block_users_stanza(UsersToBlock) ->
  467:     Childs = [item_el(U) || U <- UsersToBlock],
  468:     Payload = #xmlel{name = <<"block">>,
  469:         attrs=[{<<"xmlns">>, ?NS_BLOCKING}],
  470:         children = Childs
  471:     },
  472:     #xmlel{name = <<"iq">>,
  473:         attrs = [{<<"type">>, <<"set">>}],
  474:         children = [Payload]}.
  475: 
  476: block_users_stanza_with_white_spaces(UsersToBlock) ->
  477:     Childs = [item_el(U) || U <- UsersToBlock],
  478:     % when client adds some white characters in blocking list
  479:     WhiteSpacedChilds = Childs ++ [{xmlcdata, "\n"}],
  480:     Payload = #xmlel{name = <<"block">>,
  481:         attrs=[{<<"xmlns">>, ?NS_BLOCKING}],
  482:         children = WhiteSpacedChilds
  483:     },
  484:     #xmlel{name = <<"iq">>,
  485:         attrs = [{<<"type">>, <<"set">>}],
  486:         children = [Payload]}.
  487: 
  488: 
  489: %%block_user_stanza(UserToBlock) ->
  490: %%    Payload = #xmlel{name = <<"block">>,
  491: %%        attrs=[{<<"xmlns">>, ?NS_BLOCKING}],
  492: %%        children = [item_el(UserToBlock)]
  493: %%    },
  494: %%    #xmlel{name = <<"iq">>,
  495: %%        attrs = [{<<"type">>, <<"set">>}],
  496: %%        children = Payload}.
  497: 
  498: unblock_user_stanza(UserToUnblock) ->
  499:     Payload = #xmlel{name = <<"unblock">>,
  500:         attrs=[{<<"xmlns">>, ?NS_BLOCKING}],
  501:         children = [item_el(UserToUnblock)]
  502:     },
  503:     #xmlel{name = <<"iq">>,
  504:         attrs = [{<<"type">>, <<"set">>}],
  505:         children = [Payload]}.
  506: 
  507: unblock_users_stanza(UsersToBlock) ->
  508:     Childs = [item_el(U) || U <- UsersToBlock],
  509:     Payload = #xmlel{name = <<"unblock">>,
  510:         attrs=[{<<"xmlns">>, ?NS_BLOCKING}],
  511:         children = Childs
  512:     },
  513:     #xmlel{name = <<"iq">>,
  514:         attrs = [{<<"type">>, <<"set">>}],
  515:         children = [Payload]}.
  516: 
  517: unblock_all_stanza() ->
  518:     Payload = #xmlel{name = <<"unblock">>,
  519:         attrs=[{<<"xmlns">>, ?NS_BLOCKING}],
  520:         children = []
  521:     },
  522:     #xmlel{name = <<"iq">>,
  523:         attrs = [{<<"type">>, <<"set">>}],
  524:         children = [Payload]}.
  525: 
  526: item_el(User) when is_binary(User) ->
  527:     #xmlel{name = <<"item">>,
  528:         attrs = [{<<"jid">>, User}]}.
  529: %%
  530: %% predicates
  531: %%
  532: 
  533: is_xep191_not_available(#xmlel{} = Stanza) ->
  534:     ErrorEl = exml_query:subelement(Stanza, <<"error">>),
  535:     <<"error">> == exml_query:attr(Stanza, <<"type">>)
  536:         andalso
  537:         undefined =/= exml_query:subelement(ErrorEl, <<"not-acceptable">>)
  538:         andalso
  539:         undefined =/= exml_query:subelement(ErrorEl, <<"blocked">>)
  540:         andalso
  541:         <<"urn:xmpp:blocking:errors">> ==
  542:             exml_query:path(ErrorEl, [{element, <<"blocked">>},
  543:                 {attr, <<"xmlns">>}]).
  544: 
  545: 
  546: is_blocklist_result_empty(#xmlel{children = [#xmlel{name =Name,
  547:     attrs = Attrs,
  548:     children= Child}]} = Stanza) ->
  549:     true = escalus_pred:is_iq(Stanza),
  550:     <<"blocklist">> = Name,
  551:     {<<"xmlns">>, ?NS_BLOCKING} = lists:keyfind(<<"xmlns">>, 1, Attrs),
  552:     [] = Child,
  553:     true.
  554: 
  555: blocklist_result_has(ExpectedUser, Stanza) ->
  556:     true = escalus_pred:is_iq(Stanza),
  557:     Blocklist = hd(Stanza#xmlel.children),
  558:     Attrs = Blocklist#xmlel.attrs,
  559:     Children = Blocklist#xmlel.children,
  560:     <<"blocklist">> = Blocklist#xmlel.name,
  561:     {<<"xmlns">>, ?NS_BLOCKING} = lists:keyfind(<<"xmlns">>, 1, Attrs),
  562:     true == lists:member(ExpectedUser, get_blocklist_items(Children)).
  563: 
  564: is_xep191_push(Type, #xmlel{attrs = A, children = [#xmlel{name = Type,
  565:     attrs = Attrs}]}=Stanza) ->
  566:     true = escalus_pred:is_iq_set(Stanza),
  567:     {<<"id">>, <<"push">>} = lists:keyfind(<<"id">>, 1, A),
  568:     {<<"xmlns">>, ?NS_BLOCKING} = lists:keyfind(<<"xmlns">>, 1, Attrs),
  569:     true.
  570: 
  571: is_xep191_push(Type, [], #xmlel{children = [#xmlel{name = Type, children = []}]}=Stanza) ->
  572:     is_xep191_push(Type, Stanza);
  573: is_xep191_push(Type, [], #xmlel{children = [#xmlel{name = Type, children = _}]}) ->
  574:     false;
  575: is_xep191_push(Type, JIDs, #xmlel{attrs = _, children = [#xmlel{name = Type,
  576:     attrs = Attrs, children = Items}]}=Stanza) ->
  577:     true = escalus_pred:is_iq_set(Stanza),
  578:     {<<"xmlns">>, ?NS_BLOCKING} = lists:keyfind(<<"xmlns">>, 1, Attrs),
  579:     F = fun(El) ->
  580:         #xmlel{name = <<"item">>, attrs =  [{<<"jid">>, Value}]} = El,
  581:         lists:member(Value, JIDs)
  582:         end,
  583:     TrueList = lists:map(F, Items),
  584:     lists:all(fun(El) -> El end, TrueList);
  585: is_xep191_push(_, _, _) ->
  586:     false.
  587: 
  588: %%
  589: %% helpers
  590: %%
  591: 
  592: bare(C) ->  escalus_utils:jid_to_lower(escalus_client:short_jid(C)).
  593: 
  594: get_blocklist_items(Items) ->
  595:     lists:map(fun(#xmlel{name = <<"item">>, attrs=A}) ->
  596:         {_, R} = lists:keyfind(<<"jid">>, 1, A),
  597:         R
  598:               end, Items).
  599: 
  600: user_blocks(Blocker, Blockees) when is_list(Blockees) ->
  601:     BlockeeJIDs = [ escalus_utils:jid_to_lower(escalus_client:short_jid(B)) || B <- Blockees ],
  602:     AddStanza = block_users_stanza(BlockeeJIDs),
  603:     escalus_client:send(Blocker, AddStanza),
  604:     Res = escalus:wait_for_stanzas(Blocker, 2),
  605:     CheckPush = fun(E) -> is_xep191_push(<<"block">>, BlockeeJIDs, E) end,
  606:     Preds = [is_iq_result, CheckPush],
  607:     escalus:assert_many(Preds, Res).
  608: 
  609: blocklist_is_empty(BlockList) ->
  610:     escalus:assert(is_iq_result, BlockList),
  611:     escalus:assert(fun is_blocklist_result_empty/1, BlockList).
  612: 
  613: blocklist_contains_jid(BlockList, Client) ->
  614:     JID = escalus_utils:jid_to_lower(escalus_client:short_jid(Client)),
  615:     escalus:assert(fun blocklist_result_has/2, [JID], BlockList).
  616: 
  617: user_unblocks(Unblocker, Unblockees) when is_list(Unblockees) ->
  618:     UnblockeeJIDs = [ escalus_utils:jid_to_lower(escalus_client:short_jid(B)) || B <- Unblockees ],
  619:     AddStanza = unblock_users_stanza(UnblockeeJIDs),
  620:     escalus_client:send(Unblocker, AddStanza),
  621:     Res = escalus:wait_for_stanzas(Unblocker, 2),
  622:     CheckPush = fun(E) -> is_xep191_push(<<"unblock">>, UnblockeeJIDs, E) end,
  623:     Preds = [is_iq_result, CheckPush],
  624:     escalus:assert_many(Preds, Res);
  625: user_unblocks(Unblocker, Unblockee) ->
  626:     JID = escalus_utils:jid_to_lower(escalus_client:short_jid(Unblockee)),
  627:     escalus_client:send(Unblocker, unblock_user_stanza(JID)),
  628:     user_gets_remove_result(Unblocker, [JID]).
  629: 
  630: blocklist_doesnt_contain_jid(BlockList, Client) ->
  631:     JID = escalus_utils:jid_to_lower(escalus_client:short_jid(Client)),
  632:     escalus:assert(is_iq_result, BlockList),
  633:     ?assertNot(blocklist_result_has(JID, BlockList)).
  634: 
  635: user_gets_remove_result(Client, ContactList) ->
  636:     RemoveResult = escalus:wait_for_stanzas(Client, 2),
  637:     CheckPush = fun(E) -> is_xep191_push(<<"unblock">>, ContactList, E) end,
  638:     Preds = [is_iq_result, CheckPush],
  639:     escalus:assert_many(Preds, RemoveResult).
  640: 
  641: 
  642: user_unblocks_all(User) ->
  643:     escalus_client:send(User, unblock_all_stanza()),
  644:     user_gets_remove_result(User, []).
  645: 
  646: message(From, To, MsgTxt) ->
  647:     escalus_client:send(From, escalus_stanza:chat_to(To, MsgTxt)).
  648: 
  649: message_is_delivered(From, [To|_] = Tos, MessageText) ->
  650:     BareTo = escalus_utils:jid_to_lower(escalus_client:short_jid(To)),
  651:     escalus:send(From, escalus_stanza:chat_to(BareTo, MessageText)),
  652:     [ escalus:assert(is_chat_message, [MessageText], escalus:wait_for_stanza(C)) ||
  653:         C <- Tos ];
  654: message_is_delivered(From, To, MessageText) ->
  655:     BareTo =  escalus_utils:jid_to_lower(escalus_client:short_jid(To)),
  656:     escalus:send(From, escalus_stanza:chat_to(BareTo, MessageText)),
  657:     escalus:assert(is_chat_message, [MessageText], escalus:wait_for_stanza(To)).
  658: 
  659: message_is_not_delivered(From, [To|_] = Tos, MessageText) ->
  660:     BareTo = escalus_utils:jid_to_lower(escalus_client:short_jid(To)),
  661:     escalus:send(From, escalus_stanza:chat_to(BareTo, MessageText)),
  662:     clients_have_no_messages(Tos).
  663: 
  664: clients_have_no_messages(Cs) when is_list (Cs) -> [ client_has_no_messages(C) || C <- Cs ].
  665: 
  666: client_has_no_messages(C) -> escalus_assert:has_no_stanzas(C).
  667: 
  668: client_gets_blocking_error(C) ->
  669:     Stanza = escalus_client:wait_for_stanza(C),
  670:     escalus:assert(fun is_xep191_not_available/1, [], Stanza).
  671: 
  672: client_gets_block_iq(C) ->
  673:     escalus:assert(fun is_xep191_push/2, [<<"block">>], escalus:wait_for_stanza(C)).
  674: 
  675: flush(User) ->
  676:     escalus:wait_for_stanzas(User, 10, 100).
  677: 
  678: add_sample_contact(Alice, Bob) ->
  679:     escalus:send(Alice, escalus_stanza:roster_add_contact(Bob,
  680:         [<<"friends">>],
  681:         <<"Bobby">>)),
  682: 
  683:     Received = escalus:wait_for_stanzas(Alice, 2),
  684:     escalus:assert_many([is_roster_set, is_iq_result], Received),
  685: 
  686:     Result = hd([R || R <- Received, escalus_pred:is_roster_set(R)]),
  687:     escalus:assert(count_roster_items, [1], Result),
  688:     escalus:send(Alice, escalus_stanza:iq_result(Result)).
  689: 
  690: 
  691: subscribe(Bob, Alice) ->
  692:     %% Bob adds Alice as a contact
  693:     add_sample_contact(Bob, Alice),
  694:     %% He subscribes to her presences
  695:     escalus:send(Bob, escalus_stanza:presence_direct(alice, <<"subscribe">>)),
  696:     PushReq = escalus:wait_for_stanza(Bob),
  697:     escalus:assert(is_roster_set, PushReq),
  698:     escalus:send(Bob, escalus_stanza:iq_result(PushReq)),
  699:     %% Alice receives subscription request
  700:     Received = escalus:wait_for_stanza(Alice),
  701:     escalus:assert(is_presence_with_type, [<<"subscribe">>], Received),
  702:     %% Alice adds new contact to his roster
  703:     escalus:send(Alice, escalus_stanza:roster_add_contact(Bob,
  704:         [<<"enemies">>],
  705:         <<"Bob">>)),
  706:     PushReqB = escalus:wait_for_stanza(Alice),
  707:     escalus:assert(is_roster_set, PushReqB),
  708:     escalus:send(Alice, escalus_stanza:iq_result(PushReqB)),
  709:     escalus:assert(is_iq_result, escalus:wait_for_stanza(Alice)),
  710:     %% Alice sends subscribed presence
  711:     escalus:send(Alice, escalus_stanza:presence_direct(bob, <<"subscribed">>)),
  712:     %% Bob receives subscribed
  713:     Stanzas = escalus:wait_for_stanzas(Bob, 2),
  714:     check_subscription_stanzas(Stanzas, <<"subscribed">>),
  715:     escalus:assert(is_presence, escalus:wait_for_stanza(Bob)),
  716:     %% Alice receives roster push
  717:     PushReqB1 = escalus:wait_for_stanza(Alice),
  718:     escalus:assert(is_roster_set, PushReqB1),
  719:     %% Alice sends presence
  720:     escalus:send(Alice, escalus_stanza:presence(<<"available">>)),
  721:     escalus:assert(is_presence, escalus:wait_for_stanza(Bob)),
  722:     escalus:assert(is_presence, escalus:wait_for_stanza(Alice)),
  723:     ok.
  724: 
  725: 
  726: check_subscription_stanzas(Stanzas, Type) ->
  727:     IsPresWithType = fun(S) -> escalus_pred:is_presence_with_type(Type, S) end,
  728:     escalus:assert_many([is_roster_set, IsPresWithType], Stanzas).