diff --git a/doc/guide.html b/doc/guide.html index 50daeb690..2d1fb8eaa 100644 --- a/doc/guide.html +++ b/doc/guide.html @@ -1958,6 +1958,7 @@ disabled for instances of ejabberd with hundreds of thousands users.

This module adds support for Service Discovery (XEP-0030). With this module enabled, services on your server can be discovered by Jabber clients. Note that ejabberd has no modules with support @@ -1969,8 +1970,16 @@ the services you offer.

Options: iqdisc

This specifies the processing discipline for Service Discovery (http://jabber.org/protocol/disco#items and http://jabber.org/protocol/disco#info) IQ queries (see section 3.3.2). -
extra_domains
With this option, -extra domains can be added to the Service Discovery item list. +
{extra_domains, [ Domain ]}
With this option, +you can specify a list of extra domains that are added to the Service Discovery item list. +
{server_info, [ {Modules, Field, [Value]} ]}
+Specify additional information about the server, +as described in Contact Addresses for XMPP Services (XEP-0157). +Modules can be the keyword ‘all’, +in which case the information is reported in all the services; +or a list of ejabberd modules, +in which case the information is only specified for the services provided by those modules. +Any arbitrary Field and Value can be specified, not only contact addresses.

Examples:

3.3.5  mod_echo

This module simply echoes any Jabber @@ -3275,6 +3306,10 @@ Starts the Erlang system detached from the system console. Maximum number of Erlang processes.

-remsh ejabberd@localhost
Open an Erlang shell in a remote Erlang node. +
-hidden
+ The connections to other nodes are hidden (not published). + The result is that this node is not considered part of the cluster. + This is important when starting a temporary ctl or debug node.

Note that some characters need to be escaped when used in shell scripts, for instance " and {}. You can find other options in the Erlang manual page (erl -man erl).

diff --git a/doc/guide.tex b/doc/guide.tex index 80bc03277..7c8466eed 100644 --- a/doc/guide.tex +++ b/doc/guide.tex @@ -2600,6 +2600,7 @@ disabled for instances of \ejabberd{} with hundreds of thousands users. \ind{protocols!XEP-0030: Service Discovery} \ind{protocols!XEP-0011: Jabber Browsing} \ind{protocols!XEP-0094: Agent Information} +\ind{protocols!XEP-0157: Contact Addresses for XMPP Services} This module adds support for Service Discovery (\xepref{0030}). With this module enabled, services on your server can be discovered by @@ -2613,8 +2614,16 @@ Options: \begin{description} \iqdiscitem{Service Discovery (\ns{http://jabber.org/protocol/disco\#items} and \ns{http://jabber.org/protocol/disco\#info})} -\titem{extra\_domains} \ind{options!extra\_domains}With this option, - extra domains can be added to the Service Discovery item list. +\titem{\{extra\_domains, [ Domain ]\}} \ind{options!extra\_domains}With this option, + you can specify a list of extra domains that are added to the Service Discovery item list. +\titem{\{server\_info, [ \{Modules, Field, [Value]\} ]\}} \ind{options!server\_info} + Specify additional information about the server, + as described in Contact Addresses for XMPP Services (\xepref{0157}). + \term{Modules} can be the keyword `all', + in which case the information is reported in all the services; + or a list of \ejabberd{} modules, + in which case the information is only specified for the services provided by those modules. + Any arbitrary \term{Field} and \term{Value} can be specified, not only contact addresses. \end{description} Examples: @@ -2648,9 +2657,32 @@ Examples: ... ]}. \end{verbatim} +\item With this configuration, all services show abuse addresses, +feedback address on the main server, +and admin addresses for both the main server and the vJUD service: +\begin{verbatim} +{modules, + [ + ... + {mod_disco, [{server_info, [ + {all, + "abuse-addresses", + ["mailto:abuse@shakespeare.lit"]}, + {[mod_muc], + "Web chatroom logs", + ["http://www.example.org/muc-logs"]}, + {[mod_disco], + "feedback-addresses", + ["http://shakespeare.lit/feedback.php", "mailto:feedback@shakespeare.lit", "xmpp:feedback@shakespeare.lit"]}, + {[mod_disco, mod_vcard], + "admin-addresses", + ["mailto:xmpp@shakespeare.lit", "xmpp:admins@shakespeare.lit"]} + ]}]}, + ... + ]}. +\end{verbatim} \end{itemize} - \makesubsection{modecho}{\modecho{}} \ind{modules!\modecho{}}\ind{debugging} diff --git a/src/jlib.hrl b/src/jlib.hrl index 32765e08a..955670e33 100644 --- a/src/jlib.hrl +++ b/src/jlib.hrl @@ -57,6 +57,7 @@ -define(NS_COMMANDS, "http://jabber.org/protocol/commands"). -define(NS_BYTESTREAMS, "http://jabber.org/protocol/bytestreams"). -define(NS_ADMIN, "http://jabber.org/protocol/admin"). +-define(NS_SERVERINFO, "http://jabber.org/network/serverinfo"). -define(NS_RSM, "http://jabber.org/protocol/rsm"). -define(NS_EJABBERD_CONFIG, "ejabberd:config"). diff --git a/src/mod_disco.erl b/src/mod_disco.erl index b688427b6..ad9975b15 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -1,7 +1,7 @@ %%%---------------------------------------------------------------------- %%% File : mod_disco.erl %%% Author : Alexey Shchepin -%%% Purpose : Service Discovery (JEP-0030) support +%%% Purpose : Service Discovery (XEP-0030) support %%% Created : 1 Jan 2003 by Alexey Shchepin %%% %%% @@ -41,6 +41,7 @@ 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, @@ -79,6 +80,7 @@ start(Host, Opts) -> ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, get_sm_items, 100), ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 100), ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, get_sm_identity, 100), + ejabberd_hooks:add(disco_info, Host, ?MODULE, get_info, 100), ok. stop(Host) -> @@ -88,6 +90,7 @@ stop(Host) -> ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, get_local_identity, 100), ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_local_features, 100), ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, get_local_services, 100), + ejabberd_hooks:delete(disco_info, Host, ?MODULE, get_info, 100), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS), @@ -153,6 +156,8 @@ process_local_iq_info(From, To, #iq{type = Type, lang = Lang, 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, @@ -166,6 +171,7 @@ process_local_iq_info(From, To, #iq{type = Type, lang = Lang, sub_el = [{xmlelement, "query", [{"xmlns", ?NS_DISCO_INFO} | ANode], Identity ++ + Info ++ lists:map(fun feature_to_xml/1, Features) }]}; {error, Error} -> @@ -362,3 +368,60 @@ get_user_resources(User, Server) -> [{"jid", User ++ "@" ++ Server ++ "/" ++ R}, {"name", User}], []} end, lists:sort(Rs)). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%% Support for: XEP-0157 Contact Addresses for XMPP Services + +get_info(_A, Host, Module, Node, _Lang) when Node == [] -> + Serverinfo_fields = get_fields_xml(Host, Module), + [{xmlelement, "x", + [{"xmlns", ?NS_XDATA}, {"type", "result"}], + [{xmlelement, "field", + [{"var", "FORM_TYPE"}, {"type", "hidden"}], + [{xmlelement, "value", + [], + [{xmlcdata, ?NS_SERVERINFO}] + }] + }] + ++ Serverinfo_fields + }]; + +get_info(_, _, _, _Node, _) -> + []. + +get_fields_xml(Host, Module) -> + Fields = gen_mod:get_module_opt(Host, ?MODULE, server_info, []), + + %% filter, and get only the ones allowed for this module + 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), + {xmlelement, "field", + [{"var", Var}], + Values_xml + }. + +values_to_xml(Values) -> + lists:map( + fun(Value) -> + {xmlelement, "value", + [], + [{xmlcdata, Value}] + } + end, + Values + ). diff --git a/src/mod_irc/mod_irc.erl b/src/mod_irc/mod_irc.erl index 256b767e8..f73ebde89 100644 --- a/src/mod_irc/mod_irc.erl +++ b/src/mod_irc/mod_irc.erl @@ -214,6 +214,9 @@ do_route1(Host, ServerHost, From, To, Packet) -> #iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS, sub_el = SubEl, lang = Lang} = IQ -> Node = xml:get_tag_attr_s("node", SubEl), + Info = ejabberd_hooks:run_fold( + disco_info, ServerHost, [], + [ServerHost, ?MODULE, "", ""]), case iq_disco(Node, Lang) of [] -> Res = IQ#iq{type = result, @@ -227,7 +230,7 @@ do_route1(Host, ServerHost, From, To, Packet) -> Res = IQ#iq{type = result, sub_el = [{xmlelement, "query", [{"xmlns", XMLNS}], - DiscoInfo}]}, + DiscoInfo ++ Info}]}, ejabberd_router:route(To, From, jlib:iq_to_xml(Res)) diff --git a/src/mod_muc/mod_muc.erl b/src/mod_muc/mod_muc.erl index 2a7242fdf..69f1983fb 100644 --- a/src/mod_muc/mod_muc.erl +++ b/src/mod_muc/mod_muc.erl @@ -353,10 +353,14 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper, case jlib:iq_query_info(Packet) of #iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS, sub_el = _SubEl, lang = Lang} = IQ -> + Info = ejabberd_hooks:run_fold( + disco_info, ServerHost, [], + [ServerHost, ?MODULE, "", ""]), Res = IQ#iq{type = result, sub_el = [{xmlelement, "query", [{"xmlns", XMLNS}], - iq_disco_info(Lang)}]}, + iq_disco_info(Lang) + ++Info}]}, ejabberd_router:route(To, From, jlib:iq_to_xml(Res)); diff --git a/src/mod_proxy65/mod_proxy65_service.erl b/src/mod_proxy65/mod_proxy65_service.erl index 901b5261c..c832b1a0d 100644 --- a/src/mod_proxy65/mod_proxy65_service.erl +++ b/src/mod_proxy65/mod_proxy65_service.erl @@ -120,9 +120,13 @@ delete_listener(Host) -> %%%------------------------ %% disco#info request -process_iq(_, #iq{type = get, xmlns = ?NS_DISCO_INFO, lang = Lang} = IQ, #state{name=Name}) -> +process_iq(_, #iq{type = get, xmlns = ?NS_DISCO_INFO, lang = Lang} = IQ, + #state{name=Name, serverhost=ServerHost}) -> + Info = ejabberd_hooks:run_fold( + disco_info, ServerHost, [], [ServerHost, ?MODULE, "", ""]), IQ#iq{type = result, sub_el = - [{xmlelement, "query", [{"xmlns", ?NS_DISCO_INFO}], iq_disco_info(Lang, Name)}]}; + [{xmlelement, "query", [{"xmlns", ?NS_DISCO_INFO}], + iq_disco_info(Name, Lang) ++ Info}]}; %% disco#items request process_iq(_, #iq{type = get, xmlns = ?NS_DISCO_ITEMS} = IQ, _) -> diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl index e97bf69b9..58e571509 100644 --- a/src/mod_pubsub/mod_pubsub.erl +++ b/src/mod_pubsub/mod_pubsub.erl @@ -888,12 +888,15 @@ do_route(ServerHost, Access, Plugins, Host, From, To, Packet) -> sub_el = SubEl, lang = Lang} = IQ -> {xmlelement, _, QAttrs, _} = SubEl, Node = xml:get_attr_s("node", QAttrs), + Info = ejabberd_hooks:run_fold( + disco_info, ServerHost, [], + [ServerHost, ?MODULE, "", ""]), Res = case iq_disco_info(Host, Node, From, Lang) of {result, IQRes} -> jlib:iq_to_xml( IQ#iq{type = result, sub_el = [{xmlelement, "query", - QAttrs, IQRes}]}); + QAttrs, IQRes++Info}]}); {error, Error} -> jlib:make_error_reply(Packet, Error) end, diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index 7886163b1..ee109e598 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -367,6 +367,9 @@ do_route(ServerHost, From, To, Packet) -> Packet, ?ERR_NOT_ALLOWED), ejabberd_router:route(To, From, Err); get -> + Info = ejabberd_hooks:run_fold( + disco_info, ServerHost, [], + [ServerHost, ?MODULE, "", ""]), ResIQ = IQ#iq{type = result, sub_el = [{xmlelement, @@ -384,7 +387,7 @@ do_route(ServerHost, From, To, Packet) -> [{"var", ?NS_SEARCH}], []}, {xmlelement, "feature", [{"var", ?NS_VCARD}], []} - ] + ] ++ Info }]}, ejabberd_router:route(To, From, diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl index 68faa97da..0e8541067 100644 --- a/src/mod_vcard_ldap.erl +++ b/src/mod_vcard_ldap.erl @@ -406,6 +406,7 @@ do_route(State, From, To, Packet) -> route(State, From, To, Packet) -> #jid{user = User, resource = Resource} = To, + ServerHost = State#state.serverhost, if (User /= "") or (Resource /= "") -> Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE), @@ -467,6 +468,9 @@ route(State, From, To, Packet) -> Packet, ?ERR_NOT_ALLOWED), ejabberd_router:route(To, From, Err); get -> + Info = ejabberd_hooks:run_fold( + disco_info, ServerHost, [], + [ServerHost, ?MODULE, "", ""]), ResIQ = IQ#iq{type = result, sub_el = [{xmlelement, @@ -482,7 +486,7 @@ route(State, From, To, Packet) -> [{"var", ?NS_SEARCH}], []}, {xmlelement, "feature", [{"var", ?NS_VCARD}], []} - ] + ] ++ Info }]}, ejabberd_router:route(To, From, diff --git a/src/mod_vcard_odbc.erl b/src/mod_vcard_odbc.erl index 3f74f71ba..44d8eacd6 100644 --- a/src/mod_vcard_odbc.erl +++ b/src/mod_vcard_odbc.erl @@ -344,6 +344,9 @@ do_route(ServerHost, From, To, Packet) -> Packet, ?ERR_NOT_ALLOWED), ejabberd_router:route(To, From, Err); get -> + Info = ejabberd_hooks:run_fold( + disco_info, ServerHost, [], + [ServerHost, ?MODULE, "", ""]), ResIQ = IQ#iq{type = result, sub_el = [{xmlelement, @@ -359,7 +362,7 @@ do_route(ServerHost, From, To, Packet) -> [{"var", ?NS_SEARCH}], []}, {xmlelement, "feature", [{"var", ?NS_VCARD}], []} - ] + ] ++ Info }]}, ejabberd_router:route(To, From,