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:
-
To serve a link to the Jabber User Directory on jabber.org:
@@ -1996,6 +2005,28 @@ To serve a link to the Jabber User Directory on jabber.org:
"example.com"]}]},
...
]}.
+
- 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:
+
{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"]}
+ ]}]},
+ ...
+ ]}.
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,