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