aboutsummaryrefslogtreecommitdiff
path: root/src/mod_disco.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mod_disco.erl')
-rw-r--r--src/mod_disco.erl575
1 files changed, 230 insertions, 345 deletions
diff --git a/src/mod_disco.erl b/src/mod_disco.erl
index 2e7b80c18..4ec77e847 100644
--- a/src/mod_disco.erl
+++ b/src/mod_disco.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -32,56 +32,42 @@
-behaviour(gen_mod).
--export([start/2, stop/1, process_local_iq_items/3,
- process_local_iq_info/3, get_local_identity/5,
+-export([start/2, stop/1, reload/3, process_local_iq_items/1,
+ process_local_iq_info/1, get_local_identity/5,
get_local_features/5, get_local_services/5,
- process_sm_iq_items/3, process_sm_iq_info/3,
+ process_sm_iq_items/1, process_sm_iq_info/1,
get_sm_identity/5, get_sm_features/5, get_sm_items/5,
- get_info/5, register_feature/2, unregister_feature/2,
- register_extra_domain/2, unregister_extra_domain/2,
- transform_module_options/1, mod_opt_type/1, depends/2]).
+ get_info/5, mod_opt_type/1, mod_options/1, depends/2]).
--include("ejabberd.hrl").
-include("logger.hrl").
-
--include("jlib.hrl").
-
+-include("translate.hrl").
+-include("xmpp.hrl").
+-include_lib("stdlib/include/ms_transform.hrl").
-include("mod_roster.hrl").
+-type features_acc() :: {error, stanza_error()} | {result, [binary()]} | empty.
+-type items_acc() :: {error, stanza_error()} | {result, [disco_item()]} | empty.
+-export_type([features_acc/0, items_acc/0]).
+
start(Host, Opts) ->
- ejabberd_local:refresh_iq_handlers(),
- IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
- one_queue),
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
?NS_DISCO_ITEMS, ?MODULE,
- process_local_iq_items, IQDisc),
+ process_local_iq_items),
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
?NS_DISCO_INFO, ?MODULE,
- process_local_iq_info, IQDisc),
+ process_local_iq_info),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
- ?NS_DISCO_ITEMS, ?MODULE, process_sm_iq_items,
- IQDisc),
+ ?NS_DISCO_ITEMS, ?MODULE, process_sm_iq_items),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
- ?NS_DISCO_INFO, ?MODULE, process_sm_iq_info,
- IQDisc),
- catch ets:new(disco_features,
- [named_table, ordered_set, public]),
- register_feature(Host, <<"iq">>),
- register_feature(Host, <<"presence">>),
+ ?NS_DISCO_INFO, ?MODULE, process_sm_iq_info),
catch ets:new(disco_extra_domains,
- [named_table, ordered_set, public]),
- ExtraDomains = gen_mod:get_opt(extra_domains, Opts,
- fun(Hs) ->
- [iolist_to_binary(H) || H <- Hs]
- end, []),
+ [named_table, ordered_set, public,
+ {heir, erlang:group_leader(), none}]),
+ ExtraDomains = mod_disco_opt:extra_domains(Opts),
lists:foreach(fun (Domain) ->
register_extra_domain(Host, Domain)
end,
ExtraDomains),
- catch ets:new(disco_sm_features,
- [named_table, ordered_set, public]),
- catch ets:new(disco_sm_nodes,
- [named_table, ordered_set, public]),
ejabberd_hooks:add(disco_local_items, Host, ?MODULE,
get_local_services, 100),
ejabberd_hooks:add(disco_local_features, Host, ?MODULE,
@@ -121,168 +107,135 @@ stop(Host) ->
?NS_DISCO_ITEMS),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host,
?NS_DISCO_INFO),
- catch ets:match_delete(disco_features, {{'_', Host}}),
catch ets:match_delete(disco_extra_domains,
{{'_', Host}}),
ok.
-register_feature(Host, Feature) ->
- catch ets:new(disco_features,
- [named_table, ordered_set, public]),
- ets:insert(disco_features, {{Feature, Host}}).
-
-unregister_feature(Host, Feature) ->
- catch ets:new(disco_features,
- [named_table, ordered_set, public]),
- ets:delete(disco_features, {Feature, Host}).
-
+reload(Host, NewOpts, OldOpts) ->
+ NewDomains = mod_disco_opt:extra_domains(NewOpts),
+ OldDomains = mod_disco_opt:extra_domains(OldOpts),
+ lists:foreach(
+ fun(Domain) ->
+ register_extra_domain(Host, Domain)
+ end, NewDomains -- OldDomains),
+ lists:foreach(
+ fun(Domain) ->
+ unregister_extra_domain(Host, Domain)
+ end, OldDomains -- NewDomains).
+
+-spec register_extra_domain(binary(), binary()) -> true.
register_extra_domain(Host, Domain) ->
- catch ets:new(disco_extra_domains,
- [named_table, ordered_set, public]),
ets:insert(disco_extra_domains, {{Domain, Host}}).
+-spec unregister_extra_domain(binary(), binary()) -> true.
unregister_extra_domain(Host, Domain) ->
- catch ets:new(disco_extra_domains,
- [named_table, ordered_set, public]),
- ets:delete(disco_extra_domains, {Domain, Host}).
-
-process_local_iq_items(From, To,
- #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
- case Type of
- set ->
- Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
- IQ#iq{type = error, sub_el = [SubEl, ?ERRT_NOT_ALLOWED(Lang, Txt)]};
- get ->
- Node = fxml:get_tag_attr_s(<<"node">>, SubEl),
- Host = To#jid.lserver,
- case ejabberd_hooks:run_fold(disco_local_items, Host,
- empty, [From, To, Node, Lang])
- of
- {result, Items} ->
- ANode = case Node of
- <<"">> -> [];
- _ -> [{<<"node">>, Node}]
- end,
- IQ#iq{type = result,
- sub_el =
- [#xmlel{name = <<"query">>,
- attrs =
- [{<<"xmlns">>, ?NS_DISCO_ITEMS} | ANode],
- children = Items}]};
- {error, Error} ->
- IQ#iq{type = error, sub_el = [SubEl, Error]}
- end
+ ets:delete_object(disco_extra_domains, {{Domain, Host}}).
+
+-spec process_local_iq_items(iq()) -> iq().
+process_local_iq_items(#iq{type = set, lang = Lang} = IQ) ->
+ Txt = ?T("Value 'set' of 'type' attribute is not allowed"),
+ xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
+process_local_iq_items(#iq{type = get, lang = Lang,
+ from = From, to = To,
+ sub_els = [#disco_items{node = Node}]} = IQ) ->
+ Host = To#jid.lserver,
+ case ejabberd_hooks:run_fold(disco_local_items, Host,
+ empty, [From, To, Node, Lang]) of
+ {result, Items} ->
+ xmpp:make_iq_result(IQ, #disco_items{node = Node, items = Items});
+ {error, Error} ->
+ xmpp:make_error(IQ, Error)
end.
-process_local_iq_info(From, To,
- #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
- case Type of
- set ->
- Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
- IQ#iq{type = error, sub_el = [SubEl, ?ERRT_NOT_ALLOWED(Lang, Txt)]};
- get ->
- Host = To#jid.lserver,
- Node = fxml:get_tag_attr_s(<<"node">>, SubEl),
- Identity = ejabberd_hooks:run_fold(disco_local_identity,
- Host, [], [From, To, Node, Lang]),
- Info = ejabberd_hooks:run_fold(disco_info, Host, [],
- [Host, ?MODULE, Node, Lang]),
- case ejabberd_hooks:run_fold(disco_local_features, Host,
- empty, [From, To, Node, Lang])
- of
- {result, Features} ->
- ANode = case Node of
- <<"">> -> [];
- _ -> [{<<"node">>, Node}]
- end,
- IQ#iq{type = result,
- sub_el =
- [#xmlel{name = <<"query">>,
- attrs =
- [{<<"xmlns">>, ?NS_DISCO_INFO} | ANode],
- children =
- Identity ++
- Info ++ features_to_xml(Features)}]};
- {error, Error} ->
- IQ#iq{type = error, sub_el = [SubEl, Error]}
- end
+-spec process_local_iq_info(iq()) -> iq().
+process_local_iq_info(#iq{type = set, lang = Lang} = IQ) ->
+ Txt = ?T("Value 'set' of 'type' attribute is not allowed"),
+ xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
+process_local_iq_info(#iq{type = get, lang = Lang,
+ from = From, to = To,
+ sub_els = [#disco_info{node = Node}]} = IQ) ->
+ Host = To#jid.lserver,
+ Identity = ejabberd_hooks:run_fold(disco_local_identity,
+ Host, [], [From, To, Node, Lang]),
+ Info = ejabberd_hooks:run_fold(disco_info, Host, [],
+ [Host, ?MODULE, Node, Lang]),
+ case ejabberd_hooks:run_fold(disco_local_features, Host,
+ empty, [From, To, Node, Lang]) of
+ {result, Features} ->
+ xmpp:make_iq_result(IQ, #disco_info{node = Node,
+ identities = Identity,
+ xdata = Info,
+ features = Features});
+ {error, Error} ->
+ xmpp:make_error(IQ, Error)
end.
-get_local_identity(Acc, _From, _To, <<>>, _Lang) ->
- Acc ++
- [#xmlel{name = <<"identity">>,
- attrs =
- [{<<"category">>, <<"server">>}, {<<"type">>, <<"im">>},
- {<<"name">>, <<"ejabberd">>}],
- children = []}];
+-spec get_local_identity([identity()], jid(), jid(),
+ binary(), binary()) -> [identity()].
+get_local_identity(Acc, _From, To, <<"">>, _Lang) ->
+ Host = To#jid.lserver,
+ Name = mod_disco_opt:name(Host),
+ Acc ++ [#identity{category = <<"server">>,
+ type = <<"im">>,
+ name = Name}];
get_local_identity(Acc, _From, _To, _Node, _Lang) ->
Acc.
+-spec get_local_features(features_acc(), jid(), jid(), binary(), binary()) ->
+ {error, stanza_error()} | {result, [binary()]}.
get_local_features({error, _Error} = Acc, _From, _To,
_Node, _Lang) ->
Acc;
-get_local_features(Acc, _From, To, <<>>, _Lang) ->
+get_local_features(Acc, _From, To, <<"">>, _Lang) ->
Feats = case Acc of
- {result, Features} -> Features;
- empty -> []
+ {result, Features} -> Features;
+ empty -> []
end,
- Host = To#jid.lserver,
- {result,
- ets:select(disco_features,
- [{{{'_', Host}}, [], ['$_']}])
- ++ Feats};
+ {result, lists:usort(
+ lists:flatten(
+ [?NS_FEATURE_IQ, ?NS_FEATURE_PRESENCE,
+ ?NS_DISCO_INFO, ?NS_DISCO_ITEMS, Feats,
+ ejabberd_local:get_features(To#jid.lserver)]))};
get_local_features(Acc, _From, _To, _Node, Lang) ->
case Acc of
{result, _Features} -> Acc;
empty ->
- Txt = <<"No features available">>,
- {error, ?ERRT_ITEM_NOT_FOUND(Lang, Txt)}
+ Txt = ?T("No features available"),
+ {error, xmpp:err_item_not_found(Txt, Lang)}
end.
-features_to_xml(FeatureList) ->
- [#xmlel{name = <<"feature">>,
- attrs = [{<<"var">>, Feat}], children = []}
- || Feat
- <- lists:usort(lists:map(fun ({{Feature, _Host}}) ->
- Feature;
- (Feature) when is_binary(Feature) ->
- Feature
- end,
- FeatureList))].
-
-domain_to_xml({Domain}) ->
- #xmlel{name = <<"item">>, attrs = [{<<"jid">>, Domain}],
- children = []};
-domain_to_xml(Domain) ->
- #xmlel{name = <<"item">>, attrs = [{<<"jid">>, Domain}],
- children = []}.
-
+-spec get_local_services(items_acc(), jid(), jid(), binary(), binary()) ->
+ {error, stanza_error()} | {result, [disco_item()]}.
get_local_services({error, _Error} = Acc, _From, _To,
_Node, _Lang) ->
Acc;
-get_local_services(Acc, _From, To, <<>>, _Lang) ->
+get_local_services(Acc, _From, To, <<"">>, _Lang) ->
Items = case Acc of
{result, Its} -> Its;
empty -> []
end,
Host = To#jid.lserver,
{result,
- lists:usort(lists:map(fun domain_to_xml/1,
- get_vh_services(Host) ++
- ets:select(disco_extra_domains,
- [{{{'$1', Host}}, [], ['$1']}])))
- ++ Items};
+ lists:usort(
+ lists:map(
+ fun(Domain) -> #disco_item{jid = jid:make(Domain)} end,
+ get_vh_services(Host) ++
+ ets:select(disco_extra_domains,
+ ets:fun2ms(
+ fun({{D, H}}) when H == Host -> D end))))
+ ++ Items};
get_local_services({result, _} = Acc, _From, _To, _Node,
_Lang) ->
Acc;
get_local_services(empty, _From, _To, _Node, Lang) ->
- {error, ?ERRT_ITEM_NOT_FOUND(Lang, <<"No services available">>)}.
+ {error, xmpp:err_item_not_found(?T("No services available"), Lang)}.
+-spec get_vh_services(binary()) -> [binary()].
get_vh_services(Host) ->
Hosts = lists:sort(fun (H1, H2) ->
byte_size(H1) >= byte_size(H2)
end,
- ?MYHOSTS),
+ ejabberd_option:hosts()),
lists:filter(fun (H) ->
case lists:dropwhile(fun (VH) ->
not
@@ -296,56 +249,45 @@ get_vh_services(Host) ->
[VH | _] -> VH == Host
end
end,
- ejabberd_router:dirty_get_all_routes()).
+ ejabberd_router:get_all_routes()).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-process_sm_iq_items(From, To,
- #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
- case Type of
- set ->
- Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
- IQ#iq{type = error, sub_el = [SubEl, ?ERRT_NOT_ALLOWED(Lang, Txt)]};
- get ->
- case is_presence_subscribed(From, To) of
- true ->
- Host = To#jid.lserver,
- Node = fxml:get_tag_attr_s(<<"node">>, SubEl),
- case ejabberd_hooks:run_fold(disco_sm_items, Host,
- empty, [From, To, Node, Lang])
- of
- {result, Items} ->
- ANode = case Node of
- <<"">> -> [];
- _ -> [{<<"node">>, Node}]
- end,
- IQ#iq{type = result,
- sub_el =
- [#xmlel{name = <<"query">>,
- attrs =
- [{<<"xmlns">>, ?NS_DISCO_ITEMS}
- | ANode],
- children = Items}]};
- {error, Error} ->
- IQ#iq{type = error, sub_el = [SubEl, Error]}
- end;
- false ->
- Txt = <<"Not subscribed">>,
- IQ#iq{type = error,
- sub_el = [SubEl, ?ERRT_SERVICE_UNAVAILABLE(Lang, Txt)]}
- end
+-spec process_sm_iq_items(iq()) -> iq().
+process_sm_iq_items(#iq{type = set, lang = Lang} = IQ) ->
+ Txt = ?T("Value 'set' of 'type' attribute is not allowed"),
+ xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
+process_sm_iq_items(#iq{type = get, lang = Lang,
+ from = From, to = To,
+ sub_els = [#disco_items{node = Node}]} = IQ) ->
+ case mod_roster:is_subscribed(From, To) of
+ true ->
+ Host = To#jid.lserver,
+ case ejabberd_hooks:run_fold(disco_sm_items, Host,
+ empty, [From, To, Node, Lang]) of
+ {result, Items} ->
+ xmpp:make_iq_result(
+ IQ, #disco_items{node = Node, items = Items});
+ {error, Error} ->
+ xmpp:make_error(IQ, Error)
+ end;
+ false ->
+ Txt = ?T("Not subscribed"),
+ xmpp:make_error(IQ, xmpp:err_subscription_required(Txt, Lang))
end.
+-spec get_sm_items(items_acc(), jid(), jid(), binary(), binary()) ->
+ {error, stanza_error()} | {result, [disco_item()]}.
get_sm_items({error, _Error} = Acc, _From, _To, _Node,
_Lang) ->
Acc;
get_sm_items(Acc, From,
- #jid{user = User, server = Server} = To, <<>>, _Lang) ->
+ #jid{user = User, server = Server} = To, <<"">>, _Lang) ->
Items = case Acc of
{result, Its} -> Its;
empty -> []
end,
- Items1 = case is_presence_subscribed(From, To) of
+ Items1 = case mod_roster:is_subscribed(From, To) of
true -> get_user_resources(User, Server);
_ -> []
end,
@@ -357,197 +299,140 @@ get_sm_items(empty, From, To, _Node, Lang) ->
#jid{luser = LFrom, lserver = LSFrom} = From,
#jid{luser = LTo, lserver = LSTo} = To,
case {LFrom, LSFrom} of
- {LTo, LSTo} -> {error, ?ERR_ITEM_NOT_FOUND};
+ {LTo, LSTo} -> {error, xmpp:err_item_not_found()};
_ ->
- Txt = <<"Query to another users is forbidden">>,
- {error, ?ERRT_NOT_ALLOWED(Lang, Txt)}
+ Txt = ?T("Query to another users is forbidden"),
+ {error, xmpp:err_not_allowed(Txt, Lang)}
end.
-is_presence_subscribed(#jid{luser = User, lserver = Server},
- #jid{luser = User, lserver = Server}) -> true;
-is_presence_subscribed(#jid{luser = FromUser, lserver = FromServer},
- #jid{luser = ToUser, lserver = ToServer}) ->
- lists:any(fun (#roster{jid = {SubUser, SubServer, _}, subscription = Sub})
- when FromUser == SubUser, FromServer == SubServer,
- Sub /= none ->
- true;
- (_RosterEntry) ->
- false
- end,
- ejabberd_hooks:run_fold(roster_get, ToServer, [],
- [{ToUser, ToServer}])).
-
-process_sm_iq_info(From, To,
- #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
- case Type of
- set ->
- Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
- IQ#iq{type = error, sub_el = [SubEl, ?ERRT_NOT_ALLOWED(Lang, Txt)]};
- get ->
- case is_presence_subscribed(From, To) of
- true ->
- Host = To#jid.lserver,
- Node = fxml:get_tag_attr_s(<<"node">>, SubEl),
- Identity = ejabberd_hooks:run_fold(disco_sm_identity,
- Host, [],
- [From, To, Node, Lang]),
- Info = ejabberd_hooks:run_fold(disco_info, Host, [],
+-spec process_sm_iq_info(iq()) -> iq().
+process_sm_iq_info(#iq{type = set, lang = Lang} = IQ) ->
+ Txt = ?T("Value 'set' of 'type' attribute is not allowed"),
+ xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
+process_sm_iq_info(#iq{type = get, lang = Lang,
+ from = From, to = To,
+ sub_els = [#disco_info{node = Node}]} = IQ) ->
+ case mod_roster:is_subscribed(From, To) of
+ true ->
+ Host = To#jid.lserver,
+ Identity = ejabberd_hooks:run_fold(disco_sm_identity,
+ Host, [],
[From, To, Node, Lang]),
- case ejabberd_hooks:run_fold(disco_sm_features, Host,
- empty, [From, To, Node, Lang])
- of
- {result, Features} ->
- ANode = case Node of
- <<"">> -> [];
- _ -> [{<<"node">>, Node}]
- end,
- IQ#iq{type = result,
- sub_el =
- [#xmlel{name = <<"query">>,
- attrs =
- [{<<"xmlns">>, ?NS_DISCO_INFO}
- | ANode],
- children =
- Identity ++ Info ++
- features_to_xml(Features)}]};
- {error, Error} ->
- IQ#iq{type = error, sub_el = [SubEl, Error]}
- end;
- false ->
- Txt = <<"Not subscribed">>,
- IQ#iq{type = error,
- sub_el = [SubEl, ?ERRT_SERVICE_UNAVAILABLE(Lang, Txt)]}
- end
+ Info = ejabberd_hooks:run_fold(disco_info, Host, [],
+ [From, To, Node, Lang]),
+ case ejabberd_hooks:run_fold(disco_sm_features, Host,
+ empty, [From, To, Node, Lang]) of
+ {result, Features} ->
+ xmpp:make_iq_result(IQ, #disco_info{node = Node,
+ identities = Identity,
+ xdata = Info,
+ features = Features});
+ {error, Error} ->
+ xmpp:make_error(IQ, Error)
+ end;
+ false ->
+ Txt = ?T("Not subscribed"),
+ xmpp:make_error(IQ, xmpp:err_subscription_required(Txt, Lang))
end.
+-spec get_sm_identity([identity()], jid(), jid(),
+ binary(), binary()) -> [identity()].
get_sm_identity(Acc, _From,
#jid{luser = LUser, lserver = LServer}, _Node, _Lang) ->
Acc ++
- case ejabberd_auth:is_user_exists(LUser, LServer) of
+ case ejabberd_auth:user_exists(LUser, LServer) of
true ->
- [#xmlel{name = <<"identity">>,
- attrs =
- [{<<"category">>, <<"account">>},
- {<<"type">>, <<"registered">>}],
- children = []}];
+ [#identity{category = <<"account">>, type = <<"registered">>}];
_ -> []
end.
-get_sm_features(empty, From, To, _Node, Lang) ->
+-spec get_sm_features(features_acc(), jid(), jid(), binary(), binary()) ->
+ {error, stanza_error()} | {result, [binary()]}.
+get_sm_features(empty, From, To, Node, Lang) ->
#jid{luser = LFrom, lserver = LSFrom} = From,
#jid{luser = LTo, lserver = LSTo} = To,
case {LFrom, LSFrom} of
- {LTo, LSTo} -> {error, ?ERR_ITEM_NOT_FOUND};
- _ ->
- Txt = <<"Query to another users is forbidden">>,
- {error, ?ERRT_NOT_ALLOWED(Lang, Txt)}
+ {LTo, LSTo} ->
+ case Node of
+ <<"">> -> {result, [?NS_DISCO_INFO, ?NS_DISCO_ITEMS]};
+ _ -> {error, xmpp:err_item_not_found()}
+ end;
+ _ ->
+ Txt = ?T("Query to another users is forbidden"),
+ {error, xmpp:err_not_allowed(Txt, Lang)}
end;
+get_sm_features({result, Features}, _From, _To, <<"">>, _Lang) ->
+ {result, [?NS_DISCO_INFO, ?NS_DISCO_ITEMS|Features]};
get_sm_features(Acc, _From, _To, _Node, _Lang) -> Acc.
+-spec get_user_resources(binary(), binary()) -> [disco_item()].
get_user_resources(User, Server) ->
Rs = ejabberd_sm:get_user_resources(User, Server),
- lists:map(fun (R) ->
- #xmlel{name = <<"item">>,
- attrs =
- [{<<"jid">>,
- <<User/binary, "@", Server/binary, "/",
- R/binary>>},
- {<<"name">>, User}],
- children = []}
- end,
- lists:sort(Rs)).
-
-transform_module_options(Opts) ->
- lists:map(
- fun({server_info, Infos}) ->
- NewInfos = lists:map(
- fun({Modules, Name, URLs}) ->
- [[{modules, Modules},
- {name, Name},
- {urls, URLs}]];
- (Opt) ->
- Opt
- end, Infos),
- {server_info, NewInfos};
- (Opt) ->
- Opt
- end, Opts).
+ [#disco_item{jid = jid:make(User, Server, Resource), name = User}
+ || Resource <- lists:sort(Rs)].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Support for: XEP-0157 Contact Addresses for XMPP Services
-get_info(_A, Host, Mod, Node, _Lang) when Node == <<>> ->
+-spec get_info([xdata()], binary(), module(), binary(), binary()) -> [xdata()];
+ ([xdata()], jid(), jid(), binary(), binary()) -> [xdata()].
+get_info(_A, Host, Mod, Node, _Lang) when is_atom(Mod), Node == <<"">> ->
Module = case Mod of
undefined -> ?MODULE;
_ -> Mod
end,
- Serverinfo_fields = get_fields_xml(Host, Module),
- [#xmlel{name = <<"x">>,
- attrs =
- [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"result">>}],
- children =
- [#xmlel{name = <<"field">>,
- attrs =
- [{<<"var">>, <<"FORM_TYPE">>},
- {<<"type">>, <<"hidden">>}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children = [{xmlcdata, ?NS_SERVERINFO}]}]}]
- ++ Serverinfo_fields}];
+ [#xdata{type = result,
+ fields = [#xdata_field{type = hidden,
+ var = <<"FORM_TYPE">>,
+ values = [?NS_SERVERINFO]}
+ | get_fields(Host, Module)]}];
get_info(Acc, _, _, _Node, _) -> Acc.
-get_fields_xml(Host, Module) ->
- Fields = gen_mod:get_module_opt(
- Host, ?MODULE, server_info,
- fun(L) ->
- lists:map(
- fun(Opts) ->
- Mods = proplists:get_value(modules, Opts, all),
- Name = proplists:get_value(name, Opts, <<>>),
- URLs = proplists:get_value(urls, Opts, []),
- {Mods, Name, URLs}
- end, L)
- end, []),
- Fields_good = lists:filter(fun ({Modules, _, _}) ->
- case Modules of
- all -> true;
- Modules ->
- lists:member(Module, Modules)
- end
- end,
- Fields),
- fields_to_xml(Fields_good).
-
-fields_to_xml(Fields) ->
- [field_to_xml(Field) || Field <- Fields].
-
-field_to_xml({_, Var, Values}) ->
- Values_xml = values_to_xml(Values),
- #xmlel{name = <<"field">>, attrs = [{<<"var">>, Var}],
- children = Values_xml}.
-
-values_to_xml(Values) ->
- lists:map(fun (Value) ->
- #xmlel{name = <<"value">>, attrs = [],
- children = [{xmlcdata, Value}]}
- end,
- Values).
-
+-spec get_fields(binary(), module()) -> [xdata_field()].
+get_fields(Host, Module) ->
+ Fields = mod_disco_opt:server_info(Host),
+ Fields1 = lists:filter(fun ({Modules, _, _}) ->
+ case Modules of
+ all -> true;
+ Modules ->
+ lists:member(Module, Modules)
+ end
+ end,
+ Fields),
+ [#xdata_field{var = Var,
+ type = 'list-multi',
+ values = Values} || {_, Var, Values} <- Fields1].
+
+-spec depends(binary(), gen_mod:opts()) -> [].
depends(_Host, _Opts) ->
[].
mod_opt_type(extra_domains) ->
- fun (Hs) -> [iolist_to_binary(H) || H <- Hs] end;
-mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
+ econf:list(econf:binary());
+mod_opt_type(name) ->
+ econf:binary();
mod_opt_type(server_info) ->
- fun (L) ->
- lists:map(fun (Opts) ->
- Mods = proplists:get_value(modules, Opts, all),
- Name = proplists:get_value(name, Opts, <<>>),
- URLs = proplists:get_value(urls, Opts, []),
- {Mods, Name, URLs}
- end,
- L)
- end;
-mod_opt_type(_) -> [extra_domains, iqdisc, server_info].
+ econf:list(
+ econf:and_then(
+ econf:options(
+ #{name => econf:binary(),
+ urls => econf:list(econf:binary()),
+ modules =>
+ econf:either(
+ all,
+ econf:list(econf:beam()))}),
+ fun(Opts) ->
+ Mods = proplists:get_value(modules, Opts, all),
+ Name = proplists:get_value(name, Opts, <<>>),
+ URLs = proplists:get_value(urls, Opts, []),
+ {Mods, Name, URLs}
+ end)).
+
+-spec mod_options(binary()) -> [{server_info,
+ [{all | [module()], binary(), [binary()]}]} |
+ {atom(), any()}].
+mod_options(_Host) ->
+ [{extra_domains, []},
+ {server_info, []},
+ {name, ?T("ejabberd")}].