Rewrite multicast code to use XML generator

This commit is contained in:
Evgeniy Khramtsov 2016-08-04 11:49:17 +03:00
parent abb4446b51
commit bc33a3873d
2 changed files with 202 additions and 304 deletions

View File

@ -43,9 +43,10 @@
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-include("logger.hrl"). -include("logger.hrl").
-include("jlib.hrl"). -include("xmpp.hrl").
-record(route_multicast, {domain, pid}). -record(route_multicast, {domain = <<"">> :: binary(),
pid = self() :: pid()}).
-record(state, {}). -record(state, {}).
%%==================================================================== %%====================================================================
@ -58,7 +59,7 @@
start_link() -> start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
-spec route_multicast(jid(), binary(), [jid()], stanza()) -> ok.
route_multicast(From, Domain, Destinations, Packet) -> route_multicast(From, Domain, Destinations, Packet) ->
case catch do_route(From, Domain, Destinations, Packet) of case catch do_route(From, Domain, Destinations, Packet) of
{'EXIT', Reason} -> {'EXIT', Reason} ->
@ -68,6 +69,7 @@ route_multicast(From, Domain, Destinations, Packet) ->
ok ok
end. end.
-spec register_route(binary()) -> any().
register_route(Domain) -> register_route(Domain) ->
case jid:nameprep(Domain) of case jid:nameprep(Domain) of
error -> error ->
@ -81,6 +83,7 @@ register_route(Domain) ->
mnesia:transaction(F) mnesia:transaction(F)
end. end.
-spec unregister_route(binary()) -> any().
unregister_route(Domain) -> unregister_route(Domain) ->
case jid:nameprep(Domain) of case jid:nameprep(Domain) of
error -> error ->
@ -206,6 +209,7 @@ code_change(_OldVsn, State, _Extra) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% From = #jid %% From = #jid
%% Destinations = [#jid] %% Destinations = [#jid]
-spec do_route(jid(), binary(), [jid()], stanza()) -> any().
do_route(From, Domain, Destinations, Packet) -> do_route(From, Domain, Destinations, Packet) ->
?DEBUG("route_multicast~n\tfrom ~s~n\tdomain ~s~n\tdestinations ~p~n\tpacket ~p~n", ?DEBUG("route_multicast~n\tfrom ~s~n\tdomain ~s~n\tdestinations ~p~n\tpacket ~p~n",
@ -226,6 +230,7 @@ do_route(From, Domain, Destinations, Packet) ->
Pid ! {route_trusted, From, Destinations, Packet} Pid ! {route_trusted, From, Destinations, Packet}
end. end.
-spec pick_multicast_pid([#route_multicast{}]) -> pid().
pick_multicast_pid(Rs) -> pick_multicast_pid(Rs) ->
List = case [R || R <- Rs, node(R#route_multicast.pid) == node()] of List = case [R || R <- Rs, node(R#route_multicast.pid) == node()] of
[] -> Rs; [] -> Rs;
@ -233,5 +238,6 @@ pick_multicast_pid(Rs) ->
end, end,
(hd(List))#route_multicast.pid. (hd(List))#route_multicast.pid.
-spec do_route_normal(jid(), [jid()], stanza()) -> any().
do_route_normal(From, Destinations, Packet) -> do_route_normal(From, Destinations, Packet) ->
[ejabberd_router:route(From, To, Packet) || To <- Destinations]. [ejabberd_router:route(From, To, Packet) || To <- Destinations].

View File

@ -45,16 +45,20 @@
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-include("logger.hrl"). -include("logger.hrl").
-include("jlib.hrl"). -include("xmpp.hrl").
-record(state, -record(state,
{lserver, lservice, access, service_limits}). {lserver, lservice, access, service_limits}).
-type state() :: #state{}.
-record(multicastc, {rserver, response, ts}). -record(multicastc, {rserver, response, ts}).
%% ts: timestamp (in seconds) when the cache item was last updated %% ts: timestamp (in seconds) when the cache item was last updated
-record(dest, {jid_string, jid_jid, type, full_xml}). -record(dest, {jid_string = none :: binary(),
jid_jid :: jid(),
type :: atom(),
full_xml :: address()}).
%% jid_string = string() %% jid_string = string()
%% jid_jid = jid() %% jid_jid = jid()
@ -168,10 +172,8 @@ handle_cast(_Msg, State) -> {noreply, State}.
%% Description: Handling all non call/cast messages %% Description: Handling all non call/cast messages
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
handle_info({route, From, To, handle_info({route, From, To, #iq{} = Packet}, State) ->
#xmlel{name = <<"iq">>, attrs = Attrs} = Packet}, case catch handle_iq(From, To, Packet, State) of
State) ->
case catch handle_iq(From, To, #xmlel{attrs = Attrs} = Packet, State) of
{'EXIT', Reason} -> {'EXIT', Reason} ->
?ERROR_MSG("Error when processing IQ stanza: ~p", ?ERROR_MSG("Error when processing IQ stanza: ~p",
[Reason]); [Reason]);
@ -179,13 +181,10 @@ handle_info({route, From, To,
end, end,
{noreply, State}; {noreply, State};
%% XEP33 allows only 'message' and 'presence' stanza type %% XEP33 allows only 'message' and 'presence' stanza type
handle_info({route, From, To, handle_info({route, From, To, Packet},
#xmlel{name = Stanza_type} = Packet},
#state{lservice = LServiceS, lserver = LServerS, #state{lservice = LServiceS, lserver = LServerS,
access = Access, service_limits = SLimits} = access = Access, service_limits = SLimits} =
State) State) when ?is_stanza(Packet) ->
when (Stanza_type == <<"message">>) or
(Stanza_type == <<"presence">>) ->
route_untrusted(LServiceS, LServerS, Access, SLimits, route_untrusted(LServiceS, LServerS, Access, SLimits,
From, To, Packet), From, To, Packet),
{noreply, State}; {noreply, State};
@ -220,91 +219,59 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
%%% IQ Request Processing %%% IQ Request Processing
%%%------------------------ %%%------------------------
handle_iq(From, To, #xmlel{attrs = Attrs} = Packet, State) -> handle_iq(From, To, Packet, State) ->
IQ = jlib:iq_query_info(Packet), try
case catch process_iq(From, IQ, State) of IQ = xmpp:decode_els(Packet),
Result when is_record(Result, iq) -> case process_iq(From, IQ, State) of
ejabberd_router:route(To, From, jlib:iq_to_xml(Result)); {result, SubEl} ->
{'EXIT', Reason} -> ejabberd_router:route(To, From, xmpp:make_iq_result(Packet, SubEl));
?ERROR_MSG("Error when processing IQ stanza: ~p", {error, Error} ->
[Reason]), ejabberd_router:route_error(To, From, Packet, Error);
Err = jlib:make_error_reply(Packet, reply ->
?ERR_INTERNAL_SERVER_ERROR), LServiceS = jid:to_string(To),
ejabberd_router:route(To, From, Err); case Packet#iq.type of
reply -> result ->
LServiceS = jts(To), process_iqreply_result(From, LServiceS, IQ);
case fxml:get_attr_s(<<"type">>, Attrs) of error ->
<<"result">> -> process_iqreply_error(From, LServiceS, IQ)
process_iqreply_result(From, LServiceS, Packet, State); end
<<"error">> -> end
process_iqreply_error(From, LServiceS, Packet) catch _:{xmpp_codec, Why} ->
end; Lang = xmpp:get_lang(Packet),
ok -> ok Err = xmpp:err_bad_request(xmpp:format_error(Why), Lang),
ejabberd_router:route_error(To, From, Packet, Err)
end. end.
process_iq(From, -spec process_iq(jid(), iq(), state()) -> {result, xmpp_element()} |
#iq{type = get, xmlns = ?NS_DISCO_INFO, lang = Lang} = {error, error()} | reply.
IQ, process_iq(From, #iq{type = get, lang = Lang,
State) -> sub_els = [#disco_info{}]}, State) ->
IQ#iq{type = result, {result, iq_disco_info(From, Lang, State)};
sub_el = process_iq(_, #iq{type = get, sub_els = [#disco_items{}]}, _) ->
[#xmlel{name = <<"query">>, {result, #disco_items{}};
attrs = [{<<"xmlns">>, ?NS_DISCO_INFO}], process_iq(_, #iq{type = get, lang = Lang, sub_els = [#vcard_temp{}]}, _) ->
children = iq_disco_info(From, Lang, State)}]}; {result, iq_vcard(Lang)};
%% disco#items request process_iq(_, #iq{type = T}, _) when T == set; T == get ->
process_iq(_, {error, xmpp:err_service_unavailable()};
#iq{type = get, xmlns = ?NS_DISCO_ITEMS} = IQ, _) -> process_iq(_, _, _) ->
IQ#iq{type = result, reply.
sub_el =
[#xmlel{name = <<"query">>,
attrs = [{<<"xmlns">>, ?NS_DISCO_ITEMS}],
children = []}]};
%% vCard request
process_iq(_,
#iq{type = get, xmlns = ?NS_VCARD, lang = Lang} = IQ,
_) ->
IQ#iq{type = result,
sub_el =
[#xmlel{name = <<"vCard">>,
attrs = [{<<"xmlns">>, ?NS_VCARD}],
children = iq_vcard(Lang)}]};
%% Unknown "set" or "get" request
process_iq(_, #iq{type = Type, sub_el = SubEl} = IQ, _)
when Type == get; Type == set ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]};
%% IQ "result" or "error".
process_iq(_, reply, _) -> reply;
%% IQ "result" or "error".
process_iq(_, _, _) -> ok.
-define(FEATURE(Feat), -define(FEATURE(Feat), Feat).
#xmlel{name = <<"feature">>,
attrs = [{<<"var">>, Feat}], children = []}).
iq_disco_info(From, Lang, State) -> iq_disco_info(From, Lang, State) ->
[#xmlel{name = <<"identity">>, #disco_info{
attrs = identities = [#identity{category = <<"service">>,
[{<<"category">>, <<"service">>}, type = <<"multicast">>,
{<<"type">>, <<"multicast">>}, name = translate:translate(Lang, <<"Multicast">>)}],
{<<"name">>, features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS, ?NS_VCARD, ?NS_ADDRESS],
translate:translate(Lang, <<"Multicast">>)}], xdata = iq_disco_info_extras(From, State)}.
children = []},
?FEATURE((?NS_DISCO_INFO)), ?FEATURE((?NS_DISCO_ITEMS)),
?FEATURE((?NS_VCARD)), ?FEATURE((?NS_ADDRESS))]
++ iq_disco_info_extras(From, State).
iq_vcard(Lang) -> iq_vcard(Lang) ->
[#xmlel{name = <<"FN">>, attrs = [], Desc = translate:translate(Lang, <<"ejabberd Multicast service">>),
children = [{xmlcdata, <<"ejabberd/mod_multicast">>}]}, Copyright = <<"Copyright (c) 2002-2016 ProcessOne">>,
#xmlel{name = <<"URL">>, attrs = [], #vcard_temp{fn = <<"ejabberd/mod_multicast">>,
children = [{xmlcdata, ?EJABBERD_URI}]}, url = ?EJABBERD_URI,
#xmlel{name = <<"DESC">>, attrs = [], desc = <<Desc/binary, $\n, Copyright/binary>>}.
children =
[{xmlcdata,
<<(translate:translate(Lang,
<<"ejabberd Multicast service">>))/binary,
"\nCopyright (c) 2002-2016 ProcessOne">>}]}].
%%%------------------------- %%%-------------------------
%%% Route %%% Route
@ -313,19 +280,14 @@ iq_vcard(Lang) ->
route_trusted(LServiceS, LServerS, FromJID, route_trusted(LServiceS, LServerS, FromJID,
Destinations, Packet) -> Destinations, Packet) ->
Packet_stripped = Packet, Packet_stripped = Packet,
AAttrs = [{<<"xmlns">>, ?NS_ADDRESS}], AAttrs = [],
Delivereds = [], Delivereds = [],
Dests2 = lists:map(fun (D) -> Dests2 = lists:map(
DS = jts(D), fun(D) ->
XML = #xmlel{name = <<"address">>, #dest{jid_string = jid:to_string(D),
attrs = jid_jid = D, type = bcc,
[{<<"type">>, <<"bcc">>}, full_xml = #address{type = bcc, jid = D}}
{<<"jid">>, DS}], end, Destinations),
children = []},
#dest{jid_string = DS, jid_jid = D,
type = <<"bcc">>, full_xml = XML}
end,
Destinations),
Groups = group_dests(Dests2), Groups = group_dests(Dests2),
route_common(LServerS, LServiceS, FromJID, Groups, route_common(LServerS, LServiceS, FromJID, Groups,
Delivereds, Packet_stripped, AAttrs). Delivereds, Packet_stripped, AAttrs).
@ -363,20 +325,19 @@ route_untrusted(LServiceS, LServerS, Access, SLimits,
route_untrusted2(LServiceS, LServerS, Access, SLimits, route_untrusted2(LServiceS, LServerS, Access, SLimits,
FromJID, Packet) -> FromJID, Packet) ->
ok = check_access(LServerS, Access, FromJID), ok = check_access(LServerS, Access, FromJID),
{ok, Packet_stripped, AAttrs, Addresses} = {ok, Packet_stripped, Addresses} = strip_addresses_element(Packet),
strip_addresses_element(Packet), {To_deliver, Delivereds} = split_addresses_todeliver(Addresses),
{To_deliver, Delivereds} =
split_addresses_todeliver(Addresses),
Dests = convert_dest_record(To_deliver), Dests = convert_dest_record(To_deliver),
{Dests2, Not_jids} = split_dests_jid(Dests), {Dests2, Not_jids} = split_dests_jid(Dests),
report_not_jid(FromJID, Packet, Not_jids), report_not_jid(FromJID, Packet, Not_jids),
ok = check_limit_dests(SLimits, FromJID, Packet, ok = check_limit_dests(SLimits, FromJID, Packet, Dests2),
Dests2),
Groups = group_dests(Dests2), Groups = group_dests(Dests2),
ok = check_relay(FromJID#jid.server, LServerS, Groups), ok = check_relay(FromJID#jid.server, LServerS, Groups),
route_common(LServerS, LServiceS, FromJID, Groups, route_common(LServerS, LServiceS, FromJID, Groups,
Delivereds, Packet_stripped, AAttrs). Delivereds, Packet_stripped, []).
-spec route_common(binary(), binary(), jid(), [#group{}],
[address()], stanza(), list()) -> any().
route_common(LServerS, LServiceS, FromJID, Groups, route_common(LServerS, LServiceS, FromJID, Groups,
Delivereds, Packet_stripped, AAttrs) -> Delivereds, Packet_stripped, AAttrs) ->
Groups2 = look_cached_servers(LServerS, Groups), Groups2 = look_cached_servers(LServerS, Groups),
@ -435,52 +396,39 @@ check_access(LServerS, Access, From) ->
%%% Strip 'addresses' XML element %%% Strip 'addresses' XML element
%%%------------------------- %%%-------------------------
-spec strip_addresses_element(stanza()) -> {ok, stanza(), [address()]}.
strip_addresses_element(Packet) -> strip_addresses_element(Packet) ->
case fxml:get_subtag(Packet, <<"addresses">>) of case xmpp:get_subtag(Packet, #addresses{}) of
#xmlel{name = <<"addresses">>, attrs = AAttrs, #addresses{list = Addrs} ->
children = Addresses} -> PacketStripped = xmpp:remove_subtag(Packet, #addresses{}),
case fxml:get_attr_s(<<"xmlns">>, AAttrs) of {ok, PacketStripped, Addrs};
?NS_ADDRESS -> undefined ->
#xmlel{name = Name, attrs = Attrs, children = Els} = throw(eadsele)
Packet,
Els_stripped = lists:keydelete(<<"addresses">>, 2, Els),
Packet_stripped = #xmlel{name = Name, attrs = Attrs,
children = Els_stripped},
{ok, Packet_stripped, AAttrs, fxml:remove_cdata(Addresses)};
_ -> throw(ewxmlns)
end;
_ -> throw(eadsele)
end. end.
%%%------------------------- %%%-------------------------
%%% Split Addresses %%% Split Addresses
%%%------------------------- %%%-------------------------
-spec split_addresses_todeliver([address()]) -> {[address()], [address()]}.
split_addresses_todeliver(Addresses) -> split_addresses_todeliver(Addresses) ->
lists:partition(fun (XML) -> lists:partition(
case XML of fun(#address{delivered = true}) ->
#xmlel{name = <<"address">>, attrs = Attrs} -> false;
case fxml:get_attr_s(<<"delivered">>, Attrs) of (#address{type = Type}) ->
<<"true">> -> false; case Type of
_ -> to -> true;
Type = fxml:get_attr_s(<<"type">>, cc -> true;
Attrs), bcc -> true;
case Type of _ -> false
<<"to">> -> true; end
<<"cc">> -> true; end, Addresses).
<<"bcc">> -> true;
_ -> false
end
end;
_ -> false
end
end,
Addresses).
%%%------------------------- %%%-------------------------
%%% Check does not exceed limit of destinations %%% Check does not exceed limit of destinations
%%%------------------------- %%%-------------------------
-spec check_limit_dests(_, jid(), stanza(), [address()]) -> ok.
check_limit_dests(SLimits, FromJID, Packet, check_limit_dests(SLimits, FromJID, Packet,
Addresses) -> Addresses) ->
SenderT = sender_type(FromJID), SenderT = sender_type(FromJID),
@ -497,24 +445,22 @@ check_limit_dests(SLimits, FromJID, Packet,
%%% Convert Destination XML to record %%% Convert Destination XML to record
%%%------------------------- %%%-------------------------
convert_dest_record(XMLs) -> -spec convert_dest_record([address()]) -> [#dest{}].
lists:map(fun (XML) -> convert_dest_record(Addrs) ->
case fxml:get_tag_attr_s(<<"jid">>, XML) of lists:map(
<<"">> -> #dest{jid_string = none, full_xml = XML}; fun(#address{jid = undefined} = Addr) ->
JIDS -> #dest{jid_string = none, full_xml = Addr};
Type = fxml:get_tag_attr_s(<<"type">>, XML), (#address{jid = JID, type = Type} = Addr) ->
JIDJ = stj(JIDS), #dest{jid_string = jid:to_string(JID), jid_jid = JID,
#dest{jid_string = JIDS, jid_jid = JIDJ, type = Type, full_xml = Addr}
type = Type, full_xml = XML} end, Addrs).
end
end,
XMLs).
%%%------------------------- %%%-------------------------
%%% Split destinations by existence of JID %%% Split destinations by existence of JID
%%% and send error messages for other dests %%% and send error messages for other dests
%%%------------------------- %%%-------------------------
-spec split_dests_jid([#dest{}]) -> {[#dest{}], [#dest{}]}.
split_dests_jid(Dests) -> split_dests_jid(Dests) ->
lists:partition(fun (Dest) -> lists:partition(fun (Dest) ->
case Dest#dest.jid_string of case Dest#dest.jid_string of
@ -524,8 +470,9 @@ split_dests_jid(Dests) ->
end, end,
Dests). Dests).
-spec report_not_jid(jid(), stanza(), #dest{}) -> any().
report_not_jid(From, Packet, Dests) -> report_not_jid(From, Packet, Dests) ->
Dests2 = [fxml:element_to_binary(Dest#dest.full_xml) Dests2 = [fxml:element_to_binary(xmpp:encode(Dest#dest.full_xml))
|| Dest <- Dests], || Dest <- Dests],
[route_error(From, From, Packet, jid_malformed, [route_error(From, From, Packet, jid_malformed,
<<"This service can not process the address: ", <<"This service can not process the address: ",
@ -536,6 +483,7 @@ report_not_jid(From, Packet, Dests) ->
%%% Group destinations by their servers %%% Group destinations by their servers
%%%------------------------- %%%-------------------------
-spec group_dests([#dest{}]) -> [#group{}].
group_dests(Dests) -> group_dests(Dests) ->
D = lists:foldl(fun (Dest, Dict) -> D = lists:foldl(fun (Dest, Dict) ->
ServerS = (Dest#dest.jid_jid)#jid.server, ServerS = (Dest#dest.jid_jid)#jid.server,
@ -575,18 +523,17 @@ build_other_xml(Dests) ->
lists:foldl(fun (Dest, R) -> lists:foldl(fun (Dest, R) ->
XML = Dest#dest.full_xml, XML = Dest#dest.full_xml,
case Dest#dest.type of case Dest#dest.type of
<<"to">> -> [add_delivered(XML) | R]; to -> [add_delivered(XML) | R];
<<"cc">> -> [add_delivered(XML) | R]; cc -> [add_delivered(XML) | R];
<<"bcc">> -> R; bcc -> R;
_ -> [XML | R] _ -> [XML | R]
end end
end, end,
[], Dests). [], Dests).
add_delivered(#xmlel{name = Name, attrs = Attrs, -spec add_delivered(address()) -> address().
children = Els}) -> add_delivered(Addr) ->
Attrs2 = [{<<"delivered">>, <<"true">>} | Attrs], Addr#address{delivered = true}.
#xmlel{name = Name, attrs = Attrs2, children = Els}.
%%%------------------------- %%%-------------------------
%%% Add preliminary packets %%% Add preliminary packets
@ -636,7 +583,7 @@ decide_action_group(Group) ->
route_packet(From, ToDest, Packet, AAttrs, Others, Addresses) -> route_packet(From, ToDest, Packet, AAttrs, Others, Addresses) ->
Dests = case ToDest#dest.type of Dests = case ToDest#dest.type of
<<"bcc">> -> []; bcc -> [];
_ -> [ToDest] _ -> [ToDest]
end, end,
route_packet2(From, ToDest#dest.jid_string, Dests, route_packet2(From, ToDest#dest.jid_string, Dests,
@ -652,20 +599,20 @@ route_packet_multicast(From, ToS, Packet, AAttrs, Dests,
Addresses) Addresses)
|| DFragment <- Fragmented_dests]. || DFragment <- Fragmented_dests].
route_packet2(From, ToS, Dests, Packet, AAttrs, -spec route_packet2(jid(), binary(), [#dest{}], stanza(), list(), [address()]) -> ok.
route_packet2(From, ToS, Dests, Packet, _AAttrs,
Addresses) -> Addresses) ->
#xmlel{name = T, attrs = A, children = C} = Packet, Els = case append_dests(Dests, Addresses) of
C2 = case append_dests(Dests, Addresses) of [] ->
[] -> C; xmpp:get_els(Packet);
ACs -> ACs ->
[#xmlel{name = <<"addresses">>, attrs = AAttrs, [#addresses{list = ACs}|xmpp:get_els(Packet)]
children = ACs} end,
| C] Packet2 = xmpp:set_els(Packet, Els),
end,
Packet2 = #xmlel{name = T, attrs = A, children = C2},
ToJID = stj(ToS), ToJID = stj(ToS),
ejabberd_router:route(From, ToJID, Packet2). ejabberd_router:route(From, ToJID, Packet2).
-spec append_dests([#dest{}], {[address()], [address()]} | [address()]) -> [address()].
append_dests(_Dests, {Others, Addresses}) -> append_dests(_Dests, {Others, Addresses}) ->
Addresses++Others; Addresses++Others;
append_dests([], Addresses) -> Addresses; append_dests([], Addresses) -> Addresses;
@ -676,12 +623,14 @@ append_dests([Dest | Dests], Addresses) ->
%%% Check relay %%% Check relay
%%%------------------------- %%%-------------------------
-spec check_relay(binary(), binary(), [#group{}]) -> ok.
check_relay(RS, LS, Gs) -> check_relay(RS, LS, Gs) ->
case check_relay_required(RS, LS, Gs) of case check_relay_required(RS, LS, Gs) of
false -> ok; false -> ok;
true -> throw(edrelay) true -> throw(edrelay)
end. end.
-spec check_relay_required(binary(), binary(), [#group{}]) -> boolean().
check_relay_required(RServer, LServerS, Groups) -> check_relay_required(RServer, LServerS, Groups) ->
case lists:suffix(str:tokens(LServerS, <<".">>), case lists:suffix(str:tokens(LServerS, <<".">>),
str:tokens(RServer, <<".">>)) of str:tokens(RServer, <<".">>)) of
@ -689,6 +638,7 @@ check_relay_required(RServer, LServerS, Groups) ->
false -> check_relay_required(LServerS, Groups) false -> check_relay_required(LServerS, Groups)
end. end.
-spec check_relay_required(binary(), [#group{}]) -> boolean().
check_relay_required(LServerS, Groups) -> check_relay_required(LServerS, Groups) ->
lists:any(fun (Group) -> Group#group.server /= LServerS lists:any(fun (Group) -> Group#group.server /= LServerS
end, end,
@ -701,19 +651,16 @@ check_relay_required(LServerS, Groups) ->
send_query_info(RServerS, LServiceS) -> send_query_info(RServerS, LServiceS) ->
case str:str(RServerS, <<"echo.">>) of case str:str(RServerS, <<"echo.">>) of
1 -> false; 1 -> false;
_ -> send_query(RServerS, LServiceS, ?NS_DISCO_INFO) _ -> send_query(RServerS, LServiceS, #disco_info{})
end. end.
send_query_items(RServerS, LServiceS) -> send_query_items(RServerS, LServiceS) ->
send_query(RServerS, LServiceS, ?NS_DISCO_ITEMS). send_query(RServerS, LServiceS, #disco_items{}).
send_query(RServerS, LServiceS, XMLNS) -> -spec send_query(binary(), binary(), [disco_info()|disco_items()]) -> ok.
Packet = #xmlel{name = <<"iq">>, send_query(RServerS, LServiceS, SubEl) ->
attrs = [{<<"to">>, RServerS}, {<<"type">>, <<"get">>}], Packet = #iq{id = randoms:get_string(),
children = type = get, sub_els = [SubEl]},
[#xmlel{name = <<"query">>,
attrs = [{<<"xmlns">>, XMLNS}],
children = []}]},
ejabberd_router:route(stj(LServiceS), stj(RServerS), ejabberd_router:route(stj(LServiceS), stj(RServerS),
Packet). Packet).
@ -733,49 +680,40 @@ process_iqreply_error(From, LServiceS, _Packet) ->
%%% Check protocol support: Receive response: Disco %%% Check protocol support: Receive response: Disco
%%%------------------------- %%%-------------------------
process_iqreply_result(From, LServiceS, Packet, State) -> -spec process_iqreply_result(jid(), binary(), iq()) -> any().
#xmlel{name = <<"query">>, attrs = Attrs2, process_iqreply_result(From, LServiceS, #iq{sub_els = [SubEl]}) ->
children = Els2} = case SubEl of
fxml:get_subtag(Packet, <<"query">>), #disco_info{} ->
case fxml:get_attr_s(<<"xmlns">>, Attrs2) of process_discoinfo_result(From, LServiceS, SubEl);
?NS_DISCO_INFO -> #disco_items{} ->
process_discoinfo_result(From, LServiceS, Els2, State); process_discoitems_result(From, LServiceS, SubEl);
?NS_DISCO_ITEMS -> _ ->
process_discoitems_result(From, LServiceS, Els2) ok
end. end.
%%%------------------------- %%%-------------------------
%%% Check protocol support: Receive response: Disco Info %%% Check protocol support: Receive response: Disco Info
%%%------------------------- %%%-------------------------
process_discoinfo_result(From, LServiceS, Els, process_discoinfo_result(From, LServiceS, DiscoInfo) ->
_State) ->
FromS = jts(From), FromS = jts(From),
case search_waiter(FromS, LServiceS, info) of case search_waiter(FromS, LServiceS, info) of
{found_waiter, Waiter} -> {found_waiter, Waiter} ->
process_discoinfo_result2(From, FromS, LServiceS, Els, process_discoinfo_result2(From, FromS, LServiceS, DiscoInfo,
Waiter); Waiter);
_ -> ok _ -> ok
end. end.
process_discoinfo_result2(From, FromS, LServiceS, Els, process_discoinfo_result2(From, FromS, LServiceS,
#disco_info{features = Feats} = DiscoInfo,
Waiter) -> Waiter) ->
Multicast_support = Multicast_support = lists:member(?NS_ADDRESS, Feats),
lists:any(
fun(XML) ->
case XML of
#xmlel{name = <<"feature">>, attrs = Attrs} ->
(?NS_ADDRESS) == fxml:get_attr_s(<<"var">>, Attrs);
_ -> false
end
end,
Els),
Group = Waiter#waiter.group, Group = Waiter#waiter.group,
RServer = Group#group.server, RServer = Group#group.server,
case Multicast_support of case Multicast_support of
true -> true ->
SenderT = sender_type(From), SenderT = sender_type(From),
RLimits = get_limits_xml(Els, SenderT), RLimits = get_limits_xml(DiscoInfo, SenderT),
add_response(RServer, {multicast_supported, FromS, RLimits}), add_response(RServer, {multicast_supported, FromS, RLimits}),
FromM = Waiter#waiter.sender, FromM = Waiter#waiter.sender,
DestsM = Group#group.dests, DestsM = Group#group.dests,
@ -799,90 +737,58 @@ process_discoinfo_result2(From, FromS, LServiceS, Els,
end end
end. end.
get_limits_xml(Els, SenderT) -> get_limits_xml(DiscoInfo, SenderT) ->
LimitOpts = get_limits_els(Els), LimitOpts = get_limits_els(DiscoInfo),
build_remote_limit_record(LimitOpts, SenderT). build_remote_limit_record(LimitOpts, SenderT).
get_limits_els(Els) -> -spec get_limits_els(disco_info()) -> [{atom(), integer()}].
lists:foldl(fun (XML, R) -> get_limits_els(DiscoInfo) ->
case XML of lists:flatmap(
#xmlel{name = <<"x">>, attrs = Attrs, fun(#xdata{type = result} = X) ->
children = SubEls} -> get_limits_fields(X);
case ((?NS_XDATA) == (_) ->
fxml:get_attr_s(<<"xmlns">>, Attrs)) []
and end, DiscoInfo#disco_info.xdata).
(<<"result">> ==
fxml:get_attr_s(<<"type">>, Attrs))
of
true -> get_limits_fields(SubEls) ++ R;
false -> R
end;
_ -> R
end
end,
[], Els).
get_limits_fields(Fields) -> -spec get_limits_fields(xdata()) -> [{atom(), integer()}].
{Head, Tail} = lists:partition(fun (Field) -> get_limits_fields(X) ->
case Field of {Head, Tail} = lists:partition(
#xmlel{name = <<"field">>, fun(#xdata_field{var = Var, type = Type}) ->
attrs = Attrs} -> Var == <<"FORM_TYPE">> andalso Type == hidden
(<<"FORM_TYPE">> == end, X#xdata.fields),
fxml:get_attr_s(<<"var">>,
Attrs))
and
(<<"hidden">> ==
fxml:get_attr_s(<<"type">>,
Attrs));
_ -> false
end
end,
Fields),
case Head of case Head of
[] -> []; [] -> [];
_ -> get_limits_values(Tail) _ -> get_limits_values(Tail)
end. end.
get_limits_values(Values) -> -spec get_limits_values([xdata_field()]) -> [{atom(), integer()}].
lists:foldl(fun (Value, R) -> get_limits_values(Fields) ->
case Value of lists:flatmap(
#xmlel{name = <<"field">>, attrs = Attrs, fun(#xdata_field{var = Name, values = [Number]}) ->
children = SubEls} -> try
[#xmlel{name = <<"value">>, children = SubElsV}] = [{binary_to_atom(Name, utf8), binary_to_integer(Number)}]
SubEls, catch _:badarg ->
Number = fxml:get_cdata(SubElsV), []
Name = fxml:get_attr_s(<<"var">>, Attrs), end;
[{jlib:binary_to_atom(Name), (_) ->
jlib:binary_to_integer(Number)} []
| R]; end, Fields).
_ -> R
end
end,
[], Values).
%%%------------------------- %%%-------------------------
%%% Check protocol support: Receive response: Disco Items %%% Check protocol support: Receive response: Disco Items
%%%------------------------- %%%-------------------------
process_discoitems_result(From, LServiceS, Els) -> process_discoitems_result(From, LServiceS, #disco_items{items = Items}) ->
FromS = jts(From), FromS = jts(From),
case search_waiter(FromS, LServiceS, items) of case search_waiter(FromS, LServiceS, items) of
{found_waiter, Waiter} -> {found_waiter, Waiter} ->
List = lists:foldl( List = lists:flatmap(
fun(XML, Res) -> fun(#disco_item{jid = #jid{luser = <<"">>,
case XML of lresource = <<"">>} = J}) ->
#xmlel{name = <<"item">>, attrs = Attrs} -> [J];
SJID = fxml:get_attr_s(<<"jid">>, Attrs), (_) ->
case jid:from_string(SJID) of []
#jid{luser = <<"">>, end, Items),
lresource = <<"">>} ->
[SJID | Res];
_ -> Res
end;
_ -> Res
end
end,
[], Els),
case List of case List of
[] -> [] ->
received_awaiter(FromS, Waiter, LServiceS); received_awaiter(FromS, Waiter, LServiceS);
@ -1109,9 +1015,7 @@ get_limit_value(Name, Default, LimitOpts) ->
false -> {default, Default} false -> {default, Default}
end. end.
type_of_stanza(#xmlel{name = <<"message">>}) -> message; type_of_stanza(Stanza) -> element(1, Stanza).
type_of_stanza(#xmlel{name = <<"presence">>}) ->
presence.
get_limit_number(message, Limits) -> get_limit_number(message, Limits) ->
Limits#limits.message; Limits#limits.message;
@ -1144,17 +1048,10 @@ fragment_dests(Dests, Limit_number) ->
%% Some parts of code are borrowed from mod_muc_room.erl %% Some parts of code are borrowed from mod_muc_room.erl
-define(RFIELDT(Type, Var, Val), -define(RFIELDT(Type, Var, Val),
#xmlel{name = <<"field">>, #xdata_field{type = Type, var = Var, values = [Val]}).
attrs = [{<<"var">>, Var}, {<<"type">>, Type}],
children =
[#xmlel{name = <<"value">>, attrs = [],
children = [{xmlcdata, Val}]}]}).
-define(RFIELDV(Var, Val), -define(RFIELDV(Var, Val),
#xmlel{name = <<"field">>, attrs = [{<<"var">>, Var}], #xdata_field{var = Var, values = [Val]}).
children =
[#xmlel{name = <<"value">>, attrs = [],
children = [{xmlcdata, Val}]}]}).
iq_disco_info_extras(From, State) -> iq_disco_info_extras(From, State) ->
SenderT = sender_type(From), SenderT = sender_type(From),
@ -1162,12 +1059,9 @@ iq_disco_info_extras(From, State) ->
case iq_disco_info_extras2(SenderT, Service_limits) of case iq_disco_info_extras2(SenderT, Service_limits) of
[] -> []; [] -> [];
List_limits_xmpp -> List_limits_xmpp ->
[#xmlel{name = <<"x">>, #xdata{type = result,
attrs = fields = [?RFIELDT(hidden, <<"FORM_TYPE">>, ?NS_ADDRESS)
[{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"result">>}], | List_limits_xmpp]}
children =
[?RFIELDT(<<"hidden">>, <<"FORM_TYPE">>, (?NS_ADDRESS))]
++ List_limits_xmpp}]
end. end.
sender_type(From) -> sender_type(From) ->
@ -1198,22 +1092,20 @@ to_binary(A) -> list_to_binary(hd(io_lib:format("~p", [A]))).
%%%------------------------- %%%-------------------------
route_error(From, To, Packet, ErrType, ErrText) -> route_error(From, To, Packet, ErrType, ErrText) ->
#xmlel{attrs = Attrs} = Packet, Lang = xmpp:get_lang(Packet),
Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs), Err = make_reply(ErrType, Lang, ErrText),
Reply = make_reply(ErrType, Lang, ErrText), ejabberd_router:route_error(From, To, Packet, Err).
Err = jlib:make_error_reply(Packet, Reply),
ejabberd_router:route(From, To, Err).
make_reply(bad_request, Lang, ErrText) -> make_reply(bad_request, Lang, ErrText) ->
?ERRT_BAD_REQUEST(Lang, ErrText); xmpp:err_bad_request(ErrText, Lang);
make_reply(jid_malformed, Lang, ErrText) -> make_reply(jid_malformed, Lang, ErrText) ->
?ERRT_JID_MALFORMED(Lang, ErrText); xmpp:err_jid_malformed(ErrText, Lang);
make_reply(not_acceptable, Lang, ErrText) -> make_reply(not_acceptable, Lang, ErrText) ->
?ERRT_NOT_ACCEPTABLE(Lang, ErrText); xmpp:err_not_acceptable(ErrText, Lang);
make_reply(internal_server_error, Lang, ErrText) -> make_reply(internal_server_error, Lang, ErrText) ->
?ERRT_INTERNAL_SERVER_ERROR(Lang, ErrText); xmpp:err_internal_server_error(ErrText, Lang);
make_reply(forbidden, Lang, ErrText) -> make_reply(forbidden, Lang, ErrText) ->
?ERRT_FORBIDDEN(Lang, ErrText). xmpp:err_forbidden(ErrText, Lang).
stj(String) -> jid:from_string(String). stj(String) -> jid:from_string(String).