diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index f7d8e9dbb..986310546 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -881,18 +881,20 @@ decode_element(#xmlel{} = El, StateName, StateData) -> end catch error:{xmpp_codec, Why} -> NS = xmpp:get_ns(El), - case xmpp:is_stanza(El) of - true -> - Lang = xmpp:get_lang(El), - Txt = xmpp:format_error(Why), - send_error(StateData, El, xmpp:err_bad_request(Txt, Lang)); - false when NS == ?NS_STREAM_MGMT_2; NS == ?NS_STREAM_MGMT_3 -> - Err = #sm_failed{reason = 'bad-request', xmlns = NS}, - send_element(StateData, Err); - false -> - ok - end, - fsm_next_state(StateName, StateData) + fsm_next_state( + StateName, + case xmpp:is_stanza(El) of + true -> + Lang = xmpp:get_lang(El), + Txt = xmpp:format_error(Why), + send_error(StateData, El, xmpp:err_bad_request(Txt, Lang)); + false when NS == ?NS_STREAM_MGMT_2; NS == ?NS_STREAM_MGMT_3 -> + Err = #sm_failed{reason = 'bad-request', xmlns = NS}, + send_element(StateData, Err), + StateData; + false -> + StateData + end) end. wait_for_bind({xmlstreamelement, El}, StateData) -> @@ -957,13 +959,14 @@ wait_for_bind(closed, StateData) -> wait_for_bind(stop, StateData) -> {stop, normal, StateData}; wait_for_bind(Pkt, StateData) -> - case xmpp:is_stanza(Pkt) of - true -> - send_error(StateData, Pkt, xmpp:err_not_acceptable()); - false -> - ok - end, - fsm_next_state(wait_for_bind, StateData). + fsm_next_state( + wait_for_bind, + case xmpp:is_stanza(Pkt) of + true -> + send_error(StateData, Pkt, xmpp:err_not_acceptable()); + false -> + StateData + end). -spec open_session(state()) -> {ok, state()} | {error, stanza_error()}. open_session(StateData) -> @@ -1315,27 +1318,23 @@ handle_info({route, From, To, Packet}, StateName, StateData) when ?is_stanza(Pac allow -> {true, StateData}; deny -> - Err = xmpp:make_error( - Packet, - xmpp:err_service_unavailable()), - ejabberd_router:route(To, From, Err), + ejabberd_router:route_error( + To, From, Packet, + xmpp:err_service_unavailable()), {false, StateData} end; _ -> - Err = xmpp:make_error(Packet, xmpp:err_forbidden()), - ejabberd_router:route(To, From, Err), + ejabberd_router:route_error( + To, From, Packet, xmpp:err_forbidden()), {false, StateData} end; _ -> case privacy_check_packet(StateData, From, To, Packet, in) of allow -> {true, StateData}; - deny when T == get; T == set -> - Err = xmpp:make_error( - Packet, xmpp:err_service_unavailable()), - ejabberd_router:route(To, From, Err), - {false, StateData}; deny -> + ejabberd_router:route_error( + To, From, Packet, xmpp:err_service_unavailable()), {false, StateData} end end; @@ -1345,13 +1344,11 @@ handle_info({route, From, To, Packet}, StateName, StateData) when ?is_stanza(Pac {true, StateData}; deny -> case T of - error -> ok; groupchat -> ok; headline -> ok; _ -> - Err = xmpp:make_error( - Packet, xmpp:err_service_unavailable()), - ejabberd_router:route(To, From, Err) + ejabberd_router:route_error( + To, From, Packet, xmpp:err_service_unavailable()) end, {false, StateData} end @@ -1572,14 +1569,14 @@ send_element(StateData, #xmlel{} = El) -> send_element(StateData, Pkt) -> send_element(StateData, xmpp:encode(Pkt, ?NS_CLIENT)). --spec send_error(state(), xmlel() | stanza(), stanza_error()) -> ok. +-spec send_error(state(), xmlel() | stanza(), stanza_error()) -> state(). send_error(StateData, Stanza, Error) -> Type = xmpp:get_type(Stanza), if Type == error; Type == result; Type == <<"error">>; Type == <<"result">> -> - ok; + StateData; true -> - send_element(StateData, xmpp:make_error(Stanza, Error)) + send_stanza(StateData, xmpp:make_error(Stanza, Error)) end. -spec send_stanza(state(), xmpp_element()) -> state(). @@ -1754,47 +1751,56 @@ presence_track(From, To, Packet, StateData) -> LTo = jid:tolower(To), User = StateData#state.user, Server = StateData#state.server, - case Type of - unavailable -> - A = ?SETS:del_element(LTo, StateData#state.pres_a), - check_privacy_route(From, StateData#state{pres_a = A}, From, To, Packet); - subscribe -> - try_roster_subscribe(subscribe, User, Server, From, To, Packet, StateData); - subscribed -> - ejabberd_hooks:run(roster_out_subscription, Server, - [User, Server, To, subscribed]), - check_privacy_route(From, StateData, - jid:remove_resource(From), To, Packet); - unsubscribe -> - try_roster_subscribe(unsubscribe, User, Server, From, To, Packet, StateData); - unsubscribed -> - ejabberd_hooks:run(roster_out_subscription, Server, - [User, Server, To, unsubscribed]), - check_privacy_route(From, StateData, - jid:remove_resource(From), To, Packet); - error -> - check_privacy_route(From, StateData, From, To, Packet); - probe -> - check_privacy_route(From, StateData, From, To, Packet); - _ -> - A = (?SETS):add_element(LTo, StateData#state.pres_a), - check_privacy_route(From, StateData#state{pres_a = A}, From, To, Packet) + Lang = StateData#state.lang, + case privacy_check_packet(StateData, From, To, Packet, out) of + deny -> + ErrText = <<"Your active privacy list has denied " + "the routing of this stanza.">>, + Err = xmpp:err_not_acceptable(ErrText, Lang), + send_error(StateData, xmpp:set_from_to(Packet, From, To), Err); + allow when Type == subscribe; Type == subscribed; + Type == unsubscribe; Type == unsubscribed -> + Access = gen_mod:get_module_opt(Server, mod_roster, access, + fun(A) when is_atom(A) -> A end, + all), + MyBareJID = jid:make(User, Server, <<"">>), + case acl:match_rule(Server, Access, MyBareJID) of + deny -> + ErrText = <<"Denied by ACL">>, + Err = xmpp:err_forbidden(ErrText, Lang), + send_error(StateData, xmpp:set_from_to(Packet, From, To), Err); + allow -> + ejabberd_hooks:run(roster_out_subscription, + Server, + [User, Server, To, Type]), + ejabberd_router:route(jid:remove_resource(From), To, Packet), + StateData + end; + allow when Type == error; Type == probe -> + ejabberd_router:route(From, To, Packet), + StateData; + allow -> + ejabberd_router:route(From, To, Packet), + A = case Type of + available -> + ?SETS:add_element(LTo, StateData#state.pres_a); + unavailable -> + ?SETS:del_element(LTo, StateData#state.pres_a) + end, + StateData#state{pres_a = A} end. -spec check_privacy_route(jid(), state(), jid(), jid(), stanza()) -> state(). check_privacy_route(From, StateData, FromRoute, To, Packet) -> case privacy_check_packet(StateData, From, To, Packet, - out) - of + out) of deny -> Lang = StateData#state.lang, ErrText = <<"Your active privacy list has denied " - "the routing of this stanza.">>, - Err = xmpp:make_error( - xmpp:set_from_to(Packet, From, To), - xmpp:err_not_acceptable(ErrText, Lang)), - send_stanza(StateData, Err); + "the routing of this stanza.">>, + Err = xmpp:err_not_acceptable(ErrText, Lang), + send_error(StateData, xmpp:set_from_to(Packet, From, To), Err); allow -> ejabberd_router:route(FromRoute, To, Packet), StateData @@ -1815,24 +1821,6 @@ is_privacy_allow(StateData, From, To, Packet, Dir) -> allow == privacy_check_packet(StateData, From, To, Packet, Dir). -%%% Check ACL before allowing to send a subscription stanza --spec try_roster_subscribe(subscribe | unsubscribe, binary(), binary(), - jid(), jid(), presence(), state()) -> state(). -try_roster_subscribe(Type, User, Server, From, To, Packet, StateData) -> - JID1 = jid:make(User, Server, <<"">>), - Access = gen_mod:get_module_opt(Server, mod_roster, access, fun(A) when is_atom(A) -> A end, all), - case acl:match_rule(Server, Access, JID1) of - deny -> - %% Silently drop this (un)subscription request - StateData; - allow -> - ejabberd_hooks:run(roster_out_subscription, - Server, - [User, Server, To, Type]), - check_privacy_route(From, StateData, jid:remove_resource(From), - To, Packet) - end. - %% Send presence when disconnecting -spec presence_broadcast(state(), jid(), ?SETS:set(), presence()) -> ok. presence_broadcast(StateData, From, JIDSet, Packet) -> @@ -1980,7 +1968,7 @@ process_privacy_iq(#iq{from = From, to = To, privacy_iq_set, StateData#state.server, {error, xmpp:err_feature_not_implemented(Txt, Lang)}, - [IQ]) + [IQ, StateData#state.privacy_list]) of {result, R, NewPrivList} -> {{result, R}, @@ -2522,9 +2510,8 @@ handle_unacked_stanzas(#state{mgmt_state = MgmtState} = StateData) false -> fun(From, To, El, _Time) -> Txt = <<"User session terminated">>, - Err = xmpp:make_error( - El, xmpp:err_service_unavailable(Txt, Lang)), - ejabberd_router:route(To, From, Err) + ejabberd_router:route_error( + To, From, El, xmpp:err_service_unavailable(Txt, Lang)) end end, F = fun(From, _To, #presence{}, _Time) -> @@ -2532,9 +2519,8 @@ handle_unacked_stanzas(#state{mgmt_state = MgmtState} = StateData) [jid:to_string(From)]); (From, To, #iq{} = El, _Time) -> Txt = <<"User session terminated">>, - Err = xmpp:make_error( - El, xmpp:err_service_unavailable(Txt, Lang)), - ejabberd_router:route(To, From, Err); + ejabberd_router:route_error( + To, From, El, xmpp:err_service_unavailable(Txt, Lang)); (From, To, El, Time) -> %% We'll drop the stanza if it was by some %% encapsulating protocol as per XEP-0297. One such protocol is diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl index 5e6e1bf58..0e79c9913 100644 --- a/src/ejabberd_piefxis.erl +++ b/src/ejabberd_piefxis.erl @@ -486,7 +486,7 @@ process_privacy(#privacy_query{lists = Lists, from = JID, to = JID, sub_els = [PrivacyQuery]}, Txt = <<"No module is handling this query">>, Error = {error, xmpp:err_feature_not_implemented(Txt, ?MYLANG)}, - case mod_privacy:process_iq_set(Error, IQ) of + case mod_privacy:process_iq_set(Error, IQ, #userlist{}) of {error, #stanza_error{reason = Reason}} = Err -> if Reason == 'item-not-found', Lists == [], Active == undefined, Default /= undefined -> diff --git a/src/ejabberd_socket.erl b/src/ejabberd_socket.erl index f8dc84630..b5fa52ded 100644 --- a/src/ejabberd_socket.erl +++ b/src/ejabberd_socket.erl @@ -31,6 +31,7 @@ -export([start/4, connect/3, connect/4, + connect/5, starttls/2, starttls/3, compress/1, @@ -125,19 +126,21 @@ start(Module, SockMod, Socket, Opts) -> end. connect(Addr, Port, Opts) -> - connect(Addr, Port, Opts, infinity). + connect(Addr, Port, Opts, infinity, self()). connect(Addr, Port, Opts, Timeout) -> + connect(Addr, Port, Opts, Timeout, self()). + +connect(Addr, Port, Opts, Timeout, Owner) -> case gen_tcp:connect(Addr, Port, Opts, Timeout) of {ok, Socket} -> Receiver = ejabberd_receiver:start(Socket, gen_tcp, none), SocketData = #socket_state{sockmod = gen_tcp, socket = Socket, receiver = Receiver}, - Pid = self(), case gen_tcp:controlling_process(Socket, Receiver) of ok -> - ejabberd_receiver:become_controller(Receiver, Pid), + ejabberd_receiver:become_controller(Receiver, Owner), {ok, SocketData}; {error, _Reason} = Error -> gen_tcp:close(Socket), Error end; diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 627b5b58f..4598805c2 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -53,6 +53,7 @@ -include("ejabberd.hrl"). -include("ejabberd_commands.hrl"). -include("mod_roster.hrl"). +-include("mod_privacy.hrl"). -include("ejabberd_sm.hrl"). -include("xmpp.hrl"). @@ -1380,11 +1381,12 @@ privacy_set(Username, Host, QueryS) -> To = jid:make(Host), QueryEl = fxml_stream:parse_element(QueryS), SubEl = xmpp:decode(QueryEl), - IQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl]}, + IQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl], + from = From, to = To}, ejabberd_hooks:run_fold(privacy_iq_set, Host, {error, xmpp:err_feature_not_implemented()}, - [From, To, IQ]), + [IQ, #userlist{}]), ok. %%% diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl index b3bbff96e..d2b187d26 100644 --- a/src/mod_blocking.erl +++ b/src/mod_blocking.erl @@ -30,7 +30,7 @@ -protocol({xep, 191, '1.2'}). -export([start/2, stop/1, process_iq/1, - process_iq_set/2, process_iq_get/3, mod_opt_type/1, depends/2]). + process_iq_set/3, process_iq_get/3, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -85,10 +85,11 @@ process_iq_get(Acc, _, _) -> Acc. -spec process_iq_set({error, stanza_error()} | {result, xmpp_element() | undefined} | {result, xmpp_element() | undefined, userlist()}, - iq()) -> {error, stanza_error()} | - {result, xmpp_element() | undefined} | - {result, xmpp_element() | undefined, userlist()}. -process_iq_set(Acc, #iq{from = From, lang = Lang, sub_els = [SubEl]}) -> + iq(), userlist()) -> + {error, stanza_error()} | + {result, xmpp_element() | undefined} | + {result, xmpp_element() | undefined, userlist()}. +process_iq_set(Acc, #iq{from = From, lang = Lang, sub_els = [SubEl]}, _) -> #jid{luser = LUser, lserver = LServer} = From, case SubEl of #block{items = []} -> @@ -105,7 +106,7 @@ process_iq_set(Acc, #iq{from = From, lang = Lang, sub_els = [SubEl]}) -> _ -> Acc end; -process_iq_set(Acc, _) -> Acc. +process_iq_set(Acc, _, _) -> Acc. -spec list_to_blocklist_jids([listitem()], [ljid()]) -> [ljid()]. list_to_blocklist_jids([], JIDs) -> JIDs; diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl index 2f318deec..d4c8464f1 100644 --- a/src/mod_privacy.erl +++ b/src/mod_privacy.erl @@ -32,7 +32,7 @@ -behaviour(gen_mod). -export([start/2, stop/1, process_iq/1, export/1, import/1, - process_iq_set/2, process_iq_get/3, get_user_list/3, + process_iq_set/3, process_iq_get/3, get_user_list/3, check_packet/6, remove_user/2, encode_list_item/1, is_list_needdb/1, updated_list/3, item_to_xml/1, get_user_lists/2, import/3, @@ -103,6 +103,12 @@ process_iq(IQ) -> -spec process_iq_get({error, stanza_error()} | {result, xmpp_element() | undefined}, iq(), userlist()) -> {error, stanza_error()} | {result, xmpp_element() | undefined}. +process_iq_get(_, #iq{lang = Lang, + sub_els = [#privacy_query{default = Default, + active = Active}]}, + _) when Default /= undefined; Active /= undefined -> + Txt = <<"Only element is allowed in this query">>, + {error, xmpp:err_bad_request(Txt, Lang)}; process_iq_get(_, #iq{from = From, lang = Lang, sub_els = [#privacy_query{lists = Lists}]}, #userlist{name = Active}) -> @@ -205,7 +211,7 @@ encode_value(Type, Val) -> listitem_value(). decode_value(Type, Value) -> case Type of - jid -> jid:from_string(Value); + jid -> jid:tolower(jid:from_string(Value)); subscription -> case Value of <<"from">> -> from; @@ -213,35 +219,37 @@ decode_value(Type, Value) -> <<"both">> -> both; <<"none">> -> none end; - group -> Value; + group when Value /= <<"">> -> Value; undefined -> none end. -spec process_iq_set({error, stanza_error()} | {result, xmpp_element() | undefined} | {result, xmpp_element() | undefined, userlist()}, - iq()) -> {error, stanza_error()} | - {result, xmpp_element() | undefined} | - {result, xmpp_element() | undefined, userlist()}. + iq(), #userlist{}) -> + {error, stanza_error()} | + {result, xmpp_element() | undefined} | + {result, xmpp_element() | undefined, userlist()}. process_iq_set(_, #iq{from = From, lang = Lang, sub_els = [#privacy_query{default = Default, active = Active, - lists = Lists}]}) -> + lists = Lists}]}, + #userlist{} = UserList) -> #jid{luser = LUser, lserver = LServer} = From, case Lists of [#privacy_list{items = Items, name = ListName}] when Default == undefined, Active == undefined -> - process_lists_set(LUser, LServer, ListName, Items, Lang); + process_lists_set(LUser, LServer, ListName, Items, UserList, Lang); [] when Default == undefined, Active /= undefined -> process_active_set(LUser, LServer, Active, Lang); [] when Active == undefined, Default /= undefined -> process_default_set(LUser, LServer, Default, Lang); _ -> - Txt = <<"There should be exactly one element in this query: " - ", or ">>, + Txt = <<"The stanza MUST contain only one element, " + "one element, or one element">>, {error, xmpp:err_bad_request(Txt, Lang)} end; -process_iq_set(Acc, _) -> +process_iq_set(Acc, _, _) -> Acc. -spec process_default_set(binary(), binary(), none | binary(), @@ -286,13 +294,20 @@ set_privacy_list(#privacy{us = {_, LServer}} = Privacy) -> Mod:set_privacy_list(Privacy). -spec process_lists_set(binary(), binary(), binary(), [privacy_item()], - binary()) -> {error, stanza_error()} | {result, undefined}. -process_lists_set(LUser, LServer, Name, [], Lang) -> + #userlist{}, binary()) -> {error, stanza_error()} | + {result, undefined}. +process_lists_set(_LUser, _LServer, Name, [], #userlist{name = Name}, Lang) -> + Txt = <<"Cannot remove active list">>, + {error, xmpp:err_conflict(Txt, Lang)}; +process_lists_set(LUser, LServer, Name, [], _UserList, Lang) -> Mod = gen_mod:db_mod(LServer, ?MODULE), case Mod:remove_privacy_list(LUser, LServer, Name) of {atomic, conflict} -> Txt = <<"Cannot remove default list">>, {error, xmpp:err_conflict(Txt, Lang)}; + {atomic, not_found} -> + Txt = <<"No privacy list with this name found">>, + {error, xmpp:err_item_not_found(Txt, Lang)}; {atomic, ok} -> ejabberd_sm:route(jid:make(LUser, LServer, <<"">>), @@ -308,7 +323,7 @@ process_lists_set(LUser, LServer, Name, [], Lang) -> Txt = <<"Database failure">>, {error, xmpp:err_internal_server_error(Txt, Lang)} end; -process_lists_set(LUser, LServer, Name, Items, Lang) -> +process_lists_set(LUser, LServer, Name, Items, _UserList, Lang) -> case catch lists:map(fun decode_item/1, Items) of {error, Why} -> Txt = xmpp:format_error(Why), @@ -358,9 +373,7 @@ decode_item(#privacy_item{order = Order, action = Action, type = Type, value = Value}, - if MatchMessage and MatchIQ and MatchPresenceIn and MatchPresenceOut -> - ListItem#listitem{match_all = true}; - not (MatchMessage or MatchIQ or MatchPresenceIn or MatchPresenceOut) -> + if not (MatchMessage or MatchIQ or MatchPresenceIn or MatchPresenceOut) -> ListItem#listitem{match_all = true}; true -> ListItem#listitem{match_iq = MatchIQ, @@ -468,17 +481,11 @@ check_packet_aux([Item | List], PType, JID, Item, case is_ptype_match(Item, PType) of true -> - case Type of - none -> Action; - _ -> - case is_type_match(Type, Value, JID, Subscription, - Groups) - of - true -> Action; - false -> - check_packet_aux(List, PType, JID, Subscription, Groups) - end - end; + case is_type_match(Type, Value, JID, Subscription, Groups) of + true -> Action; + false -> + check_packet_aux(List, PType, JID, Subscription, Groups) + end; false -> check_packet_aux(List, PType, JID, Subscription, Groups) end. @@ -499,8 +506,10 @@ is_ptype_match(Item, PType) -> end end. --spec is_type_match(jid | subscription | group, listitem_value(), +-spec is_type_match(none | jid | subscription | group, listitem_value(), ljid(), none | both | from | to, [binary()]) -> boolean(). +is_type_match(none, _Value, _JID, _Subscription, _Groups) -> + true; is_type_match(Type, Value, JID, Subscription, Groups) -> case Type of jid -> diff --git a/src/mod_roster.erl b/src/mod_roster.erl index feebd3945..423fe9e0e 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -49,7 +49,7 @@ get_jid_info/4, encode_item/1, webadmin_page/3, webadmin_user/4, get_versioning_feature/2, roster_versioning_enabled/1, roster_version/2, - mod_opt_type/1, set_roster/1, depends/2]). + mod_opt_type/1, set_roster/1, del_roster/3, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -297,6 +297,13 @@ set_roster(#roster{us = {LUser, LServer}, jid = LJID} = Item) -> roster_subscribe_t(LUser, LServer, LJID, Item) end). +del_roster(LUser, LServer, LJID) -> + transaction( + LServer, + fun() -> + del_roster_t(LUser, LServer, LJID) + end). + encode_item(Item) -> #roster_item{jid = jid:make(Item#roster.jid), name = Item#roster.name, diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 1a5c89076..59936352b 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -22,11 +22,12 @@ stop_event_relay/1, put_event/2, get_event/1, bind/1, auth/1, auth/2, open_session/1, open_session/2, zlib/1, starttls/1, starttls/2, close_socket/1, init_stream/1, - auth_legacy/2, auth_legacy/3, tcp_connect/1, send_text/2]). + auth_legacy/2, auth_legacy/3, tcp_connect/1, send_text/2, + set_roster/3, del_roster/1]). -include("suite.hrl"). suite() -> - [{timetrap, {seconds, 30}}]. + [{timetrap, {seconds, 120}}]. init_per_suite(Config) -> NewConfig = init_config(Config), @@ -183,9 +184,18 @@ end_per_group(_GroupName, Config) -> set_opt(anonymous, false, Config). init_per_testcase(stop_ejabberd, Config) -> - open_session(bind(auth(connect(Config)))); + NewConfig = set_opt(resource, <<"">>, + set_opt(anonymous, true, Config)), + open_session(bind(auth(connect(NewConfig)))); init_per_testcase(TestCase, OrigConfig) -> - subscribe_to_events(OrigConfig), + Test = atom_to_list(TestCase), + IsMaster = lists:suffix("_master", Test), + IsSlave = lists:suffix("_slave", Test), + if IsMaster or IsSlave -> + subscribe_to_events(OrigConfig); + true -> + ok + end, TestGroup = proplists:get_value( name, ?config(tc_group_properties, OrigConfig)), Server = ?config(server, OrigConfig), @@ -199,9 +209,6 @@ init_per_testcase(TestCase, OrigConfig) -> end, MasterResource = ?config(master_resource, OrigConfig), SlaveResource = ?config(slave_resource, OrigConfig), - Test = atom_to_list(TestCase), - IsMaster = lists:suffix("_master", Test), - IsSlave = lists:suffix("_slave", Test), Mode = if IsSlave -> slave; IsMaster -> master; true -> single @@ -389,12 +396,12 @@ db_tests(riak) -> last, roster_get, private, - privacy, - blocking, + privacy_tests:single_cases(), vcard, muc_tests:single_cases(), test_unregister]}, muc_tests:master_slave_cases(), + privacy_tests:master_slave_cases(), {test_roster_subscribe, [parallel], [roster_subscribe_master, roster_subscribe_slave]}, @@ -420,13 +427,13 @@ db_tests(DB) when DB == mnesia; DB == redis -> roster_get, roster_ver, private, - privacy, - blocking, + privacy_tests:single_cases(), vcard, pubsub_single_tests(), muc_tests:single_cases(), test_unregister]}, muc_tests:master_slave_cases(), + privacy_tests:master_slave_cases(), pubsub_multiple_tests(), {test_mix, [parallel], [mix_master, mix_slave]}, @@ -466,13 +473,13 @@ db_tests(_) -> roster_get, roster_ver, private, - privacy, - blocking, + privacy_tests:single_cases(), vcard, pubsub_single_tests(), muc_tests:single_cases(), test_unregister]}, muc_tests:master_slave_cases(), + privacy_tests:master_slave_cases(), pubsub_multiple_tests(), {test_mix, [parallel], [mix_master, mix_slave]}, @@ -797,7 +804,7 @@ s2s_ping(Config) -> To = jid:make(?MNESIA_VHOST), ID = randoms:get_string(), ejabberd_s2s:route(From, To, #iq{id = ID, type = get, sub_els = [#ping{}]}), - ?recv1(#iq{type = result, id = ID, sub_els = []}), + #iq{type = result, id = ID, sub_els = []} = recv_iq(Config), disconnect(Config). auth_md5(Config) -> @@ -872,7 +879,7 @@ roster_ver(Config) -> %% Attempting to subscribe to server's JID send(Config, #presence{type = subscribe, to = server_jid(Config)}), %% Receive a single roster push with the new "ver" - #iq{type = set, sub_els = [#roster_query{ver = Ver2}]} = recv(Config), + #iq{type = set, sub_els = [#roster_query{ver = Ver2}]} = recv_iq(Config), %% Requesting roster with the previous "ver". Should receive Ver2 again #iq{type = result, sub_els = [#roster_query{ver = Ver2}]} = send_recv(Config, #iq{type = get, @@ -901,9 +908,8 @@ unsupported_query(Config) -> disconnect(Config). presence(Config) -> - send(Config, #presence{}), JID = my_jid(Config), - ?recv1(#presence{from = JID, to = JID}), + #presence{from = JID, to = JID} = send_recv(Config, #presence{}), disconnect(Config). presence_broadcast(Config) -> @@ -927,11 +933,11 @@ presence_broadcast(Config) -> %% 1) disco#info iq request for CAPS %% 2) welcome message %% 3) presence broadcast - {IQ, _, _} = ?recv3(#iq{type = get, - from = ServerJID, - sub_els = [#disco_info{node = Node}]}, - #message{type = normal}, - #presence{from = JID, to = JID}), + IQ = #iq{type = get, + from = ServerJID, + sub_els = [#disco_info{node = Node}]} = recv_iq(Config), + #message{type = normal} = recv_message(Config), + #presence{from = JID, to = JID} = recv_presence(Config), send(Config, #iq{type = result, id = IQ#iq.id, to = ServerJID, sub_els = [Info]}), %% We're trying to read our feature from ejabberd database @@ -1045,7 +1051,7 @@ sm_resume(Config) -> ejabberd_router:route(ServerJID, MyJID, Msg), send(Config, #sm_resume{previd = ID, h = 0, xmlns = ?NS_STREAM_MGMT_3}), ?recv1(#sm_resumed{previd = ID, h = 3}), - ?recv1(#message{from = ServerJID, to = MyJID, body = [Txt]}), + #message{from = ServerJID, to = MyJID, body = [Txt]} = recv_message(Config), ?recv1(#sm_r{}), send(Config, #sm_a{h = 1, xmlns = ?NS_STREAM_MGMT_3}), %% Send another stanza to increment the server's 'h' for sm_resume_failed. @@ -1095,90 +1101,97 @@ last(Config) -> to = server_jid(Config)}), disconnect(Config). -privacy(Config) -> - true = is_feature_advertised(Config, ?NS_PRIVACY), - #iq{type = result, sub_els = [#privacy_query{}]} = - send_recv(Config, #iq{type = get, sub_els = [#privacy_query{}]}), - JID = <<"tybalt@example.com">>, - I1 = send(Config, - #iq{type = set, - sub_els = [#privacy_query{ - lists = [#privacy_list{ - name = <<"public">>, - items = - [#privacy_item{ - type = jid, - order = 3, - action = deny, - presence_in = true, - value = JID}]}]}]}), - {Push1, _} = - ?recv2( - #iq{type = set, - sub_els = [#privacy_query{ - lists = [#privacy_list{ - name = <<"public">>}]}]}, - #iq{type = result, id = I1, sub_els = []}), - send(Config, make_iq_result(Push1)), - #iq{type = result, sub_els = []} = - send_recv(Config, #iq{type = set, - sub_els = [#privacy_query{active = <<"public">>}]}), - #iq{type = result, sub_els = []} = - send_recv(Config, #iq{type = set, - sub_els = [#privacy_query{default = <<"public">>}]}), - #iq{type = result, - sub_els = [#privacy_query{default = <<"public">>, - active = <<"public">>, - lists = [#privacy_list{name = <<"public">>}]}]} = - send_recv(Config, #iq{type = get, sub_els = [#privacy_query{}]}), - #iq{type = result, sub_els = []} = - send_recv(Config, - #iq{type = set, sub_els = [#privacy_query{default = none}]}), - #iq{type = result, sub_els = []} = - send_recv(Config, #iq{type = set, sub_els = [#privacy_query{active = none}]}), - I2 = send(Config, #iq{type = set, - sub_els = [#privacy_query{ - lists = - [#privacy_list{ - name = <<"public">>}]}]}), - {Push2, _} = - ?recv2( - #iq{type = set, - sub_els = [#privacy_query{ - lists = [#privacy_list{ - name = <<"public">>}]}]}, - #iq{type = result, id = I2, sub_els = []}), - send(Config, make_iq_result(Push2)), - disconnect(Config). +privacy_feature_enabled(Config) -> + privacy_tests:feature_enabled(Config). +privacy_set_get_list(Config) -> + privacy_tests:set_get_list(Config). +privacy_get_list_non_existent(Config) -> + privacy_tests:get_list_non_existent(Config). +privacy_set_default(Config) -> + privacy_tests:set_default(Config). +privacy_del_default(Config) -> + privacy_tests:del_default(Config). +privacy_set_default_non_existent(Config) -> + privacy_tests:set_default_non_existent(Config). +privacy_set_active(Config) -> + privacy_tests:set_active(Config). +privacy_del_active(Config) -> + privacy_tests:del_active(Config). +privacy_set_active_non_existent(Config) -> + privacy_tests:set_active_non_existent(Config). +privacy_remove_list(Config) -> + privacy_tests:remove_list(Config). +privacy_remove_active_list(Config) -> + privacy_tests:remove_active_list(Config). +privacy_remove_default_list(Config) -> + privacy_tests:remove_default_list(Config). +privacy_remove_list_non_existent(Config) -> + privacy_tests:remove_list_non_existent(Config). +privacy_allow_local_server(Config) -> + privacy_tests:allow_local_server(Config). +privacy_malformed_iq_query(Config) -> + privacy_tests:malformed_iq_query(Config). +privacy_malformed_get(Config) -> + privacy_tests:malformed_get(Config). +privacy_malformed_set(Config) -> + privacy_tests:malformed_set(Config). +privacy_malformed_type_value(Config) -> + privacy_tests:malformed_type_value(Config). +privacy_set_get_block(Config) -> + privacy_tests:set_get_block(Config). -blocking(Config) -> - true = is_feature_advertised(Config, ?NS_BLOCKING), - JID = jid:make(<<"romeo">>, <<"montague.net">>, <<>>), - #iq{type = result, sub_els = [#block_list{}]} = - send_recv(Config, #iq{type = get, sub_els = [#block_list{}]}), - I1 = send(Config, #iq{type = set, - sub_els = [#block{items = [JID]}]}), - {Push1, Push2, _} = - ?recv3( - #iq{type = set, - sub_els = [#privacy_query{lists = [#privacy_list{}]}]}, - #iq{type = set, - sub_els = [#block{items = [JID]}]}, - #iq{type = result, id = I1, sub_els = []}), - send(Config, make_iq_result(Push1)), - send(Config, make_iq_result(Push2)), - I2 = send(Config, #iq{type = set, - sub_els = [#unblock{items = [JID]}]}), - {Push3, Push4, _} = - ?recv3( - #iq{type = set, - sub_els = [#privacy_query{lists = [#privacy_list{}]}]}, - #iq{type = set, - sub_els = [#unblock{items = [JID]}]}, - #iq{type = result, id = I2, sub_els = []}), - send(Config, make_iq_result(Push3)), - send(Config, make_iq_result(Push4)), - disconnect(Config). +privacy_deny_bare_jid_master(Config) -> + privacy_tests:deny_bare_jid_master(Config). +privacy_deny_bare_jid_slave(Config) -> + privacy_tests:deny_bare_jid_slave(Config). +privacy_deny_full_jid_master(Config) -> + privacy_tests:deny_full_jid_master(Config). +privacy_deny_full_jid_slave(Config) -> + privacy_tests:deny_full_jid_slave(Config). +privacy_deny_server_jid_master(Config) -> + privacy_tests:deny_server_jid_master(Config). +privacy_deny_server_jid_slave(Config) -> + privacy_tests:deny_server_jid_slave(Config). +privacy_deny_group_master(Config) -> + privacy_tests:deny_group_master(Config). +privacy_deny_group_slave(Config) -> + privacy_tests:deny_group_slave(Config). +privacy_deny_sub_both_master(Config) -> + privacy_tests:deny_sub_both_master(Config). +privacy_deny_sub_both_slave(Config) -> + privacy_tests:deny_sub_both_slave(Config). +privacy_deny_sub_from_master(Config) -> + privacy_tests:deny_sub_from_master(Config). +privacy_deny_sub_from_slave(Config) -> + privacy_tests:deny_sub_from_slave(Config). +privacy_deny_sub_to_master(Config) -> + privacy_tests:deny_sub_to_master(Config). +privacy_deny_sub_to_slave(Config) -> + privacy_tests:deny_sub_to_slave(Config). +privacy_deny_sub_none_master(Config) -> + privacy_tests:deny_sub_none_master(Config). +privacy_deny_sub_none_slave(Config) -> + privacy_tests:deny_sub_none_slave(Config). +privacy_deny_all_master(Config) -> + privacy_tests:deny_all_master(Config). +privacy_deny_all_slave(Config) -> + privacy_tests:deny_all_slave(Config). +privacy_deny_offline_master(Config) -> + privacy_tests:deny_offline_master(Config). +privacy_deny_offline_slave(Config) -> + privacy_tests:deny_offline_slave(Config). +privacy_block_master(Config) -> + privacy_tests:block_master(Config). +privacy_block_slave(Config) -> + privacy_tests:block_slave(Config). +privacy_unblock_master(Config) -> + privacy_tests:unblock_master(Config). +privacy_unblock_slave(Config) -> + privacy_tests:unblock_slave(Config). +privacy_unblock_all_master(Config) -> + privacy_tests:unblock_all_master(Config). +privacy_unblock_all_slave(Config) -> + privacy_tests:unblock_all_slave(Config). vcard(Config) -> true = is_feature_advertised(Config, ?NS_VCARD), @@ -1239,17 +1252,16 @@ vcard_xupdate_master(Config) -> MyJID = my_jid(Config), Peer = ?config(slave, Config), wait_for_slave(Config), - send(Config, #presence{}), - ?recv2(#presence{from = MyJID, type = available}, - #presence{from = Peer, type = available}), + #presence{from = MyJID, type = available} = send_recv(Config, #presence{}), + #presence{from = Peer, type = available} = recv_presence(Config), VCard = #vcard_temp{photo = #vcard_photo{type = <<"image/png">>, binval = Img}}, - I1 = send(Config, #iq{type = set, sub_els = [VCard]}), - ?recv2(#iq{type = result, sub_els = [], id = I1}, - #presence{from = MyJID, type = available, - sub_els = [#vcard_xupdate{hash = ImgHash}]}), - I2 = send(Config, #iq{type = set, sub_els = [#vcard_temp{}]}), - ?recv3(#iq{type = result, sub_els = [], id = I2}, - #presence{from = MyJID, type = available, + #iq{type = result, sub_els = []} = + send_recv(Config, #iq{type = set, sub_els = [VCard]}), + #presence{from = MyJID, type = available, + sub_els = [#vcard_xupdate{hash = ImgHash}]} = recv_presence(Config), + #iq{type = result, sub_els = []} = + send_recv(Config, #iq{type = set, sub_els = [#vcard_temp{}]}), + ?recv2(#presence{from = MyJID, type = available, sub_els = [#vcard_xupdate{hash = undefined}]}, #presence{from = Peer, type = unavailable}), disconnect(Config). @@ -1259,14 +1271,13 @@ vcard_xupdate_slave(Config) -> ImgHash = p1_sha:sha(Img), MyJID = my_jid(Config), Peer = ?config(master, Config), - send(Config, #presence{}), - ?recv1(#presence{from = MyJID, type = available}), + #presence{from = MyJID, type = available} = send_recv(Config, #presence{}), wait_for_master(Config), - ?recv1(#presence{from = Peer, type = available}), - ?recv1(#presence{from = Peer, type = available, - sub_els = [#vcard_xupdate{hash = ImgHash}]}), - ?recv1(#presence{from = Peer, type = available, - sub_els = [#vcard_xupdate{hash = undefined}]}), + #presence{from = Peer, type = available} = recv_presence(Config), + #presence{from = Peer, type = available, + sub_els = [#vcard_xupdate{hash = ImgHash}]} = recv_presence(Config), + #presence{from = Peer, type = available, + sub_els = [#vcard_xupdate{hash = undefined}]} = recv_presence(Config), disconnect(Config). stats(Config) -> @@ -1439,7 +1450,7 @@ pubsub_publish_slave(Config) -> sub_els = [#ps_event{ items = #ps_items{node = Node, - items = [Item]}}]} = recv(Config), + items = [Item]}}]} = recv_message(Config), put_event(Config, Item), disconnect(Config). @@ -1478,13 +1489,14 @@ pubsub_subscriptions_slave(Config) -> type = Type}}]}, #message{sub_els = [#ps_event{}]}); (Type) -> - ?recv1(#message{ - sub_els = - [#ps_event{ - subscription = #ps_subscription{ - node = Node, - jid = MyJID, - type = Type}}]}) + #message{ + sub_els = + [#ps_event{ + subscription = #ps_subscription{ + node = Node, + jid = MyJID, + type = Type}}]} = + recv_message(Config) end, [subscribed, unconfigured, pending, none]), disconnect(Config). @@ -1644,7 +1656,7 @@ pubsub_affiliations_slave(Config, disconnect) -> pubsub_authorize_master(Config) -> send(Config, #presence{}), - ?recv1(#presence{}), + #presence{} = recv_presence(Config), Peer = ?config(slave, Config), PJID = pubsub_jid(Config), NodeConfig = set_opts(default_node_config(Config), @@ -1652,7 +1664,7 @@ pubsub_authorize_master(Config) -> Node = ?config(pubsub_node, Config), Node = create_node(Config, Node, NodeConfig), wait_for_slave(Config), - #message{sub_els = [#xdata{fields = F1}]} = recv(Config), + #message{sub_els = [#xdata{fields = F1}]} = recv_message(Config), C1 = pubsub_subscribe_authorization:decode(F1), Node = proplists:get_value(node, C1), Peer = proplists:get_value(subscriber_jid, C1), @@ -1666,7 +1678,7 @@ pubsub_authorize_master(Config) -> %% We should not have any subscriptions [] = get_subscriptions(Config, Node), wait_for_slave(Config), - #message{sub_els = [#xdata{fields = F2}]} = recv(Config), + #message{sub_els = [#xdata{fields = F2}]} = recv_message(Config), C2 = pubsub_subscribe_authorization:decode(F2), Node = proplists:get_value(node, C2), Peer = proplists:get_value(subscriber_jid, C2), @@ -1687,19 +1699,21 @@ pubsub_authorize_slave(Config) -> wait_for_master(Config), #ps_subscription{type = pending} = subscribe_node(Config, Node), %% We're denied at first - ?recv1(#message{ - sub_els = - [#ps_event{ - subscription = #ps_subscription{type = none, - jid = MyJID}}]}), + #message{ + sub_els = + [#ps_event{ + subscription = #ps_subscription{type = none, + jid = MyJID}}]} = + recv_message(Config), wait_for_master(Config), #ps_subscription{type = pending} = subscribe_node(Config, Node), %% Now much better! - ?recv1(#message{ - sub_els = - [#ps_event{ - subscription = #ps_subscription{type = subscribed, - jid = MyJID}}]}), + #message{ + sub_els = + [#ps_event{ + subscription = #ps_subscription{type = subscribed, + jid = MyJID}}]} = + recv_message(Config), wait_for_master(Config), disconnect(Config). @@ -1961,46 +1975,48 @@ mix_master(Config) -> Nodes = [?NS_MIX_NODES_MESSAGES, ?NS_MIX_NODES_PRESENCE, ?NS_MIX_NODES_PARTICIPANTS, ?NS_MIX_NODES_SUBJECT, ?NS_MIX_NODES_CONFIG], - I0 = send(Config, #iq{type = set, to = Room, - sub_els = [#mix_join{subscribe = Nodes}]}), - {_, #message{sub_els = - [#ps_event{ - items = #ps_items{ - node = ?NS_MIX_NODES_PARTICIPANTS, - items = [#ps_item{ - id = ParticipantID, - xml_els = [PXML]}]}}]}} = - ?recv2(#iq{type = result, id = I0, - sub_els = [#mix_join{subscribe = Nodes, jid = MyBareJID}]}, - #message{from = Room}), + #iq{type = result, + sub_els = [#mix_join{subscribe = Nodes, jid = MyBareJID}]} = + send_recv(Config, #iq{type = set, to = Room, + sub_els = [#mix_join{subscribe = Nodes}]}), + #message{from = Room, + sub_els = + [#ps_event{ + items = #ps_items{ + node = ?NS_MIX_NODES_PARTICIPANTS, + items = [#ps_item{ + id = ParticipantID, + xml_els = [PXML]}]}}]} = + recv_message(Config), #mix_participant{jid = MyBareJID} = xmpp:decode(PXML), %% Coming online PresenceID = randoms:get_string(), Presence = xmpp:encode(#presence{}), - I1 = send( - Config, - #iq{type = set, to = Room, - sub_els = - [#pubsub{ - publish = #ps_publish{ - node = ?NS_MIX_NODES_PRESENCE, - items = [#ps_item{ - id = PresenceID, - xml_els = [Presence]}]}}]}), - ?recv2(#iq{type = result, id = I1, - sub_els = - [#pubsub{ - publish = #ps_publish{ - node = ?NS_MIX_NODES_PRESENCE, - items = [#ps_item{id = PresenceID}]}}]}, - #message{from = Room, - sub_els = - [#ps_event{ - items = #ps_items{ - node = ?NS_MIX_NODES_PRESENCE, - items = [#ps_item{ - id = PresenceID, - xml_els = [Presence]}]}}]}), + #iq{type = result, + sub_els = + [#pubsub{ + publish = #ps_publish{ + node = ?NS_MIX_NODES_PRESENCE, + items = [#ps_item{id = PresenceID}]}}]} = + send_recv( + Config, + #iq{type = set, to = Room, + sub_els = + [#pubsub{ + publish = #ps_publish{ + node = ?NS_MIX_NODES_PRESENCE, + items = [#ps_item{ + id = PresenceID, + xml_els = [Presence]}]}}]}), + #message{from = Room, + sub_els = + [#ps_event{ + items = #ps_items{ + node = ?NS_MIX_NODES_PRESENCE, + items = [#ps_item{ + id = PresenceID, + xml_els = [Presence]}]}}]} = + recv_message(Config), %% Coming offline send(Config, #presence{type = unavailable, to = Room}), %% Receiving presence retract event @@ -2008,101 +2024,109 @@ mix_master(Config) -> sub_els = [#ps_event{ items = #ps_items{ node = ?NS_MIX_NODES_PRESENCE, - retract = PresenceID}}]} = recv(Config), + retract = PresenceID}}]} = + recv_message(Config), %% Leaving - I2 = send(Config, #iq{type = set, to = Room, sub_els = [#mix_leave{}]}), - ?recv2(#iq{type = result, id = I2, sub_els = []}, - #message{from = Room, - sub_els = - [#ps_event{ - items = #ps_items{ - node = ?NS_MIX_NODES_PARTICIPANTS, - retract = ParticipantID}}]}), + #iq{type = result, sub_els = []} = + send_recv(Config, #iq{type = set, to = Room, sub_els = [#mix_leave{}]}), + #message{from = Room, + sub_els = + [#ps_event{ + items = #ps_items{ + node = ?NS_MIX_NODES_PARTICIPANTS, + retract = ParticipantID}}]} = + recv_message(Config), + put_event(Config, disconnect), disconnect(Config). mix_slave(Config) -> + disconnect = get_event(Config), disconnect(Config). roster_subscribe_master(Config) -> - send(Config, #presence{}), - ?recv1(#presence{}), + #presence{} = send_recv(Config, #presence{}), wait_for_slave(Config), - Peer = ?config(slave, Config), + Peer = ?config(peer, Config), LPeer = jid:remove_resource(Peer), send(Config, #presence{type = subscribe, to = LPeer}), - Push1 = ?recv1(#iq{type = set, + Push1 = #iq{type = set, sub_els = [#roster_query{items = [#roster_item{ - ask = subscribe, - subscription = none, - jid = LPeer}]}]}), + ask = subscribe, + subscription = none, + jid = LPeer}]}]} = + recv_iq(Config), send(Config, make_iq_result(Push1)), - {Push2, _} = ?recv2( - #iq{type = set, - sub_els = [#roster_query{items = [#roster_item{ - subscription = to, - jid = LPeer}]}]}, - #presence{type = subscribed, from = LPeer}), + #presence{type = subscribed, from = LPeer} = recv_presence(Config), + Push2 = #iq{type = set, + sub_els = [#roster_query{items = [#roster_item{ + subscription = to, + jid = LPeer}]}]} = + recv_iq(Config), send(Config, make_iq_result(Push2)), - ?recv1(#presence{type = available, from = Peer}), + #presence{type = available, from = Peer} = recv_presence(Config), %% BUG: ejabberd sends previous push again. Is it ok? - Push3 = ?recv1(#iq{type = set, + Push3 = #iq{type = set, sub_els = [#roster_query{items = [#roster_item{ - subscription = to, - jid = LPeer}]}]}), + subscription = to, + jid = LPeer}]}]} = + recv_iq(Config), send(Config, make_iq_result(Push3)), - ?recv1(#presence{type = subscribe, from = LPeer}), + #presence{type = subscribe, from = LPeer} = recv_presence(Config), send(Config, #presence{type = subscribed, to = LPeer}), - Push4 = ?recv1(#iq{type = set, + Push4 = #iq{type = set, sub_els = [#roster_query{items = [#roster_item{ - subscription = both, - jid = LPeer}]}]}), + subscription = both, + jid = LPeer}]}]} = + recv_iq(Config), send(Config, make_iq_result(Push4)), %% Move into a group Groups = [<<"A">>, <<"B">>], Item = #roster_item{jid = LPeer, groups = Groups}, - I1 = send(Config, #iq{type = set, sub_els = [#roster_query{items = [Item]}]}), - {Push5, _} = ?recv2( - #iq{type = set, - sub_els = - [#roster_query{items = [#roster_item{ - jid = LPeer, - subscription = both}]}]}, - #iq{type = result, id = I1, sub_els = []}), + #iq{type = result, sub_els = []} = + send_recv(Config, + #iq{type = set, sub_els = [#roster_query{items = [Item]}]}), + Push5 = #iq{type = set, + sub_els = + [#roster_query{items = [#roster_item{ + jid = LPeer, + subscription = both}]}]} = + recv_iq(Config), send(Config, make_iq_result(Push5)), #iq{sub_els = [#roster_query{items = [#roster_item{groups = G1}]}]} = Push5, Groups = lists:sort(G1), wait_for_slave(Config), - ?recv1(#presence{type = unavailable, from = Peer}), + #presence{type = unavailable, from = Peer} = recv_presence(Config), disconnect(Config). roster_subscribe_slave(Config) -> - send(Config, #presence{}), - ?recv1(#presence{}), + #presence{} = send_recv(Config, #presence{}), wait_for_master(Config), Peer = ?config(master, Config), LPeer = jid:remove_resource(Peer), - ?recv1(#presence{type = subscribe, from = LPeer}), + #presence{type = subscribe, from = LPeer} = recv_presence(Config), send(Config, #presence{type = subscribed, to = LPeer}), - Push1 = ?recv1(#iq{type = set, + Push1 = #iq{type = set, sub_els = [#roster_query{items = [#roster_item{ - subscription = from, - jid = LPeer}]}]}), + subscription = from, + jid = LPeer}]}]} = + recv_iq(Config), send(Config, make_iq_result(Push1)), send(Config, #presence{type = subscribe, to = LPeer}), - Push2 = ?recv1(#iq{type = set, + Push2 = #iq{type = set, sub_els = [#roster_query{items = [#roster_item{ - ask = subscribe, - subscription = from, - jid = LPeer}]}]}), + ask = subscribe, + subscription = from, + jid = LPeer}]}]} = + recv_iq(Config), send(Config, make_iq_result(Push2)), - {Push3, _} = ?recv2( - #iq{type = set, - sub_els = [#roster_query{items = [#roster_item{ - subscription = both, - jid = LPeer}]}]}, - #presence{type = subscribed, from = LPeer}), + #presence{type = subscribed, from = LPeer} = recv_presence(Config), + Push3 = #iq{type = set, + sub_els = [#roster_query{items = [#roster_item{ + subscription = both, + jid = LPeer}]}]} = + recv_iq(Config), send(Config, make_iq_result(Push3)), - ?recv1(#presence{type = available, from = Peer}), + #presence{type = available, from = Peer} = recv_presence(Config), wait_for_master(Config), disconnect(Config). @@ -2112,9 +2136,8 @@ roster_remove_master(Config) -> LPeer = jid:remove_resource(Peer), Groups = [<<"A">>, <<"B">>], wait_for_slave(Config), - send(Config, #presence{}), - ?recv2(#presence{from = MyJID, type = available}, - #presence{from = Peer, type = available}), + #presence{from = MyJID, type = available} = send_recv(Config, #presence{}), + #presence{from = Peer, type = available} = recv_presence(Config), %% The peer removed us from its roster. {Push1, Push2, _, _, _} = ?recv5( @@ -2144,21 +2167,21 @@ roster_remove_slave(Config) -> MyJID = my_jid(Config), Peer = ?config(master, Config), LPeer = jid:remove_resource(Peer), - send(Config, #presence{}), - ?recv1(#presence{from = MyJID, type = available}), + #presence{from = MyJID, type = available} = send_recv(Config, #presence{}), wait_for_master(Config), - ?recv1(#presence{from = Peer, type = available}), + #presence{from = Peer, type = available} = recv_presence(Config), %% Remove the peer from roster. Item = #roster_item{jid = LPeer, subscription = remove}, - I = send(Config, #iq{type = set, sub_els = [#roster_query{items = [Item]}]}), - {Push, _, _} = ?recv3( - #iq{type = set, - sub_els = - [#roster_query{items = [#roster_item{ - jid = LPeer, - subscription = remove}]}]}, - #iq{type = result, id = I, sub_els = []}, - #presence{type = unavailable, from = Peer}), + #iq{type = result, sub_els = []} = + send_recv(Config, #iq{type = set, + sub_els = [#roster_query{items = [Item]}]}), + Push = #iq{type = set, + sub_els = + [#roster_query{items = [#roster_item{ + jid = LPeer, + subscription = remove}]}]} = + recv_iq(Config), + #presence{type = unavailable, from = Peer} = recv_presence(Config), send(Config, make_iq_result(Push)), disconnect(Config). @@ -2168,7 +2191,7 @@ proxy65_master(Config) -> Peer = ?config(slave, Config), wait_for_slave(Config), send(Config, #presence{}), - ?recv1(#presence{from = MyJID, type = available}), + #presence{from = MyJID, type = available} = recv_presence(Config), true = is_feature_advertised(Config, ?NS_BYTESTREAMS, Proxy), #iq{type = result, sub_els = [#bytestreams{hosts = [StreamHost]}]} = send_recv( @@ -2191,7 +2214,7 @@ proxy65_slave(Config) -> MyJID = my_jid(Config), Peer = ?config(master, Config), send(Config, #presence{}), - ?recv1(#presence{from = MyJID, type = available}), + #presence{from = MyJID, type = available} = recv_presence(Config), wait_for_master(Config), {StreamHost, SID, Data} = get_event(Config), Socks5 = socks5_connect(StreamHost, {SID, Peer, MyJID}), @@ -2206,11 +2229,11 @@ send_messages_to_room(Config, Range) -> lists:foreach( fun(N) -> Text = #text{data = integer_to_binary(N)}, - I = send(Config, #message{to = Room, body = [Text], - type = groupchat}), - ?recv1(#message{from = MyNickJID, id = I, - type = groupchat, - body = [Text]}) + #message{from = MyNickJID, id = I, + type = groupchat, + body = [Text]} = + send_recv(Config, #message{to = Room, body = [Text], + type = groupchat}) end, Range). retrieve_messages_from_room_via_mam(Config, Range) -> @@ -2225,34 +2248,33 @@ retrieve_messages_from_room_via_mam(Config, Range) -> lists:foreach( fun(N) -> Text = #text{data = integer_to_binary(N)}, - ?recv1(#message{ - to = MyJID, from = Room, - sub_els = - [#mam_result{ - xmlns = ?NS_MAM_1, - queryid = QID, - sub_els = - [#forwarded{ - delay = #delay{}, - sub_els = [#message{ - from = MyNickJID, - type = groupchat, - body = [Text]}]}]}]}) + #message{ + to = MyJID, from = Room, + sub_els = + [#mam_result{ + xmlns = ?NS_MAM_1, + queryid = QID, + sub_els = + [#forwarded{ + delay = #delay{}, + sub_els = [#message{ + from = MyNickJID, + type = groupchat, + body = [Text]}]}]}]} = + recv_message(Config) end, Range), - ?recv1(#iq{from = Room, id = I, type = result, - sub_els = [#mam_fin{xmlns = ?NS_MAM_1, - id = QID, - rsm = #rsm_set{count = Count}, - complete = true}]}). + #iq{from = Room, id = I, type = result, + sub_els = [#mam_fin{xmlns = ?NS_MAM_1, + id = QID, + rsm = #rsm_set{count = Count}, + complete = true}]} = recv_iq(Config). muc_mam_master(Config) -> MyNick = ?config(master_nick, Config), Room = muc_room_jid(Config), MyNickJID = jid:replace_resource(Room, MyNick), %% Joining - send(Config, #presence{to = MyNickJID, sub_els = [#muc{}]}), - %% Receive self-presence - ?recv1(#presence{from = MyNickJID}), + ok = muc_tests:muc_join_new(Config), %% MAM feature should not be advertised at this point, %% because MAM is not enabled so far false = is_feature_advertised(Config, ?NS_MAM_1, Room), @@ -2274,20 +2296,22 @@ muc_mam_master(Config) -> [] end, RoomCfg#xdata.fields), NewRoomCfg = #xdata{type = submit, fields = NewFields}, - I1 = send(Config, #iq{type = set, to = Room, - sub_els = [#muc_owner{config = NewRoomCfg}]}), - ?recv2(#iq{type = result, id = I1}, - #message{from = Room, type = groupchat, - sub_els = [#muc_user{status_codes = [104]}]}), + #iq{type = result, sub_els = []} = + send_recv(Config, #iq{type = set, to = Room, + sub_els = [#muc_owner{config = NewRoomCfg}]}), + #message{from = Room, type = groupchat, + sub_els = [#muc_user{status_codes = [104]}]} = recv_message(Config), %% Check if MAM has been enabled true = is_feature_advertised(Config, ?NS_MAM_1, Room), %% We now sending some messages again send_messages_to_room(Config, lists:seq(1, 5)), %% And retrieve them via MAM again. retrieve_messages_from_room_via_mam(Config, lists:seq(1, 5)), + put_event(Config, disconnect), disconnect(Config). muc_mam_slave(Config) -> + disconnect = get_event(Config), disconnect(Config). %% OK, I know this is retarded, but I didn't find a better way to @@ -2441,12 +2465,11 @@ announce_master(Config) -> ServerJID = server_jid(Config), MotdJID = jid:replace_resource(ServerJID, <<"announce/motd">>), MotdText = #text{data = <<"motd">>}, - send(Config, #presence{}), - ?recv1(#presence{from = MyJID}), + #presence{from = MyJID} = send_recv(Config, #presence{}), %% Set message of the day send(Config, #message{to = MotdJID, body = [MotdText]}), %% Receive this message back - ?recv1(#message{from = ServerJID, body = [MotdText]}), + #message{from = ServerJID, body = [MotdText]} = recv_message(Config), disconnect(Config). announce_slave(Config) -> @@ -2454,9 +2477,8 @@ announce_slave(Config) -> ServerJID = server_jid(Config), MotdDelJID = jid:replace_resource(ServerJID, <<"announce/motd/delete">>), MotdText = #text{data = <<"motd">>}, - send(Config, #presence{}), - ?recv2(#presence{from = MyJID}, - #message{from = ServerJID, body = [MotdText]}), + #presence{from = MyJID} = send_recv(Config, #presence{}), + #message{from = ServerJID, body = [MotdText]} = recv_message(Config), %% Delete message of the day send(Config, #message{to = MotdDelJID}), disconnect(Config). @@ -2520,39 +2542,39 @@ flex_offline_slave(Config) -> end, DiscoItems)), %% Since headers are received we can send initial presence without a risk %% of getting offline messages flood - send(Config, #presence{}), - ?recv1(#presence{from = MyJID}), + #presence{from = MyJID} = send_recv(Config, #presence{}), %% Check full fetch - I0 = send(Config, #iq{type = get, sub_els = [#offline{fetch = true}]}), + #iq{type = result, sub_els = []} = + send_recv(Config, #iq{type = get, sub_els = [#offline{fetch = true}]}), lists:foreach( fun({I, N}) -> Text = integer_to_binary(I), - #message{body = Body, sub_els = SubEls} = recv(Config), + #message{body = Body, sub_els = SubEls} = recv_message(Config), [#text{data = Text}] = Body, #offline{items = [#offline_item{node = N}]} = lists:keyfind(offline, 1, SubEls), #delay{} = lists:keyfind(delay, 1, SubEls) end, lists:zip(lists:seq(1, 5), Nodes)), - ?recv1(#iq{type = result, id = I0, sub_els = []}), %% Fetch 2nd and 4th message - I1 = send(Config, - #iq{type = get, - sub_els = [#offline{ - items = [#offline_item{ - action = view, - node = lists:nth(2, Nodes)}, - #offline_item{ - action = view, - node = lists:nth(4, Nodes)}]}]}), + #iq{type = result, sub_els = []} = + send_recv( + Config, + #iq{type = get, + sub_els = [#offline{ + items = [#offline_item{ + action = view, + node = lists:nth(2, Nodes)}, + #offline_item{ + action = view, + node = lists:nth(4, Nodes)}]}]}), lists:foreach( fun({I, N}) -> Text = integer_to_binary(I), #message{body = [#text{data = Text}], - sub_els = SubEls} = recv(Config), + sub_els = SubEls} = recv_message(Config), #offline{items = [#offline_item{node = N}]} = lists:keyfind(offline, 1, SubEls) end, lists:zip([2, 4], [lists:nth(2, Nodes), lists:nth(4, Nodes)])), - ?recv1(#iq{type = result, id = I1, sub_els = []}), %% Delete 2nd and 4th message #iq{type = result, sub_els = []} = send_recv( @@ -2601,12 +2623,12 @@ offline_master(Config) -> offline_slave(Config) -> Peer = ?config(master, Config), - send(Config, #presence{}), - {_, #message{sub_els = SubEls}} = - ?recv2(#presence{}, - #message{from = Peer, - body = [#text{data = <<"body">>}], - subject = [#text{data = <<"subject">>}]}), + #presence{} = send_recv(Config, #presence{}), + #message{sub_els = SubEls, + from = Peer, + body = [#text{data = <<"body">>}], + subject = [#text{data = <<"subject">>}]} = + recv_message(Config), true = lists:keymember(delay, 1, SubEls), disconnect(Config). @@ -2616,10 +2638,9 @@ carbons_master(Config) -> Peer = ?config(slave, Config), Txt = #text{data = <<"body">>}, true = is_feature_advertised(Config, ?NS_CARBONS_2), - send(Config, #presence{priority = 10}), - ?recv1(#presence{from = MyJID}), + #presence{from = MyJID} = send_recv(Config, #presence{priority = 10}), wait_for_slave(Config), - ?recv1(#presence{from = Peer}), + #presence{from = Peer} = recv_presence(Config), %% Enable carbons #iq{type = result, sub_els = []} = send_recv(Config, @@ -2670,8 +2691,8 @@ carbons_slave(Config) -> Peer = ?config(master, Config), Txt = #text{data = <<"body">>}, wait_for_master(Config), - send(Config, #presence{priority = 5}), - ?recv2(#presence{from = MyJID}, #presence{from = Peer}), + #presence{from = MyJID} = send_recv(Config, #presence{priority = 5}), + #presence{from = Peer} = recv_presence(Config), %% Enable carbons #iq{type = result, sub_els = []} = send_recv(Config, @@ -2722,7 +2743,7 @@ carbons_slave(Config) -> sub_els = [#carbons_disable{}]}), wait_for_master(Config), %% Now we should receive nothing but presence unavailable from the peer - ?recv1(#presence{from = Peer, type = unavailable}), + #presence{from = Peer, type = unavailable} = recv_presence(Config), disconnect(Config). mam_old_master(Config) -> @@ -2736,10 +2757,9 @@ mam_master(Config, NS) -> MyJID = my_jid(Config), BareMyJID = jid:remove_resource(MyJID), Peer = ?config(slave, Config), - send(Config, #presence{}), - ?recv1(#presence{}), + #presence{} = send_recv(Config, #presence{}), wait_for_slave(Config), - ?recv1(#presence{from = Peer}), + #presence{from = Peer} = recv_presence(Config), #iq{type = result, sub_els = [#mam_prefs{xmlns = NS, default = roster}]} = send_recv(Config, #iq{type = set, @@ -2747,18 +2767,18 @@ mam_master(Config, NS) -> default = roster, never = [MyJID]}]}), if NS == ?NS_MAM_TMP -> - FakeArchived = #mam_archived{id = randoms:get_string(), - by = server_jid(Config)}, - send(Config, #message{to = MyJID, - sub_els = [FakeArchived], - body = [#text{data = <<"a">>}]}), - send(Config, #message{to = BareMyJID, - sub_els = [FakeArchived], - body = [#text{data = <<"b">>}]}), %% NOTE: The server should strip fake archived tags, %% i.e. the sub_els received should be []. - ?recv2(#message{body = [#text{data = <<"a">>}], sub_els = []}, - #message{body = [#text{data = <<"b">>}], sub_els = []}); + FakeArchived = #mam_archived{id = randoms:get_string(), + by = server_jid(Config)}, + #message{body = [#text{data = <<"a">>}], sub_els = []} = + send_recv(Config, #message{to = MyJID, + sub_els = [FakeArchived], + body = [#text{data = <<"a">>}]}), + #message{body = [#text{data = <<"b">>}], sub_els = []} = + send_recv(Config, #message{to = BareMyJID, + sub_els = [FakeArchived], + body = [#text{data = <<"b">>}]}); true -> ok end, @@ -2766,10 +2786,9 @@ mam_master(Config, NS) -> lists:foreach( fun(N) -> Text = #text{data = integer_to_binary(N)}, - send(Config, - #message{to = Peer, body = [Text]}) + send(Config, #message{to = Peer, body = [Text]}) end, lists:seq(1, 5)), - ?recv1(#presence{type = unavailable, from = Peer}), + #presence{type = unavailable, from = Peer} = recv_presence(Config), mam_query_all(Config, NS), mam_query_with(Config, Peer, NS), %% mam_query_with(Config, jid:remove_resource(Peer)), @@ -2791,8 +2810,8 @@ mam_slave(Config, NS) -> MyJID = my_jid(Config), ServerJID = server_jid(Config), wait_for_master(Config), - send(Config, #presence{}), - ?recv2(#presence{from = MyJID}, #presence{from = Peer}), + #presence{from = MyJID} = send_recv(Config, #presence{}), + #presence{from = Peer} = recv_presence(Config), #iq{type = result, sub_els = [#mam_prefs{xmlns = NS, default = always}]} = send_recv(Config, #iq{type = set, @@ -2801,7 +2820,7 @@ mam_slave(Config, NS) -> lists:foreach( fun(N) -> Text = #text{data = integer_to_binary(N)}, - Msg = ?recv1(#message{from = Peer, body = [Text]}), + Msg = #message{from = Peer, body = [Text]} = recv_message(Config), #mam_archived{by = ServerJID} = xmpp:get_subtag(Msg, #mam_archived{}), #stanza_id{by = ServerJID} = @@ -2828,7 +2847,7 @@ mam_query_all(Config, NS) -> lists:foreach( fun(N) -> Text = #text{data = integer_to_binary(N)}, - ?recv1(#message{to = MyJID, + #message{to = MyJID, sub_els = [#mam_result{ queryid = QID, @@ -2838,13 +2857,15 @@ mam_query_all(Config, NS) -> sub_els = [#message{ from = MyJID, to = Peer, - body = [Text]}]}]}]}) + body = [Text]}]}]}]} = + recv_message(Config) end, Iter), if NS == ?NS_MAM_TMP -> - ?recv1(#iq{type = result, id = I, - sub_els = [#mam_query{xmlns = NS, id = QID}]}); + #iq{type = result, id = I, + sub_els = [#mam_query{xmlns = NS, id = QID}]} = recv_iq(Config); true -> - ?recv1(#message{sub_els = [#mam_fin{complete = true, id = QID}]}) + #message{sub_els = [#mam_fin{complete = true, id = QID}]} = + recv_message(Config) end. mam_query_with(Config, JID, NS) -> @@ -2866,7 +2887,7 @@ mam_query_with(Config, JID, NS) -> lists:foreach( fun(N) -> Text = #text{data = integer_to_binary(N)}, - ?recv1(#message{to = MyJID, + #message{to = MyJID, sub_els = [#mam_result{ sub_els = @@ -2875,17 +2896,19 @@ mam_query_with(Config, JID, NS) -> sub_els = [#message{ from = MyJID, to = Peer, - body = [Text]}]}]}]}) + body = [Text]}]}]}]} = + recv_message(Config) end, Iter), if NS == ?NS_MAM_TMP -> - ?recv1(#iq{type = result, id = I, - sub_els = [#mam_query{xmlns = NS}]}); + #iq{type = result, id = I, + sub_els = [#mam_query{xmlns = NS}]} = recv_iq(Config); true -> - ?recv1(#message{sub_els = [#mam_fin{complete = true}]}) + #message{sub_els = [#mam_fin{complete = true}]} = + recv_message(Config) end. maybe_recv_iq_result(Config, ?NS_MAM_0, I1) -> - ?recv1(#iq{type = result, id = I1}); + #iq{type = result, id = I1} = recv_iq(Config); maybe_recv_iq_result(_, _, _) -> ok. @@ -2904,7 +2927,7 @@ mam_query_rsm(Config, NS) -> lists:foreach( fun(N) -> Text = #text{data = integer_to_binary(N)}, - ?recv1(#message{to = MyJID, + #message{to = MyJID, sub_els = [#mam_result{ xmlns = NS, @@ -2914,20 +2937,21 @@ mam_query_rsm(Config, NS) -> sub_els = [#message{ from = MyJID, to = Peer, - body = [Text]}]}]}]}) + body = [Text]}]}]}]} = + recv_message(Config) end, lists:seq(1, 3)), if NS == ?NS_MAM_TMP -> #iq{type = result, id = I1, sub_els = [#mam_query{xmlns = NS, rsm = #rsm_set{last = Last, count = 5}}]} = - recv(Config); + recv_iq(Config); true -> #message{sub_els = [#mam_fin{ complete = false, rsm = #rsm_set{last = Last, count = 10}}]} = - recv(Config) + recv_message(Config) end, %% Get the next items starting from the `Last`. %% Limit the response to 2 items. @@ -2940,7 +2964,7 @@ mam_query_rsm(Config, NS) -> lists:foreach( fun(N) -> Text = #text{data = integer_to_binary(N)}, - ?recv1(#message{to = MyJID, + #message{to = MyJID, sub_els = [#mam_result{ xmlns = NS, @@ -2950,7 +2974,8 @@ mam_query_rsm(Config, NS) -> sub_els = [#message{ from = MyJID, to = Peer, - body = [Text]}]}]}]}) + body = [Text]}]}]}]} = + recv_message(Config) end, lists:seq(4, 5)), if NS == ?NS_MAM_TMP -> #iq{type = result, id = I2, @@ -2959,7 +2984,7 @@ mam_query_rsm(Config, NS) -> rsm = #rsm_set{ count = 5, first = #rsm_first{data = First}}}]} = - recv(Config); + recv_iq(Config); true -> #message{ sub_els = [#mam_fin{ @@ -2967,7 +2992,7 @@ mam_query_rsm(Config, NS) -> rsm = #rsm_set{ count = 10, first = #rsm_first{data = First}}}]} = - recv(Config) + recv_message(Config) end, %% Paging back. Should receive 3 elements: 1, 2, 3. I3 = send(Config, @@ -2979,7 +3004,7 @@ mam_query_rsm(Config, NS) -> lists:foreach( fun(N) -> Text = #text{data = integer_to_binary(N)}, - ?recv1(#message{to = MyJID, + #message{to = MyJID, sub_els = [#mam_result{ xmlns = NS, @@ -2989,15 +3014,18 @@ mam_query_rsm(Config, NS) -> sub_els = [#message{ from = MyJID, to = Peer, - body = [Text]}]}]}]}) + body = [Text]}]}]}]} = + recv_message(Config) end, lists:seq(1, 3)), if NS == ?NS_MAM_TMP -> - ?recv1(#iq{type = result, id = I3, - sub_els = [#mam_query{xmlns = NS, rsm = #rsm_set{count = 5}}]}); + #iq{type = result, id = I3, + sub_els = [#mam_query{xmlns = NS, rsm = #rsm_set{count = 5}}]} = + recv_iq(Config); true -> - ?recv1(#message{ - sub_els = [#mam_fin{complete = true, - rsm = #rsm_set{count = 10}}]}) + #message{ + sub_els = [#mam_fin{complete = true, + rsm = #rsm_set{count = 10}}]} = + recv_message(Config) end, %% Getting the item count. Should be 5 (or 10). I4 = send(Config, @@ -3006,19 +3034,21 @@ mam_query_rsm(Config, NS) -> rsm = #rsm_set{max = 0}}]}), maybe_recv_iq_result(Config, NS, I4), if NS == ?NS_MAM_TMP -> - ?recv1(#iq{type = result, id = I4, - sub_els = [#mam_query{ - xmlns = NS, - rsm = #rsm_set{count = 5, - first = undefined, - last = undefined}}]}); + #iq{type = result, id = I4, + sub_els = [#mam_query{ + xmlns = NS, + rsm = #rsm_set{count = 5, + first = undefined, + last = undefined}}]} = + recv_iq(Config); true -> - ?recv1(#message{ - sub_els = [#mam_fin{ - complete = false, - rsm = #rsm_set{count = 10, - first = undefined, - last = undefined}}]}) + #message{ + sub_els = [#mam_fin{ + complete = false, + rsm = #rsm_set{count = 10, + first = undefined, + last = undefined}}]} = + recv_message(Config) end, %% Should receive 2 last messages I5 = send(Config, @@ -3030,25 +3060,28 @@ mam_query_rsm(Config, NS) -> lists:foreach( fun(N) -> Text = #text{data = integer_to_binary(N)}, - ?recv1(#message{to = MyJID, - sub_els = - [#mam_result{ - xmlns = NS, - sub_els = - [#forwarded{ - delay = #delay{}, - sub_els = - [#message{ - from = MyJID, to = Peer, - body = [Text]}]}]}]}) + #message{to = MyJID, + sub_els = + [#mam_result{ + xmlns = NS, + sub_els = + [#forwarded{ + delay = #delay{}, + sub_els = + [#message{ + from = MyJID, to = Peer, + body = [Text]}]}]}]} = + recv_message(Config) end, lists:seq(4, 5)), if NS == ?NS_MAM_TMP -> - ?recv1(#iq{type = result, id = I5, - sub_els = [#mam_query{xmlns = NS, rsm = #rsm_set{count = 5}}]}); + #iq{type = result, id = I5, + sub_els = [#mam_query{xmlns = NS, rsm = #rsm_set{count = 5}}]} = + recv_iq(Config); true -> - ?recv1(#message{ - sub_els = [#mam_fin{complete = false, - rsm = #rsm_set{count = 10}}]}) + #message{ + sub_els = [#mam_fin{complete = false, + rsm = #rsm_set{count = 10}}]} = + recv_message(Config) end. client_state_master(Config) -> @@ -3109,8 +3142,8 @@ client_state_slave(Config) -> Peer = ?config(master, Config), change_client_state(Config, inactive), wait_for_master(Config), - ?recv1(#presence{from = Peer, type = unavailable, - sub_els = [#delay{}]}), + #presence{from = Peer, type = unavailable, sub_els = [#delay{}]} = + recv_presence(Config), #message{ from = Peer, sub_els = @@ -3121,7 +3154,7 @@ client_state_slave(Config) -> items = [#ps_item{ id = <<"pep-1">>}]}}, - #delay{}]} = recv(Config), + #delay{}]} = recv_message(Config), #message{ from = Peer, sub_els = @@ -3132,17 +3165,17 @@ client_state_slave(Config) -> items = [#ps_item{ id = <<"pep-2">>}]}}, - #delay{}]} = recv(Config), - ?recv1(#message{from = Peer, thread = <<"1">>, - sub_els = [#chatstate{type = composing}, - #delay{}]}), - ?recv1(#message{from = Peer, thread = <<"1">>, - body = [#text{data = <<"body">>}], - sub_els = [#chatstate{type = active}]}), + #delay{}]} = recv_message(Config), + #message{from = Peer, thread = <<"1">>, + sub_els = [#chatstate{type = composing}, + #delay{}]} = recv_message(Config), + #message{from = Peer, thread = <<"1">>, + body = [#text{data = <<"body">>}], + sub_els = [#chatstate{type = active}]} = recv_message(Config), change_client_state(Config, active), wait_for_master(Config), - ?recv1(#message{from = Peer, thread = <<"1">>, - sub_els = [#chatstate{type = active}]}), + #message{from = Peer, thread = <<"1">>, + sub_els = [#chatstate{type = active}]} = recv_message(Config), disconnect(Config). %%%=================================================================== diff --git a/test/muc_tests.erl b/test/muc_tests.erl index c89b97742..709a82a22 100644 --- a/test/muc_tests.erl +++ b/test/muc_tests.erl @@ -425,6 +425,7 @@ muc_history_master(Config) -> ServerHost = ?config(server_host, Config), MyNick = ?config(nick, Config), MyNickJID = jid:replace_resource(Room, MyNick), + PeerNickJID = peer_muc_jid(Config), Size = gen_mod:get_module_opt(ServerHost, mod_muc, history_size, fun(I) when is_integer(I), I>=0 -> I end, 20), @@ -439,9 +440,14 @@ muc_history_master(Config) -> #message{type = groupchat, from = MyNickJID, body = Body} = recv_message(Config) end, lists:seq(0, Size)), - wait_for_slave(Config), - wait_for_slave(Config), - flush(Config), + put_event(Config, join), + lists:foreach( + fun(Type) -> + recv_muc_presence(Config, PeerNickJID, Type) + end, [available, unavailable, + available, unavailable, + available, unavailable, + available, unavailable]), ok = muc_leave(Config), disconnect(Config). @@ -453,7 +459,9 @@ muc_history_slave(Config) -> Size = gen_mod:get_module_opt(ServerHost, mod_muc, history_size, fun(I) when is_integer(I), I>=0 -> I end, 20), - {History, _, _} = muc_slave_join(Config), + ct:comment("Waiting for 'join' command from the master"), + join = get_event(Config), + {History, _, _} = muc_join(Config), ct:comment("Checking ordering of history events"), BodyList = [binary_to_integer(xmpp:get_text(Body)) || #message{type = groupchat, from = From, @@ -486,7 +494,6 @@ muc_history_slave(Config) -> From == PeerNickJID], BodyListWithoutFirst = lists:nthtail(1, lists:seq(1, Size)), ok = muc_leave(Config), - wait_for_master(Config), disconnect(Config). muc_invite_master(Config) -> @@ -663,8 +670,7 @@ muc_change_role_master(Config) -> nick = PeerNick}|_] = muc_get_role(Config, Role) end, [visitor, participant, moderator]), put_event(Config, disconnect), - wait_for_slave(Config), - flush(Config), + recv_muc_presence(Config, PeerNickJID, unavailable), ok = muc_leave(Config), disconnect(Config). @@ -687,7 +693,6 @@ muc_change_role_slave(Config, {Role, Reason}) -> muc_change_role_slave(Config, get_event(Config)); muc_change_role_slave(Config, disconnect) -> ok = muc_leave(Config), - wait_for_master(Config), disconnect(Config). muc_change_affiliation_master(Config) -> @@ -1522,7 +1527,7 @@ muc_join_new(Config, Room) -> items = [#muc_item{role = moderator, jid = MyJID, affiliation = owner}]} = - xmpp:get_subtag(?recv1(#presence{from = MyNickJID}), #muc_user{}), + recv_muc_presence(Config, MyNickJID, available), ct:comment("Checking if codes '110' (self-presence) and " "'201' (new room) is set"), true = lists:member(110, Codes), @@ -1623,8 +1628,7 @@ muc_leave(Config, Room) -> #muc_user{ status_codes = Codes, items = [#muc_item{role = none, jid = MyJID}]} = - xmpp:get_subtag(?recv1(#presence{from = MyNickJID, - type = unavailable}), #muc_user{}), + recv_muc_presence(Config, MyNickJID, unavailable), ct:comment("Checking if code '110' (self-presence) is set"), true = lists:member(110, Codes), ok. diff --git a/test/privacy_tests.erl b/test/privacy_tests.erl new file mode 100644 index 000000000..2ee945f1e --- /dev/null +++ b/test/privacy_tests.erl @@ -0,0 +1,821 @@ +%%%------------------------------------------------------------------- +%%% @author Evgeny Khramtsov +%%% @copyright (C) 2016, Evgeny Khramtsov +%%% @doc +%%% +%%% @end +%%% Created : 18 Oct 2016 by Evgeny Khramtsov +%%%------------------------------------------------------------------- +-module(privacy_tests). + +%% API +-compile(export_all). +-import(suite, [disconnect/1, send_recv/2, get_event/1, put_event/2, + recv_iq/1, recv_presence/1, recv_message/1, recv/1, + send/2, my_jid/1, server_jid/1, get_features/1, + set_roster/3, del_roster/1]). +-include("suite.hrl"). +-include("mod_roster.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +%%%=================================================================== +%%% Single cases +%%%=================================================================== +single_cases() -> + {privacy_single, [sequence], + [single_test(feature_enabled), + single_test(set_get_list), + single_test(get_list_non_existent), + single_test(set_default), + single_test(del_default), + single_test(set_default_non_existent), + single_test(set_active), + single_test(del_active), + single_test(set_active_non_existent), + single_test(remove_list), + single_test(remove_default_list), + single_test(remove_active_list), + %% TODO: this should be fixed + %% single_test(remove_list_non_existent), + single_test(allow_local_server), + single_test(malformed_iq_query), + single_test(malformed_get), + single_test(malformed_set), + single_test(malformed_type_value), + single_test(set_get_block)]}. + +feature_enabled(Config) -> + Features = get_features(Config), + true = lists:member(?NS_PRIVACY, Features), + true = lists:member(?NS_BLOCKING, Features), + disconnect(Config). + +set_get_list(Config) -> + ListName = <<"set-get-list">>, + Items = [#privacy_item{order = 0, action = deny, + type = jid, value = <<"user@jabber.org">>, + iq = true}, + #privacy_item{order = 1, action = allow, + type = group, value = <<"group">>, + message = true}, + #privacy_item{order = 2, action = allow, + type = subscription, value = <<"both">>, + presence_in = true}, + #privacy_item{order = 3, action = deny, + type = subscription, value = <<"from">>, + presence_out = true}, + #privacy_item{order = 4, action = deny, + type = subscription, value = <<"to">>, + iq = true, message = true}, + #privacy_item{order = 5, action = deny, + type = subscription, value = <<"none">>, + _ = true}, + #privacy_item{order = 6, action = deny}], + ok = set_items(Config, ListName, Items), + #privacy_list{name = ListName, items = Items1} = get_list(Config, ListName), + Items = lists:keysort(#privacy_item.order, Items1), + del_privacy(disconnect(Config)). + +get_list_non_existent(Config) -> + ListName = <<"get-list-non-existent">>, + #stanza_error{reason = 'item-not-found'} = get_list(Config, ListName), + disconnect(Config). + +set_default(Config) -> + ListName = <<"set-default">>, + Item = #privacy_item{order = 0, action = deny}, + ok = set_items(Config, ListName, [Item]), + ok = set_default(Config, ListName), + #privacy_query{default = ListName} = get_lists(Config), + del_privacy(disconnect(Config)). + +del_default(Config) -> + ListName = <<"del-default">>, + Item = #privacy_item{order = 0, action = deny}, + ok = set_items(Config, ListName, [Item]), + ok = set_default(Config, ListName), + #privacy_query{default = ListName} = get_lists(Config), + ok = set_default(Config, none), + #privacy_query{default = none} = get_lists(Config), + del_privacy(disconnect(Config)). + +set_default_non_existent(Config) -> + ListName = <<"set-default-non-existent">>, + #stanza_error{reason = 'item-not-found'} = set_default(Config, ListName), + disconnect(Config). + +set_active(Config) -> + ListName = <<"set-active">>, + Item = #privacy_item{order = 0, action = deny}, + ok = set_items(Config, ListName, [Item]), + ok = set_active(Config, ListName), + #privacy_query{active = ListName} = get_lists(Config), + del_privacy(disconnect(Config)). + +del_active(Config) -> + ListName = <<"del-active">>, + Item = #privacy_item{order = 0, action = deny}, + ok = set_items(Config, ListName, [Item]), + ok = set_active(Config, ListName), + #privacy_query{active = ListName} = get_lists(Config), + ok = set_active(Config, none), + #privacy_query{active = none} = get_lists(Config), + del_privacy(disconnect(Config)). + +set_active_non_existent(Config) -> + ListName = <<"set-active-non-existent">>, + #stanza_error{reason = 'item-not-found'} = set_active(Config, ListName), + disconnect(Config). + +remove_list(Config) -> + ListName = <<"remove-list">>, + Item = #privacy_item{order = 0, action = deny}, + ok = set_items(Config, ListName, [Item]), + ok = del_list(Config, ListName), + #privacy_query{lists = []} = get_lists(Config), + del_privacy(disconnect(Config)). + +remove_active_list(Config) -> + ListName = <<"remove-active-list">>, + Item = #privacy_item{order = 0, action = deny}, + ok = set_items(Config, ListName, [Item]), + ok = set_active(Config, ListName), + #stanza_error{reason = 'conflict'} = del_list(Config, ListName), + del_privacy(disconnect(Config)). + +remove_default_list(Config) -> + ListName = <<"remove-default-list">>, + Item = #privacy_item{order = 0, action = deny}, + ok = set_items(Config, ListName, [Item]), + ok = set_default(Config, ListName), + #stanza_error{reason = 'conflict'} = del_list(Config, ListName), + del_privacy(disconnect(Config)). + +remove_list_non_existent(Config) -> + ListName = <<"remove-list-non-existent">>, + #stanza_error{reason = 'item-not-found'} = del_list(Config, ListName), + disconnect(Config). + +allow_local_server(Config) -> + ListName = <<"allow-local-server">>, + Item = #privacy_item{order = 0, action = deny}, + ok = set_items(Config, ListName, [Item]), + ok = set_active(Config, ListName), + %% Whatever privacy rules are set, we should always communicate + %% with our home server + server_send_iqs(Config), + server_recv_iqs(Config), + send_stanzas_to_server_resource(Config), + del_privacy(disconnect(Config)). + +malformed_iq_query(Config) -> + lists:foreach( + fun(Type) -> + #iq{type = error} = + send_recv(Config, + #iq{type = Type, + sub_els = [#privacy_list{name = <<"foo">>}]}) + end, [get, set]), + disconnect(Config). + +malformed_get(Config) -> + JID = jid:make(randoms:get_string()), + lists:foreach( + fun(SubEl) -> + #iq{type = error} = + send_recv(Config, #iq{type = get, sub_els = [SubEl]}) + end, [#privacy_query{active = none}, + #privacy_query{default = none}, + #privacy_query{lists = [#privacy_list{name = <<"1">>}, + #privacy_list{name = <<"2">>}]}, + #block{items = [JID]}, #unblock{items = [JID]}, + #block{}, #unblock{}]), + disconnect(Config). + +malformed_set(Config) -> + lists:foreach( + fun(SubEl) -> + #iq{type = error} = + send_recv(Config, #iq{type = set, sub_els = [SubEl]}) + end, [#privacy_query{active = none, default = none}, + #privacy_query{lists = [#privacy_list{name = <<"1">>}, + #privacy_list{name = <<"2">>}]}, + #block{}, + #block_list{}, + #block_list{items = [jid:make(randoms:get_string())]}]). + +malformed_type_value(Config) -> + Item = #privacy_item{order = 0, action = deny}, + #stanza_error{reason = 'bad-request'} = + set_items(Config, <<"malformed-jid">>, + [Item#privacy_item{type = jid, value = <<"@bad">>}]), + #stanza_error{reason = 'bad-request'} = + set_items(Config, <<"malformed-group">>, + [Item#privacy_item{type = group, value = <<"">>}]), + #stanza_error{reason = 'bad-request'} = + set_items(Config, <<"malformed-subscription">>, + [Item#privacy_item{type = subscription, value = <<"bad">>}]), + disconnect(Config). + +set_get_block(Config) -> + J1 = jid:make(randoms:get_string(), randoms:get_string()), + J2 = jid:make(randoms:get_string(), randoms:get_string()), + {ok, ListName} = set_block(Config, [J1, J2]), + JIDs = get_block(Config), + JIDs = lists:sort([J1, J2]), + {ok, ListName} = set_unblock(Config, [J2, J1]), + [] = get_block(Config), + del_privacy(disconnect(Config)). + +%%%=================================================================== +%%% Master-slave cases +%%%=================================================================== +master_slave_cases() -> + {privacy_master_slave, [parallel], + [master_slave_test(deny_bare_jid), + master_slave_test(deny_full_jid), + master_slave_test(deny_server_jid), + master_slave_test(deny_group), + master_slave_test(deny_sub_both), + master_slave_test(deny_sub_from), + master_slave_test(deny_sub_to), + master_slave_test(deny_sub_none), + master_slave_test(deny_all), + master_slave_test(deny_offline), + master_slave_test(block), + master_slave_test(unblock), + master_slave_test(unblock_all)]}. + +deny_bare_jid_master(Config) -> + PeerJID = ?config(peer, Config), + PeerBareJID = jid:remove_resource(PeerJID), + deny_master(Config, {jid, jid:to_string(PeerBareJID)}). + +deny_bare_jid_slave(Config) -> + deny_slave(Config). + +deny_full_jid_master(Config) -> + PeerJID = ?config(peer, Config), + deny_master(Config, {jid, jid:to_string(PeerJID)}). + +deny_full_jid_slave(Config) -> + deny_slave(Config). + +deny_server_jid_master(Config) -> + {_, Server, _} = jid:tolower(?config(peer, Config)), + deny_master(Config, {jid, Server}). + +deny_server_jid_slave(Config) -> + deny_slave(Config). + +deny_group_master(Config) -> + Group = randoms:get_string(), + deny_master(Config, {group, Group}). + +deny_group_slave(Config) -> + deny_slave(Config). + +deny_sub_both_master(Config) -> + deny_master(Config, {subscription, <<"both">>}). + +deny_sub_both_slave(Config) -> + deny_slave(Config). + +deny_sub_from_master(Config) -> + deny_master(Config, {subscription, <<"from">>}). + +deny_sub_from_slave(Config) -> + deny_slave(Config). + +deny_sub_to_master(Config) -> + deny_master(Config, {subscription, <<"to">>}). + +deny_sub_to_slave(Config) -> + deny_slave(Config). + +deny_sub_none_master(Config) -> + deny_master(Config, {subscription, <<"none">>}). + +deny_sub_none_slave(Config) -> + deny_slave(Config). + +deny_all_master(Config) -> + deny_master(Config, {undefined, <<"">>}). + +deny_all_slave(Config) -> + deny_slave(Config). + +deny_master(Config, {Type, Value}) -> + Sub = if Type == subscription -> + erlang:binary_to_atom(Value, utf8); + true -> + both + end, + Groups = if Type == group -> [Value]; + true -> [] + end, + set_roster(Config, Sub, Groups), + lists:foreach( + fun(Opts) -> + ListName = str:format("deny-~s-~s-~p", [Type, Value, Opts]), + Item = #privacy_item{order = 0, + action = deny, + iq = proplists:get_bool(iq, Opts), + message = proplists:get_bool(message, Opts), + presence_in = proplists:get_bool(presence_in, Opts), + presence_out = proplists:get_bool(presence_out, Opts), + type = Type, + value = Value}, + ok = set_items(Config, ListName, [Item]), + ok = set_active(Config, ListName), + put_event(Config, Opts), + case is_presence_in_blocked(Opts) of + true -> ok; + false -> recv_presences(Config) + end, + case is_iq_in_blocked(Opts) of + true -> ok; + false -> recv_iqs(Config) + end, + case is_message_in_blocked(Opts) of + true -> ok; + false -> recv_messages(Config) + end, + ct:comment("Waiting for 'send' command from the slave"), + send = get_event(Config), + case is_presence_out_blocked(Opts) of + true -> check_presence_blocked(Config, 'not-acceptable'); + false -> ok + end, + case is_iq_out_blocked(Opts) of + true -> check_iq_blocked(Config, 'not-acceptable'); + false -> send_iqs(Config) + end, + case is_message_out_blocked(Opts) of + true -> check_message_blocked(Config, 'not-acceptable'); + false -> send_messages(Config) + end, + case is_other_blocked(Opts) of + true -> check_other_blocked(Config, 'not-acceptable'); + false -> ok + end, + ct:comment("Waiting for slave to finish processing our stanzas"), + done = get_event(Config) + end, + [[iq], [message], [presence_in], [presence_out], + [iq, message, presence_in, presence_out], []]), + put_event(Config, disconnect), + clean_up(disconnect(Config)). + +deny_slave(Config) -> + set_roster(Config, both, []), + deny_slave(Config, get_event(Config)). + +deny_slave(Config, disconnect) -> + clean_up(disconnect(Config)); +deny_slave(Config, Opts) -> + send_presences(Config), + case is_iq_in_blocked(Opts) of + true -> check_iq_blocked(Config, 'service-unavailable'); + false -> send_iqs(Config) + end, + case is_message_in_blocked(Opts) of + true -> check_message_blocked(Config, 'service-unavailable'); + false -> send_messages(Config) + end, + put_event(Config, send), + case is_iq_out_blocked(Opts) of + true -> ok; + false -> recv_iqs(Config) + end, + case is_message_out_blocked(Opts) of + true -> ok; + false -> recv_messages(Config) + end, + put_event(Config, done), + deny_slave(Config, get_event(Config)). + +deny_offline_master(Config) -> + set_roster(Config, both, []), + ListName = <<"deny-offline">>, + Item = #privacy_item{order = 0, action = deny}, + ok = set_items(Config, ListName, [Item]), + ok = set_default(Config, ListName), + NewConfig = disconnect(Config), + put_event(NewConfig, send), + ct:comment("Waiting for the slave to finish"), + done = get_event(NewConfig), + clean_up(NewConfig). + +deny_offline_slave(Config) -> + set_roster(Config, both, []), + ct:comment("Waiting for 'send' command from the master"), + send = get_event(Config), + send_presences(Config), + check_iq_blocked(Config, 'service-unavailable'), + check_message_blocked(Config, 'service-unavailable'), + put_event(Config, done), + clean_up(disconnect(Config)). + +block_master(Config) -> + PeerJID = ?config(peer, Config), + set_roster(Config, both, []), + {ok, _} = set_block(Config, [PeerJID]), + check_presence_blocked(Config, 'not-acceptable'), + check_iq_blocked(Config, 'not-acceptable'), + check_message_blocked(Config, 'not-acceptable'), + check_other_blocked(Config, 'not-acceptable'), + %% We should always be able to communicate with our home server + server_send_iqs(Config), + server_recv_iqs(Config), + send_stanzas_to_server_resource(Config), + put_event(Config, send), + done = get_event(Config), + clean_up(disconnect(Config)). + +block_slave(Config) -> + set_roster(Config, both, []), + ct:comment("Waiting for 'send' command from master"), + send = get_event(Config), + send_presences(Config), + check_iq_blocked(Config, 'service-unavailable'), + check_message_blocked(Config, 'service-unavailable'), + put_event(Config, done), + clean_up(disconnect(Config)). + +unblock_master(Config) -> + PeerJID = ?config(peer, Config), + set_roster(Config, both, []), + {ok, ListName} = set_block(Config, [PeerJID]), + {ok, ListName} = set_unblock(Config, [PeerJID]), + put_event(Config, send), + recv_presences(Config), + recv_iqs(Config), + recv_messages(Config), + clean_up(disconnect(Config)). + +unblock_slave(Config) -> + set_roster(Config, both, []), + ct:comment("Waiting for 'send' command from master"), + send = get_event(Config), + send_presences(Config), + send_iqs(Config), + send_messages(Config), + clean_up(disconnect(Config)). + +unblock_all_master(Config) -> + PeerJID = ?config(peer, Config), + set_roster(Config, both, []), + {ok, ListName} = set_block(Config, [PeerJID]), + {ok, ListName} = set_unblock(Config, []), + put_event(Config, send), + recv_presences(Config), + recv_iqs(Config), + recv_messages(Config), + clean_up(disconnect(Config)). + +unblock_all_slave(Config) -> + set_roster(Config, both, []), + ct:comment("Waiting for 'send' command from master"), + send = get_event(Config), + send_presences(Config), + send_iqs(Config), + send_messages(Config), + clean_up(disconnect(Config)). + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +single_test(T) -> + list_to_atom("privacy_" ++ atom_to_list(T)). + +master_slave_test(T) -> + {list_to_atom("privacy_" ++ atom_to_list(T)), [parallel], + [list_to_atom("privacy_" ++ atom_to_list(T) ++ "_master"), + list_to_atom("privacy_" ++ atom_to_list(T) ++ "_slave")]}. + +set_items(Config, Name, Items) -> + ct:comment("Setting privacy list ~s with items = ~p", [Name, Items]), + case send_recv( + Config, + #iq{type = set, sub_els = [#privacy_query{ + lists = [#privacy_list{ + name = Name, + items = Items}]}]}) of + #iq{type = result, sub_els = []} -> + ct:comment("Receiving privacy list push"), + #iq{type = set, id = ID, + sub_els = [#privacy_query{lists = [#privacy_list{ + name = Name}]}]} = + recv_iq(Config), + send(Config, #iq{type = result, id = ID}), + ok; + #iq{type = error} = Err -> + xmpp:get_error(Err) + end. + +get_list(Config, Name) -> + ct:comment("Requesting privacy list ~s", [Name]), + case send_recv(Config, + #iq{type = get, + sub_els = [#privacy_query{ + lists = [#privacy_list{name = Name}]}]}) of + #iq{type = result, sub_els = [#privacy_query{lists = [List]}]} -> + List; + #iq{type = error} = Err -> + xmpp:get_error(Err) + end. + +get_lists(Config) -> + ct:comment("Requesting privacy lists"), + case send_recv(Config, #iq{type = get, sub_els = [#privacy_query{}]}) of + #iq{type = result, sub_els = [SubEl]} -> + SubEl; + #iq{type = error} = Err -> + xmpp:get_error(Err) + end. + +del_list(Config, Name) -> + case send_recv( + Config, + #iq{type = set, sub_els = [#privacy_query{ + lists = [#privacy_list{ + name = Name}]}]}) of + #iq{type = result, sub_els = []} -> + ct:comment("Receiving privacy list push"), + #iq{type = set, id = ID, + sub_els = [#privacy_query{lists = [#privacy_list{ + name = Name}]}]} = + recv_iq(Config), + send(Config, #iq{type = result, id = ID}), + ok; + #iq{type = error} = Err -> + xmpp:get_error(Err) + end. + +set_active(Config, Name) -> + ct:comment("Setting active privacy list ~s", [Name]), + case send_recv( + Config, + #iq{type = set, sub_els = [#privacy_query{active = Name}]}) of + #iq{type = result, sub_els = []} -> + ok; + #iq{type = error} = Err -> + xmpp:get_error(Err) + end. + +set_default(Config, Name) -> + ct:comment("Setting default privacy list ~s", [Name]), + case send_recv( + Config, + #iq{type = set, sub_els = [#privacy_query{default = Name}]}) of + #iq{type = result, sub_els = []} -> + ok; + #iq{type = error} = Err -> + xmpp:get_error(Err) + end. + +get_block(Config) -> + case send_recv(Config, #iq{type = get, sub_els = [#block_list{}]}) of + #iq{type = result, sub_els = [#block_list{items = JIDs}]} -> + lists:sort(JIDs); + #iq{type = error} = Err -> + xmpp:get_error(Err) + end. + +set_block(Config, JIDs) -> + case send_recv(Config, #iq{type = set, + sub_els = [#block{items = JIDs}]}) of + #iq{type = result, sub_els = []} -> + {#iq{id = I1, sub_els = [#block{items = Items}]}, + #iq{id = I2, sub_els = [#privacy_query{lists = Lists}]}} = + ?recv2(#iq{type = set, sub_els = [#block{}]}, + #iq{type = set, sub_els = [#privacy_query{}]}), + send(Config, #iq{type = result, id = I1}), + send(Config, #iq{type = result, id = I2}), + ct:comment("Checking if all JIDs present in the push"), + true = lists:sort(JIDs) == lists:sort(Items), + ct:comment("Getting name of the corresponding privacy list"), + [#privacy_list{name = Name}] = Lists, + {ok, Name}; + #iq{type = error} = Err -> + xmpp:get_error(Err) + end. + +set_unblock(Config, JIDs) -> + ct:comment("Unblocking ~p", [JIDs]), + case send_recv(Config, #iq{type = set, + sub_els = [#unblock{items = JIDs}]}) of + #iq{type = result, sub_els = []} -> + {#iq{id = I1, sub_els = [#unblock{items = Items}]}, + #iq{id = I2, sub_els = [#privacy_query{lists = Lists}]}} = + ?recv2(#iq{type = set, sub_els = [#unblock{}]}, + #iq{type = set, sub_els = [#privacy_query{}]}), + send(Config, #iq{type = result, id = I1}), + send(Config, #iq{type = result, id = I2}), + ct:comment("Checking if all JIDs present in the push"), + true = lists:sort(JIDs) == lists:sort(Items), + ct:comment("Getting name of the corresponding privacy list"), + [#privacy_list{name = Name}] = Lists, + {ok, Name}; + #iq{type = error} = Err -> + xmpp:get_error(Err) + end. + +del_privacy(Config) -> + {U, S, _} = jid:tolower(my_jid(Config)), + ct:comment("Removing all privacy data"), + mod_privacy:remove_user(U, S), + Config. + +clean_up(Config) -> + del_privacy(del_roster(Config)). + +check_iq_blocked(Config, Reason) -> + PeerJID = ?config(peer, Config), + ct:comment("Checking if all IQs are blocked"), + lists:foreach( + fun(Type) -> + send(Config, #iq{type = Type, to = PeerJID}) + end, [error, result]), + lists:foreach( + fun(Type) -> + #iq{type = error} = Err = + send_recv(Config, #iq{type = Type, to = PeerJID, + sub_els = [#ping{}]}), + #stanza_error{reason = Reason} = xmpp:get_error(Err) + end, [set, get]). + +check_message_blocked(Config, Reason) -> + PeerJID = ?config(peer, Config), + ct:comment("Checking if all messages are blocked"), + %% TODO: do something with headline and groupchat. + %% The hack from 64d96778b452aad72349b21d2ac94e744617b07a + %% screws this up. + lists:foreach( + fun(Type) -> + send(Config, #message{type = Type, to = PeerJID}) + end, [error]), + lists:foreach( + fun(Type) -> + #message{type = error} = Err = + send_recv(Config, #message{type = Type, to = PeerJID}), + #stanza_error{reason = Reason} = xmpp:get_error(Err) + end, [chat, normal]). + +check_presence_blocked(Config, Reason) -> + PeerJID = ?config(peer, Config), + ct:comment("Checking if all presences are blocked"), + lists:foreach( + fun(Type) -> + #presence{type = error} = Err = + send_recv(Config, #presence{type = Type, to = PeerJID}), + #stanza_error{reason = Reason} = xmpp:get_error(Err) + end, [available, unavailable]). + +check_other_blocked(Config, Reason) -> + PeerJID = ?config(peer, Config), + ct:comment("Checking if subscriptions and presence-errors are blocked"), + send(Config, #presence{type = error, to = PeerJID}), + lists:foreach( + fun(Type) -> + #presence{type = error} = Err = + send_recv(Config, #presence{type = Type, to = PeerJID}), + #stanza_error{reason = Reason} = xmpp:get_error(Err) + end, [subscribe, subscribed, unsubscribe, unsubscribed]). + +send_presences(Config) -> + PeerJID = ?config(peer, Config), + ct:comment("Sending all types of presences to the peer"), + lists:foreach( + fun(Type) -> + send(Config, #presence{type = Type, to = PeerJID}) + end, [available, unavailable]). + +send_iqs(Config) -> + PeerJID = ?config(peer, Config), + ct:comment("Sending all types of IQs to the peer"), + lists:foreach( + fun(Type) -> + send(Config, #iq{type = Type, to = PeerJID}) + end, [set, get, error, result]). + +send_messages(Config) -> + PeerJID = ?config(peer, Config), + ct:comment("Sending all types of messages to the peer"), + lists:foreach( + fun(Type) -> + send(Config, #message{type = Type, to = PeerJID}) + end, [chat, error, groupchat, headline, normal]). + +recv_presences(Config) -> + PeerJID = ?config(peer, Config), + lists:foreach( + fun(Type) -> + #presence{type = Type, from = PeerJID} = + recv_presence(Config) + end, [available, unavailable]). + +recv_iqs(Config) -> + PeerJID = ?config(peer, Config), + lists:foreach( + fun(Type) -> + #iq{type = Type, from = PeerJID} = recv_iq(Config) + end, [set, get, error, result]). + +recv_messages(Config) -> + PeerJID = ?config(peer, Config), + lists:foreach( + fun(Type) -> + #message{type = Type, from = PeerJID} = recv_message(Config) + end, [chat, error, groupchat, headline, normal]). + +match_all(Opts) -> + IQ = proplists:get_bool(iq, Opts), + Message = proplists:get_bool(message, Opts), + PresenceIn = proplists:get_bool(presence_in, Opts), + PresenceOut = proplists:get_bool(presence_out, Opts), + not (IQ or Message or PresenceIn or PresenceOut). + +is_message_in_blocked(Opts) -> + proplists:get_bool(message, Opts) or match_all(Opts). + +is_message_out_blocked(Opts) -> + match_all(Opts). + +is_iq_in_blocked(Opts) -> + proplists:get_bool(iq, Opts) or match_all(Opts). + +is_iq_out_blocked(Opts) -> + match_all(Opts). + +is_presence_in_blocked(Opts) -> + proplists:get_bool(presence_in, Opts) or match_all(Opts). + +is_presence_out_blocked(Opts) -> + proplists:get_bool(presence_out, Opts) or match_all(Opts). + +is_other_blocked(Opts) -> + %% 'other' means subscriptions and presence-errors + match_all(Opts). + +server_send_iqs(Config) -> + ServerJID = server_jid(Config), + MyJID = my_jid(Config), + ct:comment("Sending IQs from ~s to ~s", + [jid:to_string(ServerJID), jid:to_string(MyJID)]), + lists:foreach( + fun(Type) -> + ejabberd_router:route( + ServerJID, MyJID, #iq{type = Type}) + end, [error, result]), + lists:foreach( + fun(Type) -> + ejabberd_local:route_iq( + ServerJID, MyJID, #iq{type = Type}, + fun(#iq{type = result, sub_els = []}) -> ok; + (IQ) -> ct:fail({unexpected_iq_result, IQ}) + end) + end, [set, get]). + +server_recv_iqs(Config) -> + ServerJID = server_jid(Config), + ct:comment("Receiving IQs from ~s", [jid:to_string(ServerJID)]), + lists:foreach( + fun(Type) -> + #iq{type = Type, from = ServerJID} = recv_iq(Config) + end, [error, result]), + lists:foreach( + fun(Type) -> + #iq{type = Type, from = ServerJID, id = I} = recv_iq(Config), + send(Config, #iq{to = ServerJID, type = result, id = I}) + end, [set, get]). + +send_stanzas_to_server_resource(Config) -> + ServerJID = server_jid(Config), + ServerJIDResource = jid:replace_resource(ServerJID, <<"resource">>), + %% All stanzas sent should be handled by local_send_to_resource_hook + %% and should be bounced with item-not-found error + ct:comment("Sending IQs to ~s", [jid:to_string(ServerJIDResource)]), + lists:foreach( + fun(Type) -> + #iq{type = error} = Err = + send_recv(Config, #iq{type = Type, to = ServerJIDResource}), + #stanza_error{reason = 'item-not-found'} = xmpp:get_error(Err) + end, [set, get]), + ct:comment("Sending messages to ~s", [jid:to_string(ServerJIDResource)]), + lists:foreach( + fun(Type) -> + #message{type = error} = Err = + send_recv(Config, #message{type = Type, to = ServerJIDResource}), + #stanza_error{reason = 'item-not-found'} = xmpp:get_error(Err) + end, [normal, chat, groupchat, headline]), + ct:comment("Sending presences to ~s", [jid:to_string(ServerJIDResource)]), + lists:foreach( + fun(Type) -> + #presence{type = error} = Err = + send_recv(Config, #presence{type = Type, to = ServerJIDResource}), + #stanza_error{reason = 'item-not-found'} = xmpp:get_error(Err) + end, [available, unavailable]). diff --git a/test/suite.erl b/test/suite.erl index 7a823844b..3bed62562 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -13,6 +13,7 @@ -include("suite.hrl"). -include_lib("kernel/include/file.hrl"). +-include("mod_roster.hrl"). %%%=================================================================== %%% API @@ -171,10 +172,18 @@ connect(Config) -> tcp_connect(Config) -> case ?config(socket, Config) of undefined -> + Owner = self(), + NS = case ?config(type, Config) of + client -> ?NS_CLIENT; + server -> ?NS_SERVER; + component -> ?NS_COMPONENT + end, + ReceiverPid = spawn(fun() -> receiver(NS, Owner) end), {ok, Sock} = ejabberd_socket:connect( ?config(server_host, Config), ?config(server_port, Config), - [binary, {packet, 0}, {active, false}]), + [binary, {packet, 0}, {active, false}], + infinity, ReceiverPid), set_opt(socket, Sock, Config); _ -> Config @@ -219,9 +228,11 @@ disconnect(Config) -> catch exit:normal -> ok end, - {xmlstreamend, <<"stream:stream">>} = recv(Config), + receive {xmlstreamend, <<"stream:stream">>} -> ok end, + flush(Config), ejabberd_socket:close(Socket), - Config. + ct:comment("Disconnected"), + set_opt(socket, undefined, Config). close_socket(Config) -> Socket = ?config(socket, Config), @@ -435,40 +446,25 @@ match_failure(Received, [Match]) when is_list(Match)-> match_failure(Received, Matches) -> ct:fail("Received input:~n~n~p~n~ndon't match expected patterns:~n~n~p", [Received, Matches]). -recv(Config) -> +recv(_Config) -> receive - {'$gen_event', {xmlstreamelement, El}} -> - decode_stream_element(Config, El); - {'$gen_event', {xmlstreamstart, Name, Attrs}} -> - decode(#xmlel{name = Name, attrs = Attrs}, <<>>, []); - {'$gen_event', Event} -> - Event + {fail, El, Why} -> + ct:fail("recv failed: ~p->~n~s", + [El, xmpp:format_error(Why)]); + Event -> + Event end. -recv_iq(Config) -> - receive - {'$gen_event', {xmlstreamelement, #xmlel{name = <<"iq">>} = El}} -> - decode_stream_element(Config, El) - end. +recv_iq(_Config) -> + receive #iq{} = IQ -> IQ end. -recv_presence(Config) -> - receive - {'$gen_event', {xmlstreamelement, #xmlel{name = <<"presence">>} = El}} -> - decode_stream_element(Config, El) - end. +recv_presence(_Config) -> + receive #presence{} = Pres -> Pres end. -recv_message(Config) -> - receive - {'$gen_event', {xmlstreamelement, #xmlel{name = <<"message">>} = El}} -> - decode_stream_element(Config, El) - end. +recv_message(_Config) -> + receive #message{} = Msg -> Msg end. -decode_stream_element(Config, El) -> - NS = case ?config(type, Config) of - client -> ?NS_CLIENT; - server -> ?NS_SERVER; - component -> ?NS_COMPONENT - end, +decode_stream_element(NS, El) -> decode(El, NS, []). format_element(El) -> @@ -517,13 +513,13 @@ send(State, Pkt) -> send_recv(State, #message{} = Msg) -> ID = send(State, Msg), - #message{id = ID} = recv_message(State); + receive #message{id = ID} = Result -> Result end; send_recv(State, #presence{} = Pres) -> ID = send(State, Pres), - #presence{id = ID} = recv_presence(State); + receive #presence{id = ID} = Result -> Result end; send_recv(State, #iq{} = IQ) -> ID = send(State, IQ), - #iq{id = ID} = recv_iq(State). + receive #iq{id = ID} = Result -> Result end. sasl_new(<<"PLAIN">>, User, Server, Password) -> {<>, @@ -698,6 +694,50 @@ wait_for_slave(Config) -> make_iq_result(#iq{from = From} = IQ) -> IQ#iq{type = result, to = From, from = undefined, sub_els = []}. +set_roster(Config, Subscription, Groups) -> + MyJID = my_jid(Config), + {U, S, _} = jid:tolower(MyJID), + PeerJID = ?config(peer, Config), + PeerBareJID = jid:remove_resource(PeerJID), + PeerLJID = jid:tolower(PeerBareJID), + ct:comment("Adding ~s to roster with subscription '~s' in groups ~p", + [jid:to_string(PeerBareJID), Subscription, Groups]), + {atomic, _} = mod_roster:set_roster(#roster{usj = {U, S, PeerLJID}, + us = {U, S}, + jid = PeerLJID, + subscription = Subscription, + groups = Groups}), + Config. + +del_roster(Config) -> + MyJID = my_jid(Config), + {U, S, _} = jid:tolower(MyJID), + PeerJID = ?config(peer, Config), + PeerBareJID = jid:remove_resource(PeerJID), + PeerLJID = jid:tolower(PeerBareJID), + ct:comment("Removing ~s from roster", [jid:to_string(PeerBareJID)]), + {atomic, _} = mod_roster:del_roster(U, S, PeerLJID), + Config. + +receiver(NS, Owner) -> + MRef = erlang:monitor(process, Owner), + receiver(NS, Owner, MRef). + +receiver(NS, Owner, MRef) -> + receive + {'$gen_event', {xmlstreamelement, El}} -> + Owner ! decode_stream_element(NS, El), + receiver(NS, Owner, MRef); + {'$gen_event', {xmlstreamstart, Name, Attrs}} -> + Owner ! decode(#xmlel{name = Name, attrs = Attrs}, <<>>, []), + receiver(NS, Owner, MRef); + {'$gen_event', Event} -> + Owner ! Event, + receiver(NS, Owner, MRef); + {'DOWN', MRef, process, Owner, _} -> + ok + end. + %%%=================================================================== %%% Clients puts and gets events via this relay. %%%=================================================================== @@ -730,12 +770,17 @@ event_relay(Events, Subscribers) -> end, Subscribers), event_relay([Event|Events], Subscribers); {'DOWN', _MRef, process, Pid, _Info} -> - NewSubscribers = lists:delete(Pid, Subscribers), - lists:foreach( - fun(Subscriber) -> - Subscriber ! {event, peer_down, self()} - end, NewSubscribers), - event_relay(Events, NewSubscribers) + case lists:member(Pid, Subscribers) of + true -> + NewSubscribers = lists:delete(Pid, Subscribers), + lists:foreach( + fun(Subscriber) -> + Subscriber ! {event, peer_down, self()} + end, NewSubscribers), + event_relay(Events, NewSubscribers); + false -> + event_relay(Events, Subscribers) + end end. subscribe_to_events(Config) -> @@ -762,8 +807,10 @@ get_event(Config) -> end. flush(Config) -> - flush(Config, []). - -flush(Config, Msgs) -> - receive Msg -> flush(Config, [Msg|Msgs]) - after 1000 -> lists:reverse(Msgs) end. + receive + {event, peer_down, _} -> flush(Config); + closed -> flush(Config); + Msg -> ct:fail({unexpected_msg, Msg}) + after 0 -> + ok + end.