+
</P><P>This module adds support for Service Discovery (<A HREF="http://www.xmpp.org/extensions/xep-0030.html">XEP-0030</A>). With
this module enabled, services on your server can be discovered by
Jabber clients. Note that <TT>ejabberd</TT> has no modules with support
<B><TT>iqdisc</TT></B></DT><DD CLASS="dd-description"> This specifies
the processing discipline for Service Discovery (<TT>http://jabber.org/protocol/disco#items</TT> and
<TT>http://jabber.org/protocol/disco#info</TT>) IQ queries (see section <A HREF="#modiqdiscoption">3.3.2</A>).
-</DD><DT CLASS="dt-description"><B><TT>extra_domains</TT></B></DT><DD CLASS="dd-description"> With this option,
-extra domains can be added to the Service Discovery item list.
+</DD><DT CLASS="dt-description"><B><TT>{extra_domains, [ Domain ]}</TT></B></DT><DD CLASS="dd-description"> With this option,
+you can specify a list of extra domains that are added to the Service Discovery item list.
+</DD><DT CLASS="dt-description"><B><TT>{server_info, [ {Modules, Field, [Value]} ]}</TT></B></DT><DD CLASS="dd-description">
+Specify additional information about the server,
+as described in Contact Addresses for XMPP Services (<A HREF="http://www.xmpp.org/extensions/xep-0157.html">XEP-0157</A>).
+<TT>Modules</TT> can be the keyword ‘all’,
+in which case the information is reported in all the services;
+or a list of <TT>ejabberd</TT> modules,
+in which case the information is only specified for the services provided by those modules.
+Any arbitrary <TT>Field</TT> and <TT>Value</TT> can be specified, not only contact addresses.
</DD></DL><P>Examples:
</P><UL CLASS="itemize"><LI CLASS="li-itemize">
To serve a link to the Jabber User Directory on <TT>jabber.org</TT>:
"example.com"]}]},
...
]}.
+</PRE></LI><LI CLASS="li-itemize">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:
+<PRE CLASS="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"]}
+ ]}]},
+ ...
+ ]}.
</PRE></LI></UL><P> <A NAME="modecho"></A> </P><!--TOC subsection <TT>mod_echo</TT>-->
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc42">3.3.5</A>  <A HREF="#modecho"><TT>mod_echo</TT></A></H3><!--SEC END --><P> <A NAME="modecho"></A>
</P><P>This module simply echoes any Jabber
Maximum number of Erlang processes.
</DD><DT CLASS="dt-description"><B><TT>-remsh ejabberd@localhost</TT></B></DT><DD CLASS="dd-description">
Open an Erlang shell in a remote Erlang node.
+</DD><DT CLASS="dt-description"><B><TT>-hidden</TT></B></DT><DD CLASS="dd-description">
+ 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 <TT>ctl</TT> or <TT>debug</TT> node.
</DD></DL><P>
Note that some characters need to be escaped when used in shell scripts, for instance <CODE>"</CODE> and <CODE>{}</CODE>.
You can find other options in the Erlang manual page (<TT>erl -man erl</TT>).</P><P> <A NAME="eja-commands"></A> </P><!--TOC section <TT>ejabberd</TT> Commands-->
\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
\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:
...
]}.
\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}
-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").
%%%----------------------------------------------------------------------
%%% File : mod_disco.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Service Discovery (JEP-0030) support
+%%% Purpose : Service Discovery (XEP-0030) support
%%% Created : 1 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
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,
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) ->
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),
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,
sub_el = [{xmlelement, "query",
[{"xmlns", ?NS_DISCO_INFO} | ANode],
Identity ++
+ Info ++
lists:map(fun feature_to_xml/1, Features)
}]};
{error, Error} ->
[{"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
+ ).
#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,
Res = IQ#iq{type = result,
sub_el = [{xmlelement, "query",
[{"xmlns", XMLNS}],
- DiscoInfo}]},
+ DiscoInfo ++ Info}]},
ejabberd_router:route(To,
From,
jlib:iq_to_xml(Res))
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));
%%%------------------------
%% 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, _) ->
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,
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,
[{"var", ?NS_SEARCH}], []},
{xmlelement, "feature",
[{"var", ?NS_VCARD}], []}
- ]
+ ] ++ Info
}]},
ejabberd_router:route(To,
From,
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),
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,
[{"var", ?NS_SEARCH}], []},
{xmlelement, "feature",
[{"var", ?NS_VCARD}], []}
- ]
+ ] ++ Info
}]},
ejabberd_router:route(To,
From,
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,
[{"var", ?NS_SEARCH}], []},
{xmlelement, "feature",
[{"var", ?NS_VCARD}], []}
- ]
+ ] ++ Info
}]},
ejabberd_router:route(To,
From,