mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-20 16:15:59 +01:00
Add more tests for privacy lists and blocking command
This commit is contained in:
parent
67720c7713
commit
f6236d456d
@ -881,18 +881,20 @@ decode_element(#xmlel{} = El, StateName, StateData) ->
|
||||
end
|
||||
catch error:{xmpp_codec, Why} ->
|
||||
NS = xmpp:get_ns(El),
|
||||
case xmpp:is_stanza(El) of
|
||||
true ->
|
||||
Lang = xmpp:get_lang(El),
|
||||
Txt = xmpp:format_error(Why),
|
||||
send_error(StateData, El, xmpp:err_bad_request(Txt, Lang));
|
||||
false when NS == ?NS_STREAM_MGMT_2; NS == ?NS_STREAM_MGMT_3 ->
|
||||
Err = #sm_failed{reason = 'bad-request', xmlns = NS},
|
||||
send_element(StateData, Err);
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
fsm_next_state(StateName, StateData)
|
||||
fsm_next_state(
|
||||
StateName,
|
||||
case xmpp:is_stanza(El) of
|
||||
true ->
|
||||
Lang = xmpp:get_lang(El),
|
||||
Txt = xmpp:format_error(Why),
|
||||
send_error(StateData, El, xmpp:err_bad_request(Txt, Lang));
|
||||
false when NS == ?NS_STREAM_MGMT_2; NS == ?NS_STREAM_MGMT_3 ->
|
||||
Err = #sm_failed{reason = 'bad-request', xmlns = NS},
|
||||
send_element(StateData, Err),
|
||||
StateData;
|
||||
false ->
|
||||
StateData
|
||||
end)
|
||||
end.
|
||||
|
||||
wait_for_bind({xmlstreamelement, El}, StateData) ->
|
||||
@ -957,13 +959,14 @@ wait_for_bind(closed, StateData) ->
|
||||
wait_for_bind(stop, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
wait_for_bind(Pkt, StateData) ->
|
||||
case xmpp:is_stanza(Pkt) of
|
||||
true ->
|
||||
send_error(StateData, Pkt, xmpp:err_not_acceptable());
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
fsm_next_state(wait_for_bind, StateData).
|
||||
fsm_next_state(
|
||||
wait_for_bind,
|
||||
case xmpp:is_stanza(Pkt) of
|
||||
true ->
|
||||
send_error(StateData, Pkt, xmpp:err_not_acceptable());
|
||||
false ->
|
||||
StateData
|
||||
end).
|
||||
|
||||
-spec open_session(state()) -> {ok, state()} | {error, stanza_error()}.
|
||||
open_session(StateData) ->
|
||||
@ -1315,27 +1318,23 @@ handle_info({route, From, To, Packet}, StateName, StateData) when ?is_stanza(Pac
|
||||
allow ->
|
||||
{true, StateData};
|
||||
deny ->
|
||||
Err = xmpp:make_error(
|
||||
Packet,
|
||||
xmpp:err_service_unavailable()),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
ejabberd_router:route_error(
|
||||
To, From, Packet,
|
||||
xmpp:err_service_unavailable()),
|
||||
{false, StateData}
|
||||
end;
|
||||
_ ->
|
||||
Err = xmpp:make_error(Packet, xmpp:err_forbidden()),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
ejabberd_router:route_error(
|
||||
To, From, Packet, xmpp:err_forbidden()),
|
||||
{false, StateData}
|
||||
end;
|
||||
_ ->
|
||||
case privacy_check_packet(StateData, From, To, Packet, in) of
|
||||
allow ->
|
||||
{true, StateData};
|
||||
deny when T == get; T == set ->
|
||||
Err = xmpp:make_error(
|
||||
Packet, xmpp:err_service_unavailable()),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
{false, StateData};
|
||||
deny ->
|
||||
ejabberd_router:route_error(
|
||||
To, From, Packet, xmpp:err_service_unavailable()),
|
||||
{false, StateData}
|
||||
end
|
||||
end;
|
||||
@ -1345,13 +1344,11 @@ handle_info({route, From, To, Packet}, StateName, StateData) when ?is_stanza(Pac
|
||||
{true, StateData};
|
||||
deny ->
|
||||
case T of
|
||||
error -> ok;
|
||||
groupchat -> ok;
|
||||
headline -> ok;
|
||||
_ ->
|
||||
Err = xmpp:make_error(
|
||||
Packet, xmpp:err_service_unavailable()),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
ejabberd_router:route_error(
|
||||
To, From, Packet, xmpp:err_service_unavailable())
|
||||
end,
|
||||
{false, StateData}
|
||||
end
|
||||
@ -1572,14 +1569,14 @@ send_element(StateData, #xmlel{} = El) ->
|
||||
send_element(StateData, Pkt) ->
|
||||
send_element(StateData, xmpp:encode(Pkt, ?NS_CLIENT)).
|
||||
|
||||
-spec send_error(state(), xmlel() | stanza(), stanza_error()) -> ok.
|
||||
-spec send_error(state(), xmlel() | stanza(), stanza_error()) -> state().
|
||||
send_error(StateData, Stanza, Error) ->
|
||||
Type = xmpp:get_type(Stanza),
|
||||
if Type == error; Type == result;
|
||||
Type == <<"error">>; Type == <<"result">> ->
|
||||
ok;
|
||||
StateData;
|
||||
true ->
|
||||
send_element(StateData, xmpp:make_error(Stanza, Error))
|
||||
send_stanza(StateData, xmpp:make_error(Stanza, Error))
|
||||
end.
|
||||
|
||||
-spec send_stanza(state(), xmpp_element()) -> state().
|
||||
@ -1754,47 +1751,56 @@ presence_track(From, To, Packet, StateData) ->
|
||||
LTo = jid:tolower(To),
|
||||
User = StateData#state.user,
|
||||
Server = StateData#state.server,
|
||||
case Type of
|
||||
unavailable ->
|
||||
A = ?SETS:del_element(LTo, StateData#state.pres_a),
|
||||
check_privacy_route(From, StateData#state{pres_a = A}, From, To, Packet);
|
||||
subscribe ->
|
||||
try_roster_subscribe(subscribe, User, Server, From, To, Packet, StateData);
|
||||
subscribed ->
|
||||
ejabberd_hooks:run(roster_out_subscription, Server,
|
||||
[User, Server, To, subscribed]),
|
||||
check_privacy_route(From, StateData,
|
||||
jid:remove_resource(From), To, Packet);
|
||||
unsubscribe ->
|
||||
try_roster_subscribe(unsubscribe, User, Server, From, To, Packet, StateData);
|
||||
unsubscribed ->
|
||||
ejabberd_hooks:run(roster_out_subscription, Server,
|
||||
[User, Server, To, unsubscribed]),
|
||||
check_privacy_route(From, StateData,
|
||||
jid:remove_resource(From), To, Packet);
|
||||
error ->
|
||||
check_privacy_route(From, StateData, From, To, Packet);
|
||||
probe ->
|
||||
check_privacy_route(From, StateData, From, To, Packet);
|
||||
_ ->
|
||||
A = (?SETS):add_element(LTo, StateData#state.pres_a),
|
||||
check_privacy_route(From, StateData#state{pres_a = A}, From, To, Packet)
|
||||
Lang = StateData#state.lang,
|
||||
case privacy_check_packet(StateData, From, To, Packet, out) of
|
||||
deny ->
|
||||
ErrText = <<"Your active privacy list has denied "
|
||||
"the routing of this stanza.">>,
|
||||
Err = xmpp:err_not_acceptable(ErrText, Lang),
|
||||
send_error(StateData, xmpp:set_from_to(Packet, From, To), Err);
|
||||
allow when Type == subscribe; Type == subscribed;
|
||||
Type == unsubscribe; Type == unsubscribed ->
|
||||
Access = gen_mod:get_module_opt(Server, mod_roster, access,
|
||||
fun(A) when is_atom(A) -> A end,
|
||||
all),
|
||||
MyBareJID = jid:make(User, Server, <<"">>),
|
||||
case acl:match_rule(Server, Access, MyBareJID) of
|
||||
deny ->
|
||||
ErrText = <<"Denied by ACL">>,
|
||||
Err = xmpp:err_forbidden(ErrText, Lang),
|
||||
send_error(StateData, xmpp:set_from_to(Packet, From, To), Err);
|
||||
allow ->
|
||||
ejabberd_hooks:run(roster_out_subscription,
|
||||
Server,
|
||||
[User, Server, To, Type]),
|
||||
ejabberd_router:route(jid:remove_resource(From), To, Packet),
|
||||
StateData
|
||||
end;
|
||||
allow when Type == error; Type == probe ->
|
||||
ejabberd_router:route(From, To, Packet),
|
||||
StateData;
|
||||
allow ->
|
||||
ejabberd_router:route(From, To, Packet),
|
||||
A = case Type of
|
||||
available ->
|
||||
?SETS:add_element(LTo, StateData#state.pres_a);
|
||||
unavailable ->
|
||||
?SETS:del_element(LTo, StateData#state.pres_a)
|
||||
end,
|
||||
StateData#state{pres_a = A}
|
||||
end.
|
||||
|
||||
-spec check_privacy_route(jid(), state(), jid(), jid(), stanza()) -> state().
|
||||
check_privacy_route(From, StateData, FromRoute, To,
|
||||
Packet) ->
|
||||
case privacy_check_packet(StateData, From, To, Packet,
|
||||
out)
|
||||
of
|
||||
out) of
|
||||
deny ->
|
||||
Lang = StateData#state.lang,
|
||||
ErrText = <<"Your active privacy list has denied "
|
||||
"the routing of this stanza.">>,
|
||||
Err = xmpp:make_error(
|
||||
xmpp:set_from_to(Packet, From, To),
|
||||
xmpp:err_not_acceptable(ErrText, Lang)),
|
||||
send_stanza(StateData, Err);
|
||||
"the routing of this stanza.">>,
|
||||
Err = xmpp:err_not_acceptable(ErrText, Lang),
|
||||
send_error(StateData, xmpp:set_from_to(Packet, From, To), Err);
|
||||
allow ->
|
||||
ejabberd_router:route(FromRoute, To, Packet),
|
||||
StateData
|
||||
@ -1815,24 +1821,6 @@ is_privacy_allow(StateData, From, To, Packet, Dir) ->
|
||||
allow ==
|
||||
privacy_check_packet(StateData, From, To, Packet, Dir).
|
||||
|
||||
%%% Check ACL before allowing to send a subscription stanza
|
||||
-spec try_roster_subscribe(subscribe | unsubscribe, binary(), binary(),
|
||||
jid(), jid(), presence(), state()) -> state().
|
||||
try_roster_subscribe(Type, User, Server, From, To, Packet, StateData) ->
|
||||
JID1 = jid:make(User, Server, <<"">>),
|
||||
Access = gen_mod:get_module_opt(Server, mod_roster, access, fun(A) when is_atom(A) -> A end, all),
|
||||
case acl:match_rule(Server, Access, JID1) of
|
||||
deny ->
|
||||
%% Silently drop this (un)subscription request
|
||||
StateData;
|
||||
allow ->
|
||||
ejabberd_hooks:run(roster_out_subscription,
|
||||
Server,
|
||||
[User, Server, To, Type]),
|
||||
check_privacy_route(From, StateData, jid:remove_resource(From),
|
||||
To, Packet)
|
||||
end.
|
||||
|
||||
%% Send presence when disconnecting
|
||||
-spec presence_broadcast(state(), jid(), ?SETS:set(), presence()) -> ok.
|
||||
presence_broadcast(StateData, From, JIDSet, Packet) ->
|
||||
@ -1980,7 +1968,7 @@ process_privacy_iq(#iq{from = From, to = To,
|
||||
privacy_iq_set,
|
||||
StateData#state.server,
|
||||
{error, xmpp:err_feature_not_implemented(Txt, Lang)},
|
||||
[IQ])
|
||||
[IQ, StateData#state.privacy_list])
|
||||
of
|
||||
{result, R, NewPrivList} ->
|
||||
{{result, R},
|
||||
@ -2522,9 +2510,8 @@ handle_unacked_stanzas(#state{mgmt_state = MgmtState} = StateData)
|
||||
false ->
|
||||
fun(From, To, El, _Time) ->
|
||||
Txt = <<"User session terminated">>,
|
||||
Err = xmpp:make_error(
|
||||
El, xmpp:err_service_unavailable(Txt, Lang)),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
ejabberd_router:route_error(
|
||||
To, From, El, xmpp:err_service_unavailable(Txt, Lang))
|
||||
end
|
||||
end,
|
||||
F = fun(From, _To, #presence{}, _Time) ->
|
||||
@ -2532,9 +2519,8 @@ handle_unacked_stanzas(#state{mgmt_state = MgmtState} = StateData)
|
||||
[jid:to_string(From)]);
|
||||
(From, To, #iq{} = El, _Time) ->
|
||||
Txt = <<"User session terminated">>,
|
||||
Err = xmpp:make_error(
|
||||
El, xmpp:err_service_unavailable(Txt, Lang)),
|
||||
ejabberd_router:route(To, From, Err);
|
||||
ejabberd_router:route_error(
|
||||
To, From, El, xmpp:err_service_unavailable(Txt, Lang));
|
||||
(From, To, El, Time) ->
|
||||
%% We'll drop the stanza if it was <forwarded/> by some
|
||||
%% encapsulating protocol as per XEP-0297. One such protocol is
|
||||
|
@ -486,7 +486,7 @@ process_privacy(#privacy_query{lists = Lists,
|
||||
from = JID, to = JID, sub_els = [PrivacyQuery]},
|
||||
Txt = <<"No module is handling this query">>,
|
||||
Error = {error, xmpp:err_feature_not_implemented(Txt, ?MYLANG)},
|
||||
case mod_privacy:process_iq_set(Error, IQ) of
|
||||
case mod_privacy:process_iq_set(Error, IQ, #userlist{}) of
|
||||
{error, #stanza_error{reason = Reason}} = Err ->
|
||||
if Reason == 'item-not-found', Lists == [],
|
||||
Active == undefined, Default /= undefined ->
|
||||
|
@ -31,6 +31,7 @@
|
||||
-export([start/4,
|
||||
connect/3,
|
||||
connect/4,
|
||||
connect/5,
|
||||
starttls/2,
|
||||
starttls/3,
|
||||
compress/1,
|
||||
@ -125,19 +126,21 @@ start(Module, SockMod, Socket, Opts) ->
|
||||
end.
|
||||
|
||||
connect(Addr, Port, Opts) ->
|
||||
connect(Addr, Port, Opts, infinity).
|
||||
connect(Addr, Port, Opts, infinity, self()).
|
||||
|
||||
connect(Addr, Port, Opts, Timeout) ->
|
||||
connect(Addr, Port, Opts, Timeout, self()).
|
||||
|
||||
connect(Addr, Port, Opts, Timeout, Owner) ->
|
||||
case gen_tcp:connect(Addr, Port, Opts, Timeout) of
|
||||
{ok, Socket} ->
|
||||
Receiver = ejabberd_receiver:start(Socket, gen_tcp,
|
||||
none),
|
||||
SocketData = #socket_state{sockmod = gen_tcp,
|
||||
socket = Socket, receiver = Receiver},
|
||||
Pid = self(),
|
||||
case gen_tcp:controlling_process(Socket, Receiver) of
|
||||
ok ->
|
||||
ejabberd_receiver:become_controller(Receiver, Pid),
|
||||
ejabberd_receiver:become_controller(Receiver, Owner),
|
||||
{ok, SocketData};
|
||||
{error, _Reason} = Error -> gen_tcp:close(Socket), Error
|
||||
end;
|
||||
|
@ -53,6 +53,7 @@
|
||||
-include("ejabberd.hrl").
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("mod_roster.hrl").
|
||||
-include("mod_privacy.hrl").
|
||||
-include("ejabberd_sm.hrl").
|
||||
-include("xmpp.hrl").
|
||||
|
||||
@ -1380,11 +1381,12 @@ privacy_set(Username, Host, QueryS) ->
|
||||
To = jid:make(Host),
|
||||
QueryEl = fxml_stream:parse_element(QueryS),
|
||||
SubEl = xmpp:decode(QueryEl),
|
||||
IQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl]},
|
||||
IQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl],
|
||||
from = From, to = To},
|
||||
ejabberd_hooks:run_fold(privacy_iq_set,
|
||||
Host,
|
||||
{error, xmpp:err_feature_not_implemented()},
|
||||
[From, To, IQ]),
|
||||
[IQ, #userlist{}]),
|
||||
ok.
|
||||
|
||||
%%%
|
||||
|
@ -30,7 +30,7 @@
|
||||
-protocol({xep, 191, '1.2'}).
|
||||
|
||||
-export([start/2, stop/1, process_iq/1,
|
||||
process_iq_set/2, process_iq_get/3, mod_opt_type/1, depends/2]).
|
||||
process_iq_set/3, process_iq_get/3, mod_opt_type/1, depends/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@ -85,10 +85,11 @@ process_iq_get(Acc, _, _) -> Acc.
|
||||
-spec process_iq_set({error, stanza_error()} |
|
||||
{result, xmpp_element() | undefined} |
|
||||
{result, xmpp_element() | undefined, userlist()},
|
||||
iq()) -> {error, stanza_error()} |
|
||||
{result, xmpp_element() | undefined} |
|
||||
{result, xmpp_element() | undefined, userlist()}.
|
||||
process_iq_set(Acc, #iq{from = From, lang = Lang, sub_els = [SubEl]}) ->
|
||||
iq(), userlist()) ->
|
||||
{error, stanza_error()} |
|
||||
{result, xmpp_element() | undefined} |
|
||||
{result, xmpp_element() | undefined, userlist()}.
|
||||
process_iq_set(Acc, #iq{from = From, lang = Lang, sub_els = [SubEl]}, _) ->
|
||||
#jid{luser = LUser, lserver = LServer} = From,
|
||||
case SubEl of
|
||||
#block{items = []} ->
|
||||
@ -105,7 +106,7 @@ process_iq_set(Acc, #iq{from = From, lang = Lang, sub_els = [SubEl]}) ->
|
||||
_ ->
|
||||
Acc
|
||||
end;
|
||||
process_iq_set(Acc, _) -> Acc.
|
||||
process_iq_set(Acc, _, _) -> Acc.
|
||||
|
||||
-spec list_to_blocklist_jids([listitem()], [ljid()]) -> [ljid()].
|
||||
list_to_blocklist_jids([], JIDs) -> JIDs;
|
||||
|
@ -32,7 +32,7 @@
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2, stop/1, process_iq/1, export/1, import/1,
|
||||
process_iq_set/2, process_iq_get/3, get_user_list/3,
|
||||
process_iq_set/3, process_iq_get/3, get_user_list/3,
|
||||
check_packet/6, remove_user/2, encode_list_item/1,
|
||||
is_list_needdb/1, updated_list/3,
|
||||
item_to_xml/1, get_user_lists/2, import/3,
|
||||
@ -103,6 +103,12 @@ process_iq(IQ) ->
|
||||
-spec process_iq_get({error, stanza_error()} | {result, xmpp_element() | undefined},
|
||||
iq(), userlist()) -> {error, stanza_error()} |
|
||||
{result, xmpp_element() | undefined}.
|
||||
process_iq_get(_, #iq{lang = Lang,
|
||||
sub_els = [#privacy_query{default = Default,
|
||||
active = Active}]},
|
||||
_) when Default /= undefined; Active /= undefined ->
|
||||
Txt = <<"Only <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}) ->
|
||||
@ -205,7 +211,7 @@ encode_value(Type, Val) ->
|
||||
listitem_value().
|
||||
decode_value(Type, Value) ->
|
||||
case Type of
|
||||
jid -> jid:from_string(Value);
|
||||
jid -> jid:tolower(jid:from_string(Value));
|
||||
subscription ->
|
||||
case Value of
|
||||
<<"from">> -> from;
|
||||
@ -213,35 +219,37 @@ decode_value(Type, Value) ->
|
||||
<<"both">> -> both;
|
||||
<<"none">> -> none
|
||||
end;
|
||||
group -> Value;
|
||||
group when Value /= <<"">> -> Value;
|
||||
undefined -> none
|
||||
end.
|
||||
|
||||
-spec process_iq_set({error, stanza_error()} |
|
||||
{result, xmpp_element() | undefined} |
|
||||
{result, xmpp_element() | undefined, userlist()},
|
||||
iq()) -> {error, stanza_error()} |
|
||||
{result, xmpp_element() | undefined} |
|
||||
{result, xmpp_element() | undefined, userlist()}.
|
||||
iq(), #userlist{}) ->
|
||||
{error, stanza_error()} |
|
||||
{result, xmpp_element() | undefined} |
|
||||
{result, xmpp_element() | undefined, userlist()}.
|
||||
process_iq_set(_, #iq{from = From, lang = Lang,
|
||||
sub_els = [#privacy_query{default = Default,
|
||||
active = Active,
|
||||
lists = Lists}]}) ->
|
||||
lists = Lists}]},
|
||||
#userlist{} = UserList) ->
|
||||
#jid{luser = LUser, lserver = LServer} = From,
|
||||
case Lists of
|
||||
[#privacy_list{items = Items, name = ListName}]
|
||||
when Default == undefined, Active == undefined ->
|
||||
process_lists_set(LUser, LServer, ListName, Items, Lang);
|
||||
process_lists_set(LUser, LServer, ListName, Items, UserList, Lang);
|
||||
[] when Default == undefined, Active /= undefined ->
|
||||
process_active_set(LUser, LServer, Active, Lang);
|
||||
[] when Active == undefined, Default /= undefined ->
|
||||
process_default_set(LUser, LServer, Default, Lang);
|
||||
_ ->
|
||||
Txt = <<"There should be exactly one element in this query: "
|
||||
"<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(),
|
||||
@ -286,13 +294,20 @@ set_privacy_list(#privacy{us = {_, LServer}} = Privacy) ->
|
||||
Mod:set_privacy_list(Privacy).
|
||||
|
||||
-spec process_lists_set(binary(), binary(), binary(), [privacy_item()],
|
||||
binary()) -> {error, stanza_error()} | {result, undefined}.
|
||||
process_lists_set(LUser, LServer, Name, [], Lang) ->
|
||||
#userlist{}, binary()) -> {error, stanza_error()} |
|
||||
{result, undefined}.
|
||||
process_lists_set(_LUser, _LServer, Name, [], #userlist{name = Name}, Lang) ->
|
||||
Txt = <<"Cannot remove active list">>,
|
||||
{error, xmpp:err_conflict(Txt, Lang)};
|
||||
process_lists_set(LUser, LServer, Name, [], _UserList, Lang) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case Mod:remove_privacy_list(LUser, LServer, Name) of
|
||||
{atomic, conflict} ->
|
||||
Txt = <<"Cannot remove default list">>,
|
||||
{error, xmpp:err_conflict(Txt, Lang)};
|
||||
{atomic, not_found} ->
|
||||
Txt = <<"No privacy list with this name found">>,
|
||||
{error, xmpp:err_item_not_found(Txt, Lang)};
|
||||
{atomic, ok} ->
|
||||
ejabberd_sm:route(jid:make(LUser, LServer,
|
||||
<<"">>),
|
||||
@ -308,7 +323,7 @@ process_lists_set(LUser, LServer, Name, [], Lang) ->
|
||||
Txt = <<"Database failure">>,
|
||||
{error, xmpp:err_internal_server_error(Txt, Lang)}
|
||||
end;
|
||||
process_lists_set(LUser, LServer, Name, Items, Lang) ->
|
||||
process_lists_set(LUser, LServer, Name, Items, _UserList, Lang) ->
|
||||
case catch lists:map(fun decode_item/1, Items) of
|
||||
{error, Why} ->
|
||||
Txt = xmpp:format_error(Why),
|
||||
@ -358,9 +373,7 @@ decode_item(#privacy_item{order = Order,
|
||||
action = Action,
|
||||
type = Type,
|
||||
value = Value},
|
||||
if MatchMessage and MatchIQ and MatchPresenceIn and MatchPresenceOut ->
|
||||
ListItem#listitem{match_all = true};
|
||||
not (MatchMessage or MatchIQ or MatchPresenceIn or MatchPresenceOut) ->
|
||||
if not (MatchMessage or MatchIQ or MatchPresenceIn or MatchPresenceOut) ->
|
||||
ListItem#listitem{match_all = true};
|
||||
true ->
|
||||
ListItem#listitem{match_iq = MatchIQ,
|
||||
@ -468,17 +481,11 @@ check_packet_aux([Item | List], PType, JID,
|
||||
Item,
|
||||
case is_ptype_match(Item, PType) of
|
||||
true ->
|
||||
case Type of
|
||||
none -> Action;
|
||||
_ ->
|
||||
case is_type_match(Type, Value, JID, Subscription,
|
||||
Groups)
|
||||
of
|
||||
true -> Action;
|
||||
false ->
|
||||
check_packet_aux(List, PType, JID, Subscription, Groups)
|
||||
end
|
||||
end;
|
||||
case is_type_match(Type, Value, JID, Subscription, Groups) of
|
||||
true -> Action;
|
||||
false ->
|
||||
check_packet_aux(List, PType, JID, Subscription, Groups)
|
||||
end;
|
||||
false ->
|
||||
check_packet_aux(List, PType, JID, Subscription, Groups)
|
||||
end.
|
||||
@ -499,8 +506,10 @@ is_ptype_match(Item, PType) ->
|
||||
end
|
||||
end.
|
||||
|
||||
-spec is_type_match(jid | subscription | group, listitem_value(),
|
||||
-spec is_type_match(none | jid | subscription | group, listitem_value(),
|
||||
ljid(), none | both | from | to, [binary()]) -> boolean().
|
||||
is_type_match(none, _Value, _JID, _Subscription, _Groups) ->
|
||||
true;
|
||||
is_type_match(Type, Value, JID, Subscription, Groups) ->
|
||||
case Type of
|
||||
jid ->
|
||||
|
@ -49,7 +49,7 @@
|
||||
get_jid_info/4, encode_item/1, webadmin_page/3,
|
||||
webadmin_user/4, get_versioning_feature/2,
|
||||
roster_versioning_enabled/1, roster_version/2,
|
||||
mod_opt_type/1, set_roster/1, depends/2]).
|
||||
mod_opt_type/1, set_roster/1, del_roster/3, depends/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@ -297,6 +297,13 @@ set_roster(#roster{us = {LUser, LServer}, jid = LJID} = Item) ->
|
||||
roster_subscribe_t(LUser, LServer, LJID, Item)
|
||||
end).
|
||||
|
||||
del_roster(LUser, LServer, LJID) ->
|
||||
transaction(
|
||||
LServer,
|
||||
fun() ->
|
||||
del_roster_t(LUser, LServer, LJID)
|
||||
end).
|
||||
|
||||
encode_item(Item) ->
|
||||
#roster_item{jid = jid:make(Item#roster.jid),
|
||||
name = Item#roster.name,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -425,6 +425,7 @@ muc_history_master(Config) ->
|
||||
ServerHost = ?config(server_host, Config),
|
||||
MyNick = ?config(nick, Config),
|
||||
MyNickJID = jid:replace_resource(Room, MyNick),
|
||||
PeerNickJID = peer_muc_jid(Config),
|
||||
Size = gen_mod:get_module_opt(ServerHost, mod_muc, history_size,
|
||||
fun(I) when is_integer(I), I>=0 -> I end,
|
||||
20),
|
||||
@ -439,9 +440,14 @@ muc_history_master(Config) ->
|
||||
#message{type = groupchat, from = MyNickJID,
|
||||
body = Body} = recv_message(Config)
|
||||
end, lists:seq(0, Size)),
|
||||
wait_for_slave(Config),
|
||||
wait_for_slave(Config),
|
||||
flush(Config),
|
||||
put_event(Config, join),
|
||||
lists:foreach(
|
||||
fun(Type) ->
|
||||
recv_muc_presence(Config, PeerNickJID, Type)
|
||||
end, [available, unavailable,
|
||||
available, unavailable,
|
||||
available, unavailable,
|
||||
available, unavailable]),
|
||||
ok = muc_leave(Config),
|
||||
disconnect(Config).
|
||||
|
||||
@ -453,7 +459,9 @@ muc_history_slave(Config) ->
|
||||
Size = gen_mod:get_module_opt(ServerHost, mod_muc, history_size,
|
||||
fun(I) when is_integer(I), I>=0 -> I end,
|
||||
20),
|
||||
{History, _, _} = muc_slave_join(Config),
|
||||
ct:comment("Waiting for 'join' command from the master"),
|
||||
join = get_event(Config),
|
||||
{History, _, _} = muc_join(Config),
|
||||
ct:comment("Checking ordering of history events"),
|
||||
BodyList = [binary_to_integer(xmpp:get_text(Body))
|
||||
|| #message{type = groupchat, from = From,
|
||||
@ -486,7 +494,6 @@ muc_history_slave(Config) ->
|
||||
From == PeerNickJID],
|
||||
BodyListWithoutFirst = lists:nthtail(1, lists:seq(1, Size)),
|
||||
ok = muc_leave(Config),
|
||||
wait_for_master(Config),
|
||||
disconnect(Config).
|
||||
|
||||
muc_invite_master(Config) ->
|
||||
@ -663,8 +670,7 @@ muc_change_role_master(Config) ->
|
||||
nick = PeerNick}|_] = muc_get_role(Config, Role)
|
||||
end, [visitor, participant, moderator]),
|
||||
put_event(Config, disconnect),
|
||||
wait_for_slave(Config),
|
||||
flush(Config),
|
||||
recv_muc_presence(Config, PeerNickJID, unavailable),
|
||||
ok = muc_leave(Config),
|
||||
disconnect(Config).
|
||||
|
||||
@ -687,7 +693,6 @@ muc_change_role_slave(Config, {Role, Reason}) ->
|
||||
muc_change_role_slave(Config, get_event(Config));
|
||||
muc_change_role_slave(Config, disconnect) ->
|
||||
ok = muc_leave(Config),
|
||||
wait_for_master(Config),
|
||||
disconnect(Config).
|
||||
|
||||
muc_change_affiliation_master(Config) ->
|
||||
@ -1522,7 +1527,7 @@ muc_join_new(Config, Room) ->
|
||||
items = [#muc_item{role = moderator,
|
||||
jid = MyJID,
|
||||
affiliation = owner}]} =
|
||||
xmpp:get_subtag(?recv1(#presence{from = MyNickJID}), #muc_user{}),
|
||||
recv_muc_presence(Config, MyNickJID, available),
|
||||
ct:comment("Checking if codes '110' (self-presence) and "
|
||||
"'201' (new room) is set"),
|
||||
true = lists:member(110, Codes),
|
||||
@ -1623,8 +1628,7 @@ muc_leave(Config, Room) ->
|
||||
#muc_user{
|
||||
status_codes = Codes,
|
||||
items = [#muc_item{role = none, jid = MyJID}]} =
|
||||
xmpp:get_subtag(?recv1(#presence{from = MyNickJID,
|
||||
type = unavailable}), #muc_user{}),
|
||||
recv_muc_presence(Config, MyNickJID, unavailable),
|
||||
ct:comment("Checking if code '110' (self-presence) is set"),
|
||||
true = lists:member(110, Codes),
|
||||
ok.
|
||||
|
821
test/privacy_tests.erl
Normal file
821
test/privacy_tests.erl
Normal file
@ -0,0 +1,821 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @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]).
|
137
test/suite.erl
137
test/suite.erl
@ -13,6 +13,7 @@
|
||||
|
||||
-include("suite.hrl").
|
||||
-include_lib("kernel/include/file.hrl").
|
||||
-include("mod_roster.hrl").
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
@ -171,10 +172,18 @@ connect(Config) ->
|
||||
tcp_connect(Config) ->
|
||||
case ?config(socket, Config) of
|
||||
undefined ->
|
||||
Owner = self(),
|
||||
NS = case ?config(type, Config) of
|
||||
client -> ?NS_CLIENT;
|
||||
server -> ?NS_SERVER;
|
||||
component -> ?NS_COMPONENT
|
||||
end,
|
||||
ReceiverPid = spawn(fun() -> receiver(NS, Owner) end),
|
||||
{ok, Sock} = ejabberd_socket:connect(
|
||||
?config(server_host, Config),
|
||||
?config(server_port, Config),
|
||||
[binary, {packet, 0}, {active, false}]),
|
||||
[binary, {packet, 0}, {active, false}],
|
||||
infinity, ReceiverPid),
|
||||
set_opt(socket, Sock, Config);
|
||||
_ ->
|
||||
Config
|
||||
@ -219,9 +228,11 @@ disconnect(Config) ->
|
||||
catch exit:normal ->
|
||||
ok
|
||||
end,
|
||||
{xmlstreamend, <<"stream:stream">>} = recv(Config),
|
||||
receive {xmlstreamend, <<"stream:stream">>} -> ok end,
|
||||
flush(Config),
|
||||
ejabberd_socket:close(Socket),
|
||||
Config.
|
||||
ct:comment("Disconnected"),
|
||||
set_opt(socket, undefined, Config).
|
||||
|
||||
close_socket(Config) ->
|
||||
Socket = ?config(socket, Config),
|
||||
@ -435,40 +446,25 @@ match_failure(Received, [Match]) when is_list(Match)->
|
||||
match_failure(Received, Matches) ->
|
||||
ct:fail("Received input:~n~n~p~n~ndon't match expected patterns:~n~n~p", [Received, Matches]).
|
||||
|
||||
recv(Config) ->
|
||||
recv(_Config) ->
|
||||
receive
|
||||
{'$gen_event', {xmlstreamelement, El}} ->
|
||||
decode_stream_element(Config, El);
|
||||
{'$gen_event', {xmlstreamstart, Name, Attrs}} ->
|
||||
decode(#xmlel{name = Name, attrs = Attrs}, <<>>, []);
|
||||
{'$gen_event', Event} ->
|
||||
Event
|
||||
{fail, El, Why} ->
|
||||
ct:fail("recv failed: ~p->~n~s",
|
||||
[El, xmpp:format_error(Why)]);
|
||||
Event ->
|
||||
Event
|
||||
end.
|
||||
|
||||
recv_iq(Config) ->
|
||||
receive
|
||||
{'$gen_event', {xmlstreamelement, #xmlel{name = <<"iq">>} = El}} ->
|
||||
decode_stream_element(Config, El)
|
||||
end.
|
||||
recv_iq(_Config) ->
|
||||
receive #iq{} = IQ -> IQ end.
|
||||
|
||||
recv_presence(Config) ->
|
||||
receive
|
||||
{'$gen_event', {xmlstreamelement, #xmlel{name = <<"presence">>} = El}} ->
|
||||
decode_stream_element(Config, El)
|
||||
end.
|
||||
recv_presence(_Config) ->
|
||||
receive #presence{} = Pres -> Pres end.
|
||||
|
||||
recv_message(Config) ->
|
||||
receive
|
||||
{'$gen_event', {xmlstreamelement, #xmlel{name = <<"message">>} = El}} ->
|
||||
decode_stream_element(Config, El)
|
||||
end.
|
||||
recv_message(_Config) ->
|
||||
receive #message{} = Msg -> Msg end.
|
||||
|
||||
decode_stream_element(Config, El) ->
|
||||
NS = case ?config(type, Config) of
|
||||
client -> ?NS_CLIENT;
|
||||
server -> ?NS_SERVER;
|
||||
component -> ?NS_COMPONENT
|
||||
end,
|
||||
decode_stream_element(NS, El) ->
|
||||
decode(El, NS, []).
|
||||
|
||||
format_element(El) ->
|
||||
@ -517,13 +513,13 @@ send(State, Pkt) ->
|
||||
|
||||
send_recv(State, #message{} = Msg) ->
|
||||
ID = send(State, Msg),
|
||||
#message{id = ID} = recv_message(State);
|
||||
receive #message{id = ID} = Result -> Result end;
|
||||
send_recv(State, #presence{} = Pres) ->
|
||||
ID = send(State, Pres),
|
||||
#presence{id = ID} = recv_presence(State);
|
||||
receive #presence{id = ID} = Result -> Result end;
|
||||
send_recv(State, #iq{} = IQ) ->
|
||||
ID = send(State, IQ),
|
||||
#iq{id = ID} = recv_iq(State).
|
||||
receive #iq{id = ID} = Result -> Result end.
|
||||
|
||||
sasl_new(<<"PLAIN">>, User, Server, Password) ->
|
||||
{<<User/binary, $@, Server/binary, 0, User/binary, 0, Password/binary>>,
|
||||
@ -698,6 +694,50 @@ wait_for_slave(Config) ->
|
||||
make_iq_result(#iq{from = From} = IQ) ->
|
||||
IQ#iq{type = result, to = From, from = undefined, sub_els = []}.
|
||||
|
||||
set_roster(Config, Subscription, Groups) ->
|
||||
MyJID = my_jid(Config),
|
||||
{U, S, _} = jid:tolower(MyJID),
|
||||
PeerJID = ?config(peer, Config),
|
||||
PeerBareJID = jid:remove_resource(PeerJID),
|
||||
PeerLJID = jid:tolower(PeerBareJID),
|
||||
ct:comment("Adding ~s to roster with subscription '~s' in groups ~p",
|
||||
[jid:to_string(PeerBareJID), Subscription, Groups]),
|
||||
{atomic, _} = mod_roster:set_roster(#roster{usj = {U, S, PeerLJID},
|
||||
us = {U, S},
|
||||
jid = PeerLJID,
|
||||
subscription = Subscription,
|
||||
groups = Groups}),
|
||||
Config.
|
||||
|
||||
del_roster(Config) ->
|
||||
MyJID = my_jid(Config),
|
||||
{U, S, _} = jid:tolower(MyJID),
|
||||
PeerJID = ?config(peer, Config),
|
||||
PeerBareJID = jid:remove_resource(PeerJID),
|
||||
PeerLJID = jid:tolower(PeerBareJID),
|
||||
ct:comment("Removing ~s from roster", [jid:to_string(PeerBareJID)]),
|
||||
{atomic, _} = mod_roster:del_roster(U, S, PeerLJID),
|
||||
Config.
|
||||
|
||||
receiver(NS, Owner) ->
|
||||
MRef = erlang:monitor(process, Owner),
|
||||
receiver(NS, Owner, MRef).
|
||||
|
||||
receiver(NS, Owner, MRef) ->
|
||||
receive
|
||||
{'$gen_event', {xmlstreamelement, El}} ->
|
||||
Owner ! decode_stream_element(NS, El),
|
||||
receiver(NS, Owner, MRef);
|
||||
{'$gen_event', {xmlstreamstart, Name, Attrs}} ->
|
||||
Owner ! decode(#xmlel{name = Name, attrs = Attrs}, <<>>, []),
|
||||
receiver(NS, Owner, MRef);
|
||||
{'$gen_event', Event} ->
|
||||
Owner ! Event,
|
||||
receiver(NS, Owner, MRef);
|
||||
{'DOWN', MRef, process, Owner, _} ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Clients puts and gets events via this relay.
|
||||
%%%===================================================================
|
||||
@ -730,12 +770,17 @@ event_relay(Events, Subscribers) ->
|
||||
end, Subscribers),
|
||||
event_relay([Event|Events], Subscribers);
|
||||
{'DOWN', _MRef, process, Pid, _Info} ->
|
||||
NewSubscribers = lists:delete(Pid, Subscribers),
|
||||
lists:foreach(
|
||||
fun(Subscriber) ->
|
||||
Subscriber ! {event, peer_down, self()}
|
||||
end, NewSubscribers),
|
||||
event_relay(Events, NewSubscribers)
|
||||
case lists:member(Pid, Subscribers) of
|
||||
true ->
|
||||
NewSubscribers = lists:delete(Pid, Subscribers),
|
||||
lists:foreach(
|
||||
fun(Subscriber) ->
|
||||
Subscriber ! {event, peer_down, self()}
|
||||
end, NewSubscribers),
|
||||
event_relay(Events, NewSubscribers);
|
||||
false ->
|
||||
event_relay(Events, Subscribers)
|
||||
end
|
||||
end.
|
||||
|
||||
subscribe_to_events(Config) ->
|
||||
@ -762,8 +807,10 @@ get_event(Config) ->
|
||||
end.
|
||||
|
||||
flush(Config) ->
|
||||
flush(Config, []).
|
||||
|
||||
flush(Config, Msgs) ->
|
||||
receive Msg -> flush(Config, [Msg|Msgs])
|
||||
after 1000 -> lists:reverse(Msgs) end.
|
||||
receive
|
||||
{event, peer_down, _} -> flush(Config);
|
||||
closed -> flush(Config);
|
||||
Msg -> ct:fail({unexpected_msg, Msg})
|
||||
after 0 ->
|
||||
ok
|
||||
end.
|
||||
|
Loading…
Reference in New Issue
Block a user