diff options
author | Evgeniy Khramtsov <ekhramtsov@process-one.net> | 2016-11-07 10:10:57 +0300 |
---|---|---|
committer | Evgeniy Khramtsov <ekhramtsov@process-one.net> | 2016-11-07 10:10:57 +0300 |
commit | 56c91d3c58ec5461600f70d2d0413c846767f882 (patch) | |
tree | fae4ac81fe1b3556c059f69deff4209f0591add7 /src | |
parent | Use base64:mime_decode/1 for SASL packets (diff) |
Add roster tests
Diffstat (limited to 'src')
-rw-r--r-- | src/ejabberd_sm.erl | 213 | ||||
-rw-r--r-- | src/mod_roster.erl | 96 |
2 files changed, 150 insertions, 159 deletions
diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index f6d0e765d..6f6a196e5 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -442,135 +442,96 @@ online(Sessions) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec do_route(jid(), jid(), stanza() | broadcast()) -> any(). +do_route(From, #jid{lresource = <<"">>} = To, {broadcast, _} = Packet) -> + ?DEBUG("processing broadcast to bare JID: ~p", [Packet]), + lists:foreach( + fun(R) -> + do_route(From, jid:replace_resource(To, R), Packet) + end, get_user_resources(To#jid.user, To#jid.server)); do_route(From, To, {broadcast, _} = Packet) -> - case To#jid.lresource of - <<"">> -> - lists:foreach(fun(R) -> - do_route(From, - jid:replace_resource(To, R), - Packet) - end, - get_user_resources(To#jid.user, To#jid.server)); - _ -> - {U, S, R} = jid:tolower(To), - Mod = get_sm_backend(S), - case online(Mod:get_sessions(U, S, R)) of - [] -> - ?DEBUG("packet dropped~n", []); - Ss -> - Session = lists:max(Ss), - Pid = element(2, Session#session.sid), - ?DEBUG("sending to process ~p~n", [Pid]), - Pid ! {route, From, To, Packet} - end + ?DEBUG("processing broadcast to full JID: ~p", [Packet]), + {U, S, R} = jid:tolower(To), + Mod = get_sm_backend(S), + case online(Mod:get_sessions(U, S, R)) of + [] -> + ?DEBUG("dropping broadcast to unavailable resourse: ~p", [Packet]); + Ss -> + Session = lists:max(Ss), + Pid = element(2, Session#session.sid), + ?DEBUG("sending to process ~p: ~p", [Pid, Packet]), + Pid ! {route, From, To, Packet} end; -do_route(From, To, Packet) -> - ?DEBUG("session manager~n\tfrom ~p~n\tto ~p~n\tpacket " - "~P~n", - [From, To, Packet, 8]), +do_route(From, To, #presence{type = T, status = Status} = Packet) + when T == subscribe; T == subscribed; T == unsubscribe; T == unsubscribed -> + ?DEBUG("processing subscription:~n~s", [xmpp:pp(Packet)]), #jid{user = User, server = Server, - luser = LUser, lserver = LServer, lresource = LResource} = To, - Lang = xmpp:get_lang(Packet), - case LResource of - <<"">> -> - case Packet of - #presence{type = T, status = Status} -> - {Pass, _Subsc} = case T of - subscribe -> - Reason = xmpp:get_text(Status), - {is_privacy_allow(From, To, Packet) - andalso - ejabberd_hooks:run_fold(roster_in_subscription, - LServer, - false, - [User, Server, - From, - subscribe, - Reason]), - true}; - subscribed -> - {is_privacy_allow(From, To, Packet) - andalso - ejabberd_hooks:run_fold(roster_in_subscription, - LServer, - false, - [User, Server, - From, - subscribed, - <<"">>]), - true}; - unsubscribe -> - {is_privacy_allow(From, To, Packet) - andalso - ejabberd_hooks:run_fold(roster_in_subscription, - LServer, - false, - [User, Server, - From, - unsubscribe, - <<"">>]), - true}; - unsubscribed -> - {is_privacy_allow(From, To, Packet) - andalso - ejabberd_hooks:run_fold(roster_in_subscription, - LServer, - false, - [User, Server, - From, - unsubscribed, - <<"">>]), - true}; - _ -> {true, false} - end, - if Pass -> - PResources = get_user_present_resources(LUser, LServer), - lists:foreach(fun ({_, R}) -> - do_route(From, - jid:replace_resource(To, - R), - Packet) - end, - PResources); - true -> ok - end; - #message{type = T} when T == chat; T == headline; T == normal -> - route_message(From, To, Packet, T); - #message{type = groupchat} -> - ErrTxt = <<"User session not found">>, - Err = xmpp:make_error( - Packet, xmpp:err_service_unavailable(ErrTxt, Lang)), - ejabberd_router:route(To, From, Err); - #iq{} -> process_iq(From, To, Packet); - _ -> ok - end; - _ -> - Mod = get_sm_backend(LServer), - case online(Mod:get_sessions(LUser, LServer, LResource)) of - [] -> - case Packet of - #message{type = T} when T == chat; T == normal -> - route_message(From, To, Packet, T); - #message{type = groupchat} -> - ErrTxt = <<"User session not found">>, - Err = xmpp:make_error( - Packet, - xmpp:err_service_unavailable(ErrTxt, Lang)), - ejabberd_router:route(To, From, Err); - #iq{type = T} when T == get; T == set -> - ErrTxt = <<"User session not found">>, - Err = xmpp:make_error( - Packet, - xmpp:err_service_unavailable(ErrTxt, Lang)), - ejabberd_router:route(To, From, Err); - _ -> ?DEBUG("packet dropped~n", []) - end; - Ss -> - Session = lists:max(Ss), - Pid = element(2, Session#session.sid), - ?DEBUG("sending to process ~p~n", [Pid]), - Pid ! {route, From, To, Packet} - end + luser = LUser, lserver = LServer} = To, + Reason = if T == subscribe -> xmpp:get_text(Status); + true -> <<"">> + end, + case is_privacy_allow(From, To, Packet) andalso + ejabberd_hooks:run_fold( + roster_in_subscription, + LServer, false, + [User, Server, From, T, Reason]) of + true -> + Mod = get_sm_backend(LServer), + lists:foreach( + fun(#session{sid = SID, usr = {_, _, R}, + priority = Prio}) when is_integer(Prio) -> + Pid = element(2, SID), + ?DEBUG("sending to process ~p:~n~s", + [Pid, xmpp:pp(Packet)]), + Pid ! {route, From, jid:replace_resource(To, R), Packet}; + (_) -> + ok + end, online(Mod:get_sessions(LUser, LServer))); + false -> + ok + end; +do_route(From, #jid{lresource = <<"">>} = To, #presence{} = Packet) -> + ?DEBUG("processing presence to bare JID:~n~s", [xmpp:pp(Packet)]), + {LUser, LServer, _} = jid:tolower(To), + lists:foreach( + fun({_, R}) -> + do_route(From, jid:replace_resource(To, R), Packet) + end, get_user_present_resources(LUser, LServer)); +do_route(From, #jid{lresource = <<"">>} = To, #message{type = T} = Packet) -> + ?DEBUG("processing message to bare JID:~n~s", [xmpp:pp(Packet)]), + if T == chat; T == headline; T == normal -> + route_message(From, To, Packet, T); + true -> + Lang = xmpp:get_lang(Packet), + ErrTxt = <<"User session not found">>, + Err = xmpp:err_service_unavailable(ErrTxt, Lang), + ejabberd_router:route_error(To, From, Packet, Err) + end; +do_route(From, #jid{lresource = <<"">>} = To, #iq{} = Packet) -> + ?DEBUG("processing IQ to bare JID:~n~s", [xmpp:pp(Packet)]), + process_iq(From, To, Packet); +do_route(From, To, Packet) -> + ?DEBUG("processing packet to full JID:~n~s", [xmpp:pp(Packet)]), + {LUser, LServer, LResource} = jid:tolower(To), + Mod = get_sm_backend(LServer), + case online(Mod:get_sessions(LUser, LServer, LResource)) of + [] -> + case Packet of + #message{type = T} when T == chat; T == normal -> + route_message(From, To, Packet, T); + #presence{} -> + ?DEBUG("dropping presence to unavalable resource:~n~s", + [xmpp:pp(Packet)]); + _ -> + Lang = xmpp:get_lang(Packet), + ErrTxt = <<"User session not found">>, + Err = xmpp:err_service_unavailable(ErrTxt, Lang), + ejabberd_router:route_error(To, From, Packet, Err) + end; + Ss -> + Session = lists:max(Ss), + Pid = element(2, Session#session.sid), + ?DEBUG("sending to process ~p:~n~s", [Pid, xmpp:pp(Packet)]), + Pid ! {route, From, To, Packet} end. %% The default list applies to the user as a whole, diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 423fe9e0e..fa27f866c 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -142,21 +142,54 @@ depends(_Host, _Opts) -> process_iq(#iq{from = #jid{luser = <<"">>}, to = #jid{resource = <<"">>}} = IQ) -> process_iq_manager(IQ); - -process_iq(#iq{from = From, lang = Lang} = IQ) -> - #jid{lserver = LServer} = From, - case lists:member(LServer, ?MYHOSTS) of - true -> process_local_iq(IQ); - _ -> - Txt = <<"The query is only allowed from local users">>, - xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang)) - end. - -process_local_iq(#iq{type = Type} = IQ) -> - case Type of - set -> try_process_iq_set(IQ); - get -> process_iq_get(IQ) - end. +process_iq(#iq{from = #jid{luser = U, lserver = S}, + to = #jid{luser = U, lserver = S}} = IQ) -> + process_local_iq(IQ); +process_iq(#iq{lang = Lang} = IQ) -> + Txt = <<"Query to another users is forbidden">>, + xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)). + +process_local_iq(#iq{type = set,lang = Lang, + sub_els = [#roster_query{ + items = [#roster_item{ask = Ask}]}]} = IQ) + when Ask /= undefined -> + Txt = <<"Possessing 'ask' attribute is not allowed by RFC6121">>, + xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); +process_local_iq(#iq{type = set, from = From, lang = Lang, + sub_els = [#roster_query{ + items = [#roster_item{} = Item]}]} = IQ) -> + case has_duplicated_groups(Item#roster_item.groups) of + true -> + Txt = <<"Duplicated groups are not allowed by RFC6121">>, + xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); + false -> + #jid{server = Server} = From, + Access = gen_mod:get_module_opt(Server, ?MODULE, + access, fun(A) -> A end, all), + case acl:match_rule(Server, Access, From) of + deny -> + Txt = <<"Denied by ACL">>, + xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); + allow -> + process_iq_set(IQ) + end + end; +process_local_iq(#iq{type = set, lang = Lang, + sub_els = [#roster_query{items = [_|_]}]} = IQ) -> + Txt = <<"Multiple <item/> elements are not allowed by RFC6121">>, + xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); +process_local_iq(#iq{type = get, lang = Lang, + sub_els = [#roster_query{items = Items}]} = IQ) -> + case Items of + [] -> + process_iq_get(IQ); + [_|_] -> + Txt = <<"The query must not contain <item/> elements">>, + xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)) + end; +process_local_iq(#iq{lang = Lang} = IQ) -> + Txt = <<"No module is handling this query">>, + xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). roster_hash(Items) -> p1_sha:sha(term_to_binary(lists:sort([R#roster{groups = @@ -315,11 +348,18 @@ encode_item(Item) -> end, groups = Item#roster.groups}. +decode_item(#roster_item{subscription = remove} = Item, R, _) -> + R#roster{jid = jid:tolower(Item#roster_item.jid), + name = <<"">>, + subscription = remove, + ask = none, + groups = [], + askmessage = <<"">>, + xs = []}; decode_item(Item, R, Managed) -> R#roster{jid = jid:tolower(Item#roster_item.jid), name = Item#roster_item.name, subscription = case Item#roster_item.subscription of - remove -> remove; Sub when Managed -> Sub; _ -> R#roster.subscription end, @@ -329,17 +369,6 @@ get_roster_by_jid_t(LUser, LServer, LJID) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:get_roster_by_jid(LUser, LServer, LJID). -try_process_iq_set(#iq{from = From, lang = Lang} = IQ) -> - #jid{server = Server} = From, - Access = gen_mod:get_module_opt(Server, ?MODULE, access, fun(A) -> A end, all), - case acl:match_rule(Server, Access, From) of - deny -> - Txt = <<"Denied by ACL">>, - xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); - allow -> - process_iq_set(IQ) - end. - process_iq_set(#iq{from = From, to = To, id = Id, sub_els = [#roster_query{items = QueryItems}]} = IQ) -> Managed = is_managed_from_id(Id), @@ -515,8 +544,7 @@ process_subscription(Direction, User, Server, JID1, {Subscription, Pending} -> NewItem = Item#roster{subscription = Subscription, ask = Pending, - askmessage = - iolist_to_binary(AskMessage)}, + askmessage = AskMessage}, roster_subscribe_t(LUser, LServer, LJID, NewItem), case roster_version_on_db(LServer) of true -> write_roster_version_t(LUser, LServer); @@ -730,10 +758,8 @@ del_roster_t(LUser, LServer, LJID) -> Mod:del_roster(LUser, LServer, LJID). process_item_set_t(LUser, LServer, #roster_item{jid = JID1} = QueryItem) -> - JID = {JID1#jid.user, JID1#jid.server, - JID1#jid.resource}, - LJID = {JID1#jid.luser, JID1#jid.lserver, - JID1#jid.lresource}, + JID = {JID1#jid.user, JID1#jid.server, <<>>}, + LJID = {JID1#jid.luser, JID1#jid.lserver, <<>>}, Item = #roster{usj = {LUser, LServer, LJID}, us = {LUser, LServer}, jid = JID}, Item2 = decode_item(QueryItem, Item, _Managed = true), @@ -1046,6 +1072,10 @@ is_managed_from_id(<<"roster-remotely-managed">>) -> is_managed_from_id(_Id) -> false. +has_duplicated_groups(Groups) -> + GroupsPrep = lists:usort([jid:resourceprep(G) || G <- Groups]), + not (length(GroupsPrep) == length(Groups)). + export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:export(LServer). |