mirror of
https://github.com/processone/ejabberd.git
synced 2024-08-14 01:38:09 +02:00
Merge branch '2.1.x' of git://github.com/processone/ejabberd.git into applepush
This commit is contained in:
commit
8fe6ed011d
6
README
6
README
@ -9,10 +9,8 @@ To compile ejabberd you need:
|
||||
- GNU Make
|
||||
- GCC
|
||||
- Libexpat 1.95 or higher
|
||||
- Erlang/OTP R10B-9 or higher. The recommended version is R12B-5.
|
||||
Support for R13 is experimental.
|
||||
- OpenSSL 0.9.6 or higher, for STARTTLS, SASL and SSL
|
||||
encryption. Optional, highly recommended.
|
||||
- Erlang/OTP R10B-9 or higher. Recommended versions: R12B-5 and R13B04
|
||||
- OpenSSL 0.9.8 or higher, for STARTTLS, SASL and SSL encryption.
|
||||
- Zlib 1.2.3 or higher, for Stream Compression support
|
||||
(XEP-0138). Optional.
|
||||
- Erlang mysql library. Optional. MySQL authentication/storage.
|
||||
|
@ -2,7 +2,7 @@
|
||||
"http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>Ejabberd 2.1.3 Developers Guide
|
||||
<TITLE>Ejabberd 2.1.x Developers Guide
|
||||
</TITLE>
|
||||
|
||||
<META http-equiv="Content-Type" content="text/html; charset=US-ASCII">
|
||||
@ -49,7 +49,7 @@ TD P{margin:0px;}
|
||||
<!--HEVEA command line is: /usr/bin/hevea -fix -pedantic dev.tex -->
|
||||
<!--CUT DEF section 1 --><P><A NAME="titlepage"></A>
|
||||
|
||||
</P><TABLE CLASS="title"><TR><TD><H1 CLASS="titlemain">Ejabberd 2.1.3 Developers Guide</H1><H3 CLASS="titlerest">Alexey Shchepin<BR>
|
||||
</P><TABLE CLASS="title"><TR><TD><H1 CLASS="titlemain">Ejabberd 2.1.x Developers Guide</H1><H3 CLASS="titlerest">Alexey Shchepin<BR>
|
||||
<A HREF="mailto:alexey@sevcom.net"><TT>mailto:alexey@sevcom.net</TT></A><BR>
|
||||
<A HREF="xmpp:aleksey@jabber.ru"><TT>xmpp:aleksey@jabber.ru</TT></A></H3></TD></TR>
|
||||
</TABLE><DIV CLASS="center">
|
||||
@ -194,6 +194,9 @@ operation are as follows:
|
||||
auth:User:Server:Password (check if a username/password pair is correct)
|
||||
</LI><LI CLASS="li-itemize">isuser:User:Server (check if it’s a valid user)
|
||||
</LI><LI CLASS="li-itemize">setpass:User:Server:Password (set user’s password)
|
||||
</LI><LI CLASS="li-itemize">tryregister:User:Server:Password (try to register an account)
|
||||
</LI><LI CLASS="li-itemize">removeuser:User:Server (remove this account)
|
||||
</LI><LI CLASS="li-itemize">removeuser3:User:Server:Password (remove this account if the password is correct)
|
||||
</LI></UL>
|
||||
</LI></UL>
|
||||
</LI><LI CLASS="li-itemize">write to stdout: AABB
|
||||
|
@ -176,6 +176,9 @@ That script is supposed to do theses actions, in an infinite loop:
|
||||
\item auth:User:Server:Password (check if a username/password pair is correct)
|
||||
\item isuser:User:Server (check if it's a valid user)
|
||||
\item setpass:User:Server:Password (set user's password)
|
||||
\item tryregister:User:Server:Password (try to register an account)
|
||||
\item removeuser:User:Server (remove this account)
|
||||
\item removeuser3:User:Server:Password (remove this account if the password is correct)
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
\item write to stdout: AABB
|
||||
|
@ -2,7 +2,7 @@
|
||||
"http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>Ejabberd 2.1.3 Feature Sheet
|
||||
<TITLE>Ejabberd 2.1.x Feature Sheet
|
||||
</TITLE>
|
||||
|
||||
<META http-equiv="Content-Type" content="text/html; charset=US-ASCII">
|
||||
@ -50,7 +50,7 @@ SPAN{width:20%; float:right; text-align:left; margin-left:auto;}
|
||||
<!--HEVEA command line is: /usr/bin/hevea -fix -pedantic features.tex -->
|
||||
<!--CUT DEF section 1 --><P><A NAME="titlepage"></A>
|
||||
|
||||
</P><TABLE CLASS="title"><TR><TD><H1 CLASS="titlemain">Ejabberd 2.1.3 Feature Sheet</H1><H3 CLASS="titlerest">Sander Devrieze<BR>
|
||||
</P><TABLE CLASS="title"><TR><TD><H1 CLASS="titlemain">Ejabberd 2.1.x Feature Sheet</H1><H3 CLASS="titlerest">Sander Devrieze<BR>
|
||||
<A HREF="mailto:s.devrieze@pandora.be"><TT>mailto:s.devrieze@pandora.be</TT></A><BR>
|
||||
<A HREF="xmpp:sander@devrieze.dyndns.org"><TT>xmpp:sander@devrieze.dyndns.org</TT></A></H3></TD></TR>
|
||||
</TABLE><DIV CLASS="center">
|
||||
|
302
doc/guide.html
302
doc/guide.html
@ -6,7 +6,7 @@
|
||||
|
||||
|
||||
|
||||
ejabberd 2.1.3
|
||||
ejabberd 2.1.x
|
||||
|
||||
Installation and Operation Guide
|
||||
|
||||
@ -76,7 +76,7 @@ BLOCKQUOTE.figure DIV.center DIV.center HR{display:none;}
|
||||
<HR SIZE=2><BR>
|
||||
<BR>
|
||||
|
||||
<TABLE CELLSPACING=6 CELLPADDING=0><TR><TD ALIGN=right NOWRAP> <FONT SIZE=6><B>ejabberd 2.1.3 </B></FONT></TD></TR>
|
||||
<TABLE CELLSPACING=6 CELLPADDING=0><TR><TD ALIGN=right NOWRAP> <FONT SIZE=6><B>ejabberd 2.1.x </B></FONT></TD></TR>
|
||||
<TR><TD ALIGN=right NOWRAP> </TD></TR>
|
||||
<TR><TD ALIGN=right NOWRAP> <FONT SIZE=6>Installation and Operation Guide</FONT></TD></TR>
|
||||
</TABLE><BR>
|
||||
@ -164,60 +164,61 @@ BLOCKQUOTE.figure DIV.center DIV.center HR{display:none;}
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc62">3.3.24  <TT>mod_time</TT></A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc63">3.3.25  <TT>mod_vcard</TT></A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc64">3.3.26  <TT>mod_vcard_ldap</TT></A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc65">3.3.27  <TT>mod_version</TT></A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc65">3.3.27  <TT>mod_vcard_xupdate</TT></A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc66">3.3.28  <TT>mod_version</TT></A>
|
||||
</LI></UL>
|
||||
</LI></UL>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc66">Chapter 4  Managing an <TT>ejabberd</TT> Server</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc67">Chapter 4  Managing an <TT>ejabberd</TT> Server</A>
|
||||
<UL CLASS="toc"><LI CLASS="li-toc">
|
||||
<A HREF="#htoc67">4.1  <TT>ejabberdctl</TT></A>
|
||||
<A HREF="#htoc68">4.1  <TT>ejabberdctl</TT></A>
|
||||
<UL CLASS="toc"><LI CLASS="li-toc">
|
||||
<A HREF="#htoc68">4.1.1  ejabberdctl Commands</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc69">4.1.2  Erlang Runtime System</A>
|
||||
<A HREF="#htoc69">4.1.1  ejabberdctl Commands</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc70">4.1.2  Erlang Runtime System</A>
|
||||
</LI></UL>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc70">4.2  <TT>ejabberd</TT> Commands</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc71">4.2  <TT>ejabberd</TT> Commands</A>
|
||||
<UL CLASS="toc"><LI CLASS="li-toc">
|
||||
<A HREF="#htoc71">4.2.1  List of ejabberd Commands</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc72">4.2.2  Restrict Execution with AccessCommands</A>
|
||||
<A HREF="#htoc72">4.2.1  List of ejabberd Commands</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc73">4.2.2  Restrict Execution with AccessCommands</A>
|
||||
</LI></UL>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc73">4.3  Web Admin</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc74">4.4  Ad-hoc Commands</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc75">4.5  Change Computer Hostname</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc74">4.3  Web Admin</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc75">4.4  Ad-hoc Commands</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc76">4.5  Change Computer Hostname</A>
|
||||
</LI></UL>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc76">Chapter 5  Securing <TT>ejabberd</TT></A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc77">Chapter 5  Securing <TT>ejabberd</TT></A>
|
||||
<UL CLASS="toc"><LI CLASS="li-toc">
|
||||
<A HREF="#htoc77">5.1  Firewall Settings</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc78">5.2  epmd</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc79">5.3  Erlang Cookie</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc80">5.4  Erlang Node Name</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc81">5.5  Securing Sensitive Files</A>
|
||||
<A HREF="#htoc78">5.1  Firewall Settings</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc79">5.2  epmd</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc80">5.3  Erlang Cookie</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc81">5.4  Erlang Node Name</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc82">5.5  Securing Sensitive Files</A>
|
||||
</LI></UL>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc82">Chapter 6  Clustering</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc83">Chapter 6  Clustering</A>
|
||||
<UL CLASS="toc"><LI CLASS="li-toc">
|
||||
<A HREF="#htoc83">6.1  How it Works</A>
|
||||
<A HREF="#htoc84">6.1  How it Works</A>
|
||||
<UL CLASS="toc"><LI CLASS="li-toc">
|
||||
<A HREF="#htoc84">6.1.1  Router</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc85">6.1.2  Local Router</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc86">6.1.3  Session Manager</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc87">6.1.4  s2s Manager</A>
|
||||
<A HREF="#htoc85">6.1.1  Router</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc86">6.1.2  Local Router</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc87">6.1.3  Session Manager</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc88">6.1.4  s2s Manager</A>
|
||||
</LI></UL>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc88">6.2  Clustering Setup</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc89">6.3  Service Load-Balancing</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc89">6.2  Clustering Setup</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc90">6.3  Service Load-Balancing</A>
|
||||
<UL CLASS="toc"><LI CLASS="li-toc">
|
||||
<A HREF="#htoc90">6.3.1  Components Load-Balancing</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc91">6.3.2  Domain Load-Balancing Algorithm</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc92">6.3.3  Load-Balancing Buckets</A>
|
||||
<A HREF="#htoc91">6.3.1  Components Load-Balancing</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc92">6.3.2  Domain Load-Balancing Algorithm</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc93">6.3.3  Load-Balancing Buckets</A>
|
||||
</LI></UL>
|
||||
</LI></UL>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc93">Chapter 7  Debugging</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc94">Chapter 7  Debugging</A>
|
||||
<UL CLASS="toc"><LI CLASS="li-toc">
|
||||
<A HREF="#htoc94">7.1  Log Files</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc95">7.2  Debug Console</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc96">7.3  Watchdog Alerts</A>
|
||||
<A HREF="#htoc95">7.1  Log Files</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc96">7.2  Debug Console</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc97">7.3  Watchdog Alerts</A>
|
||||
</LI></UL>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc97">Appendix A  Internationalization and Localization</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc98">Appendix B  Release Notes</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc99">Appendix C  Acknowledgements</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc100">Appendix D  Copyright Information</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc98">Appendix A  Internationalization and Localization</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc99">Appendix B  Release Notes</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc100">Appendix C  Acknowledgements</A>
|
||||
</LI><LI CLASS="li-toc"><A HREF="#htoc101">Appendix D  Copyright Information</A>
|
||||
</LI></UL><!--TOC chapter Introduction-->
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc1">Chapter 1</A>  Introduction</H1><!--SEC END --><P>
|
||||
<A NAME="intro"></A></P><P><TT>ejabberd</TT> is a free and open source instant messaging server written in <A HREF="http://www.erlang.org/">Erlang/OTP</A>.</P><P><TT>ejabberd</TT> is cross-platform, distributed, fault-tolerant, and based on open standards to achieve real-time communication.</P><P><TT>ejabberd</TT> is designed to be a rock-solid and feature rich XMPP server.</P><P><TT>ejabberd</TT> is suitable for small deployments, whether they need to be scalable or not, as well as extremely big deployments.</P><!--TOC section Key Features-->
|
||||
@ -344,8 +345,8 @@ as long as your system have all the dependencies.</P><P> <A NAME="installreq"></
|
||||
GNU Make
|
||||
</LI><LI CLASS="li-itemize">GCC
|
||||
</LI><LI CLASS="li-itemize">Libexpat 1.95 or higher
|
||||
</LI><LI CLASS="li-itemize">Erlang/OTP R10B-9 or higher. The recommended version is R12B-5. Support for R13 is experimental.
|
||||
</LI><LI CLASS="li-itemize">OpenSSL 0.9.6 or higher, for STARTTLS, SASL and SSL encryption. Optional, highly recommended.
|
||||
</LI><LI CLASS="li-itemize">Erlang/OTP R10B-9 or higher. The recommended versions are R12B-5 and R13B04.
|
||||
</LI><LI CLASS="li-itemize">OpenSSL 0.9.8 or higher, for STARTTLS, SASL and SSL encryption.
|
||||
</LI><LI CLASS="li-itemize">Zlib 1.2.3 or higher, for Stream Compression support (<A HREF="http://xmpp.org/extensions/xep-0138.html">XEP-0138</A>). Optional.
|
||||
</LI><LI CLASS="li-itemize">Erlang mysql library. Optional. For MySQL authentication or storage. See section <A HREF="#compilemysql">3.2.1</A>.
|
||||
</LI><LI CLASS="li-itemize">Erlang pgsql library. Optional. For PostgreSQL authentication or storage. See section <A HREF="#compilepgsql">3.2.3</A>.
|
||||
@ -389,7 +390,9 @@ To get the full list run the command:
|
||||
Enable the use of XML based optimisations.
|
||||
It will for example use CDATA to escape characters in the XMPP stream.
|
||||
Use this option only if you are sure your XMPP clients include a fully compliant XML parser.<P> </P></DD><DT CLASS="dt-description"><B><TT>--disable-transient-supervisors</TT></B></DT><DD CLASS="dd-description">
|
||||
Disable the use of Erlang/OTP supervision for transient processes.
|
||||
Disable the use of Erlang/OTP supervision for transient processes.<P> </P></DD><DT CLASS="dt-description"><B><TT>--enable-nif</TT></B></DT><DD CLASS="dd-description">
|
||||
Replaces some critical Erlang functions with equivalents written in C to improve performance.
|
||||
This feature requires Erlang/OTP R13B04 or higher.
|
||||
</DD></DL><P> <A NAME="install"></A> </P><!--TOC subsection Install-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc12">2.4.4</A>  <A HREF="#install">Install</A></H3><!--SEC END --><P> <A NAME="install"></A>
|
||||
</P><P>To install <TT>ejabberd</TT> in the destination directories, run the command:
|
||||
@ -676,12 +679,12 @@ Handles c2s connections.<BR>
|
||||
<TT>zlib</TT>
|
||||
</DD><DT CLASS="dt-description"><B><TT>ejabberd_s2s_in</TT></B></DT><DD CLASS="dd-description">
|
||||
Handles incoming s2s connections.<BR>
|
||||
Options: <TT>max_stanza_size</TT>
|
||||
Options: <TT>max_stanza_size</TT>, <TT>shaper</TT>
|
||||
</DD><DT CLASS="dt-description"><B><TT>ejabberd_service</TT></B></DT><DD CLASS="dd-description">
|
||||
Interacts with an <A HREF="http://www.ejabberd.im/tutorials-transports">external component</A>
|
||||
(as defined in the Jabber Component Protocol (<A HREF="http://xmpp.org/extensions/xep-0114.html">XEP-0114</A>).<BR>
|
||||
Options: <TT>access</TT>, <TT>hosts</TT>, <TT>max_fsm_queue</TT>,
|
||||
<TT>shaper</TT>, <TT>service_check_from</TT>
|
||||
<TT>service_check_from</TT>, <TT>shaper</TT>
|
||||
</DD><DT CLASS="dt-description"><B><TT>ejabberd_stun</TT></B></DT><DD CLASS="dd-description">
|
||||
Handles STUN Binding requests as defined in
|
||||
<A HREF="http://tools.ietf.org/html/rfc5389">RFC 5389</A>.<BR>
|
||||
@ -701,13 +704,10 @@ grow to. This should be increased if the server is going to handle
|
||||
lots of new incoming connections as they may be dropped if there is
|
||||
no space in the queue (and ejabberd was not able to accept them
|
||||
immediately). Default value is 5.
|
||||
</DD><DT CLASS="dt-description"><B><TT>captcha</TT></B></DT><DD CLASS="dd-description">
|
||||
Simple web page that allows a user to fill a CAPTCHA challenge (see section <A HREF="#captcha">3.1.8</A>).
|
||||
</DD><DT CLASS="dt-description"><B><TT>{certfile, Path}</TT></B></DT><DD CLASS="dd-description"> Full path to a file containing the default SSL certificate.
|
||||
To define a certificate file specific for a given domain, use the global option <TT>domain_certfile</TT>.
|
||||
</DD><DT CLASS="dt-description"><B><TT>{service_check_from, true|false}</TT></B></DT><DD CLASS="dd-description">
|
||||
This option can be used with <TT>ejabberd_service</TT> only. It is
|
||||
used to disable control on the from field on packets send by an
|
||||
external components. The option can be either <TT>true</TT> or
|
||||
<TT>false</TT>. The default value is <TT>true</TT> which conforms to <A HREF="http://xmpp.org/extensions/xep-0114.html">XEP-0114</A>.
|
||||
</DD><DT CLASS="dt-description"><B><TT>{hosts, [Hostname, ...], [HostOption, ...]}</TT></B></DT><DD CLASS="dd-description">
|
||||
The external Jabber component that connects to this <TT>ejabberd_service</TT>
|
||||
can serve one or more hostnames.
|
||||
@ -717,8 +717,6 @@ when attempt to connect to ejabberd: <TT>{password, Secret}</TT>.
|
||||
Note that you cannot define in a single <TT>ejabberd_service</TT> components of
|
||||
different services: add an <TT>ejabberd_service</TT> for each service,
|
||||
as seen in an example below.
|
||||
</DD><DT CLASS="dt-description"><B><TT>captcha</TT></B></DT><DD CLASS="dd-description">
|
||||
Simple web page that allows a user to fill a CAPTCHA challenge (see section <A HREF="#captcha">3.1.8</A>).
|
||||
</DD><DT CLASS="dt-description"><B><TT>http_bind</TT></B></DT><DD CLASS="dd-description">
|
||||
This option enables HTTP Binding (<A HREF="http://xmpp.org/extensions/xep-0124.html">XEP-0124</A> and <A HREF="http://xmpp.org/extensions/xep-0206.html">XEP-0206</A>) support. HTTP Bind
|
||||
enables access via HTTP requests to <TT>ejabberd</TT> from behind firewalls which
|
||||
@ -763,7 +761,7 @@ Default value: ’undefined’.
|
||||
</DD><DT CLASS="dt-description"><B><TT>{max_stanza_size, Size}</TT></B></DT><DD CLASS="dd-description">
|
||||
This option specifies an
|
||||
approximate maximum size in bytes of XML stanzas. Approximate,
|
||||
because it is calculated with the precision of one block of readed
|
||||
because it is calculated with the precision of one block of read
|
||||
data. For example <CODE>{max_stanza_size, 65536}</CODE>. The default
|
||||
value is <TT>infinity</TT>. Recommended values are 65536 for c2s
|
||||
connections and 131072 for s2s connections. s2s max stanza size
|
||||
@ -775,10 +773,13 @@ For example, if you want <TT>mod_foo</TT> to serve the URIs that start with <TT>
|
||||
and you also want <TT>mod_http_bind</TT> to serve the URIs <TT>/http-bind/</TT>,
|
||||
use this option: <TT>{request_handlers, [{["a", "b"], mod_foo}, {["http-bind"], mod_http_bind}]}</TT>
|
||||
</DD><DT CLASS="dt-description"><B><TT>{service_check_from, true|false}</TT></B></DT><DD CLASS="dd-description">
|
||||
By enabling this option, <TT>ejabberd</TT> allows the component to send packets with any arbitrary domain in the ’from’ attribute.
|
||||
Note that <A HREF="http://xmpp.org/extensions/xep-0114.html">XEP-0114</A> requires that the domain must match the hostname of the component.
|
||||
Only enable this option if you are completely sure you need to enable it.
|
||||
Default value: false.
|
||||
|
||||
This option can be used with <TT>ejabberd_service</TT> only.
|
||||
<A HREF="http://xmpp.org/extensions/xep-0114.html">XEP-0114</A> requires that the domain must match the hostname of the component.
|
||||
If this option is set to <TT>false</TT>, <TT>ejabberd</TT> will allow the component
|
||||
to send stanzas with any arbitrary domain in the ’from’ attribute.
|
||||
Only use this option if you are completely sure about it.
|
||||
The default value is <TT>true</TT>, to be compliant with <A HREF="http://xmpp.org/extensions/xep-0114.html">XEP-0114</A>.
|
||||
</DD><DT CLASS="dt-description"><B><TT>{shaper, none|ShaperName}</TT></B></DT><DD CLASS="dd-description"> This option defines a
|
||||
shaper for the port (see section <A HREF="#shapers">3.1.6</A>). The default value
|
||||
is <TT>none</TT>.
|
||||
@ -1031,14 +1032,13 @@ for user authentication. The syntax is:
|
||||
</P><DL CLASS="description"><DT CLASS="dt-description"><B><TT>{auth_method, [Method, ...]}.</TT></B></DT></DL><P>The following authentication methods are supported by <TT>ejabberd</TT>:
|
||||
</P><UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
internal (default) — See section <A HREF="#internalauth">3.1.4</A>.
|
||||
</LI><LI CLASS="li-itemize">external — There are <A HREF="http://www.ejabberd.im/extauth">some
|
||||
example authentication scripts</A>.
|
||||
</LI><LI CLASS="li-itemize">external — See section <A HREF="#extauth">3.1.4</A>.
|
||||
</LI><LI CLASS="li-itemize">ldap — See section <A HREF="#ldap">3.2.5</A>.
|
||||
</LI><LI CLASS="li-itemize">odbc — See section <A HREF="#mysql">3.2.1</A>, <A HREF="#pgsql">3.2.3</A>,
|
||||
<A HREF="#mssql">3.2.2</A> and <A HREF="#odbc">3.2.4</A>.
|
||||
</LI><LI CLASS="li-itemize">anonymous — See section <A HREF="#saslanonymous">3.1.4</A>.
|
||||
</LI><LI CLASS="li-itemize">pam — See section <A HREF="#pam">3.1.4</A>.
|
||||
</LI></UL><P>Account creation is only supported by internal and odbc methods.</P><P> <A NAME="internalauth"></A> </P><!--TOC subsubsection Internal-->
|
||||
</LI></UL><P>Account creation is only supported by internal, external and odbc methods.</P><P> <A NAME="internalauth"></A> </P><!--TOC subsubsection Internal-->
|
||||
<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#internalauth">Internal</A></H4><!--SEC END --><P> <A NAME="internalauth"></A>
|
||||
</P><P><TT>ejabberd</TT> uses its internal Mnesia database as the default authentication method.
|
||||
The value <TT>internal</TT> will enable the internal authentication method.</P><P>Examples:
|
||||
@ -1049,7 +1049,35 @@ authentication on <TT>example.net</TT>:
|
||||
{host_config, "example.net", [{auth_method, [ldap]}]}.
|
||||
</PRE></LI><LI CLASS="li-itemize">To use internal authentication on all virtual hosts:
|
||||
<PRE CLASS="verbatim">{auth_method, internal}.
|
||||
</PRE></LI></UL><P> <A NAME="saslanonymous"></A> </P><!--TOC subsubsection SASL Anonymous and Anonymous Login-->
|
||||
</PRE></LI></UL><P> <A NAME="extauth"></A> </P><!--TOC subsubsection External Script-->
|
||||
<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#extauth">External Script</A></H4><!--SEC END --><P> <A NAME="extauth"></A>
|
||||
</P><P>In this authentication method, when <TT>ejabberd</TT> starts,
|
||||
it start a script, and calls it to perform authentication tasks.</P><P>The server administrator can write the external authentication script
|
||||
in any language.
|
||||
The details on the interface between ejabberd and the script are described
|
||||
in the <TT>ejabberd Developers Guide</TT>.
|
||||
There are also <A HREF="http://www.ejabberd.im/extauth">several example authentication scripts</A>.</P><P>These are the specific options:
|
||||
</P><DL CLASS="description"><DT CLASS="dt-description">
|
||||
<B><TT>{extauth_program, PathToScript}</TT></B></DT><DD CLASS="dd-description">
|
||||
Indicate in this option the full path to the external authentication script.
|
||||
The script must be executable by ejabberd.</DD><DT CLASS="dt-description"><B><TT>{extauth_instances, Integer}</TT></B></DT><DD CLASS="dd-description">
|
||||
Indicate how many instances of the script to run simultaneously to serve authentication in the virtual host.
|
||||
The default value is the minimum number: 1.</DD><DT CLASS="dt-description"><B><TT>{extauth_cache, false|CacheTimeInteger}</TT></B></DT><DD CLASS="dd-description">
|
||||
The value <TT>false</TT> disables the caching feature, this is the default.
|
||||
The integer <TT>0</TT> (zero) enables caching for statistics, but doesn’t use that cached information to authenticate users.
|
||||
If another integer value is set, caching is enabled both for statistics and for authentication:
|
||||
the CacheTimeInteger indicates the number of seconds that ejabberd can reuse
|
||||
the authentication information since the user last disconnected,
|
||||
to verify again the user authentication without querying again the extauth script.
|
||||
Note: caching should not be enabled in a host if internal auth is also enabled.
|
||||
If caching is enabled, <TT>mod_last</TT> or <TT>mod_last_odbc</TT> must be enabled also in that vhost.
|
||||
</DD></DL><P>This example sets external authentication, the extauth script, enables caching for 10 minutes,
|
||||
and starts three instances of the script for each virtual host defined in ejabberd:
|
||||
</P><PRE CLASS="verbatim">{auth_method, [external]}.
|
||||
{extauth_program, "/etc/ejabberd/JabberAuth.class.php"}.
|
||||
{extauth_cache, 600}.
|
||||
{extauth_instances, 3}.
|
||||
</PRE><P> <A NAME="saslanonymous"></A> </P><!--TOC subsubsection SASL Anonymous and Anonymous Login-->
|
||||
<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#saslanonymous">SASL Anonymous and Anonymous Login</A></H4><!--SEC END --><P> <A NAME="saslanonymous"></A>
|
||||
</P><P>The value <TT>anonymous</TT> will enable the internal authentication method.</P><P>The anonymous authentication method can be configured with the following
|
||||
options. Remember that you can use the <TT>host_config</TT> option to set virtual
|
||||
@ -1101,6 +1129,10 @@ PAM authentication is disabled by default, so you have to configure and compile
|
||||
<B><TT>{pam_service, Name}</TT></B></DT><DD CLASS="dd-description">This option defines the PAM service name.
|
||||
Default is <TT>"ejabberd"</TT>. Refer to the PAM documentation of your operation system
|
||||
for more information.
|
||||
</DD><DT CLASS="dt-description"><B><TT>{pam_userinfotype, username|jid}</TT></B></DT><DD CLASS="dd-description">
|
||||
This option defines what type of information about the user ejabberd
|
||||
provides to the PAM service: only the username, or the user JID.
|
||||
Default is <TT>username</TT>.
|
||||
</DD></DL><P>Example:
|
||||
</P><PRE CLASS="verbatim">{auth_method, [pam]}.
|
||||
{pam_service, "ejabberd"}.
|
||||
@ -1509,17 +1541,12 @@ enabled. This can be done, by using next commands:
|
||||
</P><PRE CLASS="verbatim">./configure --enable-odbc --enable-mssql && make install
|
||||
</PRE><P> <A NAME="configuremssql"></A> </P><!--TOC subsubsection Database Connection-->
|
||||
<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#configuremssql">Database Connection</A></H4><!--SEC END --><P> <A NAME="configuremssql"></A>
|
||||
</P><P>By default <TT>ejabberd</TT> opens 10 connections to the database for each virtual host.
|
||||
Use this option to modify the value:
|
||||
</P><PRE CLASS="verbatim">{odbc_pool_size, 10}.
|
||||
</PRE><P>You can configure an interval to make a dummy SQL request
|
||||
to keep alive the connections to the database.
|
||||
The default value is ’undefined’, so no keepalive requests are made.
|
||||
Specify in seconds: for example 28800 means 8 hours.
|
||||
</P><PRE CLASS="verbatim">{odbc_keepalive_interval, undefined}.
|
||||
</PRE><P> <A NAME="mssqlauth"></A> </P><!--TOC subsubsection Authentication-->
|
||||
</P><P>The configuration of Database Connection for a Microsoft SQL Server
|
||||
is the same as the configuration for
|
||||
ODBC compatible servers (see section <A HREF="#configureodbc">3.2.4</A>).</P><P> <A NAME="mssqlauth"></A> </P><!--TOC subsubsection Authentication-->
|
||||
<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#mssqlauth">Authentication</A></H4><!--SEC END --><P> <A NAME="mssqlauth"></A>
|
||||
</P><P>The configuration of Microsoft SQL Server is the same as the configuration of
|
||||
</P><P>The configuration of Authentication for a Microsoft SQL Server
|
||||
is the same as the configuration for
|
||||
ODBC compatible servers (see section <A HREF="#odbcauth">3.2.4</A>).</P><P> <A NAME="mssqlstorage"></A> </P><!--TOC subsubsection Storage-->
|
||||
<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#mssqlstorage">Storage</A></H4><!--SEC END --><P> <A NAME="mssqlstorage"></A>
|
||||
</P><P>Microsoft SQL Server also can be used to store information into from several
|
||||
@ -1635,10 +1662,14 @@ module loaded!</P><P> <A NAME="ldap"></A> </P><!--TOC subsection LDAP-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc37">3.2.5</A>  <A HREF="#ldap">LDAP</A></H3><!--SEC END --><P> <A NAME="ldap"></A>
|
||||
</P><P><TT>ejabberd</TT> has built-in LDAP support. You can authenticate users against LDAP
|
||||
server and use LDAP directory as vCard storage. Shared rosters are not supported
|
||||
yet.</P><P>Note that <TT>ejabberd</TT> treats LDAP as a read-only storage:
|
||||
yet.</P><P>Usually <TT>ejabberd</TT> treats LDAP as a read-only storage:
|
||||
it is possible to consult data, but not possible to
|
||||
create accounts, change password or edit vCard that is stored in LDAP.</P><P> <A NAME="ldapconnection"></A> </P><!--TOC subsubsection Connection-->
|
||||
<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#ldapconnection">Connection</A></H4><!--SEC END --><P> <A NAME="ldapconnection"></A> </P><P>Parameters:
|
||||
create accounts or edit vCard that is stored in LDAP.
|
||||
However, it is possible to change passwords if <TT>mod_register</TT> module is enabled
|
||||
and LDAP server supports
|
||||
<A HREF="http://tools.ietf.org/html/rfc3062">RFC 3062</A>.</P><P> <A NAME="ldapconnection"></A> </P><!--TOC subsubsection Connection-->
|
||||
<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#ldapconnection">Connection</A></H4><!--SEC END --><P> <A NAME="ldapconnection"></A> </P><P>Two connections are established to the LDAP server per vhost,
|
||||
one for authentication and other for regular calls.</P><P>Parameters:
|
||||
</P><DL CLASS="description"><DT CLASS="dt-description">
|
||||
<B><TT>{ldap_servers, [Servers, ...]}</TT></B></DT><DD CLASS="dd-description"> List of IP addresses or DNS names of your
|
||||
LDAP servers. This option is required.
|
||||
@ -1647,6 +1678,11 @@ Allowed values are: <TT>none</TT>, <TT>tls</TT>.
|
||||
The value <TT>tls</TT> enables encryption by using LDAP over SSL.
|
||||
Note that STARTTLS encryption is not supported.
|
||||
The default value is: <TT>none</TT>.
|
||||
</DD><DT CLASS="dt-description"><B><TT>{ldap_tls_verify, false|soft|hard}</TT></B></DT><DD CLASS="dd-description">
|
||||
This option specifies whether to verify LDAP server certificate or not when TLS is enabled.
|
||||
When <TT>hard</TT> is enabled <TT>ejabberd</TT> doesn’t proceed if a certificate is invalid.
|
||||
When <TT>soft</TT> is enabled <TT>ejabberd</TT> proceeds even if check fails.
|
||||
The default is <TT>false</TT> which means no checks are performed.
|
||||
</DD><DT CLASS="dt-description"><B><TT>{ldap_port, Number}</TT></B></DT><DD CLASS="dd-description"> Port to connect to your LDAP server.
|
||||
The default port is 389 if encryption is disabled; and 636 if encryption is enabled.
|
||||
If you configure a value, it is stored in <TT>ejabberd</TT>’s database.
|
||||
@ -1662,8 +1698,9 @@ value is <TT>""</TT>.
|
||||
{ldap_port, 389}.
|
||||
{ldap_rootdn, "cn=Manager,dc=domain,dc=org"}.
|
||||
{ldap_password, "secret"}.
|
||||
</PRE><P>Note that current LDAP implementation does not support SASL authentication.</P><P> <A NAME="ldapauth"></A> </P><!--TOC subsubsection Authentication-->
|
||||
<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#ldapauth">Authentication</A></H4><!--SEC END --><P> <A NAME="ldapauth"></A> </P><P>You can authenticate users against an LDAP directory. Available options are:</P><DL CLASS="description"><DT CLASS="dt-description">
|
||||
</PRE><P> <A NAME="ldapauth"></A> </P><!--TOC subsubsection Authentication-->
|
||||
<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#ldapauth">Authentication</A></H4><!--SEC END --><P> <A NAME="ldapauth"></A> </P><P>You can authenticate users against an LDAP directory.
|
||||
Note that current LDAP implementation does not support SASL authentication.</P><P>Available options are:</P><DL CLASS="description"><DT CLASS="dt-description">
|
||||
<B><TT>{ldap_base, Base}</TT></B></DT><DD CLASS="dd-description">LDAP base directory which stores
|
||||
users accounts. This option is required.
|
||||
</DD><DT CLASS="dt-description"><B><TT>{ldap_uids, [ {ldap_uidattr} | {ldap_uidattr, ldap_uidattr_format}, ...]}</TT></B></DT><DD CLASS="dd-description">
|
||||
@ -1865,6 +1902,8 @@ all entries end with a comma:
|
||||
<TR><TD ALIGN=left NOWRAP><TT>mod_configure</TT></TD><TD ALIGN=left NOWRAP>Server configuration using Ad-Hoc</TD><TD ALIGN=left NOWRAP><TT>mod_adhoc</TT></TD></TR>
|
||||
<TR><TD ALIGN=left NOWRAP><A HREF="#moddisco"><TT>mod_disco</TT></A></TD><TD ALIGN=left NOWRAP>Service Discovery (<A HREF="http://xmpp.org/extensions/xep-0030.html">XEP-0030</A>)</TD><TD ALIGN=left NOWRAP> </TD></TR>
|
||||
<TR><TD ALIGN=left NOWRAP><A HREF="#modecho"><TT>mod_echo</TT></A></TD><TD ALIGN=left NOWRAP>Echoes XMPP stanzas</TD><TD ALIGN=left NOWRAP> </TD></TR>
|
||||
<TR><TD ALIGN=left NOWRAP><A HREF="#modhttpbind"><TT>mod_http_bind</TT></A></TD><TD ALIGN=left NOWRAP>XMPP over Bosh service (HTTP Binding)</TD><TD ALIGN=left NOWRAP> </TD></TR>
|
||||
<TR><TD ALIGN=left NOWRAP><A HREF="#modhttpfileserver"><TT>mod_http_fileserver</TT></A></TD><TD ALIGN=left NOWRAP>Small HTTP file server</TD><TD ALIGN=left NOWRAP> </TD></TR>
|
||||
<TR><TD ALIGN=left NOWRAP><A HREF="#modirc"><TT>mod_irc</TT></A></TD><TD ALIGN=left NOWRAP>IRC transport</TD><TD ALIGN=left NOWRAP> </TD></TR>
|
||||
<TR><TD ALIGN=left NOWRAP><A HREF="#modlast"><TT>mod_last</TT></A></TD><TD ALIGN=left NOWRAP>Last Activity (<A HREF="http://xmpp.org/extensions/xep-0012.html">XEP-0012</A>)</TD><TD ALIGN=left NOWRAP> </TD></TR>
|
||||
<TR><TD ALIGN=left NOWRAP><A HREF="#modlast"><TT>mod_last_odbc</TT></A></TD><TD ALIGN=left NOWRAP>Last Activity (<A HREF="http://xmpp.org/extensions/xep-0012.html">XEP-0012</A>)</TD><TD ALIGN=left NOWRAP>supported DB (*)</TD></TR>
|
||||
@ -1892,6 +1931,7 @@ all entries end with a comma:
|
||||
<TR><TD ALIGN=left NOWRAP><A HREF="#modvcard"><TT>mod_vcard</TT></A></TD><TD ALIGN=left NOWRAP>vcard-temp (<A HREF="http://xmpp.org/extensions/xep-0054.html">XEP-0054</A>)</TD><TD ALIGN=left NOWRAP> </TD></TR>
|
||||
<TR><TD ALIGN=left NOWRAP><A HREF="#modvcardldap"><TT>mod_vcard_ldap</TT></A></TD><TD ALIGN=left NOWRAP>vcard-temp (<A HREF="http://xmpp.org/extensions/xep-0054.html">XEP-0054</A>)</TD><TD ALIGN=left NOWRAP>LDAP server</TD></TR>
|
||||
<TR><TD ALIGN=left NOWRAP><A HREF="#modvcard"><TT>mod_vcard_odbc</TT></A></TD><TD ALIGN=left NOWRAP>vcard-temp (<A HREF="http://xmpp.org/extensions/xep-0054.html">XEP-0054</A>)</TD><TD ALIGN=left NOWRAP>supported DB (*)</TD></TR>
|
||||
<TR><TD ALIGN=left NOWRAP><A HREF="#modvcardxupdate"><TT>mod_vcard_xupdate</TT></A></TD><TD ALIGN=left NOWRAP>vCard-Based Avatars (<A HREF="http://xmpp.org/extensions/xep-0153.html">XEP-0153</A>)</TD><TD ALIGN=left NOWRAP><TT>mod_vcard</TT> or <TT>mod_vcard_odbc</TT></TD></TR>
|
||||
<TR><TD ALIGN=left NOWRAP><A HREF="#modversion"><TT>mod_version</TT></A></TD><TD ALIGN=left NOWRAP>Software Version (<A HREF="http://xmpp.org/extensions/xep-0092.html">XEP-0092</A>)</TD><TD ALIGN=left NOWRAP> </TD></TR>
|
||||
</TABLE>
|
||||
<DIV CLASS="center"><HR WIDTH="80%" SIZE=2></DIV></DIV></BLOCKQUOTE><UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
@ -2443,6 +2483,10 @@ the room occupants.
|
||||
</DD><DT CLASS="dt-description"><B><TT>{anonymous, true|false}</TT></B></DT><DD CLASS="dd-description"> The room is anonymous:
|
||||
occupants don’t see the real JIDs of other occupants.
|
||||
Note that the room moderators can always see the real JIDs of the occupants.
|
||||
</DD><DT CLASS="dt-description"><B><TT>{captcha_protected, false}</TT></B></DT><DD CLASS="dd-description">
|
||||
When a user tries to join a room where he has no affiliation (not owner, admin or member),
|
||||
the room requires him to fill a CAPTCHA challenge (see section <A HREF="#captcha">3.1.8</A>)
|
||||
in order to accept her join in the room.
|
||||
</DD><DT CLASS="dt-description"><B><TT>{logging, false|true}</TT></B></DT><DD CLASS="dd-description"> The public messages are logged using <TT>mod_muc_log</TT>.
|
||||
</DD><DT CLASS="dt-description"><B><TT>{max_users, 200}</TT></B></DT><DD CLASS="dd-description"> Maximum number of occupants in the room.
|
||||
</DD><DT CLASS="dt-description"><B><TT>{members_by_default, true|false}</TT></B></DT><DD CLASS="dd-description"> The occupants that enter the room are participants by default, so they have ’voice’.
|
||||
@ -2935,9 +2979,11 @@ the processing discipline for In-Band Registration (<TT>jabber:iq:register</TT>)
|
||||
</DD></DL><P>This module reads also another option defined globally for the server:
|
||||
<TT>{registration_timeout, Timeout}</TT>.
|
||||
This option limits the frequency of registration from a given IP or username.
|
||||
So, a user can’t register a new account from the same IP address or JID during
|
||||
this number of seconds after previous registration.
|
||||
Timeout is expressed in seconds, and must be an integer.
|
||||
So, a user that tries to register a new account from the same IP address or JID during
|
||||
this number of seconds after his previous registration
|
||||
will receive an error <TT>resource-constraint</TT> with the explanation:
|
||||
“Users are not allowed to register accounts so quickly”.
|
||||
The timeout is expressed in seconds, and it must be an integer.
|
||||
To disable this limitation,
|
||||
instead of an integer put a word like: <TT>infinity</TT>.
|
||||
Default value: 600 seconds.</P><P>Examples:
|
||||
@ -3225,9 +3271,12 @@ and that all virtual hosts will be searched instead of only the current one:
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc64">3.3.26</A>  <A HREF="#modvcardldap"><TT>mod_vcard_ldap</TT></A></H3><!--SEC END --><P> <A NAME="modvcardldap"></A>
|
||||
</P><P><TT>ejabberd</TT> can map LDAP attributes to vCard fields. This behaviour is
|
||||
implemented in the <TT>mod_vcard_ldap</TT> module. This module does not depend on the
|
||||
authentication method (see <A HREF="#ldapauth">3.2.5</A>).</P><P>Note that <TT>ejabberd</TT> treats LDAP as a read-only storage:
|
||||
authentication method (see <A HREF="#ldapauth">3.2.5</A>).</P><P>Usually <TT>ejabberd</TT> treats LDAP as a read-only storage:
|
||||
it is possible to consult data, but not possible to
|
||||
create accounts, change password or edit vCard that is stored in LDAP.</P><P>The <TT>mod_vcard_ldap</TT> module has
|
||||
create accounts or edit vCard that is stored in LDAP.
|
||||
However, it is possible to change passwords if <TT>mod_register</TT> module is enabled
|
||||
and LDAP server supports
|
||||
<A HREF="http://tools.ietf.org/html/rfc3062">RFC 3062</A>.</P><P>The <TT>mod_vcard_ldap</TT> module has
|
||||
its own optional parameters. The first group of parameters has the same
|
||||
meaning as the top-level LDAP parameters to set the authentication method:
|
||||
<TT>ldap_servers</TT>, <TT>ldap_port</TT>, <TT>ldap_rootdn</TT>,
|
||||
@ -3400,8 +3449,22 @@ searching his info in LDAP.</P></LI><LI CLASS="li-itemize"><TT>ldap_vcard_map</T
|
||||
{"Birthday", "BDAY"},
|
||||
{"Nickname", "NICKNAME"}
|
||||
]},
|
||||
</PRE></LI></UL><P> <A NAME="modversion"></A> </P><!--TOC subsection <TT>mod_version</TT>-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc65">3.3.27</A>  <A HREF="#modversion"><TT>mod_version</TT></A></H3><!--SEC END --><P> <A NAME="modversion"></A>
|
||||
</PRE></LI></UL><P> <A NAME="modvcardxupdate"></A> </P><!--TOC subsection <TT>mod_vcard_xupdate</TT>-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc65">3.3.27</A>  <A HREF="#modvcardxupdate"><TT>mod_vcard_xupdate</TT></A></H3><!--SEC END --><P> <A NAME="modvcardxupdate"></A>
|
||||
</P><P>The user’s client can store an avatar in the user vCard.
|
||||
The vCard-Based Avatars protocol (<A HREF="http://xmpp.org/extensions/xep-0153.html">XEP-0153</A>)
|
||||
provides a method for clients to inform the contacts what is the avatar hash value.
|
||||
However, simple or small clients may not implement that protocol.</P><P>If this module is enabled, all the outgoing client presence stanzas get automatically
|
||||
the avatar hash on behalf of the client.
|
||||
So, the contacts receive the presence stanzas with the Update Data described
|
||||
in <A HREF="http://xmpp.org/extensions/xep-0153.html">XEP-0153</A> as if the client would had inserted it itself.
|
||||
If the client had already included such element in the presence stanza,
|
||||
it is replaced with the element generated by ejabberd.</P><P>By enabling this module, each vCard modification produces a hash recalculation,
|
||||
and each presence sent by a client produces hash retrieval and a
|
||||
presence stanza rewrite.
|
||||
For this reason, enabling this module will introduce a computational overhead
|
||||
in servers with clients that change frequently their presence.</P><P> <A NAME="modversion"></A> </P><!--TOC subsection <TT>mod_version</TT>-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc66">3.3.28</A>  <A HREF="#modversion"><TT>mod_version</TT></A></H3><!--SEC END --><P> <A NAME="modversion"></A>
|
||||
</P><P>This module implements Software Version (<A HREF="http://xmpp.org/extensions/xep-0092.html">XEP-0092</A>). Consequently, it
|
||||
answers <TT>ejabberd</TT>’s version when queried.</P><P>Options:
|
||||
</P><DL CLASS="description"><DT CLASS="dt-description">
|
||||
@ -3410,8 +3473,8 @@ The default value is <TT>true</TT>.
|
||||
</DD><DT CLASS="dt-description"><B><TT>{iqdisc, Discipline}</TT></B></DT><DD CLASS="dd-description"> This specifies
|
||||
the processing discipline for Software Version (<TT>jabber:iq:version</TT>) IQ queries (see section <A HREF="#modiqdiscoption">3.3.2</A>).
|
||||
</DD></DL><P> <A NAME="manage"></A> </P><!--TOC chapter Managing an <TT>ejabberd</TT> Server-->
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc66">Chapter 4</A>  <A HREF="#manage">Managing an <TT>ejabberd</TT> Server</A></H1><!--SEC END --><P> <A NAME="manage"></A> </P><P> <A NAME="ejabberdctl"></A> </P><!--TOC section <TT>ejabberdctl</TT>-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc67">4.1</A>  <A HREF="#ejabberdctl"><TT>ejabberdctl</TT></A></H2><!--SEC END --><P> <A NAME="ejabberdctl"></A> </P><P>With the <TT>ejabberdctl</TT> command line administration script
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc67">Chapter 4</A>  <A HREF="#manage">Managing an <TT>ejabberd</TT> Server</A></H1><!--SEC END --><P> <A NAME="manage"></A> </P><P> <A NAME="ejabberdctl"></A> </P><!--TOC section <TT>ejabberdctl</TT>-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc68">4.1</A>  <A HREF="#ejabberdctl"><TT>ejabberdctl</TT></A></H2><!--SEC END --><P> <A NAME="ejabberdctl"></A> </P><P>With the <TT>ejabberdctl</TT> command line administration script
|
||||
you can execute <TT>ejabberdctl commands</TT> (described in the next section, <A HREF="#ectl-commands">4.1.1</A>)
|
||||
and also many general <TT>ejabberd commands</TT> (described in section <A HREF="#eja-commands">4.2</A>).
|
||||
This means you can start, stop and perform many other administrative tasks
|
||||
@ -3423,7 +3486,7 @@ and other codes may be used for specific results.
|
||||
This can be used by other scripts to determine automatically
|
||||
if a command succeeded or failed,
|
||||
for example using: <TT>echo $?</TT></P><P> <A NAME="ectl-commands"></A> </P><!--TOC subsection ejabberdctl Commands-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc68">4.1.1</A>  <A HREF="#ectl-commands">ejabberdctl Commands</A></H3><!--SEC END --><P> <A NAME="ectl-commands"></A> </P><P>When <TT>ejabberdctl</TT> is executed without any parameter,
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc69">4.1.1</A>  <A HREF="#ectl-commands">ejabberdctl Commands</A></H3><!--SEC END --><P> <A NAME="ectl-commands"></A> </P><P>When <TT>ejabberdctl</TT> is executed without any parameter,
|
||||
it displays the available options. If there isn’t an <TT>ejabberd</TT> server running,
|
||||
the available parameters are:
|
||||
</P><DL CLASS="description"><DT CLASS="dt-description">
|
||||
@ -3459,7 +3522,7 @@ robot1
|
||||
testuser1
|
||||
testuser2
|
||||
</PRE><P> <A NAME="erlangconfiguration"></A> </P><!--TOC subsection Erlang Runtime System-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc69">4.1.2</A>  <A HREF="#erlangconfiguration">Erlang Runtime System</A></H3><!--SEC END --><P> <A NAME="erlangconfiguration"></A> </P><P><TT>ejabberd</TT> is an Erlang/OTP application that runs inside an Erlang runtime system.
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc70">4.1.2</A>  <A HREF="#erlangconfiguration">Erlang Runtime System</A></H3><!--SEC END --><P> <A NAME="erlangconfiguration"></A> </P><P><TT>ejabberd</TT> is an Erlang/OTP application that runs inside an Erlang runtime system.
|
||||
This system is configured using environment variables and command line parameters.
|
||||
The <TT>ejabberdctl</TT> administration script uses many of those possibilities.
|
||||
You can configure some of them with the file <TT>ejabberdctl.cfg</TT>,
|
||||
@ -3536,7 +3599,7 @@ not “Simple Authentication and Security Layer”.
|
||||
</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-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc70">4.2</A>  <A HREF="#eja-commands"><TT>ejabberd</TT> Commands</A></H2><!--SEC END --><P> <A NAME="eja-commands"></A> </P><P>An <TT>ejabberd command</TT> is an abstract function identified by a name,
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc71">4.2</A>  <A HREF="#eja-commands"><TT>ejabberd</TT> Commands</A></H2><!--SEC END --><P> <A NAME="eja-commands"></A> </P><P>An <TT>ejabberd command</TT> is an abstract function identified by a name,
|
||||
with a defined number and type of calling arguments and type of result
|
||||
that is registered in the <TT>ejabberd_commands</TT> service.
|
||||
Those commands can be defined in any Erlang module and executed using any valid frontend.</P><P><TT>ejabberd</TT> includes a frontend to execute <TT>ejabberd commands</TT>: the script <TT>ejabberdctl</TT>.
|
||||
@ -3544,7 +3607,7 @@ Other known frontends that can be installed to execute ejabberd commands in diff
|
||||
<TT>ejabberd_xmlrpc</TT> (XML-RPC service),
|
||||
<TT>mod_rest</TT> (HTTP POST service),
|
||||
<TT>mod_shcommands</TT> (ejabberd WebAdmin page).</P><P> <A NAME="list-eja-commands"></A> </P><!--TOC subsection List of ejabberd Commands-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc71">4.2.1</A>  <A HREF="#list-eja-commands">List of ejabberd Commands</A></H3><!--SEC END --><P> <A NAME="list-eja-commands"></A> </P><P><TT>ejabberd</TT> includes a few ejabberd Commands by default.
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc72">4.2.1</A>  <A HREF="#list-eja-commands">List of ejabberd Commands</A></H3><!--SEC END --><P> <A NAME="list-eja-commands"></A> </P><P><TT>ejabberd</TT> includes a few ejabberd Commands by default.
|
||||
When more modules are installed, new commands may be available in the frontends.</P><P>The easiest way to get a list of the available commands, and get help for them is to use
|
||||
the ejabberdctl script:
|
||||
</P><PRE CLASS="verbatim">$ ejabberdctl help
|
||||
@ -3596,7 +3659,7 @@ is very high.
|
||||
</DD><DT CLASS="dt-description"><B><TT>register user host password</TT></B></DT><DD CLASS="dd-description"> Register an account in that domain with the given password.
|
||||
</DD><DT CLASS="dt-description"><B><TT>unregister user host</TT></B></DT><DD CLASS="dd-description"> Unregister the given account.
|
||||
</DD></DL><P> <A NAME="accesscommands"></A> </P><!--TOC subsection Restrict Execution with AccessCommands-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc72">4.2.2</A>  <A HREF="#accesscommands">Restrict Execution with AccessCommands</A></H3><!--SEC END --><P> <A NAME="accesscommands"></A> </P><P>The frontends can be configured to restrict access to certain commands.
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc73">4.2.2</A>  <A HREF="#accesscommands">Restrict Execution with AccessCommands</A></H3><!--SEC END --><P> <A NAME="accesscommands"></A> </P><P>The frontends can be configured to restrict access to certain commands.
|
||||
In that case, authentication information must be provided.
|
||||
In each frontend the <TT>AccessCommands</TT> option is defined
|
||||
in a different place. But in all cases the option syntax is the same:
|
||||
@ -3641,7 +3704,7 @@ and the provided arguments do not contradict Arguments.</P><P>As an example to u
|
||||
{_bot_reg_test, [register, unregister], [{host, "test.org"}]}
|
||||
]
|
||||
</PRE><P> <A NAME="webadmin"></A> </P><!--TOC section Web Admin-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc73">4.3</A>  <A HREF="#webadmin">Web Admin</A></H2><!--SEC END --><P> <A NAME="webadmin"></A>
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc74">4.3</A>  <A HREF="#webadmin">Web Admin</A></H2><!--SEC END --><P> <A NAME="webadmin"></A>
|
||||
</P><P>The <TT>ejabberd</TT> Web Admin allows to administer most of <TT>ejabberd</TT> using a web browser.</P><P>This feature is enabled by default:
|
||||
a <TT>ejabberd_http</TT> listener with the option <TT>web_admin</TT> (see
|
||||
section <A HREF="#listened">3.1.3</A>) is included in the listening ports. Then you can open
|
||||
@ -3659,7 +3722,8 @@ you will see a page similar to figure <A HREF="#fig:webadmmain">4.1</A>.</P
|
||||
<DIV CLASS="center"><HR WIDTH="80%" SIZE=2></DIV></DIV></BLOCKQUOTE><P>
|
||||
Here you can edit access restrictions, manage users, create backups,
|
||||
manage the database, enable/disable ports listened for, view server
|
||||
statistics,…</P><P>Examples:
|
||||
statistics,…</P><P>The access rule <TT>configure</TT> determines what accounts can access the Web Admin and modify it.
|
||||
The access rule <TT>webadmin_view</TT> is to grant only view access: those accounts can browse the Web Admin with read-only access.</P><P>Example configurations:
|
||||
</P><UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
You can serve the Web Admin on the same port as the
|
||||
HTTP Polling interface. In this example
|
||||
@ -3673,9 +3737,13 @@ username ‘<TT>admin@example.net</TT>’ to administer all virtual ho
|
||||
URL). If you log in with ‘<TT>admin@example.com</TT>’ on<BR>
|
||||
<CODE>http://example.org:5280/admin/server/example.com/</CODE> you can only
|
||||
administer the virtual host <TT>example.com</TT>.
|
||||
The account ‘<TT>reviewer@example.com</TT>’ can browse that vhost in read-only mode.
|
||||
<PRE CLASS="verbatim">{acl, admins, {user, "admin", "example.net"}}.
|
||||
{host_config, "example.com", [{acl, admins, {user, "admin", "example.com"}}]}.
|
||||
{host_config, "example.com", [{acl, viewers, {user, "reviewer", "example.com"}}]}.
|
||||
|
||||
{access, configure, [{allow, admins}]}.
|
||||
{access, webadmin_view, [{allow, viewers}]}.
|
||||
|
||||
{hosts, ["example.org"]}.
|
||||
|
||||
@ -3713,13 +3781,13 @@ The file is searched by default in
|
||||
The directory of the documentation can be specified in
|
||||
the environment variable <TT>EJABBERD_DOC_PATH</TT>.
|
||||
See section <A HREF="#erlangconfiguration">4.1.2</A>.</P><P> <A NAME="adhoccommands"></A> </P><!--TOC section Ad-hoc Commands-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc74">4.4</A>  <A HREF="#adhoccommands">Ad-hoc Commands</A></H2><!--SEC END --><P> <A NAME="adhoccommands"></A> </P><P>If you enable <TT>mod_configure</TT> and <TT>mod_adhoc</TT>,
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc75">4.4</A>  <A HREF="#adhoccommands">Ad-hoc Commands</A></H2><!--SEC END --><P> <A NAME="adhoccommands"></A> </P><P>If you enable <TT>mod_configure</TT> and <TT>mod_adhoc</TT>,
|
||||
you can perform several administrative tasks in <TT>ejabberd</TT>
|
||||
with a XMPP client.
|
||||
The client must support Ad-Hoc Commands (<A HREF="http://xmpp.org/extensions/xep-0050.html">XEP-0050</A>),
|
||||
and you must login in the XMPP server with
|
||||
an account with proper privileges.</P><P> <A NAME="changeerlangnodename"></A> </P><!--TOC section Change Computer Hostname-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc75">4.5</A>  <A HREF="#changeerlangnodename">Change Computer Hostname</A></H2><!--SEC END --><P> <A NAME="changeerlangnodename"></A> </P><P><TT>ejabberd</TT> uses the distributed Mnesia database.
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc76">4.5</A>  <A HREF="#changeerlangnodename">Change Computer Hostname</A></H2><!--SEC END --><P> <A NAME="changeerlangnodename"></A> </P><P><TT>ejabberd</TT> uses the distributed Mnesia database.
|
||||
Being distributed, Mnesia enforces consistency of its file,
|
||||
so it stores the name of the Erlang node in it (see section <A HREF="#nodename">5.4</A>).
|
||||
The name of an Erlang node includes the hostname of the computer.
|
||||
@ -3765,8 +3833,8 @@ mv /var/lib/ejabberd/*.* /var/lib/ejabberd/oldfiles/
|
||||
</PRE></LI><LI CLASS="li-enumerate">Check that the information of the old database is available: accounts, rosters...
|
||||
After you finish, remember to delete the temporary backup files from public directories.
|
||||
</LI></OL><P> <A NAME="secure"></A> </P><!--TOC chapter Securing <TT>ejabberd</TT>-->
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc76">Chapter 5</A>  <A HREF="#secure">Securing <TT>ejabberd</TT></A></H1><!--SEC END --><P> <A NAME="secure"></A> </P><P> <A NAME="firewall"></A> </P><!--TOC section Firewall Settings-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc77">5.1</A>  <A HREF="#firewall">Firewall Settings</A></H2><!--SEC END --><P> <A NAME="firewall"></A>
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc77">Chapter 5</A>  <A HREF="#secure">Securing <TT>ejabberd</TT></A></H1><!--SEC END --><P> <A NAME="secure"></A> </P><P> <A NAME="firewall"></A> </P><!--TOC section Firewall Settings-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc78">5.1</A>  <A HREF="#firewall">Firewall Settings</A></H2><!--SEC END --><P> <A NAME="firewall"></A>
|
||||
</P><P>You need to take the following TCP ports in mind when configuring your firewall:
|
||||
</P><BLOCKQUOTE CLASS="table"><DIV CLASS="center"><DIV CLASS="center"><HR WIDTH="80%" SIZE=2></DIV>
|
||||
<TABLE BORDER=1 CELLSPACING=0 CELLPADDING=1><TR><TD ALIGN=left NOWRAP><B>Port</B></TD><TD ALIGN=left NOWRAP><B>Description</B></TD></TR>
|
||||
@ -3777,7 +3845,7 @@ After you finish, remember to delete the temporary backup files from public dire
|
||||
<TR><TD ALIGN=left NOWRAP>port range</TD><TD ALIGN=left NOWRAP>Used for connections between Erlang nodes. This range is configurable (see section <A HREF="#epmd">5.2</A>).</TD></TR>
|
||||
</TABLE>
|
||||
<DIV CLASS="center"><HR WIDTH="80%" SIZE=2></DIV></DIV></BLOCKQUOTE><P> <A NAME="epmd"></A> </P><!--TOC section epmd-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc78">5.2</A>  <A HREF="#epmd">epmd</A></H2><!--SEC END --><P> <A NAME="epmd"></A> </P><P><A HREF="http://www.erlang.org/doc/man/epmd.html">epmd (Erlang Port Mapper Daemon)</A>
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc79">5.2</A>  <A HREF="#epmd">epmd</A></H2><!--SEC END --><P> <A NAME="epmd"></A> </P><P><A HREF="http://www.erlang.org/doc/man/epmd.html">epmd (Erlang Port Mapper Daemon)</A>
|
||||
is a small name server included in Erlang/OTP
|
||||
and used by Erlang programs when establishing distributed Erlang communications.
|
||||
<TT>ejabberd</TT> needs <TT>epmd</TT> to use <TT>ejabberdctl</TT> and also when clustering <TT>ejabberd</TT> nodes.
|
||||
@ -3802,7 +3870,7 @@ but can be configured in the file <TT>ejabberdctl.cfg</TT>.
|
||||
The Erlang command-line parameter used internally is, for example:
|
||||
</P><PRE CLASS="verbatim">erl ... -kernel inet_dist_listen_min 4370 inet_dist_listen_max 4375
|
||||
</PRE><P> <A NAME="cookie"></A> </P><!--TOC section Erlang Cookie-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc79">5.3</A>  <A HREF="#cookie">Erlang Cookie</A></H2><!--SEC END --><P> <A NAME="cookie"></A> </P><P>The Erlang cookie is a string with numbers and letters.
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc80">5.3</A>  <A HREF="#cookie">Erlang Cookie</A></H2><!--SEC END --><P> <A NAME="cookie"></A> </P><P>The Erlang cookie is a string with numbers and letters.
|
||||
An Erlang node reads the cookie at startup from the command-line parameter <TT>-setcookie</TT>.
|
||||
If not indicated, the cookie is read from the cookie file <TT>$HOME/.erlang.cookie</TT>.
|
||||
If this file does not exist, it is created immediately with a random cookie.
|
||||
@ -3816,7 +3884,7 @@ to prevent unauthorized access or intrusion to an Erlang node.
|
||||
The communication between Erlang nodes are not encrypted,
|
||||
so the cookie could be read sniffing the traffic on the network.
|
||||
The recommended way to secure the Erlang node is to block the port 4369.</P><P> <A NAME="nodename"></A> </P><!--TOC section Erlang Node Name-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc80">5.4</A>  <A HREF="#nodename">Erlang Node Name</A></H2><!--SEC END --><P> <A NAME="nodename"></A> </P><P>An Erlang node may have a node name.
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc81">5.4</A>  <A HREF="#nodename">Erlang Node Name</A></H2><!--SEC END --><P> <A NAME="nodename"></A> </P><P>An Erlang node may have a node name.
|
||||
The name can be short (if indicated with the command-line parameter <TT>-sname</TT>)
|
||||
or long (if indicated with the parameter <TT>-name</TT>).
|
||||
Starting an Erlang node with -sname limits the communication between Erlang nodes to the LAN.</P><P>Using the option <TT>-sname</TT> instead of <TT>-name</TT> is a simple method
|
||||
@ -3825,7 +3893,7 @@ However, it is not ultimately effective to prevent access to the Erlang node,
|
||||
because it may be possible to fake the fact that you are on another network
|
||||
using a modified version of Erlang <TT>epmd</TT>.
|
||||
The recommended way to secure the Erlang node is to block the port 4369.</P><P> <A NAME="secure-files"></A> </P><!--TOC section Securing Sensitive Files-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc81">5.5</A>  <A HREF="#secure-files">Securing Sensitive Files</A></H2><!--SEC END --><P> <A NAME="secure-files"></A> </P><P><TT>ejabberd</TT> stores sensitive data in the file system either in plain text or binary files.
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc82">5.5</A>  <A HREF="#secure-files">Securing Sensitive Files</A></H2><!--SEC END --><P> <A NAME="secure-files"></A> </P><P><TT>ejabberd</TT> stores sensitive data in the file system either in plain text or binary files.
|
||||
The file system permissions should be set to only allow the proper user to read,
|
||||
write and execute those files and directories.</P><DL CLASS="description"><DT CLASS="dt-description">
|
||||
<B><TT>ejabberd configuration file: /etc/ejabberd/ejabberd.cfg</TT></B></DT><DD CLASS="dd-description">
|
||||
@ -3845,9 +3913,9 @@ so it is preferable to secure the whole <TT>/var/lib/ejabberd/</TT> directory.
|
||||
</DD><DT CLASS="dt-description"><B><TT>Erlang cookie file: /var/lib/ejabberd/.erlang.cookie</TT></B></DT><DD CLASS="dd-description">
|
||||
See section <A HREF="#cookie">5.3</A>.
|
||||
</DD></DL><P> <A NAME="clustering"></A> </P><!--TOC chapter Clustering-->
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc82">Chapter 6</A>  <A HREF="#clustering">Clustering</A></H1><!--SEC END --><P> <A NAME="clustering"></A>
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc83">Chapter 6</A>  <A HREF="#clustering">Clustering</A></H1><!--SEC END --><P> <A NAME="clustering"></A>
|
||||
</P><P> <A NAME="howitworks"></A> </P><!--TOC section How it Works-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc83">6.1</A>  <A HREF="#howitworks">How it Works</A></H2><!--SEC END --><P> <A NAME="howitworks"></A>
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc84">6.1</A>  <A HREF="#howitworks">How it Works</A></H2><!--SEC END --><P> <A NAME="howitworks"></A>
|
||||
</P><P>A XMPP domain is served by one or more <TT>ejabberd</TT> nodes. These nodes can
|
||||
be run on different machines that are connected via a network. They all
|
||||
must have the ability to connect to port 4369 of all another nodes, and must
|
||||
@ -3861,29 +3929,29 @@ router,
|
||||
</LI><LI CLASS="li-itemize">session manager,
|
||||
</LI><LI CLASS="li-itemize">s2s manager.
|
||||
</LI></UL><P> <A NAME="router"></A> </P><!--TOC subsection Router-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc84">6.1.1</A>  <A HREF="#router">Router</A></H3><!--SEC END --><P> <A NAME="router"></A>
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc85">6.1.1</A>  <A HREF="#router">Router</A></H3><!--SEC END --><P> <A NAME="router"></A>
|
||||
</P><P>This module is the main router of XMPP packets on each node. It
|
||||
routes them based on their destination’s domains. It uses a global
|
||||
routing table. The domain of the packet’s destination is searched in the
|
||||
routing table, and if it is found, the packet is routed to the
|
||||
appropriate process. If not, it is sent to the s2s manager.</P><P> <A NAME="localrouter"></A> </P><!--TOC subsection Local Router-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc85">6.1.2</A>  <A HREF="#localrouter">Local Router</A></H3><!--SEC END --><P> <A NAME="localrouter"></A>
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc86">6.1.2</A>  <A HREF="#localrouter">Local Router</A></H3><!--SEC END --><P> <A NAME="localrouter"></A>
|
||||
</P><P>This module routes packets which have a destination domain equal to
|
||||
one of this server’s host names. If the destination JID has a non-empty user
|
||||
part, it is routed to the session manager, otherwise it is processed depending
|
||||
on its content.</P><P> <A NAME="sessionmanager"></A> </P><!--TOC subsection Session Manager-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc86">6.1.3</A>  <A HREF="#sessionmanager">Session Manager</A></H3><!--SEC END --><P> <A NAME="sessionmanager"></A>
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc87">6.1.3</A>  <A HREF="#sessionmanager">Session Manager</A></H3><!--SEC END --><P> <A NAME="sessionmanager"></A>
|
||||
</P><P>This module routes packets to local users. It looks up to which user
|
||||
resource a packet must be sent via a presence table. Then the packet is
|
||||
either routed to the appropriate c2s process, or stored in offline
|
||||
storage, or bounced back.</P><P> <A NAME="s2smanager"></A> </P><!--TOC subsection s2s Manager-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc87">6.1.4</A>  <A HREF="#s2smanager">s2s Manager</A></H3><!--SEC END --><P> <A NAME="s2smanager"></A>
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc88">6.1.4</A>  <A HREF="#s2smanager">s2s Manager</A></H3><!--SEC END --><P> <A NAME="s2smanager"></A>
|
||||
</P><P>This module routes packets to other XMPP servers. First, it
|
||||
checks if an opened s2s connection from the domain of the packet’s
|
||||
source to the domain of the packet’s destination exists. If that is the case,
|
||||
the s2s manager routes the packet to the process
|
||||
serving this connection, otherwise a new connection is opened.</P><P> <A NAME="cluster"></A> </P><!--TOC section Clustering Setup-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc88">6.2</A>  <A HREF="#cluster">Clustering Setup</A></H2><!--SEC END --><P> <A NAME="cluster"></A>
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc89">6.2</A>  <A HREF="#cluster">Clustering Setup</A></H2><!--SEC END --><P> <A NAME="cluster"></A>
|
||||
</P><P>Suppose you already configured <TT>ejabberd</TT> on one machine named (<TT>first</TT>),
|
||||
and you need to setup another one to make an <TT>ejabberd</TT> cluster. Then do
|
||||
following steps:</P><OL CLASS="enumerate" type=1><LI CLASS="li-enumerate">
|
||||
@ -3921,10 +3989,10 @@ and ‘<CODE>access</CODE>’ options because they will be taken from
|
||||
enabled only on one machine in the cluster.
|
||||
</LI></OL><P>You can repeat these steps for other machines supposed to serve this
|
||||
domain.</P><P> <A NAME="servicelb"></A> </P><!--TOC section Service Load-Balancing-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc89">6.3</A>  <A HREF="#servicelb">Service Load-Balancing</A></H2><!--SEC END --><P> <A NAME="servicelb"></A>
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc90">6.3</A>  <A HREF="#servicelb">Service Load-Balancing</A></H2><!--SEC END --><P> <A NAME="servicelb"></A>
|
||||
</P><P> <A NAME="componentlb"></A> </P><!--TOC subsection Components Load-Balancing-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc90">6.3.1</A>  <A HREF="#componentlb">Components Load-Balancing</A></H3><!--SEC END --><P> <A NAME="componentlb"></A> </P><P> <A NAME="domainlb"></A> </P><!--TOC subsection Domain Load-Balancing Algorithm-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc91">6.3.2</A>  <A HREF="#domainlb">Domain Load-Balancing Algorithm</A></H3><!--SEC END --><P> <A NAME="domainlb"></A>
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc91">6.3.1</A>  <A HREF="#componentlb">Components Load-Balancing</A></H3><!--SEC END --><P> <A NAME="componentlb"></A> </P><P> <A NAME="domainlb"></A> </P><!--TOC subsection Domain Load-Balancing Algorithm-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc92">6.3.2</A>  <A HREF="#domainlb">Domain Load-Balancing Algorithm</A></H3><!--SEC END --><P> <A NAME="domainlb"></A>
|
||||
</P><P><TT>ejabberd</TT> includes an algorithm to load balance the components that are plugged on an <TT>ejabberd</TT> cluster. It means that you can plug one or several instances of the same component on each <TT>ejabberd</TT> cluster and that the traffic will be automatically distributed.</P><P>The default distribution algorithm try to deliver to a local instance of a component. If several local instances are available, one instance is chosen randomly. If no instance is available locally, one instance is chosen randomly among the remote component instances.</P><P>If you need a different behaviour, you can change the load balancing behaviour with the option <TT>domain_balancing</TT>. The syntax of the option is the following:
|
||||
</P><DL CLASS="description"><DT CLASS="dt-description"><B><TT>{domain_balancing, "component.example.com", BalancingCriteria}.</TT></B></DT></DL><P>Several balancing criteria are available:
|
||||
</P><UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
@ -3933,12 +4001,12 @@ domain.</P><P> <A NAME="servicelb"></A> </P><!--TOC section Service Load-Balanci
|
||||
</LI><LI CLASS="li-itemize"><TT>bare_destination</TT>: the bare JID (without resource) of the packet <TT>to</TT> attribute is used.
|
||||
</LI><LI CLASS="li-itemize"><TT>bare_source</TT>: the bare JID (without resource) of the packet <TT>from</TT> attribute is used.
|
||||
</LI></UL><P>If the value corresponding to the criteria is the same, the same component instance in the cluster will be used.</P><P> <A NAME="lbbuckets"></A> </P><!--TOC subsection Load-Balancing Buckets-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc92">6.3.3</A>  <A HREF="#lbbuckets">Load-Balancing Buckets</A></H3><!--SEC END --><P> <A NAME="lbbuckets"></A>
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc93">6.3.3</A>  <A HREF="#lbbuckets">Load-Balancing Buckets</A></H3><!--SEC END --><P> <A NAME="lbbuckets"></A>
|
||||
</P><P>When there is a risk of failure for a given component, domain balancing can cause service trouble. If one component is failing the service will not work correctly unless the sessions are rebalanced.</P><P>In this case, it is best to limit the problem to the sessions handled by the failing component. This is what the <TT>domain_balancing_component_number</TT> option does, making the load balancing algorithm not dynamic, but sticky on a fix number of component instances.</P><P>The syntax is:
|
||||
</P><DL CLASS="description"><DT CLASS="dt-description"><B><TT>{domain_balancing_component_number, "component.example.com", Number}.</TT></B></DT></DL><P> <A NAME="debugging"></A> </P><!--TOC chapter Debugging-->
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc93">Chapter 7</A>  <A HREF="#debugging">Debugging</A></H1><!--SEC END --><P> <A NAME="debugging"></A>
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc94">Chapter 7</A>  <A HREF="#debugging">Debugging</A></H1><!--SEC END --><P> <A NAME="debugging"></A>
|
||||
</P><P> <A NAME="logfiles"></A> </P><!--TOC section Log Files-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc94">7.1</A>  <A HREF="#logfiles">Log Files</A></H2><!--SEC END --><P> <A NAME="logfiles"></A> </P><P>An <TT>ejabberd</TT> node writes two log files:
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc95">7.1</A>  <A HREF="#logfiles">Log Files</A></H2><!--SEC END --><P> <A NAME="logfiles"></A> </P><P>An <TT>ejabberd</TT> node writes two log files:
|
||||
</P><DL CLASS="description"><DT CLASS="dt-description">
|
||||
<B><TT>ejabberd.log</TT></B></DT><DD CLASS="dd-description"> is the ejabberd service log, with the messages reported by <TT>ejabberd</TT> code
|
||||
</DD><DT CLASS="dt-description"><B><TT>erlang.log</TT></B></DT><DD CLASS="dd-description"> is the Erlang/OTP system log, with the messages reported by Erlang/OTP using SASL (System Architecture Support Libraries)
|
||||
@ -3960,12 +4028,12 @@ The ejabberdctl command <TT>reopen-log</TT>
|
||||
(please refer to section <A HREF="#ectl-commands">4.1.1</A>)
|
||||
reopens the log files,
|
||||
and also renames the old ones if you didn’t rename them.</P><P> <A NAME="debugconsole"></A> </P><!--TOC section Debug Console-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc95">7.2</A>  <A HREF="#debugconsole">Debug Console</A></H2><!--SEC END --><P> <A NAME="debugconsole"></A> </P><P>The Debug Console is an Erlang shell attached to an already running <TT>ejabberd</TT> server.
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc96">7.2</A>  <A HREF="#debugconsole">Debug Console</A></H2><!--SEC END --><P> <A NAME="debugconsole"></A> </P><P>The Debug Console is an Erlang shell attached to an already running <TT>ejabberd</TT> server.
|
||||
With this Erlang shell, an experienced administrator can perform complex tasks.</P><P>This shell gives complete control over the <TT>ejabberd</TT> server,
|
||||
so it is important to use it with extremely care.
|
||||
There are some simple and safe examples in the article
|
||||
<A HREF="http://www.ejabberd.im/interconnect-erl-nodes">Interconnecting Erlang Nodes</A></P><P>To exit the shell, close the window or press the keys: control+c control+c.</P><P> <A NAME="watchdog"></A> </P><!--TOC section Watchdog Alerts-->
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc96">7.3</A>  <A HREF="#watchdog">Watchdog Alerts</A></H2><!--SEC END --><P> <A NAME="watchdog"></A>
|
||||
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc97">7.3</A>  <A HREF="#watchdog">Watchdog Alerts</A></H2><!--SEC END --><P> <A NAME="watchdog"></A>
|
||||
</P><P><TT>ejabberd</TT> includes a watchdog mechanism that may be useful to developers
|
||||
when troubleshooting a problem related to memory usage.
|
||||
If a process in the <TT>ejabberd</TT> server consumes more memory than the configured threshold,
|
||||
@ -3985,7 +4053,7 @@ or in a conversation with the watchdog alert bot.</P><P>The syntax is:
|
||||
To remove all watchdog admins, set the option with an empty list:
|
||||
</P><PRE CLASS="verbatim">{watchdog_admins, []}.
|
||||
</PRE><P> <A NAME="i18ni10n"></A> </P><!--TOC chapter Internationalization and Localization-->
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc97">Appendix A</A>  <A HREF="#i18ni10n">Internationalization and Localization</A></H1><!--SEC END --><P> <A NAME="i18ni10n"></A>
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc98">Appendix A</A>  <A HREF="#i18ni10n">Internationalization and Localization</A></H1><!--SEC END --><P> <A NAME="i18ni10n"></A>
|
||||
</P><P>The source code of <TT>ejabberd</TT> supports localization.
|
||||
The translators can edit the
|
||||
<A HREF="http://www.gnu.org/software/gettext/">gettext</A> .po files
|
||||
@ -4020,9 +4088,9 @@ HTTP header ‘Accept-Language: ru’</TD></TR>
|
||||
</TABLE></DIV>
|
||||
<A NAME="fig:webadmmainru"></A>
|
||||
<DIV CLASS="center"><HR WIDTH="80%" SIZE=2></DIV></DIV></BLOCKQUOTE><P> <A NAME="releasenotes"></A> </P><!--TOC chapter Release Notes-->
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc98">Appendix B</A>  <A HREF="#releasenotes">Release Notes</A></H1><!--SEC END --><P> <A NAME="releasenotes"></A>
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc99">Appendix B</A>  <A HREF="#releasenotes">Release Notes</A></H1><!--SEC END --><P> <A NAME="releasenotes"></A>
|
||||
</P><P>Release notes are available from <A HREF="http://www.process-one.net/en/ejabberd/release_notes/">ejabberd Home Page</A></P><P> <A NAME="acknowledgements"></A> </P><!--TOC chapter Acknowledgements-->
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc99">Appendix C</A>  <A HREF="#acknowledgements">Acknowledgements</A></H1><!--SEC END --><P> <A NAME="acknowledgements"></A> </P><P>Thanks to all people who contributed to this guide:
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc100">Appendix C</A>  <A HREF="#acknowledgements">Acknowledgements</A></H1><!--SEC END --><P> <A NAME="acknowledgements"></A> </P><P>Thanks to all people who contributed to this guide:
|
||||
</P><UL CLASS="itemize"><LI CLASS="li-itemize">
|
||||
Alexey Shchepin (<A HREF="xmpp:aleksey@jabber.ru"><TT>xmpp:aleksey@jabber.ru</TT></A>)
|
||||
</LI><LI CLASS="li-itemize">Badlop (<A HREF="xmpp:badlop@jabberes.org"><TT>xmpp:badlop@jabberes.org</TT></A>)
|
||||
@ -4034,7 +4102,7 @@ Alexey Shchepin (<A HREF="xmpp:aleksey@jabber.ru"><TT>xmpp:aleksey@jabber.ru</TT
|
||||
</LI><LI CLASS="li-itemize">Sergei Golovan (<A HREF="xmpp:sgolovan@nes.ru"><TT>xmpp:sgolovan@nes.ru</TT></A>)
|
||||
</LI><LI CLASS="li-itemize">Vsevolod Pelipas (<A HREF="xmpp:vsevoload@jabber.ru"><TT>xmpp:vsevoload@jabber.ru</TT></A>)
|
||||
</LI></UL><P> <A NAME="copyright"></A> </P><!--TOC chapter Copyright Information-->
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc100">Appendix D</A>  <A HREF="#copyright">Copyright Information</A></H1><!--SEC END --><P> <A NAME="copyright"></A> </P><P>Ejabberd Installation and Operation Guide.<BR>
|
||||
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc101">Appendix D</A>  <A HREF="#copyright">Copyright Information</A></H1><!--SEC END --><P> <A NAME="copyright"></A> </P><P>Ejabberd Installation and Operation Guide.<BR>
|
||||
Copyright © 2003 — 2010 ProcessOne</P><P>This document is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
|
179
doc/guide.tex
179
doc/guide.tex
@ -98,6 +98,7 @@
|
||||
\newcommand{\modvcard}{\module{mod\_vcard}}
|
||||
\newcommand{\modvcardldap}{\module{mod\_vcard\_ldap}}
|
||||
\newcommand{\modvcardodbc}{\module{mod\_vcard\_odbc}}
|
||||
\newcommand{\modvcardxupdate}{\module{mod\_vcard\_xupdate}}
|
||||
\newcommand{\modversion}{\module{mod\_version}}
|
||||
|
||||
%% Contributed modules
|
||||
@ -309,8 +310,8 @@ To compile \ejabberd{} on a `Unix-like' operating system, you need:
|
||||
\item GNU Make
|
||||
\item GCC
|
||||
\item Libexpat 1.95 or higher
|
||||
\item Erlang/OTP R10B-9 or higher. The recommended version is R12B-5. Support for R13 is experimental.
|
||||
\item OpenSSL 0.9.6 or higher, for STARTTLS, SASL and SSL encryption. Optional, highly recommended.
|
||||
\item Erlang/OTP R10B-9 or higher. The recommended versions are R12B-5 and R13B04.
|
||||
\item OpenSSL 0.9.8 or higher, for STARTTLS, SASL and SSL encryption.
|
||||
\item Zlib 1.2.3 or higher, for Stream Compression support (\xepref{0138}). Optional.
|
||||
\item Erlang mysql library. Optional. For MySQL authentication or storage. See section \ref{compilemysql}.
|
||||
\item Erlang pgsql library. Optional. For PostgreSQL authentication or storage. See section \ref{compilepgsql}.
|
||||
@ -381,6 +382,10 @@ Some options that you may be interested in modifying:
|
||||
|
||||
\titem{--disable-transient-supervisors}
|
||||
Disable the use of Erlang/OTP supervision for transient processes.
|
||||
|
||||
\titem{--enable-nif}
|
||||
Replaces some critical Erlang functions with equivalents written in C to improve performance.
|
||||
This feature requires Erlang/OTP R13B04 or higher.
|
||||
\end{description}
|
||||
|
||||
\makesubsection{install}{Install}
|
||||
@ -803,12 +808,12 @@ The available modules, their purpose and the options allowed by each one are:
|
||||
\texttt{zlib}
|
||||
\titem{\texttt{ejabberd\_s2s\_in}}
|
||||
Handles incoming s2s connections.\\
|
||||
Options: \texttt{max\_stanza\_size}
|
||||
Options: \texttt{max\_stanza\_size}, \texttt{shaper}
|
||||
\titem{\texttt{ejabberd\_service}}
|
||||
Interacts with an \footahref{http://www.ejabberd.im/tutorials-transports}{external component}
|
||||
(as defined in the Jabber Component Protocol (\xepref{0114}).\\
|
||||
Options: \texttt{access}, \texttt{hosts}, \texttt{max\_fsm\_queue},
|
||||
\texttt{shaper}, \texttt{service\_check\_from}
|
||||
\texttt{service\_check\_from}, \texttt{shaper}
|
||||
\titem{\texttt{ejabberd\_stun}}
|
||||
Handles STUN Binding requests as defined in
|
||||
\footahref{http://tools.ietf.org/html/rfc5389}{RFC 5389}.\\
|
||||
@ -832,13 +837,10 @@ This is a detailed description of each option allowed by the listening modules:
|
||||
lots of new incoming connections as they may be dropped if there is
|
||||
no space in the queue (and ejabberd was not able to accept them
|
||||
immediately). Default value is 5.
|
||||
\titem{captcha} \ind{options!http-captcha}
|
||||
Simple web page that allows a user to fill a CAPTCHA challenge (see section \ref{captcha}).
|
||||
\titem{\{certfile, Path\}} Full path to a file containing the default SSL certificate.
|
||||
To define a certificate file specific for a given domain, use the global option \term{domain\_certfile}.
|
||||
\titem{\{service\_check\_from, true|false\}} \ind{options!service\_check\_from}
|
||||
This option can be used with \term{ejabberd\_service} only. It is
|
||||
used to disable control on the from field on packets send by an
|
||||
external components. The option can be either \term{true} or
|
||||
\term{false}. The default value is \term{true} which conforms to \xepref{0114}.
|
||||
\titem{\{hosts, [Hostname, ...], [HostOption, ...]\}} \ind{options!hosts}
|
||||
The external Jabber component that connects to this \term{ejabberd\_service}
|
||||
can serve one or more hostnames.
|
||||
@ -848,8 +850,6 @@ This is a detailed description of each option allowed by the listening modules:
|
||||
Note that you cannot define in a single \term{ejabberd\_service} components of
|
||||
different services: add an \term{ejabberd\_service} for each service,
|
||||
as seen in an example below.
|
||||
\titem{captcha} \ind{options!http-captcha}
|
||||
Simple web page that allows a user to fill a CAPTCHA challenge (see section \ref{captcha}).
|
||||
\titem{http\_bind} \ind{options!http\_bind}\ind{protocols!XEP-0206: HTTP Binding}\ind{JWChat}\ind{web-based XMPP client}
|
||||
This option enables HTTP Binding (\xepref{0124} and \xepref{0206}) support. HTTP Bind
|
||||
enables access via HTTP requests to \ejabberd{} from behind firewalls which
|
||||
@ -902,7 +902,7 @@ This is a detailed description of each option allowed by the listening modules:
|
||||
\titem{\{max\_stanza\_size, Size\}}
|
||||
\ind{options!max\_stanza\_size}This option specifies an
|
||||
approximate maximum size in bytes of XML stanzas. Approximate,
|
||||
because it is calculated with the precision of one block of readed
|
||||
because it is calculated with the precision of one block of read
|
||||
data. For example \verb|{max_stanza_size, 65536}|. The default
|
||||
value is \term{infinity}. Recommended values are 65536 for c2s
|
||||
connections and 131072 for s2s connections. s2s max stanza size
|
||||
@ -914,10 +914,13 @@ This is a detailed description of each option allowed by the listening modules:
|
||||
and you also want \term{mod\_http\_bind} to serve the URIs \term{/http-bind/},
|
||||
use this option: \term{\{request\_handlers, [\{["a", "b"], mod\_foo\}, \{["http-bind"], mod\_http\_bind\}]\}}
|
||||
\titem{\{service\_check\_from, true|false\}}
|
||||
By enabling this option, \ejabberd{} allows the component to send packets with any arbitrary domain in the 'from' attribute.
|
||||
Note that \xepref{0114} requires that the domain must match the hostname of the component.
|
||||
Only enable this option if you are completely sure you need to enable it.
|
||||
Default value: false.
|
||||
\ind{options!service\_check\_from}
|
||||
This option can be used with \term{ejabberd\_service} only.
|
||||
\xepref{0114} requires that the domain must match the hostname of the component.
|
||||
If this option is set to \term{false}, \ejabberd{} will allow the component
|
||||
to send stanzas with any arbitrary domain in the 'from' attribute.
|
||||
Only use this option if you are completely sure about it.
|
||||
The default value is \term{true}, to be compliant with \xepref{0114}.
|
||||
\titem{\{shaper, none|ShaperName\}} \ind{options!shaper}This option defines a
|
||||
shaper for the port (see section~\ref{shapers}). The default value
|
||||
is \term{none}.
|
||||
@ -1188,8 +1191,7 @@ for user authentication. The syntax is:
|
||||
The following authentication methods are supported by \ejabberd{}:
|
||||
\begin{itemize}
|
||||
\item internal (default) --- See section~\ref{internalauth}.
|
||||
\item external --- There are \footahref{http://www.ejabberd.im/extauth}{some
|
||||
example authentication scripts}.
|
||||
\item external --- See section~\ref{extauth}.
|
||||
\item ldap --- See section~\ref{ldap}.
|
||||
\item odbc --- See section~\ref{mysql}, \ref{pgsql},
|
||||
\ref{mssql} and \ref{odbc}.
|
||||
@ -1197,7 +1199,7 @@ The following authentication methods are supported by \ejabberd{}:
|
||||
\item pam --- See section~\ref{pam}.
|
||||
\end{itemize}
|
||||
|
||||
Account creation is only supported by internal and odbc methods.
|
||||
Account creation is only supported by internal, external and odbc methods.
|
||||
|
||||
\makesubsubsection{internalauth}{Internal}
|
||||
\ind{internal authentication}\ind{Mnesia}
|
||||
@ -1219,6 +1221,48 @@ Examples:
|
||||
\end{verbatim}
|
||||
\end{itemize}
|
||||
|
||||
\makesubsubsection{extauth}{External Script}
|
||||
\ind{external authentication}
|
||||
|
||||
In this authentication method, when \ejabberd{} starts,
|
||||
it start a script, and calls it to perform authentication tasks.
|
||||
|
||||
The server administrator can write the external authentication script
|
||||
in any language.
|
||||
The details on the interface between ejabberd and the script are described
|
||||
in the \term{ejabberd Developers Guide}.
|
||||
There are also \footahref{http://www.ejabberd.im/extauth}{several example authentication scripts}.
|
||||
|
||||
These are the specific options:
|
||||
\begin{description}
|
||||
\titem{\{extauth\_program, PathToScript\}}
|
||||
Indicate in this option the full path to the external authentication script.
|
||||
The script must be executable by ejabberd.
|
||||
|
||||
\titem{\{extauth\_instances, Integer\}}
|
||||
Indicate how many instances of the script to run simultaneously to serve authentication in the virtual host.
|
||||
The default value is the minimum number: 1.
|
||||
|
||||
\titem{\{extauth\_cache, false|CacheTimeInteger\}}
|
||||
The value \term{false} disables the caching feature, this is the default.
|
||||
The integer \term{0} (zero) enables caching for statistics, but doesn't use that cached information to authenticate users.
|
||||
If another integer value is set, caching is enabled both for statistics and for authentication:
|
||||
the CacheTimeInteger indicates the number of seconds that ejabberd can reuse
|
||||
the authentication information since the user last disconnected,
|
||||
to verify again the user authentication without querying again the extauth script.
|
||||
Note: caching should not be enabled in a host if internal auth is also enabled.
|
||||
If caching is enabled, \term{mod\_last} or \term{mod\_last\_odbc} must be enabled also in that vhost.
|
||||
\end{description}
|
||||
|
||||
This example sets external authentication, the extauth script, enables caching for 10 minutes,
|
||||
and starts three instances of the script for each virtual host defined in ejabberd:
|
||||
\begin{verbatim}
|
||||
{auth_method, [external]}.
|
||||
{extauth_program, "/etc/ejabberd/JabberAuth.class.php"}.
|
||||
{extauth_cache, 600}.
|
||||
{extauth_instances, 3}.
|
||||
\end{verbatim}
|
||||
|
||||
\makesubsubsection{saslanonymous}{SASL Anonymous and Anonymous Login}
|
||||
\ind{sasl anonymous}\ind{anonymous login}
|
||||
|
||||
@ -1299,6 +1343,10 @@ Options:
|
||||
\titem{\{pam\_service, Name\}}\ind{options!pam\_service}This option defines the PAM service name.
|
||||
Default is \term{"ejabberd"}. Refer to the PAM documentation of your operation system
|
||||
for more information.
|
||||
\titem{\{pam\_userinfotype, username|jid\}}\ind{options!pam\_userinfotype}
|
||||
This option defines what type of information about the user ejabberd
|
||||
provides to the PAM service: only the username, or the user JID.
|
||||
Default is \term{username}.
|
||||
\end{description}
|
||||
|
||||
Example:
|
||||
@ -1952,19 +2000,9 @@ enabled. This can be done, by using next commands:
|
||||
\makesubsubsection{configuremssql}{Database Connection}
|
||||
\ind{Microsoft SQL Server!Database Connection}
|
||||
|
||||
By default \ejabberd{} opens 10 connections to the database for each virtual host.
|
||||
Use this option to modify the value:
|
||||
\begin{verbatim}
|
||||
{odbc_pool_size, 10}.
|
||||
\end{verbatim}
|
||||
|
||||
You can configure an interval to make a dummy SQL request
|
||||
to keep alive the connections to the database.
|
||||
The default value is 'undefined', so no keepalive requests are made.
|
||||
Specify in seconds: for example 28800 means 8 hours.
|
||||
\begin{verbatim}
|
||||
{odbc_keepalive_interval, undefined}.
|
||||
\end{verbatim}
|
||||
The configuration of Database Connection for a Microsoft SQL Server
|
||||
is the same as the configuration for
|
||||
ODBC compatible servers (see section~\ref{configureodbc}).
|
||||
|
||||
|
||||
\makesubsubsection{mssqlauth}{Authentication}
|
||||
@ -1972,7 +2010,8 @@ Specify in seconds: for example 28800 means 8 hours.
|
||||
|
||||
%TODO: not sure if this section is right!!!!!!
|
||||
|
||||
The configuration of Microsoft SQL Server is the same as the configuration of
|
||||
The configuration of Authentication for a Microsoft SQL Server
|
||||
is the same as the configuration for
|
||||
ODBC compatible servers (see section~\ref{odbcauth}).
|
||||
|
||||
\makesubsubsection{mssqlstorage}{Storage}
|
||||
@ -2169,13 +2208,19 @@ module loaded!
|
||||
server and use LDAP directory as vCard storage. Shared rosters are not supported
|
||||
yet.
|
||||
|
||||
Note that \ejabberd{} treats LDAP as a read-only storage:
|
||||
Usually \ejabberd{} treats LDAP as a read-only storage:
|
||||
it is possible to consult data, but not possible to
|
||||
create accounts, change password or edit vCard that is stored in LDAP.
|
||||
create accounts or edit vCard that is stored in LDAP.
|
||||
However, it is possible to change passwords if \module{mod\_register} module is enabled
|
||||
and LDAP server supports
|
||||
\footahref{http://tools.ietf.org/html/rfc3062}{RFC 3062}.
|
||||
|
||||
|
||||
\makesubsubsection{ldapconnection}{Connection}
|
||||
|
||||
Two connections are established to the LDAP server per vhost,
|
||||
one for authentication and other for regular calls.
|
||||
|
||||
Parameters:
|
||||
\begin{description}
|
||||
\titem{\{ldap\_servers, [Servers, ...]\}} \ind{options!ldap\_server}List of IP addresses or DNS names of your
|
||||
@ -2185,6 +2230,11 @@ Allowed values are: \term{none}, \term{tls}.
|
||||
The value \term{tls} enables encryption by using LDAP over SSL.
|
||||
Note that STARTTLS encryption is not supported.
|
||||
The default value is: \term{none}.
|
||||
\titem{\{ldap\_tls\_verify, false|soft|hard\}} \ind{options!ldap\_tls\_verify}
|
||||
This option specifies whether to verify LDAP server certificate or not when TLS is enabled.
|
||||
When \term{hard} is enabled \ejabberd{} doesn't proceed if a certificate is invalid.
|
||||
When \term{soft} is enabled \ejabberd{} proceeds even if check fails.
|
||||
The default is \term{false} which means no checks are performed.
|
||||
\titem{\{ldap\_port, Number\}} \ind{options!ldap\_port}Port to connect to your LDAP server.
|
||||
The default port is~389 if encryption is disabled; and 636 if encryption is enabled.
|
||||
If you configure a value, it is stored in \ejabberd{}'s database.
|
||||
@ -2205,11 +2255,12 @@ Example:
|
||||
{ldap_password, "secret"}.
|
||||
\end{verbatim}
|
||||
|
||||
Note that current LDAP implementation does not support SASL authentication.
|
||||
|
||||
\makesubsubsection{ldapauth}{Authentication}
|
||||
|
||||
You can authenticate users against an LDAP directory. Available options are:
|
||||
You can authenticate users against an LDAP directory.
|
||||
Note that current LDAP implementation does not support SASL authentication.
|
||||
|
||||
Available options are:
|
||||
|
||||
\begin{description}
|
||||
\titem{\{ldap\_base, Base\}}\ind{options!ldap\_base}LDAP base directory which stores
|
||||
@ -2460,6 +2511,8 @@ The following table lists all modules included in \ejabberd{}.
|
||||
\hline \modconfigure{} & Server configuration using Ad-Hoc & \modadhoc{} \\
|
||||
\hline \ahrefloc{moddisco}{\moddisco{}} & Service Discovery (\xepref{0030}) & \\
|
||||
\hline \ahrefloc{modecho}{\modecho{}} & Echoes XMPP stanzas & \\
|
||||
\hline \ahrefloc{modhttpbind}{\modhttpbind{}} & XMPP over Bosh service (HTTP Binding) & \\
|
||||
\hline \ahrefloc{modhttpfileserver}{\modhttpfileserver{}} & Small HTTP file server & \\
|
||||
\hline \ahrefloc{modirc}{\modirc{}} & IRC transport & \\
|
||||
\hline \ahrefloc{modlast}{\modlast{}} & Last Activity (\xepref{0012}) & \\
|
||||
\hline \ahrefloc{modlast}{\modlastodbc{}} & Last Activity (\xepref{0012}) & supported DB (*) \\
|
||||
@ -2487,6 +2540,7 @@ The following table lists all modules included in \ejabberd{}.
|
||||
\hline \ahrefloc{modvcard}{\modvcard{}} & vcard-temp (\xepref{0054}) & \\
|
||||
\hline \ahrefloc{modvcardldap}{\modvcardldap{}} & vcard-temp (\xepref{0054}) & LDAP server \\
|
||||
\hline \ahrefloc{modvcard}{\modvcardodbc{}} & vcard-temp (\xepref{0054}) & supported DB (*) \\
|
||||
\hline \ahrefloc{modvcardxupdate}{\modvcardxupdate{}} & vCard-Based Avatars (\xepref{0153}) & \modvcard{} or \modvcardodbc{} \\
|
||||
\hline \ahrefloc{modversion}{\modversion{}} & Software Version (\xepref{0092}) & \\
|
||||
\hline
|
||||
\end{tabular}
|
||||
@ -3155,6 +3209,10 @@ Module options:
|
||||
\titem{\{anonymous, true|false\}} The room is anonymous:
|
||||
occupants don't see the real JIDs of other occupants.
|
||||
Note that the room moderators can always see the real JIDs of the occupants.
|
||||
\titem{\{captcha\_protected, false\}}
|
||||
When a user tries to join a room where he has no affiliation (not owner, admin or member),
|
||||
the room requires him to fill a CAPTCHA challenge (see section \ref{captcha})
|
||||
in order to accept her join in the room.
|
||||
\titem{\{logging, false|true\}} The public messages are logged using \term{mod\_muc\_log}.
|
||||
\titem{\{max\_users, 200\}} Maximum number of occupants in the room.
|
||||
\titem{\{members\_by\_default, true|false\}} The occupants that enter the room are participants by default, so they have 'voice'.
|
||||
@ -3727,9 +3785,11 @@ from s2s leads to uncontrolled massive accounts creation by rogue users.
|
||||
This module reads also another option defined globally for the server:
|
||||
\term{\{registration\_timeout, Timeout\}}. \ind{options!registratimeout}
|
||||
This option limits the frequency of registration from a given IP or username.
|
||||
So, a user can't register a new account from the same IP address or JID during
|
||||
this number of seconds after previous registration.
|
||||
Timeout is expressed in seconds, and must be an integer.
|
||||
So, a user that tries to register a new account from the same IP address or JID during
|
||||
this number of seconds after his previous registration
|
||||
will receive an error \term{resource-constraint} with the explanation:
|
||||
``Users are not allowed to register accounts so quickly''.
|
||||
The timeout is expressed in seconds, and it must be an integer.
|
||||
To disable this limitation,
|
||||
instead of an integer put a word like: \term{infinity}.
|
||||
Default value: 600 seconds.
|
||||
@ -4109,9 +4169,12 @@ Examples:
|
||||
implemented in the \modvcardldap{} module. This module does not depend on the
|
||||
authentication method (see~\ref{ldapauth}).
|
||||
|
||||
Note that \ejabberd{} treats LDAP as a read-only storage:
|
||||
Usually \ejabberd{} treats LDAP as a read-only storage:
|
||||
it is possible to consult data, but not possible to
|
||||
create accounts, change password or edit vCard that is stored in LDAP.
|
||||
create accounts or edit vCard that is stored in LDAP.
|
||||
However, it is possible to change passwords if \module{mod\_register} module is enabled
|
||||
and LDAP server supports
|
||||
\footahref{http://tools.ietf.org/html/rfc3062}{RFC 3062}.
|
||||
|
||||
The \modvcardldap{} module has
|
||||
its own optional parameters. The first group of parameters has the same
|
||||
@ -4315,6 +4378,27 @@ searching his info in LDAP.
|
||||
\end{verbatim}
|
||||
\end{itemize}
|
||||
|
||||
\makesubsection{modvcardxupdate}{\modvcardxupdate{}}
|
||||
\ind{modules!\modvcardxupdate{}}\ind{protocols!XEP-0153: vCard-Based Avatars}
|
||||
|
||||
The user's client can store an avatar in the user vCard.
|
||||
The vCard-Based Avatars protocol (\xepref{0153})
|
||||
provides a method for clients to inform the contacts what is the avatar hash value.
|
||||
However, simple or small clients may not implement that protocol.
|
||||
|
||||
If this module is enabled, all the outgoing client presence stanzas get automatically
|
||||
the avatar hash on behalf of the client.
|
||||
So, the contacts receive the presence stanzas with the Update Data described
|
||||
in \xepref{0153} as if the client would had inserted it itself.
|
||||
If the client had already included such element in the presence stanza,
|
||||
it is replaced with the element generated by ejabberd.
|
||||
|
||||
By enabling this module, each vCard modification produces a hash recalculation,
|
||||
and each presence sent by a client produces hash retrieval and a
|
||||
presence stanza rewrite.
|
||||
For this reason, enabling this module will introduce a computational overhead
|
||||
in servers with clients that change frequently their presence.
|
||||
|
||||
\makesubsection{modversion}{\modversion{}}
|
||||
\ind{modules!\modversion{}}\ind{protocols!XEP-0092: Software Version}
|
||||
|
||||
@ -4646,7 +4730,10 @@ Here you can edit access restrictions, manage users, create backups,
|
||||
manage the database, enable/disable ports listened for, view server
|
||||
statistics,\ldots
|
||||
|
||||
Examples:
|
||||
The access rule \term{configure} determines what accounts can access the Web Admin and modify it.
|
||||
The access rule \term{webadmin\_view} is to grant only view access: those accounts can browse the Web Admin with read-only access.
|
||||
|
||||
Example configurations:
|
||||
\begin{itemize}
|
||||
\item You can serve the Web Admin on the same port as the
|
||||
\ind{protocols!XEP-0025: HTTP Polling}HTTP Polling interface. In this example
|
||||
@ -4660,10 +4747,14 @@ Examples:
|
||||
URL). If you log in with `\jid{admin@example.com}' on \\
|
||||
\verb|http://example.org:5280/admin/server/example.com/| you can only
|
||||
administer the virtual host \jid{example.com}.
|
||||
The account `\jid{reviewer@example.com}' can browse that vhost in read-only mode.
|
||||
\begin{verbatim}
|
||||
{acl, admins, {user, "admin", "example.net"}}.
|
||||
{host_config, "example.com", [{acl, admins, {user, "admin", "example.com"}}]}.
|
||||
{host_config, "example.com", [{acl, viewers, {user, "reviewer", "example.com"}}]}.
|
||||
|
||||
{access, configure, [{allow, admins}]}.
|
||||
{access, webadmin_view, [{allow, viewers}]}.
|
||||
|
||||
{hosts, ["example.org"]}.
|
||||
|
||||
|
80
doc/release_notes_2.1.4.txt
Normal file
80
doc/release_notes_2.1.4.txt
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.1.4
|
||||
|
||||
ejabberd 2.1.4 is the fourth release in ejabberd 2.1.x branch,
|
||||
and includes many small bugfixes and improvements.
|
||||
|
||||
Read more details about the changes in:
|
||||
http://redir.process-one.net/ejabberd-2.1.4
|
||||
|
||||
Download the source code and installers from:
|
||||
http://www.process-one.net/en/ejabberd/
|
||||
|
||||
|
||||
This is the full list of changes:
|
||||
|
||||
* Authentication
|
||||
- Extauth: Optionally cache extauth users in mnesia (EJAB-641)
|
||||
- LDAP: Allow inband password change (EJAB-199)
|
||||
- LDAP: Extensible match support (EJAB-722)
|
||||
- LDAP: New option ldap_tls_verify is added (EJAB-1229)
|
||||
- PAM: New option pam_userinfotype to provide username or JID (EJAB-652)
|
||||
|
||||
* HTTP
|
||||
- Add xml default content type
|
||||
- Don't show HTTP request in logs, because reveals password (EJAB-1231)
|
||||
- Move HTTP session timeout log from warning level to info
|
||||
- New Access rule webadmin_view for read-only
|
||||
|
||||
* HTTP-Bind (BOSH)
|
||||
- Change max inactivity from 30 to 120 seconds
|
||||
- Export functions to facilitate prebinding methods
|
||||
- Use dirty_delete when removing the session
|
||||
- Remove an unneeded delay of 100 milliseconds
|
||||
|
||||
* Pubsub, PEP and Caps
|
||||
- Enforce pubsub#presence_based_delivery (EJAB-1221)
|
||||
- Enforce pubsub#show_values subscription option (EJAB-1096)
|
||||
- Fix error code when unsubscribing from a non-existent node
|
||||
- Fix to send node notifications (EJAB-1225)
|
||||
- Full support for XEP-0115 v1.5 (EJAB-1223)(EJAB-1189)
|
||||
- Make last_item_cache feature to be cluster aware
|
||||
- Prevent orphaned pubsub node (EJAB-1233)
|
||||
- Send created node notifications
|
||||
|
||||
* Other
|
||||
- Bounce messages when closing c2s session
|
||||
- Bugfixes when handling Service Discovery to contacts (EJAB-1207)
|
||||
- Compilation of ejabberd_debug.erl is now optional
|
||||
- Don't send error stanza as reply to error stanza (EJAB-930)
|
||||
- Don't store blocked messages in offline queue
|
||||
- Reduce verbosity of log when captcha_cmd is checked but not configured
|
||||
- Use a standard method to get a random seed (EJAB-1229)
|
||||
- Commands: new update_list and update to update modified modules (EJAB-1237)
|
||||
- Localization: Updated most translations
|
||||
- MUC: Refactor code to reduce calls to get_affiliation and get_role
|
||||
- ODBC: Add created_at column also to PostgreSQL schema
|
||||
- Vcard: Automatic vcard avatar addition in presence
|
||||
|
||||
|
||||
Upgrading From previous ejabberd releases:
|
||||
|
||||
- If you use PostgreSQL, maybe you want to add the column created_at
|
||||
to several tables. This is only a suggestion; ejabberd doesn't use
|
||||
that column. Add it to your existing database executing those SQL
|
||||
statements:
|
||||
|
||||
ALTER TABLE users ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT now();
|
||||
ALTER TABLE rosterusers ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT now();
|
||||
ALTER TABLE spool ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT now();
|
||||
ALTER TABLE vcard ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT now();
|
||||
ALTER TABLE privacy_list ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT now();
|
||||
ALTER TABLE privacy_storage ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT now();
|
||||
|
||||
|
||||
Bug reports
|
||||
|
||||
You can officially report bugs on ProcessOne support site:
|
||||
http://support.process-one.net/
|
||||
|
70
doc/release_notes_2.1.5.txt
Normal file
70
doc/release_notes_2.1.5.txt
Normal file
@ -0,0 +1,70 @@
|
||||
|
||||
Release Notes
|
||||
ejabberd 2.1.5
|
||||
|
||||
ejabberd 2.1.5 is the fifth release in ejabberd 2.1.x branch,
|
||||
and includes several minor bugfixes and a few improvements.
|
||||
|
||||
Read more details about the changes in:
|
||||
http://redir.process-one.net/ejabberd-2.1.4
|
||||
|
||||
Download the source code and installers from:
|
||||
http://www.process-one.net/en/ejabberd/
|
||||
|
||||
|
||||
This is the full list of changes:
|
||||
|
||||
* Authentication
|
||||
- Extauth: Support parallel script running (EJAB-1280)
|
||||
- mod_register: Return Registered element when account exists
|
||||
|
||||
* ejabberdctl
|
||||
- Fix print of command result that contains ~
|
||||
- Fix problem when FIREWALL_WINDOW options for erl kernel were used
|
||||
- Fix typo in update_list command (EJAB-1237)
|
||||
- Some systems delete the lock dir; in such case don't use flock at all
|
||||
- The command Update now returns meaningful message and exit-status (EJAB-1237)
|
||||
|
||||
* HTTP-Bind (BOSH)
|
||||
- Don't say v1.2 in the Bind HTTP page
|
||||
- New optional BOSH connection attribute process-delay (EJAB-1257)
|
||||
|
||||
* MUC
|
||||
- Document the mod_muc option captcha_protected
|
||||
- Now admins are able to see private rooms in disco (EJAB-1269)
|
||||
- Show some more room options in the log file
|
||||
|
||||
* ODBC
|
||||
- Correct handling of SQL boolean types (EJAB-1275)
|
||||
- Discard too old queued requests (the caller has already got a timeout)
|
||||
- Fixes wrong SQL escaping when --enable-full-xml is set
|
||||
- Use ets insead of asking supervisor in ejabberd_odbc_sup:get_pids/1
|
||||
|
||||
* Pubsub, PEP and Caps
|
||||
- Enforce disco features results (EJAB-1033, EJAB-1228, EJAB-1238)
|
||||
- Support all the hash functions required by Caps XEP-0115
|
||||
|
||||
* Requirements
|
||||
- Fixed support for Erlang R12; which doesn't support: true andalso ok
|
||||
- Support OTP R14A by using public_key library instead of old ssl (EJAB-953)
|
||||
- Requirement of OpenSSL increased from 0.9.6 to 0.9.8
|
||||
- OpenSSL is now required, not optional
|
||||
|
||||
* Other
|
||||
- Don't ask for client certificate when using tls (EJAB-1267)
|
||||
- Fix typo in --enable-transient_supervisors
|
||||
- Fix privacy check when serving local Last (EJAB-1271)
|
||||
- Inform client that SSL session caching is disabled
|
||||
- New configure option: --enable-nif
|
||||
- Use driver allocator in C files for reflecting memory in erlang:memory(system)
|
||||
- Debug: New p1_prof compiled with: make debugtools=true
|
||||
- Debug: Added functions to collect stats about queues, memory, reductions etc
|
||||
- HTTP: Log error if request has ambiguous Host header (EJAB-1261)
|
||||
- Logs: When logging s2s out connection attempt or success, log if TLS is used
|
||||
- Shared Rosters: When account is deleted, delete also member of stored rosters
|
||||
|
||||
|
||||
Bug reports
|
||||
|
||||
You can officially report bugs on ProcessOne support site:
|
||||
http://support.process-one.net/
|
@ -1,2 +1,2 @@
|
||||
% ejabberd version (automatically generated).
|
||||
\newcommand{\version}{2.1.3}
|
||||
\newcommand{\version}{2.1.x}
|
||||
|
0
examples/extauth/check_pass_null.pl
Normal file → Executable file
0
examples/extauth/check_pass_null.pl
Normal file → Executable file
@ -30,15 +30,17 @@ else
|
||||
INIT_USER=$(INSTALLUSER)
|
||||
endif
|
||||
|
||||
EFLAGS += @ERLANG_SSL39@ -pa .
|
||||
EFLAGS += @ERLANG_SSLVER@ -pa .
|
||||
ERLANG_CFLAGS += @ERLANG_SSLVER@
|
||||
|
||||
# make debug=true to compile Erlang module with debug informations.
|
||||
ifdef debug
|
||||
EFLAGS+=+debug_info +export_all
|
||||
endif
|
||||
|
||||
ifdef ejabberd_debug
|
||||
EFLAGS+=-Dejabberd_debug
|
||||
DEBUGTOOLS = p1_prof.erl
|
||||
ifdef debugtools
|
||||
SOURCES+=$(DEBUGTOOLS)
|
||||
endif
|
||||
|
||||
ifeq (@hipe@, true)
|
||||
@ -53,10 +55,20 @@ ifeq (@full_xml@, true)
|
||||
EFLAGS+=-DFULL_XML_SUPPORT
|
||||
endif
|
||||
|
||||
ifeq (@nif@, true)
|
||||
EFLAGS+=-DNIF
|
||||
ERLSHLIBS=xml.so
|
||||
endif
|
||||
|
||||
ifeq (@transient_supervisors@, false)
|
||||
EFLAGS+=-DNO_TRANSIENT_SUPERVISORS
|
||||
endif
|
||||
|
||||
ifeq (@md2@, true)
|
||||
EFLAGS+=-DHAVE_MD2
|
||||
ERLANG_CFLAGS += -DHAVE_MD2
|
||||
endif
|
||||
|
||||
INSTALL_EPAM=
|
||||
ifeq (@pam@, pam)
|
||||
INSTALL_EPAM=install -m 750 $(O_USER) epam $(PBINDIR)
|
||||
@ -66,10 +78,11 @@ prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
|
||||
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @mod_proxy65@ @eldap@ @pam@ @web@ stringprep stun @tls@ @odbc@ @ejabberd_zlib@
|
||||
ERLSHLIBS = expat_erl.so
|
||||
ERLSHLIBS += expat_erl.so
|
||||
ERLBEHAVS = cyrsasl.erl gen_mod.erl p1_fsm.erl
|
||||
SOURCES_ALL = $(wildcard *.erl)
|
||||
SOURCES = $(filter-out $(ERLBEHAVS),$(SOURCES_ALL))
|
||||
SOURCES_MISC = $(ERLBEHAVS) $(DEBUGTOOLS)
|
||||
SOURCES += $(filter-out $(SOURCES_MISC),$(SOURCES_ALL))
|
||||
ERLBEHAVBEAMS = $(ERLBEHAVS:.erl=.beam)
|
||||
BEAMS = $(SOURCES:.erl=.beam)
|
||||
|
||||
@ -247,9 +260,14 @@ install: all
|
||||
#
|
||||
# Documentation
|
||||
install -d $(DOCDIR)
|
||||
install -m 644 ../doc/dev.html $(DOCDIR)
|
||||
install -m 644 ../doc/guide.html $(DOCDIR)
|
||||
install -m 644 ../doc/*.png $(DOCDIR)
|
||||
install -m 644 ../doc/*.txt $(DOCDIR)
|
||||
[ -f ../doc/guide.pdf ] \
|
||||
&& install -m 644 ../doc/guide.pdf $(DOCDIR) \
|
||||
|| echo "No ../doc/guide.pdf was built"
|
||||
install -m 644 ../COPYING $(DOCDIR)
|
||||
|
||||
uninstall: uninstall-binary
|
||||
|
||||
|
17
src/aclocal.m4
vendored
17
src/aclocal.m4
vendored
@ -121,7 +121,6 @@ AC_DEFUN(AM_WITH_ERLANG,
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-export([[start/0]]).
|
||||
-include_lib("ssl/include/ssl_pkix.hrl").
|
||||
|
||||
start() ->
|
||||
EIDirS = code:lib_dir("erl_interface") ++ "\n",
|
||||
@ -130,11 +129,13 @@ start() ->
|
||||
file:write_file("conftest.out", list_to_binary(EIDirS ++ EILibS ++ ssldef() ++ RootDirS)),
|
||||
halt().
|
||||
|
||||
-[ifdef]('id-pkix').
|
||||
ssldef() -> "-DSSL39\n".
|
||||
-else.
|
||||
ssldef() -> "\n".
|
||||
-endif.
|
||||
ssldef() ->
|
||||
OTP = (catch erlang:system_info(otp_release)),
|
||||
if
|
||||
OTP >= "R14" -> "-DSSL40\n";
|
||||
OTP >= "R12" -> "-DSSL39\n";
|
||||
true -> "\n"
|
||||
end.
|
||||
|
||||
%% return physical architecture based on OS/Processor
|
||||
archname() ->
|
||||
@ -184,7 +185,7 @@ _EOF
|
||||
# Second line
|
||||
ERLANG_EI_LIB=`cat conftest.out | head -n 2 | tail -n 1`
|
||||
# Third line
|
||||
ERLANG_SSL39=`cat conftest.out | head -n 3 | tail -n 1`
|
||||
ERLANG_SSLVER=`cat conftest.out | head -n 3 | tail -n 1`
|
||||
# End line
|
||||
ERLANG_DIR=`cat conftest.out | tail -n 1`
|
||||
|
||||
@ -193,7 +194,7 @@ _EOF
|
||||
|
||||
AC_SUBST(ERLANG_CFLAGS)
|
||||
AC_SUBST(ERLANG_LIBS)
|
||||
AC_SUBST(ERLANG_SSL39)
|
||||
AC_SUBST(ERLANG_SSLVER)
|
||||
AC_SUBST(ERLC)
|
||||
AC_SUBST(ERL)
|
||||
])
|
||||
|
@ -1,7 +1,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : adhoc.erl
|
||||
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%% Purpose : Provide helper functions for ad-hoc commands (JEP-0050)
|
||||
%%% Purpose : Provide helper functions for ad-hoc commands (XEP-0050)
|
||||
%%% Created : 31 Oct 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%%
|
||||
%%%
|
||||
|
244
src/configure
vendored
244
src/configure
vendored
@ -1,12 +1,14 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.64 for ejabberd.erl version.
|
||||
# Generated by GNU Autoconf 2.65 for ejabberd 2.1.x.
|
||||
#
|
||||
# Report bugs to <ejabberd@process-one.net>.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
|
||||
# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software
|
||||
# Foundation, Inc.
|
||||
# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
|
||||
# Inc.
|
||||
#
|
||||
#
|
||||
# This configure script is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy, distribute and modify it.
|
||||
@ -527,7 +529,8 @@ as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
|
||||
as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
|
||||
|
||||
|
||||
exec 7<&0 </dev/null 6>&1
|
||||
test -n "$DJDIR" || exec 7<&0 </dev/null
|
||||
exec 6>&1
|
||||
|
||||
# Name of the host.
|
||||
# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
|
||||
@ -547,10 +550,10 @@ MFLAGS=
|
||||
MAKEFLAGS=
|
||||
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='ejabberd.erl'
|
||||
PACKAGE_NAME='ejabberd'
|
||||
PACKAGE_TARNAME='ejabberd'
|
||||
PACKAGE_VERSION='version'
|
||||
PACKAGE_STRING='ejabberd.erl version'
|
||||
PACKAGE_VERSION='2.1.x'
|
||||
PACKAGE_STRING='ejabberd 2.1.x'
|
||||
PACKAGE_BUGREPORT='ejabberd@process-one.net'
|
||||
PACKAGE_URL=''
|
||||
|
||||
@ -605,9 +608,11 @@ build_os
|
||||
build_vendor
|
||||
build_cpu
|
||||
build
|
||||
md2
|
||||
INSTALLUSER
|
||||
SSL_CFLAGS
|
||||
SSL_LIBS
|
||||
nif
|
||||
full_xml
|
||||
transient_supervisors
|
||||
db_type
|
||||
@ -644,7 +649,7 @@ EGREP
|
||||
GREP
|
||||
CPP
|
||||
LIBICONV
|
||||
ERLANG_SSL39
|
||||
ERLANG_SSLVER
|
||||
ERLANG_LIBS
|
||||
ERLANG_CFLAGS
|
||||
ERL
|
||||
@ -718,6 +723,7 @@ enable_roster_gateway_workaround
|
||||
enable_mssql
|
||||
enable_transient_supervisors
|
||||
enable_full_xml
|
||||
enable_nif
|
||||
with_openssl
|
||||
enable_user
|
||||
'
|
||||
@ -1273,7 +1279,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures ejabberd.erl version to adapt to many kinds of systems.
|
||||
\`configure' configures ejabberd 2.1.x to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@ -1339,7 +1345,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of ejabberd.erl version:";;
|
||||
short | recursive ) echo "Configuration of ejabberd 2.1.x:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@ -1369,6 +1375,8 @@ Optional Features:
|
||||
(default: yes)
|
||||
--enable-full-xml use XML features in XMPP stream (ex: CDATA)
|
||||
(default: no, requires XML compliant clients)
|
||||
--enable-nif replace some functions with C equivalents. Requires
|
||||
Erlang R13B04 or higher (default: no)
|
||||
--enable-user[[[=USER]]]
|
||||
allow this system user to start ejabberd (default:
|
||||
no)
|
||||
@ -1390,7 +1398,7 @@ Some influential environment variables:
|
||||
LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
|
||||
nonstandard directory <lib dir>
|
||||
LIBS libraries to pass to the linker, e.g. -l<library>
|
||||
CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
|
||||
CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
|
||||
you have headers in a nonstandard directory <include dir>
|
||||
CPP C preprocessor
|
||||
ERLC Erlang/OTP compiler command [autodetected]
|
||||
@ -1462,8 +1470,8 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
ejabberd.erl configure version
|
||||
generated by GNU Autoconf 2.64
|
||||
ejabberd configure 2.1.x
|
||||
generated by GNU Autoconf 2.65
|
||||
|
||||
Copyright (C) 2009 Free Software Foundation, Inc.
|
||||
This configure script is free software; the Free Software Foundation
|
||||
@ -1510,7 +1518,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
|
||||
ac_retval=1
|
||||
fi
|
||||
eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
|
||||
return $ac_retval
|
||||
as_fn_set_status $ac_retval
|
||||
|
||||
} # ac_fn_c_try_compile
|
||||
|
||||
@ -1556,7 +1564,7 @@ fi
|
||||
# left behind by Apple's compiler. We do this before executing the actions.
|
||||
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
|
||||
eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
|
||||
return $ac_retval
|
||||
as_fn_set_status $ac_retval
|
||||
|
||||
} # ac_fn_c_try_link
|
||||
|
||||
@ -1593,7 +1601,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
|
||||
ac_retval=1
|
||||
fi
|
||||
eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
|
||||
return $ac_retval
|
||||
as_fn_set_status $ac_retval
|
||||
|
||||
} # ac_fn_c_try_cpp
|
||||
|
||||
@ -1728,7 +1736,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
|
||||
fi
|
||||
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
|
||||
eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
|
||||
return $ac_retval
|
||||
as_fn_set_status $ac_retval
|
||||
|
||||
} # ac_fn_c_try_run
|
||||
|
||||
@ -1801,15 +1809,15 @@ sed 's/^/| /' conftest.$ac_ext >&5
|
||||
fi
|
||||
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
|
||||
eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
|
||||
return $ac_retval
|
||||
as_fn_set_status $ac_retval
|
||||
|
||||
} # ac_fn_erl_try_run
|
||||
cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by ejabberd.erl $as_me version, which was
|
||||
generated by GNU Autoconf 2.64. Invocation command line was
|
||||
It was created by ejabberd $as_me 2.1.x, which was
|
||||
generated by GNU Autoconf 2.65. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
|
||||
@ -2062,7 +2070,7 @@ fi
|
||||
for ac_site_file in "$ac_site_file1" "$ac_site_file2"
|
||||
do
|
||||
test "x$ac_site_file" = xNONE && continue
|
||||
if test -r "$ac_site_file"; then
|
||||
if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
|
||||
$as_echo "$as_me: loading site script $ac_site_file" >&6;}
|
||||
sed 's/^/| /' "$ac_site_file" >&5
|
||||
@ -2071,9 +2079,9 @@ $as_echo "$as_me: loading site script $ac_site_file" >&6;}
|
||||
done
|
||||
|
||||
if test -r "$cache_file"; then
|
||||
# Some versions of bash will fail to source /dev/null (special
|
||||
# files actually), so we avoid doing that.
|
||||
if test -f "$cache_file"; then
|
||||
# Some versions of bash will fail to source /dev/null (special files
|
||||
# actually), so we avoid doing that. DJGPP emulates it as a regular file.
|
||||
if test /dev/null != "$cache_file" && test -f "$cache_file"; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
|
||||
$as_echo "$as_me: loading cache $cache_file" >&6;}
|
||||
case $cache_file in
|
||||
@ -2479,32 +2487,30 @@ $as_echo "$ac_try_echo"; } >&5
|
||||
... rest of stderr output deleted ...
|
||||
10q' conftest.err >conftest.er1
|
||||
cat conftest.er1 >&5
|
||||
rm -f conftest.er1 conftest.err
|
||||
fi
|
||||
rm -f conftest.er1 conftest.err
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }
|
||||
done
|
||||
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
#include <stdio.h>
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
FILE *f = fopen ("conftest.out", "w");
|
||||
return ferror (f) || fclose (f) != 0;
|
||||
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
ac_clean_files_save=$ac_clean_files
|
||||
ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out conftest.out"
|
||||
ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
|
||||
# Try to create an executable without -o first, disregard a.out.
|
||||
# It will help us diagnose broken compilers, and finding out an intuition
|
||||
# of exeext.
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
|
||||
$as_echo_n "checking for C compiler default output file name... " >&6; }
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
|
||||
$as_echo_n "checking whether the C compiler works... " >&6; }
|
||||
ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
|
||||
|
||||
# The possible output files:
|
||||
@ -2566,10 +2572,10 @@ test "$ac_cv_exeext" = no && ac_cv_exeext=
|
||||
else
|
||||
ac_file=''
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
|
||||
$as_echo "$ac_file" >&6; }
|
||||
if test -z "$ac_file"; then :
|
||||
$as_echo "$as_me: failed program was:" >&5
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
$as_echo "$as_me: failed program was:" >&5
|
||||
sed 's/^/| /' conftest.$ac_ext >&5
|
||||
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
|
||||
@ -2577,51 +2583,18 @@ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
|
||||
{ as_fn_set_status 77
|
||||
as_fn_error "C compiler cannot create executables
|
||||
See \`config.log' for more details." "$LINENO" 5; }; }
|
||||
else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
|
||||
$as_echo_n "checking for C compiler default output file name... " >&6; }
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
|
||||
$as_echo "$ac_file" >&6; }
|
||||
ac_exeext=$ac_cv_exeext
|
||||
|
||||
# Check that the compiler produces executables we can run. If not, either
|
||||
# the compiler is broken, or we cross compile.
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
|
||||
$as_echo_n "checking whether the C compiler works... " >&6; }
|
||||
# If not cross compiling, check that we can run a simple program.
|
||||
if test "$cross_compiling" != yes; then
|
||||
if { ac_try='./$ac_file'
|
||||
{ { case "(($ac_try" in
|
||||
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
|
||||
*) ac_try_echo=$ac_try;;
|
||||
esac
|
||||
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
|
||||
$as_echo "$ac_try_echo"; } >&5
|
||||
(eval "$ac_try") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; }; then
|
||||
cross_compiling=no
|
||||
else
|
||||
if test "$cross_compiling" = maybe; then
|
||||
cross_compiling=yes
|
||||
else
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
|
||||
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
|
||||
as_fn_error "cannot run C compiled programs.
|
||||
If you meant to cross compile, use \`--host'.
|
||||
See \`config.log' for more details." "$LINENO" 5; }
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
|
||||
rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out conftest.out
|
||||
rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
|
||||
ac_clean_files=$ac_clean_files_save
|
||||
# Check that the compiler produces executables we can run. If not, either
|
||||
# the compiler is broken, or we cross compile.
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
|
||||
$as_echo_n "checking whether we are cross compiling... " >&6; }
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
|
||||
$as_echo "$cross_compiling" >&6; }
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
|
||||
$as_echo_n "checking for suffix of executables... " >&6; }
|
||||
if { { ac_try="$ac_link"
|
||||
@ -2654,13 +2627,72 @@ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
|
||||
as_fn_error "cannot compute suffix of executables: cannot compile and link
|
||||
See \`config.log' for more details." "$LINENO" 5; }
|
||||
fi
|
||||
rm -f conftest$ac_cv_exeext
|
||||
rm -f conftest conftest$ac_cv_exeext
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
|
||||
$as_echo "$ac_cv_exeext" >&6; }
|
||||
|
||||
rm -f conftest.$ac_ext
|
||||
EXEEXT=$ac_cv_exeext
|
||||
ac_exeext=$EXEEXT
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
#include <stdio.h>
|
||||
int
|
||||
main ()
|
||||
{
|
||||
FILE *f = fopen ("conftest.out", "w");
|
||||
return ferror (f) || fclose (f) != 0;
|
||||
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
ac_clean_files="$ac_clean_files conftest.out"
|
||||
# Check that the compiler produces executables we can run. If not, either
|
||||
# the compiler is broken, or we cross compile.
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
|
||||
$as_echo_n "checking whether we are cross compiling... " >&6; }
|
||||
if test "$cross_compiling" != yes; then
|
||||
{ { ac_try="$ac_link"
|
||||
case "(($ac_try" in
|
||||
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
|
||||
*) ac_try_echo=$ac_try;;
|
||||
esac
|
||||
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
|
||||
$as_echo "$ac_try_echo"; } >&5
|
||||
(eval "$ac_link") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }
|
||||
if { ac_try='./conftest$ac_cv_exeext'
|
||||
{ { case "(($ac_try" in
|
||||
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
|
||||
*) ac_try_echo=$ac_try;;
|
||||
esac
|
||||
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
|
||||
$as_echo "$ac_try_echo"; } >&5
|
||||
(eval "$ac_try") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; }; then
|
||||
cross_compiling=no
|
||||
else
|
||||
if test "$cross_compiling" = maybe; then
|
||||
cross_compiling=yes
|
||||
else
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
|
||||
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
|
||||
as_fn_error "cannot run C compiled programs.
|
||||
If you meant to cross compile, use \`--host'.
|
||||
See \`config.log' for more details." "$LINENO" 5; }
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
|
||||
$as_echo "$cross_compiling" >&6; }
|
||||
|
||||
rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
|
||||
ac_clean_files=$ac_clean_files_save
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
|
||||
$as_echo_n "checking for suffix of object files... " >&6; }
|
||||
if test "${ac_cv_objext+set}" = set; then :
|
||||
@ -3180,7 +3212,6 @@ fi
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-export([start/0]).
|
||||
-include_lib("ssl/include/ssl_pkix.hrl").
|
||||
|
||||
start() ->
|
||||
EIDirS = code:lib_dir("erl_interface") ++ "\n",
|
||||
@ -3189,11 +3220,13 @@ start() ->
|
||||
file:write_file("conftest.out", list_to_binary(EIDirS ++ EILibS ++ ssldef() ++ RootDirS)),
|
||||
halt().
|
||||
|
||||
-ifdef('id-pkix').
|
||||
ssldef() -> "-DSSL39\n".
|
||||
-else.
|
||||
ssldef() -> "\n".
|
||||
-endif.
|
||||
ssldef() ->
|
||||
OTP = (catch erlang:system_info(otp_release)),
|
||||
if
|
||||
OTP >= "R14" -> "-DSSL40\n";
|
||||
OTP >= "R12" -> "-DSSL39\n";
|
||||
true -> "\n"
|
||||
end.
|
||||
|
||||
%% return physical architecture based on OS/Processor
|
||||
archname() ->
|
||||
@ -3243,7 +3276,7 @@ _EOF
|
||||
# Second line
|
||||
ERLANG_EI_LIB=`cat conftest.out | head -n 2 | tail -n 1`
|
||||
# Third line
|
||||
ERLANG_SSL39=`cat conftest.out | head -n 3 | tail -n 1`
|
||||
ERLANG_SSLVER=`cat conftest.out | head -n 3 | tail -n 1`
|
||||
# End line
|
||||
ERLANG_DIR=`cat conftest.out | tail -n 1`
|
||||
|
||||
@ -4623,7 +4656,7 @@ if test "${enable_transient_supervisors+set}" = set; then :
|
||||
enableval=$enable_transient_supervisors; case "${enableval}" in
|
||||
yes) transient_supervisors=true ;;
|
||||
no) transient_supervisors=false ;;
|
||||
*) as_fn_error "bad value ${enableval} for --enable-full-xml" "$LINENO" 5 ;;
|
||||
*) as_fn_error "bad value ${enableval} for --enable-transient_supervisors" "$LINENO" 5 ;;
|
||||
esac
|
||||
else
|
||||
transient_supervisors=true
|
||||
@ -4644,6 +4677,19 @@ fi
|
||||
|
||||
|
||||
|
||||
# Check whether --enable-nif was given.
|
||||
if test "${enable_nif+set}" = set; then :
|
||||
enableval=$enable_nif; case "${enableval}" in
|
||||
yes) nif=true ;;
|
||||
no) nif=false ;;
|
||||
*) as_fn_error "bad value ${enableval} for --enable-nif" "$LINENO" 5 ;;
|
||||
esac
|
||||
else
|
||||
nif=false
|
||||
fi
|
||||
|
||||
|
||||
|
||||
ac_config_files="$ac_config_files Makefile $make_mod_irc $make_mod_muc $make_mod_pubsub $make_mod_proxy65 $make_eldap $make_pam $make_web stringprep/Makefile stun/Makefile $make_tls $make_odbc $make_ejabberd_zlib"
|
||||
|
||||
#openssl
|
||||
@ -4777,6 +4823,16 @@ if test "$ENABLEUSER" != ""; then
|
||||
|
||||
fi
|
||||
|
||||
ac_fn_c_check_header_mongrel "$LINENO" "openssl/md2.h" "ac_cv_header_openssl_md2_h" "$ac_includes_default"
|
||||
if test "x$ac_cv_header_openssl_md2_h" = x""yes; then :
|
||||
md2=true
|
||||
else
|
||||
md2=false
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
ac_aux_dir=
|
||||
for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
|
||||
for ac_t in install-sh install.sh shtool; do
|
||||
@ -4920,7 +4976,7 @@ case "$target_os" in
|
||||
echo "Target OS is 'Darwin10'"
|
||||
ac_ext=erl
|
||||
ac_compile='$ERLC $ERLCFLAGS -b beam conftest.$ac_ext >&5'
|
||||
ac_link='$ERLC $ERLCFLAGS -b beam conftest.$ac_ext >&5 ; echo "#!/bin/sh" > conftest$ac_exeext ; $as_echo "\"$ERL\" -run conftest start -run init stop -noshell" >> conftest$ac_exeext ; chmod +x conftest$ac_exeext'
|
||||
ac_link='$ERLC $ERLCFLAGS -b beam conftest.$ac_ext >&5 && echo "#!/bin/sh" > conftest$ac_exeext && $as_echo "\"$ERL\" -run conftest start -run init stop -noshell" >> conftest$ac_exeext && chmod +x conftest$ac_exeext'
|
||||
|
||||
if test -n "$ERLC"; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for erlc" >&5
|
||||
@ -5609,8 +5665,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by ejabberd.erl $as_me version, which was
|
||||
generated by GNU Autoconf 2.64. Invocation command line was
|
||||
This file was extended by ejabberd $as_me 2.1.x, which was
|
||||
generated by GNU Autoconf 2.65. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
CONFIG_HEADERS = $CONFIG_HEADERS
|
||||
@ -5645,6 +5701,7 @@ Usage: $0 [OPTION]... [TAG]...
|
||||
|
||||
-h, --help print this help, then exit
|
||||
-V, --version print version number and configuration settings, then exit
|
||||
--config print configuration, then exit
|
||||
-q, --quiet, --silent
|
||||
do not print progress messages
|
||||
-d, --debug don't remove temporary files
|
||||
@ -5659,10 +5716,11 @@ Report bugs to <ejabberd@process-one.net>."
|
||||
|
||||
_ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
ejabberd.erl config.status version
|
||||
configured by $0, generated by GNU Autoconf 2.64,
|
||||
with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
|
||||
ejabberd config.status 2.1.x
|
||||
configured by $0, generated by GNU Autoconf 2.65,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
Copyright (C) 2009 Free Software Foundation, Inc.
|
||||
This config.status script is free software; the Free Software Foundation
|
||||
@ -5697,6 +5755,8 @@ do
|
||||
ac_cs_recheck=: ;;
|
||||
--version | --versio | --versi | --vers | --ver | --ve | --v | -V )
|
||||
$as_echo "$ac_cs_version"; exit ;;
|
||||
--config | --confi | --conf | --con | --co | --c )
|
||||
$as_echo "$ac_cs_config"; exit ;;
|
||||
--debug | --debu | --deb | --de | --d | -d )
|
||||
debug=: ;;
|
||||
--file | --fil | --fi | --f )
|
||||
@ -5876,7 +5936,7 @@ s/'"$ac_delim"'$//
|
||||
t delim
|
||||
:nl
|
||||
h
|
||||
s/\(.\{148\}\).*/\1/
|
||||
s/\(.\{148\}\)..*/\1/
|
||||
t more1
|
||||
s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
|
||||
p
|
||||
@ -5890,7 +5950,7 @@ s/.\{148\}//
|
||||
t nl
|
||||
:delim
|
||||
h
|
||||
s/\(.\{148\}\).*/\1/
|
||||
s/\(.\{148\}\)..*/\1/
|
||||
t more2
|
||||
s/["\\]/\\&/g; s/^/"/; s/$/"/
|
||||
p
|
||||
|
@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.53)
|
||||
AC_INIT(ejabberd.erl, version, [ejabberd@process-one.net], [ejabberd])
|
||||
AC_INIT(ejabberd, m4_esyscmd([grep -o -E "\{vsn,.\".*\"\}" ejabberd.app | cut -d \" -f 2 | tr -d '\n']), [ejabberd@process-one.net], [ejabberd])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
@ -81,7 +81,7 @@ AC_ARG_ENABLE(transient_supervisors,
|
||||
[case "${enableval}" in
|
||||
yes) transient_supervisors=true ;;
|
||||
no) transient_supervisors=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-full-xml) ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-transient_supervisors) ;;
|
||||
esac],[transient_supervisors=true])
|
||||
AC_SUBST(transient_supervisors)
|
||||
|
||||
@ -94,6 +94,15 @@ AC_ARG_ENABLE(full_xml,
|
||||
esac],[full_xml=false])
|
||||
AC_SUBST(full_xml)
|
||||
|
||||
AC_ARG_ENABLE(nif,
|
||||
[AC_HELP_STRING([--enable-nif], [replace some functions with C equivalents. Requires Erlang R13B04 or higher (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) nif=true ;;
|
||||
no) nif=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-nif) ;;
|
||||
esac],[nif=false])
|
||||
AC_SUBST(nif)
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
$make_mod_irc
|
||||
$make_mod_muc
|
||||
@ -139,6 +148,9 @@ if test "$ENABLEUSER" != ""; then
|
||||
AC_SUBST([INSTALLUSER], [$ENABLEUSER])
|
||||
fi
|
||||
|
||||
AC_CHECK_HEADER(openssl/md2.h, md2=true, md2=false)
|
||||
AC_SUBST(md2)
|
||||
|
||||
AC_CANONICAL_SYSTEM
|
||||
#AC_DEFINE_UNQUOTED(CPU_VENDOR_OS, "$target")
|
||||
#AC_SUBST(target_os)
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
{application, ejabberd,
|
||||
[{description, "ejabberd"},
|
||||
{vsn, "2.1.3"},
|
||||
{vsn, "2.1.x"},
|
||||
{modules, [acl,
|
||||
adhoc,
|
||||
configure,
|
||||
@ -115,6 +115,7 @@
|
||||
nodetree_virtual,
|
||||
p1_fsm,
|
||||
p1_mnesia,
|
||||
p1_prof,
|
||||
randoms,
|
||||
sha,
|
||||
shaper,
|
||||
|
@ -31,6 +31,8 @@
|
||||
%% Server
|
||||
status/0, reopen_log/0,
|
||||
stop_kindly/2, send_service_message_all_mucs/2,
|
||||
%% Erlang
|
||||
update_list/0, update/1,
|
||||
%% Accounts
|
||||
register/3, unregister/2,
|
||||
registered_users/1,
|
||||
@ -95,6 +97,17 @@ commands() ->
|
||||
{leveldesc, string}
|
||||
]}}},
|
||||
|
||||
#ejabberd_commands{name = update_list, tags = [server],
|
||||
desc = "List modified modules that can be updated",
|
||||
module = ?MODULE, function = update_list,
|
||||
args = [],
|
||||
result = {modules, {list, {module, string}}}},
|
||||
#ejabberd_commands{name = update, tags = [server],
|
||||
desc = "Update the given module, or use the keyword: all",
|
||||
module = ?MODULE, function = update,
|
||||
args = [{module, string}],
|
||||
result = {res, restuple}},
|
||||
|
||||
#ejabberd_commands{name = register, tags = [accounts],
|
||||
desc = "Register a user",
|
||||
module = ?MODULE, function = register,
|
||||
@ -150,7 +163,7 @@ commands() ->
|
||||
|
||||
#ejabberd_commands{name = set_master, tags = [mnesia],
|
||||
desc = "Set master node of the clustered Mnesia tables",
|
||||
longdesc = "If you provie as nodename \"self\", this "
|
||||
longdesc = "If you provide as nodename \"self\", this "
|
||||
"node will be set as its own master.",
|
||||
module = ?MODULE, function = set_master,
|
||||
args = [{nodename, string}], result = {res, restuple}},
|
||||
@ -276,6 +289,27 @@ send_service_message_all_mucs(Subject, AnnouncementText) ->
|
||||
end,
|
||||
?MYHOSTS).
|
||||
|
||||
%%%
|
||||
%%% ejabberd_update
|
||||
%%%
|
||||
|
||||
update_list() ->
|
||||
{ok, _Dir, UpdatedBeams, _Script, _LowLevelScript, _Check} =
|
||||
ejabberd_update:update_info(),
|
||||
[atom_to_list(Beam) || Beam <- UpdatedBeams].
|
||||
|
||||
update("all") ->
|
||||
[update_module(ModStr) || ModStr <- update_list()];
|
||||
update(ModStr) ->
|
||||
update_module(ModStr).
|
||||
|
||||
update_module(ModuleNameString) ->
|
||||
ModuleName = list_to_atom(ModuleNameString),
|
||||
case ejabberd_update:update([ModuleName]) of
|
||||
{ok, Res} -> {ok, io_lib:format("Updated: ~p", [Res])};
|
||||
{error, Reason} -> {error, Reason}
|
||||
end.
|
||||
|
||||
%%%
|
||||
%%% Account management
|
||||
%%%
|
||||
@ -283,7 +317,7 @@ send_service_message_all_mucs(Subject, AnnouncementText) ->
|
||||
register(User, Host, Password) ->
|
||||
case ejabberd_auth:try_register(User, Host, Password) of
|
||||
{atomic, ok} ->
|
||||
{ok, io_lib:format("User ~s@~s succesfully registered", [User, Host])};
|
||||
{ok, io_lib:format("User ~s@~s successfully registered", [User, Host])};
|
||||
{atomic, exists} ->
|
||||
String = io_lib:format("User ~s@~s already registered at node ~p",
|
||||
[User, Host, node()]),
|
||||
|
@ -46,6 +46,7 @@ start(normal, _Args) ->
|
||||
db_init(),
|
||||
sha:start(),
|
||||
stringprep_sup:start_link(),
|
||||
xml:start(),
|
||||
start(),
|
||||
translate:start(),
|
||||
acl:start(),
|
||||
@ -56,6 +57,8 @@ start(normal, _Args) ->
|
||||
ejabberd_config:start(),
|
||||
ejabberd_check:config(),
|
||||
connect_nodes(),
|
||||
%% Loading ASN.1 driver explicitly to avoid races in LDAP
|
||||
catch asn1rt:load_driver(),
|
||||
Sup = ejabberd_sup:start_link(),
|
||||
ejabberd_rdbms:start(),
|
||||
ejabberd_auth:start(),
|
||||
@ -85,7 +88,7 @@ prep_stop(State) ->
|
||||
stop(_State) ->
|
||||
?INFO_MSG("ejabberd ~s is stopped in the node ~p", [?VERSION, node()]),
|
||||
delete_pid_file(),
|
||||
ejabberd_debug:stop(),
|
||||
%%ejabberd_debug:stop(),
|
||||
ok.
|
||||
|
||||
|
||||
|
@ -251,7 +251,16 @@ get_password_with_authmodule(User, Server) ->
|
||||
is_user_exists(User, Server) ->
|
||||
lists:any(
|
||||
fun(M) ->
|
||||
M:is_user_exists(User, Server)
|
||||
case M:is_user_exists(User, Server) of
|
||||
{error, Error} ->
|
||||
?ERROR_MSG("The authentication module ~p returned an "
|
||||
"error~nwhen checking user ~p in server ~p~n"
|
||||
"Error message: ~p",
|
||||
[M, User, Server, Error]),
|
||||
false;
|
||||
Else ->
|
||||
Else
|
||||
end
|
||||
end, auth_modules(Server)).
|
||||
|
||||
%% Check if the user exists in all authentications module except the module
|
||||
|
@ -35,6 +35,9 @@
|
||||
try_register/3,
|
||||
dirty_get_registered_users/0,
|
||||
get_vh_registered_users/1,
|
||||
get_vh_registered_users/2,
|
||||
get_vh_registered_users_number/1,
|
||||
get_vh_registered_users_number/2,
|
||||
get_password/2,
|
||||
get_password_s/2,
|
||||
is_user_exists/2,
|
||||
@ -43,45 +46,87 @@
|
||||
plain_password_required/0
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(Host) ->
|
||||
extauth:start(
|
||||
Host, ejabberd_config:get_local_option({extauth_program, Host})),
|
||||
ok.
|
||||
case check_cache_last_options(Host) of
|
||||
cache ->
|
||||
ok = ejabberd_auth_internal:start(Host);
|
||||
no_cache ->
|
||||
ok
|
||||
end.
|
||||
|
||||
check_cache_last_options(Server) ->
|
||||
%% if extauth_cache is enabled, then a mod_last module must also be enabled
|
||||
case get_cache_option(Server) of
|
||||
false -> no_cache;
|
||||
{true, _CacheTime} ->
|
||||
case get_mod_last_enabled(Server) of
|
||||
no_mod_last ->
|
||||
?ERROR_MSG("In host ~p extauth is used, extauth_cache is enabled but "
|
||||
"mod_last is not enabled.", [Server]),
|
||||
no_cache;
|
||||
_ -> cache
|
||||
end
|
||||
end.
|
||||
|
||||
plain_password_required() ->
|
||||
true.
|
||||
|
||||
check_password(User, Server, Password) ->
|
||||
extauth:check_password(User, Server, Password) andalso Password /= "".
|
||||
case get_cache_option(Server) of
|
||||
false -> check_password_extauth(User, Server, Password);
|
||||
{true, CacheTime} -> check_password_cache(User, Server, Password, CacheTime)
|
||||
end.
|
||||
|
||||
check_password(User, Server, Password, _Digest, _DigestGen) ->
|
||||
check_password(User, Server, Password).
|
||||
|
||||
set_password(User, Server, Password) ->
|
||||
case extauth:set_password(User, Server, Password) of
|
||||
true -> ok;
|
||||
true -> set_password_internal(User, Server, Password),
|
||||
ok;
|
||||
_ -> {error, unknown_problem}
|
||||
end.
|
||||
|
||||
try_register(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
try_register(User, Server, Password) ->
|
||||
case get_cache_option(Server) of
|
||||
false -> try_register_extauth(User, Server, Password);
|
||||
{true, _CacheTime} -> try_register_external_cache(User, Server, Password)
|
||||
end.
|
||||
|
||||
%% TODO
|
||||
%% Return the list of all users handled by external
|
||||
dirty_get_registered_users() ->
|
||||
[].
|
||||
ejabberd_auth_internal:dirty_get_registered_users().
|
||||
|
||||
get_vh_registered_users(_Server) ->
|
||||
[].
|
||||
get_vh_registered_users(Server) ->
|
||||
ejabberd_auth_internal:get_vh_registered_users(Server).
|
||||
|
||||
get_password(_User, _Server) ->
|
||||
false.
|
||||
get_vh_registered_users(Server, Data) ->
|
||||
ejabberd_auth_internal:get_vh_registered_users(Server, Data).
|
||||
|
||||
get_password_s(_User, _Server) ->
|
||||
"".
|
||||
get_vh_registered_users_number(Server) ->
|
||||
ejabberd_auth_internal:get_vh_registered_users_number(Server).
|
||||
|
||||
get_vh_registered_users_number(Server, Data) ->
|
||||
ejabberd_auth_internal:get_vh_registered_users_number(Server, Data).
|
||||
|
||||
%% The password can only be returned if cache is enabled, cached info exists and is fresh enough.
|
||||
get_password(User, Server) ->
|
||||
case get_cache_option(Server) of
|
||||
false -> false;
|
||||
{true, CacheTime} -> get_password_cache(User, Server, CacheTime)
|
||||
end.
|
||||
|
||||
get_password_s(User, Server) ->
|
||||
case get_password(User, Server) of
|
||||
false -> [];
|
||||
Other -> Other
|
||||
end.
|
||||
|
||||
%% @spec (User, Server) -> true | false | {error, Error}
|
||||
is_user_exists(User, Server) ->
|
||||
@ -91,9 +136,169 @@ is_user_exists(User, Server) ->
|
||||
_:Error -> {error, Error}
|
||||
end.
|
||||
|
||||
remove_user(_User, _Server) ->
|
||||
{error, not_allowed}.
|
||||
remove_user(User, Server) ->
|
||||
case extauth:remove_user(User, Server) of
|
||||
false -> false;
|
||||
true ->
|
||||
case get_cache_option(Server) of
|
||||
false -> false;
|
||||
{true, _CacheTime} ->
|
||||
ejabberd_auth_internal:remove_user(User, Server)
|
||||
end
|
||||
end.
|
||||
|
||||
remove_user(_User, _Server, _Password) ->
|
||||
not_allowed.
|
||||
remove_user(User, Server, Password) ->
|
||||
case extauth:remove_user(User, Server, Password) of
|
||||
false -> false;
|
||||
true ->
|
||||
case get_cache_option(Server) of
|
||||
false -> false;
|
||||
{true, _CacheTime} ->
|
||||
ejabberd_auth_internal:remove_user(User, Server, Password)
|
||||
end
|
||||
end.
|
||||
|
||||
%%%
|
||||
%%% Extauth cache management
|
||||
%%%
|
||||
|
||||
%% @spec (Host::string()) -> false | {true, CacheTime::integer()}
|
||||
get_cache_option(Host) ->
|
||||
case ejabberd_config:get_local_option({extauth_cache, Host}) of
|
||||
CacheTime when is_integer(CacheTime) -> {true, CacheTime};
|
||||
_ -> false
|
||||
end.
|
||||
|
||||
%% @spec (User, Server, Password) -> true | false
|
||||
check_password_extauth(User, Server, Password) ->
|
||||
extauth:check_password(User, Server, Password) andalso Password /= "".
|
||||
|
||||
%% @spec (User, Server, Password) -> true | false
|
||||
try_register_extauth(User, Server, Password) ->
|
||||
extauth:try_register(User, Server, Password).
|
||||
|
||||
check_password_cache(User, Server, Password, CacheTime) ->
|
||||
case get_last_access(User, Server) of
|
||||
online ->
|
||||
check_password_internal(User, Server, Password);
|
||||
never ->
|
||||
check_password_external_cache(User, Server, Password);
|
||||
mod_last_required ->
|
||||
?ERROR_MSG("extauth is used, extauth_cache is enabled but mod_last is not enabled in that host", []),
|
||||
check_password_external_cache(User, Server, Password);
|
||||
TimeStamp ->
|
||||
%% If last access exists, compare last access with cache refresh time
|
||||
case is_fresh_enough(TimeStamp, CacheTime) of
|
||||
%% If no need to refresh, check password against Mnesia
|
||||
true ->
|
||||
case check_password_internal(User, Server, Password) of
|
||||
%% If password valid in Mnesia, accept it
|
||||
true ->
|
||||
true;
|
||||
%% Else (password nonvalid in Mnesia), check in extauth and cache result
|
||||
false ->
|
||||
check_password_external_cache(User, Server, Password)
|
||||
end;
|
||||
%% Else (need to refresh), check in extauth and cache result
|
||||
false ->
|
||||
check_password_external_cache(User, Server, Password)
|
||||
end
|
||||
end.
|
||||
|
||||
get_password_internal(User, Server) ->
|
||||
ejabberd_auth_internal:get_password(User, Server).
|
||||
|
||||
%% @spec (User, Server, CacheTime) -> false | Password::string()
|
||||
get_password_cache(User, Server, CacheTime) ->
|
||||
case get_last_access(User, Server) of
|
||||
online ->
|
||||
get_password_internal(User, Server);
|
||||
never ->
|
||||
false;
|
||||
mod_last_required ->
|
||||
?ERROR_MSG("extauth is used, extauth_cache is enabled but mod_last is not enabled in that host", []),
|
||||
false;
|
||||
TimeStamp ->
|
||||
case is_fresh_enough(TimeStamp, CacheTime) of
|
||||
true ->
|
||||
get_password_internal(User, Server);
|
||||
false ->
|
||||
false
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
%% Check the password using extauth; if success then cache it
|
||||
check_password_external_cache(User, Server, Password) ->
|
||||
case check_password_extauth(User, Server, Password) of
|
||||
true ->
|
||||
set_password_internal(User, Server, Password), true;
|
||||
false ->
|
||||
false
|
||||
end.
|
||||
|
||||
%% Try to register using extauth; if success then cache it
|
||||
try_register_external_cache(User, Server, Password) ->
|
||||
case try_register_extauth(User, Server, Password) of
|
||||
{atomic, ok} = R ->
|
||||
set_password_internal(User, Server, Password),
|
||||
R;
|
||||
_ -> {error, not_allowed}
|
||||
end.
|
||||
|
||||
%% @spec (User, Server, Password) -> true | false
|
||||
check_password_internal(User, Server, Password) ->
|
||||
ejabberd_auth_internal:check_password(User, Server, Password).
|
||||
|
||||
%% @spec (User, Server, Password) -> ok | {error, invalid_jid}
|
||||
set_password_internal(User, Server, Password) ->
|
||||
ejabberd_auth_internal:set_password(User, Server, Password).
|
||||
|
||||
%% @spec (TimeLast, CacheTime) -> true | false
|
||||
%% TimeLast = online | never | integer()
|
||||
%% CacheTime = integer() | false
|
||||
is_fresh_enough(online, _CacheTime) ->
|
||||
true;
|
||||
is_fresh_enough(never, _CacheTime) ->
|
||||
false;
|
||||
is_fresh_enough(TimeStampLast, CacheTime) ->
|
||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||
Now = MegaSecs * 1000000 + Secs,
|
||||
(TimeStampLast + CacheTime > Now).
|
||||
|
||||
%% @spec (User, Server) -> online | never | mod_last_required | TimeStamp::integer()
|
||||
%% Code copied from mod_configure.erl
|
||||
%% Code copied from web/ejabberd_web_admin.erl
|
||||
%% TODO: Update time format to XEP-0202: Entity Time
|
||||
get_last_access(User, Server) ->
|
||||
case ejabberd_sm:get_user_resources(User, Server) of
|
||||
[] ->
|
||||
_US = {User, Server},
|
||||
case get_last_info(User, Server) of
|
||||
mod_last_required ->
|
||||
mod_last_required;
|
||||
not_found ->
|
||||
never;
|
||||
{ok, Timestamp, _Status} ->
|
||||
Timestamp
|
||||
end;
|
||||
_ ->
|
||||
online
|
||||
end.
|
||||
%% @spec (User, Server) -> {ok, Timestamp, Status} | not_found | mod_last_required
|
||||
get_last_info(User, Server) ->
|
||||
case get_mod_last_enabled(Server) of
|
||||
mod_last -> mod_last:get_last_info(User, Server);
|
||||
mod_last_odbc -> mod_last_odbc:get_last_info(User, Server);
|
||||
no_mod_last -> mod_last_required
|
||||
end.
|
||||
|
||||
%% @spec (Server) -> mod_last | mod_last_odbc | no_mod_last
|
||||
get_mod_last_enabled(Server) ->
|
||||
ML = lists:member(mod_last, gen_mod:loaded_modules(Server)),
|
||||
MLO = lists:member(mod_last_odbc, gen_mod:loaded_modules(Server)),
|
||||
case {ML, MLO} of
|
||||
{true, _} -> mod_last;
|
||||
{false, true} -> mod_last_odbc;
|
||||
{false, false} -> no_mod_last
|
||||
end.
|
||||
|
@ -66,7 +66,7 @@
|
||||
servers,
|
||||
backups,
|
||||
port,
|
||||
encrypt,
|
||||
tls_options,
|
||||
dn,
|
||||
password,
|
||||
base,
|
||||
@ -124,14 +124,14 @@ init(Host) ->
|
||||
State#state.port,
|
||||
State#state.dn,
|
||||
State#state.password,
|
||||
State#state.encrypt),
|
||||
State#state.tls_options),
|
||||
eldap_pool:start_link(State#state.bind_eldap_id,
|
||||
State#state.servers,
|
||||
State#state.backups,
|
||||
State#state.port,
|
||||
State#state.dn,
|
||||
State#state.password,
|
||||
State#state.encrypt),
|
||||
State#state.tls_options),
|
||||
{ok, State}.
|
||||
|
||||
plain_password_required() ->
|
||||
@ -153,8 +153,14 @@ check_password(User, Server, Password) ->
|
||||
check_password(User, Server, Password, _Digest, _DigestGen) ->
|
||||
check_password(User, Server, Password).
|
||||
|
||||
set_password(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
set_password(User, Server, Password) ->
|
||||
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
|
||||
case find_user_dn(User, State) of
|
||||
false ->
|
||||
{error, user_not_found};
|
||||
DN ->
|
||||
eldap_pool:modify_passwd(State#state.eldap_id, DN, Password)
|
||||
end.
|
||||
|
||||
%% @spec (User, Server, Password) -> {error, not_allowed}
|
||||
try_register(_User, _Server, _Password) ->
|
||||
@ -367,6 +373,7 @@ parse_options(Host) ->
|
||||
Backups -> Backups
|
||||
end,
|
||||
LDAPEncrypt = ejabberd_config:get_local_option({ldap_encrypt, Host}),
|
||||
LDAPTLSVerify = ejabberd_config:get_local_option({ldap_tls_verify, Host}),
|
||||
LDAPPort = case ejabberd_config:get_local_option({ldap_port, Host}) of
|
||||
undefined -> case LDAPEncrypt of
|
||||
tls -> ?LDAPS_PORT;
|
||||
@ -411,7 +418,8 @@ parse_options(Host) ->
|
||||
servers = LDAPServers,
|
||||
backups = LDAPBackups,
|
||||
port = LDAPPort,
|
||||
encrypt = LDAPEncrypt,
|
||||
tls_options = [{encrypt, LDAPEncrypt},
|
||||
{tls_verify, LDAPTLSVerify}],
|
||||
dn = RootDN,
|
||||
password = Password,
|
||||
base = LDAPBase,
|
||||
|
@ -60,7 +60,11 @@ check_password(User, Server, Password, _Digest, _DigestGen) ->
|
||||
|
||||
check_password(User, Host, Password) ->
|
||||
Service = get_pam_service(Host),
|
||||
case catch epam:authenticate(Service, User, Password) of
|
||||
UserInfo = case get_pam_userinfotype(Host) of
|
||||
username -> User;
|
||||
jid -> User++"@"++Host
|
||||
end,
|
||||
case catch epam:authenticate(Service, UserInfo, Password) of
|
||||
true -> true;
|
||||
_ -> false
|
||||
end.
|
||||
@ -84,7 +88,11 @@ get_password_s(_User, _Server) ->
|
||||
%% TODO: Improve this function to return an error instead of 'false' when connection to PAM failed
|
||||
is_user_exists(User, Host) ->
|
||||
Service = get_pam_service(Host),
|
||||
case catch epam:acct_mgmt(Service, User) of
|
||||
UserInfo = case get_pam_userinfotype(Host) of
|
||||
username -> User;
|
||||
jid -> User++"@"++Host
|
||||
end,
|
||||
case catch epam:acct_mgmt(Service, UserInfo) of
|
||||
true -> true;
|
||||
_ -> false
|
||||
end.
|
||||
@ -106,3 +114,8 @@ get_pam_service(Host) ->
|
||||
undefined -> "ejabberd";
|
||||
Service -> Service
|
||||
end.
|
||||
get_pam_userinfotype(Host) ->
|
||||
case ejabberd_config:get_local_option({pam_userinfotype, Host}) of
|
||||
undefined -> username;
|
||||
Type -> Type
|
||||
end.
|
||||
|
@ -55,7 +55,9 @@
|
||||
handle_sync_event/4,
|
||||
code_change/4,
|
||||
handle_info/3,
|
||||
terminate/3]).
|
||||
terminate/3,
|
||||
print_state/1
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
@ -362,7 +364,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
ejabberd_hooks:run_fold(
|
||||
c2s_stream_features,
|
||||
Server,
|
||||
[], [])}),
|
||||
[], [Server])}),
|
||||
fsm_next_state(wait_for_feature_request,
|
||||
StateData#state{
|
||||
server = Server,
|
||||
@ -371,13 +373,22 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
_ ->
|
||||
case StateData#state.resource of
|
||||
"" ->
|
||||
RosterVersioningFeature = ejabberd_hooks:run_fold(roster_get_versioning_feature, Server, [], [Server]),
|
||||
StreamFeatures = [{xmlelement, "push",
|
||||
RosterVersioningFeature =
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_get_versioning_feature,
|
||||
Server, [], [Server]),
|
||||
StreamFeatures =
|
||||
[{xmlelement, "push",
|
||||
[{"xmlns", ?NS_P1_PUSH}], []},
|
||||
{xmlelement, "bind",
|
||||
[{"xmlns", ?NS_BIND}], []},
|
||||
{xmlelement, "session",
|
||||
[{"xmlns", ?NS_SESSION}], []} | RosterVersioningFeature],
|
||||
[{"xmlns", ?NS_SESSION}], []}]
|
||||
++ RosterVersioningFeature
|
||||
++ ejabberd_hooks:run_fold(
|
||||
c2s_stream_features,
|
||||
Server,
|
||||
[], [Server]),
|
||||
send_element(
|
||||
StateData,
|
||||
{xmlelement, "stream:features", [],
|
||||
@ -687,7 +698,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
Socket = StateData#state.socket,
|
||||
TLSSocket = (StateData#state.sockmod):starttls(
|
||||
Socket, TLSOpts,
|
||||
xml:element_to_string(
|
||||
xml:element_to_binary(
|
||||
{xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []})),
|
||||
fsm_next_state(wait_for_stream,
|
||||
StateData#state{socket = TLSSocket,
|
||||
@ -710,7 +721,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
Socket = StateData#state.socket,
|
||||
ZlibSocket = (StateData#state.sockmod):compress(
|
||||
Socket,
|
||||
xml:element_to_string(
|
||||
xml:element_to_binary(
|
||||
{xmlelement, "compressed",
|
||||
[{"xmlns", ?NS_COMPRESS}], []})),
|
||||
fsm_next_state(wait_for_stream,
|
||||
@ -1514,10 +1525,42 @@ handle_info(system_shutdown, StateName, StateData) ->
|
||||
ok
|
||||
end,
|
||||
{stop, normal, StateData};
|
||||
handle_info({force_update_presence, LUser}, StateName,
|
||||
#state{user = LUser, server = LServer} = StateData) ->
|
||||
NewStateData =
|
||||
case StateData#state.pres_last of
|
||||
{xmlelement, "presence", _Attrs, _Els} ->
|
||||
PresenceEl = ejabberd_hooks:run_fold(
|
||||
c2s_update_presence,
|
||||
LServer,
|
||||
StateData#state.pres_last,
|
||||
[LUser, LServer]),
|
||||
StateData2 = StateData#state{pres_last = PresenceEl},
|
||||
presence_update(StateData2#state.jid,
|
||||
PresenceEl,
|
||||
StateData2),
|
||||
StateData2;
|
||||
_ ->
|
||||
StateData
|
||||
end,
|
||||
{next_state, StateName, NewStateData};
|
||||
handle_info(Info, StateName, StateData) ->
|
||||
?ERROR_MSG("Unexpected info: ~p", [Info]),
|
||||
fsm_next_state(StateName, StateData).
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: print_state/1
|
||||
%% Purpose: Prepare the state to be printed on error log
|
||||
%% Returns: State to print
|
||||
%%----------------------------------------------------------------------
|
||||
print_state(State = #state{pres_t = T, pres_f = F, pres_a = A, pres_i = I}) ->
|
||||
State#state{pres_t = {pres_t, ?SETS:size(T)},
|
||||
pres_f = {pres_f, ?SETS:size(F)},
|
||||
pres_a = {pres_a, ?SETS:size(A)},
|
||||
pres_i = {pres_i, ?SETS:size(I)}
|
||||
}.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: terminate/3
|
||||
%% Purpose: Shutdown the fsm
|
||||
@ -1631,14 +1674,14 @@ change_shaper(StateData, JID) ->
|
||||
(StateData#state.sockmod):change_shaper(StateData#state.socket, Shaper).
|
||||
|
||||
send_text(StateData, Text) ->
|
||||
?DEBUG("Send XML on stream = ~p", [lists:flatten(Text)]),
|
||||
?DEBUG("Send XML on stream = ~p", [Text]),
|
||||
(StateData#state.sockmod):send(StateData#state.socket, Text).
|
||||
|
||||
send_element(StateData, El) when StateData#state.xml_socket ->
|
||||
(StateData#state.sockmod):send_xml(StateData#state.socket,
|
||||
{xmlstreamelement, El});
|
||||
send_element(StateData, El) ->
|
||||
send_text(StateData, xml:element_to_string(El)).
|
||||
send_text(StateData, xml:element_to_binary(El)).
|
||||
|
||||
send_header(StateData, Server, Version, Lang)
|
||||
when StateData#state.xml_socket ->
|
||||
|
@ -328,7 +328,7 @@ get_prog_name() ->
|
||||
FileName when is_list(FileName) ->
|
||||
FileName;
|
||||
_ ->
|
||||
?CRITICAL_MSG("The option captcha_cmd is not configured, but some "
|
||||
?DEBUG("The option captcha_cmd is not configured, but some "
|
||||
"module wants to use the CAPTCHA feature.", []),
|
||||
throw({error, option_not_configured_captcha_cmd})
|
||||
end.
|
||||
|
@ -201,8 +201,7 @@ process(Args) ->
|
||||
case String of
|
||||
[] -> ok;
|
||||
_ ->
|
||||
io:format(String),
|
||||
io:format("\n")
|
||||
io:format("~s~n", [String])
|
||||
end,
|
||||
Code.
|
||||
|
||||
|
@ -1,97 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_debug.erl
|
||||
%%% Author : Mickael Remond
|
||||
%%% Purpose : ejabberd's application callback module
|
||||
%%% Created : 6 may 2009 by Mickael Remond <mremond@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License
|
||||
%%% along with this program; if not, write to the Free Software
|
||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_debug).
|
||||
|
||||
-export([eprof_start/0, fprof_start/0, stop/0]).
|
||||
-export([pids/0]).
|
||||
|
||||
eprof_start() ->
|
||||
eprof:start(),
|
||||
eprof:profile(pids()).
|
||||
|
||||
fprof_start() ->
|
||||
fprof:trace([start, {file, "/tmp/fprof"}, {procs, pids()}]).
|
||||
|
||||
%% Stop all profilers
|
||||
stop() ->
|
||||
catch eprof:stop(),
|
||||
catch fprof:stop(),
|
||||
ok.
|
||||
|
||||
pids() ->
|
||||
lists:zf(
|
||||
fun(Pid) ->
|
||||
case process_info(Pid) of
|
||||
ProcessInfo when is_list(ProcessInfo) ->
|
||||
CurrentFunction = current_function(ProcessInfo),
|
||||
InitialCall = initial_call(ProcessInfo),
|
||||
RegisteredName = registered_name(ProcessInfo),
|
||||
Ancestor = ancestor(ProcessInfo),
|
||||
filter_pid(Pid, CurrentFunction, InitialCall, RegisteredName, Ancestor);
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end,
|
||||
processes()).
|
||||
|
||||
current_function(ProcessInfo) ->
|
||||
{value, {_, {CurrentFunction, _,_}}} =
|
||||
lists:keysearch(current_function, 1, ProcessInfo),
|
||||
atom_to_list(CurrentFunction).
|
||||
|
||||
initial_call(ProcessInfo) ->
|
||||
{value, {_, {InitialCall, _,_}}} =
|
||||
lists:keysearch(initial_call, 1, ProcessInfo),
|
||||
atom_to_list(InitialCall).
|
||||
|
||||
registered_name(ProcessInfo) ->
|
||||
case lists:keysearch(registered_name, 1, ProcessInfo) of
|
||||
{value, {_, Name}} when is_atom(Name) -> atom_to_list(Name);
|
||||
_ -> ""
|
||||
end.
|
||||
|
||||
ancestor(ProcessInfo) ->
|
||||
{value, {_, Dictionary}} = lists:keysearch(dictionary, 1, ProcessInfo),
|
||||
case lists:keysearch('$ancestors', 1, Dictionary) of
|
||||
{value, {_, [Ancestor|_T]}} when is_atom(Ancestor) ->
|
||||
atom_to_list(Ancestor);
|
||||
_ ->
|
||||
""
|
||||
end.
|
||||
|
||||
filter_pid(Pid, "ejabberd" ++ _, _InitialCall, _RegisteredName, _Ancestor) ->
|
||||
{true, Pid};
|
||||
filter_pid(Pid, _CurrentFunction, "ejabberd" ++ _, _RegisteredName, _Ancestor) ->
|
||||
{true, Pid};
|
||||
filter_pid(Pid, _CurrentFunction, _InitialCall, "ejabberd"++_, _Ancestor) ->
|
||||
{true, Pid};
|
||||
filter_pid(Pid, _CurrentFunction, _InitialCall, "stringprep"++_, _Ancestor) ->
|
||||
{true, Pid};
|
||||
filter_pid(Pid, _CurrentFunction, _InitialCall, _RegisteredName, "ejabberd"++_) ->
|
||||
{true, Pid};
|
||||
filter_pid(_Pid, _CurrentFunction, _InitialCall, _RegisteredName, _Ancestor) ->
|
||||
false.
|
@ -31,6 +31,7 @@
|
||||
|
||||
%% API
|
||||
-export([route/3,
|
||||
route_error/4,
|
||||
register_route/1,
|
||||
register_route/2,
|
||||
register_routes/1,
|
||||
@ -72,6 +73,17 @@ route(From, To, Packet) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%% Route the error packet only if the originating packet is not an error itself.
|
||||
%% RFC3920 9.3.1
|
||||
route_error(From, To, ErrPacket, OrigPacket) ->
|
||||
{xmlelement, _Name, Attrs, _Els} = OrigPacket,
|
||||
case "error" == xml:get_attr_s("type", Attrs) of
|
||||
false ->
|
||||
route(From, To, ErrPacket);
|
||||
true ->
|
||||
ok
|
||||
end.
|
||||
|
||||
register_route(Domain) ->
|
||||
register_route(Domain, undefined).
|
||||
|
||||
|
@ -48,6 +48,11 @@
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
-ifdef(SSL40).
|
||||
-include_lib("public_key/include/public_key.hrl").
|
||||
-define(PKIXEXPLICIT, 'OTP-PUB-KEY').
|
||||
-define(PKIXIMPLICIT, 'OTP-PUB-KEY').
|
||||
-else.
|
||||
-ifdef(SSL39).
|
||||
-include_lib("ssl/include/ssl_pkix.hrl").
|
||||
-define(PKIXEXPLICIT, 'OTP-PKIX').
|
||||
@ -58,6 +63,7 @@
|
||||
-define(PKIXEXPLICIT, 'PKIX1Explicit88').
|
||||
-define(PKIXIMPLICIT, 'PKIX1Implicit88').
|
||||
-endif.
|
||||
-endif.
|
||||
-include("XmppAddr.hrl").
|
||||
|
||||
-define(DICT, dict).
|
||||
@ -177,8 +183,9 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
case {xml:get_attr_s("xmlns", Attrs),
|
||||
xml:get_attr_s("xmlns:db", Attrs),
|
||||
xml:get_attr_s("to", Attrs),
|
||||
xml:get_attr_s("version", Attrs) == "1.0"} of
|
||||
{"jabber:server", _, true} when
|
||||
{"jabber:server", _, Server, true} when
|
||||
StateData#state.tls and (not StateData#state.authenticated) ->
|
||||
send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
|
||||
SASL =
|
||||
@ -212,15 +219,23 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||
end,
|
||||
send_element(StateData,
|
||||
{xmlelement, "stream:features", [],
|
||||
SASL ++ StartTLS}),
|
||||
SASL ++ StartTLS ++
|
||||
ejabberd_hooks:run_fold(
|
||||
s2s_stream_features,
|
||||
Server,
|
||||
[], [Server])}),
|
||||
{next_state, wait_for_feature_request, StateData};
|
||||
{"jabber:server", _, true} when
|
||||
{"jabber:server", _, Server, true} when
|
||||
StateData#state.authenticated ->
|
||||
send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
|
||||
send_element(StateData,
|
||||
{xmlelement, "stream:features", [], []}),
|
||||
{xmlelement, "stream:features", [],
|
||||
ejabberd_hooks:run_fold(
|
||||
s2s_stream_features,
|
||||
Server,
|
||||
[], [Server])}),
|
||||
{next_state, stream_established, StateData};
|
||||
{"jabber:server", "jabber:server:dialback", _} ->
|
||||
{"jabber:server", "jabber:server:dialback", _Server, _} ->
|
||||
send_text(StateData, ?STREAM_HEADER("")),
|
||||
{next_state, stream_established, StateData};
|
||||
_ ->
|
||||
@ -254,7 +269,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
TLSOpts = StateData#state.tls_options,
|
||||
TLSSocket = (StateData#state.sockmod):starttls(
|
||||
Socket, TLSOpts,
|
||||
xml:element_to_string(
|
||||
xml:element_to_binary(
|
||||
{xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []})),
|
||||
{next_state, wait_for_stream,
|
||||
StateData#state{socket = TLSSocket,
|
||||
@ -609,7 +624,7 @@ send_text(StateData, Text) ->
|
||||
(StateData#state.sockmod):send(StateData#state.socket, Text).
|
||||
|
||||
send_element(StateData, El) ->
|
||||
send_text(StateData, xml:element_to_string(El)).
|
||||
send_text(StateData, xml:element_to_binary(El)).
|
||||
|
||||
|
||||
change_shaper(StateData, Host, JID) ->
|
||||
|
@ -51,6 +51,7 @@
|
||||
handle_sync_event/4,
|
||||
handle_info/3,
|
||||
terminate/3,
|
||||
print_state/1,
|
||||
code_change/4,
|
||||
test_get_addr_port/1,
|
||||
get_addr_port/1]).
|
||||
@ -191,7 +192,8 @@ init([From, Server, Type]) ->
|
||||
open_socket(init, StateData) ->
|
||||
log_s2s_out(StateData#state.new,
|
||||
StateData#state.myname,
|
||||
StateData#state.server),
|
||||
StateData#state.server,
|
||||
StateData#state.tls),
|
||||
?DEBUG("open_socket: ~p", [{StateData#state.myname,
|
||||
StateData#state.server,
|
||||
StateData#state.new,
|
||||
@ -339,8 +341,8 @@ wait_for_validation({xmlstreamelement, El}, StateData) ->
|
||||
case Type of
|
||||
"valid" ->
|
||||
send_queue(StateData, StateData#state.queue),
|
||||
?INFO_MSG("Connection established: ~s -> ~s",
|
||||
[StateData#state.myname, StateData#state.server]),
|
||||
?INFO_MSG("Connection established: ~s -> ~s with TLS=~p",
|
||||
[StateData#state.myname, StateData#state.server, StateData#state.tls_enabled]),
|
||||
ejabberd_hooks:run(s2s_connect_hook,
|
||||
[StateData#state.myname,
|
||||
StateData#state.server]),
|
||||
@ -875,6 +877,14 @@ terminate(Reason, StateName, StateData) ->
|
||||
end,
|
||||
ok.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: print_state/1
|
||||
%% Purpose: Prepare the state to be printed on error log
|
||||
%% Returns: State to print
|
||||
%%----------------------------------------------------------------------
|
||||
print_state(State) ->
|
||||
State.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
@ -883,7 +893,7 @@ send_text(StateData, Text) ->
|
||||
ejabberd_socket:send(StateData#state.socket, Text).
|
||||
|
||||
send_element(StateData, El) ->
|
||||
send_text(StateData, xml:element_to_string(El)).
|
||||
send_text(StateData, xml:element_to_binary(El)).
|
||||
|
||||
send_queue(StateData, Q) ->
|
||||
case queue:out(Q) of
|
||||
@ -1142,10 +1152,10 @@ outgoing_s2s_timeout() ->
|
||||
|
||||
%% Human readable S2S logging: Log only new outgoing connections as INFO
|
||||
%% Do not log dialback
|
||||
log_s2s_out(false, _, _) -> ok;
|
||||
log_s2s_out(false, _, _, _) -> ok;
|
||||
%% Log new outgoing connections:
|
||||
log_s2s_out(_, Myname, Server) ->
|
||||
?INFO_MSG("Trying to open s2s connection: ~s -> ~s",[Myname, Server]).
|
||||
log_s2s_out(_, Myname, Server, Tls) ->
|
||||
?INFO_MSG("Trying to open s2s connection: ~s -> ~s with TLS=~p", [Myname, Server, Tls]).
|
||||
|
||||
%% Calculate timeout depending on which state we are in:
|
||||
%% Can return integer > 0 | infinity
|
||||
|
@ -47,7 +47,8 @@
|
||||
handle_sync_event/4,
|
||||
code_change/4,
|
||||
handle_info/3,
|
||||
terminate/3]).
|
||||
terminate/3,
|
||||
print_state/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
@ -346,11 +347,11 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
Attrs2 = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
|
||||
jlib:jid_to_string(To),
|
||||
Attrs),
|
||||
Text = xml:element_to_string({xmlelement, Name, Attrs2, Els}),
|
||||
Text = xml:element_to_binary({xmlelement, Name, Attrs2, Els}),
|
||||
send_text(StateData, Text);
|
||||
deny ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
ejabberd_router:route_error(To, From, Err, Packet)
|
||||
end,
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
@ -374,6 +375,14 @@ terminate(Reason, StateName, StateData) ->
|
||||
(StateData#state.sockmod):close(StateData#state.socket),
|
||||
ok.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: print_state/1
|
||||
%% Purpose: Prepare the state to be printed on error log
|
||||
%% Returns: State to print
|
||||
%%----------------------------------------------------------------------
|
||||
print_state(State) ->
|
||||
State.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
@ -382,7 +391,7 @@ send_text(StateData, Text) ->
|
||||
(StateData#state.sockmod):send(StateData#state.socket, Text).
|
||||
|
||||
send_element(StateData, El) ->
|
||||
send_text(StateData, xml:element_to_string(El)).
|
||||
send_text(StateData, xml:element_to_binary(El)).
|
||||
|
||||
new_id() ->
|
||||
randoms:get_string().
|
||||
|
@ -47,6 +47,7 @@
|
||||
register_iq_handler/4,
|
||||
register_iq_handler/5,
|
||||
unregister_iq_handler/2,
|
||||
force_update_presence/1,
|
||||
connected_users/0,
|
||||
connected_users_number/0,
|
||||
user_resources/2,
|
||||
@ -571,10 +572,15 @@ route_message(From, To, Packet) ->
|
||||
bounce_offline_message(From, To, Packet);
|
||||
_ ->
|
||||
case ejabberd_auth:is_user_exists(LUser, LServer) of
|
||||
true ->
|
||||
case is_privacy_allow(From, To, Packet) of
|
||||
true ->
|
||||
ejabberd_hooks:run(offline_message_hook,
|
||||
LServer,
|
||||
[From, To, Packet]);
|
||||
false ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERR_SERVICE_UNAVAILABLE),
|
||||
@ -711,6 +717,16 @@ process_iq(From, To, Packet) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
force_update_presence({LUser, _LServer} = US) ->
|
||||
case catch mnesia:dirty_index_read(session, US, #session.us) of
|
||||
{'EXIT', _Reason} ->
|
||||
ok;
|
||||
Ss ->
|
||||
lists:foreach(fun(#session{sid = {_, Pid}}) ->
|
||||
Pid ! {force_update_presence, LUser}
|
||||
end, Ss)
|
||||
end.
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%% ejabberd commands
|
||||
|
@ -84,7 +84,7 @@ update_info() ->
|
||||
update_info(Dir, Files) ->
|
||||
Beams = lists:sort(get_beams(Files)),
|
||||
UpdatedBeams = get_updated_beams(Beams),
|
||||
?INFO_MSG("beam files: ~p~n", [UpdatedBeams]),
|
||||
?DEBUG("beam files: ~p~n", [UpdatedBeams]),
|
||||
{Script, LowLevelScript, Check} = build_script(Dir, UpdatedBeams),
|
||||
{ok, Dir, UpdatedBeams, Script, LowLevelScript, Check}.
|
||||
|
||||
@ -124,14 +124,21 @@ get_current_version(Module) ->
|
||||
%% @spec(Dir::string(), UpdatedBeams::[atom()]) -> {Script,LowLevelScript,Check}
|
||||
build_script(Dir, UpdatedBeams) ->
|
||||
Script = make_script(UpdatedBeams),
|
||||
?INFO_MSG("script: ~p~n", [Script]),
|
||||
LowLevelScript = make_low_level_script(UpdatedBeams, Script),
|
||||
?INFO_MSG("low level script: ~p~n", [LowLevelScript]),
|
||||
Check =
|
||||
release_handler_1:check_script(
|
||||
LowLevelScript,
|
||||
[{ejabberd, "", filename:join(Dir, "..")}]),
|
||||
?INFO_MSG("check: ~p~n", [Check]),
|
||||
case Check of
|
||||
ok ->
|
||||
?DEBUG("script: ~p~n", [Script]),
|
||||
?DEBUG("low level script: ~p~n", [LowLevelScript]),
|
||||
?DEBUG("check: ~p~n", [Check]);
|
||||
_ ->
|
||||
?ERROR_MSG("script: ~p~n", [Script]),
|
||||
?ERROR_MSG("low level script: ~p~n", [LowLevelScript]),
|
||||
?ERROR_MSG("check: ~p~n", [Check])
|
||||
end,
|
||||
{Script, LowLevelScript, Check}.
|
||||
|
||||
%% Copied from Erlang/OTP file: lib/sasl/src/systools.hrl
|
||||
|
@ -32,6 +32,15 @@ typedef struct {
|
||||
z_stream *i_stream;
|
||||
} ejabberd_zlib_data;
|
||||
|
||||
static void* zlib_alloc(void* data, unsigned int items, unsigned int size)
|
||||
{
|
||||
return (void*) driver_alloc(items*size);
|
||||
}
|
||||
|
||||
static void zlib_free(void* data, void* addr)
|
||||
{
|
||||
driver_free(addr);
|
||||
}
|
||||
|
||||
static ErlDrvData ejabberd_zlib_drv_start(ErlDrvPort port, char *buff)
|
||||
{
|
||||
@ -39,18 +48,18 @@ static ErlDrvData ejabberd_zlib_drv_start(ErlDrvPort port, char *buff)
|
||||
(ejabberd_zlib_data *)driver_alloc(sizeof(ejabberd_zlib_data));
|
||||
d->port = port;
|
||||
|
||||
d->d_stream = (z_stream *)malloc(sizeof(z_stream));
|
||||
d->d_stream = (z_stream *)driver_alloc(sizeof(z_stream));
|
||||
|
||||
d->d_stream->zalloc = (alloc_func)0;
|
||||
d->d_stream->zfree = (free_func)0;
|
||||
d->d_stream->zalloc = zlib_alloc;
|
||||
d->d_stream->zfree = zlib_free;
|
||||
d->d_stream->opaque = (voidpf)0;
|
||||
|
||||
deflateInit(d->d_stream, Z_DEFAULT_COMPRESSION);
|
||||
|
||||
d->i_stream = (z_stream *)malloc(sizeof(z_stream));
|
||||
d->i_stream = (z_stream *)driver_alloc(sizeof(z_stream));
|
||||
|
||||
d->i_stream->zalloc = (alloc_func)0;
|
||||
d->i_stream->zfree = (free_func)0;
|
||||
d->i_stream->zalloc = zlib_alloc;
|
||||
d->i_stream->zfree = zlib_free;
|
||||
d->i_stream->opaque = (voidpf)0;
|
||||
|
||||
inflateInit(d->i_stream);
|
||||
@ -65,10 +74,10 @@ static void ejabberd_zlib_drv_stop(ErlDrvData handle)
|
||||
ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle;
|
||||
|
||||
deflateEnd(d->d_stream);
|
||||
free(d->d_stream);
|
||||
driver_free(d->d_stream);
|
||||
|
||||
inflateEnd(d->i_stream);
|
||||
free(d->i_stream);
|
||||
driver_free(d->i_stream);
|
||||
|
||||
driver_free((char *)handle);
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ else
|
||||
KERNEL_OPTS="-kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}"
|
||||
fi
|
||||
|
||||
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS $KERNEL_OPTS"
|
||||
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS"
|
||||
|
||||
# define additional environment variables
|
||||
if [ "$EJABBERDDIR" = "" ]; then
|
||||
@ -141,6 +141,7 @@ start ()
|
||||
-noinput -detached \
|
||||
-pa $EJABBERD_EBIN_PATH \
|
||||
-mnesia dir \"\\\"$SPOOLDIR\\\"\" \
|
||||
$KERNEL_OPTS \
|
||||
-s ejabberd \
|
||||
-sasl sasl_error_logger \\{file,\\\"$SASL_LOG_PATH\\\"\\} \
|
||||
$ERLANG_OPTS $ARGS \"$@\""
|
||||
@ -174,6 +175,7 @@ debug ()
|
||||
$NAME debug-${TTY}-${ERLANG_NODE} \
|
||||
-remsh $ERLANG_NODE \
|
||||
-hidden \
|
||||
$KERNEL_OPTS \
|
||||
$ERLANG_OPTS $ARGS \"$@\""
|
||||
}
|
||||
|
||||
@ -203,6 +205,7 @@ live ()
|
||||
$NAME $ERLANG_NODE \
|
||||
-pa $EJABBERD_EBIN_PATH \
|
||||
-mnesia dir \"\\\"$SPOOLDIR\\\"\" \
|
||||
$KERNEL_OPTS \
|
||||
-s ejabberd \
|
||||
$ERLANG_OPTS $ARGS \"$@\""
|
||||
}
|
||||
@ -236,7 +239,7 @@ ctl ()
|
||||
MAXCONNID=100
|
||||
CONNLOCKDIR=@LOCALSTATEDIR@/lock/ejabberdctl
|
||||
FLOCK='/usr/bin/flock'
|
||||
if [ ! -x "$FLOCK" ] ; then
|
||||
if [ ! -x "$FLOCK" ] || [ ! -d "$CONNLOCKDIR" ] ; then
|
||||
JOT='/usr/bin/jot'
|
||||
if [ ! -x "$JOT" ] ; then
|
||||
# no flock or jot, simply invoke ctlexec()
|
||||
@ -310,6 +313,7 @@ ctlexec ()
|
||||
-noinput \
|
||||
-hidden \
|
||||
-pa $EJABBERD_EBIN_PATH \
|
||||
$KERNEL_OPTS \
|
||||
-s ejabberd_ctl -extra $ERLANG_NODE $COMMAND"
|
||||
}
|
||||
|
||||
|
@ -141,8 +141,7 @@ export_offline(Server, Output) ->
|
||||
TimeStamp))]},
|
||||
XML =
|
||||
ejabberd_odbc:escape(
|
||||
lists:flatten(
|
||||
xml:element_to_string(NewPacket))),
|
||||
xml:element_to_binary(NewPacket)),
|
||||
["insert into spool(username, xml) "
|
||||
"values ('", Username, "', '",
|
||||
XML,
|
||||
@ -176,7 +175,7 @@ export_vcard(Server, Output) ->
|
||||
when LServer == Host ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
SVCARD = ejabberd_odbc:escape(
|
||||
lists:flatten(xml:element_to_string(VCARD))),
|
||||
xml:element_to_binary(VCARD)),
|
||||
["delete from vcard where username='", Username, "';"
|
||||
"insert into vcard(username, vcard) "
|
||||
"values ('", Username, "', '", SVCARD, "');"];
|
||||
@ -260,7 +259,7 @@ export_private_storage(Server, Output) ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
LXMLNS = ejabberd_odbc:escape(XMLNS),
|
||||
SData = ejabberd_odbc:escape(
|
||||
lists:flatten(xml:element_to_string(Data))),
|
||||
xml:element_to_binary(Data)),
|
||||
odbc_queries:set_private_data_sql(Username, LXMLNS, SData);
|
||||
(_Host, _R) ->
|
||||
[]
|
||||
|
@ -286,6 +286,16 @@ ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
|
||||
responseName [10] LDAPOID OPTIONAL,
|
||||
response [11] OCTET STRING OPTIONAL }
|
||||
|
||||
passwdModifyOID LDAPOID ::= "1.3.6.1.4.1.4203.1.11.1"
|
||||
|
||||
PasswdModifyRequestValue ::= SEQUENCE {
|
||||
userIdentity [0] OCTET STRING OPTIONAL,
|
||||
oldPasswd [1] OCTET STRING OPTIONAL,
|
||||
newPasswd [2] OCTET STRING OPTIONAL }
|
||||
|
||||
PasswdModifyResponseValue ::= SEQUENCE {
|
||||
genPasswd [0] OCTET STRING OPTIONAL }
|
||||
|
||||
END
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $Id$
|
||||
# $Id: Makefile.in 2842 2009-12-29 19:10:52Z badlop $
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
@ -20,18 +20,23 @@ ifdef debug
|
||||
endif
|
||||
|
||||
OUTDIR = ..
|
||||
SOURCES = $(wildcard *.erl) ELDAPv3.erl
|
||||
SOURCES = $(wildcard *.erl) ELDAPv3.erl eldap_filter_yecc.erl
|
||||
BEAMS = $(addprefix $(OUTDIR)/,$(SOURCES:.erl=.beam))
|
||||
|
||||
|
||||
all: $(BEAMS) ELDAPv3.beam
|
||||
all: $(BEAMS) ELDAPv3.beam eldap_filter_yecc.beam
|
||||
|
||||
ELDAPv3.beam: ELDAPv3.erl
|
||||
|
||||
ELDAPv3.erl: ELDAPv3.asn
|
||||
@ERLC@ $(ASN_FLAGS) -W $(EFLAGS) $<
|
||||
|
||||
$(OUTDIR)/%.beam: %.erl ELDAPv3.erl
|
||||
eldap_filter_yecc.beam: eldap_filter_yecc.erl
|
||||
|
||||
eldap_filter_yecc.erl: eldap_filter_yecc.yrl
|
||||
@ERLC@ -W $<
|
||||
|
||||
$(OUTDIR)/%.beam: %.erl ELDAPv3.erl eldap_filter_yecc.erl
|
||||
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
|
||||
|
||||
clean:
|
||||
@ -39,6 +44,8 @@ clean:
|
||||
rm -f ELDAPv3.erl
|
||||
rm -f ELDAPv3.hrl
|
||||
rm -f ELDAPv3.beam
|
||||
rm -f eldap_filter_yecc.erl
|
||||
rm -f eldap_filter_yecc.beam
|
||||
rm -f $(BEAMS)
|
||||
|
||||
distclean: clean
|
||||
|
@ -4,7 +4,7 @@ include ..\Makefile.inc
|
||||
EFLAGS = -I .. -pz ..
|
||||
|
||||
OUTDIR = ..
|
||||
BEAMS = ..\eldap.beam ..\eldap_filter.beam ..\eldap_pool.beam ..\eldap_utils.beam
|
||||
BEAMS = ..\eldap.beam ..\eldap_filter.beam ..\eldap_pool.beam ..\eldap_utils.beam ..\eldap_filter_yecc.beam
|
||||
|
||||
ASN_FLAGS = -bber_bin +optimize +driver
|
||||
|
||||
@ -15,11 +15,16 @@ Clean :
|
||||
-@erase ELDAPv3.erl
|
||||
-@erase ELDAPv3.hrl
|
||||
-@erase ELDAPv3.beam
|
||||
-@erase eldap_filter_yecc.erl
|
||||
-@erase eldap_filter_yecc.beam
|
||||
-@erase $(BEAMS)
|
||||
|
||||
ELDAPv3.erl : ELDAPv3.asn
|
||||
erlc $(ASN_FLAGS) -W $(EFLAGS) ELDAPv3.asn
|
||||
|
||||
eldap_filter_yecc.erl: eldap_filter_yecc.yrl
|
||||
erlc -W eldap_filter_yecc.yrl
|
||||
|
||||
$(OUTDIR)\eldap.beam : eldap.erl ELDAPv3.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) eldap.erl
|
||||
|
||||
@ -34,3 +39,6 @@ $(OUTDIR)\eldap_utils.beam : eldap_utils.erl
|
||||
|
||||
$(OUTDIR)\eldap_pool.beam : eldap_pool.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) eldap_pool.erl
|
||||
|
||||
$(OUTDIR)\eldap_filter_yecc.beam : eldap_filter_yecc.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) eldap_filter_yecc.erl
|
||||
|
@ -33,8 +33,11 @@
|
||||
|
||||
%%% Modified by Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
%%% Modified by Evgeniy Khramtsov <xram@jabber.ru>
|
||||
%%% Modified by Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%% Implemented queue for bind() requests to prevent pending binds.
|
||||
%%% Implemented extensibleMatch/2 function.
|
||||
%%% Implemented LDAP Extended Operations (currently only Password Modify
|
||||
%%% is supported - RFC 3062).
|
||||
|
||||
%%% Modified by Christophe Romain <christophe.romain@process-one.net>
|
||||
%%% Improve error case handling
|
||||
@ -71,9 +74,9 @@
|
||||
|
||||
-export([baseObject/0,singleLevel/0,wholeSubtree/0,close/1,
|
||||
equalityMatch/2,greaterOrEqual/2,lessOrEqual/2,
|
||||
approxMatch/2,search/2,substrings/2,present/1,
|
||||
approxMatch/2,search/2,substrings/2,present/1,extensibleMatch/2,
|
||||
'and'/1,'or'/1,'not'/1,modify/3, mod_add/2, mod_delete/2,
|
||||
mod_replace/2, add/3, delete/2, modify_dn/5, bind/3]).
|
||||
mod_replace/2, add/3, delete/2, modify_dn/5, modify_passwd/3, bind/3]).
|
||||
-export([get_status/1]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
@ -127,9 +130,10 @@ start_link(Name) ->
|
||||
Reg_name = list_to_atom("eldap_" ++ Name),
|
||||
gen_fsm:start_link({local, Reg_name}, ?MODULE, [], []).
|
||||
|
||||
start_link(Name, Hosts, Port, Rootdn, Passwd, Encrypt) ->
|
||||
start_link(Name, Hosts, Port, Rootdn, Passwd, Opts) ->
|
||||
Reg_name = list_to_atom("eldap_" ++ Name),
|
||||
gen_fsm:start_link({local, Reg_name}, ?MODULE, {Hosts, Port, Rootdn, Passwd, Encrypt}, []).
|
||||
gen_fsm:start_link({local, Reg_name}, ?MODULE,
|
||||
{Hosts, Port, Rootdn, Passwd, Opts}, []).
|
||||
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Get status of connection.
|
||||
@ -239,6 +243,10 @@ modify_dn(Handle, Entry, NewRDN, DelOldRDN, NewSup)
|
||||
{modify_dn, Entry, NewRDN, bool_p(DelOldRDN), optional(NewSup)},
|
||||
?CALL_TIMEOUT).
|
||||
|
||||
modify_passwd(Handle, DN, Passwd) when is_list(DN), is_list(Passwd) ->
|
||||
Handle1 = get_handle(Handle),
|
||||
gen_fsm:sync_send_event(
|
||||
Handle1, {modify_passwd, DN, Passwd}, ?CALL_TIMEOUT).
|
||||
|
||||
%%% --------------------------------------------------------------------
|
||||
%%% Bind.
|
||||
@ -374,6 +382,29 @@ substrings(Type, SubStr) when is_list(Type), is_list(SubStr) ->
|
||||
{substrings,#'SubstringFilter'{type = Type,
|
||||
substrings = Ss}}.
|
||||
|
||||
%%%
|
||||
%%% extensibleMatch filter.
|
||||
%%% FIXME: Describe the purpose of this filter.
|
||||
%%%
|
||||
%%% Value ::= string( <attribute> )
|
||||
%%% Opts ::= listof( {matchingRule, Str} | {type, Str} | {dnAttributes, true} )
|
||||
%%%
|
||||
%%% Example: extensibleMatch("Fred", [{matchingRule, "1.2.3.4.5"}, {type, "cn"}]).
|
||||
%%%
|
||||
extensibleMatch(Value, Opts) when is_list(Value), is_list(Opts) ->
|
||||
MRA = #'MatchingRuleAssertion'{matchValue=Value},
|
||||
{extensibleMatch, extensibleMatch_opts(Opts, MRA)}.
|
||||
|
||||
extensibleMatch_opts([{matchingRule, Rule} | Opts], MRA) when is_list(Rule) ->
|
||||
extensibleMatch_opts(Opts, MRA#'MatchingRuleAssertion'{matchingRule=Rule});
|
||||
extensibleMatch_opts([{type, Desc} | Opts], MRA) when is_list(Desc) ->
|
||||
extensibleMatch_opts(Opts, MRA#'MatchingRuleAssertion'{type=Desc});
|
||||
extensibleMatch_opts([{dnAttributes, true} | Opts], MRA) ->
|
||||
extensibleMatch_opts(Opts, MRA#'MatchingRuleAssertion'{dnAttributes=true});
|
||||
extensibleMatch_opts([_ | Opts], MRA) ->
|
||||
extensibleMatch_opts(Opts, MRA);
|
||||
extensibleMatch_opts([], MRA) ->
|
||||
MRA.
|
||||
|
||||
get_handle(Pid) when is_pid(Pid) -> Pid;
|
||||
get_handle(Atom) when is_atom(Atom) -> Atom;
|
||||
@ -393,15 +424,18 @@ get_handle(Name) when is_list(Name) -> list_to_atom("eldap_" ++ Name).
|
||||
%%----------------------------------------------------------------------
|
||||
init([]) ->
|
||||
case get_config() of
|
||||
{ok, Hosts, Rootdn, Passwd, Encrypt} ->
|
||||
init({Hosts, Rootdn, Passwd, Encrypt});
|
||||
{ok, Hosts, Rootdn, Passwd, Opts} ->
|
||||
init({Hosts, Rootdn, Passwd, Opts});
|
||||
{error, Reason} ->
|
||||
{stop, Reason}
|
||||
end;
|
||||
init({Hosts, Port, Rootdn, Passwd, Encrypt}) ->
|
||||
init({Hosts, Port, Rootdn, Passwd, Opts}) ->
|
||||
catch ssl:start(),
|
||||
{X1,X2,X3} = erlang:now(),
|
||||
ssl:seed(integer_to_list(X1) ++ integer_to_list(X2) ++ integer_to_list(X3)),
|
||||
ssl:seed(randoms:get_string()),
|
||||
Encrypt = case proplists:get_value(encrypt, Opts) of
|
||||
tls -> tls;
|
||||
_ -> none
|
||||
end,
|
||||
PortTemp = case Port of
|
||||
undefined ->
|
||||
case Encrypt of
|
||||
@ -414,7 +448,14 @@ init({Hosts, Port, Rootdn, Passwd, Encrypt}) ->
|
||||
end;
|
||||
PT -> PT
|
||||
end,
|
||||
TLSOpts = [verify_none],
|
||||
TLSOpts = case proplists:get_value(tls_verify, Opts) of
|
||||
soft ->
|
||||
[{verify, 1}];
|
||||
hard ->
|
||||
[{verify, 2}];
|
||||
_ ->
|
||||
[{verify, 0}]
|
||||
end,
|
||||
{ok, connecting, #eldap{hosts = Hosts,
|
||||
port = PortTemp,
|
||||
rootdn = Rootdn,
|
||||
@ -671,6 +712,16 @@ gen_req({modify_dn, Entry, NewRDN, DelOldRDN, NewSup}) ->
|
||||
deleteoldrdn = DelOldRDN,
|
||||
newSuperior = NewSup}};
|
||||
|
||||
gen_req({modify_passwd, DN, Passwd}) ->
|
||||
{ok, ReqVal} = asn1rt:encode(
|
||||
'ELDAPv3', 'PasswdModifyRequestValue',
|
||||
#'PasswdModifyRequestValue'{
|
||||
userIdentity = DN,
|
||||
newPasswd = Passwd}),
|
||||
{extendedReq,
|
||||
#'ExtendedRequest'{requestName = ?passwdModifyOID,
|
||||
requestValue = list_to_binary(ReqVal)}};
|
||||
|
||||
gen_req({bind, RootDN, Passwd}) ->
|
||||
{bindRequest,
|
||||
#'BindRequest'{version = ?LDAP_VERSION,
|
||||
@ -745,6 +796,11 @@ recvd_packet(Pkt, S) ->
|
||||
cancel_timer(Timer),
|
||||
Reply = check_bind_reply(Result, From),
|
||||
{reply, Reply, From, S#eldap{dict = New_dict}};
|
||||
{extendedReq, {extendedResp, Result}} ->
|
||||
New_dict = dict:erase(Id, Dict),
|
||||
cancel_timer(Timer),
|
||||
Reply = check_extended_reply(Result, From),
|
||||
{reply, Reply, From, S#eldap{dict = New_dict}};
|
||||
{OtherName, OtherResult} ->
|
||||
New_dict = dict:erase(Id, Dict),
|
||||
cancel_timer(Timer),
|
||||
@ -769,6 +825,15 @@ check_bind_reply(#'BindResponse'{resultCode = Reason}, _From) ->
|
||||
check_bind_reply(Other, _From) ->
|
||||
{error, Other}.
|
||||
|
||||
%% TODO: process reply depending on requestName:
|
||||
%% this requires BER-decoding of #'ExtendedResponse'.response
|
||||
check_extended_reply(#'ExtendedResponse'{resultCode = success}, _From) ->
|
||||
ok;
|
||||
check_extended_reply(#'ExtendedResponse'{resultCode = Reason}, _From) ->
|
||||
{error, Reason};
|
||||
check_extended_reply(Other, _From) ->
|
||||
{error, Other}.
|
||||
|
||||
get_op_rec(Id, Dict) ->
|
||||
case dict:find(Id, Dict) of
|
||||
{ok, [{Timer, _Command, From, Name}|Res]} ->
|
||||
@ -904,7 +969,7 @@ connect_bind(S) ->
|
||||
tls ->
|
||||
SockMod = ssl,
|
||||
SslOpts = [{packet, asn1}, {active, true}, {keepalive, true},
|
||||
binary],
|
||||
binary | S#eldap.tls_options],
|
||||
ssl:connect(Host, S#eldap.port, SslOpts);
|
||||
%% starttls -> %% TODO: Implement STARTTLS;
|
||||
_ ->
|
||||
@ -973,6 +1038,8 @@ v_filter({lessOrEqual,AV}) -> {lessOrEqual,AV};
|
||||
v_filter({approxMatch,AV}) -> {approxMatch,AV};
|
||||
v_filter({present,A}) -> {present,A};
|
||||
v_filter({substrings,S}) when is_record(S,'SubstringFilter') -> {substrings,S};
|
||||
v_filter({extensibleMatch, S}) when is_record(S, 'MatchingRuleAssertion') ->
|
||||
{extensibleMatch, S};
|
||||
v_filter(_Filter) -> throw({error,concat(["unknown filter: ",_Filter])}).
|
||||
|
||||
v_modifications(Mods) ->
|
||||
@ -1018,8 +1085,8 @@ get_config() ->
|
||||
case file:consult(File) of
|
||||
{ok, Entries} ->
|
||||
case catch parse(Entries) of
|
||||
{ok, Hosts, Port, Rootdn, Passwd, Encrypt} ->
|
||||
{ok, Hosts, Port, Rootdn, Passwd, Encrypt};
|
||||
{ok, Hosts, Port, Rootdn, Passwd, Opts} ->
|
||||
{ok, Hosts, Port, Rootdn, Passwd, Opts};
|
||||
{error, Reason} ->
|
||||
{error, Reason};
|
||||
{'EXIT', Reason} ->
|
||||
@ -1035,7 +1102,7 @@ parse(Entries) ->
|
||||
get_integer(port, Entries),
|
||||
get_list(rootdn, Entries),
|
||||
get_list(passwd, Entries),
|
||||
get_atom(encrypt, Entries)}.
|
||||
get_list(options, Entries)}.
|
||||
|
||||
get_integer(Key, List) ->
|
||||
case lists:keysearch(Key, 1, List) of
|
||||
@ -1057,15 +1124,15 @@ get_list(Key, List) ->
|
||||
throw({error, "No Entry in Config for " ++ atom_to_list(Key)})
|
||||
end.
|
||||
|
||||
get_atom(Key, List) ->
|
||||
case lists:keysearch(Key, 1, List) of
|
||||
{value, {Key, Value}} when is_atom(Value) ->
|
||||
Value;
|
||||
{value, {Key, _Value}} ->
|
||||
throw({error, "Bad Value in Config for " ++ atom_to_list(Key)});
|
||||
false ->
|
||||
throw({error, "No Entry in Config for " ++ atom_to_list(Key)})
|
||||
end.
|
||||
%% get_atom(Key, List) ->
|
||||
%% case lists:keysearch(Key, 1, List) of
|
||||
%% {value, {Key, Value}} when is_atom(Value) ->
|
||||
%% Value;
|
||||
%% {value, {Key, _Value}} ->
|
||||
%% throw({error, "Bad Value in Config for " ++ atom_to_list(Key)});
|
||||
%% false ->
|
||||
%% throw({error, "No Entry in Config for " ++ atom_to_list(Key)})
|
||||
%% end.
|
||||
|
||||
get_hosts(Key, List) ->
|
||||
lists:map(fun({Key1, {A,B,C,D}}) when is_integer(A),
|
||||
|
@ -3,7 +3,7 @@
|
||||
%%% Purpose: Converts String Representation of
|
||||
%%% LDAP Search Filter (RFC 2254)
|
||||
%%% to eldap's representation of filter
|
||||
%%% Author: Evgeniy Khramtsov <xramtsov@gmail.com>
|
||||
%%% Author: Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
|
||||
@ -24,19 +24,17 @@
|
||||
%%% 02111-1307 USA
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(eldap_filter).
|
||||
|
||||
%%%======================
|
||||
%%% Export functions
|
||||
%%%======================
|
||||
%% TODO: remove this when new regexp module will be used
|
||||
-compile({nowarn_deprecated_function, {regexp, sub, 3}}).
|
||||
|
||||
-export([parse/1,
|
||||
parse/2,
|
||||
do_sub/2
|
||||
]).
|
||||
-export([parse/1, parse/2, do_sub/2]).
|
||||
|
||||
%%%-------------------------------------------------------------------------
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% Arity: parse/1
|
||||
%%% Function: parse(RFC2254_Filter) -> {ok, EldapFilter} |
|
||||
%%% {error, bad_filter}
|
||||
@ -51,11 +49,11 @@
|
||||
%%%
|
||||
%%% {ok,{'and',[{'not',{lessOrEqual,{'AttributeValueAssertion',"uid","100"}}},
|
||||
%%% {present,"mail"}]}}
|
||||
%%%-------------------------------------------------------------------------
|
||||
parse(RFC2254_Filter) ->
|
||||
parse(RFC2254_Filter, []).
|
||||
%%%-------------------------------------------------------------------
|
||||
parse(L) when is_list(L) ->
|
||||
parse(L, []).
|
||||
|
||||
%%%-------------------------------------------------------------------------
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% Arity: parse/2
|
||||
%%% Function: parse(RFC2254_Filter, [SubstValue |...]) ->
|
||||
%%% {ok, EldapFilter} |
|
||||
@ -81,135 +79,53 @@ parse(RFC2254_Filter) ->
|
||||
%%% {equalityMatch,{'AttributeValueAssertion',
|
||||
%%% "jid",
|
||||
%%% "xramtsov@gmail.com"}}]}}
|
||||
%%%--------------------------------------------------------------------------
|
||||
parse(RFC2254_Filter, ListOfSubValues) ->
|
||||
case catch convert_filter(parse_filter(RFC2254_Filter), ListOfSubValues) of
|
||||
[EldapFilter] when is_tuple(EldapFilter) ->
|
||||
{ok, EldapFilter};
|
||||
{regexp, Error} ->
|
||||
{error, Error};
|
||||
_ ->
|
||||
{error, bad_filter}
|
||||
%%%-------------------------------------------------------------------
|
||||
parse(L, SList) when is_list(L), is_list(SList) ->
|
||||
case catch eldap_filter_yecc:parse(scan(L, SList)) of
|
||||
{error, {_, _, Msg}} ->
|
||||
{error, Msg};
|
||||
{ok, Result} ->
|
||||
{ok, Result};
|
||||
{regexp, Err} ->
|
||||
{error, Err}
|
||||
end.
|
||||
|
||||
%%%==========================
|
||||
%%% Internal functions
|
||||
%%%==========================
|
||||
%%====================================================================
|
||||
%% Internal functions
|
||||
%%====================================================================
|
||||
-define(do_scan(L), scan(Rest, [], [{L, 1} | check(Buf, S) ++ Result], L, S)).
|
||||
|
||||
%%%----------------------
|
||||
%%% split/1,4
|
||||
%%%----------------------
|
||||
split(Filter) ->
|
||||
split(Filter, 0, [], []).
|
||||
scan(L, SList) ->
|
||||
scan(L, "", [], undefined, SList).
|
||||
|
||||
split([], _, _, Result) ->
|
||||
Result;
|
||||
scan("=*)" ++ Rest, Buf, Result, '(', S) ->
|
||||
scan(Rest, [], [{')', 1}, {'=*', 1} | check(Buf, S) ++ Result], ')', S);
|
||||
scan(":dn" ++ Rest, Buf, Result, '(', S) -> ?do_scan(':dn');
|
||||
scan(":=" ++ Rest, Buf, Result, '(', S) -> ?do_scan(':=');
|
||||
scan(":=" ++ Rest, Buf, Result, ':dn', S) -> ?do_scan(':=');
|
||||
scan(":=" ++ Rest, Buf, Result, ':', S) -> ?do_scan(':=');
|
||||
scan("~=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('~=');
|
||||
scan(">=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('>=');
|
||||
scan("<=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('<=');
|
||||
scan("=" ++ Rest, Buf, Result, '(', S) -> ?do_scan('=');
|
||||
scan(":" ++ Rest, Buf, Result, '(', S) -> ?do_scan(':');
|
||||
scan(":" ++ Rest, Buf, Result, ':dn', S) -> ?do_scan(':');
|
||||
scan("&" ++ Rest, Buf, Result, '(', S) when Buf=="" -> ?do_scan('&');
|
||||
scan("|" ++ Rest, Buf, Result, '(', S) when Buf=="" -> ?do_scan('|');
|
||||
scan("!" ++ Rest, Buf, Result, '(', S) when Buf=="" -> ?do_scan('!');
|
||||
scan("*" ++ Rest, Buf, Result, '*', S) -> ?do_scan('*');
|
||||
scan("*" ++ Rest, Buf, Result, '=', S) -> ?do_scan('*');
|
||||
scan("(" ++ Rest, Buf, Result, _, S) -> ?do_scan('(');
|
||||
scan(")" ++ Rest, Buf, Result, _, S) -> ?do_scan(')');
|
||||
scan([Letter | Rest], Buf, Result, PreviosAtom, S) ->
|
||||
scan(Rest, [Letter|Buf], Result, PreviosAtom, S);
|
||||
scan([], Buf, Result, _, S) ->
|
||||
lists:reverse(check(Buf, S) ++ Result).
|
||||
|
||||
split([H|T], Num, Rest, Result) ->
|
||||
NewNum = case H of
|
||||
$( -> Num + 1;
|
||||
$) -> Num - 1;
|
||||
_ -> Num
|
||||
end,
|
||||
if
|
||||
NewNum == 0 ->
|
||||
X = Rest++[H],
|
||||
LenX = length(X),
|
||||
if
|
||||
LenX > 2 ->
|
||||
split(T, 0, [], Result ++ [lists:sublist(X, 2, LenX-2)]);
|
||||
true ->
|
||||
split(T, 0, Rest, Result)
|
||||
end;
|
||||
true ->
|
||||
split(T, NewNum, Rest++[H], Result)
|
||||
end.
|
||||
|
||||
%%%-----------------------
|
||||
%%% parse_filter/1
|
||||
%%%-----------------------
|
||||
parse_filter(Filter) ->
|
||||
case Filter of
|
||||
[$! | T] ->
|
||||
{'not', parse_filter(T)};
|
||||
[$| | T] ->
|
||||
{'or', parse_filter(T)};
|
||||
[$& | T] ->
|
||||
{'and', parse_filter(T)};
|
||||
[$( | _] ->
|
||||
parse_filter(split(Filter));
|
||||
[List | _] when is_list(List) ->
|
||||
[parse_filter(X) || X <- Filter];
|
||||
_ ->
|
||||
Filter
|
||||
end.
|
||||
|
||||
%%%--------------------
|
||||
%%% convert_filter/2
|
||||
%%%--------------------
|
||||
convert_filter({'not', [Val | _]}, Replace) ->
|
||||
eldap:'not'(convert_filter(Val, Replace));
|
||||
|
||||
convert_filter({'or', Vals}, Replace) ->
|
||||
eldap:'or'([convert_filter(X, Replace) || X <- Vals]);
|
||||
|
||||
convert_filter({'and', Vals}, Replace) ->
|
||||
eldap:'and'([convert_filter(X, Replace) || X <- Vals]);
|
||||
|
||||
convert_filter([H|_] = Filter, Replace) when is_integer(H) ->
|
||||
parse_attr(Filter, Replace);
|
||||
|
||||
convert_filter(Filter, Replace) when is_list(Filter) ->
|
||||
[convert_filter(X, Replace) || X <- Filter].
|
||||
|
||||
%%%-----------------
|
||||
%%% parse_attr/2,3
|
||||
%%%-----------------
|
||||
parse_attr(Attr, ListOfSubValues) ->
|
||||
{Action, [_|_] = Name, [_|_] = Value} = split_attribute(Attr),
|
||||
parse_attr(Action, {Name, Value}, ListOfSubValues).
|
||||
|
||||
parse_attr(approx, {Name, Value}, ListOfSubValues) ->
|
||||
NewValue = do_sub(Value, ListOfSubValues),
|
||||
eldap:approxMatch(Name, NewValue);
|
||||
|
||||
parse_attr(greater, {Name, Value}, ListOfSubValues) ->
|
||||
NewValue = do_sub(Value, ListOfSubValues),
|
||||
eldap:greaterOrEqual(Name, NewValue);
|
||||
|
||||
parse_attr(less, {Name, Value}, ListOfSubValues) ->
|
||||
NewValue = do_sub(Value, ListOfSubValues),
|
||||
eldap:lessOrEqual(Name, NewValue);
|
||||
|
||||
parse_attr(equal, {Name, Value}, ListOfSubValues) ->
|
||||
{ok, RegSList} = regexp:split(remove_extra_asterisks(Value), "[*]"),
|
||||
Pattern = case [do_sub(X, ListOfSubValues) || X <- RegSList] of
|
||||
[Head | Tail] when Tail /= [] ->
|
||||
{Head, lists:sublist(Tail, length(Tail)-1), lists:last(Tail)};
|
||||
R ->
|
||||
R
|
||||
end,
|
||||
case Pattern of
|
||||
[V] ->
|
||||
eldap:equalityMatch(Name, V);
|
||||
{[], [], []} ->
|
||||
eldap:present(Name);
|
||||
{"", Any, ""} ->
|
||||
eldap:substrings(Name, [{any, X} || X<-Any]);
|
||||
{H, Any, ""} ->
|
||||
eldap:substrings(Name, [{initial, H}]++[{any, X} || X<-Any]);
|
||||
{"", Any, T} ->
|
||||
eldap:substrings(Name, [{any, X} || X<-Any]++[{final, T}]);
|
||||
{H, Any, T} ->
|
||||
eldap:substrings(Name, [{initial, H}]++[{any, X} || X<-Any]++[{final, T}])
|
||||
end;
|
||||
|
||||
parse_attr(_, _, _) ->
|
||||
false.
|
||||
|
||||
%%%--------------------
|
||||
%%% do_sub/2,3
|
||||
%%%--------------------
|
||||
check([], _) ->
|
||||
[];
|
||||
check(Buf, S) ->
|
||||
[{str, 1, do_sub(lists:reverse(Buf), S)}].
|
||||
|
||||
-define(MAX_RECURSION, 100).
|
||||
|
||||
@ -234,9 +150,9 @@ do_sub(S, {RegExp, New}, Iter) ->
|
||||
{ok, NewS, _} when Iter =< ?MAX_RECURSION ->
|
||||
do_sub(NewS, {RegExp, New}, Iter+1);
|
||||
{ok, _, _} when Iter > ?MAX_RECURSION ->
|
||||
throw({regexp, max_substitute_recursion});
|
||||
erlang:error(max_substitute_recursion);
|
||||
_ ->
|
||||
throw({regexp, bad_regexp})
|
||||
erlang:error(bad_regexp)
|
||||
end;
|
||||
|
||||
do_sub(S, {_, _, N}, _) when N<1 ->
|
||||
@ -251,52 +167,11 @@ do_sub(S, {RegExp, New, Times}, Iter) ->
|
||||
{ok, NewS, _} ->
|
||||
NewS;
|
||||
_ ->
|
||||
throw({regexp, bad_regexp})
|
||||
erlang:error(bad_regexp)
|
||||
end.
|
||||
|
||||
remove_extra_asterisks(String) ->
|
||||
{Res, _} = lists:foldl(
|
||||
fun(X, {Acc, Last}) ->
|
||||
case X of
|
||||
$* when Last==$* ->
|
||||
{Acc, X};
|
||||
_ ->
|
||||
{Acc ++ [X], X}
|
||||
end
|
||||
end,
|
||||
{"", ""}, String),
|
||||
Res.
|
||||
|
||||
replace_amps(String) ->
|
||||
lists:foldl(
|
||||
fun(X, Acc) ->
|
||||
if
|
||||
X == $& ->
|
||||
Acc ++ "\\&";
|
||||
true ->
|
||||
Acc ++ [X]
|
||||
end
|
||||
end,
|
||||
"", String).
|
||||
|
||||
split_attribute(String) ->
|
||||
split_attribute(String, "", $0).
|
||||
|
||||
split_attribute([], _, _) ->
|
||||
{error, "", ""};
|
||||
|
||||
split_attribute([H|Tail], Acc, Last) ->
|
||||
case H of
|
||||
$= when Last==$> ->
|
||||
{greater, lists:sublist(Acc, 1, length(Acc)-1), Tail};
|
||||
$= when Last==$< ->
|
||||
{less, lists:sublist(Acc, 1, length(Acc)-1), Tail};
|
||||
$= when Last==$~ ->
|
||||
{approx, lists:sublist(Acc, 1, length(Acc)-1), Tail};
|
||||
$= when Last==$: ->
|
||||
{equal, lists:sublist(Acc, 1, length(Acc)-1), Tail};
|
||||
$= ->
|
||||
{equal, Acc, Tail};
|
||||
_ ->
|
||||
split_attribute(Tail, Acc++[H], H)
|
||||
end.
|
||||
lists:flatmap(
|
||||
fun($&) -> "\\&";
|
||||
(Chr) -> [Chr]
|
||||
end, String).
|
||||
|
71
src/eldap/eldap_filter_yecc.yrl
Normal file
71
src/eldap/eldap_filter_yecc.yrl
Normal file
@ -0,0 +1,71 @@
|
||||
Nonterminals
|
||||
filter filtercomp filterlist item
|
||||
simple present substring extensible
|
||||
initial any final matchingrule xattr
|
||||
attr value.
|
||||
|
||||
Terminals str
|
||||
'(' ')' '&' '|' '!' '=' '~=' '>=' '<=' '=*' '*' ':dn' ':' ':='.
|
||||
|
||||
Rootsymbol filter.
|
||||
|
||||
filter -> '(' filtercomp ')': '$2'.
|
||||
filtercomp -> '&' filterlist: 'and'('$2').
|
||||
filtercomp -> '|' filterlist: 'or'('$2').
|
||||
filtercomp -> '!' filter: 'not'('$2').
|
||||
filtercomp -> item: '$1'.
|
||||
filterlist -> filter: '$1'.
|
||||
filterlist -> filter filterlist: flatten(['$1', '$2']).
|
||||
|
||||
item -> simple: '$1'.
|
||||
item -> present: '$1'.
|
||||
item -> substring: '$1'.
|
||||
item -> extensible: '$1'.
|
||||
|
||||
simple -> attr '=' value: equal('$1', '$3').
|
||||
simple -> attr '~=' value: approx('$1', '$3').
|
||||
simple -> attr '>=' value: greater('$1', '$3').
|
||||
simple -> attr '<=' value: less('$1', '$3').
|
||||
|
||||
present -> attr '=*': present('$1').
|
||||
|
||||
substring -> attr '=' initial '*' any: substrings('$1', ['$3', '$5']).
|
||||
substring -> attr '=' '*' any final: substrings('$1', ['$4', '$5']).
|
||||
substring -> attr '=' initial '*' any final: substrings('$1', ['$3', '$5', '$6']).
|
||||
substring -> attr '=' '*' any: substrings('$1', ['$4']).
|
||||
any -> any value '*': 'any'('$1', '$2').
|
||||
any -> '$empty': [].
|
||||
initial -> value: initial('$1').
|
||||
final -> value: final('$1').
|
||||
|
||||
extensible -> xattr ':dn' ':' matchingrule ':=' value: extensible('$6', ['$1', '$4']).
|
||||
extensible -> xattr ':' matchingrule ':=' value: extensible('$5', ['$1', '$3']).
|
||||
extensible -> xattr ':dn' ':=' value: extensible('$4', ['$1']).
|
||||
extensible -> xattr ':=' value: extensible('$3', ['$1']).
|
||||
extensible -> ':dn' ':' matchingrule ':=' value: extensible('$5', ['$3']).
|
||||
extensible -> ':' matchingrule ':=' value: extensible('$4', ['$2']).
|
||||
xattr -> value: xattr('$1').
|
||||
matchingrule -> value: matchingrule('$1').
|
||||
|
||||
attr -> str: value_of('$1').
|
||||
value -> str: value_of('$1').
|
||||
|
||||
Erlang code.
|
||||
|
||||
'and'(Value) -> eldap:'and'(Value).
|
||||
'or'(Value) -> eldap:'or'(Value).
|
||||
'not'(Value) -> eldap:'not'(Value).
|
||||
equal(Desc, Value) -> eldap:equalityMatch(Desc, Value).
|
||||
approx(Desc, Value) -> eldap:approxMatch(Desc, Value).
|
||||
greater(Desc, Value) -> eldap:greaterOrEqual(Desc, Value).
|
||||
less(Desc, Value) -> eldap:lessOrEqual(Desc, Value).
|
||||
present(Value) -> eldap:present(Value).
|
||||
extensible(Value, Opts) -> eldap:extensibleMatch(Value, Opts).
|
||||
substrings(Desc, ValueList) -> eldap:substrings(Desc, flatten(ValueList)).
|
||||
initial(Value) -> {initial, Value}.
|
||||
final(Value) -> {final, Value}.
|
||||
'any'(Token, Value) -> [Token, {any, Value}].
|
||||
xattr(Value) -> {type, Value}.
|
||||
matchingrule(Value) -> {matchingRule, Value}.
|
||||
value_of(Token) -> element(3, Token).
|
||||
flatten(List) -> lists:flatten(List).
|
@ -31,7 +31,8 @@
|
||||
-export([
|
||||
start_link/7,
|
||||
bind/3,
|
||||
search/2
|
||||
search/2,
|
||||
modify_passwd/3
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@ -45,12 +46,17 @@ bind(PoolName, DN, Passwd) ->
|
||||
search(PoolName, Opts) ->
|
||||
do_request(PoolName, {search, [Opts]}).
|
||||
|
||||
start_link(Name, Hosts, Backups, Port, Rootdn, Passwd, Encrypt) ->
|
||||
modify_passwd(PoolName, DN, Passwd) ->
|
||||
do_request(PoolName, {modify_passwd, [DN, Passwd]}).
|
||||
|
||||
start_link(Name, Hosts, Backups, Port, Rootdn, Passwd, Opts) ->
|
||||
PoolName = make_id(Name),
|
||||
pg2:create(PoolName),
|
||||
lists:foreach(fun(Host) ->
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ID = erlang:ref_to_list(make_ref()),
|
||||
case catch eldap:start_link(ID, [Host|Backups], Port, Rootdn, Passwd, Encrypt) of
|
||||
case catch eldap:start_link(ID, [Host|Backups], Port,
|
||||
Rootdn, Passwd, Opts) of
|
||||
{ok, Pid} ->
|
||||
pg2:join(PoolName, Pid);
|
||||
_ ->
|
||||
|
@ -41,6 +41,8 @@ typedef struct {
|
||||
XML_Parser parser;
|
||||
} expat_data;
|
||||
|
||||
static XML_Memory_Handling_Suite ms = {driver_alloc, driver_realloc, driver_free};
|
||||
|
||||
void *erlXML_StartElementHandler(expat_data *d,
|
||||
const XML_Char *name,
|
||||
const XML_Char **atts)
|
||||
@ -98,7 +100,7 @@ static ErlDrvData expat_erl_start(ErlDrvPort port, char *buff)
|
||||
{
|
||||
expat_data* d = (expat_data*)driver_alloc(sizeof(expat_data));
|
||||
d->port = port;
|
||||
d->parser = XML_ParserCreate("UTF-8");
|
||||
d->parser = XML_ParserCreate_MM("UTF-8", &ms, NULL);
|
||||
XML_SetUserData(d->parser, d);
|
||||
|
||||
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
|
||||
|
@ -27,8 +27,15 @@
|
||||
-module(extauth).
|
||||
-author('leifj@it.su.se').
|
||||
|
||||
-export([start/2, stop/1, init/2,
|
||||
check_password/3, set_password/3, is_user_exists/2]).
|
||||
-export([start/2,
|
||||
stop/1,
|
||||
init/2,
|
||||
check_password/3,
|
||||
set_password/3,
|
||||
try_register/3,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
is_user_exists/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
@ -36,16 +43,29 @@
|
||||
-define(CALL_TIMEOUT, 10000). % Timeout is in milliseconds: 10 seconds == 10000
|
||||
|
||||
start(Host, ExtPrg) ->
|
||||
spawn(?MODULE, init, [Host, ExtPrg]).
|
||||
lists:foreach(
|
||||
fun(This) ->
|
||||
spawn(?MODULE, init, [get_process_name(Host, This), ExtPrg])
|
||||
end,
|
||||
lists:seq(0, get_instances(Host)-1)
|
||||
).
|
||||
|
||||
init(Host, ExtPrg) ->
|
||||
register(gen_mod:get_module_proc(Host, eauth), self()),
|
||||
init(ProcessName, ExtPrg) ->
|
||||
register(ProcessName, self()),
|
||||
process_flag(trap_exit,true),
|
||||
Port = open_port({spawn, ExtPrg}, [{packet,2}]),
|
||||
loop(Port, ?INIT_TIMEOUT).
|
||||
|
||||
stop(Host) ->
|
||||
gen_mod:get_module_proc(Host, eauth) ! stop.
|
||||
lists:foreach(
|
||||
fun(This) ->
|
||||
get_process_name(Host, This) ! stop
|
||||
end,
|
||||
lists:seq(0, get_instances(Host)-1)
|
||||
).
|
||||
|
||||
get_process_name(Host, Integer) ->
|
||||
gen_mod:get_module_proc(lists:append([Host, integer_to_list(Integer)]), eauth).
|
||||
|
||||
check_password(User, Server, Password) ->
|
||||
call_port(Server, ["auth", User, Server, Password]).
|
||||
@ -56,14 +76,38 @@ is_user_exists(User, Server) ->
|
||||
set_password(User, Server, Password) ->
|
||||
call_port(Server, ["setpass", User, Server, Password]).
|
||||
|
||||
try_register(User, Server, Password) ->
|
||||
case call_port(Server, ["tryregister", User, Server, Password]) of
|
||||
true -> {atomic, ok};
|
||||
false -> {error, not_allowed}
|
||||
end.
|
||||
|
||||
remove_user(User, Server) ->
|
||||
call_port(Server, ["removeuser", User, Server]).
|
||||
|
||||
remove_user(User, Server, Password) ->
|
||||
call_port(Server, ["removeuser3", User, Server, Password]).
|
||||
|
||||
call_port(Server, Msg) ->
|
||||
LServer = jlib:nameprep(Server),
|
||||
gen_mod:get_module_proc(LServer, eauth) ! {call, self(), Msg},
|
||||
ProcessName = get_process_name(LServer, random_instance(get_instances(LServer))),
|
||||
ProcessName ! {call, self(), Msg},
|
||||
receive
|
||||
{eauth,Result} ->
|
||||
Result
|
||||
end.
|
||||
|
||||
random_instance(MaxNum) ->
|
||||
{A1,A2,A3} = now(),
|
||||
random:seed(A1, A2, A3),
|
||||
random:uniform(MaxNum) - 1.
|
||||
|
||||
get_instances(Server) ->
|
||||
case ejabberd_config:get_local_option({extauth_instances, Server}) of
|
||||
Num when is_integer(Num) -> Num;
|
||||
_ -> 1
|
||||
end.
|
||||
|
||||
loop(Port, Timeout) ->
|
||||
receive
|
||||
{call, Caller, Msg} ->
|
||||
|
@ -22,6 +22,7 @@
|
||||
-define(NS_DISCO_ITEMS, "http://jabber.org/protocol/disco#items").
|
||||
-define(NS_DISCO_INFO, "http://jabber.org/protocol/disco#info").
|
||||
-define(NS_VCARD, "vcard-temp").
|
||||
-define(NS_VCARD_UPDATE, "vcard-temp:x:update").
|
||||
-define(NS_AUTH, "jabber:iq:auth").
|
||||
-define(NS_AUTH_ERROR, "jabber:iq:auth:error").
|
||||
-define(NS_REGISTER, "jabber:iq:register").
|
||||
@ -140,6 +141,8 @@
|
||||
?STANZA_ERROR("407", "auth", "subscription-required")).
|
||||
-define(ERR_UNEXPECTED_REQUEST,
|
||||
?STANZA_ERROR("400", "wait", "unexpected-request")).
|
||||
-define(ERR_UNEXPECTED_REQUEST_CANCEL,
|
||||
?STANZA_ERROR("401", "cancel", "unexpected-request")).
|
||||
%-define(ERR_,
|
||||
% ?STANZA_ERROR("", "", "")).
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_adhoc.erl
|
||||
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%% Purpose : Handle incoming ad-doc requests (JEP-0050)
|
||||
%%% Purpose : Handle incoming ad-doc requests (XEP-0050)
|
||||
%%% Created : 15 Nov 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%%
|
||||
%%%
|
||||
|
242
src/mod_caps.erl
242
src/mod_caps.erl
@ -33,6 +33,10 @@
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([read_caps/1,
|
||||
caps_stream_features/2,
|
||||
disco_features/5,
|
||||
disco_identity/5,
|
||||
disco_info/5,
|
||||
get_features/1]).
|
||||
|
||||
%% gen_mod callbacks
|
||||
@ -56,7 +60,7 @@
|
||||
|
||||
-define(PROCNAME, ejabberd_mod_caps).
|
||||
|
||||
-record(caps, {node, version, exts}).
|
||||
-record(caps, {node, version, hash, exts}).
|
||||
-record(caps_features, {node_pair, features = []}).
|
||||
|
||||
-record(state, {host}).
|
||||
@ -115,8 +119,10 @@ read_caps([{xmlelement, "c", Attrs, _Els} | Tail], Result) ->
|
||||
?NS_CAPS ->
|
||||
Node = xml:get_attr_s("node", Attrs),
|
||||
Version = xml:get_attr_s("ver", Attrs),
|
||||
Hash = xml:get_attr_s("hash", Attrs),
|
||||
Exts = string:tokens(xml:get_attr_s("ext", Attrs), " "),
|
||||
read_caps(Tail, #caps{node = Node, version = Version, exts = Exts});
|
||||
read_caps(Tail, #caps{node = Node, hash = Hash,
|
||||
version = Version, exts = Exts});
|
||||
_ ->
|
||||
read_caps(Tail, Result)
|
||||
end;
|
||||
@ -152,6 +158,41 @@ user_send_packet(#jid{luser = User, lserver = Server} = From,
|
||||
user_send_packet(_From, _To, _Packet) ->
|
||||
ok.
|
||||
|
||||
caps_stream_features(Acc, MyHost) ->
|
||||
case make_my_disco_hash(MyHost) of
|
||||
"" ->
|
||||
Acc;
|
||||
Hash ->
|
||||
[{xmlelement, "c", [{"xmlns", ?NS_CAPS},
|
||||
{"hash", "sha-1"},
|
||||
{"node", ?EJABBERD_URI},
|
||||
{"ver", Hash}], []} | Acc]
|
||||
end.
|
||||
|
||||
disco_features(_Acc, From, To, ?EJABBERD_URI ++ "#" ++ [_|_], Lang) ->
|
||||
ejabberd_hooks:run_fold(disco_local_features,
|
||||
To#jid.lserver,
|
||||
empty,
|
||||
[From, To, "", Lang]);
|
||||
disco_features(Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc.
|
||||
|
||||
disco_identity(_Acc, From, To, ?EJABBERD_URI ++ "#" ++ [_|_], Lang) ->
|
||||
ejabberd_hooks:run_fold(disco_local_identity,
|
||||
To#jid.lserver,
|
||||
[],
|
||||
[From, To, "", Lang]);
|
||||
disco_identity(Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc.
|
||||
|
||||
disco_info(_Acc, Host, Module, ?EJABBERD_URI ++ "#" ++ [_|_], Lang) ->
|
||||
ejabberd_hooks:run_fold(disco_info,
|
||||
Host,
|
||||
[],
|
||||
[Host, Module, "", Lang]);
|
||||
disco_info(Acc, _Host, _Module, _Node, _Lang) ->
|
||||
Acc.
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
@ -163,6 +204,16 @@ init([Host, _Opts]) ->
|
||||
mnesia:add_table_copy(caps_features, node(), disc_copies),
|
||||
ejabberd_hooks:add(user_send_packet, Host,
|
||||
?MODULE, user_send_packet, 75),
|
||||
ejabberd_hooks:add(c2s_stream_features, Host,
|
||||
?MODULE, caps_stream_features, 75),
|
||||
ejabberd_hooks:add(s2s_stream_features, Host,
|
||||
?MODULE, caps_stream_features, 75),
|
||||
ejabberd_hooks:add(disco_local_features, Host,
|
||||
?MODULE, disco_features, 75),
|
||||
ejabberd_hooks:add(disco_local_identity, Host,
|
||||
?MODULE, disco_identity, 75),
|
||||
ejabberd_hooks:add(disco_info, Host,
|
||||
?MODULE, disco_info, 75),
|
||||
{ok, #state{host = Host}}.
|
||||
|
||||
handle_call(stop, _From, State) ->
|
||||
@ -180,6 +231,16 @@ terminate(_Reason, State) ->
|
||||
Host = State#state.host,
|
||||
ejabberd_hooks:delete(user_send_packet, Host,
|
||||
?MODULE, user_send_packet, 75),
|
||||
ejabberd_hooks:delete(c2s_stream_features, Host,
|
||||
?MODULE, caps_stream_features, 75),
|
||||
ejabberd_hooks:delete(s2s_stream_features, Host,
|
||||
?MODULE, caps_stream_features, 75),
|
||||
ejabberd_hooks:delete(disco_local_features, Host,
|
||||
?MODULE, disco_features, 75),
|
||||
ejabberd_hooks:delete(disco_local_identity, Host,
|
||||
?MODULE, disco_identity, 75),
|
||||
ejabberd_hooks:delete(disco_info, Host,
|
||||
?MODULE, disco_info, 75),
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
@ -214,16 +275,21 @@ feature_request(_Host, _From, _Caps, []) ->
|
||||
feature_response(#iq{type = result,
|
||||
sub_el = [{xmlelement, _, _, Els}]},
|
||||
Host, From, Caps, [SubNode | SubNodes]) ->
|
||||
BinaryNode = node_to_binary(Caps#caps.node, SubNode),
|
||||
case check_hash(Caps, Els) of
|
||||
true ->
|
||||
Features = lists:flatmap(
|
||||
fun({xmlelement, "feature", FAttrs, _}) ->
|
||||
[xml:get_attr_s("var", FAttrs)];
|
||||
(_) ->
|
||||
[]
|
||||
end, Els),
|
||||
BinaryNode = node_to_binary(Caps#caps.node, SubNode),
|
||||
mnesia:dirty_write(
|
||||
#caps_features{node_pair = BinaryNode,
|
||||
features = features_to_binary(Features)}),
|
||||
features = features_to_binary(Features)});
|
||||
false ->
|
||||
mnesia:dirty_write(#caps_features{node_pair = BinaryNode})
|
||||
end,
|
||||
feature_request(Host, From, Caps, SubNodes);
|
||||
feature_response(timeout, _Host, _From, _Caps, _SubNodes) ->
|
||||
ok;
|
||||
@ -239,3 +305,171 @@ node_to_binary(Node, SubNode) ->
|
||||
|
||||
features_to_binary(L) -> [list_to_binary(I) || I <- L].
|
||||
binary_to_features(L) -> [binary_to_list(I) || I <- L].
|
||||
|
||||
make_my_disco_hash(Host) ->
|
||||
JID = jlib:make_jid("", Host, ""),
|
||||
case {ejabberd_hooks:run_fold(disco_local_features,
|
||||
Host,
|
||||
empty,
|
||||
[JID, JID, "", ""]),
|
||||
ejabberd_hooks:run_fold(disco_local_identity,
|
||||
Host,
|
||||
[],
|
||||
[JID, JID, "", ""]),
|
||||
ejabberd_hooks:run_fold(disco_info,
|
||||
Host,
|
||||
[],
|
||||
[Host, undefined, "", ""])} of
|
||||
{{result, Features}, Identities, Info} ->
|
||||
Feats = lists:map(
|
||||
fun({{Feat, _Host}}) ->
|
||||
{xmlelement, "feature", [{"var", Feat}], []};
|
||||
(Feat) ->
|
||||
{xmlelement, "feature", [{"var", Feat}], []}
|
||||
end, Features),
|
||||
make_disco_hash(Identities ++ Info ++ Feats, sha1);
|
||||
_Err ->
|
||||
""
|
||||
end.
|
||||
|
||||
-ifdef(HAVE_MD2).
|
||||
make_disco_hash(DiscoEls, Algo) ->
|
||||
Concat = [concat_identities(DiscoEls),
|
||||
concat_features(DiscoEls),
|
||||
concat_info(DiscoEls)],
|
||||
base64:encode_to_string(
|
||||
if Algo == md2 ->
|
||||
sha:md2(Concat);
|
||||
Algo == md5 ->
|
||||
crypto:md5(Concat);
|
||||
Algo == sha1 ->
|
||||
crypto:sha(Concat);
|
||||
Algo == sha224 ->
|
||||
sha:sha224(Concat);
|
||||
Algo == sha256 ->
|
||||
sha:sha256(Concat);
|
||||
Algo == sha384 ->
|
||||
sha:sha384(Concat);
|
||||
Algo == sha512 ->
|
||||
sha:sha512(Concat)
|
||||
end).
|
||||
|
||||
check_hash(Caps, Els) ->
|
||||
case Caps#caps.hash of
|
||||
"md2" ->
|
||||
Caps#caps.version == make_disco_hash(Els, md2);
|
||||
"md5" ->
|
||||
Caps#caps.version == make_disco_hash(Els, md5);
|
||||
"sha-1" ->
|
||||
Caps#caps.version == make_disco_hash(Els, sha1);
|
||||
"sha-224" ->
|
||||
Caps#caps.version == make_disco_hash(Els, sha224);
|
||||
"sha-256" ->
|
||||
Caps#caps.version == make_disco_hash(Els, sha256);
|
||||
"sha-384" ->
|
||||
Caps#caps.version == make_disco_hash(Els, sha384);
|
||||
"sha-512" ->
|
||||
Caps#caps.version == make_disco_hash(Els, sha512);
|
||||
_ ->
|
||||
true
|
||||
end.
|
||||
-else.
|
||||
make_disco_hash(DiscoEls, Algo) ->
|
||||
Concat = [concat_identities(DiscoEls),
|
||||
concat_features(DiscoEls),
|
||||
concat_info(DiscoEls)],
|
||||
base64:encode_to_string(
|
||||
if Algo == md5 ->
|
||||
crypto:md5(Concat);
|
||||
Algo == sha1 ->
|
||||
crypto:sha(Concat);
|
||||
Algo == sha224 ->
|
||||
sha:sha224(Concat);
|
||||
Algo == sha256 ->
|
||||
sha:sha256(Concat);
|
||||
Algo == sha384 ->
|
||||
sha:sha384(Concat);
|
||||
Algo == sha512 ->
|
||||
sha:sha512(Concat)
|
||||
end).
|
||||
|
||||
check_hash(Caps, Els) ->
|
||||
case Caps#caps.hash of
|
||||
"md5" ->
|
||||
Caps#caps.version == make_disco_hash(Els, md5);
|
||||
"sha-1" ->
|
||||
Caps#caps.version == make_disco_hash(Els, sha1);
|
||||
"sha-224" ->
|
||||
Caps#caps.version == make_disco_hash(Els, sha224);
|
||||
"sha-256" ->
|
||||
Caps#caps.version == make_disco_hash(Els, sha256);
|
||||
"sha-384" ->
|
||||
Caps#caps.version == make_disco_hash(Els, sha384);
|
||||
"sha-512" ->
|
||||
Caps#caps.version == make_disco_hash(Els, sha512);
|
||||
_ ->
|
||||
true
|
||||
end.
|
||||
-endif.
|
||||
|
||||
concat_features(Els) ->
|
||||
lists:usort(
|
||||
lists:flatmap(
|
||||
fun({xmlelement, "feature", Attrs, _}) ->
|
||||
[[xml:get_attr_s("var", Attrs), $<]];
|
||||
(_) ->
|
||||
[]
|
||||
end, Els)).
|
||||
|
||||
concat_identities(Els) ->
|
||||
lists:sort(
|
||||
lists:flatmap(
|
||||
fun({xmlelement, "identity", Attrs, _}) ->
|
||||
[[xml:get_attr_s("category", Attrs), $/,
|
||||
xml:get_attr_s("type", Attrs), $/,
|
||||
xml:get_attr_s("xml:lang", Attrs), $/,
|
||||
xml:get_attr_s("name", Attrs), $<]];
|
||||
(_) ->
|
||||
[]
|
||||
end, Els)).
|
||||
|
||||
concat_info(Els) ->
|
||||
lists:sort(
|
||||
lists:flatmap(
|
||||
fun({xmlelement, "x", Attrs, Fields}) ->
|
||||
case {xml:get_attr_s("xmlns", Attrs),
|
||||
xml:get_attr_s("type", Attrs)} of
|
||||
{?NS_XDATA, "result"} ->
|
||||
[concat_xdata_fields(Fields)];
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
(_) ->
|
||||
[]
|
||||
end, Els)).
|
||||
|
||||
concat_xdata_fields(Fields) ->
|
||||
[Form, Res] =
|
||||
lists:foldl(
|
||||
fun({xmlelement, "field", Attrs, Els} = El,
|
||||
[FormType, VarFields] = Acc) ->
|
||||
case xml:get_attr_s("var", Attrs) of
|
||||
"" ->
|
||||
Acc;
|
||||
"FORM_TYPE" ->
|
||||
[xml:get_subtag_cdata(El, "value"), VarFields];
|
||||
Var ->
|
||||
[FormType,
|
||||
[[[Var, $<],
|
||||
lists:sort(
|
||||
lists:flatmap(
|
||||
fun({xmlelement, "value", _, VEls}) ->
|
||||
[[xml:get_cdata(VEls), $<]];
|
||||
(_) ->
|
||||
[]
|
||||
end, Els))] | VarFields]]
|
||||
end;
|
||||
(_, Acc) ->
|
||||
Acc
|
||||
end, ["", []], Fields),
|
||||
[Form, $<, lists:sort(Res)].
|
||||
|
@ -237,7 +237,23 @@ get_local_features(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS]);
|
||||
["config", _] ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS]);
|
||||
["http:" | _] ->
|
||||
?NS_ADMINL("add-user") ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS]);
|
||||
?NS_ADMINL("delete-user") ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS]);
|
||||
?NS_ADMINL("end-user-session") ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS]);
|
||||
?NS_ADMINL("get-user-password") ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS]);
|
||||
?NS_ADMINL("change-user-password") ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS]);
|
||||
?NS_ADMINL("get-user-lastlogin") ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS]);
|
||||
?NS_ADMINL("user-stats") ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS]);
|
||||
?NS_ADMINL("get-registered-users-num") ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS]);
|
||||
?NS_ADMINL("get-online-users-num") ->
|
||||
?INFO_RESULT(Allow, [?NS_COMMANDS]);
|
||||
_ ->
|
||||
Acc
|
||||
@ -449,7 +465,23 @@ get_local_items(Acc, From, #jid{lserver = LServer} = To, Node, Lang) ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
|
||||
["config", _] ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
|
||||
?NS_ADMINL(_) ->
|
||||
?NS_ADMINL("add-user") ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
|
||||
?NS_ADMINL("delete-user") ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
|
||||
?NS_ADMINL("end-user-session") ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
|
||||
?NS_ADMINL("get-user-password") ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
|
||||
?NS_ADMINL("change-user-password") ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
|
||||
?NS_ADMINL("get-user-lastlogin") ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
|
||||
?NS_ADMINL("user-stats") ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
|
||||
?NS_ADMINL("get-registered-users-num") ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
|
||||
?NS_ADMINL("get-online-users-num") ->
|
||||
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
|
||||
_ ->
|
||||
Acc
|
||||
|
@ -172,7 +172,7 @@ process_local_iq_info(From, To, #iq{type = Type, lang = Lang,
|
||||
[{"xmlns", ?NS_DISCO_INFO} | ANode],
|
||||
Identity ++
|
||||
Info ++
|
||||
lists:map(fun feature_to_xml/1, Features)
|
||||
features_to_xml(Features)
|
||||
}]};
|
||||
{error, Error} ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
||||
@ -209,10 +209,16 @@ get_local_features(Acc, _From, _To, _Node, _Lang) ->
|
||||
end.
|
||||
|
||||
|
||||
feature_to_xml({{Feature, _Host}}) ->
|
||||
feature_to_xml(Feature);
|
||||
feature_to_xml(Feature) when is_list(Feature) ->
|
||||
{xmlelement, "feature", [{"var", Feature}], []}.
|
||||
features_to_xml(FeatureList) ->
|
||||
%% Avoid duplicating features
|
||||
[{xmlelement, "feature", [{"var", Feat}], []} ||
|
||||
Feat <- lists:usort(
|
||||
lists:map(
|
||||
fun({{Feature, _Host}}) ->
|
||||
Feature;
|
||||
(Feature) when is_list(Feature) ->
|
||||
Feature
|
||||
end, FeatureList))].
|
||||
|
||||
domain_to_xml({Domain}) ->
|
||||
{xmlelement, "item", [{"jid", Domain}], []};
|
||||
@ -263,6 +269,8 @@ process_sm_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ
|
||||
set ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
case is_presence_subscribed(From, To) of
|
||||
true ->
|
||||
Host = To#jid.lserver,
|
||||
Node = xml:get_tag_attr_s("node", SubEl),
|
||||
case ejabberd_hooks:run_fold(disco_sm_items,
|
||||
@ -281,23 +289,27 @@ process_sm_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ
|
||||
}]};
|
||||
{error, Error} ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
||||
end;
|
||||
false ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]}
|
||||
end
|
||||
end.
|
||||
|
||||
get_sm_items({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc;
|
||||
|
||||
get_sm_items(Acc,
|
||||
#jid{luser = LFrom, lserver = LSFrom},
|
||||
#jid{user = User, server = Server, luser = LTo, lserver = LSTo} = _To,
|
||||
get_sm_items(Acc, From,
|
||||
#jid{user = User, server = Server} = To,
|
||||
[], _Lang) ->
|
||||
Items = case Acc of
|
||||
{result, Its} -> Its;
|
||||
empty -> []
|
||||
end,
|
||||
Items1 = case {LFrom, LSFrom} of
|
||||
{LTo, LSTo} -> get_user_resources(User, Server);
|
||||
_ -> []
|
||||
Items1 = case is_presence_subscribed(From, To) of
|
||||
true ->
|
||||
get_user_resources(User, Server);
|
||||
_ ->
|
||||
[]
|
||||
end,
|
||||
{result, Items ++ Items1};
|
||||
|
||||
@ -314,11 +326,25 @@ get_sm_items(empty, From, To, _Node, _Lang) ->
|
||||
{error, ?ERR_NOT_ALLOWED}
|
||||
end.
|
||||
|
||||
is_presence_subscribed(#jid{luser=User, lserver=Server}, #jid{luser=LUser, lserver=LServer}) ->
|
||||
lists:any(fun({roster, _, _, {TUser, TServer, _}, _, S, _, _, _, _}) ->
|
||||
if
|
||||
LUser == TUser, LServer == TServer, S/=none ->
|
||||
true;
|
||||
true ->
|
||||
false
|
||||
end
|
||||
end,
|
||||
ejabberd_hooks:run_fold(roster_get, Server, [], [{User, Server}]))
|
||||
orelse User == LUser andalso Server == LServer.
|
||||
|
||||
process_sm_iq_info(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
|
||||
case Type of
|
||||
set ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
get ->
|
||||
case is_presence_subscribed(From, To) of
|
||||
true ->
|
||||
Host = To#jid.lserver,
|
||||
Node = xml:get_tag_attr_s("node", SubEl),
|
||||
Identity = ejabberd_hooks:run_fold(disco_sm_identity,
|
||||
@ -338,15 +364,25 @@ process_sm_iq_info(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ)
|
||||
sub_el = [{xmlelement, "query",
|
||||
[{"xmlns", ?NS_DISCO_INFO} | ANode],
|
||||
Identity ++
|
||||
lists:map(fun feature_to_xml/1, Features)
|
||||
features_to_xml(Features)
|
||||
}]};
|
||||
{error, Error} ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, Error]}
|
||||
end;
|
||||
false ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]}
|
||||
end
|
||||
end.
|
||||
|
||||
get_sm_identity(Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc.
|
||||
get_sm_identity(Acc, _From, #jid{luser = LUser, lserver=LServer}, _Node, _Lang) ->
|
||||
Acc ++ case ejabberd_auth:is_user_exists(LUser, LServer) of
|
||||
true ->
|
||||
[{xmlelement, "identity", [{"category", "account"},
|
||||
{"type", "registered"}], []}];
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
|
||||
get_sm_features(empty, From, To, _Node, _Lang) ->
|
||||
#jid{luser = LFrom, lserver = LSFrom} = From,
|
||||
@ -373,7 +409,13 @@ get_user_resources(User, Server) ->
|
||||
|
||||
%%% Support for: XEP-0157 Contact Addresses for XMPP Services
|
||||
|
||||
get_info(_A, Host, Module, Node, _Lang) when Node == [] ->
|
||||
get_info(_A, Host, Mod, Node, _Lang) when Node == [] ->
|
||||
Module = case Mod of
|
||||
undefined ->
|
||||
?MODULE;
|
||||
_ ->
|
||||
Mod
|
||||
end,
|
||||
Serverinfo_fields = get_fields_xml(Host, Module),
|
||||
[{xmlelement, "x",
|
||||
[{"xmlns", ?NS_XDATA}, {"type", "result"}],
|
||||
@ -387,8 +429,8 @@ get_info(_A, Host, Module, Node, _Lang) when Node == [] ->
|
||||
++ Serverinfo_fields
|
||||
}];
|
||||
|
||||
get_info(_, _, _, _Node, _) ->
|
||||
[].
|
||||
get_info(Acc, _, _, _Node, _) ->
|
||||
Acc.
|
||||
|
||||
get_fields_xml(Host, Module) ->
|
||||
Fields = gen_mod:get_module_opt(Host, ?MODULE, server_info, []),
|
||||
|
@ -64,15 +64,15 @@ static int iconv_erl_control(ErlDrvData drv_data,
|
||||
ei_decode_version(buf, &index, &i);
|
||||
ei_decode_tuple_header(buf, &index, &i);
|
||||
ei_get_type(buf, &index, &i, &size);
|
||||
from = malloc(size + 1);
|
||||
from = driver_alloc(size + 1);
|
||||
ei_decode_string(buf, &index, from);
|
||||
|
||||
ei_get_type(buf, &index, &i, &size);
|
||||
to = malloc(size + 1);
|
||||
to = driver_alloc(size + 1);
|
||||
ei_decode_string(buf, &index, to);
|
||||
|
||||
ei_get_type(buf, &index, &i, &size);
|
||||
stmp = string = malloc(size + 1);
|
||||
stmp = string = driver_alloc(size + 1);
|
||||
ei_decode_string(buf, &index, string);
|
||||
|
||||
/* Special mode: parse as UTF-8 if possible; otherwise assume it's
|
||||
@ -92,9 +92,9 @@ static int iconv_erl_control(ErlDrvData drv_data,
|
||||
*rbuf = (char*)(b = driver_alloc_binary(size));
|
||||
memcpy(b->orig_bytes, string, size);
|
||||
|
||||
free(from);
|
||||
free(to);
|
||||
free(string);
|
||||
driver_free(from);
|
||||
driver_free(to);
|
||||
driver_free(string);
|
||||
|
||||
return size;
|
||||
}
|
||||
@ -102,7 +102,7 @@ static int iconv_erl_control(ErlDrvData drv_data,
|
||||
|
||||
outleft = avail = 4*size;
|
||||
inleft = size;
|
||||
rtmp = rstring = malloc(avail);
|
||||
rtmp = rstring = driver_alloc(avail);
|
||||
while (inleft > 0) {
|
||||
if (iconv(cd, &stmp, &inleft, &rtmp, &outleft) == (size_t) -1) {
|
||||
if (invalid_utf8_as_latin1 && (*stmp & 0x80) && outleft >= 2) {
|
||||
@ -121,10 +121,10 @@ static int iconv_erl_control(ErlDrvData drv_data,
|
||||
*rbuf = (char*)(b = driver_alloc_binary(size));
|
||||
memcpy(b->orig_bytes, rstring, size);
|
||||
|
||||
free(from);
|
||||
free(to);
|
||||
free(string);
|
||||
free(rstring);
|
||||
driver_free(from);
|
||||
driver_free(to);
|
||||
driver_free(string);
|
||||
driver_free(rstring);
|
||||
iconv_close(cd);
|
||||
|
||||
return size;
|
||||
|
@ -1,7 +1,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_last.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : jabber:iq:last support (JEP-0012)
|
||||
%%% Purpose : jabber:iq:last support (XEP-0012)
|
||||
%%% Created : 24 Oct 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
@ -127,7 +127,7 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
[User, Server, UserListRecord,
|
||||
{From, To,
|
||||
{xmlelement, "presence", [], []}},
|
||||
out]) of
|
||||
in]) of
|
||||
allow ->
|
||||
get_last(IQ, SubEl, User, Server);
|
||||
deny ->
|
||||
|
@ -1,7 +1,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_last_odbc.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : jabber:iq:last support (JEP-0012)
|
||||
%%% Purpose : jabber:iq:last support (XEP-0012)
|
||||
%%% Created : 24 Oct 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
@ -119,7 +119,7 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
|
||||
[User, Server, UserListRecord,
|
||||
{From, To,
|
||||
{xmlelement, "presence", [], []}},
|
||||
out]) of
|
||||
in]) of
|
||||
allow ->
|
||||
get_last(IQ, SubEl, User, Server);
|
||||
deny ->
|
||||
|
@ -1,7 +1,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_muc.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : MUC support (JEP-0045)
|
||||
%%% Purpose : MUC support (XEP-0045)
|
||||
%%% Created : 19 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
@ -336,7 +336,7 @@ do_route(Host, ServerHost, Access, HistorySize, RoomShaper,
|
||||
ErrText = "Access denied by service policy",
|
||||
Err = jlib:make_error_reply(Packet,
|
||||
?ERRT_FORBIDDEN(Lang, ErrText)),
|
||||
ejabberd_router:route(To, From, Err)
|
||||
ejabberd_router:route_error(To, From, Err, Packet)
|
||||
end.
|
||||
|
||||
|
||||
|
@ -860,7 +860,9 @@ roomconfig_to_string(Options, Lang, FileFormat) ->
|
||||
T ->
|
||||
case Opt of
|
||||
password -> "<div class=\"rcoe\">" ++ OptText ++ "</div>";
|
||||
title -> "<div class=\"rcot\">" ++ ?T("Room title") ++ ": \"" ++ htmlize(T, FileFormat) ++ "\"</div>";
|
||||
max_users -> "<div class=\"rcot\">" ++ OptText ++ ": \"" ++ htmlize(integer_to_list(T), FileFormat) ++ "\"</div>";
|
||||
title -> "<div class=\"rcot\">" ++ OptText ++ ": \"" ++ htmlize(T, FileFormat) ++ "\"</div>";
|
||||
description -> "<div class=\"rcot\">" ++ OptText ++ ": \"" ++ htmlize(T, FileFormat) ++ "\"</div>";
|
||||
_ -> "\"" ++ T ++ "\""
|
||||
end
|
||||
end,
|
||||
@ -876,7 +878,7 @@ get_roomconfig_text(public) -> "Make room public searchable";
|
||||
get_roomconfig_text(public_list) -> "Make participants list public";
|
||||
get_roomconfig_text(password_protected) -> "Make room password protected";
|
||||
get_roomconfig_text(password) -> "Password";
|
||||
get_roomconfig_text(anonymous) -> "Make room semianonymous";
|
||||
get_roomconfig_text(anonymous) -> "This room is not anonymous";
|
||||
get_roomconfig_text(members_only) -> "Make room members-only";
|
||||
get_roomconfig_text(moderated) -> "Make room moderated";
|
||||
get_roomconfig_text(members_by_default) -> "Default users as participants";
|
||||
@ -885,6 +887,13 @@ get_roomconfig_text(allow_private_messages) -> "Allow users to send private mess
|
||||
get_roomconfig_text(allow_query_users) -> "Allow users to query other users";
|
||||
get_roomconfig_text(allow_user_invites) -> "Allow users to send invites";
|
||||
get_roomconfig_text(logging) -> "Enable logging";
|
||||
get_roomconfig_text(allow_visitor_nickchange) -> "Allow visitors to change nickname";
|
||||
get_roomconfig_text(allow_visitor_status) -> "Allow visitors to send status text in presence updates";
|
||||
get_roomconfig_text(captcha_protected) -> "Make room captcha protected";
|
||||
get_roomconfig_text(description) -> "Room description";
|
||||
%% get_roomconfig_text(subject) -> "Subject";
|
||||
%% get_roomconfig_text(subject_author) -> "Subject author";
|
||||
get_roomconfig_text(max_users) -> "Maximum Number of Occupants";
|
||||
get_roomconfig_text(_) -> undefined.
|
||||
|
||||
%% Users = [{JID, Nick, Role}]
|
||||
|
@ -162,7 +162,7 @@ normal_state({route, From, "",
|
||||
trunc(gen_mod:get_module_opt(
|
||||
StateData#state.server_host,
|
||||
mod_muc, min_message_interval, 0) * 1000000),
|
||||
Size = lists:flatlength(xml:element_to_string(Packet)),
|
||||
Size = element_size(Packet),
|
||||
{MessageShaper, MessageShaperInterval} =
|
||||
shaper:update(Activity#activity.message_shaper, Size),
|
||||
if
|
||||
@ -593,35 +593,8 @@ handle_event(_Event, StateName, StateData) ->
|
||||
%% {stop, Reason, Reply, NewStateData}
|
||||
%%----------------------------------------------------------------------
|
||||
handle_sync_event({get_disco_item, JID, Lang}, _From, StateName, StateData) ->
|
||||
FAffiliation = get_affiliation(JID, StateData),
|
||||
FRole = get_role(JID, StateData),
|
||||
Tail =
|
||||
case ((StateData#state.config)#config.public_list == true) orelse
|
||||
(FRole /= none) orelse
|
||||
(FAffiliation == admin) orelse
|
||||
(FAffiliation == owner) of
|
||||
true ->
|
||||
Desc = case (StateData#state.config)#config.public of
|
||||
true ->
|
||||
"";
|
||||
_ ->
|
||||
translate:translate(Lang, "private, ")
|
||||
end,
|
||||
Len = ?DICT:fold(fun(_, _, Acc) -> Acc + 1 end, 0,
|
||||
StateData#state.users),
|
||||
" (" ++ Desc ++ integer_to_list(Len) ++ ")";
|
||||
_ ->
|
||||
" (n/a)"
|
||||
end,
|
||||
Reply = case ((StateData#state.config)#config.public == true) orelse
|
||||
(FRole /= none) orelse
|
||||
(FAffiliation == admin) orelse
|
||||
(FAffiliation == owner) of
|
||||
true ->
|
||||
{item, get_title(StateData) ++ Tail};
|
||||
_ ->
|
||||
false
|
||||
end,
|
||||
Reply = get_roomdesc_reply(JID, StateData,
|
||||
get_roomdesc_tail(StateData, Lang)),
|
||||
{reply, Reply, StateName, StateData};
|
||||
handle_sync_event(get_config, _From, StateName, StateData) ->
|
||||
{reply, {ok, StateData#state.config}, StateName, StateData};
|
||||
@ -1000,6 +973,18 @@ is_user_online(JID, StateData) ->
|
||||
LJID = jlib:jid_tolower(JID),
|
||||
?DICT:is_key(LJID, StateData#state.users).
|
||||
|
||||
%% Check if the user is occupant of the room, or at least is an admin or owner.
|
||||
is_occupant_or_admin(JID, StateData) ->
|
||||
FAffiliation = get_affiliation(JID, StateData),
|
||||
FRole = get_role(JID, StateData),
|
||||
case (FRole /= none) orelse
|
||||
(FAffiliation == admin) orelse
|
||||
(FAffiliation == owner) of
|
||||
true ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
%%%
|
||||
%%% Handle IQ queries of vCard
|
||||
@ -1409,7 +1394,7 @@ prepare_room_queue(StateData) ->
|
||||
{{value, {message, From}}, _RoomQueue} ->
|
||||
Activity = get_user_activity(From, StateData),
|
||||
Packet = Activity#activity.message,
|
||||
Size = lists:flatlength(xml:element_to_string(Packet)),
|
||||
Size = element_size(Packet),
|
||||
{RoomShaper, RoomShaperInterval} =
|
||||
shaper:update(StateData#state.room_shaper, Size),
|
||||
erlang:send_after(
|
||||
@ -1420,7 +1405,7 @@ prepare_room_queue(StateData) ->
|
||||
{{value, {presence, From}}, _RoomQueue} ->
|
||||
Activity = get_user_activity(From, StateData),
|
||||
{_Nick, Packet} = Activity#activity.presence,
|
||||
Size = lists:flatlength(xml:element_to_string(Packet)),
|
||||
Size = element_size(Packet),
|
||||
{RoomShaper, RoomShaperInterval} =
|
||||
shaper:update(StateData#state.room_shaper, Size),
|
||||
erlang:send_after(
|
||||
@ -1902,10 +1887,18 @@ send_new_presence(NJID, Reason, StateData) ->
|
||||
false ->
|
||||
[]
|
||||
end,
|
||||
Status2 = case ((StateData#state.config)#config.anonymous==false)
|
||||
andalso (NJID == Info#user.jid) of
|
||||
true ->
|
||||
[{xmlelement, "status", [{"code", "100"}], []}
|
||||
| Status];
|
||||
false ->
|
||||
Status
|
||||
end,
|
||||
Packet = xml:append_subtags(
|
||||
Presence,
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
|
||||
[{xmlelement, "item", ItemAttrs, ItemEls} | Status]}]),
|
||||
[{xmlelement, "item", ItemAttrs, ItemEls} | Status2]}]),
|
||||
ejabberd_router:route(
|
||||
jlib:jid_replace_resource(StateData#state.jid, Nick),
|
||||
Info#user.jid,
|
||||
@ -2083,7 +2076,7 @@ add_message_to_history(FromNick, FromJID, Packet, StateData) ->
|
||||
jlib:jid_replace_resource(StateData#state.jid, FromNick),
|
||||
StateData#state.jid,
|
||||
TSPacket),
|
||||
Size = lists:flatlength(xml:element_to_string(SPacket)),
|
||||
Size = element_size(SPacket),
|
||||
Q1 = lqueue_in({FromNick, TSPacket, HaveSubject, TimeStamp, Size},
|
||||
StateData#state.history),
|
||||
add_to_log(text, {FromNick, Packet}, StateData),
|
||||
@ -3329,28 +3322,16 @@ process_iq_disco_items(_From, set, _Lang, _StateData) ->
|
||||
{error, ?ERR_NOT_ALLOWED};
|
||||
|
||||
process_iq_disco_items(From, get, _Lang, StateData) ->
|
||||
FAffiliation = get_affiliation(From, StateData),
|
||||
FRole = get_role(From, StateData),
|
||||
case ((StateData#state.config)#config.public_list == true) orelse
|
||||
(FRole /= none) orelse
|
||||
(FAffiliation == admin) orelse
|
||||
(FAffiliation == owner) of
|
||||
case (StateData#state.config)#config.public_list of
|
||||
true ->
|
||||
UList =
|
||||
lists:map(
|
||||
fun({_LJID, Info}) ->
|
||||
Nick = Info#user.nick,
|
||||
{xmlelement, "item",
|
||||
[{"jid", jlib:jid_to_string(
|
||||
{StateData#state.room,
|
||||
StateData#state.host,
|
||||
Nick})},
|
||||
{"name", Nick}], []}
|
||||
end,
|
||||
?DICT:to_list(StateData#state.users)),
|
||||
{result, UList, StateData};
|
||||
{result, get_mucroom_disco_items(StateData), StateData};
|
||||
_ ->
|
||||
case is_occupant_or_admin(From, StateData) of
|
||||
true ->
|
||||
{result, get_mucroom_disco_items(StateData), StateData};
|
||||
_ ->
|
||||
{error, ?ERR_FORBIDDEN}
|
||||
end
|
||||
end.
|
||||
|
||||
process_iq_captcha(_From, get, _Lang, _SubEl, _StateData) ->
|
||||
@ -3372,6 +3353,38 @@ get_title(StateData) ->
|
||||
Name
|
||||
end.
|
||||
|
||||
get_roomdesc_reply(JID, StateData, Tail) ->
|
||||
IsOccupantOrAdmin = is_occupant_or_admin(JID, StateData),
|
||||
if (StateData#state.config)#config.public or IsOccupantOrAdmin ->
|
||||
if (StateData#state.config)#config.public_list or IsOccupantOrAdmin ->
|
||||
{item, get_title(StateData) ++ Tail};
|
||||
true ->
|
||||
{item, get_title(StateData)}
|
||||
end;
|
||||
true ->
|
||||
false
|
||||
end.
|
||||
|
||||
get_roomdesc_tail(StateData, Lang) ->
|
||||
Desc = case (StateData#state.config)#config.public of
|
||||
true ->
|
||||
"";
|
||||
_ ->
|
||||
translate:translate(Lang, "private, ")
|
||||
end,
|
||||
Len = ?DICT:fold(fun(_, _, Acc) -> Acc + 1 end, 0, StateData#state.users),
|
||||
" (" ++ Desc ++ integer_to_list(Len) ++ ")".
|
||||
|
||||
get_mucroom_disco_items(StateData) ->
|
||||
lists:map(
|
||||
fun({_LJID, Info}) ->
|
||||
Nick = Info#user.nick,
|
||||
{xmlelement, "item",
|
||||
[{"jid", jlib:jid_to_string({StateData#state.room,
|
||||
StateData#state.host, Nick})},
|
||||
{"name", Nick}], []}
|
||||
end,
|
||||
?DICT:to_list(StateData#state.users)).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% Invitation support
|
||||
@ -3577,3 +3590,6 @@ tab_count_user(JID) ->
|
||||
_ ->
|
||||
0
|
||||
end.
|
||||
|
||||
element_size(El) ->
|
||||
size(xml:element_to_binary(El)).
|
||||
|
@ -126,8 +126,7 @@ loop(Host, AccessMaxOfflineMsgs) ->
|
||||
M#offline_msg.timestamp))]},
|
||||
XML =
|
||||
ejabberd_odbc:escape(
|
||||
lists:flatten(
|
||||
xml:element_to_string(Packet))),
|
||||
xml:element_to_binary(Packet)),
|
||||
odbc_queries:add_spool_sql(Username, XML)
|
||||
end, Msgs),
|
||||
case catch odbc_queries:add_spool(Host, Query) of
|
||||
|
@ -726,11 +726,11 @@ raw_to_item({SType, SValue, SAction, SOrder, SMatchAll, SMatchIQ,
|
||||
"d" -> deny
|
||||
end,
|
||||
Order = list_to_integer(SOrder),
|
||||
MatchAll = SMatchAll == "1" orelse SMatchAll == "t",
|
||||
MatchIQ = SMatchIQ == "1" orelse SMatchIQ == "t" ,
|
||||
MatchMessage = SMatchMessage == "1" orelse SMatchMessage == "t",
|
||||
MatchPresenceIn = SMatchPresenceIn == "1" orelse SMatchPresenceIn == "t",
|
||||
MatchPresenceOut = SMatchPresenceOut == "1" orelse SMatchPresenceOut == "t",
|
||||
MatchAll = ejabberd_odbc:to_bool(SMatchAll),
|
||||
MatchIQ = ejabberd_odbc:to_bool(SMatchIQ),
|
||||
MatchMessage = ejabberd_odbc:to_bool(SMatchMessage),
|
||||
MatchPresenceIn = ejabberd_odbc:to_bool(SMatchPresenceIn),
|
||||
MatchPresenceOut = ejabberd_odbc:to_bool(SMatchPresenceOut),
|
||||
#listitem{type = Type,
|
||||
value = Value,
|
||||
action = Action,
|
||||
|
@ -92,7 +92,7 @@ set_data(LUser, LServer, El) ->
|
||||
Username = ejabberd_odbc:escape(LUser),
|
||||
LXMLNS = ejabberd_odbc:escape(XMLNS),
|
||||
SData = ejabberd_odbc:escape(
|
||||
lists:flatten(xml:element_to_string(El))),
|
||||
xml:element_to_binary(El)),
|
||||
odbc_queries:set_private_data(LServer, Username, LXMLNS, SData)
|
||||
end;
|
||||
_ ->
|
||||
|
@ -73,8 +73,7 @@
|
||||
disco_sm_items/5
|
||||
]).
|
||||
%% exported iq handlers
|
||||
-export([iq_local/3,
|
||||
iq_sm/3
|
||||
-export([iq_sm/3
|
||||
]).
|
||||
|
||||
%% exports for console debug manual use
|
||||
@ -183,9 +182,8 @@ init([ServerHost, Opts]) ->
|
||||
pubsub_index:init(Host, ServerHost, Opts),
|
||||
ets:new(gen_mod:get_module_proc(Host, config), [set, named_table]),
|
||||
ets:new(gen_mod:get_module_proc(ServerHost, config), [set, named_table]),
|
||||
ets:new(gen_mod:get_module_proc(Host, last_items), [set, named_table]),
|
||||
ets:new(gen_mod:get_module_proc(ServerHost, last_items), [set, named_table]),
|
||||
{Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts),
|
||||
mnesia:create_table(pubsub_last_item, [{ram_copies, [node()]}, {attributes, record_info(fields, pubsub_last_item)}]),
|
||||
mod_disco:register_feature(ServerHost, ?NS_PUBSUB),
|
||||
ets:insert(gen_mod:get_module_proc(Host, config), {nodetree, NodeTree}),
|
||||
ets:insert(gen_mod:get_module_proc(Host, config), {plugins, Plugins}),
|
||||
@ -199,24 +197,22 @@ init([ServerHost, Opts]) ->
|
||||
ets:insert(gen_mod:get_module_proc(ServerHost, config), {ignore_pep_from_offline, PepOffline}),
|
||||
ets:insert(gen_mod:get_module_proc(ServerHost, config), {host, Host}),
|
||||
ejabberd_hooks:add(sm_remove_connection_hook, ServerHost, ?MODULE, on_user_offline, 75),
|
||||
ejabberd_hooks:add(disco_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75),
|
||||
ejabberd_hooks:add(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
|
||||
ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
|
||||
ejabberd_hooks:add(disco_local_identity, ServerHost, ?MODULE, disco_local_identity, 75),
|
||||
ejabberd_hooks:add(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75),
|
||||
ejabberd_hooks:add(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75),
|
||||
ejabberd_hooks:add(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 80),
|
||||
ejabberd_hooks:add(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50),
|
||||
ejabberd_hooks:add(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50),
|
||||
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE, remove_user, 50),
|
||||
ejabberd_hooks:add(anonymous_purge_hook, ServerHost, ?MODULE, remove_user, 50),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB, ?MODULE, iq_sm, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER, ?MODULE, iq_sm, IQDisc),
|
||||
case lists:member(?PEPNODE, Plugins) of
|
||||
true ->
|
||||
ejabberd_hooks:add(feature_check_packet, ServerHost, ?MODULE, feature_check_packet, 75),
|
||||
ejabberd_hooks:add(disco_local_identity, ServerHost, ?MODULE, disco_local_identity, 75),
|
||||
ejabberd_hooks:add(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75),
|
||||
ejabberd_hooks:add(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB, ?MODULE, iq_local, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB_OWNER, ?MODULE, iq_local, IQDisc);
|
||||
ejabberd_hooks:add(disco_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75),
|
||||
ejabberd_hooks:add(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
|
||||
ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB, ?MODULE, iq_sm, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER, ?MODULE, iq_sm, IQDisc);
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
@ -585,15 +581,12 @@ send_loop(State) ->
|
||||
%% disco hooks handling functions
|
||||
%%
|
||||
|
||||
identity(Host) ->
|
||||
Identity = case lists:member(?PEPNODE, plugins(Host)) of
|
||||
true -> [{"category", "pubsub"}, {"type", "pep"}];
|
||||
false -> [{"category", "pubsub"}, {"type", "service"}]
|
||||
end,
|
||||
{xmlelement, "identity", Identity, []}.
|
||||
|
||||
disco_local_identity(Acc, _From, To, [], _Lang) ->
|
||||
Acc ++ [identity(To#jid.lserver)];
|
||||
case lists:member(?PEPNODE, plugins(To#jid.lserver)) of
|
||||
true ->
|
||||
[{xmlelement, "identity", [{"category", "pubsub"}, {"type", "pep"}], []} | Acc];
|
||||
false -> Acc
|
||||
end;
|
||||
disco_local_identity(Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc.
|
||||
|
||||
@ -614,76 +607,116 @@ disco_local_items(Acc, _From, _To, [], _Lang) ->
|
||||
disco_local_items(Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc.
|
||||
|
||||
disco_sm_identity(Acc, _From, To, [], _Lang) ->
|
||||
Acc ++ [identity(To#jid.lserver)];
|
||||
disco_sm_identity(Acc, From, To, Node, Lang) when is_list(Node) ->
|
||||
disco_sm_identity(Acc, From, To, list_to_binary(Node), Lang);
|
||||
disco_sm_identity(empty, From, To, Node, Lang) ->
|
||||
disco_sm_identity([], From, To, Node, Lang);
|
||||
disco_sm_identity(Acc, From, To, Node, _Lang) ->
|
||||
LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)),
|
||||
Acc ++ case node_disco_identity(LOwner, From, Node) of
|
||||
{result, I} -> I;
|
||||
_ -> []
|
||||
end.
|
||||
disco_identity(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From) ++ Acc.
|
||||
|
||||
disco_sm_features(Acc, _From, _To, [], _Lang) ->
|
||||
Acc;
|
||||
disco_sm_features(Acc, From, To, Node, _Lang) ->
|
||||
LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)),
|
||||
Features = node_disco_features(LOwner, From, Node),
|
||||
case {Acc, Features} of
|
||||
{{result, AccFeatures}, {result, AddFeatures}} ->
|
||||
{result, AccFeatures++AddFeatures};
|
||||
{_, {result, AddFeatures}} ->
|
||||
{result, AddFeatures};
|
||||
{_, _} ->
|
||||
Acc
|
||||
end.
|
||||
|
||||
disco_sm_items(Acc, From, To, [], _Lang) ->
|
||||
Host = To#jid.lserver,
|
||||
case tree_action(Host, get_subnodes, [Host, <<>>, From]) of
|
||||
[] ->
|
||||
Acc;
|
||||
Nodes ->
|
||||
SBJID = jlib:jid_to_string(jlib:jid_remove_resource(To)),
|
||||
Items = case Acc of
|
||||
{result, I} -> I;
|
||||
_ -> []
|
||||
end,
|
||||
NodeItems = lists:map(
|
||||
fun(#pubsub_node{nodeid = {_, Node}}) ->
|
||||
{xmlelement, "item",
|
||||
[{"jid", SBJID}|nodeAttr(Node)],
|
||||
[]}
|
||||
end, Nodes),
|
||||
{result, NodeItems ++ Items}
|
||||
end;
|
||||
|
||||
disco_sm_items(Acc, From, To, SNode, _Lang) ->
|
||||
Host = To#jid.lserver,
|
||||
Node = string_to_node(SNode),
|
||||
Action = fun(#pubsub_node{type = Type, id = NodeId}) ->
|
||||
% TODO call get_items/6 instead for access control (EJAB-1033)
|
||||
case node_call(Type, get_items, [NodeId, From]) of
|
||||
{result, []} ->
|
||||
none;
|
||||
{result, AllItems} ->
|
||||
SBJID = jlib:jid_to_string(jlib:jid_remove_resource(To)),
|
||||
Items = case Acc of
|
||||
{result, I} -> I;
|
||||
_ -> []
|
||||
end,
|
||||
NodeItems = lists:map(
|
||||
fun(#pubsub_item{itemid = {Id, _}}) ->
|
||||
{result, Name} = node_call(Type, get_item_name, [Host, Node, Id]),
|
||||
{xmlelement, "item", [{"jid", SBJID}, {"name", Name}], []}
|
||||
end, AllItems),
|
||||
{result, NodeItems ++ Items};
|
||||
_ ->
|
||||
none
|
||||
disco_identity(_Host, <<>>, _From) ->
|
||||
[{xmlelement, "identity", [{"category", "pubsub"}, {"type", "pep"}], []}];
|
||||
disco_identity(Host, Node, From) ->
|
||||
Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) ->
|
||||
case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
|
||||
{result, _} ->
|
||||
{result, [{xmlelement, "identity", [{"category", "pubsub"}, {"type", "pep"}], []},
|
||||
{xmlelement, "identity",
|
||||
[{"category", "pubsub"},
|
||||
{"type", "leaf"}
|
||||
| case get_option(Options, title) of
|
||||
false -> [];
|
||||
[Title] -> [{"name", Title}]
|
||||
end],
|
||||
[]}]};
|
||||
_ -> {result, []}
|
||||
end
|
||||
end,
|
||||
case transaction(Host, Node, Action, sync_dirty) of
|
||||
{result, {_, Items}} -> {result, Items};
|
||||
{result, {_, Result}} -> Result;
|
||||
_ -> []
|
||||
end.
|
||||
|
||||
disco_sm_features(Acc, From, To, Node, Lang) when is_list(Node) ->
|
||||
disco_sm_features(Acc, From, To, list_to_binary(Node), Lang);
|
||||
disco_sm_features(empty, From, To, Node, Lang) ->
|
||||
disco_sm_features({result, []}, From, To, Node, Lang);
|
||||
disco_sm_features({result, OtherFeatures} = _Acc, From, To, Node, _Lang) ->
|
||||
{result,
|
||||
OtherFeatures ++
|
||||
disco_features(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From)};
|
||||
disco_sm_features(Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc.
|
||||
|
||||
disco_features(_Host, <<>>, _From) ->
|
||||
[?NS_PUBSUB
|
||||
| [?NS_PUBSUB++"#"++Feature || Feature <- features("pep")]];
|
||||
disco_features(Host, Node, From) ->
|
||||
Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) ->
|
||||
case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
|
||||
{result, _} ->
|
||||
{result, [?NS_PUBSUB
|
||||
| [?NS_PUBSUB ++ "#" ++ Feature || Feature <- features("pep")]]};
|
||||
_ -> {result, []}
|
||||
end
|
||||
end,
|
||||
case transaction(Host, Node, Action, sync_dirty) of
|
||||
{result, {_, Result}} -> Result;
|
||||
_ -> []
|
||||
end.
|
||||
|
||||
disco_sm_items(Acc, From, To, Node, Lang) when is_list(Node) ->
|
||||
disco_sm_items(Acc, From, To, list_to_binary(Node), Lang);
|
||||
disco_sm_items(empty, From, To, Node, Lang) ->
|
||||
disco_sm_items({result, []}, From, To, Node, Lang);
|
||||
disco_sm_items({result, OtherItems}, From, To, Node, _Lang) ->
|
||||
{result,
|
||||
lists:usort(OtherItems ++
|
||||
disco_items(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From))};
|
||||
disco_sm_items(Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc.
|
||||
|
||||
disco_items(Host, <<>>, From) ->
|
||||
Action = fun(#pubsub_node{nodeid ={_, NodeID}, options = Options, type = Type, id = Idx, owners = Owners}, Acc) ->
|
||||
case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
|
||||
{result, _} ->
|
||||
[{xmlelement, "item",
|
||||
[{"node", binary_to_list(NodeID)},
|
||||
{"jid", case Host of
|
||||
{_,_,_} -> jlib:jid_to_string(Host);
|
||||
_Host -> Host
|
||||
end}
|
||||
| case get_option(Options, title) of
|
||||
false -> [];
|
||||
[Title] -> [{"name", Title}]
|
||||
end],
|
||||
[]}
|
||||
| Acc];
|
||||
_ -> Acc
|
||||
end
|
||||
end,
|
||||
case transaction(Host, Action, sync_dirty) of
|
||||
{result, Items} -> Items;
|
||||
_ -> []
|
||||
end;
|
||||
|
||||
disco_items(Host, Node, From) ->
|
||||
Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) ->
|
||||
case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
|
||||
{result, Items} ->
|
||||
{result, [{xmlelement, "item",
|
||||
[{"jid", case Host of
|
||||
{_,_,_} -> jlib:jid_to_string(Host);
|
||||
_Host -> Host
|
||||
end},
|
||||
{"name", ItemID}], []}
|
||||
|| #pubsub_item{itemid = {ItemID,_}} <- Items]};
|
||||
_ -> {result, []}
|
||||
end
|
||||
end,
|
||||
case transaction(Host, Node, Action, sync_dirty) of
|
||||
{result, {_, Result}} -> Result;
|
||||
_ -> []
|
||||
end.
|
||||
|
||||
%% -------
|
||||
@ -859,25 +892,23 @@ terminate(_Reason, #state{host = Host,
|
||||
case lists:member(?PEPNODE, Plugins) of
|
||||
true ->
|
||||
ejabberd_hooks:delete(feature_check_packet, ServerHost, ?MODULE, feature_check_packet, 75),
|
||||
ejabberd_hooks:delete(disco_local_identity, ServerHost, ?MODULE, disco_local_identity, 75),
|
||||
ejabberd_hooks:delete(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75),
|
||||
ejabberd_hooks:delete(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB_OWNER);
|
||||
ejabberd_hooks:delete(disco_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75),
|
||||
ejabberd_hooks:delete(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
|
||||
ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER);
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
ejabberd_hooks:delete(sm_remove_connection_hook, ServerHost, ?MODULE, on_user_offline, 75),
|
||||
ejabberd_hooks:delete(disco_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75),
|
||||
ejabberd_hooks:delete(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
|
||||
ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
|
||||
ejabberd_hooks:delete(disco_local_identity, ServerHost, ?MODULE, disco_local_identity, 75),
|
||||
ejabberd_hooks:delete(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75),
|
||||
ejabberd_hooks:delete(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75),
|
||||
ejabberd_hooks:delete(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 80),
|
||||
ejabberd_hooks:delete(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50),
|
||||
ejabberd_hooks:delete(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50),
|
||||
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50),
|
||||
ejabberd_hooks:delete(anonymous_purge_hook, ServerHost, ?MODULE, remove_user, 50),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER),
|
||||
mod_disco:unregister_feature(ServerHost, ?NS_PUBSUB),
|
||||
gen_mod:get_module_proc(ServerHost, ?LOOPNAME) ! stop,
|
||||
terminate_plugins(Host, ServerHost, Plugins, TreePlugin).
|
||||
@ -1018,10 +1049,10 @@ command_disco_info(_Host, <<?NS_PUBSUB_GET_PENDING>>, _From) ->
|
||||
|
||||
node_disco_info(Host, Node, From) ->
|
||||
node_disco_info(Host, Node, From, true, true).
|
||||
node_disco_identity(Host, Node, From) ->
|
||||
node_disco_info(Host, Node, From, true, false).
|
||||
node_disco_features(Host, Node, From) ->
|
||||
node_disco_info(Host, Node, From, false, true).
|
||||
%node_disco_identity(Host, Node, From) ->
|
||||
% node_disco_info(Host, Node, From, true, false).
|
||||
%node_disco_features(Host, Node, From) ->
|
||||
% node_disco_info(Host, Node, From, false, true).
|
||||
node_disco_info(Host, Node, From, Identity, Features) ->
|
||||
Action =
|
||||
fun(#pubsub_node{type = Type, id = NodeId}) ->
|
||||
@ -1121,17 +1152,15 @@ iq_disco_items(Host, Item, From) ->
|
||||
{result, []};
|
||||
[SNode] ->
|
||||
Node = string_to_node(SNode),
|
||||
Action =
|
||||
fun(#pubsub_node{type = Type, id = NodeId}) ->
|
||||
% TODO call get_items/6 instead for access control (EJAB-1033)
|
||||
NodeItems = case node_call(Type, get_items, [NodeId, From]) of
|
||||
{result, I} -> I;
|
||||
Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) ->
|
||||
NodeItems = case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
|
||||
{result, R} -> R;
|
||||
_ -> []
|
||||
end,
|
||||
Nodes = lists:map(
|
||||
fun(#pubsub_node{nodeid = {_, SubNode}, options = Options}) ->
|
||||
fun(#pubsub_node{nodeid = {_, SubNode}, options = SubOptions}) ->
|
||||
Attrs =
|
||||
case get_option(Options, title) of
|
||||
case get_option(SubOptions, title) of
|
||||
false ->
|
||||
[{"jid", Host} |nodeAttr(SubNode)];
|
||||
Title ->
|
||||
@ -1152,24 +1181,6 @@ iq_disco_items(Host, Item, From) ->
|
||||
end
|
||||
end.
|
||||
|
||||
iq_local(From, To, #iq{type = Type, sub_el = SubEl, xmlns = XMLNS, lang = Lang} = IQ) ->
|
||||
ServerHost = To#jid.lserver,
|
||||
%% Accept IQs to server only from our own users.
|
||||
if
|
||||
From#jid.lserver /= ServerHost ->
|
||||
IQ#iq{type = error, sub_el = [?ERR_FORBIDDEN, SubEl]};
|
||||
true ->
|
||||
LOwner = jlib:jid_tolower(jlib:jid_remove_resource(From)),
|
||||
Res = case XMLNS of
|
||||
?NS_PUBSUB -> iq_pubsub(LOwner, ServerHost, From, Type, SubEl, Lang);
|
||||
?NS_PUBSUB_OWNER -> iq_pubsub_owner(LOwner, ServerHost, From, Type, SubEl, Lang)
|
||||
end,
|
||||
case Res of
|
||||
{result, IQRes} -> IQ#iq{type = result, sub_el = IQRes};
|
||||
{error, Error} -> IQ#iq{type = error, sub_el = [Error, SubEl]}
|
||||
end
|
||||
end.
|
||||
|
||||
iq_sm(From, To, #iq{type = Type, sub_el = SubEl, xmlns = XMLNS, lang = Lang} = IQ) ->
|
||||
ServerHost = To#jid.lserver,
|
||||
LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)),
|
||||
@ -1737,9 +1748,17 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
|
||||
{result, true} ->
|
||||
case tree_call(Host, create_node, [Host, Node, Type, Owner, NodeOptions, Parents]) of
|
||||
{ok, NodeId} ->
|
||||
node_call(Type, create_node, [NodeId, Owner]);
|
||||
ParentTree = tree_call(Host, get_parentnodes_tree, [Host, Node, Owner]),
|
||||
SubsByDepth = [{Depth, [{N, get_node_subs(N)} || N <- Nodes]} || {Depth, Nodes} <- ParentTree],
|
||||
case node_call(Type, create_node, [NodeId, Owner]) of
|
||||
{result, Result} -> {result, {NodeId, SubsByDepth, Result}};
|
||||
Error -> Error
|
||||
end;
|
||||
{error, {virtual, NodeId}} ->
|
||||
node_call(Type, create_node, [NodeId, Owner]);
|
||||
case node_call(Type, create_node, [NodeId, Owner]) of
|
||||
{result, Result} -> {result, {NodeId, [], Result}};
|
||||
Error -> Error
|
||||
end;
|
||||
Error ->
|
||||
Error
|
||||
end;
|
||||
@ -1751,20 +1770,15 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
|
||||
[{xmlelement, "create", nodeAttr(Node),
|
||||
[]}]}],
|
||||
case transaction(CreateNode, transaction) of
|
||||
{result, {Result, broadcast}} ->
|
||||
%%Lang = "en", %% TODO: fix
|
||||
%%OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
|
||||
%%broadcast_publish_item(Host, Node, uniqid(), Owner,
|
||||
%% [{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "result"}],
|
||||
%% [?XFIELD("hidden", "", "FORM_TYPE", ?NS_PUBSUB_NMI),
|
||||
%% ?XFIELD("jid-single", "Node Creator", "creator", jlib:jid_to_string(OwnerKey))]}]),
|
||||
{result, {NodeId, SubsByDepth, {Result, broadcast}}} ->
|
||||
broadcast_created_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth),
|
||||
case Result of
|
||||
default -> {result, Reply};
|
||||
_ -> {result, Result}
|
||||
end;
|
||||
{result, default} ->
|
||||
{result, {_NodeId, _SubsByDepth, default}} ->
|
||||
{result, Reply};
|
||||
{result, Result} ->
|
||||
{result, {_NodeId, _SubsByDepth, Result}} ->
|
||||
{result, Result};
|
||||
Error ->
|
||||
%% in case we change transaction to sync_dirty...
|
||||
@ -1866,7 +1880,7 @@ subscribe_node(Host, Node, From, JID, Configuration) ->
|
||||
error -> {"", "", ""};
|
||||
J -> jlib:jid_tolower(J)
|
||||
end,
|
||||
Action = fun(#pubsub_node{options = Options, owners = [Owner|_], type = Type, id = NodeId}) ->
|
||||
Action = fun(#pubsub_node{options = Options, owners = Owners, type = Type, id = NodeId}) ->
|
||||
Features = features(Type),
|
||||
SubscribeFeature = lists:member("subscribe", Features),
|
||||
OptionsFeature = lists:member("subscription-options", Features),
|
||||
@ -1875,21 +1889,7 @@ subscribe_node(Host, Node, From, JID, Configuration) ->
|
||||
AccessModel = get_option(Options, access_model),
|
||||
SendLast = get_option(Options, send_last_published_item),
|
||||
AllowedGroups = get_option(Options, roster_groups_allowed, []),
|
||||
{PresenceSubscription, RosterGroup} =
|
||||
case Host of
|
||||
{OUser, OServer, _} ->
|
||||
get_roster_info(OUser, OServer,
|
||||
Subscriber, AllowedGroups);
|
||||
_ ->
|
||||
case Subscriber of
|
||||
{"", "", ""} ->
|
||||
{false, false};
|
||||
_ ->
|
||||
{OU, OS, _} = Owner,
|
||||
get_roster_info(OU, OS,
|
||||
Subscriber, AllowedGroups)
|
||||
end
|
||||
end,
|
||||
{PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, Subscriber, Owners, AccessModel, AllowedGroups),
|
||||
if
|
||||
not SubscribeFeature ->
|
||||
%% Node does not support subscriptions
|
||||
@ -2054,7 +2054,7 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
|
||||
PluginPayload -> PluginPayload
|
||||
end,
|
||||
broadcast_publish_item(Host, Node, NodeId, Type, Options, Removed, ItemId, jlib:jid_tolower(Publisher), BroadcastPayload),
|
||||
set_cached_item(Host, NodeId, ItemId, Payload),
|
||||
set_cached_item(Host, NodeId, ItemId, Publisher, Payload),
|
||||
case Result of
|
||||
default -> {result, Reply};
|
||||
_ -> {result, Result}
|
||||
@ -2064,14 +2064,14 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
|
||||
Type = TNode#pubsub_node.type,
|
||||
Options = TNode#pubsub_node.options,
|
||||
broadcast_retract_items(Host, Node, NodeId, Type, Options, Removed),
|
||||
set_cached_item(Host, NodeId, ItemId, Payload),
|
||||
set_cached_item(Host, NodeId, ItemId, Publisher, Payload),
|
||||
{result, Reply};
|
||||
{result, {TNode, {Result, Removed}}} ->
|
||||
NodeId = TNode#pubsub_node.id,
|
||||
Type = TNode#pubsub_node.type,
|
||||
Options = TNode#pubsub_node.options,
|
||||
broadcast_retract_items(Host, Node, NodeId, Type, Options, Removed),
|
||||
set_cached_item(Host, NodeId, ItemId, Payload),
|
||||
set_cached_item(Host, NodeId, ItemId, Publisher, Payload),
|
||||
{result, Result};
|
||||
{result, {_, default}} ->
|
||||
{result, Reply};
|
||||
@ -2232,20 +2232,13 @@ get_items(Host, Node, From, SubId, SMaxItems, ItemIDs) ->
|
||||
{error, Error} ->
|
||||
{error, Error};
|
||||
_ ->
|
||||
Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId}) ->
|
||||
Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId, owners = Owners}) ->
|
||||
Features = features(Type),
|
||||
RetreiveFeature = lists:member("retrieve-items", Features),
|
||||
PersistentFeature = lists:member("persistent-items", Features),
|
||||
AccessModel = get_option(Options, access_model),
|
||||
AllowedGroups = get_option(Options, roster_groups_allowed, []),
|
||||
{PresenceSubscription, RosterGroup} =
|
||||
case Host of
|
||||
{OUser, OServer, _} ->
|
||||
get_roster_info(OUser, OServer,
|
||||
jlib:jid_tolower(From), AllowedGroups);
|
||||
_ ->
|
||||
{true, true}
|
||||
end,
|
||||
{PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups),
|
||||
if
|
||||
not RetreiveFeature ->
|
||||
%% Item Retrieval Not Supported
|
||||
@ -2295,6 +2288,12 @@ get_item(Host, Node, ItemId) ->
|
||||
{result, {_, Items}} -> Items;
|
||||
Error -> Error
|
||||
end.
|
||||
get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) ->
|
||||
AccessModel = get_option(Options, access_model),
|
||||
AllowedGroups = get_option(Options, roster_groups_allowed, []),
|
||||
{PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups),
|
||||
node_call(Type, get_items, [NodeIdx, From, AccessModel, PresenceSubscription, RosterGroup, undefined]).
|
||||
|
||||
|
||||
%% @spec (Host, Node, NodeId, Type, LJID, Number) -> any()
|
||||
%% Host = pubsubHost()
|
||||
@ -2310,10 +2309,10 @@ send_items(Host, Node, NodeId, Type, LJID, last) ->
|
||||
undefined ->
|
||||
send_items(Host, Node, NodeId, Type, LJID, 1);
|
||||
LastItem ->
|
||||
{ModifNow, ModifLjid} = LastItem#pubsub_item.modification,
|
||||
{ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
|
||||
Stanza = event_stanza_with_delay(
|
||||
[{xmlelement, "items", nodeAttr(Node),
|
||||
itemsEls([LastItem])}], ModifNow, ModifLjid),
|
||||
itemsEls([LastItem])}], ModifNow, ModifUSR),
|
||||
ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza)
|
||||
end;
|
||||
send_items(Host, Node, NodeId, Type, LJID, Number) ->
|
||||
@ -2330,10 +2329,10 @@ send_items(Host, Node, NodeId, Type, LJID, Number) ->
|
||||
end,
|
||||
Stanza = case ToSend of
|
||||
[LastItem] ->
|
||||
{ModifNow, ModifLjid} = LastItem#pubsub_item.modification,
|
||||
{ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
|
||||
event_stanza_with_delay(
|
||||
[{xmlelement, "items", nodeAttr(Node),
|
||||
itemsEls(ToSend)}], ModifNow, ModifLjid);
|
||||
itemsEls(ToSend)}], ModifNow, ModifUSR);
|
||||
_ ->
|
||||
event_stanza(
|
||||
[{xmlelement, "items", nodeAttr(Node),
|
||||
@ -2444,6 +2443,11 @@ set_affiliations(Host, Node, From, EntitiesEls) ->
|
||||
Action = fun(#pubsub_node{owners = Owners, type = Type, id = NodeId}=N) ->
|
||||
case lists:member(Owner, Owners) of
|
||||
true ->
|
||||
OwnerJID = jlib:make_jid(Owner),
|
||||
FilteredEntities = case Owners of
|
||||
[Owner] -> [E || E <- Entities, element(1, E) =/= OwnerJID];
|
||||
_ -> Entities
|
||||
end,
|
||||
lists:foreach(
|
||||
fun({JID, Affiliation}) ->
|
||||
node_call(Type, set_affiliation, [NodeId, JID, Affiliation]),
|
||||
@ -2464,7 +2468,7 @@ set_affiliations(Host, Node, From, EntitiesEls) ->
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end, Entities),
|
||||
end, FilteredEntities),
|
||||
{result, []};
|
||||
_ ->
|
||||
{error, ?ERR_FORBIDDEN}
|
||||
@ -2767,8 +2771,24 @@ set_subscriptions(Host, Node, From, EntitiesEls) ->
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups) ->
|
||||
if (AccessModel == presence) or (AccessModel == roster) ->
|
||||
case Host of
|
||||
{User, Server, _} ->
|
||||
get_roster_info(User, Server, From, AllowedGroups);
|
||||
_ ->
|
||||
[{OUser, OServer, _}|_] = Owners,
|
||||
get_roster_info(OUser, OServer, From, AllowedGroups)
|
||||
end;
|
||||
true ->
|
||||
{true, true}
|
||||
end.
|
||||
|
||||
%% @spec (OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, SubscriberResource}, AllowedGroups)
|
||||
%% -> {PresenceSubscription, RosterGroup}
|
||||
get_roster_info(_, _, {"", "", _}, _) ->
|
||||
{false, false};
|
||||
get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, AllowedGroups) ->
|
||||
{Subscription, Groups} =
|
||||
ejabberd_hooks:run_fold(
|
||||
@ -2780,7 +2800,9 @@ get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, A
|
||||
RosterGroup = lists:any(fun(Group) ->
|
||||
lists:member(Group, AllowedGroups)
|
||||
end, Groups),
|
||||
{PresenceSubscription, RosterGroup}.
|
||||
{PresenceSubscription, RosterGroup};
|
||||
get_roster_info(OwnerUser, OwnerServer, JID, AllowedGroups) ->
|
||||
get_roster_info(OwnerUser, OwnerServer, jlib:jid_tolower(JID), AllowedGroups).
|
||||
|
||||
%% @spec (AffiliationStr) -> Affiliation
|
||||
%% AffiliationStr = string()
|
||||
@ -2868,15 +2890,66 @@ node_to_deliver(LJID, NodeOptions) ->
|
||||
presence_can_deliver(LJID, PresenceDelivery).
|
||||
|
||||
presence_can_deliver(_, false) -> true;
|
||||
presence_can_deliver({User, Server, _}, true) ->
|
||||
presence_can_deliver({User, Server, Resource}, true) ->
|
||||
case mnesia:dirty_match_object({session, '_', '_', {User, Server}, '_', '_'}) of
|
||||
[] -> false;
|
||||
Ss ->
|
||||
lists:foldl(fun({session, _, _, _, undefined, _}, Acc) -> Acc;
|
||||
({session, _, _, _, _Priority, _}, _Acc) -> true
|
||||
lists:foldl(fun(_, true) -> true;
|
||||
({session, _, _ , _, undefined, _}, _Acc) -> false;
|
||||
({session, _, {_, _, R}, _, _Priority, _}, _Acc) ->
|
||||
case Resource of
|
||||
[] -> true;
|
||||
R -> true;
|
||||
_ -> false
|
||||
end
|
||||
end, false, Ss)
|
||||
end.
|
||||
|
||||
state_can_deliver({U, S, R}, []) -> [{U, S, R}];
|
||||
state_can_deliver({U, S, R}, SubOptions) ->
|
||||
%% Check SubOptions for 'show_values'
|
||||
case lists:keysearch('show_values', 1, SubOptions) of
|
||||
%% If not in suboptions, item can be delivered, case doesn't apply
|
||||
false -> [{U, S, R}];
|
||||
%% If in a suboptions ...
|
||||
{_, {_, ShowValues}} ->
|
||||
%% Get subscriber resources
|
||||
Resources = case R of
|
||||
%% If the subscriber JID is a bare one, get all its resources
|
||||
[] -> user_resources(U, S);
|
||||
%% If the subscriber JID is a full one, use its resource
|
||||
R -> [R]
|
||||
end,
|
||||
%% For each resource, test if the item is allowed to be delivered
|
||||
%% based on resource state
|
||||
lists:foldl(
|
||||
fun(Resource, Acc) ->
|
||||
get_resource_state({U, S, Resource}, ShowValues, Acc)
|
||||
end, [], Resources)
|
||||
end.
|
||||
|
||||
get_resource_state({U, S, R}, ShowValues, JIDs) ->
|
||||
%% Get user session PID
|
||||
case ejabberd_sm:get_session_pid(U, S, R) of
|
||||
%% If no PID, item can be delivered
|
||||
none -> lists:append([{U, S, R}], JIDs);
|
||||
%% If PID ...
|
||||
Pid ->
|
||||
%% Get user resource state
|
||||
%% TODO : add a catch clause
|
||||
Show = case ejabberd_c2s:get_presence(Pid) of
|
||||
{_, _, "available", _} -> "online";
|
||||
{_, _, State, _} -> State
|
||||
end,
|
||||
%% Is current resource state listed in 'show-values' suboption ?
|
||||
case lists:member(Show, ShowValues) of %andalso Show =/= "online" of
|
||||
%% If yes, item can be delivered
|
||||
true -> lists:append([{U, S, R}], JIDs);
|
||||
%% If no, item can't be delivered
|
||||
false -> JIDs
|
||||
end
|
||||
end.
|
||||
|
||||
%% @spec (Payload) -> int()
|
||||
%% Payload = term()
|
||||
%% @doc <p>Count occurence of XML elements in payload.</p>
|
||||
@ -2891,9 +2964,9 @@ payload_xmlelements([_|Tail], Count) -> payload_xmlelements(Tail, Count).
|
||||
event_stanza(Els) ->
|
||||
event_stanza_withmoreels(Els, []).
|
||||
|
||||
event_stanza_with_delay(Els, ModifNow, ModifLjid) ->
|
||||
event_stanza_with_delay(Els, ModifNow, ModifUSR) ->
|
||||
DateTime = calendar:now_to_datetime(ModifNow),
|
||||
MoreEls = [jlib:timestamp_to_xml(DateTime, utc, ModifLjid, "")],
|
||||
MoreEls = [jlib:timestamp_to_xml(DateTime, utc, ModifUSR, "")],
|
||||
event_stanza_withmoreels(Els, MoreEls).
|
||||
|
||||
event_stanza_withmoreels(Els, MoreEls) ->
|
||||
@ -2993,6 +3066,13 @@ broadcast_removed_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth) ->
|
||||
{result, false}
|
||||
end.
|
||||
|
||||
broadcast_created_node(_, _, _, _, _, []) ->
|
||||
{result, false};
|
||||
broadcast_created_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth) ->
|
||||
Stanza = event_stanza([{xmlelement, "create", nodeAttr(Node), []}]),
|
||||
broadcast_stanza(Host, Node, NodeId, Type, NodeOptions, SubsByDepth, nodes, Stanza, true),
|
||||
{result, true}.
|
||||
|
||||
broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) ->
|
||||
case get_option(NodeOptions, notify_config) of
|
||||
true ->
|
||||
@ -3137,18 +3217,23 @@ subscribed_nodes_by_jid(NotifyType, SubsByDepth) ->
|
||||
case is_to_deliver(LJID, NotifyType, Depth, NodeOptions, SubOptions) of
|
||||
true ->
|
||||
%% If is to deliver :
|
||||
case lists:member(LJID, JIDs) of
|
||||
case state_can_deliver(LJID, SubOptions) of
|
||||
[] -> {JIDs, Recipients};
|
||||
JIDsToDeliver ->
|
||||
lists:foldl(
|
||||
fun(JIDToDeliver, {JIDsAcc, RecipientsAcc}) ->
|
||||
case lists:member(JIDToDeliver, JIDs) of
|
||||
%% check if the JIDs co-accumulator contains the Subscription Jid,
|
||||
false ->
|
||||
%% - if not,
|
||||
%% - add the Jid to JIDs list co-accumulator ;
|
||||
%% - create a tuple of the Jid, NodeId, and SubID (as list),
|
||||
%% and add the tuple to the Recipients list co-accumulator
|
||||
{[LJID | JIDs], [{LJID, NodeName, [SubID]} | Recipients]};
|
||||
{[JIDToDeliver | JIDsAcc], [{JIDToDeliver, NodeName, [SubID]} | RecipientsAcc]};
|
||||
true ->
|
||||
%% - if the JIDs co-accumulator contains the Jid
|
||||
%% get the tuple containing the Jid from the Recipient list co-accumulator
|
||||
{_, {LJID, NodeName1, SubIDs}} = lists:keysearch(LJID, 1, Recipients),
|
||||
{_, {JIDToDeliver, NodeName1, SubIDs}} = lists:keysearch(JIDToDeliver, 1, RecipientsAcc),
|
||||
%% delete the tuple from the Recipients list
|
||||
% v1 : Recipients1 = lists:keydelete(LJID, 1, Recipients),
|
||||
% v2 : Recipients1 = lists:keyreplace(LJID, 1, Recipients, {LJID, NodeId1, [SubID | SubIDs]}),
|
||||
@ -3157,17 +3242,19 @@ subscribed_nodes_by_jid(NotifyType, SubsByDepth) ->
|
||||
% v1.1 : {JIDs, lists:append(Recipients1, [{LJID, NodeId1, lists:append(SubIDs, [SubID])}])}
|
||||
% v1.2 : {JIDs, [{LJID, NodeId1, [SubID | SubIDs]} | Recipients1]}
|
||||
% v2: {JIDs, Recipients1}
|
||||
{JIDs, lists:keyreplace(LJID, 1, Recipients, {LJID, NodeName1, [SubID | SubIDs]})}
|
||||
{JIDsAcc, lists:keyreplace(JIDToDeliver, 1, RecipientsAcc, {JIDToDeliver, NodeName1, [SubID | SubIDs]})}
|
||||
end
|
||||
end, {JIDs, Recipients}, JIDsToDeliver)
|
||||
end;
|
||||
false ->
|
||||
{JIDs, Recipients}
|
||||
end
|
||||
end, Acc, Subs)
|
||||
end,
|
||||
DepthsToDeliver = fun({Depth, SubsByNode}, Acc) ->
|
||||
DepthsToDeliver = fun({Depth, SubsByNode}, Acc1) ->
|
||||
lists:foldl(fun({Node, Subs}, Acc2) ->
|
||||
NodesToDeliver(Depth, Node, Subs, Acc2)
|
||||
end, Acc, SubsByNode)
|
||||
end, Acc1, SubsByNode)
|
||||
end,
|
||||
{_, JIDSubs} = lists:foldl(DepthsToDeliver, {[], []}, SubsByDepth),
|
||||
JIDSubs.
|
||||
@ -3494,18 +3581,18 @@ is_last_item_cache_enabled(Host) ->
|
||||
_ -> false
|
||||
end.
|
||||
|
||||
set_cached_item({_, ServerHost, _}, NodeId, ItemId, Payload) ->
|
||||
set_cached_item(ServerHost, NodeId, ItemId, Payload);
|
||||
set_cached_item(Host, NodeId, ItemId, Payload) ->
|
||||
set_cached_item({_, ServerHost, _}, NodeId, ItemId, Publisher, Payload) ->
|
||||
set_cached_item(ServerHost, NodeId, ItemId, Publisher, Payload);
|
||||
set_cached_item(Host, NodeId, ItemId, Publisher, Payload) ->
|
||||
case is_last_item_cache_enabled(Host) of
|
||||
true -> ets:insert(gen_mod:get_module_proc(Host, last_items), {NodeId, {ItemId, Payload}});
|
||||
true -> mnesia:dirty_write({pubsub_last_item, NodeId, ItemId, {now(), jlib:jid_tolower(jlib:jid_remove_resource(Publisher))}, Payload});
|
||||
_ -> ok
|
||||
end.
|
||||
unset_cached_item({_, ServerHost, _}, NodeId) ->
|
||||
unset_cached_item(ServerHost, NodeId);
|
||||
unset_cached_item(Host, NodeId) ->
|
||||
case is_last_item_cache_enabled(Host) of
|
||||
true -> ets:delete(gen_mod:get_module_proc(Host, last_items), NodeId);
|
||||
true -> mnesia:dirty_delete({pubsub_last_item, NodeId});
|
||||
_ -> ok
|
||||
end.
|
||||
get_cached_item({_, ServerHost, _}, NodeId) ->
|
||||
@ -3513,9 +3600,10 @@ get_cached_item({_, ServerHost, _}, NodeId) ->
|
||||
get_cached_item(Host, NodeId) ->
|
||||
case is_last_item_cache_enabled(Host) of
|
||||
true ->
|
||||
case catch ets:lookup(gen_mod:get_module_proc(Host, last_items), NodeId) of
|
||||
[{NodeId, {ItemId, Payload}}] ->
|
||||
#pubsub_item{itemid = {ItemId, NodeId}, payload = Payload};
|
||||
case mnesia:dirty_read({pubsub_last_item, NodeId}) of
|
||||
[{pubsub_last_item, NodeId, ItemId, Creation, Payload}] ->
|
||||
#pubsub_item{itemid = {ItemId, NodeId}, payload = Payload,
|
||||
creation = Creation, modification = Creation};
|
||||
_ ->
|
||||
undefined
|
||||
end;
|
||||
@ -3669,6 +3757,10 @@ transaction(Host, Node, Action, Trans) ->
|
||||
Error
|
||||
end
|
||||
end, Trans).
|
||||
transaction(Host, Action, Trans) ->
|
||||
transaction(fun() ->
|
||||
{result, lists:foldl(Action, [], tree_call(Host, get_nodes, [Host]))}
|
||||
end, Trans).
|
||||
|
||||
transaction(Fun, Trans) ->
|
||||
case catch mnesia:Trans(Fun) of
|
||||
|
@ -73,8 +73,7 @@
|
||||
disco_sm_items/5
|
||||
]).
|
||||
%% exported iq handlers
|
||||
-export([iq_local/3,
|
||||
iq_sm/3
|
||||
-export([iq_sm/3
|
||||
]).
|
||||
|
||||
%% exports for console debug manual use
|
||||
@ -183,9 +182,8 @@ init([ServerHost, Opts]) ->
|
||||
pubsub_index:init(Host, ServerHost, Opts),
|
||||
ets:new(gen_mod:get_module_proc(Host, config), [set, named_table]),
|
||||
ets:new(gen_mod:get_module_proc(ServerHost, config), [set, named_table]),
|
||||
ets:new(gen_mod:get_module_proc(Host, last_items), [set, named_table]),
|
||||
ets:new(gen_mod:get_module_proc(ServerHost, last_items), [set, named_table]),
|
||||
{Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts),
|
||||
mnesia:create_table(pubsub_last_item, [{ram_copies, [node()]}, {attributes, record_info(fields, pubsub_last_item)}]),
|
||||
mod_disco:register_feature(ServerHost, ?NS_PUBSUB),
|
||||
ets:insert(gen_mod:get_module_proc(Host, config), {nodetree, NodeTree}),
|
||||
ets:insert(gen_mod:get_module_proc(Host, config), {plugins, Plugins}),
|
||||
@ -199,24 +197,22 @@ init([ServerHost, Opts]) ->
|
||||
ets:insert(gen_mod:get_module_proc(ServerHost, config), {ignore_pep_from_offline, PepOffline}),
|
||||
ets:insert(gen_mod:get_module_proc(ServerHost, config), {host, Host}),
|
||||
ejabberd_hooks:add(sm_remove_connection_hook, ServerHost, ?MODULE, on_user_offline, 75),
|
||||
ejabberd_hooks:add(disco_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75),
|
||||
ejabberd_hooks:add(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
|
||||
ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
|
||||
ejabberd_hooks:add(disco_local_identity, ServerHost, ?MODULE, disco_local_identity, 75),
|
||||
ejabberd_hooks:add(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75),
|
||||
ejabberd_hooks:add(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75),
|
||||
ejabberd_hooks:add(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 80),
|
||||
ejabberd_hooks:add(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50),
|
||||
ejabberd_hooks:add(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50),
|
||||
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE, remove_user, 50),
|
||||
ejabberd_hooks:add(anonymous_purge_hook, ServerHost, ?MODULE, remove_user, 50),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB, ?MODULE, iq_sm, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER, ?MODULE, iq_sm, IQDisc),
|
||||
case lists:member(?PEPNODE, Plugins) of
|
||||
true ->
|
||||
ejabberd_hooks:add(feature_check_packet, ServerHost, ?MODULE, feature_check_packet, 75),
|
||||
ejabberd_hooks:add(disco_local_identity, ServerHost, ?MODULE, disco_local_identity, 75),
|
||||
ejabberd_hooks:add(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75),
|
||||
ejabberd_hooks:add(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB, ?MODULE, iq_local, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB_OWNER, ?MODULE, iq_local, IQDisc);
|
||||
ejabberd_hooks:add(disco_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75),
|
||||
ejabberd_hooks:add(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
|
||||
ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB, ?MODULE, iq_sm, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER, ?MODULE, iq_sm, IQDisc);
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
@ -388,15 +384,12 @@ send_loop(State) ->
|
||||
%% disco hooks handling functions
|
||||
%%
|
||||
|
||||
identity(Host) ->
|
||||
Identity = case lists:member(?PEPNODE, plugins(Host)) of
|
||||
true -> [{"category", "pubsub"}, {"type", "pep"}];
|
||||
false -> [{"category", "pubsub"}, {"type", "service"}]
|
||||
end,
|
||||
{xmlelement, "identity", Identity, []}.
|
||||
|
||||
disco_local_identity(Acc, _From, To, [], _Lang) ->
|
||||
Acc ++ [identity(To#jid.lserver)];
|
||||
case lists:member(?PEPNODE, plugins(To#jid.lserver)) of
|
||||
true ->
|
||||
[{xmlelement, "identity", [{"category", "pubsub"}, {"type", "pep"}], []} | Acc];
|
||||
false -> Acc
|
||||
end;
|
||||
disco_local_identity(Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc.
|
||||
|
||||
@ -417,76 +410,120 @@ disco_local_items(Acc, _From, _To, [], _Lang) ->
|
||||
disco_local_items(Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc.
|
||||
|
||||
disco_sm_identity(Acc, _From, To, [], _Lang) ->
|
||||
Acc ++ [identity(To#jid.lserver)];
|
||||
disco_sm_identity(Acc, From, To, Node, Lang) when is_list(Node) ->
|
||||
disco_sm_identity(Acc, From, To, list_to_binary(Node), Lang);
|
||||
disco_sm_identity(empty, From, To, Node, Lang) ->
|
||||
disco_sm_identity([], From, To, Node, Lang);
|
||||
disco_sm_identity(Acc, From, To, Node, _Lang) ->
|
||||
LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)),
|
||||
Acc ++ case node_disco_identity(LOwner, From, Node) of
|
||||
{result, I} -> I;
|
||||
_ -> []
|
||||
end.
|
||||
disco_identity(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From) ++ Acc.
|
||||
|
||||
disco_sm_features(Acc, _From, _To, [], _Lang) ->
|
||||
Acc;
|
||||
disco_sm_features(Acc, From, To, Node, _Lang) ->
|
||||
LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)),
|
||||
Features = node_disco_features(LOwner, From, Node),
|
||||
case {Acc, Features} of
|
||||
{{result, AccFeatures}, {result, AddFeatures}} ->
|
||||
{result, AccFeatures++AddFeatures};
|
||||
{_, {result, AddFeatures}} ->
|
||||
{result, AddFeatures};
|
||||
{_, _} ->
|
||||
Acc
|
||||
end.
|
||||
|
||||
disco_sm_items(Acc, From, To, [], _Lang) ->
|
||||
Host = To#jid.lserver,
|
||||
case tree_action(Host, get_subnodes, [Host, <<>>, From]) of
|
||||
[] ->
|
||||
Acc;
|
||||
Nodes ->
|
||||
SBJID = jlib:jid_to_string(jlib:jid_remove_resource(To)),
|
||||
Items = case Acc of
|
||||
{result, I} -> I;
|
||||
_ -> []
|
||||
end,
|
||||
NodeItems = lists:map(
|
||||
fun(#pubsub_node{nodeid = {_, Node}}) ->
|
||||
{xmlelement, "item",
|
||||
[{"jid", SBJID}|nodeAttr(Node)],
|
||||
[]}
|
||||
end, Nodes),
|
||||
{result, NodeItems ++ Items}
|
||||
end;
|
||||
|
||||
disco_sm_items(Acc, From, To, SNode, _Lang) ->
|
||||
Host = To#jid.lserver,
|
||||
Node = string_to_node(SNode),
|
||||
Action = fun(#pubsub_node{type = Type, id = NodeId}) ->
|
||||
% TODO call get_items/6 instead for access control (EJAB-1033)
|
||||
case node_call(Type, get_items, [NodeId, From]) of
|
||||
{result, []} ->
|
||||
none;
|
||||
{result, AllItems} ->
|
||||
SBJID = jlib:jid_to_string(jlib:jid_remove_resource(To)),
|
||||
Items = case Acc of
|
||||
{result, I} -> I;
|
||||
_ -> []
|
||||
end,
|
||||
NodeItems = lists:map(
|
||||
fun(#pubsub_item{itemid = {Id, _}}) ->
|
||||
{result, Name} = node_call(Type, get_item_name, [Host, Node, Id]),
|
||||
{xmlelement, "item", [{"jid", SBJID}, {"name", Name}], []}
|
||||
end, AllItems),
|
||||
{result, NodeItems ++ Items};
|
||||
_ ->
|
||||
none
|
||||
disco_identity(_Host, <<>>, _From) ->
|
||||
[{xmlelement, "identity", [{"category", "pubsub"}, {"type", "pep"}], []}];
|
||||
disco_identity(Host, Node, From) ->
|
||||
Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) ->
|
||||
Owners = node_owners_call(Type, Idx),
|
||||
case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
|
||||
{result, _} ->
|
||||
{result, [{xmlelement, "identity", [{"category", "pubsub"}, {"type", "pep"}], []},
|
||||
{xmlelement, "identity",
|
||||
[{"category", "pubsub"},
|
||||
{"type", "leaf"}
|
||||
| case get_option(Options, title) of
|
||||
false -> [];
|
||||
[Title] -> [{"name", Title}]
|
||||
end],
|
||||
[]}]};
|
||||
_ -> {result, []}
|
||||
end
|
||||
end,
|
||||
case transaction(Host, Node, Action, sync_dirty) of
|
||||
{result, {_, Items}} -> {result, Items};
|
||||
{result, {_, Result}} -> Result;
|
||||
_ -> []
|
||||
end.
|
||||
|
||||
disco_sm_features(Acc, From, To, Node, Lang) when is_list(Node) ->
|
||||
disco_sm_features(Acc, From, To, list_to_binary(Node), Lang);
|
||||
disco_sm_features(empty, From, To, Node, Lang) ->
|
||||
disco_sm_features({result, []}, From, To, Node, Lang);
|
||||
disco_sm_features({result, OtherFeatures} = _Acc, From, To, Node, _Lang) ->
|
||||
{result,
|
||||
OtherFeatures ++
|
||||
disco_features(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From)};
|
||||
disco_sm_features(Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc.
|
||||
|
||||
disco_features(_Host, <<>>, _From) ->
|
||||
[?NS_PUBSUB
|
||||
| [?NS_PUBSUB++"#"++Feature || Feature <- features("pep")]];
|
||||
disco_features(Host, Node, From) ->
|
||||
Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) ->
|
||||
Owners = node_owners_call(Type, Idx),
|
||||
case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
|
||||
{result, _} ->
|
||||
{result, [?NS_PUBSUB
|
||||
| [?NS_PUBSUB ++ "#" ++ Feature || Feature <- features("pep")]]};
|
||||
_ -> {result, []}
|
||||
end
|
||||
end,
|
||||
case transaction(Host, Node, Action, sync_dirty) of
|
||||
{result, {_, Result}} -> Result;
|
||||
_ -> []
|
||||
end.
|
||||
|
||||
disco_sm_items(Acc, From, To, Node, Lang) when is_list(Node) ->
|
||||
disco_sm_items(Acc, From, To, list_to_binary(Node), Lang);
|
||||
disco_sm_items(empty, From, To, Node, Lang) ->
|
||||
disco_sm_items({result, []}, From, To, Node, Lang);
|
||||
disco_sm_items({result, OtherItems}, From, To, Node, _Lang) ->
|
||||
{result,
|
||||
lists:usort(OtherItems ++
|
||||
disco_items(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From))};
|
||||
disco_sm_items(Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc.
|
||||
|
||||
disco_items(Host, <<>>, From) ->
|
||||
Action = fun(#pubsub_node{nodeid ={_, NodeID}, options = Options, type = Type, id = Idx}, Acc) ->
|
||||
Owners = node_owners_call(Type, Idx),
|
||||
case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
|
||||
{result, _} ->
|
||||
[{xmlelement, "item",
|
||||
[{"node", binary_to_list(NodeID)},
|
||||
{"jid", case Host of
|
||||
{_,_,_} -> jlib:jid_to_string(Host);
|
||||
_Host -> Host
|
||||
end}
|
||||
| case get_option(Options, title) of
|
||||
false -> [];
|
||||
[Title] -> [{"name", Title}]
|
||||
end],
|
||||
[]}
|
||||
| Acc];
|
||||
_ -> Acc
|
||||
end
|
||||
end,
|
||||
case transaction_on_nodes(Host, Action, sync_dirty) of
|
||||
{result, Items} -> Items;
|
||||
_ -> []
|
||||
end;
|
||||
|
||||
disco_items(Host, Node, From) ->
|
||||
Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) ->
|
||||
Owners = node_owners_call(Type, Idx),
|
||||
case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
|
||||
{result, Items} ->
|
||||
{result, [{xmlelement, "item",
|
||||
[{"jid", case Host of
|
||||
{_,_,_} -> jlib:jid_to_string(Host);
|
||||
_Host -> Host
|
||||
end},
|
||||
{"name", ItemID}], []}
|
||||
|| #pubsub_item{itemid = {ItemID,_}} <- Items]};
|
||||
_ -> {result, []}
|
||||
end
|
||||
end,
|
||||
case transaction(Host, Node, Action, sync_dirty) of
|
||||
{result, {_, Result}} -> Result;
|
||||
_ -> []
|
||||
end.
|
||||
|
||||
%% -------
|
||||
@ -662,25 +699,23 @@ terminate(_Reason, #state{host = Host,
|
||||
case lists:member(?PEPNODE, Plugins) of
|
||||
true ->
|
||||
ejabberd_hooks:delete(feature_check_packet, ServerHost, ?MODULE, feature_check_packet, 75),
|
||||
ejabberd_hooks:delete(disco_local_identity, ServerHost, ?MODULE, disco_local_identity, 75),
|
||||
ejabberd_hooks:delete(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75),
|
||||
ejabberd_hooks:delete(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB_OWNER);
|
||||
ejabberd_hooks:delete(disco_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75),
|
||||
ejabberd_hooks:delete(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
|
||||
ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER);
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
ejabberd_hooks:delete(sm_remove_connection_hook, ServerHost, ?MODULE, on_user_offline, 75),
|
||||
ejabberd_hooks:delete(disco_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75),
|
||||
ejabberd_hooks:delete(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
|
||||
ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
|
||||
ejabberd_hooks:delete(disco_local_identity, ServerHost, ?MODULE, disco_local_identity, 75),
|
||||
ejabberd_hooks:delete(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75),
|
||||
ejabberd_hooks:delete(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75),
|
||||
ejabberd_hooks:delete(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 80),
|
||||
ejabberd_hooks:delete(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50),
|
||||
ejabberd_hooks:delete(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50),
|
||||
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50),
|
||||
ejabberd_hooks:delete(anonymous_purge_hook, ServerHost, ?MODULE, remove_user, 50),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER),
|
||||
mod_disco:unregister_feature(ServerHost, ?NS_PUBSUB),
|
||||
gen_mod:get_module_proc(ServerHost, ?LOOPNAME) ! stop,
|
||||
terminate_plugins(Host, ServerHost, Plugins, TreePlugin).
|
||||
@ -822,10 +857,10 @@ command_disco_info(_Host, <<?NS_PUBSUB_GET_PENDING>>, _From) ->
|
||||
|
||||
node_disco_info(Host, Node, From) ->
|
||||
node_disco_info(Host, Node, From, true, true).
|
||||
node_disco_identity(Host, Node, From) ->
|
||||
node_disco_info(Host, Node, From, true, false).
|
||||
node_disco_features(Host, Node, From) ->
|
||||
node_disco_info(Host, Node, From, false, true).
|
||||
%node_disco_identity(Host, Node, From) ->
|
||||
% node_disco_info(Host, Node, From, true, false).
|
||||
%node_disco_features(Host, Node, From) ->
|
||||
% node_disco_info(Host, Node, From, false, true).
|
||||
node_disco_info(Host, Node, From, Identity, Features) ->
|
||||
Action =
|
||||
fun(#pubsub_node{type = Type, id = NodeId}) ->
|
||||
@ -927,17 +962,16 @@ iq_disco_items(Host, Item, From, RSM) ->
|
||||
{result, []};
|
||||
[SNode] ->
|
||||
Node = string_to_node(SNode),
|
||||
Action =
|
||||
fun(#pubsub_node{type = Type, id = NodeId}) ->
|
||||
%% TODO call get_items/6 instead for access control (EJAB-1033)
|
||||
{NodeItems, RsmOut} = case node_call(Type, get_items, [NodeId, From, RSM]) of
|
||||
{result, I} -> I;
|
||||
Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) ->
|
||||
Owners = node_owners_call(Type, Idx),
|
||||
{NodeItems, RsmOut} = case get_allowed_items_call(Host, Idx, From, Type, Options, Owners, RSM) of
|
||||
{result, R} -> R;
|
||||
_ -> {[], none}
|
||||
end,
|
||||
Nodes = lists:map(
|
||||
fun(#pubsub_node{nodeid = {_, SubNode}, options = Options}) ->
|
||||
fun(#pubsub_node{nodeid = {_, SubNode}, options = SubOptions}) ->
|
||||
Attrs =
|
||||
case get_option(Options, title) of
|
||||
case get_option(SubOptions, title) of
|
||||
false ->
|
||||
[{"jid", Host} |nodeAttr(SubNode)];
|
||||
Title ->
|
||||
@ -958,24 +992,6 @@ iq_disco_items(Host, Item, From, RSM) ->
|
||||
end
|
||||
end.
|
||||
|
||||
iq_local(From, To, #iq{type = Type, sub_el = SubEl, xmlns = XMLNS, lang = Lang} = IQ) ->
|
||||
ServerHost = To#jid.lserver,
|
||||
%% Accept IQs to server only from our own users.
|
||||
if
|
||||
From#jid.lserver /= ServerHost ->
|
||||
IQ#iq{type = error, sub_el = [?ERR_FORBIDDEN, SubEl]};
|
||||
true ->
|
||||
LOwner = jlib:jid_tolower(jlib:jid_remove_resource(From)),
|
||||
Res = case XMLNS of
|
||||
?NS_PUBSUB -> iq_pubsub(LOwner, ServerHost, From, Type, SubEl, Lang);
|
||||
?NS_PUBSUB_OWNER -> iq_pubsub_owner(LOwner, ServerHost, From, Type, SubEl, Lang)
|
||||
end,
|
||||
case Res of
|
||||
{result, IQRes} -> IQ#iq{type = result, sub_el = IQRes};
|
||||
{error, Error} -> IQ#iq{type = error, sub_el = [Error, SubEl]}
|
||||
end
|
||||
end.
|
||||
|
||||
iq_sm(From, To, #iq{type = Type, sub_el = SubEl, xmlns = XMLNS, lang = Lang} = IQ) ->
|
||||
ServerHost = To#jid.lserver,
|
||||
LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)),
|
||||
@ -1547,9 +1563,17 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
|
||||
{result, true} ->
|
||||
case tree_call(Host, create_node, [Host, Node, Type, Owner, NodeOptions, Parents]) of
|
||||
{ok, NodeId} ->
|
||||
node_call(Type, create_node, [NodeId, Owner]);
|
||||
ParentTree = tree_call(Host, get_parentnodes_tree, [Host, Node, Owner]),
|
||||
SubsByDepth = [{Depth, [{N, get_node_subs(N)} || N <- Nodes]} || {Depth, Nodes} <- ParentTree],
|
||||
case node_call(Type, create_node, [NodeId, Owner]) of
|
||||
{result, Result} -> {result, {NodeId, SubsByDepth, Result}};
|
||||
Error -> Error
|
||||
end;
|
||||
{error, {virtual, NodeId}} ->
|
||||
node_call(Type, create_node, [NodeId, Owner]);
|
||||
case node_call(Type, create_node, [NodeId, Owner]) of
|
||||
{result, Result} -> {result, {NodeId, [], Result}};
|
||||
Error -> Error
|
||||
end;
|
||||
Error ->
|
||||
Error
|
||||
end;
|
||||
@ -1561,20 +1585,15 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
|
||||
[{xmlelement, "create", nodeAttr(Node),
|
||||
[]}]}],
|
||||
case transaction(Host, CreateNode, transaction) of
|
||||
{result, {Result, broadcast}} ->
|
||||
%%Lang = "en", %% TODO: fix
|
||||
%%OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
|
||||
%%broadcast_publish_item(Host, Node, uniqid(), Owner,
|
||||
%% [{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "result"}],
|
||||
%% [?XFIELD("hidden", "", "FORM_TYPE", ?NS_PUBSUB_NMI),
|
||||
%% ?XFIELD("jid-single", "Node Creator", "creator", jlib:jid_to_string(OwnerKey))]}]),
|
||||
{result, {NodeId, SubsByDepth, {Result, broadcast}}} ->
|
||||
broadcast_created_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth),
|
||||
case Result of
|
||||
default -> {result, Reply};
|
||||
_ -> {result, Result}
|
||||
end;
|
||||
{result, default} ->
|
||||
{result, {_NodeId, _SubsByDepth, default}} ->
|
||||
{result, Reply};
|
||||
{result, Result} ->
|
||||
{result, {_NodeId, _SubsByDepth, Result}} ->
|
||||
{result, Result};
|
||||
Error ->
|
||||
%% in case we change transaction to sync_dirty...
|
||||
@ -1685,25 +1704,8 @@ subscribe_node(Host, Node, From, JID, Configuration) ->
|
||||
AccessModel = get_option(Options, access_model),
|
||||
SendLast = get_option(Options, send_last_published_item),
|
||||
AllowedGroups = get_option(Options, roster_groups_allowed, []),
|
||||
{PresenceSubscription, RosterGroup} =
|
||||
case Host of
|
||||
{OUser, OServer, _} ->
|
||||
get_roster_info(OUser, OServer,
|
||||
Subscriber, AllowedGroups);
|
||||
_ ->
|
||||
case Subscriber of
|
||||
{"", "", ""} ->
|
||||
{false, false};
|
||||
_ ->
|
||||
case node_owners_call(Type, NodeId) of
|
||||
[{OU, OS, _}|_] ->
|
||||
get_roster_info(OU, OS,
|
||||
Subscriber, AllowedGroups);
|
||||
_ ->
|
||||
{false, false}
|
||||
end
|
||||
end
|
||||
end,
|
||||
Owners = node_owners_call(Type, NodeId),
|
||||
{PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, Subscriber, Owners, AccessModel, AllowedGroups),
|
||||
if
|
||||
not SubscribeFeature ->
|
||||
%% Node does not support subscriptions
|
||||
@ -1868,7 +1870,7 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
|
||||
PluginPayload -> PluginPayload
|
||||
end,
|
||||
broadcast_publish_item(Host, Node, NodeId, Type, Options, Removed, ItemId, jlib:jid_tolower(Publisher), BroadcastPayload),
|
||||
set_cached_item(Host, NodeId, ItemId, Payload),
|
||||
set_cached_item(Host, NodeId, ItemId, Publisher, Payload),
|
||||
case Result of
|
||||
default -> {result, Reply};
|
||||
_ -> {result, Result}
|
||||
@ -1878,14 +1880,14 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
|
||||
Type = TNode#pubsub_node.type,
|
||||
Options = TNode#pubsub_node.options,
|
||||
broadcast_retract_items(Host, Node, NodeId, Type, Options, Removed),
|
||||
set_cached_item(Host, NodeId, ItemId, Payload),
|
||||
set_cached_item(Host, NodeId, ItemId, Publisher, Payload),
|
||||
{result, Reply};
|
||||
{result, {TNode, {Result, Removed}}} ->
|
||||
NodeId = TNode#pubsub_node.id,
|
||||
Type = TNode#pubsub_node.type,
|
||||
Options = TNode#pubsub_node.options,
|
||||
broadcast_retract_items(Host, Node, NodeId, Type, Options, Removed),
|
||||
set_cached_item(Host, NodeId, ItemId, Payload),
|
||||
set_cached_item(Host, NodeId, ItemId, Publisher, Payload),
|
||||
{result, Result};
|
||||
{result, {_, default}} ->
|
||||
{result, Reply};
|
||||
@ -2052,14 +2054,8 @@ get_items(Host, Node, From, SubId, SMaxItems, ItemIDs, RSM) ->
|
||||
PersistentFeature = lists:member("persistent-items", Features),
|
||||
AccessModel = get_option(Options, access_model),
|
||||
AllowedGroups = get_option(Options, roster_groups_allowed, []),
|
||||
{PresenceSubscription, RosterGroup} =
|
||||
case Host of
|
||||
{OUser, OServer, _} ->
|
||||
get_roster_info(OUser, OServer,
|
||||
jlib:jid_tolower(From), AllowedGroups);
|
||||
_ ->
|
||||
{true, true}
|
||||
end,
|
||||
Owners = node_owners_call(Type, NodeId),
|
||||
{PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups),
|
||||
if
|
||||
not RetreiveFeature ->
|
||||
%% Item Retrieval Not Supported
|
||||
@ -2110,6 +2106,17 @@ get_item(Host, Node, ItemId) ->
|
||||
{result, {_, Items}} -> Items;
|
||||
Error -> Error
|
||||
end.
|
||||
get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) ->
|
||||
case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners, none) of
|
||||
{result, {I, _}} -> {result, I};
|
||||
Error -> Error
|
||||
end.
|
||||
get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners, RSM) ->
|
||||
AccessModel = get_option(Options, access_model),
|
||||
AllowedGroups = get_option(Options, roster_groups_allowed, []),
|
||||
{PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups),
|
||||
node_call(Type, get_items, [NodeIdx, From, AccessModel, PresenceSubscription, RosterGroup, undefined, RSM]).
|
||||
|
||||
|
||||
%% @spec (Host, Node, NodeId, Type, LJID, Number) -> any()
|
||||
%% Host = pubsubHost()
|
||||
@ -2126,20 +2133,20 @@ send_items(Host, Node, NodeId, Type, LJID, last) ->
|
||||
% special ODBC optimization, works only with node_hometree_odbc, node_flat_odbc and node_pep_odbc
|
||||
case node_action(Host, Type, get_last_items, [NodeId, LJID, 1]) of
|
||||
{result, [LastItem]} ->
|
||||
{ModifNow, ModifLjid} = LastItem#pubsub_item.modification,
|
||||
{ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
|
||||
event_stanza_with_delay(
|
||||
[{xmlelement, "items", nodeAttr(Node),
|
||||
itemsEls([LastItem])}], ModifNow, ModifLjid);
|
||||
itemsEls([LastItem])}], ModifNow, ModifUSR);
|
||||
_ ->
|
||||
event_stanza(
|
||||
[{xmlelement, "items", nodeAttr(Node),
|
||||
itemsEls([])}])
|
||||
end;
|
||||
LastItem ->
|
||||
{ModifNow, ModifLjid} = LastItem#pubsub_item.modification,
|
||||
{ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
|
||||
event_stanza_with_delay(
|
||||
[{xmlelement, "items", nodeAttr(Node),
|
||||
itemsEls([LastItem])}], ModifNow, ModifLjid)
|
||||
itemsEls([LastItem])}], ModifNow, ModifUSR)
|
||||
end,
|
||||
ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza);
|
||||
send_items(Host, Node, NodeId, Type, LJID, Number) ->
|
||||
@ -2156,10 +2163,10 @@ send_items(Host, Node, NodeId, Type, LJID, Number) ->
|
||||
end,
|
||||
Stanza = case ToSend of
|
||||
[LastItem] ->
|
||||
{ModifNow, ModifLjid} = LastItem#pubsub_item.modification,
|
||||
{ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
|
||||
event_stanza_with_delay(
|
||||
[{xmlelement, "items", nodeAttr(Node),
|
||||
itemsEls(ToSend)}], ModifNow, ModifLjid);
|
||||
itemsEls(ToSend)}], ModifNow, ModifUSR);
|
||||
_ ->
|
||||
event_stanza(
|
||||
[{xmlelement, "items", nodeAttr(Node),
|
||||
@ -2268,12 +2275,18 @@ set_affiliations(Host, Node, From, EntitiesEls) ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
_ ->
|
||||
Action = fun(#pubsub_node{type = Type, id = NodeId}) ->
|
||||
case lists:member(Owner, node_owners_call(Type, NodeId)) of
|
||||
Owners = node_owners_call(Type, NodeId),
|
||||
case lists:member(Owner, Owners) of
|
||||
true ->
|
||||
OwnerJID = jlib:make_jid(Owner),
|
||||
FilteredEntities = case Owners of
|
||||
[Owner] -> [E || E <- Entities, element(1, E) =/= OwnerJID];
|
||||
_ -> Entities
|
||||
end,
|
||||
lists:foreach(
|
||||
fun({JID, Affiliation}) ->
|
||||
node_call(Type, set_affiliation, [NodeId, JID, Affiliation])
|
||||
end, Entities),
|
||||
end, FilteredEntities),
|
||||
{result, []};
|
||||
_ ->
|
||||
{error, ?ERR_FORBIDDEN}
|
||||
@ -2576,8 +2589,24 @@ set_subscriptions(Host, Node, From, EntitiesEls) ->
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups) ->
|
||||
if (AccessModel == presence) or (AccessModel == roster) ->
|
||||
case Host of
|
||||
{User, Server, _} ->
|
||||
get_roster_info(User, Server, From, AllowedGroups);
|
||||
_ ->
|
||||
[{OUser, OServer, _}|_] = Owners,
|
||||
get_roster_info(OUser, OServer, From, AllowedGroups)
|
||||
end;
|
||||
true ->
|
||||
{true, true}
|
||||
end.
|
||||
|
||||
%% @spec (OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, SubscriberResource}, AllowedGroups)
|
||||
%% -> {PresenceSubscription, RosterGroup}
|
||||
get_roster_info(_, _, {"", "", _}, _) ->
|
||||
{false, false};
|
||||
get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, AllowedGroups) ->
|
||||
{Subscription, Groups} =
|
||||
ejabberd_hooks:run_fold(
|
||||
@ -2589,7 +2618,9 @@ get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, A
|
||||
RosterGroup = lists:any(fun(Group) ->
|
||||
lists:member(Group, AllowedGroups)
|
||||
end, Groups),
|
||||
{PresenceSubscription, RosterGroup}.
|
||||
{PresenceSubscription, RosterGroup};
|
||||
get_roster_info(OwnerUser, OwnerServer, JID, AllowedGroups) ->
|
||||
get_roster_info(OwnerUser, OwnerServer, jlib:jid_tolower(JID), AllowedGroups).
|
||||
|
||||
%% @spec (AffiliationStr) -> Affiliation
|
||||
%% AffiliationStr = string()
|
||||
@ -2677,15 +2708,66 @@ node_to_deliver(LJID, NodeOptions) ->
|
||||
presence_can_deliver(LJID, PresenceDelivery).
|
||||
|
||||
presence_can_deliver(_, false) -> true;
|
||||
presence_can_deliver({User, Server, _}, true) ->
|
||||
presence_can_deliver({User, Server, Resource}, true) ->
|
||||
case mnesia:dirty_match_object({session, '_', '_', {User, Server}, '_', '_'}) of
|
||||
[] -> false;
|
||||
Ss ->
|
||||
lists:foldl(fun({session, _, _, _, undefined, _}, Acc) -> Acc;
|
||||
({session, _, _, _, _Priority, _}, _Acc) -> true
|
||||
lists:foldl(fun(_, true) -> true;
|
||||
({session, _, _ , _, undefined, _}, _Acc) -> false;
|
||||
({session, _, {_, _, R}, _, _Priority, _}, _Acc) ->
|
||||
case Resource of
|
||||
[] -> true;
|
||||
R -> true;
|
||||
_ -> false
|
||||
end
|
||||
end, false, Ss)
|
||||
end.
|
||||
|
||||
state_can_deliver({U, S, R}, []) -> [{U, S, R}];
|
||||
state_can_deliver({U, S, R}, SubOptions) ->
|
||||
%% Check SubOptions for 'show_values'
|
||||
case lists:keysearch('show_values', 1, SubOptions) of
|
||||
%% If not in suboptions, item can be delivered, case doesn't apply
|
||||
false -> [{U, S, R}];
|
||||
%% If in a suboptions ...
|
||||
{_, {_, ShowValues}} ->
|
||||
%% Get subscriber resources
|
||||
Resources = case R of
|
||||
%% If the subscriber JID is a bare one, get all its resources
|
||||
[] -> user_resources(U, S);
|
||||
%% If the subscriber JID is a full one, use its resource
|
||||
R -> [R]
|
||||
end,
|
||||
%% For each resource, test if the item is allowed to be delivered
|
||||
%% based on resource state
|
||||
lists:foldl(
|
||||
fun(Resource, Acc) ->
|
||||
get_resource_state({U, S, Resource}, ShowValues, Acc)
|
||||
end, [], Resources)
|
||||
end.
|
||||
|
||||
get_resource_state({U, S, R}, ShowValues, JIDs) ->
|
||||
%% Get user session PID
|
||||
case ejabberd_sm:get_session_pid(U, S, R) of
|
||||
%% If no PID, item can be delivered
|
||||
none -> lists:append([{U, S, R}], JIDs);
|
||||
%% If PID ...
|
||||
Pid ->
|
||||
%% Get user resource state
|
||||
%% TODO : add a catch clause
|
||||
Show = case ejabberd_c2s:get_presence(Pid) of
|
||||
{_, _, "available", _} -> "online";
|
||||
{_, _, State, _} -> State
|
||||
end,
|
||||
%% Is current resource state listed in 'show-values' suboption ?
|
||||
case lists:member(Show, ShowValues) of %andalso Show =/= "online" of
|
||||
%% If yes, item can be delivered
|
||||
true -> lists:append([{U, S, R}], JIDs);
|
||||
%% If no, item can't be delivered
|
||||
false -> JIDs
|
||||
end
|
||||
end.
|
||||
|
||||
%% @spec (Payload) -> int()
|
||||
%% Payload = term()
|
||||
%% @doc <p>Count occurence of XML elements in payload.</p>
|
||||
@ -2700,9 +2782,9 @@ payload_xmlelements([_|Tail], Count) -> payload_xmlelements(Tail, Count).
|
||||
event_stanza(Els) ->
|
||||
event_stanza_withmoreels(Els, []).
|
||||
|
||||
event_stanza_with_delay(Els, ModifNow, ModifLjid) ->
|
||||
event_stanza_with_delay(Els, ModifNow, ModifUSR) ->
|
||||
DateTime = calendar:now_to_datetime(ModifNow),
|
||||
MoreEls = [jlib:timestamp_to_xml(DateTime, utc, ModifLjid, "")],
|
||||
MoreEls = [jlib:timestamp_to_xml(DateTime, utc, ModifUSR, "")],
|
||||
event_stanza_withmoreels(Els, MoreEls).
|
||||
|
||||
event_stanza_withmoreels(Els, MoreEls) ->
|
||||
@ -2802,6 +2884,13 @@ broadcast_removed_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth) ->
|
||||
{result, false}
|
||||
end.
|
||||
|
||||
broadcast_created_node(_, _, _, _, _, []) ->
|
||||
{result, false};
|
||||
broadcast_created_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth) ->
|
||||
Stanza = event_stanza([{xmlelement, "create", nodeAttr(Node), []}]),
|
||||
broadcast_stanza(Host, Node, NodeId, Type, NodeOptions, SubsByDepth, nodes, Stanza, true),
|
||||
{result, true}.
|
||||
|
||||
broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) ->
|
||||
case get_option(NodeOptions, notify_config) of
|
||||
true ->
|
||||
@ -2946,18 +3035,23 @@ subscribed_nodes_by_jid(NotifyType, SubsByDepth) ->
|
||||
case is_to_deliver(LJID, NotifyType, Depth, NodeOptions, SubOptions) of
|
||||
true ->
|
||||
%% If is to deliver :
|
||||
case lists:member(LJID, JIDs) of
|
||||
case state_can_deliver(LJID, SubOptions) of
|
||||
[] -> {JIDs, Recipients};
|
||||
JIDsToDeliver ->
|
||||
lists:foldl(
|
||||
fun(JIDToDeliver, {JIDsAcc, RecipientsAcc}) ->
|
||||
case lists:member(JIDToDeliver, JIDs) of
|
||||
%% check if the JIDs co-accumulator contains the Subscription Jid,
|
||||
false ->
|
||||
%% - if not,
|
||||
%% - add the Jid to JIDs list co-accumulator ;
|
||||
%% - create a tuple of the Jid, NodeId, and SubID (as list),
|
||||
%% and add the tuple to the Recipients list co-accumulator
|
||||
{[LJID | JIDs], [{LJID, NodeName, [SubID]} | Recipients]};
|
||||
{[JIDToDeliver | JIDsAcc], [{JIDToDeliver, NodeName, [SubID]} | RecipientsAcc]};
|
||||
true ->
|
||||
%% - if the JIDs co-accumulator contains the Jid
|
||||
%% get the tuple containing the Jid from the Recipient list co-accumulator
|
||||
{_, {LJID, NodeName1, SubIDs}} = lists:keysearch(LJID, 1, Recipients),
|
||||
{_, {JIDToDeliver, NodeName1, SubIDs}} = lists:keysearch(JIDToDeliver, 1, RecipientsAcc),
|
||||
%% delete the tuple from the Recipients list
|
||||
% v1 : Recipients1 = lists:keydelete(LJID, 1, Recipients),
|
||||
% v2 : Recipients1 = lists:keyreplace(LJID, 1, Recipients, {LJID, NodeId1, [SubID | SubIDs]}),
|
||||
@ -2966,17 +3060,19 @@ subscribed_nodes_by_jid(NotifyType, SubsByDepth) ->
|
||||
% v1.1 : {JIDs, lists:append(Recipients1, [{LJID, NodeId1, lists:append(SubIDs, [SubID])}])}
|
||||
% v1.2 : {JIDs, [{LJID, NodeId1, [SubID | SubIDs]} | Recipients1]}
|
||||
% v2: {JIDs, Recipients1}
|
||||
{JIDs, lists:keyreplace(LJID, 1, Recipients, {LJID, NodeName1, [SubID | SubIDs]})}
|
||||
{JIDsAcc, lists:keyreplace(JIDToDeliver, 1, RecipientsAcc, {JIDToDeliver, NodeName1, [SubID | SubIDs]})}
|
||||
end
|
||||
end, {JIDs, Recipients}, JIDsToDeliver)
|
||||
end;
|
||||
false ->
|
||||
{JIDs, Recipients}
|
||||
end
|
||||
end, Acc, Subs)
|
||||
end,
|
||||
DepthsToDeliver = fun({Depth, SubsByNode}, Acc) ->
|
||||
DepthsToDeliver = fun({Depth, SubsByNode}, Acc1) ->
|
||||
lists:foldl(fun({Node, Subs}, Acc2) ->
|
||||
NodesToDeliver(Depth, Node, Subs, Acc2)
|
||||
end, Acc, SubsByNode)
|
||||
end, Acc1, SubsByNode)
|
||||
end,
|
||||
{_, JIDSubs} = lists:foldl(DepthsToDeliver, {[], []}, SubsByDepth),
|
||||
JIDSubs.
|
||||
@ -3327,18 +3423,18 @@ is_last_item_cache_enabled(Host) ->
|
||||
_ -> false
|
||||
end.
|
||||
|
||||
set_cached_item({_, ServerHost, _}, NodeId, ItemId, Payload) ->
|
||||
set_cached_item(ServerHost, NodeId, ItemId, Payload);
|
||||
set_cached_item(Host, NodeId, ItemId, Payload) ->
|
||||
set_cached_item({_, ServerHost, _}, NodeId, ItemId, Publisher, Payload) ->
|
||||
set_cached_item(ServerHost, NodeId, ItemId, Publisher, Payload);
|
||||
set_cached_item(Host, NodeId, ItemId, Publisher, Payload) ->
|
||||
case is_last_item_cache_enabled(Host) of
|
||||
true -> ets:insert(gen_mod:get_module_proc(Host, last_items), {NodeId, {ItemId, Payload}});
|
||||
true -> mnesia:dirty_write({pubsub_last_item, NodeId, ItemId, {now(), jlib:jid_tolower(jlib:jid_remove_resource(Publisher))}, Payload});
|
||||
_ -> ok
|
||||
end.
|
||||
unset_cached_item({_, ServerHost, _}, NodeId) ->
|
||||
unset_cached_item(ServerHost, NodeId);
|
||||
unset_cached_item(Host, NodeId) ->
|
||||
case is_last_item_cache_enabled(Host) of
|
||||
true -> ets:delete(gen_mod:get_module_proc(Host, last_items), NodeId);
|
||||
true -> mnesia:dirty_delete({pubsub_last_item, NodeId});
|
||||
_ -> ok
|
||||
end.
|
||||
get_cached_item({_, ServerHost, _}, NodeId) ->
|
||||
@ -3346,9 +3442,10 @@ get_cached_item({_, ServerHost, _}, NodeId) ->
|
||||
get_cached_item(Host, NodeId) ->
|
||||
case is_last_item_cache_enabled(Host) of
|
||||
true ->
|
||||
case catch ets:lookup(gen_mod:get_module_proc(Host, last_items), NodeId) of
|
||||
[{NodeId, {ItemId, Payload}}] ->
|
||||
#pubsub_item{itemid = {ItemId, NodeId}, payload = Payload};
|
||||
case mnesia:dirty_read({pubsub_last_item, NodeId}) of
|
||||
[{pubsub_last_item, NodeId, ItemId, Creation, Payload}] ->
|
||||
#pubsub_item{itemid = {ItemId, NodeId}, payload = Payload,
|
||||
creation = Creation, modification = Creation};
|
||||
_ ->
|
||||
undefined
|
||||
end;
|
||||
@ -3508,6 +3605,10 @@ transaction(Host, Node, Action, Trans) ->
|
||||
Error
|
||||
end
|
||||
end, Trans).
|
||||
transaction_on_nodes(Host, Action, Trans) ->
|
||||
transaction(Host, fun() ->
|
||||
{result, lists:foldl(Action, [], tree_call(Host, get_nodes, [Host]))}
|
||||
end, Trans).
|
||||
|
||||
transaction(Host, Fun, Trans) ->
|
||||
transaction_retry(Host, Fun, Trans, 2).
|
||||
|
@ -379,7 +379,7 @@ unsubscribe_node(NodeId, Sender, Subscriber, SubId) ->
|
||||
%% {error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
|
||||
%% Requesting entity is not a subscriber
|
||||
Subscriptions == [] ->
|
||||
{error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST, "not-subscribed")};
|
||||
{error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST_CANCEL, "not-subscribed")};
|
||||
%% Subid supplied, so use that.
|
||||
SubIdExists ->
|
||||
Sub = first_in_list(fun(S) ->
|
||||
@ -393,7 +393,7 @@ unsubscribe_node(NodeId, Sender, Subscriber, SubId) ->
|
||||
delete_subscriptions(SubKey, NodeId, [S], SubState),
|
||||
{result, default};
|
||||
false ->
|
||||
{error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST, "not-subscribed")}
|
||||
{error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST_CANCEL, "not-subscribed")}
|
||||
end;
|
||||
%% Asking to remove all subscriptions to the given node
|
||||
SubId == all ->
|
||||
|
@ -379,7 +379,7 @@ unsubscribe_node(NodeId, Sender, Subscriber, SubId) ->
|
||||
%% {error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
|
||||
%% Requesting entity is not a subscriber
|
||||
Subscriptions == [] ->
|
||||
{error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST, "not-subscribed")};
|
||||
{error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST_CANCEL, "not-subscribed")};
|
||||
%% Subid supplied, so use that.
|
||||
SubIdExists ->
|
||||
Sub = first_in_list(fun(S) ->
|
||||
@ -393,7 +393,7 @@ unsubscribe_node(NodeId, Sender, Subscriber, SubId) ->
|
||||
delete_subscription(SubKey, NodeId, S, Affiliation, Subscriptions),
|
||||
{result, default};
|
||||
false ->
|
||||
{error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST, "not-subscribed")}
|
||||
{error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST_CANCEL, "not-subscribed")}
|
||||
end;
|
||||
%% Asking to remove all subscriptions to the given node
|
||||
SubId == all ->
|
||||
|
@ -135,3 +135,12 @@
|
||||
%% <p>This is the format of the <tt>subscriptions</tt> table. The type of the
|
||||
%% table is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
|
||||
-record(pubsub_subscription, {subid, options}).
|
||||
|
||||
%% @type pubsubLastItem() = #pubsub_last_item{
|
||||
%% nodeid = nodeidx(),
|
||||
%% itemid = string(),
|
||||
%% payload = XMLContent::string()}.
|
||||
%% <p>This is the format of the <tt>last items</tt> table. it stores last item payload
|
||||
%% for every node</p>
|
||||
-record(pubsub_last_item, {nodeid, itemid, creation, payload}).
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
--- mod_pubsub.erl 2010-03-11 15:55:35.000000000 +0100
|
||||
+++ mod_pubsub_odbc.erl 2010-03-11 15:59:40.000000000 +0100
|
||||
--- mod_pubsub.erl 2010-08-02 16:07:28.000000000 +0200
|
||||
+++ mod_pubsub_odbc.erl 2010-08-02 17:04:37.000000000 +0200
|
||||
@@ -42,7 +42,7 @@
|
||||
%%% 6.2.3.1, 6.2.3.5, and 6.3. For information on subscription leases see
|
||||
%%% XEP-0060 section 12.18.
|
||||
@ -22,7 +22,7 @@
|
||||
|
||||
%% exports for hooks
|
||||
-export([presence_probe/3,
|
||||
@@ -104,7 +104,7 @@
|
||||
@@ -103,7 +103,7 @@
|
||||
string_to_affiliation/1,
|
||||
extended_error/2,
|
||||
extended_error/3,
|
||||
@ -31,7 +31,7 @@
|
||||
]).
|
||||
|
||||
%% API and gen_server callbacks
|
||||
@@ -123,7 +123,7 @@
|
||||
@@ -122,7 +122,7 @@
|
||||
-export([send_loop/1
|
||||
]).
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
-define(LOOPNAME, ejabberd_mod_pubsub_loop).
|
||||
-define(PLUGIN_PREFIX, "node_").
|
||||
-define(TREE_PREFIX, "nodetree_").
|
||||
@@ -221,8 +221,6 @@
|
||||
@@ -217,8 +217,6 @@
|
||||
ok
|
||||
end,
|
||||
ejabberd_router:register_route(Host),
|
||||
@ -49,7 +49,7 @@
|
||||
init_nodes(Host, ServerHost, NodeTree, Plugins),
|
||||
State = #state{host = Host,
|
||||
server_host = ServerHost,
|
||||
@@ -281,207 +279,14 @@
|
||||
@@ -277,207 +275,14 @@
|
||||
|
||||
init_nodes(Host, ServerHost, _NodeTree, Plugins) ->
|
||||
%% TODO, this call should be done plugin side
|
||||
@ -260,7 +260,7 @@
|
||||
send_loop(State) ->
|
||||
receive
|
||||
{presence, JID, Pid} ->
|
||||
@@ -492,17 +297,15 @@
|
||||
@@ -488,17 +293,15 @@
|
||||
%% for each node From is subscribed to
|
||||
%% and if the node is so configured, send the last published item to From
|
||||
lists:foreach(fun(PType) ->
|
||||
@ -284,7 +284,54 @@
|
||||
true ->
|
||||
% resource not concerned about that subscription
|
||||
ok
|
||||
@@ -748,10 +551,10 @@
|
||||
@@ -617,7 +420,8 @@
|
||||
disco_identity(_Host, <<>>, _From) ->
|
||||
[{xmlelement, "identity", [{"category", "pubsub"}, {"type", "pep"}], []}];
|
||||
disco_identity(Host, Node, From) ->
|
||||
- Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) ->
|
||||
+ Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) ->
|
||||
+ Owners = node_owners_call(Type, Idx),
|
||||
case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
|
||||
{result, _} ->
|
||||
{result, [{xmlelement, "identity", [{"category", "pubsub"}, {"type", "pep"}], []},
|
||||
@@ -652,7 +456,8 @@
|
||||
[?NS_PUBSUB
|
||||
| [?NS_PUBSUB++"#"++Feature || Feature <- features("pep")]];
|
||||
disco_features(Host, Node, From) ->
|
||||
- Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) ->
|
||||
+ Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) ->
|
||||
+ Owners = node_owners_call(Type, Idx),
|
||||
case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
|
||||
{result, _} ->
|
||||
{result, [?NS_PUBSUB
|
||||
@@ -677,7 +482,8 @@
|
||||
Acc.
|
||||
|
||||
disco_items(Host, <<>>, From) ->
|
||||
- Action = fun(#pubsub_node{nodeid ={_, NodeID}, options = Options, type = Type, id = Idx, owners = Owners}, Acc) ->
|
||||
+ Action = fun(#pubsub_node{nodeid ={_, NodeID}, options = Options, type = Type, id = Idx}, Acc) ->
|
||||
+ Owners = node_owners_call(Type, Idx),
|
||||
case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
|
||||
{result, _} ->
|
||||
[{xmlelement, "item",
|
||||
@@ -695,13 +501,14 @@
|
||||
_ -> Acc
|
||||
end
|
||||
end,
|
||||
- case transaction(Host, Action, sync_dirty) of
|
||||
+ case transaction_on_nodes(Host, Action, sync_dirty) of
|
||||
{result, Items} -> Items;
|
||||
_ -> []
|
||||
end;
|
||||
|
||||
disco_items(Host, Node, From) ->
|
||||
- Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) ->
|
||||
+ Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) ->
|
||||
+ Owners = node_owners_call(Type, Idx),
|
||||
case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
|
||||
{result, Items} ->
|
||||
{result, [{xmlelement, "item",
|
||||
@@ -781,10 +588,10 @@
|
||||
lists:foreach(fun(PType) ->
|
||||
{result, Subscriptions} = node_action(Host, PType, get_entity_subscriptions, [Host, Entity]),
|
||||
lists:foreach(fun
|
||||
@ -297,7 +344,7 @@
|
||||
true ->
|
||||
node_action(Host, PType, unsubscribe_node, [NodeId, Entity, JID, all]);
|
||||
false ->
|
||||
@@ -921,7 +724,8 @@
|
||||
@@ -952,7 +759,8 @@
|
||||
sub_el = SubEl} = IQ ->
|
||||
{xmlelement, _, QAttrs, _} = SubEl,
|
||||
Node = xml:get_attr_s("node", QAttrs),
|
||||
@ -307,7 +354,7 @@
|
||||
{result, IQRes} ->
|
||||
jlib:iq_to_xml(
|
||||
IQ#iq{type = result,
|
||||
@@ -1034,7 +838,7 @@
|
||||
@@ -1065,7 +873,7 @@
|
||||
[] ->
|
||||
["leaf"]; %% No sub-nodes: it's a leaf node
|
||||
_ ->
|
||||
@ -316,7 +363,7 @@
|
||||
{result, []} -> ["collection"];
|
||||
{result, _} -> ["leaf", "collection"];
|
||||
_ -> []
|
||||
@@ -1050,8 +854,9 @@
|
||||
@@ -1081,8 +889,9 @@
|
||||
[];
|
||||
true ->
|
||||
[{xmlelement, "feature", [{"var", ?NS_PUBSUB}], []} |
|
||||
@ -328,7 +375,7 @@
|
||||
end, features(Type))]
|
||||
end,
|
||||
%% TODO: add meta-data info (spec section 5.4)
|
||||
@@ -1080,8 +885,9 @@
|
||||
@@ -1111,8 +920,9 @@
|
||||
{xmlelement, "feature", [{"var", ?NS_PUBSUB}], []},
|
||||
{xmlelement, "feature", [{"var", ?NS_COMMANDS}], []},
|
||||
{xmlelement, "feature", [{"var", ?NS_VCARD}], []}] ++
|
||||
@ -340,7 +387,7 @@
|
||||
end, features(Host, Node))};
|
||||
<<?NS_COMMANDS>> ->
|
||||
command_disco_info(Host, Node, From);
|
||||
@@ -1091,7 +897,7 @@
|
||||
@@ -1122,7 +932,7 @@
|
||||
node_disco_info(Host, Node, From)
|
||||
end.
|
||||
|
||||
@ -349,7 +396,7 @@
|
||||
case tree_action(Host, get_subnodes, [Host, <<>>, From]) of
|
||||
Nodes when is_list(Nodes) ->
|
||||
{result, lists:map(
|
||||
@@ -1108,14 +914,14 @@
|
||||
@@ -1139,23 +949,24 @@
|
||||
Other ->
|
||||
Other
|
||||
end;
|
||||
@ -367,21 +414,20 @@
|
||||
case string:tokens(Item, "!") of
|
||||
[_SNode, _ItemID] ->
|
||||
{result, []};
|
||||
@@ -1123,10 +929,10 @@
|
||||
[SNode] ->
|
||||
Node = string_to_node(SNode),
|
||||
Action =
|
||||
fun(#pubsub_node{type = Type, id = NodeId}) ->
|
||||
- % TODO call get_items/6 instead for access control (EJAB-1033)
|
||||
- NodeItems = case node_call(Type, get_items, [NodeId, From]) of
|
||||
+ %% TODO call get_items/6 instead for access control (EJAB-1033)
|
||||
+ {NodeItems, RsmOut} = case node_call(Type, get_items, [NodeId, From, RSM]) of
|
||||
{result, I} -> I;
|
||||
- Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) ->
|
||||
- NodeItems = case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
|
||||
+ Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) ->
|
||||
+ Owners = node_owners_call(Type, Idx),
|
||||
+ {NodeItems, RsmOut} = case get_allowed_items_call(Host, Idx, From, Type, Options, Owners, RSM) of
|
||||
{result, R} -> R;
|
||||
- _ -> []
|
||||
+ _ -> {[], none}
|
||||
end,
|
||||
Nodes = lists:map(
|
||||
fun(#pubsub_node{nodeid = {_, SubNode}, options = Options}) ->
|
||||
@@ -1144,7 +950,7 @@
|
||||
fun(#pubsub_node{nodeid = {_, SubNode}, options = SubOptions}) ->
|
||||
@@ -1173,7 +984,7 @@
|
||||
{result, Name} = node_call(Type, get_item_name, [Host, Node, RN]),
|
||||
{xmlelement, "item", [{"jid", Host}, {"name", Name}], []}
|
||||
end, NodeItems),
|
||||
@ -390,7 +436,7 @@
|
||||
end,
|
||||
case transaction(Host, Node, Action, sync_dirty) of
|
||||
{result, {_, Result}} -> {result, Result};
|
||||
@@ -1273,7 +1079,8 @@
|
||||
@@ -1284,7 +1095,8 @@
|
||||
(_, Acc) ->
|
||||
Acc
|
||||
end, [], xml:remove_cdata(Els)),
|
||||
@ -400,7 +446,7 @@
|
||||
{get, "subscriptions"} ->
|
||||
get_subscriptions(Host, Node, From, Plugins);
|
||||
{get, "affiliations"} ->
|
||||
@@ -1296,7 +1103,9 @@
|
||||
@@ -1307,7 +1119,9 @@
|
||||
|
||||
iq_pubsub_owner(Host, ServerHost, From, IQType, SubEl, Lang) ->
|
||||
{xmlelement, _, _, SubEls} = SubEl,
|
||||
@ -411,7 +457,7 @@
|
||||
case Action of
|
||||
[{xmlelement, Name, Attrs, Els}] ->
|
||||
Node = string_to_node(xml:get_attr_s("node", Attrs)),
|
||||
@@ -1426,7 +1235,8 @@
|
||||
@@ -1437,7 +1251,8 @@
|
||||
_ -> []
|
||||
end
|
||||
end,
|
||||
@ -421,7 +467,7 @@
|
||||
sync_dirty) of
|
||||
{result, Res} -> Res;
|
||||
Err -> Err
|
||||
@@ -1465,7 +1275,7 @@
|
||||
@@ -1476,7 +1291,7 @@
|
||||
|
||||
%%% authorization handling
|
||||
|
||||
@ -430,7 +476,7 @@
|
||||
Lang = "en", %% TODO fix
|
||||
Stanza = {xmlelement, "message",
|
||||
[],
|
||||
@@ -1494,7 +1304,7 @@
|
||||
@@ -1505,7 +1320,7 @@
|
||||
[{xmlelement, "value", [], [{xmlcdata, "false"}]}]}]}]},
|
||||
lists:foreach(fun(Owner) ->
|
||||
ejabberd_router:route(service_jid(Host), jlib:make_jid(Owner), Stanza)
|
||||
@ -439,7 +485,7 @@
|
||||
|
||||
find_authorization_response(Packet) ->
|
||||
{xmlelement, _Name, _Attrs, Els} = Packet,
|
||||
@@ -1558,8 +1368,8 @@
|
||||
@@ -1569,8 +1384,8 @@
|
||||
"true" -> true;
|
||||
_ -> false
|
||||
end,
|
||||
@ -450,16 +496,16 @@
|
||||
{result, Subscriptions} = node_call(Type, get_subscriptions, [NodeId, Subscriber]),
|
||||
if
|
||||
not IsApprover ->
|
||||
@@ -1750,7 +1560,7 @@
|
||||
@@ -1769,7 +1584,7 @@
|
||||
Reply = [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}],
|
||||
[{xmlelement, "create", nodeAttr(Node),
|
||||
[]}]}],
|
||||
- case transaction(CreateNode, transaction) of
|
||||
+ case transaction(Host, CreateNode, transaction) of
|
||||
{result, {Result, broadcast}} ->
|
||||
%%Lang = "en", %% TODO: fix
|
||||
%%OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
|
||||
@@ -1858,7 +1668,7 @@
|
||||
{result, {NodeId, SubsByDepth, {Result, broadcast}}} ->
|
||||
broadcast_created_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth),
|
||||
case Result of
|
||||
@@ -1872,7 +1687,7 @@
|
||||
%%<li>The node does not exist.</li>
|
||||
%%</ul>
|
||||
subscribe_node(Host, Node, From, JID, Configuration) ->
|
||||
@ -468,33 +514,24 @@
|
||||
{result, GoodSubOpts} -> GoodSubOpts;
|
||||
_ -> invalid
|
||||
end,
|
||||
@@ -1866,7 +1676,7 @@
|
||||
@@ -1880,7 +1695,7 @@
|
||||
error -> {"", "", ""};
|
||||
J -> jlib:jid_tolower(J)
|
||||
end,
|
||||
- Action = fun(#pubsub_node{options = Options, owners = [Owner|_], type = Type, id = NodeId}) ->
|
||||
- Action = fun(#pubsub_node{options = Options, owners = Owners, type = Type, id = NodeId}) ->
|
||||
+ Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId}) ->
|
||||
Features = features(Type),
|
||||
SubscribeFeature = lists:member("subscribe", Features),
|
||||
OptionsFeature = lists:member("subscription-options", Features),
|
||||
@@ -1885,9 +1695,13 @@
|
||||
{"", "", ""} ->
|
||||
{false, false};
|
||||
_ ->
|
||||
- {OU, OS, _} = Owner,
|
||||
- get_roster_info(OU, OS,
|
||||
- Subscriber, AllowedGroups)
|
||||
+ case node_owners_call(Type, NodeId) of
|
||||
+ [{OU, OS, _}|_] ->
|
||||
+ get_roster_info(OU, OS,
|
||||
+ Subscriber, AllowedGroups);
|
||||
+ _ ->
|
||||
+ {false, false}
|
||||
+ end
|
||||
end
|
||||
end,
|
||||
@@ -1889,6 +1704,7 @@
|
||||
AccessModel = get_option(Options, access_model),
|
||||
SendLast = get_option(Options, send_last_published_item),
|
||||
AllowedGroups = get_option(Options, roster_groups_allowed, []),
|
||||
+ Owners = node_owners_call(Type, NodeId),
|
||||
{PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, Subscriber, Owners, AccessModel, AllowedGroups),
|
||||
if
|
||||
@@ -2218,7 +2032,7 @@
|
||||
not SubscribeFeature ->
|
||||
@@ -2218,7 +2034,7 @@
|
||||
%% <p>The permission are not checked in this function.</p>
|
||||
%% @todo We probably need to check that the user doing the query has the right
|
||||
%% to read the items.
|
||||
@ -503,7 +540,22 @@
|
||||
MaxItems =
|
||||
if
|
||||
SMaxItems == "" -> get_max_items_node(Host);
|
||||
@@ -2257,11 +2071,11 @@
|
||||
@@ -2232,12 +2048,13 @@
|
||||
{error, Error} ->
|
||||
{error, Error};
|
||||
_ ->
|
||||
- Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId, owners = Owners}) ->
|
||||
+ Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId}) ->
|
||||
Features = features(Type),
|
||||
RetreiveFeature = lists:member("retrieve-items", Features),
|
||||
PersistentFeature = lists:member("persistent-items", Features),
|
||||
AccessModel = get_option(Options, access_model),
|
||||
AllowedGroups = get_option(Options, roster_groups_allowed, []),
|
||||
+ Owners = node_owners_call(Type, NodeId),
|
||||
{PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups),
|
||||
if
|
||||
not RetreiveFeature ->
|
||||
@@ -2250,11 +2067,11 @@
|
||||
node_call(Type, get_items,
|
||||
[NodeId, From,
|
||||
AccessModel, PresenceSubscription, RosterGroup,
|
||||
@ -517,7 +569,7 @@
|
||||
SendItems = case ItemIDs of
|
||||
[] ->
|
||||
Items;
|
||||
@@ -2274,7 +2088,8 @@
|
||||
@@ -2267,7 +2084,8 @@
|
||||
%% number of items sent to MaxItems:
|
||||
{result, [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}],
|
||||
[{xmlelement, "items", nodeAttr(Node),
|
||||
@ -527,7 +579,24 @@
|
||||
Error ->
|
||||
Error
|
||||
end
|
||||
@@ -2306,16 +2121,27 @@
|
||||
@@ -2289,10 +2107,15 @@
|
||||
Error -> Error
|
||||
end.
|
||||
get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) ->
|
||||
+ case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners, none) of
|
||||
+ {result, {I, _}} -> {result, I};
|
||||
+ Error -> Error
|
||||
+ end.
|
||||
+get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners, RSM) ->
|
||||
AccessModel = get_option(Options, access_model),
|
||||
AllowedGroups = get_option(Options, roster_groups_allowed, []),
|
||||
{PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups),
|
||||
- node_call(Type, get_items, [NodeIdx, From, AccessModel, PresenceSubscription, RosterGroup, undefined]).
|
||||
+ node_call(Type, get_items, [NodeIdx, From, AccessModel, PresenceSubscription, RosterGroup, undefined, RSM]).
|
||||
|
||||
|
||||
%% @spec (Host, Node, NodeId, Type, LJID, Number) -> any()
|
||||
@@ -2305,16 +2128,27 @@
|
||||
%% @doc <p>Resend the items of a node to the user.</p>
|
||||
%% @todo use cache-last-item feature
|
||||
send_items(Host, Node, NodeId, Type, LJID, last) ->
|
||||
@ -538,38 +607,41 @@
|
||||
+ % special ODBC optimization, works only with node_hometree_odbc, node_flat_odbc and node_pep_odbc
|
||||
+ case node_action(Host, Type, get_last_items, [NodeId, LJID, 1]) of
|
||||
+ {result, [LastItem]} ->
|
||||
+ {ModifNow, ModifLjid} = LastItem#pubsub_item.modification,
|
||||
+ {ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
|
||||
+ event_stanza_with_delay(
|
||||
+ [{xmlelement, "items", nodeAttr(Node),
|
||||
+ itemsEls([LastItem])}], ModifNow, ModifLjid);
|
||||
+ itemsEls([LastItem])}], ModifNow, ModifUSR);
|
||||
+ _ ->
|
||||
+ event_stanza(
|
||||
+ [{xmlelement, "items", nodeAttr(Node),
|
||||
+ itemsEls([])}])
|
||||
+ end;
|
||||
LastItem ->
|
||||
{ModifNow, ModifLjid} = LastItem#pubsub_item.modification,
|
||||
{ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
|
||||
- Stanza = event_stanza_with_delay(
|
||||
+ event_stanza_with_delay(
|
||||
[{xmlelement, "items", nodeAttr(Node),
|
||||
- itemsEls([LastItem])}], ModifNow, ModifLjid),
|
||||
- itemsEls([LastItem])}], ModifNow, ModifUSR),
|
||||
- ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza)
|
||||
- end;
|
||||
+ itemsEls([LastItem])}], ModifNow, ModifLjid)
|
||||
+ itemsEls([LastItem])}], ModifNow, ModifUSR)
|
||||
+ end,
|
||||
+ ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza);
|
||||
send_items(Host, Node, NodeId, Type, LJID, Number) ->
|
||||
ToSend = case node_action(Host, Type, get_items, [NodeId, LJID]) of
|
||||
{result, []} ->
|
||||
@@ -2441,29 +2267,12 @@
|
||||
@@ -2440,7 +2274,8 @@
|
||||
error ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
_ ->
|
||||
- Action = fun(#pubsub_node{owners = Owners, type = Type, id = NodeId}=N) ->
|
||||
- case lists:member(Owner, Owners) of
|
||||
+ Action = fun(#pubsub_node{type = Type, id = NodeId}) ->
|
||||
+ case lists:member(Owner, node_owners_call(Type, NodeId)) of
|
||||
+ Owners = node_owners_call(Type, NodeId),
|
||||
case lists:member(Owner, Owners) of
|
||||
true ->
|
||||
OwnerJID = jlib:make_jid(Owner),
|
||||
@@ -2450,24 +2285,7 @@
|
||||
end,
|
||||
lists:foreach(
|
||||
fun({JID, Affiliation}) ->
|
||||
- node_call(Type, set_affiliation, [NodeId, JID, Affiliation]),
|
||||
@ -591,10 +663,10 @@
|
||||
- ok
|
||||
- end
|
||||
+ node_call(Type, set_affiliation, [NodeId, JID, Affiliation])
|
||||
end, Entities),
|
||||
end, FilteredEntities),
|
||||
{result, []};
|
||||
_ ->
|
||||
@@ -2516,11 +2325,11 @@
|
||||
@@ -2520,11 +2338,11 @@
|
||||
end.
|
||||
|
||||
read_sub(Subscriber, Node, NodeID, SubID, Lang) ->
|
||||
@ -608,7 +680,7 @@
|
||||
OptionsEl = {xmlelement, "options", [{"jid", jlib:jid_to_string(Subscriber)},
|
||||
{"subid", SubID}|nodeAttr(Node)],
|
||||
[XdataEl]},
|
||||
@@ -2546,7 +2355,7 @@
|
||||
@@ -2550,7 +2368,7 @@
|
||||
end.
|
||||
|
||||
set_options_helper(Configuration, JID, NodeID, SubID, Type) ->
|
||||
@ -617,7 +689,7 @@
|
||||
{result, GoodSubOpts} -> GoodSubOpts;
|
||||
_ -> invalid
|
||||
end,
|
||||
@@ -2575,7 +2384,7 @@
|
||||
@@ -2579,7 +2397,7 @@
|
||||
write_sub(_Subscriber, _NodeID, _SubID, invalid) ->
|
||||
{error, extended_error(?ERR_BAD_REQUEST, "invalid-options")};
|
||||
write_sub(Subscriber, NodeID, SubID, Options) ->
|
||||
@ -626,7 +698,7 @@
|
||||
{error, notfound} ->
|
||||
{error, extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
|
||||
{result, _} ->
|
||||
@@ -2743,8 +2552,8 @@
|
||||
@@ -2747,8 +2565,8 @@
|
||||
{"subscription", subscription_to_string(Sub)} | nodeAttr(Node)], []}]}]},
|
||||
ejabberd_router:route(service_jid(Host), jlib:make_jid(JID), Stanza)
|
||||
end,
|
||||
@ -637,7 +709,7 @@
|
||||
true ->
|
||||
Result = lists:foldl(fun({JID, Subscription, SubId}, Acc) ->
|
||||
|
||||
@@ -3023,7 +2832,7 @@
|
||||
@@ -3103,7 +2921,7 @@
|
||||
{Depth, [{N, get_node_subs(N)} || N <- Nodes]}
|
||||
end, tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]))}
|
||||
end,
|
||||
@ -646,7 +718,7 @@
|
||||
{result, CollSubs} -> CollSubs;
|
||||
_ -> []
|
||||
end.
|
||||
@@ -3037,9 +2846,9 @@
|
||||
@@ -3117,9 +2935,9 @@
|
||||
|
||||
get_options_for_subs(NodeID, Subs) ->
|
||||
lists:foldl(fun({JID, subscribed, SubID}, Acc) ->
|
||||
@ -658,7 +730,7 @@
|
||||
_ -> Acc
|
||||
end;
|
||||
(_, Acc) ->
|
||||
@@ -3236,6 +3045,30 @@
|
||||
@@ -3323,6 +3141,30 @@
|
||||
Result
|
||||
end.
|
||||
|
||||
@ -689,7 +761,7 @@
|
||||
%% @spec (Host, Options) -> MaxItems
|
||||
%% Host = host()
|
||||
%% Options = [Option]
|
||||
@@ -3631,7 +3464,13 @@
|
||||
@@ -3719,7 +3561,13 @@
|
||||
tree_action(Host, Function, Args) ->
|
||||
?DEBUG("tree_action ~p ~p ~p",[Host,Function,Args]),
|
||||
Fun = fun() -> tree_call(Host, Function, Args) end,
|
||||
@ -704,7 +776,7 @@
|
||||
|
||||
%% @doc <p>node plugin call.</p>
|
||||
node_call(Type, Function, Args) ->
|
||||
@@ -3651,13 +3490,13 @@
|
||||
@@ -3739,13 +3587,13 @@
|
||||
|
||||
node_action(Host, Type, Function, Args) ->
|
||||
?DEBUG("node_action ~p ~p ~p ~p",[Host,Type,Function,Args]),
|
||||
@ -720,9 +792,16 @@
|
||||
case tree_call(Host, get_node, [Host, Node]) of
|
||||
N when is_record(N, pubsub_node) ->
|
||||
case Action(N) of
|
||||
@@ -3670,8 +3509,14 @@
|
||||
@@ -3757,13 +3605,19 @@
|
||||
Error
|
||||
end
|
||||
end, Trans).
|
||||
-transaction(Host, Action, Trans) ->
|
||||
- transaction(fun() ->
|
||||
+transaction_on_nodes(Host, Action, Trans) ->
|
||||
+ transaction(Host, fun() ->
|
||||
{result, lists:foldl(Action, [], tree_call(Host, get_nodes, [Host]))}
|
||||
end, Trans).
|
||||
|
||||
-transaction(Fun, Trans) ->
|
||||
- case catch mnesia:Trans(Fun) of
|
||||
@ -737,7 +816,7 @@
|
||||
{result, Result} -> {result, Result};
|
||||
{error, Error} -> {error, Error};
|
||||
{atomic, {result, Result}} -> {result, Result};
|
||||
@@ -3679,6 +3524,15 @@
|
||||
@@ -3771,6 +3625,15 @@
|
||||
{aborted, Reason} ->
|
||||
?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]),
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
@ -753,7 +832,7 @@
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]),
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
@@ -3687,6 +3541,17 @@
|
||||
@@ -3779,6 +3642,17 @@
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||
end.
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
-export([start/2,
|
||||
stop/1,
|
||||
stream_feature_register/1,
|
||||
stream_feature_register/2,
|
||||
unauthenticated_iq_register/4,
|
||||
try_register/5,
|
||||
process_iq/3]).
|
||||
@ -65,7 +65,7 @@ stop(Host) ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_REGISTER).
|
||||
|
||||
|
||||
stream_feature_register(Acc) ->
|
||||
stream_feature_register(Acc, _Host) ->
|
||||
[{xmlelement, "register",
|
||||
[{"xmlns", ?NS_FEATURE_IQREGISTER}], []} | Acc].
|
||||
|
||||
@ -187,6 +187,18 @@ process_iq(From, To,
|
||||
sub_el = [SubEl, ?ERR_BAD_REQUEST]}
|
||||
end;
|
||||
get ->
|
||||
{UsernameSubels, QuerySubels} =
|
||||
case From of
|
||||
#jid{user = User, lserver = Server} ->
|
||||
case ejabberd_auth:is_user_exists(User,Server) of
|
||||
true ->
|
||||
{[{xmlcdata, User}], [{xmlelement, "registered", [], []}]};
|
||||
false ->
|
||||
{[{xmlcdata, User}], []}
|
||||
end;
|
||||
_ ->
|
||||
{[], []}
|
||||
end,
|
||||
IQ#iq{type = result,
|
||||
sub_el = [{xmlelement,
|
||||
"query",
|
||||
@ -197,8 +209,9 @@ process_iq(From, To,
|
||||
Lang,
|
||||
"Choose a username and password "
|
||||
"to register with this server")}]},
|
||||
{xmlelement, "username", [], []},
|
||||
{xmlelement, "password", [], []}]}]}
|
||||
{xmlelement, "username", [], UsernameSubels},
|
||||
{xmlelement, "password", [], []}
|
||||
| QuerySubels]}]}
|
||||
end.
|
||||
|
||||
%% @doc Try to change password and return IQ response
|
||||
|
@ -655,6 +655,7 @@ push_user_to_members(User, Server, Subscription) ->
|
||||
UserGroups = get_user_displayed_groups(LUser, LServer, GroupsOpts),
|
||||
lists:foreach(
|
||||
fun(Group) ->
|
||||
remove_user_from_group(LServer, {LUser, LServer}, Group),
|
||||
GroupOpts = proplists:get_value(Group, GroupsOpts, []),
|
||||
GroupName = proplists:get_value(name, GroupOpts, Group),
|
||||
lists:foreach(
|
||||
|
@ -57,10 +57,7 @@ process_local_iq(#jid{user = User, server = Server, resource = Resource}, _To,
|
||||
get_ip({User, Server, Resource}, IQ);
|
||||
|
||||
process_local_iq(_From, _To, #iq{type = 'set', sub_el = SubEl} = IQ) ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
|
||||
process_local_iq(_From, _To, #iq{sub_el = SubEl} = IQ) ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_UNEXPECTED_REQUEST]}.
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}.
|
||||
|
||||
|
||||
process_sm_iq(#jid{user = User, server = Server, resource = Resource},
|
||||
@ -72,11 +69,7 @@ process_sm_iq(_From, _To, #iq{type = 'get', sub_el = SubEl} = IQ) ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]};
|
||||
|
||||
process_sm_iq(_From, _To, #iq{type = 'set', sub_el = SubEl} = IQ) ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
|
||||
|
||||
process_sm_iq(_From, _To, #iq{sub_el = SubEl} = IQ) ->
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_UNEXPECTED_REQUEST]}.
|
||||
|
||||
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}.
|
||||
|
||||
get_ip({User, Server, Resource},
|
||||
#iq{sub_el = {xmlelement, Name, Attrs, _} = SubEl} = IQ) ->
|
||||
|
@ -267,7 +267,8 @@ set_vcard(User, LServer, VCARD) ->
|
||||
orgunit = OrgUnit, lorgunit = LOrgUnit
|
||||
})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
mnesia:transaction(F),
|
||||
ejabberd_hooks:run(vcard_set, LServer, [LUser, LServer, VCARD])
|
||||
end.
|
||||
|
||||
-define(TLFIELD(Type, Label, Var),
|
||||
|
@ -62,7 +62,7 @@
|
||||
servers,
|
||||
backups,
|
||||
port,
|
||||
encrypt,
|
||||
tls_options,
|
||||
dn,
|
||||
base,
|
||||
password,
|
||||
@ -181,7 +181,7 @@ init([Host, Opts]) ->
|
||||
State#state.port,
|
||||
State#state.dn,
|
||||
State#state.password,
|
||||
State#state.encrypt),
|
||||
State#state.tls_options),
|
||||
case State#state.search of
|
||||
true ->
|
||||
ejabberd_router:register_route(State#state.myhost);
|
||||
@ -686,6 +686,11 @@ parse_options(Host, Opts) ->
|
||||
ejabberd_config:get_local_option({ldap_encrypt, Host});
|
||||
E -> E
|
||||
end,
|
||||
LDAPTLSVerify = case gen_mod:get_opt(ldap_tls_verify, Opts, undefined) of
|
||||
undefined ->
|
||||
ejabberd_config:get_local_option({ldap_tls_verify, Host});
|
||||
Verify -> Verify
|
||||
end,
|
||||
LDAPPortTemp = case gen_mod:get_opt(ldap_port, Opts, undefined) of
|
||||
undefined ->
|
||||
ejabberd_config:get_local_option({ldap_port, Host});
|
||||
@ -766,7 +771,8 @@ parse_options(Host, Opts) ->
|
||||
servers = LDAPServers,
|
||||
backups = LDAPBackups,
|
||||
port = LDAPPort,
|
||||
encrypt = LDAPEncrypt,
|
||||
tls_options = [{encrypt, LDAPEncrypt},
|
||||
{tls_verify, LDAPTLSVerify}],
|
||||
dn = RootDN,
|
||||
base = LDAPBase,
|
||||
password = Password,
|
||||
|
@ -216,7 +216,7 @@ set_vcard(User, LServer, VCARD) ->
|
||||
Username = ejabberd_odbc:escape(User),
|
||||
LUsername = ejabberd_odbc:escape(LUser),
|
||||
SVCARD = ejabberd_odbc:escape(
|
||||
lists:flatten(xml:element_to_string(VCARD))),
|
||||
xml:element_to_binary(VCARD)),
|
||||
|
||||
SFN = ejabberd_odbc:escape(FN),
|
||||
SLFN = ejabberd_odbc:escape(LFN),
|
||||
@ -247,7 +247,9 @@ set_vcard(User, LServer, VCARD) ->
|
||||
SLLocality, SLMiddle, SLNickname,
|
||||
SLOrgName, SLOrgUnit, SLocality,
|
||||
SMiddle, SNickname, SOrgName,
|
||||
SOrgUnit, SVCARD, Username)
|
||||
SOrgUnit, SVCARD, Username),
|
||||
|
||||
ejabberd_hooks:run(vcard_set, LServer, [LUser, LServer, VCARD])
|
||||
end.
|
||||
|
||||
-define(TLFIELD(Type, Label, Var),
|
||||
|
121
src/mod_vcard_xupdate.erl
Normal file
121
src/mod_vcard_xupdate.erl
Normal file
@ -0,0 +1,121 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_vcard_xupdate.erl
|
||||
%%% Author : Igor Goryachev <igor@goryachev.org>
|
||||
%%% Purpose : Add avatar hash in presence on behalf of client (XEP-0153)
|
||||
%%% Created : 9 Mar 2007 by Igor Goryachev <igor@goryachev.org>
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(mod_vcard_xupdate).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
%% gen_mod callbacks
|
||||
-export([start/2,
|
||||
stop/1]).
|
||||
|
||||
%% hooks
|
||||
-export([update_presence/3,
|
||||
vcard_set/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(vcard_xupdate, {us, hash}).
|
||||
|
||||
%%====================================================================
|
||||
%% gen_mod callbacks
|
||||
%%====================================================================
|
||||
|
||||
start(Host, _Opts) ->
|
||||
mnesia:create_table(vcard_xupdate,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, vcard_xupdate)}]),
|
||||
ejabberd_hooks:add(c2s_update_presence, Host,
|
||||
?MODULE, update_presence, 100),
|
||||
ejabberd_hooks:add(vcard_set, Host,
|
||||
?MODULE, vcard_set, 100),
|
||||
ok.
|
||||
|
||||
stop(Host) ->
|
||||
ejabberd_hooks:delete(c2s_update_presence, Host,
|
||||
?MODULE, update_presence, 100),
|
||||
ejabberd_hooks:delete(vcard_set, Host,
|
||||
?MODULE, vcard_set, 100),
|
||||
ok.
|
||||
|
||||
%%====================================================================
|
||||
%% Hooks
|
||||
%%====================================================================
|
||||
|
||||
update_presence({xmlelement, "presence", Attrs, _Els} = Packet, User, Host) ->
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
[] ->
|
||||
presence_with_xupdate(Packet, User, Host);
|
||||
_ ->
|
||||
Packet
|
||||
end;
|
||||
update_presence(Packet, _User, _Host) ->
|
||||
Packet.
|
||||
|
||||
vcard_set(LUser, LServer, VCARD) ->
|
||||
US = {LUser, LServer},
|
||||
case xml:get_path_s(VCARD, [{elem, "PHOTO"}, {elem, "BINVAL"}, cdata]) of
|
||||
[] ->
|
||||
remove_xupdate(LUser, LServer);
|
||||
BinVal ->
|
||||
add_xupdate(LUser, LServer, sha:sha(jlib:decode_base64(BinVal)))
|
||||
end,
|
||||
ejabberd_sm:force_update_presence(US).
|
||||
|
||||
%%====================================================================
|
||||
%% Mnesia storage
|
||||
%%====================================================================
|
||||
|
||||
add_xupdate(LUser, LServer, Hash) ->
|
||||
F = fun() ->
|
||||
mnesia:write(#vcard_xupdate{us = {LUser, LServer}, hash = Hash})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
get_xupdate(LUser, LServer) ->
|
||||
case mnesia:dirty_read(vcard_xupdate, {LUser, LServer}) of
|
||||
[#vcard_xupdate{hash = Hash}] ->
|
||||
Hash;
|
||||
_ ->
|
||||
undefined
|
||||
end.
|
||||
|
||||
remove_xupdate(LUser, LServer) ->
|
||||
F = fun() ->
|
||||
mnesia:delete({vcard_xupdate, {LUser, LServer}})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Presence stanza rebuilding
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
presence_with_xupdate({xmlelement, "presence", Attrs, Els}, User, Host) ->
|
||||
XPhotoEl = build_xphotoel(User, Host),
|
||||
Els2 = presence_with_xupdate2(Els, [], XPhotoEl),
|
||||
{xmlelement, "presence", Attrs, Els2}.
|
||||
|
||||
presence_with_xupdate2([], Els2, XPhotoEl) ->
|
||||
lists:reverse([XPhotoEl | Els2]);
|
||||
%% This clause assumes that the x element contains only the XMLNS attribute:
|
||||
presence_with_xupdate2([{xmlelement, "x", [{"xmlns", ?NS_VCARD_UPDATE}], _}
|
||||
| Els], Els2, XPhotoEl) ->
|
||||
presence_with_xupdate2(Els, Els2, XPhotoEl);
|
||||
presence_with_xupdate2([El | Els], Els2, XPhotoEl) ->
|
||||
presence_with_xupdate2(Els, [El | Els2], XPhotoEl).
|
||||
|
||||
build_xphotoel(User, Host) ->
|
||||
Hash = get_xupdate(User, Host),
|
||||
PhotoSubEls = case Hash of
|
||||
Hash when is_list(Hash) ->
|
||||
[{xmlcdata, Hash}];
|
||||
_ ->
|
||||
[]
|
||||
end,
|
||||
PhotoEl = [{xmlelement, "photo", [], PhotoSubEls}],
|
||||
{xmlelement, "x", [{"xmlns", ?NS_VCARD_UPDATE}], PhotoEl}.
|
@ -36,6 +36,10 @@
|
||||
{"Change Password","Canviar password"}.
|
||||
{"Change User Password","Canviar Password d'Usuari"}.
|
||||
{"Chatroom configuration modified","Configuració de la sala de xat modificada"}.
|
||||
{"Chatroom is created","La sala s'ha creat"}.
|
||||
{"Chatroom is destroyed","La sala s'ha destruït"}.
|
||||
{"Chatroom is started","La sala s'ha iniciat"}.
|
||||
{"Chatroom is stopped","La sala s'ha aturat"}.
|
||||
{"Chatrooms","Sales de xat"}.
|
||||
{"Choose a username and password to register with this server","Tria nom d'usuari i password per a registrar-te en aquest servidor"}.
|
||||
{"Choose modules to stop","Selecciona mòduls a detindre"}.
|
||||
@ -246,6 +250,7 @@
|
||||
{"Protocol","Protocol"}.
|
||||
{"Publish-Subscribe","Publicar-subscriure't"}.
|
||||
{"PubSub subscriber request","Petició de subscriptor PubSub"}.
|
||||
{"Purge all items when the relevant publisher goes offline","Eliminar tots els elements quan el publicant relevant es desconnecti"}.
|
||||
{"Queries to the conference members are not allowed in this room"," En aquesta sala no es permeten solicituts als membres de la sala"}.
|
||||
{"RAM and disc copy","Còpia en RAM i disc"}.
|
||||
{"RAM copy","Còpia en RAM"}.
|
||||
@ -298,6 +303,7 @@
|
||||
{"Shut Down Service","Apager el Servei"}.
|
||||
{"~s invites you to the room ~s","~s et convida a la sala ~s"}.
|
||||
{"Specify the access model","Especificar el model d'accés"}.
|
||||
{"Specify the event message type","Especifica el tipus de missatge d'event"}.
|
||||
{"Specify the publisher model","Especificar el model del publicant"}.
|
||||
{"~s's Offline Messages Queue","~s's cua de missatges offline"}.
|
||||
{"Start","Iniciar"}.
|
||||
|
831
src/msgs/ca.po
831
src/msgs/ca.po
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,10 @@
|
||||
{"Change Password","Změnit heslo"}.
|
||||
{"Change User Password","Změnit heslo uživatele"}.
|
||||
{"Chatroom configuration modified","Nastavení diskuzní místnosti bylo změněno"}.
|
||||
{"Chatroom is created","Konference vytvořena"}.
|
||||
{"Chatroom is destroyed","Konference zrušena"}.
|
||||
{"Chatroom is started","Konference spuštěna"}.
|
||||
{"Chatroom is stopped","Konference zastavena"}.
|
||||
{"Chatrooms","Konference"}.
|
||||
{"Choose a username and password to register with this server","Zadejte jméno uživatele a heslo pro registraci na tomto serveru"}.
|
||||
{"Choose modules to stop","Vyberte moduly, které mají být zastaveny"}.
|
||||
@ -246,6 +250,7 @@
|
||||
{"Protocol","Protokol"}.
|
||||
{"Publish-Subscribe","Publish-Subscribe"}.
|
||||
{"PubSub subscriber request","Žádost odběratele PubSub"}.
|
||||
{"Purge all items when the relevant publisher goes offline","Smazat všechny položky, pokud se příslušný poskytovatel odpojí"}.
|
||||
{"Queries to the conference members are not allowed in this room","Požadavky (queries) na členy konference nejsou v této místnosti povolené"}.
|
||||
{"RAM and disc copy","Kopie RAM a disku"}.
|
||||
{"RAM copy","Kopie RAM"}.
|
||||
@ -298,6 +303,7 @@
|
||||
{"Shut Down Service","Vypnout službu"}.
|
||||
{"~s invites you to the room ~s","~s vás zve do místnosti ~s"}.
|
||||
{"Specify the access model","Uveďte přístupový model"}.
|
||||
{"Specify the event message type","Zvolte typ zpráv pro události"}.
|
||||
{"Specify the publisher model","Specifikovat model pro publikování"}.
|
||||
{"~s's Offline Messages Queue","Fronta offline zpráv uživatele ~s"}.
|
||||
{"Start Modules at ","Spustit moduly na "}.
|
||||
|
831
src/msgs/cs.po
831
src/msgs/cs.po
File diff suppressed because it is too large
Load Diff
172
src/msgs/de.msg
172
src/msgs/de.msg
@ -1,20 +1,20 @@
|
||||
{"Access Configuration","Zugangskonfiguration"}.
|
||||
{"Access Control List Configuration","Zugangskontroll-Liste (ACL) Konfiguration"}.
|
||||
{"Access Control List Configuration","Konfiguration der Zugangskontrolllisten"}.
|
||||
{"Access control lists","Zugangskontroll-Listen (ACL)"}.
|
||||
{"Access Control Lists","Zugangskontroll-Listen (ACL)"}.
|
||||
{"Access denied by service policy","Zugang aufgrund der Dienstrichtlinien verweigert"}.
|
||||
{"Access rules","Zugangsregeln"}.
|
||||
{"Access Rules","Zugangsregeln"}.
|
||||
{"Action on user","Aktion auf Benutzer"}.
|
||||
{"Add Jabber ID","Jabber ID hinzufügen"}.
|
||||
{"Add Jabber ID","Jabber-ID hinzufügen"}.
|
||||
{"Add New","Neuen hinzufügen"}.
|
||||
{"Add User","Benutzer hinzufügen"}.
|
||||
{"Administration of ","Administration von "}.
|
||||
{"Administration","Verwaltung"}.
|
||||
{"Administrator privileges required","Administratorenrechte benötigt"}.
|
||||
{"A friendly name for the node","Ein passender Name für den Knoten"}.
|
||||
{"A friendly name for the node","Ein merkbarer Name für den Knoten"}.
|
||||
{"All activity","Alle Aktivitäten"}.
|
||||
{"Allow this Jabber ID to subscribe to this pubsub node?","Erlauben sie dieser Jabber ID das Abonnement dieses pubsub Knotens?"}.
|
||||
{"Allow this Jabber ID to subscribe to this pubsub node?","Dieser Jabber-ID das Abonnement dieses pubsub-Knotens erlauben?"}.
|
||||
{"Allow users to change the subject","Erlaube Benutzern das Thema zu ändern"}.
|
||||
{"Allow users to query other users","Erlaube Benutzern andere Benutzer abzufragen"}.
|
||||
{"Allow users to send invites","Erlaube Benutzern Einladungen zu senden"}.
|
||||
@ -28,85 +28,89 @@
|
||||
{"April","April"}.
|
||||
{"August","August"}.
|
||||
{"Backup","Datensicherung"}.
|
||||
{"Backup Management","Datensicherungsmanagement"}.
|
||||
{"Backup Management","Datensicherungsverwaltung"}.
|
||||
{"Backup of ","Sicherung von "}.
|
||||
{"Backup to File at ","Datensicherung in die Datei "}.
|
||||
{"Bad format","Ungültiges Format"}.
|
||||
{"Birthday","Geburtsdatum"}.
|
||||
{"Change Password","Passwort ändern"}.
|
||||
{"Change User Password","Benutzer Passwort ändern"}.
|
||||
{"Chatroom configuration modified","Chatraum Konfiguration geändert"}.
|
||||
{"Change User Password","Benutzer-Passwort ändern"}.
|
||||
{"Chatroom configuration modified","Chatraum-Konfiguration geändert"}.
|
||||
{"Chatroom is created","Chatraum wurde erstellt"}.
|
||||
{"Chatroom is destroyed","Chatraum wurde entfernt"}.
|
||||
{"Chatroom is started","Chatraum wurde gestartet"}.
|
||||
{"Chatroom is stopped","Chatraum wurde beendet"}.
|
||||
{"Chatrooms","Chaträume"}.
|
||||
{"Choose a username and password to register with this server","Wählen sie zum Registrieren einen Benutzernamen und ein Passwort"}.
|
||||
{"Choose modules to stop","Wähle zu stoppende Module"}.
|
||||
{"Choose storage type of tables","Wähle Speichertyp der Tabellen"}.
|
||||
{"Choose whether to approve this entity's subscription.","Wähle ob dieses Abonnement bestätigt wird."}.
|
||||
{"Choose whether to approve this entity's subscription.","Wähle Sie, ob dieses Abonnement akzeptiert werden soll."}.
|
||||
{"City","Stadt"}.
|
||||
{"Commands","Befehle"}.
|
||||
{"Conference room does not exist","Konferenzraum existiert nicht"}.
|
||||
{"Configuration","Konfiguration"}.
|
||||
{"Configuration of room ~s","Konfiguration für Raum ~s"}.
|
||||
{"Connected Resources:","Verbundene Resourcen"}.
|
||||
{"Connected Resources:","Verbundene Ressourcen"}.
|
||||
{"Connections parameters","Verbindungsparameter"}.
|
||||
{"Country","Land"}.
|
||||
{"CPU Time:","CPU Zeit:"}.
|
||||
{"CPU Time:","CPU-Zeit:"}.
|
||||
{"Database","Datenbank"}.
|
||||
{"Database Tables at ","Datenbank Tabellen bei "}.
|
||||
{"Database Tables Configuration at ","Datenbank Tabellen Konfiguration bei "}.
|
||||
{"Database Tables at ","Datenbanktabellen auf "}.
|
||||
{"Database Tables Configuration at ","Datenbanktabellen-Konfiguration auf "}.
|
||||
{"December","Dezember"}.
|
||||
{"Default users as participants","Standardbenutzer als Teilnehmer"}.
|
||||
{"Delete","Löschen"}.
|
||||
{"Delete message of the day","Lösche Nachricht des Tages"}.
|
||||
{"Delete message of the day on all hosts","Lösche Nachricht des Tages auf allen Hosts"}.
|
||||
{"Delete Selected","Markiertes löschen"}.
|
||||
{"Delete Selected","Markierte löschen"}.
|
||||
{"Delete User","Benutzer löschen"}.
|
||||
{"Deliver event notifications","Versende Ereignisbenachrichtigungen"}.
|
||||
{"Deliver payloads with event notifications","Versende Nutzlast mit Ereignisbenachrichtigungen"}.
|
||||
{"Deliver event notifications","Ereignisbenachrichtigung zustellen"}.
|
||||
{"Deliver payloads with event notifications","Nachrichten mit mit Ereignis-Benachrichtigungen zustellen"}.
|
||||
{"Description:","Beschreibung:"}.
|
||||
{"Disc only copy","Festplatten Kopie"}.
|
||||
{"Disc only copy","Nur auf Festplatte"}.
|
||||
{"Displayed Groups:","Angezeigte Gruppen:"}.
|
||||
{"Dump Backup to Text File at ","Ausgabe der Sicherung in diese Textdatei "}.
|
||||
{"Dump to Text File","Ausgabe in Textdatei"}.
|
||||
{"Edit Properties","Einstellungen ändern"}.
|
||||
{"ejabberd IRC module","ejabberd IRC Modul"}.
|
||||
{"ejabberd MUC module","ejabberd MUC Modul"}.
|
||||
{"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe Modul"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams Modul"}.
|
||||
{"ejabberd vCard module","ejabberd vCard Modul"}.
|
||||
{"ejabberd IRC module","ejabberd IRC-Modul"}.
|
||||
{"ejabberd MUC module","ejabberd MUC-Modul"}.
|
||||
{"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe-Modul"}.
|
||||
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5-Bytestreams-Modul"}.
|
||||
{"ejabberd vCard module","ejabberd vCard-Modul"}.
|
||||
{"ejabberd virtual hosts","ejabberd virtuelle Hosts"}.
|
||||
{"ejabberd Web Admin","ejabberd Web Admin"}.
|
||||
{"ejabberd Web Admin","ejabberd Web-Admin"}.
|
||||
{"Elements","Elemente"}.
|
||||
{"Email","E-Mail"}.
|
||||
{"Enable logging","Log-Funktion aktivieren"}.
|
||||
{"Enable logging","Protokollierung aktivieren"}.
|
||||
{"Encoding for server ~b","Kodierung für Server ~b"}.
|
||||
{"End User Session","Benutzer Sitzung beenden"}.
|
||||
{"End User Session","Benutzer-Sitzung beenden"}.
|
||||
{"Enter list of {Module, [Options]}","Geben sie eine Liste bestehend aus {Modul, [Optionen]} ein"}.
|
||||
{"Enter nickname you want to register","Geben sie den zu registrierenden Spitznamen ein"}.
|
||||
{"Enter path to backup file","Geben sie den Pfad zur Datensicherung ein"}.
|
||||
{"Enter path to jabberd14 spool dir","Geben sie den Pfad zum jabberd14 spool Verzeichnis ein"}.
|
||||
{"Enter path to jabberd14 spool file","Geben sie den Pfad zur jabberd14 spool Datei ein"}.
|
||||
{"Enter path to jabberd14 spool dir","Geben Sie den Pfad zum jabberd14-Spool-Verzeichnis ein"}.
|
||||
{"Enter path to jabberd14 spool file","Geben Sie den Pfad zur jabberd14-Spool-Datei ein"}.
|
||||
{"Enter path to text file","Geben sie den Pfad zur Textdatei ein"}.
|
||||
{"Enter the text you see","Geben sie den Text den sie sehen ein"}.
|
||||
{"Enter username and encodings you wish to use for connecting to IRC servers. Press 'Next' to get more fields to fill in. Press 'Complete' to save settings.","Geben sie Benutzernamen und Kodierung für Verbindungen zu IRC Servern an. Drücken sie 'Mehr' um leere Felder hinzuzufügen. Drücken sie 'Beenden' um die Einstellungen zu speichern."}.
|
||||
{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Geben sie Benutzernamen, Kodierungen, Ports und Passwörter für die Verbindung zu IRC Servern an"}.
|
||||
{"Erlang Jabber Server","Erlang Jabber Server"}.
|
||||
{"Enter username, encodings, ports and passwords you wish to use for connecting to IRC servers","Geben Sie Benutzernamen und Zeichenkodierung für die Verbindung zum IRC-Server an"}.
|
||||
{"Erlang Jabber Server","Erlang Jabber-Server"}.
|
||||
{"Error","Fehler"}.
|
||||
{"Example: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}].","Beispiel: [{\"irc.lucky.net\", \"koi8-r\", 6667, \"secret\"}, {\"vendetta.fef.net\", \"iso8859-1\", 7000}, {\"irc.sometestserver.net\", \"utf-8\"}]."}.
|
||||
{"Export data of all users in the server to PIEFXIS files (XEP-0227):","Alle Benutzerdaten des Servers in PIEFXIS Dateien (XEP-0227) exportieren:"}.
|
||||
{"Export data of users in a host to PIEFXIS files (XEP-0227):","Alle Benutzerdaten des Hosts in PIEFXIS Dateien (XEP-0227) exportieren:"}.
|
||||
{"Family Name","Nachname"}.
|
||||
{"February","Februar"}.
|
||||
{"Fill in fields to search for any matching Jabber User","Felder ausfüllen, um nach passenden Jabber Benutzern zu suchen"}.
|
||||
{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Füllen sie die Felder aus, um nach passenden Jabber Benutzern zu suchen (beenden sie ein Feld mit *, um auch nach Teilzeichenketten zu suchen)"}.
|
||||
{"Fill in fields to search for any matching Jabber User","Füllen Sie die Felder aus, um nach passenden Jabber-Benutzern zu suchen"}.
|
||||
{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Füllen Sie die Felder aus, um nach passenden Jabber-Benutzern zu suchen (beenden Sie ein Feld mit *, um auch nach Teilzeichenketten zu suchen)"}.
|
||||
{"Friday","Freitag"}.
|
||||
{"From ~s","Von ~s"}.
|
||||
{"From","Von"}.
|
||||
{"Full Name","Gesamter Name"}.
|
||||
{"Full Name","Vollständiger Name"}.
|
||||
{"Get Number of Online Users","Anzahl der angemeldeten Benutzer abrufen"}.
|
||||
{"Get Number of Registered Users","Anzahl der registrierten Benutzer abrufen"}.
|
||||
{"Get User Last Login Time","letzte Anmeldezeit abrufen"}.
|
||||
{"Get User Password","Benutzer Passwort abrufen"}.
|
||||
{"Get User Statistics","Benutzer Statistik abrufen"}.
|
||||
{"Get User Password","Benutzer-Passwort abrufen"}.
|
||||
{"Get User Statistics","Benutzer-Statistiken abrufen"}.
|
||||
{"Group ","Gruppe "}.
|
||||
{"Groups","Gruppen"}.
|
||||
{"has been banned","wurde gebannt"}.
|
||||
@ -123,8 +127,8 @@
|
||||
{"Import User from File at ","Benutzer aus dieser Datei importieren "}.
|
||||
{"Import users data from a PIEFXIS file (XEP-0227):","Benutzerdaten von einer PIEFXIS Datei (XEP-0227) importieren:"}.
|
||||
{"Import users data from jabberd14 spool directory:","Importiere Benutzer von jabberd14 Spool Verzeichnis:"}.
|
||||
{"Import Users from Dir at ","Benutzer vom Verzeichnis importieren "}.
|
||||
{"Import Users From jabberd14 Spool Files","Importiere Benutzer von jabberd14 Spool Dateien"}.
|
||||
{"Import Users from Dir at ","Benutzer importieren aus dem Verzeichnis "}.
|
||||
{"Import Users From jabberd14 Spool Files","Importiere Benutzer aus jabberd14-Spool-Dateien"}.
|
||||
{"Improper message type","Unzulässiger Nachrichtentyp"}.
|
||||
{"Incorrect password","Falsches Passwort"}.
|
||||
{"Invalid affiliation: ~s","Ungültige Mitgliedschaft: ~s"}.
|
||||
@ -136,13 +140,13 @@
|
||||
{"IRC settings","IRC Einstellungen"}.
|
||||
{"IRC Transport","IRC Transport"}.
|
||||
{"IRC username","IRC Benutzername"}.
|
||||
{"IRC Username","IRC Benutzername"}.
|
||||
{"IRC Username","IRC-Benutzername"}.
|
||||
{"is now known as","ist nun bekannt als"}.
|
||||
{"It is not allowed to send private messages","Es ist nicht erlaubt private Nachrichten zu senden"}.
|
||||
{"It is not allowed to send private messages of type \"groupchat\"","Es ist nicht erlaubt private Nachrichten des Typs \"Gruppenchat\" zu senden"}.
|
||||
{"It is not allowed to send private messages to the conference","Es ist nicht erlaubt private Nachrichten an den Raum zu schicken"}.
|
||||
{"Jabber ID","Jabber ID"}.
|
||||
{"Jabber ID ~s is invalid","Jabber ID ~s ist ungültig"}.
|
||||
{"Jabber ID ~s is invalid","Die Jabber-ID ~s ist ungültig"}.
|
||||
{"January","Januar"}.
|
||||
{"Join IRC channel","IRC Channel beitreten"}.
|
||||
{"joins the room","kommt in den Raum"}.
|
||||
@ -150,7 +154,7 @@
|
||||
{"Join the IRC channel in this Jabber ID: ~s","Den IRC Channel mit dieser Jabber ID beitreten: ~s"}.
|
||||
{"July","Juli"}.
|
||||
{"June","Juni"}.
|
||||
{"Last Activity","Letzte Aktion"}.
|
||||
{"Last Activity","Letzte Aktivität"}.
|
||||
{"Last login","Letzte Anmeldung"}.
|
||||
{"Last month","Letzter Monat"}.
|
||||
{"Last year","Letztes Jahr"}.
|
||||
@ -162,8 +166,8 @@
|
||||
{"Make participants list public","Teilnehmerliste öffentlich machen"}.
|
||||
{"Make room captcha protected","Raum mit Verifizierung (Captcha) versehen"}.
|
||||
{"Make room members-only","Raum nur für Mitglieder zugänglich machen"}.
|
||||
{"Make room moderated","Raum modieriert machen"}.
|
||||
{"Make room password protected","Raum passwortgeschützt machen"}.
|
||||
{"Make room moderated","Raum moderiert machen"}.
|
||||
{"Make room password protected","Raum mit Passwort schützen"}.
|
||||
{"Make room persistent","Raum persistent machen"}.
|
||||
{"Make room public searchable","Raum öffentlich suchbar machen"}.
|
||||
{"March","März"}.
|
||||
@ -187,11 +191,11 @@
|
||||
{"Name","Vorname"}.
|
||||
{"Never","Nie"}.
|
||||
{"Nickname Registration at ","Registrieren des Spitznamens bei"}.
|
||||
{"Nickname ~s does not exist in the room","Spitzname ~s existiert im Raum nicht"}.
|
||||
{"Nickname ~s does not exist in the room","Der Spitzname ~s existiert im Raum nicht"}.
|
||||
{"Nickname","Spitzname"}.
|
||||
{"No body provided for announce message","Kein Text für die Ankündigung angegeben"}.
|
||||
{"No Data","Keine Daten"}.
|
||||
{"Node ID","Knoten ID"}.
|
||||
{"Node ID","Knoten-ID"}.
|
||||
{"Node ","Knoten "}.
|
||||
{"Node not found","Knoten nicht gefunden"}.
|
||||
{"Nodes","Knoten"}.
|
||||
@ -200,15 +204,15 @@
|
||||
{"No resource provided","Keine Ressource angegeben"}.
|
||||
{"Not Found","nicht gefunden"}.
|
||||
{"Notify subscribers when items are removed from the node","Abonnenten benachrichtigen, wenn Einträge vom Knoten entfernt werden"}.
|
||||
{"Notify subscribers when the node configuration changes","Abonnenten benachrichtigen, wenn die Knotenkonfiguration sich ändert"}.
|
||||
{"Notify subscribers when the node configuration changes","Abonnenten benachrichtigen, wenn sich die Knotenkonfiguration ändert"}.
|
||||
{"Notify subscribers when the node is deleted","Abonnenten benachrichtigen, wenn der Knoten gelöscht wird"}.
|
||||
{"November","November"}.
|
||||
{"Number of occupants","Anzahl der Teilnehmer"}.
|
||||
{"Number of online users","Anzahl der angemeldeten Benutzer"}.
|
||||
{"Number of registered users","Anzahl der registrierten Benutzer"}.
|
||||
{"October","Oktober"}.
|
||||
{"Offline Messages:","Offline Nachrichten:"}.
|
||||
{"Offline Messages","Offline Nachrichten"}.
|
||||
{"Offline Messages:","Offline-Nachrichten:"}.
|
||||
{"Offline Messages","Offline-Nachrichten"}.
|
||||
{"OK","OK"}.
|
||||
{"Online","Angemeldet"}.
|
||||
{"Online Users:","Angemeldete Benutzer:"}.
|
||||
@ -218,13 +222,13 @@
|
||||
{"Only moderators are allowed to change the subject in this room","Nur Moderatoren dürfen das Thema in diesem Raum ändern"}.
|
||||
{"Only occupants are allowed to send messages to the conference","Nur Teilnehmer dürfen Nachrichten an den Raum schicken"}.
|
||||
{"Only occupants are allowed to send queries to the conference","Nur Teilnehmer sind berechtig Anfragen an die Konferenz zu senden"}.
|
||||
{"Only service administrators are allowed to send service messages","Nur Service Administratoren sind berechtigt, Servicenachrichten zu senden"}.
|
||||
{"Only service administrators are allowed to send service messages","Nur Service-Administratoren sind berechtigt, Servicenachrichten zu versenden"}.
|
||||
{"Options","Optionen"}.
|
||||
{"Organization Name","Firmenname"}.
|
||||
{"Organization Name","Organisation"}.
|
||||
{"Organization Unit","Abteilung"}.
|
||||
{"Outgoing s2s Connections:","Ausgehende s2s Verbindungen:"}.
|
||||
{"Outgoing s2s Connections","Ausgehende s2s Verbindungen"}.
|
||||
{"Outgoing s2s Servers:","Ausgehende s2s Server:"}.
|
||||
{"Outgoing s2s Connections:","Ausgehende s2s-Verbindungen:"}.
|
||||
{"Outgoing s2s Connections","Ausgehende s2s-Verbindungen"}.
|
||||
{"Outgoing s2s Servers:","Ausgehende s2s-Server:"}.
|
||||
{"Owner privileges required","Besitzerrechte benötigt"}.
|
||||
{"Packet","Paket"}.
|
||||
{"Password ~b","Passwort ~b"}.
|
||||
@ -233,24 +237,25 @@
|
||||
{"Password Verification","Passwort bestätigen"}.
|
||||
{"Path to Dir","Pfad zum Verzeichnis"}.
|
||||
{"Path to File","Pfad zur Datei"}.
|
||||
{"Pending","schwebend"}.
|
||||
{"Pending","anhängig"}.
|
||||
{"Period: ","Zeitraum: "}.
|
||||
{"Persist items to storage","Einträge dauerhaft speichern"}.
|
||||
{"Ping","Ping"}.
|
||||
{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Beachten sie, das diese Optionen nur die eingebaute Mnesia Datenbank sichert. Wenn sie das ODBC Modul verwenden müssen sie die SQL Datenbank zusätzlich manuell sichern."}.
|
||||
{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Beachten sie, das diese Optionen nur die eingebaute Mnesia-Datenbank sichern. Wenn sie das ODBC-Modul verwenden, müssen sie die SQL-Datenbank manuell sichern."}.
|
||||
{"Pong","Pong"}.
|
||||
{"Port ~b","Port ~b"}.
|
||||
{"Port","Port"}.
|
||||
{"Present real Jabber IDs to","Echte Jabber IDs anzeigen für"}.
|
||||
{"Present real Jabber IDs to","Echte Jabber-IDs anzeigen für"}.
|
||||
{"private, ","privat, "}.
|
||||
{"Protocol","Protokoll"}.
|
||||
{"Publish-Subscribe","Publish-Subscribe"}.
|
||||
{"PubSub subscriber request","PubSub Abonnenten Anfrage"}.
|
||||
{"PubSub subscriber request","PubSub-Abonnenten-Anfrage"}.
|
||||
{"Purge all items when the relevant publisher goes offline","Alle Einträge entfernen, wenn der relevante Veröffentlicher offline geht"}.
|
||||
{"Queries to the conference members are not allowed in this room","Anfragen an die Teilnehmer sind in diesem Raum nicht erlaubt"}.
|
||||
{"RAM and disc copy","RAM und Festplatten Kopie"}.
|
||||
{"RAM copy","RAM Kopie"}.
|
||||
{"RAM and disc copy","RAM und Festplatte"}.
|
||||
{"RAM copy","Nur RAM"}.
|
||||
{"Raw","Unformatiert"}.
|
||||
{"Really delete message of the day?","Wirklich die Nachricht des Tages löschen?"}.
|
||||
{"Really delete message of the day?","Die Nachricht des Tages wirklich löschen?"}.
|
||||
{"Recipient is not in the conference room","Der Empfänger ist nicht im Raum"}.
|
||||
{"Registered Users:","Registrierte Benutzer:"}.
|
||||
{"Registered Users","Registrierte Benutzer"}.
|
||||
@ -264,11 +269,11 @@
|
||||
{"Restart","Neustart"}.
|
||||
{"Restart Service","Dienst neustarten"}.
|
||||
{"Restore Backup from File at ","Datenwiederherstellung aus der Datei "}.
|
||||
{"Restore binary backup after next ejabberd restart (requires less memory):","Stelle binäre Sicherung beim nächsten ejabberd Neustart wieder her (benötigt weniger Speicher):"}.
|
||||
{"Restore binary backup after next ejabberd restart (requires less memory):","Stelle binäre Sicherung beim nächsten ejabberd-Neustart wieder her (benötigt weniger Speicher):"}.
|
||||
{"Restore binary backup immediately:","Stelle binäre Sicherung sofort wieder her:"}.
|
||||
{"Restore plain text backup immediately:","Stelle Klartext-Sicherung sofort wieder her:"}.
|
||||
{"Restore","Wiederherstellung"}.
|
||||
{"Room Configuration","Raum Konfiguration"}.
|
||||
{"Room Configuration","Raum-Konfiguration"}.
|
||||
{"Room creation is denied by service policy","Anlegen des Raumes aufgrund der Dienstrichtlinien verweigert"}.
|
||||
{"Room description","Raum Beschreibung"}.
|
||||
{"Room Occupants","Teilnehmer in diesem Raum"}.
|
||||
@ -277,11 +282,11 @@
|
||||
{"Roster","Kontaktliste"}.
|
||||
{"Roster of ","Kontaktliste von "}.
|
||||
{"Roster size","Kontaktlistengröße"}.
|
||||
{"RPC Call Error","RPC Abruf-Fehler"}.
|
||||
{"RPC Call Error","Fehler bei RPC-Aufruf"}.
|
||||
{"Running Nodes","Aktive Knoten"}.
|
||||
{"~s access rule configuration","~s Zugangsregel Konfiguration"}.
|
||||
{"~s access rule configuration","~s Zugangsregel-Konfiguration"}.
|
||||
{"Saturday","Samstag"}.
|
||||
{"Script check","Script Überprüfung"}.
|
||||
{"Script check","Script-Überprüfung"}.
|
||||
{"Search Results for ","Suchergebnisse für "}.
|
||||
{"Search users in ","Benutzer suchen in "}.
|
||||
{"Send announcement to all online users on all hosts","Sende Ankündigung an alle angemeldeten Benutzer auf allen Hosts"}.
|
||||
@ -296,49 +301,50 @@
|
||||
{"Show Integral Table","Vollständige Tabelle anzeigen"}.
|
||||
{"Show Ordinary Table","Normale Tabelle anzeigen"}.
|
||||
{"Shut Down Service","Dienst herunterfahren"}.
|
||||
{"~s invites you to the room ~s","~s lädt sie in den Raum ~s ein"}.
|
||||
{"~s invites you to the room ~s","~s lädt Sie in den Raum ~s ein"}.
|
||||
{"Specify the access model","Geben sie das Zugangsmodell an"}.
|
||||
{"Specify the event message type","Geben sie den Ereignis-Nachrichtentyp an"}.
|
||||
{"Specify the publisher model","Geben sie das Publikationsmodell an"}.
|
||||
{"~s's Offline Messages Queue","~s's Offline Nachrichten Warteschlange"}.
|
||||
{"Start","Anfang"}.
|
||||
{"Start Modules at ","Starte Module bei "}.
|
||||
{"~s's Offline Messages Queue","~s's Offline-Nachrichten-Warteschlange"}.
|
||||
{"Start Modules at ","Starte Module auf "}.
|
||||
{"Start Modules","Module starten"}.
|
||||
{"Start","Starten"}.
|
||||
{"Statistics of ~p","Statistiken von ~p"}.
|
||||
{"Statistics","Statistik"}.
|
||||
{"Stop Modules at ","Stoppe Module bei "}.
|
||||
{"Stop Modules at ","Stoppe Module auf "}.
|
||||
{"Stop Modules","Module stoppen"}.
|
||||
{"Stopped Nodes","Inaktive Knoten"}.
|
||||
{"Stop","Stop"}.
|
||||
{"Stop","Stoppen"}.
|
||||
{"Storage Type","Speichertyp"}.
|
||||
{"Store binary backup:","Speichere binäre Sicherung:"}.
|
||||
{"Store plain text backup:","Speichere Klartext-Sicherung:"}.
|
||||
{"Subject","Thema"}.
|
||||
{"Subject","Betreff"}.
|
||||
{"Submit","Senden"}.
|
||||
{"Submitted","Gesendet"}.
|
||||
{"Subscriber Address","Abonnenten Adresse"}.
|
||||
{"Subscriber Address","Abonnenten-Adresse"}.
|
||||
{"Subscription","Abonnement"}.
|
||||
{"Sunday","Sonntag"}.
|
||||
{"That nickname is already in use by another occupant","Dieser Spitzname wird bereits von einem Teilnehmer genutzt"}.
|
||||
{"That nickname is registered by another person","Dieser Spitzname wurde bereits von jemand anderem registriert"}.
|
||||
{"The captcha is valid.","Die Verifizierung ist gültig."}.
|
||||
{"The collections with which a node is affiliated","Sammlungen, mit denen ein Knoten verknüpft ist"}.
|
||||
{"the password is","das Passwort ist"}.
|
||||
{"This participant is kicked from the room because he sent an error message","Dieser Teilnehmer wurde aus dem Raum gekickt, da er eine Fehlernachricht gesendet hat"}.
|
||||
{"This participant is kicked from the room because he sent an error message to another participant","Dieser Teilnehmer wurde aus dem Raum gekickt, da er eine Fehlernachricht an einen anderen Teilnehmer gesendet hat"}.
|
||||
{"the password is","das Passwort lautet"}.
|
||||
{"This participant is kicked from the room because he sent an error message","Dieser Teilnehmer wurde aus dem Raum geworfen, da er eine Fehlernachricht gesendet hat"}.
|
||||
{"This participant is kicked from the room because he sent an error message to another participant","Dieser Teilnehmer wurde aus dem Raum geworfen, da er eine Fehlernachricht an einen anderen Teilnehmer gesendet hat"}.
|
||||
{"This participant is kicked from the room because he sent an error presence","Dieser Teilnehmer wurde aus dem Raum gekickt, da er einen fehlerhaften Status gesendet hat"}.
|
||||
{"This room is not anonymous","Dieser Raum ist nicht anonym"}.
|
||||
{"Thursday","Donnerstag"}.
|
||||
{"Time delay","Zeitverzögerung"}.
|
||||
{"Time","Zeit"}.
|
||||
{"To","An"}.
|
||||
{"To ~s","An ~s"}.
|
||||
{"To","Zu"}.
|
||||
{"Traffic rate limit is exceeded","Datenratenlimit wurde überschritten"}.
|
||||
{"Transactions Aborted:","Vorgänge abgebrochen:"}.
|
||||
{"Transactions Committed:","Vorgänge durchgeführt:"}.
|
||||
{"Transactions Logged:","Vorgänge protokolliert:"}.
|
||||
{"Transactions Restarted:","Vorgänge neu gestartet:"}.
|
||||
{"Transactions Aborted:","Abgebrochene Transaktionen:"}.
|
||||
{"Transactions Committed:","Durchgeführte Transaktionen:"}.
|
||||
{"Transactions Logged:","Protokollierte Transaktionen:"}.
|
||||
{"Transactions Restarted:","Neugestartete Transaktionen:"}.
|
||||
{"Tuesday","Dienstag"}.
|
||||
{"Unable to generate a captcha","Konnte Verifizierung nicht erstellen"}.
|
||||
{"Unable to generate a captcha","Konnte Verifizierung (Captcha) nicht erstellen"}.
|
||||
{"Unauthorized","Nicht berechtigt"}.
|
||||
{"Update","Aktualisieren"}.
|
||||
{"Update ","Aktualisierung "}.
|
||||
@ -350,12 +356,12 @@
|
||||
{"Use of STARTTLS required","Verwendung von STARTTLS erforderlich"}.
|
||||
{"User ","Benutzer "}.
|
||||
{"User","Benutzer"}.
|
||||
{"User Management","Benutzer Verwaltung"}.
|
||||
{"User Management","Benutzerverwaltung"}.
|
||||
{"Users are not allowed to register accounts so quickly","Benutzer dürfen Konten nicht so schnell registrieren"}.
|
||||
{"Users","Benutzer"}.
|
||||
{"Users Last Activity","Letzte Benutzeraktivität"}.
|
||||
{"Validate","Validieren"}.
|
||||
{"vCard User Search","vCard Benutzer Suche"}.
|
||||
{"vCard User Search","vCard-Benutzer-Suche"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Besucher dürfen in diesem Raum ihren Spitznamen nicht ändern"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Besucher dürfen nicht an alle Teilnehmer Nachrichten verschicken"}.
|
||||
{"Wednesday","Mittwoch"}.
|
||||
@ -363,9 +369,9 @@
|
||||
{"Whether to allow subscriptions","Ob Abonnements erlaubt sind"}.
|
||||
{"You have been banned from this room","Sie wurden aus diesem Raum verbannt"}.
|
||||
{"You must fill in field \"Nickname\" in the form","Sie müssen das Feld \"Spitzname\" ausfüllen"}.
|
||||
{"You need an x:data capable client to configure mod_irc settings","Sie benötigen einen Client, der x:data unterstützt, um die mod_irc Einstellungen zu konfigurieren"}.
|
||||
{"You need an x:data capable client to configure mod_irc settings","Sie benötigen einen Client, der x:data unterstützt, um die mod_irc-Einstellungen zu konfigurieren"}.
|
||||
{"You need an x:data capable client to configure room","Sie benötigen einen Client, der x:data unterstützt, um den Raum zu konfigurieren"}.
|
||||
{"You need an x:data capable client to register nickname","Sie benötigen einen Client, der x:data unterstützt, um Ihren Spitznamen zu registrieren"}.
|
||||
{"You need an x:data capable client to search","Sie benötigen einen Client, der x:data unterstützt, um suchen zu können"}.
|
||||
{"Your contact offline message queue is full. The message has been discarded.","Ihre offline Kontakt Warteschlange ist voll. Die Nachricht wurde verworfen."}.
|
||||
{"Your contact offline message queue is full. The message has been discarded.","Ihre Offline-Nachrichten-Warteschlange ist voll. Die Nachricht wurde verworfen."}.
|
||||
{"Your messages to ~s are being blocked. To unblock them, visit ~s","Ihre Nachrichten an ~s werden blockiert. Um dies zu ändern, besuchen sie ~s"}.
|
||||
|
689
src/msgs/de.po
689
src/msgs/de.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,10 @@
|
||||
{"Change Password","Αλλαγή κωδικού"}.
|
||||
{"Change User Password","Αλλαγή Κωδικού Πρόσβασης Χρήστη"}.
|
||||
{"Chatroom configuration modified","Διαμόρφωση Αίθουσaς σύνεδριασης τροποποιηθηκε"}.
|
||||
{"Chatroom is created","Η αίθουσα σύνεδριασης δημιουργήθηκε"}.
|
||||
{"Chatroom is destroyed","Η αίθουσα σύνεδριασης διαγράφηκε"}.
|
||||
{"Chatroom is started","Η αίθουσα σύνεδριασης έχει ξεκινήσει"}.
|
||||
{"Chatroom is stopped","Η αίθουσα σύνεδριασης έχει σταματήσει"}.
|
||||
{"Chatrooms","Αίθουσες σύνεδριασης"}.
|
||||
{"Choose a username and password to register with this server","Επιλέξτε ένα όνομα χρήστη και κωδικό πρόσβασης για να εγγραφείτε σε αυτό τον διακομιστή"}.
|
||||
{"Choose modules to stop","Επιλέξτε modules για να σταματήσουν"}.
|
||||
@ -246,6 +250,7 @@
|
||||
{"Protocol","Πρωτόκολλο"}.
|
||||
{"Publish-Subscribe","Δημοσίευση-Εγγραφή"}.
|
||||
{"PubSub subscriber request","Αίτηση συνδρομητή Δημοσίευσης-Εγγραφής"}.
|
||||
{"Purge all items when the relevant publisher goes offline","Διαγραφή όλων των στοιχείων όταν ο σχετικός εκδότης αποσυνδέεται"}.
|
||||
{"Queries to the conference members are not allowed in this room","Ερωτήματα πρώς τα μέλη της διασκέψεως δεν επιτρέπονται σε αυτήν την αίθουσα"}.
|
||||
{"RAM and disc copy","Αντίγραφο μόνο σε RAM καί δίσκο"}.
|
||||
{"RAM copy","Αντίγραφο σε RAM"}.
|
||||
@ -298,6 +303,7 @@
|
||||
{"Shut Down Service","Κλείσιμο Υπηρεσίας"}.
|
||||
{"~s invites you to the room ~s","~s σας προσκαλεί στην αίθουσα ~s"}.
|
||||
{"Specify the access model","Καθορίστε το μοντέλο πρόσβασης"}.
|
||||
{"Specify the event message type","Καθορίστε τον τύπο μηνύματος συμβάντος"}.
|
||||
{"Specify the publisher model","Καθορίστε το μοντέλο εκδότη"}.
|
||||
{"~s's Offline Messages Queue","Η Σειρά Χωρίς Σύνδεση Μηνύματων τού ~s"}.
|
||||
{"Start Modules at ","Εκκίνηση Modules στο "}.
|
||||
|
835
src/msgs/el.po
835
src/msgs/el.po
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,10 @@
|
||||
{"Change Password","Ŝanĝu pasvorton"}.
|
||||
{"Change User Password","Ŝanĝu pasvorton de uzanto"}.
|
||||
{"Chatroom configuration modified","Agordo de babilejo ŝanĝita"}.
|
||||
{"Chatroom is created","Babilejo kreita"}.
|
||||
{"Chatroom is destroyed","Babilejo neniigita"}.
|
||||
{"Chatroom is started","Babilejo lanĉita"}.
|
||||
{"Chatroom is stopped","Babilejo haltita"}.
|
||||
{"Chatrooms","Babilejoj"}.
|
||||
{"Choose a username and password to register with this server","Elektu uzantnomon kaj pasvorton por registri je ĉi tiu servilo"}.
|
||||
{"Choose modules to stop","Elektu modulojn por fini"}.
|
||||
@ -246,6 +250,7 @@
|
||||
{"Protocol","Protokolo"}.
|
||||
{"Publish-Subscribe","Public-Abonado"}.
|
||||
{"PubSub subscriber request","PubAbo abonpeto"}.
|
||||
{"Purge all items when the relevant publisher goes offline","Forigu ĉiujn erojn kiam la rilata publikanto malkonektiĝas"}.
|
||||
{"Queries to the conference members are not allowed in this room","Malpermesas informmendoj al partoprenantoj en ĉi tiu babilejo"}.
|
||||
{"RAM and disc copy","RAM- kaj disk-kopio"}.
|
||||
{"RAM copy","RAM-kopio"}.
|
||||
@ -298,6 +303,7 @@
|
||||
{"Shut Down Service","Haltigu Servon"}.
|
||||
{"~s invites you to the room ~s","~s invitas vin al la babilejo ~s"}.
|
||||
{"Specify the access model","Specifu atingo-modelon"}.
|
||||
{"Specify the event message type","Specifu tipo de event-mesaĝo"}.
|
||||
{"Specify the publisher model","Enmetu publikadan modelon"}.
|
||||
{"~s's Offline Messages Queue","Mesaĝo-atendovico de ~s"}.
|
||||
{"Start Modules at ","Startu modulojn je "}.
|
||||
|
831
src/msgs/eo.po
831
src/msgs/eo.po
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,10 @@
|
||||
{"Change Password","Cambiar contraseña"}.
|
||||
{"Change User Password","Cambiar contraseña de usuario"}.
|
||||
{"Chatroom configuration modified","Configuración de la sala modificada"}.
|
||||
{"Chatroom is created","Se ha creado la sala"}.
|
||||
{"Chatroom is destroyed","Se ha destruido la sala"}.
|
||||
{"Chatroom is started","Se ha iniciado la sala"}.
|
||||
{"Chatroom is stopped","Se ha detenido la sala"}.
|
||||
{"Chatrooms","Salas de charla"}.
|
||||
{"Choose a username and password to register with this server","Escoge un nombre de usuario y contraseña para registrarte en este servidor"}.
|
||||
{"Choose modules to stop","Selecciona módulos a detener"}.
|
||||
@ -246,6 +250,7 @@
|
||||
{"Protocol","Protocolo"}.
|
||||
{"Publish-Subscribe","Servicio de Publicar-Subscribir"}.
|
||||
{"PubSub subscriber request","Petición de subscriptor de PubSub"}.
|
||||
{"Purge all items when the relevant publisher goes offline","Borra todos los elementos cuando el publicador relevante se desconecta"}.
|
||||
{"Queries to the conference members are not allowed in this room","En esta sala no se permiten solicitudes a los miembros de la sala"}.
|
||||
{"RAM and disc copy","Copia en RAM y disco"}.
|
||||
{"RAM copy","Copia en RAM"}.
|
||||
@ -298,6 +303,7 @@
|
||||
{"Shut Down Service","Detener el servicio"}.
|
||||
{"~s invites you to the room ~s","~s te invita a la sala ~s"}.
|
||||
{"Specify the access model","Especifica el modelo de acceso"}.
|
||||
{"Specify the event message type","Especifica el tipo del mensaje de evento"}.
|
||||
{"Specify the publisher model","Especificar el modelo del publicante"}.
|
||||
{"~s's Offline Messages Queue","Cola de mensajes diferidos de ~s"}.
|
||||
{"Start","Iniciar"}.
|
||||
|
833
src/msgs/es.po
833
src/msgs/es.po
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,10 @@
|
||||
{"Change Password","Modifier le mot de passe"}.
|
||||
{"Change User Password","Changer le mot de passe de l'utilisateur"}.
|
||||
{"Chatroom configuration modified","Configuration du salon modifiée"}.
|
||||
{"Chatroom is created","Le salon de discussion est créé"}.
|
||||
{"Chatroom is destroyed","Le salon de discussion est détruit"}.
|
||||
{"Chatroom is started","Le salon de discussion a démarré"}.
|
||||
{"Chatroom is stopped","Le salon de discussion est stoppé"}.
|
||||
{"Chatrooms","Salons de discussion"}.
|
||||
{"Choose a username and password to register with this server","Choisissez un nom d'utilisateur et un mot de passe pour s'enregistrer sur ce serveur"}.
|
||||
{"Choose modules to stop","Sélectionnez les modules à arrêter"}.
|
||||
@ -246,6 +250,7 @@
|
||||
{"Protocol","Protocole"}.
|
||||
{"Publish-Subscribe","Publication-Abonnement"}.
|
||||
{"PubSub subscriber request","Demande d'abonnement PubSub"}.
|
||||
{"Purge all items when the relevant publisher goes offline","Purger tous les items lorsque publieur est hors-ligne"}.
|
||||
{"Queries to the conference members are not allowed in this room","Les requêtes sur les membres de la conférence ne sont pas autorisé dans ce salon"}.
|
||||
{"RAM and disc copy","Copie en mémoire vive (RAM) et sur disque"}.
|
||||
{"RAM copy","Copie en mémoire vive (RAM)"}.
|
||||
@ -298,6 +303,7 @@
|
||||
{"Shut Down Service","Arrêter le service"}.
|
||||
{"~s invites you to the room ~s","~s vous a invité dans la salle de discussion ~s"}.
|
||||
{"Specify the access model","Définir le modèle d'accès"}.
|
||||
{"Specify the event message type","Définir le type de message d'événement"}.
|
||||
{"Specify the publisher model","Définir le modèle de publication"}.
|
||||
{"~s's Offline Messages Queue","~s messages en file d'attente"}.
|
||||
{"Start","Démarrer"}.
|
||||
|
833
src/msgs/fr.po
833
src/msgs/fr.po
File diff suppressed because it is too large
Load Diff
830
src/msgs/gl.po
830
src/msgs/gl.po
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,10 @@
|
||||
{"Change Password","Modificare la password"}.
|
||||
{"Change User Password","Cambiare la password dell'utente"}.
|
||||
{"Chatroom configuration modified","Configurazione della stanza modificata"}.
|
||||
{"Chatroom is created","La stanza è creata"}.
|
||||
{"Chatroom is destroyed","La stanza è eliminata"}.
|
||||
{"Chatroom is started","La stanza è avviata"}.
|
||||
{"Chatroom is stopped","La stanza è arrestata"}.
|
||||
{"Chatrooms","Stanze"}.
|
||||
{"Choose a username and password to register with this server","Scegliere un nome utente e una password per la registrazione con questo server"}.
|
||||
{"Choose modules to stop","Selezionare i moduli da arrestare"}.
|
||||
@ -246,6 +250,7 @@
|
||||
{"Protocol","Protocollo"}.
|
||||
{"Publish-Subscribe","Pubblicazione-Iscrizione"}.
|
||||
{"PubSub subscriber request","Richiesta di iscrizione per PubSub"}.
|
||||
{"Purge all items when the relevant publisher goes offline","Cancella tutti gli elementi quando chi li ha pubblicati non è più online"}.
|
||||
{"Queries to the conference members are not allowed in this room","In questa stanza non sono consentite query ai membri della conferenza"}.
|
||||
{"RAM and disc copy","Copia in memoria (RAM) e su disco"}.
|
||||
{"RAM copy","Copia in memoria (RAM)"}.
|
||||
@ -298,6 +303,7 @@
|
||||
{"Shut Down Service","Terminare il servizio"}.
|
||||
{"~s invites you to the room ~s","~s ti invita nella stanza ~s"}.
|
||||
{"Specify the access model","Specificare il modello di accesso"}.
|
||||
{"Specify the event message type","Specificare il tipo di messaggio di evento"}.
|
||||
{"Specify the publisher model","Definire il modello di pubblicazione"}.
|
||||
{"~s's Offline Messages Queue","Coda di ~s messaggi offline"}.
|
||||
{"Start","Avviare"}.
|
||||
|
832
src/msgs/it.po
832
src/msgs/it.po
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,10 @@
|
||||
{"Change Password","パスワードの変更"}.
|
||||
{"Change User Password","パスワードを変更"}.
|
||||
{"Chatroom configuration modified","チャットルームの設定を変更しました"}.
|
||||
{"Chatroom is created","チャットルームは作られました。"}.
|
||||
{"Chatroom is destroyed","チャットルームは削除されました。"}.
|
||||
{"Chatroom is started","チャットルームが開始しました。"}.
|
||||
{"Chatroom is stopped","チャットルームが停止しました。"}.
|
||||
{"Chatrooms","チャットルーム"}.
|
||||
{"Choose a username and password to register with this server","サーバーに登録するユーザー名とパスワードを選択して下さい"}.
|
||||
{"Choose modules to stop","停止するモジュールの選択"}.
|
||||
@ -246,6 +250,7 @@
|
||||
{"Protocol","プロトコル"}.
|
||||
{"Publish-Subscribe","Publish-Subscribe"}.
|
||||
{"PubSub subscriber request","PubSub 購読リクエスト"}.
|
||||
{"Purge all items when the relevant publisher goes offline","公開者がオフラインになる時に、全てののアイテムを削除"}.
|
||||
{"Queries to the conference members are not allowed in this room","このチャットルームではカンファレンスメンバーへのクエリーは禁止されています"}.
|
||||
{"RAM and disc copy","RAM, ディスクコピー"}.
|
||||
{"RAM copy","RAMコピー"}.
|
||||
@ -298,6 +303,7 @@
|
||||
{"Shut Down Service","サービスを停止"}.
|
||||
{"~s invites you to the room ~s","~s はあなたをチャットルーム ~s に招待しています"}.
|
||||
{"Specify the access model","アクセスモデルを設定する"}.
|
||||
{"Specify the event message type","イベントメッセージ種別を設定"}.
|
||||
{"Specify the publisher model","公開モデルを指定する"}.
|
||||
{"~s's Offline Messages Queue","~s's オフラインメッセージキュー"}.
|
||||
{"Start Modules at ","モジュールの開始"}.
|
||||
|
831
src/msgs/ja.po
831
src/msgs/ja.po
File diff suppressed because it is too large
Load Diff
@ -25,8 +25,8 @@
|
||||
{"Announcements","Mededelingen"}.
|
||||
{"anyone","iedereen"}.
|
||||
{"A password is required to enter this room","U hebt een wachtwoord nodig om deze chatruimte te kunnen betreden"}.
|
||||
{"April","april"}.
|
||||
{"August","augustus"}.
|
||||
{"April","April"}.
|
||||
{"August","Augustus"}.
|
||||
{"Backup","Backup"}.
|
||||
{"Backup Management","Backup"}.
|
||||
{"Backup of ","Backup maken van "}.
|
||||
@ -36,6 +36,10 @@
|
||||
{"Change Password","Wachtwoord wijzigen"}.
|
||||
{"Change User Password","Verander Gebruikerswachtwoord"}.
|
||||
{"Chatroom configuration modified","De instellingen van de chatruimte werden veranderd"}.
|
||||
{"Chatroom is created","Gespreksruimte gecreëerd"}.
|
||||
{"Chatroom is destroyed","Gespreksruimte vernietigd"}.
|
||||
{"Chatroom is started","Gespreksruimte gestart"}.
|
||||
{"Chatroom is stopped","Gespreksruimte gestopt"}.
|
||||
{"Chatrooms","Groepsgesprekken"}.
|
||||
{"Choose a username and password to register with this server","Kies een gebruikersnaam en een wachtwoord om u te registreren op deze server"}.
|
||||
{"Choose modules to stop","Selecteer de modules die u wilt stoppen"}.
|
||||
@ -53,7 +57,7 @@
|
||||
{"Database","Database"}.
|
||||
{"Database Tables at ","Databasetabellen van "}.
|
||||
{"Database Tables Configuration at ","Instellingen van databasetabellen op "}.
|
||||
{"December","december"}.
|
||||
{"December","December"}.
|
||||
{"Default users as participants","Gebruikers standaard instellen als deelnemers"}.
|
||||
{"Delete message of the day","Bericht van de dag verwijderen"}.
|
||||
{"Delete message of the day on all hosts","Verwijder bericht-van-de-dag op alle hosts"}.
|
||||
@ -95,10 +99,10 @@
|
||||
{"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exporteer data van alle gebruikers in de server naar PIEFXIS-bestanden (XEP-0227):"}.
|
||||
{"Export data of users in a host to PIEFXIS files (XEP-0227):","Exporteer data van alle gebruikers van een host naar PIEXFIS-bestanden (XEP-0227):"}.
|
||||
{"Family Name","Achternaam"}.
|
||||
{"February","februari"}.
|
||||
{"February","Februari"}.
|
||||
{"Fill in fields to search for any matching Jabber User","Vul de velden in om te zoeken naar Jabber-gebruikers op deze server"}.
|
||||
{"Fill in the form to search for any matching Jabber User (Add * to the end of field to match substring)","Gebruik de velden om te zoeken (Voeg achteraan het teken * toe om te zoeken naar alles wat met het eerste deel begint.)."}.
|
||||
{"Friday","vrijdag"}.
|
||||
{"Friday","Vrijdag"}.
|
||||
{"From ~s","Van ~s"}.
|
||||
{"From","Van"}.
|
||||
{"Full Name","Volledige naam"}.
|
||||
@ -143,13 +147,13 @@
|
||||
{"It is not allowed to send private messages to the conference","Er mogen geen privéberichten naar de chatruimte worden verzonden"}.
|
||||
{"Jabber ID","Jabber ID"}.
|
||||
{"Jabber ID ~s is invalid","De Jabber ID ~s is ongeldig"}.
|
||||
{"January","januari"}.
|
||||
{"January","Januari"}.
|
||||
{"Join IRC channel","Ga IRC kanaal binnen"}.
|
||||
{"joins the room","betrad de chatruimte"}.
|
||||
{"Join the IRC channel here.","Ga het IRC kanaal binnen"}.
|
||||
{"Join the IRC channel in this Jabber ID: ~s","Ga het IRC kanaal van deze Jabber ID binnen: ~s"}.
|
||||
{"July","juli"}.
|
||||
{"June","juni"}.
|
||||
{"July","Juli"}.
|
||||
{"June","Juni"}.
|
||||
{"Last Activity","Laatste activiteit"}.
|
||||
{"Last login","Laatste Aanmelding"}.
|
||||
{"Last month","Afgelopen maand"}.
|
||||
@ -166,11 +170,11 @@
|
||||
{"Make room password protected","Chatruimte beveiligen met een wachtwoord"}.
|
||||
{"Make room persistent","Chatruimte blijvend maken"}.
|
||||
{"Make room public searchable","Chatruimte doorzoekbaar maken"}.
|
||||
{"March","maart"}.
|
||||
{"March","Maart"}.
|
||||
{"Maximum Number of Occupants","Maximum aantal aanwezigen"}.
|
||||
{"Max # of items to persist","Maximum aantal in het geheugen te bewaren items"}.
|
||||
{"Max payload size in bytes","Maximumgrootte van bericht in bytes"}.
|
||||
{"May","mei"}.
|
||||
{"May","Mei"}.
|
||||
{"Members:","Groepsleden:"}.
|
||||
{"Membership is required to enter this room","U moet lid zijn om deze chatruimte te kunnen betreden"}.
|
||||
{"Memory","Geheugen"}.
|
||||
@ -182,7 +186,7 @@
|
||||
{"Module","Module"}.
|
||||
{"Modules at ","Modules op "}.
|
||||
{"Modules","Modules"}.
|
||||
{"Monday","maandag"}.
|
||||
{"Monday","Maandag"}.
|
||||
{"Name:","Naam:"}.
|
||||
{"Name","Naam"}.
|
||||
{"Never","Nooit"}.
|
||||
@ -202,11 +206,11 @@
|
||||
{"Notify subscribers when items are removed from the node","Abonnees informeren wanneer items verwijderd worden uit de node"}.
|
||||
{"Notify subscribers when the node configuration changes","Abonnees informeren wanneer de instellingen van de node veranderen"}.
|
||||
{"Notify subscribers when the node is deleted","Abonnees informeren wanneer de node verwijderd word"}.
|
||||
{"November","november"}.
|
||||
{"November","November"}.
|
||||
{"Number of occupants","Aantal aanwezigen"}.
|
||||
{"Number of online users","Aantal Aanwezige Gebruikers"}.
|
||||
{"Number of registered users","Aantal Geregistreerde Gebruikers"}.
|
||||
{"October","oktober"}.
|
||||
{"October","Oktober"}.
|
||||
{"Offline Messages:","Offline berichten:"}.
|
||||
{"Offline Messages","Offline berichten"}.
|
||||
{"OK","OK"}.
|
||||
@ -246,6 +250,7 @@
|
||||
{"Protocol","Protocol"}.
|
||||
{"Publish-Subscribe","Publish-Subscribe"}.
|
||||
{"PubSub subscriber request","PubSub abonnee verzoek"}.
|
||||
{"Purge all items when the relevant publisher goes offline","Verwijder alle items wanneer de gerelateerde publiceerder offline gaat"}.
|
||||
{"Queries to the conference members are not allowed in this room","Er mogen geen verzoeken verzenden worden naar deelnemers in deze chatruimte"}.
|
||||
{"RAM and disc copy","RAM en harde schijf"}.
|
||||
{"RAM copy","RAM"}.
|
||||
@ -280,7 +285,7 @@
|
||||
{"RPC Call Error","RPC-oproepfout"}.
|
||||
{"Running Nodes","Draaiende nodes"}.
|
||||
{"~s access rule configuration","Access rules op ~s"}.
|
||||
{"Saturday","zaterdag"}.
|
||||
{"Saturday","Zaterdag"}.
|
||||
{"Script check","Controle van script"}.
|
||||
{"Search Results for ","Zoekresultaten voor "}.
|
||||
{"Search users in ","Gebruikers zoeken in "}.
|
||||
@ -288,7 +293,7 @@
|
||||
{"Send announcement to all online users on all hosts","Mededeling verzenden naar alle online gebruikers op alle virtuele hosts"}.
|
||||
{"Send announcement to all users","Mededeling verzenden naar alle gebruikers"}.
|
||||
{"Send announcement to all users on all hosts","Stuur aankondiging aan alle gebruikers op alle hosts"}.
|
||||
{"September","september"}.
|
||||
{"September","September"}.
|
||||
{"Server ~b","Server ~b"}.
|
||||
{"Set message of the day and send to online users","Bericht van de dag instellen en verzenden naar online gebruikers"}.
|
||||
{"Set message of the day on all hosts and send to online users","Stel bericht-van-de-dag in op alle hosts en stuur naar aanwezige gebruikers"}.
|
||||
@ -298,6 +303,7 @@
|
||||
{"Shut Down Service","Stop Service"}.
|
||||
{"~s invites you to the room ~s","~s nodigt je uit voor het groepsgesprek ~s"}.
|
||||
{"Specify the access model","Geef toegangsmodel"}.
|
||||
{"Specify the event message type","Geef type van eventbericht"}.
|
||||
{"Specify the publisher model","Publicatietype opgeven"}.
|
||||
{"~s's Offline Messages Queue","offline berichten van ~s"}.
|
||||
{"Start Modules at ","Modules starten op "}.
|
||||
@ -317,17 +323,17 @@
|
||||
{"Submit","Verzenden"}.
|
||||
{"Subscriber Address","Abonnee Adres"}.
|
||||
{"Subscription","Inschrijving"}.
|
||||
{"Sunday","zondag"}.
|
||||
{"Sunday","Zondag"}.
|
||||
{"That nickname is already in use by another occupant","Deze bijnaam is al in gebruik door een andere aanwezige"}.
|
||||
{"That nickname is registered by another person","Deze bijnaam is al geregistreerd door iemand anders"}.
|
||||
{"The captcha is valid.","De geautomatiseerde Turing test is geslaagd."}.
|
||||
{"The captcha is valid.","De geautomatiseerde Turing-test is geslaagd."}.
|
||||
{"The collections with which a node is affiliated","De collecties waar een node mee is gerelateerd"}.
|
||||
{"the password is","het wachtwoord is"}.
|
||||
{"This participant is kicked from the room because he sent an error message","Deze deelnemer wordt weggestuurd vanwege het sturen van een foutmeldingsbericht"}.
|
||||
{"This participant is kicked from the room because he sent an error message to another participant","Deze deelnemer wordt weggestuurd vanwege het sturen van een foutmeldingsbericht aan een andere deelnemer"}.
|
||||
{"This participant is kicked from the room because he sent an error presence","Deze deelnemer wordt weggestuurd vanwege het sturen van een foutmelding-aanwezigheid"}.
|
||||
{"This room is not anonymous","Deze chatruimte is niet anoniem"}.
|
||||
{"Thursday","donderdag"}.
|
||||
{"Thursday","Donderdag"}.
|
||||
{"Time delay","Vertraging"}.
|
||||
{"Time","Tijd"}.
|
||||
{"To","Aan"}.
|
||||
@ -337,7 +343,7 @@
|
||||
{"Transactions Committed:","Bevestigde transacties:"}.
|
||||
{"Transactions Logged:","Gelogde transacties:"}.
|
||||
{"Transactions Restarted:","Herstarte transacties:"}.
|
||||
{"Tuesday","dinsdag"}.
|
||||
{"Tuesday","Dinsdag"}.
|
||||
{"Unable to generate a captcha","Het generen van een gautomatiseerde Turing test is mislukt"}.
|
||||
{"Unauthorized","Niet geautoriseerd"}.
|
||||
{"Update","Bijwerken"}.
|
||||
@ -358,7 +364,7 @@
|
||||
{"vCard User Search","Gebruikers zoeken"}.
|
||||
{"Visitors are not allowed to change their nicknames in this room","Het is bezoekers niet toegestaan hun naam te veranderen in dit kanaal"}.
|
||||
{"Visitors are not allowed to send messages to all occupants","Bezoekers mogen geen berichten verzenden naar alle aanwezigen"}.
|
||||
{"Wednesday","woensdag"}.
|
||||
{"Wednesday","Woensdag"}.
|
||||
{"When to send the last published item","Wanneer het laatst gepubliceerde item verzonden moet worden"}.
|
||||
{"Whether to allow subscriptions","Abonnementsaanvraag toestaan"}.
|
||||
{"You have been banned from this room","U werd verbannen uit deze chatruimte"}.
|
||||
|
904
src/msgs/nl.po
904
src/msgs/nl.po
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,10 @@
|
||||
{"Change Password","Endre Passord"}.
|
||||
{"Change User Password","Endre Brukers Passord"}.
|
||||
{"Chatroom configuration modified","Samtalerommets konfigurasjon er endret"}.
|
||||
{"Chatroom is created","Samtalerom er opprettet"}.
|
||||
{"Chatroom is destroyed","Samtalerom er fjernet"}.
|
||||
{"Chatroom is started","Samtalerom er startet"}.
|
||||
{"Chatroom is stopped","Samtalerom er stoppet"}.
|
||||
{"Chatrooms","Samtalerom"}.
|
||||
{"Choose a username and password to register with this server","Velg et brukernavn og passord for å registrere på "}.
|
||||
{"Choose modules to stop","Velg hvilke moduler som skal stoppes"}.
|
||||
@ -246,6 +250,7 @@
|
||||
{"Protocol","Protokoll"}.
|
||||
{"Publish-Subscribe","Publish-Subscribe"}.
|
||||
{"PubSub subscriber request","PubSub abonements forespørsel"}.
|
||||
{"Purge all items when the relevant publisher goes offline","Rydd alle elementer når den aktuelle utgiveren logger av"}.
|
||||
{"Queries to the conference members are not allowed in this room","Forespørsler til konferanse medlemmene er ikke tillat i dette rommet"}.
|
||||
{"RAM and disc copy","RAM og diskkopi"}.
|
||||
{"RAM copy","RAM kopi"}.
|
||||
@ -298,6 +303,7 @@
|
||||
{"Shut Down Service","Avslutt Tjeneste"}.
|
||||
{"~s invites you to the room ~s","~s inviterer deg til rommet ~s"}.
|
||||
{"Specify the access model","Spesifiser aksess modellen"}.
|
||||
{"Specify the event message type","Spesifiser hendelsesbeskjed type"}.
|
||||
{"Specify the publisher model","Angi publiserings modell"}.
|
||||
{"~s's Offline Messages Queue","~ss kø for Frakoblede Meldinger"}.
|
||||
{"Start Modules at ","Start Moduler på "}.
|
||||
|
831
src/msgs/no.po
831
src/msgs/no.po
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,10 @@
|
||||
{"Change Password","Zmień hasło"}.
|
||||
{"Change User Password","Zmień hasło użytkownika"}.
|
||||
{"Chatroom configuration modified","Konfiguracja pokoju zmodyfikowana"}.
|
||||
{"Chatroom is created","Pokój został stworzony"}.
|
||||
{"Chatroom is destroyed","Pokój został usunięty"}.
|
||||
{"Chatroom is started","Pokój został uruchomiony"}.
|
||||
{"Chatroom is stopped","Pokój został zatrzymany"}.
|
||||
{"Chatrooms","Pokoje rozmów"}.
|
||||
{"Choose a username and password to register with this server","Wybierz nazwę użytkownika i hasło aby zarejestrować się na tym serwerze"}.
|
||||
{"Choose modules to stop","Wybierz moduły do zatrzymania"}.
|
||||
@ -246,6 +250,7 @@
|
||||
{"Protocol","Protokół"}.
|
||||
{"Publish-Subscribe","PubSub"}.
|
||||
{"PubSub subscriber request","Żądanie subskrybcji PubSub"}.
|
||||
{"Purge all items when the relevant publisher goes offline","Usuń wszystkie elementy w momencie kiedy publikujący rozłączy się"}.
|
||||
{"Queries to the conference members are not allowed in this room","Informacje o członkach konferencji nie są dostępne w tym pokoju"}.
|
||||
{"RAM and disc copy","Kopia na dysku i w pamięci RAM"}.
|
||||
{"RAM copy","Kopia w pamięci RAM"}.
|
||||
|
826
src/msgs/pl.po
826
src/msgs/pl.po
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,10 @@
|
||||
{"Change Password","Mudar senha"}.
|
||||
{"Change User Password","Alterar Senha do Usuário"}.
|
||||
{"Chatroom configuration modified","Configuração da sala de bate-papo modificada"}.
|
||||
{"Chatroom is created","A sala de chat está criada"}.
|
||||
{"Chatroom is destroyed","A sala de chat está destruída"}.
|
||||
{"Chatroom is started","A sala de chat está inciada"}.
|
||||
{"Chatroom is stopped","A sala de chat está parada"}.
|
||||
{"Chatrooms","Salas de Chat"}.
|
||||
{"Choose a username and password to register with this server","Escolha um nome de usuário e senha para registrar-se neste servidor"}.
|
||||
{"Choose modules to stop","Selecione módulos a parar"}.
|
||||
@ -246,6 +250,7 @@
|
||||
{"Protocol","Porta"}.
|
||||
{"Publish-Subscribe","Publicação de Tópico"}.
|
||||
{"PubSub subscriber request","PubSub requisição de assinante"}.
|
||||
{"Purge all items when the relevant publisher goes offline","Descartar todos os itens quando o publicante principal estiver offline"}.
|
||||
{"Queries to the conference members are not allowed in this room","Nesta sala não se permite consultas aos membros da sala"}.
|
||||
{"RAM and disc copy","Copia em RAM y disco"}.
|
||||
{"RAM copy","Copia em RAM"}.
|
||||
@ -298,6 +303,7 @@
|
||||
{"Shut Down Service","Parar Serviço"}.
|
||||
{"~s invites you to the room ~s","~s convidou você para a sala ~s"}.
|
||||
{"Specify the access model","Especificar os modelos de acesso"}.
|
||||
{"Specify the event message type","Especificar o tipo de mensagem para o evento"}.
|
||||
{"Specify the publisher model","Especificar o modelo do publicante"}.
|
||||
{"~s's Offline Messages Queue","~s's Fila de Mensagens Offline"}.
|
||||
{"Start","Iniciar"}.
|
||||
|
File diff suppressed because it is too large
Load Diff
853
src/msgs/pt.po
853
src/msgs/pt.po
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user