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) ->
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) ->
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;
{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
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().
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
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) ->
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},
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) ->
[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 <forwarded/> by some
%% encapsulating protocol as per XEP-0297. One such protocol is
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 ->
-export([start/4,
connect/3,
connect/4,
+ connect/5,
starttls/2,
starttls/3,
compress/1,
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;
-include("ejabberd.hrl").
-include("ejabberd_commands.hrl").
-include("mod_roster.hrl").
+-include("mod_privacy.hrl").
-include("ejabberd_sm.hrl").
-include("xmpp.hrl").
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.
%%%
-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").
-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 = []} ->
_ ->
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;
-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,
-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 <list/> 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}) ->
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;
<<"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: "
- "<list/>, <active/> or <default/>">>,
+ Txt = <<"The stanza MUST contain only one <active/> element, "
+ "one <default/> element, or one <list/> 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(),
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,
<<"">>),
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),
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,
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.
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 ->
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").
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,
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),
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),
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
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]},
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]},
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]},
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) ->
%% 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,
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) ->
%% 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
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.
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).
-
-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_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).
+
+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),
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).
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) ->
sub_els =
[#ps_event{
items = #ps_items{node = Node,
- items = [Item]}}]} = recv(Config),
+ items = [Item]}}]} = recv_message(Config),
put_event(Config, Item),
disconnect(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).
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),
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),
%% 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),
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).
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
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).
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(
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).
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(
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}),
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) ->
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),
[]
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
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) ->
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).
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(
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).
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,
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,
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) ->
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,
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,
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)),
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,
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} =
lists:foreach(
fun(N) ->
Text = #text{data = integer_to_binary(N)},
- ?recv1(#message{to = MyJID,
+ #message{to = MyJID,
sub_els =
[#mam_result{
queryid = QID,
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) ->
lists:foreach(
fun(N) ->
Text = #text{data = integer_to_binary(N)},
- ?recv1(#message{to = MyJID,
+ #message{to = MyJID,
sub_els =
[#mam_result{
sub_els =
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.
lists:foreach(
fun(N) ->
Text = #text{data = integer_to_binary(N)},
- ?recv1(#message{to = MyJID,
+ #message{to = MyJID,
sub_els =
[#mam_result{
xmlns = 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.
lists:foreach(
fun(N) ->
Text = #text{data = integer_to_binary(N)},
- ?recv1(#message{to = MyJID,
+ #message{to = MyJID,
sub_els =
[#mam_result{
xmlns = 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,
rsm = #rsm_set{
count = 5,
first = #rsm_first{data = First}}}]} =
- recv(Config);
+ recv_iq(Config);
true ->
#message{
sub_els = [#mam_fin{
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,
lists:foreach(
fun(N) ->
Text = #text{data = integer_to_binary(N)},
- ?recv1(#message{to = MyJID,
+ #message{to = MyJID,
sub_els =
[#mam_result{
xmlns = 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,
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,
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) ->
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 =
items =
[#ps_item{
id = <<"pep-1">>}]}},
- #delay{}]} = recv(Config),
+ #delay{}]} = recv_message(Config),
#message{
from = Peer,
sub_els =
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).
%%%===================================================================
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),
#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).
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,
From == PeerNickJID],
BodyListWithoutFirst = lists:nthtail(1, lists:seq(1, Size)),
ok = muc_leave(Config),
- wait_for_master(Config),
disconnect(Config).
muc_invite_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).
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) ->
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),
#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.
--- /dev/null
+%%%-------------------------------------------------------------------
+%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%% @copyright (C) 2016, Evgeny Khramtsov
+%%% @doc
+%%%
+%%% @end
+%%% Created : 18 Oct 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%%-------------------------------------------------------------------
+-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]).
-include("suite.hrl").
-include_lib("kernel/include/file.hrl").
+-include("mod_roster.hrl").
%%%===================================================================
%%% API
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
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),
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) ->
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) ->
{<<User/binary, $@, Server/binary, 0, User/binary, 0, Password/binary>>,
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.
%%%===================================================================
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) ->
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.