aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ejabberd_sm.erl213
-rw-r--r--src/mod_roster.erl96
-rw-r--r--src/xmpp_codec.erl8
3 files changed, 154 insertions, 163 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).
diff --git a/src/xmpp_codec.erl b/src/xmpp_codec.erl
index f230dc489..345de7031 100644
--- a/src/xmpp_codec.erl
+++ b/src/xmpp_codec.erl
@@ -29298,7 +29298,7 @@ encode_sasl_success({sasl_success, Text}, __TopXMLNS) ->
decode_sasl_success_cdata(__TopXMLNS, <<>>) -> <<>>;
decode_sasl_success_cdata(__TopXMLNS, _val) ->
- case catch base64:decode(_val) of
+ case catch base64:mime_decode(_val) of
{'EXIT', _} ->
erlang:error({xmpp_codec,
{bad_cdata_value, <<>>, <<"success">>, __TopXMLNS}});
@@ -29338,7 +29338,7 @@ encode_sasl_response({sasl_response, Text},
decode_sasl_response_cdata(__TopXMLNS, <<>>) -> <<>>;
decode_sasl_response_cdata(__TopXMLNS, _val) ->
- case catch base64:decode(_val) of
+ case catch base64:mime_decode(_val) of
{'EXIT', _} ->
erlang:error({xmpp_codec,
{bad_cdata_value, <<>>, <<"response">>, __TopXMLNS}});
@@ -29378,7 +29378,7 @@ encode_sasl_challenge({sasl_challenge, Text},
decode_sasl_challenge_cdata(__TopXMLNS, <<>>) -> <<>>;
decode_sasl_challenge_cdata(__TopXMLNS, _val) ->
- case catch base64:decode(_val) of
+ case catch base64:mime_decode(_val) of
{'EXIT', _} ->
erlang:error({xmpp_codec,
{bad_cdata_value, <<>>, <<"challenge">>, __TopXMLNS}});
@@ -29454,7 +29454,7 @@ encode_sasl_auth_attr_mechanism(_val, _acc) ->
decode_sasl_auth_cdata(__TopXMLNS, <<>>) -> <<>>;
decode_sasl_auth_cdata(__TopXMLNS, _val) ->
- case catch base64:decode(_val) of
+ case catch base64:mime_decode(_val) of
{'EXIT', _} ->
erlang:error({xmpp_codec,
{bad_cdata_value, <<>>, <<"auth">>, __TopXMLNS}});