diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ejabberd_c2s.erl | 174 | ||||
-rw-r--r-- | src/ejabberd_piefxis.erl | 2 | ||||
-rw-r--r-- | src/ejabberd_socket.erl | 9 | ||||
-rw-r--r-- | src/mod_admin_extra.erl | 6 | ||||
-rw-r--r-- | src/mod_blocking.erl | 13 | ||||
-rw-r--r-- | src/mod_privacy.erl | 67 | ||||
-rw-r--r-- | src/mod_roster.erl | 9 |
7 files changed, 144 insertions, 136 deletions
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index f7d8e9dbb..986310546 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -881,18 +881,20 @@ decode_element(#xmlel{} = El, StateName, StateData) -> end catch error:{xmpp_codec, Why} -> NS = xmpp:get_ns(El), - case xmpp:is_stanza(El) of - true -> - Lang = xmpp:get_lang(El), - Txt = xmpp:format_error(Why), - send_error(StateData, El, xmpp:err_bad_request(Txt, Lang)); - false when NS == ?NS_STREAM_MGMT_2; NS == ?NS_STREAM_MGMT_3 -> - Err = #sm_failed{reason = 'bad-request', xmlns = NS}, - send_element(StateData, Err); - false -> - ok - end, - fsm_next_state(StateName, StateData) + fsm_next_state( + StateName, + case xmpp:is_stanza(El) of + true -> + Lang = xmpp:get_lang(El), + Txt = xmpp:format_error(Why), + send_error(StateData, El, xmpp:err_bad_request(Txt, Lang)); + false when NS == ?NS_STREAM_MGMT_2; NS == ?NS_STREAM_MGMT_3 -> + Err = #sm_failed{reason = 'bad-request', xmlns = NS}, + send_element(StateData, Err), + StateData; + false -> + StateData + end) end. wait_for_bind({xmlstreamelement, El}, StateData) -> @@ -957,13 +959,14 @@ wait_for_bind(closed, StateData) -> wait_for_bind(stop, StateData) -> {stop, normal, StateData}; wait_for_bind(Pkt, StateData) -> - case xmpp:is_stanza(Pkt) of - true -> - send_error(StateData, Pkt, xmpp:err_not_acceptable()); - false -> - ok - end, - fsm_next_state(wait_for_bind, StateData). + fsm_next_state( + wait_for_bind, + case xmpp:is_stanza(Pkt) of + true -> + send_error(StateData, Pkt, xmpp:err_not_acceptable()); + false -> + StateData + end). -spec open_session(state()) -> {ok, state()} | {error, stanza_error()}. open_session(StateData) -> @@ -1315,27 +1318,23 @@ handle_info({route, From, To, Packet}, StateName, StateData) when ?is_stanza(Pac allow -> {true, StateData}; deny -> - Err = xmpp:make_error( - Packet, - xmpp:err_service_unavailable()), - ejabberd_router:route(To, From, Err), + ejabberd_router:route_error( + To, From, Packet, + xmpp:err_service_unavailable()), {false, StateData} end; _ -> - Err = xmpp:make_error(Packet, xmpp:err_forbidden()), - ejabberd_router:route(To, From, Err), + ejabberd_router:route_error( + To, From, Packet, xmpp:err_forbidden()), {false, StateData} end; _ -> case privacy_check_packet(StateData, From, To, Packet, in) of allow -> {true, StateData}; - deny when T == get; T == set -> - Err = xmpp:make_error( - Packet, xmpp:err_service_unavailable()), - ejabberd_router:route(To, From, Err), - {false, StateData}; deny -> + ejabberd_router:route_error( + To, From, Packet, xmpp:err_service_unavailable()), {false, StateData} end end; @@ -1345,13 +1344,11 @@ handle_info({route, From, To, Packet}, StateName, StateData) when ?is_stanza(Pac {true, StateData}; deny -> case T of - error -> ok; groupchat -> ok; headline -> ok; _ -> - Err = xmpp:make_error( - Packet, xmpp:err_service_unavailable()), - ejabberd_router:route(To, From, Err) + ejabberd_router:route_error( + To, From, Packet, xmpp:err_service_unavailable()) end, {false, StateData} end @@ -1572,14 +1569,14 @@ send_element(StateData, #xmlel{} = El) -> send_element(StateData, Pkt) -> send_element(StateData, xmpp:encode(Pkt, ?NS_CLIENT)). --spec send_error(state(), xmlel() | stanza(), stanza_error()) -> ok. +-spec send_error(state(), xmlel() | stanza(), stanza_error()) -> state(). send_error(StateData, Stanza, Error) -> Type = xmpp:get_type(Stanza), if Type == error; Type == result; Type == <<"error">>; Type == <<"result">> -> - ok; + StateData; true -> - send_element(StateData, xmpp:make_error(Stanza, Error)) + send_stanza(StateData, xmpp:make_error(Stanza, Error)) end. -spec send_stanza(state(), xmpp_element()) -> state(). @@ -1754,47 +1751,56 @@ presence_track(From, To, Packet, StateData) -> LTo = jid:tolower(To), User = StateData#state.user, Server = StateData#state.server, - case Type of - unavailable -> - A = ?SETS:del_element(LTo, StateData#state.pres_a), - check_privacy_route(From, StateData#state{pres_a = A}, From, To, Packet); - subscribe -> - try_roster_subscribe(subscribe, User, Server, From, To, Packet, StateData); - subscribed -> - ejabberd_hooks:run(roster_out_subscription, Server, - [User, Server, To, subscribed]), - check_privacy_route(From, StateData, - jid:remove_resource(From), To, Packet); - unsubscribe -> - try_roster_subscribe(unsubscribe, User, Server, From, To, Packet, StateData); - unsubscribed -> - ejabberd_hooks:run(roster_out_subscription, Server, - [User, Server, To, unsubscribed]), - check_privacy_route(From, StateData, - jid:remove_resource(From), To, Packet); - error -> - check_privacy_route(From, StateData, From, To, Packet); - probe -> - check_privacy_route(From, StateData, From, To, Packet); - _ -> - A = (?SETS):add_element(LTo, StateData#state.pres_a), - check_privacy_route(From, StateData#state{pres_a = A}, From, To, Packet) + Lang = StateData#state.lang, + case privacy_check_packet(StateData, From, To, Packet, out) of + deny -> + ErrText = <<"Your active privacy list has denied " + "the routing of this stanza.">>, + Err = xmpp:err_not_acceptable(ErrText, Lang), + send_error(StateData, xmpp:set_from_to(Packet, From, To), Err); + allow when Type == subscribe; Type == subscribed; + Type == unsubscribe; Type == unsubscribed -> + Access = gen_mod:get_module_opt(Server, mod_roster, access, + fun(A) when is_atom(A) -> A end, + all), + MyBareJID = jid:make(User, Server, <<"">>), + case acl:match_rule(Server, Access, MyBareJID) of + deny -> + ErrText = <<"Denied by ACL">>, + Err = xmpp:err_forbidden(ErrText, Lang), + send_error(StateData, xmpp:set_from_to(Packet, From, To), Err); + allow -> + ejabberd_hooks:run(roster_out_subscription, + Server, + [User, Server, To, Type]), + ejabberd_router:route(jid:remove_resource(From), To, Packet), + StateData + end; + allow when Type == error; Type == probe -> + ejabberd_router:route(From, To, Packet), + StateData; + allow -> + ejabberd_router:route(From, To, Packet), + A = case Type of + available -> + ?SETS:add_element(LTo, StateData#state.pres_a); + unavailable -> + ?SETS:del_element(LTo, StateData#state.pres_a) + end, + StateData#state{pres_a = A} end. -spec check_privacy_route(jid(), state(), jid(), jid(), stanza()) -> state(). check_privacy_route(From, StateData, FromRoute, To, Packet) -> case privacy_check_packet(StateData, From, To, Packet, - out) - of + out) of deny -> Lang = StateData#state.lang, ErrText = <<"Your active privacy list has denied " - "the routing of this stanza.">>, - Err = xmpp:make_error( - xmpp:set_from_to(Packet, From, To), - xmpp:err_not_acceptable(ErrText, Lang)), - send_stanza(StateData, Err); + "the routing of this stanza.">>, + Err = xmpp:err_not_acceptable(ErrText, Lang), + send_error(StateData, xmpp:set_from_to(Packet, From, To), Err); allow -> ejabberd_router:route(FromRoute, To, Packet), StateData @@ -1815,24 +1821,6 @@ is_privacy_allow(StateData, From, To, Packet, Dir) -> allow == privacy_check_packet(StateData, From, To, Packet, Dir). -%%% Check ACL before allowing to send a subscription stanza --spec try_roster_subscribe(subscribe | unsubscribe, binary(), binary(), - jid(), jid(), presence(), state()) -> state(). -try_roster_subscribe(Type, User, Server, From, To, Packet, StateData) -> - JID1 = jid:make(User, Server, <<"">>), - Access = gen_mod:get_module_opt(Server, mod_roster, access, fun(A) when is_atom(A) -> A end, all), - case acl:match_rule(Server, Access, JID1) of - deny -> - %% Silently drop this (un)subscription request - StateData; - allow -> - ejabberd_hooks:run(roster_out_subscription, - Server, - [User, Server, To, Type]), - check_privacy_route(From, StateData, jid:remove_resource(From), - To, Packet) - end. - %% Send presence when disconnecting -spec presence_broadcast(state(), jid(), ?SETS:set(), presence()) -> ok. presence_broadcast(StateData, From, JIDSet, Packet) -> @@ -1980,7 +1968,7 @@ process_privacy_iq(#iq{from = From, to = To, privacy_iq_set, StateData#state.server, {error, xmpp:err_feature_not_implemented(Txt, Lang)}, - [IQ]) + [IQ, StateData#state.privacy_list]) of {result, R, NewPrivList} -> {{result, R}, @@ -2522,9 +2510,8 @@ handle_unacked_stanzas(#state{mgmt_state = MgmtState} = StateData) false -> fun(From, To, El, _Time) -> Txt = <<"User session terminated">>, - Err = xmpp:make_error( - El, xmpp:err_service_unavailable(Txt, Lang)), - ejabberd_router:route(To, From, Err) + ejabberd_router:route_error( + To, From, El, xmpp:err_service_unavailable(Txt, Lang)) end end, F = fun(From, _To, #presence{}, _Time) -> @@ -2532,9 +2519,8 @@ handle_unacked_stanzas(#state{mgmt_state = MgmtState} = StateData) [jid:to_string(From)]); (From, To, #iq{} = El, _Time) -> Txt = <<"User session terminated">>, - Err = xmpp:make_error( - El, xmpp:err_service_unavailable(Txt, Lang)), - ejabberd_router:route(To, From, Err); + ejabberd_router:route_error( + To, From, El, xmpp:err_service_unavailable(Txt, Lang)); (From, To, El, Time) -> %% We'll drop the stanza if it was <forwarded/> by some %% encapsulating protocol as per XEP-0297. One such protocol is diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl index 5e6e1bf58..0e79c9913 100644 --- a/src/ejabberd_piefxis.erl +++ b/src/ejabberd_piefxis.erl @@ -486,7 +486,7 @@ process_privacy(#privacy_query{lists = Lists, from = JID, to = JID, sub_els = [PrivacyQuery]}, Txt = <<"No module is handling this query">>, Error = {error, xmpp:err_feature_not_implemented(Txt, ?MYLANG)}, - case mod_privacy:process_iq_set(Error, IQ) of + case mod_privacy:process_iq_set(Error, IQ, #userlist{}) of {error, #stanza_error{reason = Reason}} = Err -> if Reason == 'item-not-found', Lists == [], Active == undefined, Default /= undefined -> diff --git a/src/ejabberd_socket.erl b/src/ejabberd_socket.erl index f8dc84630..b5fa52ded 100644 --- a/src/ejabberd_socket.erl +++ b/src/ejabberd_socket.erl @@ -31,6 +31,7 @@ -export([start/4, connect/3, connect/4, + connect/5, starttls/2, starttls/3, compress/1, @@ -125,19 +126,21 @@ start(Module, SockMod, Socket, Opts) -> end. connect(Addr, Port, Opts) -> - connect(Addr, Port, Opts, infinity). + connect(Addr, Port, Opts, infinity, self()). connect(Addr, Port, Opts, Timeout) -> + connect(Addr, Port, Opts, Timeout, self()). + +connect(Addr, Port, Opts, Timeout, Owner) -> case gen_tcp:connect(Addr, Port, Opts, Timeout) of {ok, Socket} -> Receiver = ejabberd_receiver:start(Socket, gen_tcp, none), SocketData = #socket_state{sockmod = gen_tcp, socket = Socket, receiver = Receiver}, - Pid = self(), case gen_tcp:controlling_process(Socket, Receiver) of ok -> - ejabberd_receiver:become_controller(Receiver, Pid), + ejabberd_receiver:become_controller(Receiver, Owner), {ok, SocketData}; {error, _Reason} = Error -> gen_tcp:close(Socket), Error end; diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 627b5b58f..4598805c2 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -53,6 +53,7 @@ -include("ejabberd.hrl"). -include("ejabberd_commands.hrl"). -include("mod_roster.hrl"). +-include("mod_privacy.hrl"). -include("ejabberd_sm.hrl"). -include("xmpp.hrl"). @@ -1380,11 +1381,12 @@ privacy_set(Username, Host, QueryS) -> To = jid:make(Host), QueryEl = fxml_stream:parse_element(QueryS), SubEl = xmpp:decode(QueryEl), - IQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl]}, + IQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl], + from = From, to = To}, ejabberd_hooks:run_fold(privacy_iq_set, Host, {error, xmpp:err_feature_not_implemented()}, - [From, To, IQ]), + [IQ, #userlist{}]), ok. %%% diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl index b3bbff96e..d2b187d26 100644 --- a/src/mod_blocking.erl +++ b/src/mod_blocking.erl @@ -30,7 +30,7 @@ -protocol({xep, 191, '1.2'}). -export([start/2, stop/1, process_iq/1, - process_iq_set/2, process_iq_get/3, mod_opt_type/1, depends/2]). + process_iq_set/3, process_iq_get/3, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -85,10 +85,11 @@ process_iq_get(Acc, _, _) -> Acc. -spec process_iq_set({error, stanza_error()} | {result, xmpp_element() | undefined} | {result, xmpp_element() | undefined, userlist()}, - iq()) -> {error, stanza_error()} | - {result, xmpp_element() | undefined} | - {result, xmpp_element() | undefined, userlist()}. -process_iq_set(Acc, #iq{from = From, lang = Lang, sub_els = [SubEl]}) -> + iq(), userlist()) -> + {error, stanza_error()} | + {result, xmpp_element() | undefined} | + {result, xmpp_element() | undefined, userlist()}. +process_iq_set(Acc, #iq{from = From, lang = Lang, sub_els = [SubEl]}, _) -> #jid{luser = LUser, lserver = LServer} = From, case SubEl of #block{items = []} -> @@ -105,7 +106,7 @@ process_iq_set(Acc, #iq{from = From, lang = Lang, sub_els = [SubEl]}) -> _ -> Acc end; -process_iq_set(Acc, _) -> Acc. +process_iq_set(Acc, _, _) -> Acc. -spec list_to_blocklist_jids([listitem()], [ljid()]) -> [ljid()]. list_to_blocklist_jids([], JIDs) -> JIDs; diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl index 2f318deec..d4c8464f1 100644 --- a/src/mod_privacy.erl +++ b/src/mod_privacy.erl @@ -32,7 +32,7 @@ -behaviour(gen_mod). -export([start/2, stop/1, process_iq/1, export/1, import/1, - process_iq_set/2, process_iq_get/3, get_user_list/3, + process_iq_set/3, process_iq_get/3, get_user_list/3, check_packet/6, remove_user/2, encode_list_item/1, is_list_needdb/1, updated_list/3, item_to_xml/1, get_user_lists/2, import/3, @@ -103,6 +103,12 @@ process_iq(IQ) -> -spec process_iq_get({error, stanza_error()} | {result, xmpp_element() | undefined}, iq(), userlist()) -> {error, stanza_error()} | {result, xmpp_element() | undefined}. +process_iq_get(_, #iq{lang = Lang, + sub_els = [#privacy_query{default = Default, + active = Active}]}, + _) when Default /= undefined; Active /= undefined -> + Txt = <<"Only <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 -> diff --git a/src/mod_roster.erl b/src/mod_roster.erl index feebd3945..423fe9e0e 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -49,7 +49,7 @@ get_jid_info/4, encode_item/1, webadmin_page/3, webadmin_user/4, get_versioning_feature/2, roster_versioning_enabled/1, roster_version/2, - mod_opt_type/1, set_roster/1, depends/2]). + mod_opt_type/1, set_roster/1, del_roster/3, depends/2]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -297,6 +297,13 @@ set_roster(#roster{us = {LUser, LServer}, jid = LJID} = Item) -> roster_subscribe_t(LUser, LServer, LJID, Item) end). +del_roster(LUser, LServer, LJID) -> + transaction( + LServer, + fun() -> + del_roster_t(LUser, LServer, LJID) + end). + encode_item(Item) -> #roster_item{jid = jid:make(Item#roster.jid), name = Item#roster.name, |