diff options
Diffstat (limited to 'src/mod_disco.erl')
-rw-r--r-- | src/mod_disco.erl | 575 |
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")}]. |