24
1
mirror of https://github.com/processone/ejabberd.git synced 2024-07-14 23:44:18 +02:00

Merge branch '2.1.x' of git://github.com/processone/ejabberd.git into applepush

This commit is contained in:
Alexey Shchepin 2010-08-19 15:39:08 +03:00
commit 8fe6ed011d
134 changed files with 14925 additions and 11840 deletions

6
README
View File

@ -9,10 +9,8 @@ To compile ejabberd you need:
- GNU Make - GNU Make
- GCC - GCC
- Libexpat 1.95 or higher - Libexpat 1.95 or higher
- Erlang/OTP R10B-9 or higher. The recommended version is R12B-5. - Erlang/OTP R10B-9 or higher. Recommended versions: R12B-5 and R13B04
Support for R13 is experimental. - OpenSSL 0.9.8 or higher, for STARTTLS, SASL and SSL encryption.
- OpenSSL 0.9.6 or higher, for STARTTLS, SASL and SSL
encryption. Optional, highly recommended.
- Zlib 1.2.3 or higher, for Stream Compression support - Zlib 1.2.3 or higher, for Stream Compression support
(XEP-0138). Optional. (XEP-0138). Optional.
- Erlang mysql library. Optional. MySQL authentication/storage. - Erlang mysql library. Optional. MySQL authentication/storage.

View File

@ -2,7 +2,7 @@
"http://www.w3.org/TR/REC-html40/loose.dtd"> "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML> <HTML>
<HEAD> <HEAD>
<TITLE>Ejabberd 2.1.3 Developers Guide <TITLE>Ejabberd 2.1.x Developers Guide
</TITLE> </TITLE>
<META http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <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 --> <!--HEVEA command line is: /usr/bin/hevea -fix -pedantic dev.tex -->
<!--CUT DEF section 1 --><P><A NAME="titlepage"></A> <!--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="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> <A HREF="xmpp:aleksey@jabber.ru"><TT>xmpp:aleksey@jabber.ru</TT></A></H3></TD></TR>
</TABLE><DIV CLASS="center"> </TABLE><DIV CLASS="center">
@ -194,6 +194,9 @@ operation are as follows:
auth:User:Server:Password (check if a username/password pair is correct) auth:User:Server:Password (check if a username/password pair is correct)
</LI><LI CLASS="li-itemize">isuser:User:Server (check if it&#X2019;s a valid user) </LI><LI CLASS="li-itemize">isuser:User:Server (check if it&#X2019;s a valid user)
</LI><LI CLASS="li-itemize">setpass:User:Server:Password (set user&#X2019;s password) </LI><LI CLASS="li-itemize">setpass:User:Server:Password (set user&#X2019;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></UL> </LI></UL>
</LI><LI CLASS="li-itemize">write to stdout: AABB </LI><LI CLASS="li-itemize">write to stdout: AABB

View File

@ -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 auth:User:Server:Password (check if a username/password pair is correct)
\item isuser:User:Server (check if it's a valid user) \item isuser:User:Server (check if it's a valid user)
\item setpass:User:Server:Password (set user's password) \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}
\end{itemize} \end{itemize}
\item write to stdout: AABB \item write to stdout: AABB

View File

@ -2,7 +2,7 @@
"http://www.w3.org/TR/REC-html40/loose.dtd"> "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML> <HTML>
<HEAD> <HEAD>
<TITLE>Ejabberd 2.1.3 Feature Sheet <TITLE>Ejabberd 2.1.x Feature Sheet
</TITLE> </TITLE>
<META http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <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 --> <!--HEVEA command line is: /usr/bin/hevea -fix -pedantic features.tex -->
<!--CUT DEF section 1 --><P><A NAME="titlepage"></A> <!--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="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> <A HREF="xmpp:sander@devrieze.dyndns.org"><TT>xmpp:sander@devrieze.dyndns.org</TT></A></H3></TD></TR>
</TABLE><DIV CLASS="center"> </TABLE><DIV CLASS="center">

View File

@ -6,7 +6,7 @@
ejabberd 2.1.3 ejabberd 2.1.x
Installation and Operation Guide Installation and Operation Guide
@ -76,7 +76,7 @@ BLOCKQUOTE.figure DIV.center DIV.center HR{display:none;}
<HR SIZE=2><BR> <HR SIZE=2><BR>
<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>&nbsp;</TD></TR> <TR><TD ALIGN=right NOWRAP>&nbsp;</TD></TR>
<TR><TD ALIGN=right NOWRAP> <FONT SIZE=6>Installation and Operation Guide</FONT></TD></TR> <TR><TD ALIGN=right NOWRAP> <FONT SIZE=6>Installation and Operation Guide</FONT></TD></TR>
</TABLE><BR> </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&#XA0;&#XA0;<TT>mod_time</TT></A> </LI><LI CLASS="li-toc"><A HREF="#htoc62">3.3.24&#XA0;&#XA0;<TT>mod_time</TT></A>
</LI><LI CLASS="li-toc"><A HREF="#htoc63">3.3.25&#XA0;&#XA0;<TT>mod_vcard</TT></A> </LI><LI CLASS="li-toc"><A HREF="#htoc63">3.3.25&#XA0;&#XA0;<TT>mod_vcard</TT></A>
</LI><LI CLASS="li-toc"><A HREF="#htoc64">3.3.26&#XA0;&#XA0;<TT>mod_vcard_ldap</TT></A> </LI><LI CLASS="li-toc"><A HREF="#htoc64">3.3.26&#XA0;&#XA0;<TT>mod_vcard_ldap</TT></A>
</LI><LI CLASS="li-toc"><A HREF="#htoc65">3.3.27&#XA0;&#XA0;<TT>mod_version</TT></A> </LI><LI CLASS="li-toc"><A HREF="#htoc65">3.3.27&#XA0;&#XA0;<TT>mod_vcard_xupdate</TT></A>
</LI><LI CLASS="li-toc"><A HREF="#htoc66">3.3.28&#XA0;&#XA0;<TT>mod_version</TT></A>
</LI></UL> </LI></UL>
</LI></UL> </LI></UL>
</LI><LI CLASS="li-toc"><A HREF="#htoc66">Chapter&#XA0;4&#XA0;&#XA0;Managing an <TT>ejabberd</TT> Server</A> </LI><LI CLASS="li-toc"><A HREF="#htoc67">Chapter&#XA0;4&#XA0;&#XA0;Managing an <TT>ejabberd</TT> Server</A>
<UL CLASS="toc"><LI CLASS="li-toc"> <UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc67">4.1&#XA0;&#XA0;<TT>ejabberdctl</TT></A> <A HREF="#htoc68">4.1&#XA0;&#XA0;<TT>ejabberdctl</TT></A>
<UL CLASS="toc"><LI CLASS="li-toc"> <UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc68">4.1.1&#XA0;&#XA0;ejabberdctl Commands</A> <A HREF="#htoc69">4.1.1&#XA0;&#XA0;ejabberdctl Commands</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc69">4.1.2&#XA0;&#XA0;Erlang Runtime System</A> </LI><LI CLASS="li-toc"><A HREF="#htoc70">4.1.2&#XA0;&#XA0;Erlang Runtime System</A>
</LI></UL> </LI></UL>
</LI><LI CLASS="li-toc"><A HREF="#htoc70">4.2&#XA0;&#XA0;<TT>ejabberd</TT> Commands</A> </LI><LI CLASS="li-toc"><A HREF="#htoc71">4.2&#XA0;&#XA0;<TT>ejabberd</TT> Commands</A>
<UL CLASS="toc"><LI CLASS="li-toc"> <UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc71">4.2.1&#XA0;&#XA0;List of ejabberd Commands</A> <A HREF="#htoc72">4.2.1&#XA0;&#XA0;List of ejabberd Commands</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc72">4.2.2&#XA0;&#XA0;Restrict Execution with AccessCommands</A> </LI><LI CLASS="li-toc"><A HREF="#htoc73">4.2.2&#XA0;&#XA0;Restrict Execution with AccessCommands</A>
</LI></UL> </LI></UL>
</LI><LI CLASS="li-toc"><A HREF="#htoc73">4.3&#XA0;&#XA0;Web Admin</A> </LI><LI CLASS="li-toc"><A HREF="#htoc74">4.3&#XA0;&#XA0;Web Admin</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc74">4.4&#XA0;&#XA0;Ad-hoc Commands</A> </LI><LI CLASS="li-toc"><A HREF="#htoc75">4.4&#XA0;&#XA0;Ad-hoc Commands</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc75">4.5&#XA0;&#XA0;Change Computer Hostname</A> </LI><LI CLASS="li-toc"><A HREF="#htoc76">4.5&#XA0;&#XA0;Change Computer Hostname</A>
</LI></UL> </LI></UL>
</LI><LI CLASS="li-toc"><A HREF="#htoc76">Chapter&#XA0;5&#XA0;&#XA0;Securing <TT>ejabberd</TT></A> </LI><LI CLASS="li-toc"><A HREF="#htoc77">Chapter&#XA0;5&#XA0;&#XA0;Securing <TT>ejabberd</TT></A>
<UL CLASS="toc"><LI CLASS="li-toc"> <UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc77">5.1&#XA0;&#XA0;Firewall Settings</A> <A HREF="#htoc78">5.1&#XA0;&#XA0;Firewall Settings</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc78">5.2&#XA0;&#XA0;epmd</A> </LI><LI CLASS="li-toc"><A HREF="#htoc79">5.2&#XA0;&#XA0;epmd</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc79">5.3&#XA0;&#XA0;Erlang Cookie</A> </LI><LI CLASS="li-toc"><A HREF="#htoc80">5.3&#XA0;&#XA0;Erlang Cookie</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc80">5.4&#XA0;&#XA0;Erlang Node Name</A> </LI><LI CLASS="li-toc"><A HREF="#htoc81">5.4&#XA0;&#XA0;Erlang Node Name</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc81">5.5&#XA0;&#XA0;Securing Sensitive Files</A> </LI><LI CLASS="li-toc"><A HREF="#htoc82">5.5&#XA0;&#XA0;Securing Sensitive Files</A>
</LI></UL> </LI></UL>
</LI><LI CLASS="li-toc"><A HREF="#htoc82">Chapter&#XA0;6&#XA0;&#XA0;Clustering</A> </LI><LI CLASS="li-toc"><A HREF="#htoc83">Chapter&#XA0;6&#XA0;&#XA0;Clustering</A>
<UL CLASS="toc"><LI CLASS="li-toc"> <UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc83">6.1&#XA0;&#XA0;How it Works</A> <A HREF="#htoc84">6.1&#XA0;&#XA0;How it Works</A>
<UL CLASS="toc"><LI CLASS="li-toc"> <UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc84">6.1.1&#XA0;&#XA0;Router</A> <A HREF="#htoc85">6.1.1&#XA0;&#XA0;Router</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc85">6.1.2&#XA0;&#XA0;Local Router</A> </LI><LI CLASS="li-toc"><A HREF="#htoc86">6.1.2&#XA0;&#XA0;Local Router</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc86">6.1.3&#XA0;&#XA0;Session Manager</A> </LI><LI CLASS="li-toc"><A HREF="#htoc87">6.1.3&#XA0;&#XA0;Session Manager</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc87">6.1.4&#XA0;&#XA0;s2s Manager</A> </LI><LI CLASS="li-toc"><A HREF="#htoc88">6.1.4&#XA0;&#XA0;s2s Manager</A>
</LI></UL> </LI></UL>
</LI><LI CLASS="li-toc"><A HREF="#htoc88">6.2&#XA0;&#XA0;Clustering Setup</A> </LI><LI CLASS="li-toc"><A HREF="#htoc89">6.2&#XA0;&#XA0;Clustering Setup</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc89">6.3&#XA0;&#XA0;Service Load-Balancing</A> </LI><LI CLASS="li-toc"><A HREF="#htoc90">6.3&#XA0;&#XA0;Service Load-Balancing</A>
<UL CLASS="toc"><LI CLASS="li-toc"> <UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc90">6.3.1&#XA0;&#XA0;Components Load-Balancing</A> <A HREF="#htoc91">6.3.1&#XA0;&#XA0;Components Load-Balancing</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc91">6.3.2&#XA0;&#XA0;Domain Load-Balancing Algorithm</A> </LI><LI CLASS="li-toc"><A HREF="#htoc92">6.3.2&#XA0;&#XA0;Domain Load-Balancing Algorithm</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc92">6.3.3&#XA0;&#XA0;Load-Balancing Buckets</A> </LI><LI CLASS="li-toc"><A HREF="#htoc93">6.3.3&#XA0;&#XA0;Load-Balancing Buckets</A>
</LI></UL> </LI></UL>
</LI></UL> </LI></UL>
</LI><LI CLASS="li-toc"><A HREF="#htoc93">Chapter&#XA0;7&#XA0;&#XA0;Debugging</A> </LI><LI CLASS="li-toc"><A HREF="#htoc94">Chapter&#XA0;7&#XA0;&#XA0;Debugging</A>
<UL CLASS="toc"><LI CLASS="li-toc"> <UL CLASS="toc"><LI CLASS="li-toc">
<A HREF="#htoc94">7.1&#XA0;&#XA0;Log Files</A> <A HREF="#htoc95">7.1&#XA0;&#XA0;Log Files</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc95">7.2&#XA0;&#XA0;Debug Console</A> </LI><LI CLASS="li-toc"><A HREF="#htoc96">7.2&#XA0;&#XA0;Debug Console</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc96">7.3&#XA0;&#XA0;Watchdog Alerts</A> </LI><LI CLASS="li-toc"><A HREF="#htoc97">7.3&#XA0;&#XA0;Watchdog Alerts</A>
</LI></UL> </LI></UL>
</LI><LI CLASS="li-toc"><A HREF="#htoc97">Appendix&#XA0;A&#XA0;&#XA0;Internationalization and Localization</A> </LI><LI CLASS="li-toc"><A HREF="#htoc98">Appendix&#XA0;A&#XA0;&#XA0;Internationalization and Localization</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc98">Appendix&#XA0;B&#XA0;&#XA0;Release Notes</A> </LI><LI CLASS="li-toc"><A HREF="#htoc99">Appendix&#XA0;B&#XA0;&#XA0;Release Notes</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc99">Appendix&#XA0;C&#XA0;&#XA0;Acknowledgements</A> </LI><LI CLASS="li-toc"><A HREF="#htoc100">Appendix&#XA0;C&#XA0;&#XA0;Acknowledgements</A>
</LI><LI CLASS="li-toc"><A HREF="#htoc100">Appendix&#XA0;D&#XA0;&#XA0;Copyright Information</A> </LI><LI CLASS="li-toc"><A HREF="#htoc101">Appendix&#XA0;D&#XA0;&#XA0;Copyright Information</A>
</LI></UL><!--TOC chapter Introduction--> </LI></UL><!--TOC chapter Introduction-->
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc1">Chapter&#XA0;1</A>&#XA0;&#XA0;Introduction</H1><!--SEC END --><P> <H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc1">Chapter&#XA0;1</A>&#XA0;&#XA0;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--> <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 GNU Make
</LI><LI CLASS="li-itemize">GCC </LI><LI CLASS="li-itemize">GCC
</LI><LI CLASS="li-itemize">Libexpat 1.95 or higher </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">Erlang/OTP R10B-9 or higher. The recommended versions are R12B-5 and R13B04.
</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">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">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 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>. </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. Enable the use of XML based optimisations.
It will for example use CDATA to escape characters in the XMPP stream. 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"> 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--> </DD></DL><P> <A NAME="install"></A> </P><!--TOC subsection Install-->
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc12">2.4.4</A>&#XA0;&#XA0;<A HREF="#install">Install</A></H3><!--SEC END --><P> <A NAME="install"></A> <H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc12">2.4.4</A>&#XA0;&#XA0;<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: </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> <TT>zlib</TT>
</DD><DT CLASS="dt-description"><B><TT>ejabberd_s2s_in</TT></B></DT><DD CLASS="dd-description"> </DD><DT CLASS="dt-description"><B><TT>ejabberd_s2s_in</TT></B></DT><DD CLASS="dd-description">
Handles incoming s2s connections.<BR> 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"> </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> 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> (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>, 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"> </DD><DT CLASS="dt-description"><B><TT>ejabberd_stun</TT></B></DT><DD CLASS="dd-description">
Handles STUN Binding requests as defined in Handles STUN Binding requests as defined in
<A HREF="http://tools.ietf.org/html/rfc5389">RFC 5389</A>.<BR> <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 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 no space in the queue (and ejabberd was not able to accept them
immediately). Default value is 5. 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. </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>. 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"> </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> The external Jabber component that connects to this <TT>ejabberd_service</TT>
can serve one or more hostnames. 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 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, different services: add an <TT>ejabberd_service</TT> for each service,
as seen in an example below. 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"> </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 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 enables access via HTTP requests to <TT>ejabberd</TT> from behind firewalls which
@ -763,7 +761,7 @@ Default value: &#X2019;undefined&#X2019;.
</DD><DT CLASS="dt-description"><B><TT>{max_stanza_size, Size}</TT></B></DT><DD CLASS="dd-description"> </DD><DT CLASS="dt-description"><B><TT>{max_stanza_size, Size}</TT></B></DT><DD CLASS="dd-description">
This option specifies an This option specifies an
approximate maximum size in bytes of XML stanzas. Approximate, 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 data. For example <CODE>{max_stanza_size, 65536}</CODE>. The default
value is <TT>infinity</TT>. Recommended values are 65536 for c2s value is <TT>infinity</TT>. Recommended values are 65536 for c2s
connections and 131072 for s2s connections. s2s max stanza size 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>, 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> 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"> </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 &#X2019;from&#X2019; 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. This option can be used with <TT>ejabberd_service</TT> only.
Only enable this option if you are completely sure you need to enable it. <A HREF="http://xmpp.org/extensions/xep-0114.html">XEP-0114</A> requires that the domain must match the hostname of the component.
Default value: false. 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 &#X2019;from&#X2019; 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 </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&#XA0;<A HREF="#shapers">3.1.6</A>). The default value shaper for the port (see section&#XA0;<A HREF="#shapers">3.1.6</A>). The default value
is <TT>none</TT>. 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><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"> </P><UL CLASS="itemize"><LI CLASS="li-itemize">
internal (default) &#X2014; See section&#XA0;<A HREF="#internalauth">3.1.4</A>. internal (default) &#X2014; See section&#XA0;<A HREF="#internalauth">3.1.4</A>.
</LI><LI CLASS="li-itemize">external &#X2014; There are <A HREF="http://www.ejabberd.im/extauth">some </LI><LI CLASS="li-itemize">external &#X2014; See section&#XA0;<A HREF="#extauth">3.1.4</A>.
example authentication scripts</A>.
</LI><LI CLASS="li-itemize">ldap &#X2014; See section&#XA0;<A HREF="#ldap">3.2.5</A>. </LI><LI CLASS="li-itemize">ldap &#X2014; See section&#XA0;<A HREF="#ldap">3.2.5</A>.
</LI><LI CLASS="li-itemize">odbc &#X2014; See section&#XA0;<A HREF="#mysql">3.2.1</A>, <A HREF="#pgsql">3.2.3</A>, </LI><LI CLASS="li-itemize">odbc &#X2014; See section&#XA0;<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>. <A HREF="#mssql">3.2.2</A> and <A HREF="#odbc">3.2.4</A>.
</LI><LI CLASS="li-itemize">anonymous &#X2014; See section&#XA0;<A HREF="#saslanonymous">3.1.4</A>. </LI><LI CLASS="li-itemize">anonymous &#X2014; See section&#XA0;<A HREF="#saslanonymous">3.1.4</A>.
</LI><LI CLASS="li-itemize">pam &#X2014; See section&#XA0;<A HREF="#pam">3.1.4</A>. </LI><LI CLASS="li-itemize">pam &#X2014; See section&#XA0;<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> <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. </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: 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]}]}. {host_config, "example.net", [{auth_method, [ldap]}]}.
</PRE></LI><LI CLASS="li-itemize">To use internal authentication on all virtual hosts: </PRE></LI><LI CLASS="li-itemize">To use internal authentication on all virtual hosts:
<PRE CLASS="verbatim">{auth_method, internal}. <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&#X2019;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> <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 </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 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. <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 Default is <TT>"ejabberd"</TT>. Refer to the PAM documentation of your operation system
for more information. 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: </DD></DL><P>Example:
</P><PRE CLASS="verbatim">{auth_method, [pam]}. </P><PRE CLASS="verbatim">{auth_method, [pam]}.
{pam_service, "ejabberd"}. {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 &amp;&amp; make install </P><PRE CLASS="verbatim">./configure --enable-odbc --enable-mssql &amp;&amp; make install
</PRE><P> <A NAME="configuremssql"></A> </P><!--TOC subsubsection Database Connection--> </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> <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. </P><P>The configuration of Database Connection for a Microsoft SQL Server
Use this option to modify the value: is the same as the configuration for
</P><PRE CLASS="verbatim">{odbc_pool_size, 10}. ODBC compatible servers (see section&#XA0;<A HREF="#configureodbc">3.2.4</A>).</P><P> <A NAME="mssqlauth"></A> </P><!--TOC subsubsection Authentication-->
</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 &#X2019;undefined&#X2019;, 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-->
<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#mssqlauth">Authentication</A></H4><!--SEC END --><P> <A NAME="mssqlauth"></A> <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&#XA0;<A HREF="#odbcauth">3.2.4</A>).</P><P> <A NAME="mssqlstorage"></A> </P><!--TOC subsubsection Storage--> ODBC compatible servers (see section&#XA0;<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> <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 </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>&#XA0;&#XA0;<A HREF="#ldap">LDAP</A></H3><!--SEC END --><P> <A NAME="ldap"></A> <H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc37">3.2.5</A>&#XA0;&#XA0;<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 </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 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 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--> create accounts or edit vCard that is stored in LDAP.
<H4 CLASS="subsubsection"><!--SEC ANCHOR --><A HREF="#ldapconnection">Connection</A></H4><!--SEC END --><P> <A NAME="ldapconnection"></A> </P><P>Parameters: 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"> </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 <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. 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. The value <TT>tls</TT> enables encryption by using LDAP over SSL.
Note that STARTTLS encryption is not supported. Note that STARTTLS encryption is not supported.
The default value is: <TT>none</TT>. 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&#X2019;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. </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&#XA0;389 if encryption is disabled; and 636 if encryption is enabled. The default port is&#XA0;389 if encryption is disabled; and 636 if encryption is enabled.
If you configure a value, it is stored in <TT>ejabberd</TT>&#X2019;s database. If you configure a value, it is stored in <TT>ejabberd</TT>&#X2019;s database.
@ -1662,8 +1698,9 @@ value is <TT>""</TT>.
{ldap_port, 389}. {ldap_port, 389}.
{ldap_rootdn, "cn=Manager,dc=domain,dc=org"}. {ldap_rootdn, "cn=Manager,dc=domain,dc=org"}.
{ldap_password, "secret"}. {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--> </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. Available options are:</P><DL CLASS="description"><DT CLASS="dt-description"> <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 <B><TT>{ldap_base, Base}</TT></B></DT><DD CLASS="dd-description">LDAP base directory which stores
users accounts. This option is required. 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"> </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><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>&nbsp;</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>&nbsp;</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>&nbsp;</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>&nbsp;</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>&nbsp;</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>&nbsp;</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>&nbsp;</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>&nbsp;</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>&nbsp;</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>&nbsp;</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> <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>&nbsp;</TD></TR> <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>&nbsp;</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="#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="#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>&nbsp;</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>&nbsp;</TD></TR>
</TABLE> </TABLE>
<DIV CLASS="center"><HR WIDTH="80%" SIZE=2></DIV></DIV></BLOCKQUOTE><UL CLASS="itemize"><LI CLASS="li-itemize"> <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: </DD><DT CLASS="dt-description"><B><TT>{anonymous, true|false}</TT></B></DT><DD CLASS="dd-description"> The room is anonymous:
occupants don&#X2019;t see the real JIDs of other occupants. occupants don&#X2019;t see the real JIDs of other occupants.
Note that the room moderators can always see the real JIDs of the 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>{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>{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 &#X2019;voice&#X2019;. </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 &#X2019;voice&#X2019;.
@ -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: </DD></DL><P>This module reads also another option defined globally for the server:
<TT>{registration_timeout, Timeout}</TT>. <TT>{registration_timeout, Timeout}</TT>.
This option limits the frequency of registration from a given IP or username. This option limits the frequency of registration from a given IP or username.
So, a user can&#X2019;t register a new account from the same IP address or JID during So, a user that tries to register a new account from the same IP address or JID during
this number of seconds after previous registration. this number of seconds after his previous registration
Timeout is expressed in seconds, and must be an integer. will receive an error <TT>resource-constraint</TT> with the explanation:
&#X201C;Users are not allowed to register accounts so quickly&#X201D;.
The timeout is expressed in seconds, and it must be an integer.
To disable this limitation, To disable this limitation,
instead of an integer put a word like: <TT>infinity</TT>. instead of an integer put a word like: <TT>infinity</TT>.
Default value: 600 seconds.</P><P>Examples: 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>&#XA0;&#XA0;<A HREF="#modvcardldap"><TT>mod_vcard_ldap</TT></A></H3><!--SEC END --><P> <A NAME="modvcardldap"></A> <H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc64">3.3.26</A>&#XA0;&#XA0;<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 </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 implemented in the <TT>mod_vcard_ldap</TT> module. This module does not depend on the
authentication method (see&#XA0;<A HREF="#ldapauth">3.2.5</A>).</P><P>Note that <TT>ejabberd</TT> treats LDAP as a read-only storage: authentication method (see&#XA0;<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 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 its own optional parameters. The first group of parameters has the same
meaning as the top-level LDAP parameters to set the authentication method: meaning as the top-level LDAP parameters to set the authentication method:
<TT>ldap_servers</TT>, <TT>ldap_port</TT>, <TT>ldap_rootdn</TT>, <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"}, {"Birthday", "BDAY"},
{"Nickname", "NICKNAME"} {"Nickname", "NICKNAME"}
]}, ]},
</PRE></LI></UL><P> <A NAME="modversion"></A> </P><!--TOC subsection <TT>mod_version</TT>--> </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>&#XA0;&#XA0;<A HREF="#modversion"><TT>mod_version</TT></A></H3><!--SEC END --><P> <A NAME="modversion"></A> <H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc65">3.3.27</A>&#XA0;&#XA0;<A HREF="#modvcardxupdate"><TT>mod_vcard_xupdate</TT></A></H3><!--SEC END --><P> <A NAME="modvcardxupdate"></A>
</P><P>The user&#X2019;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>&#XA0;&#XA0;<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 </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>&#X2019;s version when queried.</P><P>Options: answers <TT>ejabberd</TT>&#X2019;s version when queried.</P><P>Options:
</P><DL CLASS="description"><DT CLASS="dt-description"> </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 </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&#XA0;<A HREF="#modiqdiscoption">3.3.2</A>). the processing discipline for Software Version (<TT>jabber:iq:version</TT>) IQ queries (see section&#XA0;<A HREF="#modiqdiscoption">3.3.2</A>).
</DD></DL><P> <A NAME="manage"></A> </P><!--TOC chapter Managing an <TT>ejabberd</TT> Server--> </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&#XA0;4</A>&#XA0;&#XA0;<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>--> <H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc67">Chapter&#XA0;4</A>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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 <H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc68">4.1</A>&#XA0;&#XA0;<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>) 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>). 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 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 This can be used by other scripts to determine automatically
if a command succeeded or failed, if a command succeeded or failed,
for example using: <TT>echo $?</TT></P><P> <A NAME="ectl-commands"></A> </P><!--TOC subsection ejabberdctl Commands--> 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>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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&#X2019;t an <TT>ejabberd</TT> server running, it displays the available options. If there isn&#X2019;t an <TT>ejabberd</TT> server running,
the available parameters are: the available parameters are:
</P><DL CLASS="description"><DT CLASS="dt-description"> </P><DL CLASS="description"><DT CLASS="dt-description">
@ -3459,7 +3522,7 @@ robot1
testuser1 testuser1
testuser2 testuser2
</PRE><P> <A NAME="erlangconfiguration"></A> </P><!--TOC subsection Erlang Runtime System--> </PRE><P> <A NAME="erlangconfiguration"></A> </P><!--TOC subsection Erlang Runtime System-->
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc69">4.1.2</A>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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. This system is configured using environment variables and command line parameters.
The <TT>ejabberdctl</TT> administration script uses many of those possibilities. The <TT>ejabberdctl</TT> administration script uses many of those possibilities.
You can configure some of them with the file <TT>ejabberdctl.cfg</TT>, You can configure some of them with the file <TT>ejabberdctl.cfg</TT>,
@ -3536,7 +3599,7 @@ not &#X201C;Simple Authentication and Security Layer&#X201D;.
</DD></DL><P> </DD></DL><P>
Note that some characters need to be escaped when used in shell scripts, for instance <CODE>"</CODE> and <CODE>{}</CODE>. 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--> 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>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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 with a defined number and type of calling arguments and type of result
that is registered in the <TT>ejabberd_commands</TT> service. 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>. 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>ejabberd_xmlrpc</TT> (XML-RPC service),
<TT>mod_rest</TT> (HTTP POST 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--> <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>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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 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: the ejabberdctl script:
</P><PRE CLASS="verbatim">$ ejabberdctl help </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>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><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--> </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>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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 that case, authentication information must be provided.
In each frontend the <TT>AccessCommands</TT> option is defined In each frontend the <TT>AccessCommands</TT> option is defined
in a different place. But in all cases the option syntax is the same: 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"}]} {_bot_reg_test, [register, unregister], [{host, "test.org"}]}
] ]
</PRE><P> <A NAME="webadmin"></A> </P><!--TOC section Web Admin--> </PRE><P> <A NAME="webadmin"></A> </P><!--TOC section Web Admin-->
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc73">4.3</A>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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: </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 a <TT>ejabberd_http</TT> listener with the option <TT>web_admin</TT> (see
section&#XA0;<A HREF="#listened">3.1.3</A>) is included in the listening ports. Then you can open section&#XA0;<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&#XA0;<A HREF="#fig:webadmmain">4.1</A>.</P
<DIV CLASS="center"><HR WIDTH="80%" SIZE=2></DIV></DIV></BLOCKQUOTE><P> <DIV CLASS="center"><HR WIDTH="80%" SIZE=2></DIV></DIV></BLOCKQUOTE><P>
Here you can edit access restrictions, manage users, create backups, Here you can edit access restrictions, manage users, create backups,
manage the database, enable/disable ports listened for, view server manage the database, enable/disable ports listened for, view server
statistics,&#X2026;</P><P>Examples: statistics,&#X2026;</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"> </P><UL CLASS="itemize"><LI CLASS="li-itemize">
You can serve the Web Admin on the same port as the You can serve the Web Admin on the same port as the
HTTP Polling interface. In this example HTTP Polling interface. In this example
@ -3673,9 +3737,13 @@ username &#X2018;<TT>admin@example.net</TT>&#X2019; to administer all virtual ho
URL). If you log in with &#X2018;<TT>admin@example.com</TT>&#X2019; on<BR> URL). If you log in with &#X2018;<TT>admin@example.com</TT>&#X2019; on<BR>
<CODE>http://example.org:5280/admin/server/example.com/</CODE> you can only <CODE>http://example.org:5280/admin/server/example.com/</CODE> you can only
administer the virtual host <TT>example.com</TT>. administer the virtual host <TT>example.com</TT>.
The account &#X2018;<TT>reviewer@example.com</TT>&#X2019; can browse that vhost in read-only mode.
<PRE CLASS="verbatim">{acl, admins, {user, "admin", "example.net"}}. <PRE CLASS="verbatim">{acl, admins, {user, "admin", "example.net"}}.
{host_config, "example.com", [{acl, admins, {user, "admin", "example.com"}}]}. {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, configure, [{allow, admins}]}.
{access, webadmin_view, [{allow, viewers}]}.
{hosts, ["example.org"]}. {hosts, ["example.org"]}.
@ -3713,13 +3781,13 @@ The file is searched by default in
The directory of the documentation can be specified in The directory of the documentation can be specified in
the environment variable <TT>EJABBERD_DOC_PATH</TT>. 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--> 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>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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> you can perform several administrative tasks in <TT>ejabberd</TT>
with a XMPP client. with a XMPP client.
The client must support Ad-Hoc Commands (<A HREF="http://xmpp.org/extensions/xep-0050.html">XEP-0050</A>), 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 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--> 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>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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, 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>). 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. 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... </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. 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>--> </LI></OL><P> <A NAME="secure"></A> </P><!--TOC chapter Securing <TT>ejabberd</TT>-->
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc76">Chapter&#XA0;5</A>&#XA0;&#XA0;<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--> <H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc77">Chapter&#XA0;5</A>&#XA0;&#XA0;<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>&#XA0;&#XA0;<A HREF="#firewall">Firewall Settings</A></H2><!--SEC END --><P> <A NAME="firewall"></A> <H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc78">5.1</A>&#XA0;&#XA0;<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><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> </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> <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> <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> </TABLE>
<DIV CLASS="center"><HR WIDTH="80%" SIZE=2></DIV></DIV></BLOCKQUOTE><P> <A NAME="epmd"></A> </P><!--TOC section epmd--> <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>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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 is a small name server included in Erlang/OTP
and used by Erlang programs when establishing distributed Erlang communications. 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. <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: 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 </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--> </PRE><P> <A NAME="cookie"></A> </P><!--TOC section Erlang Cookie-->
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc79">5.3</A>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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>. 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 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. 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, The communication between Erlang nodes are not encrypted,
so the cookie could be read sniffing the traffic on the network. 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--> 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>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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>) 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>). 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 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 because it may be possible to fake the fact that you are on another network
using a modified version of Erlang <TT>epmd</TT>. 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--> 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>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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, 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"> 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"> <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"> </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>. See section <A HREF="#cookie">5.3</A>.
</DD></DL><P> <A NAME="clustering"></A> </P><!--TOC chapter Clustering--> </DD></DL><P> <A NAME="clustering"></A> </P><!--TOC chapter Clustering-->
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc82">Chapter&#XA0;6</A>&#XA0;&#XA0;<A HREF="#clustering">Clustering</A></H1><!--SEC END --><P> <A NAME="clustering"></A> <H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc83">Chapter&#XA0;6</A>&#XA0;&#XA0;<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--> </P><P> <A NAME="howitworks"></A> </P><!--TOC section How it Works-->
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc83">6.1</A>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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 </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 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 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">session manager,
</LI><LI CLASS="li-itemize">s2s manager. </LI><LI CLASS="li-itemize">s2s manager.
</LI></UL><P> <A NAME="router"></A> </P><!--TOC subsection Router--> </LI></UL><P> <A NAME="router"></A> </P><!--TOC subsection Router-->
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc84">6.1.1</A>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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 </P><P>This module is the main router of XMPP packets on each node. It
routes them based on their destination&#X2019;s domains. It uses a global routes them based on their destination&#X2019;s domains. It uses a global
routing table. The domain of the packet&#X2019;s destination is searched in the routing table. The domain of the packet&#X2019;s destination is searched in the
routing table, and if it is found, the packet is routed to 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--> 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>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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 </P><P>This module routes packets which have a destination domain equal to
one of this server&#X2019;s host names. If the destination JID has a non-empty user one of this server&#X2019;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 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--> 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>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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 </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 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 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--> 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>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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 </P><P>This module routes packets to other XMPP servers. First, it
checks if an opened s2s connection from the domain of the packet&#X2019;s checks if an opened s2s connection from the domain of the packet&#X2019;s
source to the domain of the packet&#X2019;s destination exists. If that is the case, source to the domain of the packet&#X2019;s destination exists. If that is the case,
the s2s manager routes the packet to the process 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--> 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>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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>), </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 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"> following steps:</P><OL CLASS="enumerate" type=1><LI CLASS="li-enumerate">
@ -3921,10 +3989,10 @@ and &#X2018;<CODE>access</CODE>&#X2019; options because they will be taken from
enabled only on one machine in the cluster. enabled only on one machine in the cluster.
</LI></OL><P>You can repeat these steps for other machines supposed to serve this </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--> domain.</P><P> <A NAME="servicelb"></A> </P><!--TOC section Service Load-Balancing-->
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc89">6.3</A>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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--> </P><P> <A NAME="componentlb"></A> </P><!--TOC subsection Components Load-Balancing-->
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc90">6.3.1</A>&#XA0;&#XA0;<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.1</A>&#XA0;&#XA0;<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>&#XA0;&#XA0;<A HREF="#domainlb">Domain Load-Balancing Algorithm</A></H3><!--SEC END --><P> <A NAME="domainlb"></A> <H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc92">6.3.2</A>&#XA0;&#XA0;<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><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><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"> </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_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><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--> </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>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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><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--> </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&#XA0;7</A>&#XA0;&#XA0;<A HREF="#debugging">Debugging</A></H1><!--SEC END --><P> <A NAME="debugging"></A> <H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc94">Chapter&#XA0;7</A>&#XA0;&#XA0;<A HREF="#debugging">Debugging</A></H1><!--SEC END --><P> <A NAME="debugging"></A>
</P><P> <A NAME="logfiles"></A> </P><!--TOC section Log Files--> </P><P> <A NAME="logfiles"></A> </P><!--TOC section Log Files-->
<H2 CLASS="section"><!--SEC ANCHOR --><A NAME="htoc94">7.1</A>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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"> </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 <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) </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>) (please refer to section <A HREF="#ectl-commands">4.1.1</A>)
reopens the log files, reopens the log files,
and also renames the old ones if you didn&#X2019;t rename them.</P><P> <A NAME="debugconsole"></A> </P><!--TOC section Debug Console--> and also renames the old ones if you didn&#X2019;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>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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, 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. so it is important to use it with extremely care.
There are some simple and safe examples in the article 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--> <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>&#XA0;&#XA0;<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>&#XA0;&#XA0;<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 </P><P><TT>ejabberd</TT> includes a watchdog mechanism that may be useful to developers
when troubleshooting a problem related to memory usage. when troubleshooting a problem related to memory usage.
If a process in the <TT>ejabberd</TT> server consumes more memory than the configured threshold, 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: To remove all watchdog admins, set the option with an empty list:
</P><PRE CLASS="verbatim">{watchdog_admins, []}. </P><PRE CLASS="verbatim">{watchdog_admins, []}.
</PRE><P> <A NAME="i18ni10n"></A> </P><!--TOC chapter Internationalization and Localization--> </PRE><P> <A NAME="i18ni10n"></A> </P><!--TOC chapter Internationalization and Localization-->
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc97">Appendix&#XA0;A</A>&#XA0;&#XA0;<A HREF="#i18ni10n">Internationalization and Localization</A></H1><!--SEC END --><P> <A NAME="i18ni10n"></A> <H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc98">Appendix&#XA0;A</A>&#XA0;&#XA0;<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. </P><P>The source code of <TT>ejabberd</TT> supports localization.
The translators can edit the The translators can edit the
<A HREF="http://www.gnu.org/software/gettext/">gettext</A> .po files <A HREF="http://www.gnu.org/software/gettext/">gettext</A> .po files
@ -4020,9 +4088,9 @@ HTTP header &#X2018;Accept-Language: ru&#X2019;</TD></TR>
</TABLE></DIV> </TABLE></DIV>
<A NAME="fig:webadmmainru"></A> <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--> <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&#XA0;B</A>&#XA0;&#XA0;<A HREF="#releasenotes">Release Notes</A></H1><!--SEC END --><P> <A NAME="releasenotes"></A> <H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc99">Appendix&#XA0;B</A>&#XA0;&#XA0;<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--> </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&#XA0;C</A>&#XA0;&#XA0;<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&#XA0;C</A>&#XA0;&#XA0;<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"> </P><UL CLASS="itemize"><LI CLASS="li-itemize">
Alexey Shchepin (<A HREF="xmpp:aleksey@jabber.ru"><TT>xmpp:aleksey@jabber.ru</TT></A>) 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>) </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">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><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--> </LI></UL><P> <A NAME="copyright"></A> </P><!--TOC chapter Copyright Information-->
<H1 CLASS="chapter"><!--SEC ANCHOR --><A NAME="htoc100">Appendix&#XA0;D</A>&#XA0;&#XA0;<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&#XA0;D</A>&#XA0;&#XA0;<A HREF="#copyright">Copyright Information</A></H1><!--SEC END --><P> <A NAME="copyright"></A> </P><P>Ejabberd Installation and Operation Guide.<BR>
Copyright &#XA9; 2003 &#X2014; 2010 ProcessOne</P><P>This document is free software; you can redistribute it and/or Copyright &#XA9; 2003 &#X2014; 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 modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2 as published by the Free Software Foundation; either version 2

View File

@ -98,6 +98,7 @@
\newcommand{\modvcard}{\module{mod\_vcard}} \newcommand{\modvcard}{\module{mod\_vcard}}
\newcommand{\modvcardldap}{\module{mod\_vcard\_ldap}} \newcommand{\modvcardldap}{\module{mod\_vcard\_ldap}}
\newcommand{\modvcardodbc}{\module{mod\_vcard\_odbc}} \newcommand{\modvcardodbc}{\module{mod\_vcard\_odbc}}
\newcommand{\modvcardxupdate}{\module{mod\_vcard\_xupdate}}
\newcommand{\modversion}{\module{mod\_version}} \newcommand{\modversion}{\module{mod\_version}}
%% Contributed modules %% Contributed modules
@ -309,8 +310,8 @@ To compile \ejabberd{} on a `Unix-like' operating system, you need:
\item GNU Make \item GNU Make
\item GCC \item GCC
\item Libexpat 1.95 or higher \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 Erlang/OTP R10B-9 or higher. The recommended versions are R12B-5 and R13B04.
\item OpenSSL 0.9.6 or higher, for STARTTLS, SASL and SSL encryption. Optional, highly recommended. \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 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 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}. \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} \titem{--disable-transient-supervisors}
Disable the use of Erlang/OTP supervision for transient processes. 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} \end{description}
\makesubsection{install}{Install} \makesubsection{install}{Install}
@ -803,12 +808,12 @@ The available modules, their purpose and the options allowed by each one are:
\texttt{zlib} \texttt{zlib}
\titem{\texttt{ejabberd\_s2s\_in}} \titem{\texttt{ejabberd\_s2s\_in}}
Handles incoming s2s connections.\\ Handles incoming s2s connections.\\
Options: \texttt{max\_stanza\_size} Options: \texttt{max\_stanza\_size}, \texttt{shaper}
\titem{\texttt{ejabberd\_service}} \titem{\texttt{ejabberd\_service}}
Interacts with an \footahref{http://www.ejabberd.im/tutorials-transports}{external component} Interacts with an \footahref{http://www.ejabberd.im/tutorials-transports}{external component}
(as defined in the Jabber Component Protocol (\xepref{0114}).\\ (as defined in the Jabber Component Protocol (\xepref{0114}).\\
Options: \texttt{access}, \texttt{hosts}, \texttt{max\_fsm\_queue}, 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}} \titem{\texttt{ejabberd\_stun}}
Handles STUN Binding requests as defined in Handles STUN Binding requests as defined in
\footahref{http://tools.ietf.org/html/rfc5389}{RFC 5389}.\\ \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 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 no space in the queue (and ejabberd was not able to accept them
immediately). Default value is 5. 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. \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}. 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} \titem{\{hosts, [Hostname, ...], [HostOption, ...]\}} \ind{options!hosts}
The external Jabber component that connects to this \term{ejabberd\_service} The external Jabber component that connects to this \term{ejabberd\_service}
can serve one or more hostnames. 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 Note that you cannot define in a single \term{ejabberd\_service} components of
different services: add an \term{ejabberd\_service} for each service, different services: add an \term{ejabberd\_service} for each service,
as seen in an example below. 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} \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 This option enables HTTP Binding (\xepref{0124} and \xepref{0206}) support. HTTP Bind
enables access via HTTP requests to \ejabberd{} from behind firewalls which 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\}} \titem{\{max\_stanza\_size, Size\}}
\ind{options!max\_stanza\_size}This option specifies an \ind{options!max\_stanza\_size}This option specifies an
approximate maximum size in bytes of XML stanzas. Approximate, 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 data. For example \verb|{max_stanza_size, 65536}|. The default
value is \term{infinity}. Recommended values are 65536 for c2s value is \term{infinity}. Recommended values are 65536 for c2s
connections and 131072 for s2s connections. s2s max stanza size 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/}, 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\}]\}} use this option: \term{\{request\_handlers, [\{["a", "b"], mod\_foo\}, \{["http-bind"], mod\_http\_bind\}]\}}
\titem{\{service\_check\_from, true|false\}} \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. \ind{options!service\_check\_from}
Note that \xepref{0114} requires that the domain must match the hostname of the component. This option can be used with \term{ejabberd\_service} only.
Only enable this option if you are completely sure you need to enable it. \xepref{0114} requires that the domain must match the hostname of the component.
Default value: false. 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 \titem{\{shaper, none|ShaperName\}} \ind{options!shaper}This option defines a
shaper for the port (see section~\ref{shapers}). The default value shaper for the port (see section~\ref{shapers}). The default value
is \term{none}. is \term{none}.
@ -1188,8 +1191,7 @@ for user authentication. The syntax is:
The following authentication methods are supported by \ejabberd{}: The following authentication methods are supported by \ejabberd{}:
\begin{itemize} \begin{itemize}
\item internal (default) --- See section~\ref{internalauth}. \item internal (default) --- See section~\ref{internalauth}.
\item external --- There are \footahref{http://www.ejabberd.im/extauth}{some \item external --- See section~\ref{extauth}.
example authentication scripts}.
\item ldap --- See section~\ref{ldap}. \item ldap --- See section~\ref{ldap}.
\item odbc --- See section~\ref{mysql}, \ref{pgsql}, \item odbc --- See section~\ref{mysql}, \ref{pgsql},
\ref{mssql} and \ref{odbc}. \ref{mssql} and \ref{odbc}.
@ -1197,7 +1199,7 @@ The following authentication methods are supported by \ejabberd{}:
\item pam --- See section~\ref{pam}. \item pam --- See section~\ref{pam}.
\end{itemize} \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} \makesubsubsection{internalauth}{Internal}
\ind{internal authentication}\ind{Mnesia} \ind{internal authentication}\ind{Mnesia}
@ -1219,6 +1221,48 @@ Examples:
\end{verbatim} \end{verbatim}
\end{itemize} \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} \makesubsubsection{saslanonymous}{SASL Anonymous and Anonymous Login}
\ind{sasl anonymous}\ind{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. \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 Default is \term{"ejabberd"}. Refer to the PAM documentation of your operation system
for more information. 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} \end{description}
Example: Example:
@ -1952,19 +2000,9 @@ enabled. This can be done, by using next commands:
\makesubsubsection{configuremssql}{Database Connection} \makesubsubsection{configuremssql}{Database Connection}
\ind{Microsoft SQL Server!Database Connection} \ind{Microsoft SQL Server!Database Connection}
By default \ejabberd{} opens 10 connections to the database for each virtual host. The configuration of Database Connection for a Microsoft SQL Server
Use this option to modify the value: is the same as the configuration for
\begin{verbatim} ODBC compatible servers (see section~\ref{configureodbc}).
{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}
\makesubsubsection{mssqlauth}{Authentication} \makesubsubsection{mssqlauth}{Authentication}
@ -1972,7 +2010,8 @@ Specify in seconds: for example 28800 means 8 hours.
%TODO: not sure if this section is right!!!!!! %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}). ODBC compatible servers (see section~\ref{odbcauth}).
\makesubsubsection{mssqlstorage}{Storage} \makesubsubsection{mssqlstorage}{Storage}
@ -2169,13 +2208,19 @@ module loaded!
server and use LDAP directory as vCard storage. Shared rosters are not supported server and use LDAP directory as vCard storage. Shared rosters are not supported
yet. 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 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} \makesubsubsection{ldapconnection}{Connection}
Two connections are established to the LDAP server per vhost,
one for authentication and other for regular calls.
Parameters: Parameters:
\begin{description} \begin{description}
\titem{\{ldap\_servers, [Servers, ...]\}} \ind{options!ldap\_server}List of IP addresses or DNS names of your \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. The value \term{tls} enables encryption by using LDAP over SSL.
Note that STARTTLS encryption is not supported. Note that STARTTLS encryption is not supported.
The default value is: \term{none}. 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. \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. 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. If you configure a value, it is stored in \ejabberd{}'s database.
@ -2205,11 +2255,12 @@ Example:
{ldap_password, "secret"}. {ldap_password, "secret"}.
\end{verbatim} \end{verbatim}
Note that current LDAP implementation does not support SASL authentication.
\makesubsubsection{ldapauth}{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} \begin{description}
\titem{\{ldap\_base, Base\}}\ind{options!ldap\_base}LDAP base directory which stores \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 \modconfigure{} & Server configuration using Ad-Hoc & \modadhoc{} \\
\hline \ahrefloc{moddisco}{\moddisco{}} & Service Discovery (\xepref{0030}) & \\ \hline \ahrefloc{moddisco}{\moddisco{}} & Service Discovery (\xepref{0030}) & \\
\hline \ahrefloc{modecho}{\modecho{}} & Echoes XMPP stanzas & \\ \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{modirc}{\modirc{}} & IRC transport & \\
\hline \ahrefloc{modlast}{\modlast{}} & Last Activity (\xepref{0012}) & \\ \hline \ahrefloc{modlast}{\modlast{}} & Last Activity (\xepref{0012}) & \\
\hline \ahrefloc{modlast}{\modlastodbc{}} & Last Activity (\xepref{0012}) & supported DB (*) \\ \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{modvcard}{\modvcard{}} & vcard-temp (\xepref{0054}) & \\
\hline \ahrefloc{modvcardldap}{\modvcardldap{}} & vcard-temp (\xepref{0054}) & LDAP server \\ \hline \ahrefloc{modvcardldap}{\modvcardldap{}} & vcard-temp (\xepref{0054}) & LDAP server \\
\hline \ahrefloc{modvcard}{\modvcardodbc{}} & vcard-temp (\xepref{0054}) & supported DB (*) \\ \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 \ahrefloc{modversion}{\modversion{}} & Software Version (\xepref{0092}) & \\
\hline \hline
\end{tabular} \end{tabular}
@ -3155,6 +3209,10 @@ Module options:
\titem{\{anonymous, true|false\}} The room is anonymous: \titem{\{anonymous, true|false\}} The room is anonymous:
occupants don't see the real JIDs of other occupants. occupants don't see the real JIDs of other occupants.
Note that the room moderators can always see the real JIDs of the 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{\{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{\{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'. \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: This module reads also another option defined globally for the server:
\term{\{registration\_timeout, Timeout\}}. \ind{options!registratimeout} \term{\{registration\_timeout, Timeout\}}. \ind{options!registratimeout}
This option limits the frequency of registration from a given IP or username. 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 So, a user that tries to register a new account from the same IP address or JID during
this number of seconds after previous registration. this number of seconds after his previous registration
Timeout is expressed in seconds, and must be an integer. 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, To disable this limitation,
instead of an integer put a word like: \term{infinity}. instead of an integer put a word like: \term{infinity}.
Default value: 600 seconds. Default value: 600 seconds.
@ -4109,9 +4169,12 @@ Examples:
implemented in the \modvcardldap{} module. This module does not depend on the implemented in the \modvcardldap{} module. This module does not depend on the
authentication method (see~\ref{ldapauth}). 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 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 The \modvcardldap{} module has
its own optional parameters. The first group of parameters has the same its own optional parameters. The first group of parameters has the same
@ -4315,6 +4378,27 @@ searching his info in LDAP.
\end{verbatim} \end{verbatim}
\end{itemize} \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{}} \makesubsection{modversion}{\modversion{}}
\ind{modules!\modversion{}}\ind{protocols!XEP-0092: Software Version} \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 manage the database, enable/disable ports listened for, view server
statistics,\ldots 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} \begin{itemize}
\item You can serve the Web Admin on the same port as the \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 \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 \\ URL). If you log in with `\jid{admin@example.com}' on \\
\verb|http://example.org:5280/admin/server/example.com/| you can only \verb|http://example.org:5280/admin/server/example.com/| you can only
administer the virtual host \jid{example.com}. administer the virtual host \jid{example.com}.
The account `\jid{reviewer@example.com}' can browse that vhost in read-only mode.
\begin{verbatim} \begin{verbatim}
{acl, admins, {user, "admin", "example.net"}}. {acl, admins, {user, "admin", "example.net"}}.
{host_config, "example.com", [{acl, admins, {user, "admin", "example.com"}}]}. {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, configure, [{allow, admins}]}.
{access, webadmin_view, [{allow, viewers}]}.
{hosts, ["example.org"]}. {hosts, ["example.org"]}.

View 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/

View 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/

View File

@ -1,2 +1,2 @@
% ejabberd version (automatically generated). % 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
View File

View File

@ -30,15 +30,17 @@ else
INIT_USER=$(INSTALLUSER) INIT_USER=$(INSTALLUSER)
endif endif
EFLAGS += @ERLANG_SSL39@ -pa . EFLAGS += @ERLANG_SSLVER@ -pa .
ERLANG_CFLAGS += @ERLANG_SSLVER@
# make debug=true to compile Erlang module with debug informations. # make debug=true to compile Erlang module with debug informations.
ifdef debug ifdef debug
EFLAGS+=+debug_info +export_all EFLAGS+=+debug_info +export_all
endif endif
ifdef ejabberd_debug DEBUGTOOLS = p1_prof.erl
EFLAGS+=-Dejabberd_debug ifdef debugtools
SOURCES+=$(DEBUGTOOLS)
endif endif
ifeq (@hipe@, true) ifeq (@hipe@, true)
@ -53,10 +55,20 @@ ifeq (@full_xml@, true)
EFLAGS+=-DFULL_XML_SUPPORT EFLAGS+=-DFULL_XML_SUPPORT
endif endif
ifeq (@nif@, true)
EFLAGS+=-DNIF
ERLSHLIBS=xml.so
endif
ifeq (@transient_supervisors@, false) ifeq (@transient_supervisors@, false)
EFLAGS+=-DNO_TRANSIENT_SUPERVISORS EFLAGS+=-DNO_TRANSIENT_SUPERVISORS
endif endif
ifeq (@md2@, true)
EFLAGS+=-DHAVE_MD2
ERLANG_CFLAGS += -DHAVE_MD2
endif
INSTALL_EPAM= INSTALL_EPAM=
ifeq (@pam@, pam) ifeq (@pam@, pam)
INSTALL_EPAM=install -m 750 $(O_USER) epam $(PBINDIR) INSTALL_EPAM=install -m 750 $(O_USER) epam $(PBINDIR)
@ -66,10 +78,11 @@ prefix = @prefix@
exec_prefix = @exec_prefix@ exec_prefix = @exec_prefix@
SUBDIRS = @mod_irc@ @mod_pubsub@ @mod_muc@ @mod_proxy65@ @eldap@ @pam@ @web@ stringprep stun @tls@ @odbc@ @ejabberd_zlib@ 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 ERLBEHAVS = cyrsasl.erl gen_mod.erl p1_fsm.erl
SOURCES_ALL = $(wildcard *.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) ERLBEHAVBEAMS = $(ERLBEHAVS:.erl=.beam)
BEAMS = $(SOURCES:.erl=.beam) BEAMS = $(SOURCES:.erl=.beam)
@ -247,9 +260,14 @@ install: all
# #
# Documentation # Documentation
install -d $(DOCDIR) install -d $(DOCDIR)
install -m 644 ../doc/dev.html $(DOCDIR)
install -m 644 ../doc/guide.html $(DOCDIR) install -m 644 ../doc/guide.html $(DOCDIR)
install -m 644 ../doc/*.png $(DOCDIR) install -m 644 ../doc/*.png $(DOCDIR)
install -m 644 ../doc/*.txt $(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 uninstall: uninstall-binary

17
src/aclocal.m4 vendored
View File

@ -121,7 +121,6 @@ AC_DEFUN(AM_WITH_ERLANG,
-author('alexey@sevcom.net'). -author('alexey@sevcom.net').
-export([[start/0]]). -export([[start/0]]).
-include_lib("ssl/include/ssl_pkix.hrl").
start() -> start() ->
EIDirS = code:lib_dir("erl_interface") ++ "\n", EIDirS = code:lib_dir("erl_interface") ++ "\n",
@ -130,11 +129,13 @@ start() ->
file:write_file("conftest.out", list_to_binary(EIDirS ++ EILibS ++ ssldef() ++ RootDirS)), file:write_file("conftest.out", list_to_binary(EIDirS ++ EILibS ++ ssldef() ++ RootDirS)),
halt(). halt().
-[ifdef]('id-pkix'). ssldef() ->
ssldef() -> "-DSSL39\n". OTP = (catch erlang:system_info(otp_release)),
-else. if
ssldef() -> "\n". OTP >= "R14" -> "-DSSL40\n";
-endif. OTP >= "R12" -> "-DSSL39\n";
true -> "\n"
end.
%% return physical architecture based on OS/Processor %% return physical architecture based on OS/Processor
archname() -> archname() ->
@ -184,7 +185,7 @@ _EOF
# Second line # Second line
ERLANG_EI_LIB=`cat conftest.out | head -n 2 | tail -n 1` ERLANG_EI_LIB=`cat conftest.out | head -n 2 | tail -n 1`
# Third line # 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 # End line
ERLANG_DIR=`cat conftest.out | tail -n 1` ERLANG_DIR=`cat conftest.out | tail -n 1`
@ -193,7 +194,7 @@ _EOF
AC_SUBST(ERLANG_CFLAGS) AC_SUBST(ERLANG_CFLAGS)
AC_SUBST(ERLANG_LIBS) AC_SUBST(ERLANG_LIBS)
AC_SUBST(ERLANG_SSL39) AC_SUBST(ERLANG_SSLVER)
AC_SUBST(ERLC) AC_SUBST(ERLC)
AC_SUBST(ERL) AC_SUBST(ERL)
]) ])

View File

@ -1,7 +1,7 @@
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% File : adhoc.erl %%% File : adhoc.erl
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se> %%% 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> %%% Created : 31 Oct 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%% %%%
%%% %%%

240
src/configure vendored
View File

@ -1,12 +1,14 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # 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>. # Report bugs to <ejabberd@process-one.net>.
# #
#
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software # 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
# Foundation, Inc. # Inc.
#
# #
# This configure script is free software; the Free Software Foundation # This configure script is free software; the Free Software Foundation
# gives unlimited permission to copy, distribute and modify it. # 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'" 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. # Name of the host.
# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, # hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
@ -547,10 +550,10 @@ MFLAGS=
MAKEFLAGS= MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='ejabberd.erl' PACKAGE_NAME='ejabberd'
PACKAGE_TARNAME='ejabberd' PACKAGE_TARNAME='ejabberd'
PACKAGE_VERSION='version' PACKAGE_VERSION='2.1.x'
PACKAGE_STRING='ejabberd.erl version' PACKAGE_STRING='ejabberd 2.1.x'
PACKAGE_BUGREPORT='ejabberd@process-one.net' PACKAGE_BUGREPORT='ejabberd@process-one.net'
PACKAGE_URL='' PACKAGE_URL=''
@ -605,9 +608,11 @@ build_os
build_vendor build_vendor
build_cpu build_cpu
build build
md2
INSTALLUSER INSTALLUSER
SSL_CFLAGS SSL_CFLAGS
SSL_LIBS SSL_LIBS
nif
full_xml full_xml
transient_supervisors transient_supervisors
db_type db_type
@ -644,7 +649,7 @@ EGREP
GREP GREP
CPP CPP
LIBICONV LIBICONV
ERLANG_SSL39 ERLANG_SSLVER
ERLANG_LIBS ERLANG_LIBS
ERLANG_CFLAGS ERLANG_CFLAGS
ERL ERL
@ -718,6 +723,7 @@ enable_roster_gateway_workaround
enable_mssql enable_mssql
enable_transient_supervisors enable_transient_supervisors
enable_full_xml enable_full_xml
enable_nif
with_openssl with_openssl
enable_user 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. # 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. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF 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]... Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1339,7 +1345,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of ejabberd.erl version:";; short | recursive ) echo "Configuration of ejabberd 2.1.x:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@ -1369,6 +1375,8 @@ Optional Features:
(default: yes) (default: yes)
--enable-full-xml use XML features in XMPP stream (ex: CDATA) --enable-full-xml use XML features in XMPP stream (ex: CDATA)
(default: no, requires XML compliant clients) (default: no, requires XML compliant clients)
--enable-nif replace some functions with C equivalents. Requires
Erlang R13B04 or higher (default: no)
--enable-user[[[=USER]]] --enable-user[[[=USER]]]
allow this system user to start ejabberd (default: allow this system user to start ejabberd (default:
no) no)
@ -1390,7 +1398,7 @@ Some influential environment variables:
LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
nonstandard directory <lib dir> nonstandard directory <lib dir>
LIBS libraries to pass to the linker, e.g. -l<library> 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> you have headers in a nonstandard directory <include dir>
CPP C preprocessor CPP C preprocessor
ERLC Erlang/OTP compiler command [autodetected] ERLC Erlang/OTP compiler command [autodetected]
@ -1462,8 +1470,8 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
ejabberd.erl configure version ejabberd configure 2.1.x
generated by GNU Autoconf 2.64 generated by GNU Autoconf 2.65
Copyright (C) 2009 Free Software Foundation, Inc. Copyright (C) 2009 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation This configure script is free software; the Free Software Foundation
@ -1510,7 +1518,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1 ac_retval=1
fi fi
eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} 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 } # ac_fn_c_try_compile
@ -1556,7 +1564,7 @@ fi
# left behind by Apple's compiler. We do this before executing the actions. # left behind by Apple's compiler. We do this before executing the actions.
rm -rf conftest.dSYM conftest_ipa8_conftest.oo rm -rf conftest.dSYM conftest_ipa8_conftest.oo
eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} 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 } # ac_fn_c_try_link
@ -1593,7 +1601,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1 ac_retval=1
fi fi
eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} 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 } # ac_fn_c_try_cpp
@ -1728,7 +1736,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
fi fi
rm -rf conftest.dSYM conftest_ipa8_conftest.oo rm -rf conftest.dSYM conftest_ipa8_conftest.oo
eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} 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 } # ac_fn_c_try_run
@ -1801,15 +1809,15 @@ sed 's/^/| /' conftest.$ac_ext >&5
fi fi
rm -rf conftest.dSYM conftest_ipa8_conftest.oo rm -rf conftest.dSYM conftest_ipa8_conftest.oo
eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} 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 } # ac_fn_erl_try_run
cat >config.log <<_ACEOF cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. running configure, to aid debugging if configure makes a mistake.
It was created by ejabberd.erl $as_me version, which was It was created by ejabberd $as_me 2.1.x, which was
generated by GNU Autoconf 2.64. Invocation command line was generated by GNU Autoconf 2.65. Invocation command line was
$ $0 $@ $ $0 $@
@ -2062,7 +2070,7 @@ fi
for ac_site_file in "$ac_site_file1" "$ac_site_file2" for ac_site_file in "$ac_site_file1" "$ac_site_file2"
do do
test "x$ac_site_file" = xNONE && continue 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:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
$as_echo "$as_me: loading site script $ac_site_file" >&6;} $as_echo "$as_me: loading site script $ac_site_file" >&6;}
sed 's/^/| /' "$ac_site_file" >&5 sed 's/^/| /' "$ac_site_file" >&5
@ -2071,9 +2079,9 @@ $as_echo "$as_me: loading site script $ac_site_file" >&6;}
done done
if test -r "$cache_file"; then if test -r "$cache_file"; then
# Some versions of bash will fail to source /dev/null (special # Some versions of bash will fail to source /dev/null (special files
# files actually), so we avoid doing that. # actually), so we avoid doing that. DJGPP emulates it as a regular file.
if test -f "$cache_file"; then 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:${as_lineno-$LINENO}: loading cache $cache_file" >&5
$as_echo "$as_me: loading cache $cache_file" >&6;} $as_echo "$as_me: loading cache $cache_file" >&6;}
case $cache_file in case $cache_file in
@ -2479,32 +2487,30 @@ $as_echo "$ac_try_echo"; } >&5
... rest of stderr output deleted ... ... rest of stderr output deleted ...
10q' conftest.err >conftest.er1 10q' conftest.err >conftest.er1
cat conftest.er1 >&5 cat conftest.er1 >&5
rm -f conftest.er1 conftest.err
fi fi
rm -f conftest.er1 conftest.err
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } test $ac_status = 0; }
done done
cat confdefs.h - <<_ACEOF >conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */ /* end confdefs.h. */
#include <stdio.h>
int int
main () main ()
{ {
FILE *f = fopen ("conftest.out", "w");
return ferror (f) || fclose (f) != 0;
; ;
return 0; return 0;
} }
_ACEOF _ACEOF
ac_clean_files_save=$ac_clean_files 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. # Try to create an executable without -o first, disregard a.out.
# It will help us diagnose broken compilers, and finding out an intuition # It will help us diagnose broken compilers, and finding out an intuition
# of exeext. # of exeext.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
$as_echo_n "checking for C compiler default output file name... " >&6; } $as_echo_n "checking whether the C compiler works... " >&6; }
ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
# The possible output files: # The possible output files:
@ -2566,9 +2572,9 @@ test "$ac_cv_exeext" = no && ac_cv_exeext=
else else
ac_file='' ac_file=''
fi fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
$as_echo "$ac_file" >&6; }
if test -z "$ac_file"; then : if test -z "$ac_file"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
$as_echo "$as_me: failed program was:" >&5 $as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5 sed 's/^/| /' conftest.$ac_ext >&5
@ -2577,51 +2583,18 @@ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
{ as_fn_set_status 77 { as_fn_set_status 77
as_fn_error "C compiler cannot create executables as_fn_error "C compiler cannot create executables
See \`config.log' for more details." "$LINENO" 5; }; } See \`config.log' for more details." "$LINENO" 5; }; }
fi
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 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 "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; } $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
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 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 "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
$as_echo_n "checking for suffix of executables... " >&6; } $as_echo_n "checking for suffix of executables... " >&6; }
if { { ac_try="$ac_link" 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 as_fn_error "cannot compute suffix of executables: cannot compile and link
See \`config.log' for more details." "$LINENO" 5; } See \`config.log' for more details." "$LINENO" 5; }
fi 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 "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
$as_echo "$ac_cv_exeext" >&6; } $as_echo "$ac_cv_exeext" >&6; }
rm -f conftest.$ac_ext rm -f conftest.$ac_ext
EXEEXT=$ac_cv_exeext EXEEXT=$ac_cv_exeext
ac_exeext=$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 "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
$as_echo_n "checking for suffix of object files... " >&6; } $as_echo_n "checking for suffix of object files... " >&6; }
if test "${ac_cv_objext+set}" = set; then : if test "${ac_cv_objext+set}" = set; then :
@ -3180,7 +3212,6 @@ fi
-author('alexey@sevcom.net'). -author('alexey@sevcom.net').
-export([start/0]). -export([start/0]).
-include_lib("ssl/include/ssl_pkix.hrl").
start() -> start() ->
EIDirS = code:lib_dir("erl_interface") ++ "\n", EIDirS = code:lib_dir("erl_interface") ++ "\n",
@ -3189,11 +3220,13 @@ start() ->
file:write_file("conftest.out", list_to_binary(EIDirS ++ EILibS ++ ssldef() ++ RootDirS)), file:write_file("conftest.out", list_to_binary(EIDirS ++ EILibS ++ ssldef() ++ RootDirS)),
halt(). halt().
-ifdef('id-pkix'). ssldef() ->
ssldef() -> "-DSSL39\n". OTP = (catch erlang:system_info(otp_release)),
-else. if
ssldef() -> "\n". OTP >= "R14" -> "-DSSL40\n";
-endif. OTP >= "R12" -> "-DSSL39\n";
true -> "\n"
end.
%% return physical architecture based on OS/Processor %% return physical architecture based on OS/Processor
archname() -> archname() ->
@ -3243,7 +3276,7 @@ _EOF
# Second line # Second line
ERLANG_EI_LIB=`cat conftest.out | head -n 2 | tail -n 1` ERLANG_EI_LIB=`cat conftest.out | head -n 2 | tail -n 1`
# Third line # 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 # End line
ERLANG_DIR=`cat conftest.out | tail -n 1` 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 enableval=$enable_transient_supervisors; case "${enableval}" in
yes) transient_supervisors=true ;; yes) transient_supervisors=true ;;
no) transient_supervisors=false ;; 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 esac
else else
transient_supervisors=true 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" 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 #openssl
@ -4777,6 +4823,16 @@ if test "$ENABLEUSER" != ""; then
fi 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= ac_aux_dir=
for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
for ac_t in install-sh install.sh shtool; do for ac_t in install-sh install.sh shtool; do
@ -4920,7 +4976,7 @@ case "$target_os" in
echo "Target OS is 'Darwin10'" echo "Target OS is 'Darwin10'"
ac_ext=erl ac_ext=erl
ac_compile='$ERLC $ERLCFLAGS -b beam conftest.$ac_ext >&5' 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 if test -n "$ERLC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for erlc" >&5 { $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 # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" ac_log="
This file was extended by ejabberd.erl $as_me version, which was This file was extended by ejabberd $as_me 2.1.x, which was
generated by GNU Autoconf 2.64. Invocation command line was generated by GNU Autoconf 2.65. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_HEADERS = $CONFIG_HEADERS
@ -5645,6 +5701,7 @@ Usage: $0 [OPTION]... [TAG]...
-h, --help print this help, then exit -h, --help print this help, then exit
-V, --version print version number and configuration settings, then exit -V, --version print version number and configuration settings, then exit
--config print configuration, then exit
-q, --quiet, --silent -q, --quiet, --silent
do not print progress messages do not print progress messages
-d, --debug don't remove temporary files -d, --debug don't remove temporary files
@ -5659,10 +5716,11 @@ Report bugs to <ejabberd@process-one.net>."
_ACEOF _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\ ac_cs_version="\\
ejabberd.erl config.status version ejabberd config.status 2.1.x
configured by $0, generated by GNU Autoconf 2.64, configured by $0, generated by GNU Autoconf 2.65,
with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" with options \\"\$ac_cs_config\\"
Copyright (C) 2009 Free Software Foundation, Inc. Copyright (C) 2009 Free Software Foundation, Inc.
This config.status script is free software; the Free Software Foundation This config.status script is free software; the Free Software Foundation
@ -5697,6 +5755,8 @@ do
ac_cs_recheck=: ;; ac_cs_recheck=: ;;
--version | --versio | --versi | --vers | --ver | --ve | --v | -V ) --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
$as_echo "$ac_cs_version"; exit ;; $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 | --debu | --deb | --de | --d | -d )
debug=: ;; debug=: ;;
--file | --fil | --fi | --f ) --file | --fil | --fi | --f )
@ -5876,7 +5936,7 @@ s/'"$ac_delim"'$//
t delim t delim
:nl :nl
h h
s/\(.\{148\}\).*/\1/ s/\(.\{148\}\)..*/\1/
t more1 t more1
s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
p p
@ -5890,7 +5950,7 @@ s/.\{148\}//
t nl t nl
:delim :delim
h h
s/\(.\{148\}\).*/\1/ s/\(.\{148\}\)..*/\1/
t more2 t more2
s/["\\]/\\&/g; s/^/"/; s/$/"/ s/["\\]/\\&/g; s/^/"/; s/$/"/
p p

View File

@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ(2.53) 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. # Checks for programs.
AC_PROG_CC AC_PROG_CC
@ -81,7 +81,7 @@ AC_ARG_ENABLE(transient_supervisors,
[case "${enableval}" in [case "${enableval}" in
yes) transient_supervisors=true ;; yes) transient_supervisors=true ;;
no) transient_supervisors=false ;; 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]) esac],[transient_supervisors=true])
AC_SUBST(transient_supervisors) AC_SUBST(transient_supervisors)
@ -94,6 +94,15 @@ AC_ARG_ENABLE(full_xml,
esac],[full_xml=false]) esac],[full_xml=false])
AC_SUBST(full_xml) 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 AC_CONFIG_FILES([Makefile
$make_mod_irc $make_mod_irc
$make_mod_muc $make_mod_muc
@ -139,6 +148,9 @@ if test "$ENABLEUSER" != ""; then
AC_SUBST([INSTALLUSER], [$ENABLEUSER]) AC_SUBST([INSTALLUSER], [$ENABLEUSER])
fi fi
AC_CHECK_HEADER(openssl/md2.h, md2=true, md2=false)
AC_SUBST(md2)
AC_CANONICAL_SYSTEM AC_CANONICAL_SYSTEM
#AC_DEFINE_UNQUOTED(CPU_VENDOR_OS, "$target") #AC_DEFINE_UNQUOTED(CPU_VENDOR_OS, "$target")
#AC_SUBST(target_os) #AC_SUBST(target_os)

View File

@ -2,7 +2,7 @@
{application, ejabberd, {application, ejabberd,
[{description, "ejabberd"}, [{description, "ejabberd"},
{vsn, "2.1.3"}, {vsn, "2.1.x"},
{modules, [acl, {modules, [acl,
adhoc, adhoc,
configure, configure,
@ -115,6 +115,7 @@
nodetree_virtual, nodetree_virtual,
p1_fsm, p1_fsm,
p1_mnesia, p1_mnesia,
p1_prof,
randoms, randoms,
sha, sha,
shaper, shaper,

View File

@ -31,6 +31,8 @@
%% Server %% Server
status/0, reopen_log/0, status/0, reopen_log/0,
stop_kindly/2, send_service_message_all_mucs/2, stop_kindly/2, send_service_message_all_mucs/2,
%% Erlang
update_list/0, update/1,
%% Accounts %% Accounts
register/3, unregister/2, register/3, unregister/2,
registered_users/1, registered_users/1,
@ -95,6 +97,17 @@ commands() ->
{leveldesc, string} {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], #ejabberd_commands{name = register, tags = [accounts],
desc = "Register a user", desc = "Register a user",
module = ?MODULE, function = register, module = ?MODULE, function = register,
@ -150,7 +163,7 @@ commands() ->
#ejabberd_commands{name = set_master, tags = [mnesia], #ejabberd_commands{name = set_master, tags = [mnesia],
desc = "Set master node of the clustered Mnesia tables", 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.", "node will be set as its own master.",
module = ?MODULE, function = set_master, module = ?MODULE, function = set_master,
args = [{nodename, string}], result = {res, restuple}}, args = [{nodename, string}], result = {res, restuple}},
@ -276,6 +289,27 @@ send_service_message_all_mucs(Subject, AnnouncementText) ->
end, end,
?MYHOSTS). ?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 %%% Account management
%%% %%%
@ -283,7 +317,7 @@ send_service_message_all_mucs(Subject, AnnouncementText) ->
register(User, Host, Password) -> register(User, Host, Password) ->
case ejabberd_auth:try_register(User, Host, Password) of case ejabberd_auth:try_register(User, Host, Password) of
{atomic, ok} -> {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} -> {atomic, exists} ->
String = io_lib:format("User ~s@~s already registered at node ~p", String = io_lib:format("User ~s@~s already registered at node ~p",
[User, Host, node()]), [User, Host, node()]),

View File

@ -46,6 +46,7 @@ start(normal, _Args) ->
db_init(), db_init(),
sha:start(), sha:start(),
stringprep_sup:start_link(), stringprep_sup:start_link(),
xml:start(),
start(), start(),
translate:start(), translate:start(),
acl:start(), acl:start(),
@ -56,6 +57,8 @@ start(normal, _Args) ->
ejabberd_config:start(), ejabberd_config:start(),
ejabberd_check:config(), ejabberd_check:config(),
connect_nodes(), connect_nodes(),
%% Loading ASN.1 driver explicitly to avoid races in LDAP
catch asn1rt:load_driver(),
Sup = ejabberd_sup:start_link(), Sup = ejabberd_sup:start_link(),
ejabberd_rdbms:start(), ejabberd_rdbms:start(),
ejabberd_auth:start(), ejabberd_auth:start(),
@ -85,7 +88,7 @@ prep_stop(State) ->
stop(_State) -> stop(_State) ->
?INFO_MSG("ejabberd ~s is stopped in the node ~p", [?VERSION, node()]), ?INFO_MSG("ejabberd ~s is stopped in the node ~p", [?VERSION, node()]),
delete_pid_file(), delete_pid_file(),
ejabberd_debug:stop(), %%ejabberd_debug:stop(),
ok. ok.

View File

@ -251,7 +251,16 @@ get_password_with_authmodule(User, Server) ->
is_user_exists(User, Server) -> is_user_exists(User, Server) ->
lists:any( lists:any(
fun(M) -> 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)). end, auth_modules(Server)).
%% Check if the user exists in all authentications module except the module %% Check if the user exists in all authentications module except the module

View File

@ -35,6 +35,9 @@
try_register/3, try_register/3,
dirty_get_registered_users/0, dirty_get_registered_users/0,
get_vh_registered_users/1, 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/2,
get_password_s/2, get_password_s/2,
is_user_exists/2, is_user_exists/2,
@ -43,45 +46,87 @@
plain_password_required/0 plain_password_required/0
]). ]).
-include("ejabberd.hrl").
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% API %%% API
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
start(Host) -> start(Host) ->
extauth:start( extauth:start(
Host, ejabberd_config:get_local_option({extauth_program, Host})), 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() -> plain_password_required() ->
true. true.
check_password(User, Server, Password) -> 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, _Digest, _DigestGen) ->
check_password(User, Server, Password). check_password(User, Server, Password).
set_password(User, Server, Password) -> set_password(User, Server, Password) ->
case extauth:set_password(User, Server, Password) of case extauth:set_password(User, Server, Password) of
true -> ok; true -> set_password_internal(User, Server, Password),
ok;
_ -> {error, unknown_problem} _ -> {error, unknown_problem}
end. end.
try_register(_User, _Server, _Password) -> try_register(User, Server, Password) ->
{error, not_allowed}. 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() -> 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) -> get_vh_registered_users(Server, Data) ->
false. 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} %% @spec (User, Server) -> true | false | {error, Error}
is_user_exists(User, Server) -> is_user_exists(User, Server) ->
@ -91,9 +136,169 @@ is_user_exists(User, Server) ->
_:Error -> {error, Error} _:Error -> {error, Error}
end. end.
remove_user(_User, _Server) -> remove_user(User, Server) ->
{error, not_allowed}. 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) -> remove_user(User, Server, Password) ->
not_allowed. 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.

View File

@ -66,7 +66,7 @@
servers, servers,
backups, backups,
port, port,
encrypt, tls_options,
dn, dn,
password, password,
base, base,
@ -124,14 +124,14 @@ init(Host) ->
State#state.port, State#state.port,
State#state.dn, State#state.dn,
State#state.password, State#state.password,
State#state.encrypt), State#state.tls_options),
eldap_pool:start_link(State#state.bind_eldap_id, eldap_pool:start_link(State#state.bind_eldap_id,
State#state.servers, State#state.servers,
State#state.backups, State#state.backups,
State#state.port, State#state.port,
State#state.dn, State#state.dn,
State#state.password, State#state.password,
State#state.encrypt), State#state.tls_options),
{ok, State}. {ok, State}.
plain_password_required() -> plain_password_required() ->
@ -153,8 +153,14 @@ check_password(User, Server, Password) ->
check_password(User, Server, Password, _Digest, _DigestGen) -> check_password(User, Server, Password, _Digest, _DigestGen) ->
check_password(User, Server, Password). check_password(User, Server, Password).
set_password(_User, _Server, _Password) -> set_password(User, Server, Password) ->
{error, not_allowed}. {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} %% @spec (User, Server, Password) -> {error, not_allowed}
try_register(_User, _Server, _Password) -> try_register(_User, _Server, _Password) ->
@ -367,6 +373,7 @@ parse_options(Host) ->
Backups -> Backups Backups -> Backups
end, end,
LDAPEncrypt = ejabberd_config:get_local_option({ldap_encrypt, Host}), 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 LDAPPort = case ejabberd_config:get_local_option({ldap_port, Host}) of
undefined -> case LDAPEncrypt of undefined -> case LDAPEncrypt of
tls -> ?LDAPS_PORT; tls -> ?LDAPS_PORT;
@ -411,7 +418,8 @@ parse_options(Host) ->
servers = LDAPServers, servers = LDAPServers,
backups = LDAPBackups, backups = LDAPBackups,
port = LDAPPort, port = LDAPPort,
encrypt = LDAPEncrypt, tls_options = [{encrypt, LDAPEncrypt},
{tls_verify, LDAPTLSVerify}],
dn = RootDN, dn = RootDN,
password = Password, password = Password,
base = LDAPBase, base = LDAPBase,

View File

@ -60,7 +60,11 @@ check_password(User, Server, Password, _Digest, _DigestGen) ->
check_password(User, Host, Password) -> check_password(User, Host, Password) ->
Service = get_pam_service(Host), 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; true -> true;
_ -> false _ -> false
end. 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 %% TODO: Improve this function to return an error instead of 'false' when connection to PAM failed
is_user_exists(User, Host) -> is_user_exists(User, Host) ->
Service = get_pam_service(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; true -> true;
_ -> false _ -> false
end. end.
@ -106,3 +114,8 @@ get_pam_service(Host) ->
undefined -> "ejabberd"; undefined -> "ejabberd";
Service -> Service Service -> Service
end. end.
get_pam_userinfotype(Host) ->
case ejabberd_config:get_local_option({pam_userinfotype, Host}) of
undefined -> username;
Type -> Type
end.

View File

@ -55,7 +55,9 @@
handle_sync_event/4, handle_sync_event/4,
code_change/4, code_change/4,
handle_info/3, handle_info/3,
terminate/3]). terminate/3,
print_state/1
]).
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-include("jlib.hrl"). -include("jlib.hrl").
@ -362,7 +364,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
ejabberd_hooks:run_fold( ejabberd_hooks:run_fold(
c2s_stream_features, c2s_stream_features,
Server, Server,
[], [])}), [], [Server])}),
fsm_next_state(wait_for_feature_request, fsm_next_state(wait_for_feature_request,
StateData#state{ StateData#state{
server = Server, server = Server,
@ -371,13 +373,22 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
_ -> _ ->
case StateData#state.resource of case StateData#state.resource of
"" -> "" ->
RosterVersioningFeature = ejabberd_hooks:run_fold(roster_get_versioning_feature, Server, [], [Server]), RosterVersioningFeature =
StreamFeatures = [{xmlelement, "push", ejabberd_hooks:run_fold(
roster_get_versioning_feature,
Server, [], [Server]),
StreamFeatures =
[{xmlelement, "push",
[{"xmlns", ?NS_P1_PUSH}], []}, [{"xmlns", ?NS_P1_PUSH}], []},
{xmlelement, "bind", {xmlelement, "bind",
[{"xmlns", ?NS_BIND}], []}, [{"xmlns", ?NS_BIND}], []},
{xmlelement, "session", {xmlelement, "session",
[{"xmlns", ?NS_SESSION}], []} | RosterVersioningFeature], [{"xmlns", ?NS_SESSION}], []}]
++ RosterVersioningFeature
++ ejabberd_hooks:run_fold(
c2s_stream_features,
Server,
[], [Server]),
send_element( send_element(
StateData, StateData,
{xmlelement, "stream:features", [], {xmlelement, "stream:features", [],
@ -687,7 +698,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
Socket = StateData#state.socket, Socket = StateData#state.socket,
TLSSocket = (StateData#state.sockmod):starttls( TLSSocket = (StateData#state.sockmod):starttls(
Socket, TLSOpts, Socket, TLSOpts,
xml:element_to_string( xml:element_to_binary(
{xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []})), {xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []})),
fsm_next_state(wait_for_stream, fsm_next_state(wait_for_stream,
StateData#state{socket = TLSSocket, StateData#state{socket = TLSSocket,
@ -710,7 +721,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
Socket = StateData#state.socket, Socket = StateData#state.socket,
ZlibSocket = (StateData#state.sockmod):compress( ZlibSocket = (StateData#state.sockmod):compress(
Socket, Socket,
xml:element_to_string( xml:element_to_binary(
{xmlelement, "compressed", {xmlelement, "compressed",
[{"xmlns", ?NS_COMPRESS}], []})), [{"xmlns", ?NS_COMPRESS}], []})),
fsm_next_state(wait_for_stream, fsm_next_state(wait_for_stream,
@ -1514,10 +1525,42 @@ handle_info(system_shutdown, StateName, StateData) ->
ok ok
end, end,
{stop, normal, StateData}; {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) -> handle_info(Info, StateName, StateData) ->
?ERROR_MSG("Unexpected info: ~p", [Info]), ?ERROR_MSG("Unexpected info: ~p", [Info]),
fsm_next_state(StateName, StateData). 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 %% Func: terminate/3
%% Purpose: Shutdown the fsm %% Purpose: Shutdown the fsm
@ -1631,14 +1674,14 @@ change_shaper(StateData, JID) ->
(StateData#state.sockmod):change_shaper(StateData#state.socket, Shaper). (StateData#state.sockmod):change_shaper(StateData#state.socket, Shaper).
send_text(StateData, Text) -> 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). (StateData#state.sockmod):send(StateData#state.socket, Text).
send_element(StateData, El) when StateData#state.xml_socket -> send_element(StateData, El) when StateData#state.xml_socket ->
(StateData#state.sockmod):send_xml(StateData#state.socket, (StateData#state.sockmod):send_xml(StateData#state.socket,
{xmlstreamelement, El}); {xmlstreamelement, El});
send_element(StateData, 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) send_header(StateData, Server, Version, Lang)
when StateData#state.xml_socket -> when StateData#state.xml_socket ->

View File

@ -328,7 +328,7 @@ get_prog_name() ->
FileName when is_list(FileName) -> FileName when is_list(FileName) ->
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.", []), "module wants to use the CAPTCHA feature.", []),
throw({error, option_not_configured_captcha_cmd}) throw({error, option_not_configured_captcha_cmd})
end. end.

View File

@ -201,8 +201,7 @@ process(Args) ->
case String of case String of
[] -> ok; [] -> ok;
_ -> _ ->
io:format(String), io:format("~s~n", [String])
io:format("\n")
end, end,
Code. Code.

View File

@ -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.

View File

@ -31,6 +31,7 @@
%% API %% API
-export([route/3, -export([route/3,
route_error/4,
register_route/1, register_route/1,
register_route/2, register_route/2,
register_routes/1, register_routes/1,
@ -72,6 +73,17 @@ route(From, To, Packet) ->
ok ok
end. 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) ->
register_route(Domain, undefined). register_route(Domain, undefined).

View File

@ -48,6 +48,11 @@
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-include("jlib.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). -ifdef(SSL39).
-include_lib("ssl/include/ssl_pkix.hrl"). -include_lib("ssl/include/ssl_pkix.hrl").
-define(PKIXEXPLICIT, 'OTP-PKIX'). -define(PKIXEXPLICIT, 'OTP-PKIX').
@ -58,6 +63,7 @@
-define(PKIXEXPLICIT, 'PKIX1Explicit88'). -define(PKIXEXPLICIT, 'PKIX1Explicit88').
-define(PKIXIMPLICIT, 'PKIX1Implicit88'). -define(PKIXIMPLICIT, 'PKIX1Implicit88').
-endif. -endif.
-endif.
-include("XmppAddr.hrl"). -include("XmppAddr.hrl").
-define(DICT, dict). -define(DICT, dict).
@ -177,8 +183,9 @@ init([{SockMod, Socket}, Opts]) ->
wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) -> wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
case {xml:get_attr_s("xmlns", Attrs), case {xml:get_attr_s("xmlns", Attrs),
xml:get_attr_s("xmlns:db", Attrs), xml:get_attr_s("xmlns:db", Attrs),
xml:get_attr_s("to", Attrs),
xml:get_attr_s("version", Attrs) == "1.0"} of 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) -> StateData#state.tls and (not StateData#state.authenticated) ->
send_text(StateData, ?STREAM_HEADER(" version='1.0'")), send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
SASL = SASL =
@ -212,15 +219,23 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
end, end,
send_element(StateData, send_element(StateData,
{xmlelement, "stream:features", [], {xmlelement, "stream:features", [],
SASL ++ StartTLS}), SASL ++ StartTLS ++
ejabberd_hooks:run_fold(
s2s_stream_features,
Server,
[], [Server])}),
{next_state, wait_for_feature_request, StateData}; {next_state, wait_for_feature_request, StateData};
{"jabber:server", _, true} when {"jabber:server", _, Server, true} when
StateData#state.authenticated -> StateData#state.authenticated ->
send_text(StateData, ?STREAM_HEADER(" version='1.0'")), send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
send_element(StateData, send_element(StateData,
{xmlelement, "stream:features", [], []}), {xmlelement, "stream:features", [],
ejabberd_hooks:run_fold(
s2s_stream_features,
Server,
[], [Server])}),
{next_state, stream_established, StateData}; {next_state, stream_established, StateData};
{"jabber:server", "jabber:server:dialback", _} -> {"jabber:server", "jabber:server:dialback", _Server, _} ->
send_text(StateData, ?STREAM_HEADER("")), send_text(StateData, ?STREAM_HEADER("")),
{next_state, stream_established, StateData}; {next_state, stream_established, StateData};
_ -> _ ->
@ -254,7 +269,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
TLSOpts = StateData#state.tls_options, TLSOpts = StateData#state.tls_options,
TLSSocket = (StateData#state.sockmod):starttls( TLSSocket = (StateData#state.sockmod):starttls(
Socket, TLSOpts, Socket, TLSOpts,
xml:element_to_string( xml:element_to_binary(
{xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []})), {xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []})),
{next_state, wait_for_stream, {next_state, wait_for_stream,
StateData#state{socket = TLSSocket, StateData#state{socket = TLSSocket,
@ -609,7 +624,7 @@ send_text(StateData, Text) ->
(StateData#state.sockmod):send(StateData#state.socket, Text). (StateData#state.sockmod):send(StateData#state.socket, Text).
send_element(StateData, El) -> 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) -> change_shaper(StateData, Host, JID) ->

View File

@ -51,6 +51,7 @@
handle_sync_event/4, handle_sync_event/4,
handle_info/3, handle_info/3,
terminate/3, terminate/3,
print_state/1,
code_change/4, code_change/4,
test_get_addr_port/1, test_get_addr_port/1,
get_addr_port/1]). get_addr_port/1]).
@ -191,7 +192,8 @@ init([From, Server, Type]) ->
open_socket(init, StateData) -> open_socket(init, StateData) ->
log_s2s_out(StateData#state.new, log_s2s_out(StateData#state.new,
StateData#state.myname, StateData#state.myname,
StateData#state.server), StateData#state.server,
StateData#state.tls),
?DEBUG("open_socket: ~p", [{StateData#state.myname, ?DEBUG("open_socket: ~p", [{StateData#state.myname,
StateData#state.server, StateData#state.server,
StateData#state.new, StateData#state.new,
@ -339,8 +341,8 @@ wait_for_validation({xmlstreamelement, El}, StateData) ->
case Type of case Type of
"valid" -> "valid" ->
send_queue(StateData, StateData#state.queue), send_queue(StateData, StateData#state.queue),
?INFO_MSG("Connection established: ~s -> ~s", ?INFO_MSG("Connection established: ~s -> ~s with TLS=~p",
[StateData#state.myname, StateData#state.server]), [StateData#state.myname, StateData#state.server, StateData#state.tls_enabled]),
ejabberd_hooks:run(s2s_connect_hook, ejabberd_hooks:run(s2s_connect_hook,
[StateData#state.myname, [StateData#state.myname,
StateData#state.server]), StateData#state.server]),
@ -875,6 +877,14 @@ terminate(Reason, StateName, StateData) ->
end, end,
ok. 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 %%% Internal functions
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
@ -883,7 +893,7 @@ send_text(StateData, Text) ->
ejabberd_socket:send(StateData#state.socket, Text). ejabberd_socket:send(StateData#state.socket, Text).
send_element(StateData, El) -> send_element(StateData, El) ->
send_text(StateData, xml:element_to_string(El)). send_text(StateData, xml:element_to_binary(El)).
send_queue(StateData, Q) -> send_queue(StateData, Q) ->
case queue:out(Q) of case queue:out(Q) of
@ -1142,10 +1152,10 @@ outgoing_s2s_timeout() ->
%% Human readable S2S logging: Log only new outgoing connections as INFO %% Human readable S2S logging: Log only new outgoing connections as INFO
%% Do not log dialback %% Do not log dialback
log_s2s_out(false, _, _) -> ok; log_s2s_out(false, _, _, _) -> ok;
%% Log new outgoing connections: %% Log new outgoing connections:
log_s2s_out(_, Myname, Server) -> log_s2s_out(_, Myname, Server, Tls) ->
?INFO_MSG("Trying to open s2s connection: ~s -> ~s",[Myname, Server]). ?INFO_MSG("Trying to open s2s connection: ~s -> ~s with TLS=~p", [Myname, Server, Tls]).
%% Calculate timeout depending on which state we are in: %% Calculate timeout depending on which state we are in:
%% Can return integer > 0 | infinity %% Can return integer > 0 | infinity

View File

@ -47,7 +47,8 @@
handle_sync_event/4, handle_sync_event/4,
code_change/4, code_change/4,
handle_info/3, handle_info/3,
terminate/3]). terminate/3,
print_state/1]).
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-include("jlib.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), Attrs2 = jlib:replace_from_to_attrs(jlib:jid_to_string(From),
jlib:jid_to_string(To), jlib:jid_to_string(To),
Attrs), Attrs),
Text = xml:element_to_string({xmlelement, Name, Attrs2, Els}), Text = xml:element_to_binary({xmlelement, Name, Attrs2, Els}),
send_text(StateData, Text); send_text(StateData, Text);
deny -> deny ->
Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED), Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED),
ejabberd_router:route(To, From, Err) ejabberd_router:route_error(To, From, Err, Packet)
end, end,
{next_state, StateName, StateData}. {next_state, StateName, StateData}.
@ -374,6 +375,14 @@ terminate(Reason, StateName, StateData) ->
(StateData#state.sockmod):close(StateData#state.socket), (StateData#state.sockmod):close(StateData#state.socket),
ok. 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 %%% Internal functions
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
@ -382,7 +391,7 @@ send_text(StateData, Text) ->
(StateData#state.sockmod):send(StateData#state.socket, Text). (StateData#state.sockmod):send(StateData#state.socket, Text).
send_element(StateData, El) -> send_element(StateData, El) ->
send_text(StateData, xml:element_to_string(El)). send_text(StateData, xml:element_to_binary(El)).
new_id() -> new_id() ->
randoms:get_string(). randoms:get_string().

View File

@ -47,6 +47,7 @@
register_iq_handler/4, register_iq_handler/4,
register_iq_handler/5, register_iq_handler/5,
unregister_iq_handler/2, unregister_iq_handler/2,
force_update_presence/1,
connected_users/0, connected_users/0,
connected_users_number/0, connected_users_number/0,
user_resources/2, user_resources/2,
@ -571,10 +572,15 @@ route_message(From, To, Packet) ->
bounce_offline_message(From, To, Packet); bounce_offline_message(From, To, Packet);
_ -> _ ->
case ejabberd_auth:is_user_exists(LUser, LServer) of case ejabberd_auth:is_user_exists(LUser, LServer) of
true ->
case is_privacy_allow(From, To, Packet) of
true -> true ->
ejabberd_hooks:run(offline_message_hook, ejabberd_hooks:run(offline_message_hook,
LServer, LServer,
[From, To, Packet]); [From, To, Packet]);
false ->
ok
end;
_ -> _ ->
Err = jlib:make_error_reply( Err = jlib:make_error_reply(
Packet, ?ERR_SERVICE_UNAVAILABLE), Packet, ?ERR_SERVICE_UNAVAILABLE),
@ -711,6 +717,16 @@ process_iq(From, To, Packet) ->
ok ok
end. 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 %%% ejabberd commands

View File

@ -84,7 +84,7 @@ update_info() ->
update_info(Dir, Files) -> update_info(Dir, Files) ->
Beams = lists:sort(get_beams(Files)), Beams = lists:sort(get_beams(Files)),
UpdatedBeams = get_updated_beams(Beams), 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), {Script, LowLevelScript, Check} = build_script(Dir, UpdatedBeams),
{ok, Dir, UpdatedBeams, Script, LowLevelScript, Check}. {ok, Dir, UpdatedBeams, Script, LowLevelScript, Check}.
@ -124,14 +124,21 @@ get_current_version(Module) ->
%% @spec(Dir::string(), UpdatedBeams::[atom()]) -> {Script,LowLevelScript,Check} %% @spec(Dir::string(), UpdatedBeams::[atom()]) -> {Script,LowLevelScript,Check}
build_script(Dir, UpdatedBeams) -> build_script(Dir, UpdatedBeams) ->
Script = make_script(UpdatedBeams), Script = make_script(UpdatedBeams),
?INFO_MSG("script: ~p~n", [Script]),
LowLevelScript = make_low_level_script(UpdatedBeams, Script), LowLevelScript = make_low_level_script(UpdatedBeams, Script),
?INFO_MSG("low level script: ~p~n", [LowLevelScript]),
Check = Check =
release_handler_1:check_script( release_handler_1:check_script(
LowLevelScript, LowLevelScript,
[{ejabberd, "", filename:join(Dir, "..")}]), [{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}. {Script, LowLevelScript, Check}.
%% Copied from Erlang/OTP file: lib/sasl/src/systools.hrl %% Copied from Erlang/OTP file: lib/sasl/src/systools.hrl

View File

@ -32,6 +32,15 @@ typedef struct {
z_stream *i_stream; z_stream *i_stream;
} ejabberd_zlib_data; } 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) 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)); (ejabberd_zlib_data *)driver_alloc(sizeof(ejabberd_zlib_data));
d->port = port; 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->zalloc = zlib_alloc;
d->d_stream->zfree = (free_func)0; d->d_stream->zfree = zlib_free;
d->d_stream->opaque = (voidpf)0; d->d_stream->opaque = (voidpf)0;
deflateInit(d->d_stream, Z_DEFAULT_COMPRESSION); 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->zalloc = zlib_alloc;
d->i_stream->zfree = (free_func)0; d->i_stream->zfree = zlib_free;
d->i_stream->opaque = (voidpf)0; d->i_stream->opaque = (voidpf)0;
inflateInit(d->i_stream); inflateInit(d->i_stream);
@ -65,10 +74,10 @@ static void ejabberd_zlib_drv_stop(ErlDrvData handle)
ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle; ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle;
deflateEnd(d->d_stream); deflateEnd(d->d_stream);
free(d->d_stream); driver_free(d->d_stream);
inflateEnd(d->i_stream); inflateEnd(d->i_stream);
free(d->i_stream); driver_free(d->i_stream);
driver_free((char *)handle); driver_free((char *)handle);
} }

View File

@ -82,7 +82,7 @@ else
KERNEL_OPTS="-kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}" KERNEL_OPTS="-kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}"
fi 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 # define additional environment variables
if [ "$EJABBERDDIR" = "" ]; then if [ "$EJABBERDDIR" = "" ]; then
@ -141,6 +141,7 @@ start ()
-noinput -detached \ -noinput -detached \
-pa $EJABBERD_EBIN_PATH \ -pa $EJABBERD_EBIN_PATH \
-mnesia dir \"\\\"$SPOOLDIR\\\"\" \ -mnesia dir \"\\\"$SPOOLDIR\\\"\" \
$KERNEL_OPTS \
-s ejabberd \ -s ejabberd \
-sasl sasl_error_logger \\{file,\\\"$SASL_LOG_PATH\\\"\\} \ -sasl sasl_error_logger \\{file,\\\"$SASL_LOG_PATH\\\"\\} \
$ERLANG_OPTS $ARGS \"$@\"" $ERLANG_OPTS $ARGS \"$@\""
@ -174,6 +175,7 @@ debug ()
$NAME debug-${TTY}-${ERLANG_NODE} \ $NAME debug-${TTY}-${ERLANG_NODE} \
-remsh $ERLANG_NODE \ -remsh $ERLANG_NODE \
-hidden \ -hidden \
$KERNEL_OPTS \
$ERLANG_OPTS $ARGS \"$@\"" $ERLANG_OPTS $ARGS \"$@\""
} }
@ -203,6 +205,7 @@ live ()
$NAME $ERLANG_NODE \ $NAME $ERLANG_NODE \
-pa $EJABBERD_EBIN_PATH \ -pa $EJABBERD_EBIN_PATH \
-mnesia dir \"\\\"$SPOOLDIR\\\"\" \ -mnesia dir \"\\\"$SPOOLDIR\\\"\" \
$KERNEL_OPTS \
-s ejabberd \ -s ejabberd \
$ERLANG_OPTS $ARGS \"$@\"" $ERLANG_OPTS $ARGS \"$@\""
} }
@ -236,7 +239,7 @@ ctl ()
MAXCONNID=100 MAXCONNID=100
CONNLOCKDIR=@LOCALSTATEDIR@/lock/ejabberdctl CONNLOCKDIR=@LOCALSTATEDIR@/lock/ejabberdctl
FLOCK='/usr/bin/flock' FLOCK='/usr/bin/flock'
if [ ! -x "$FLOCK" ] ; then if [ ! -x "$FLOCK" ] || [ ! -d "$CONNLOCKDIR" ] ; then
JOT='/usr/bin/jot' JOT='/usr/bin/jot'
if [ ! -x "$JOT" ] ; then if [ ! -x "$JOT" ] ; then
# no flock or jot, simply invoke ctlexec() # no flock or jot, simply invoke ctlexec()
@ -310,6 +313,7 @@ ctlexec ()
-noinput \ -noinput \
-hidden \ -hidden \
-pa $EJABBERD_EBIN_PATH \ -pa $EJABBERD_EBIN_PATH \
$KERNEL_OPTS \
-s ejabberd_ctl -extra $ERLANG_NODE $COMMAND" -s ejabberd_ctl -extra $ERLANG_NODE $COMMAND"
} }

View File

@ -141,8 +141,7 @@ export_offline(Server, Output) ->
TimeStamp))]}, TimeStamp))]},
XML = XML =
ejabberd_odbc:escape( ejabberd_odbc:escape(
lists:flatten( xml:element_to_binary(NewPacket)),
xml:element_to_string(NewPacket))),
["insert into spool(username, xml) " ["insert into spool(username, xml) "
"values ('", Username, "', '", "values ('", Username, "', '",
XML, XML,
@ -176,7 +175,7 @@ export_vcard(Server, Output) ->
when LServer == Host -> when LServer == Host ->
Username = ejabberd_odbc:escape(LUser), Username = ejabberd_odbc:escape(LUser),
SVCARD = ejabberd_odbc:escape( SVCARD = ejabberd_odbc:escape(
lists:flatten(xml:element_to_string(VCARD))), xml:element_to_binary(VCARD)),
["delete from vcard where username='", Username, "';" ["delete from vcard where username='", Username, "';"
"insert into vcard(username, vcard) " "insert into vcard(username, vcard) "
"values ('", Username, "', '", SVCARD, "');"]; "values ('", Username, "', '", SVCARD, "');"];
@ -260,7 +259,7 @@ export_private_storage(Server, Output) ->
Username = ejabberd_odbc:escape(LUser), Username = ejabberd_odbc:escape(LUser),
LXMLNS = ejabberd_odbc:escape(XMLNS), LXMLNS = ejabberd_odbc:escape(XMLNS),
SData = ejabberd_odbc:escape( 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); odbc_queries:set_private_data_sql(Username, LXMLNS, SData);
(_Host, _R) -> (_Host, _R) ->
[] []

View File

@ -286,6 +286,16 @@ ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
responseName [10] LDAPOID OPTIONAL, responseName [10] LDAPOID OPTIONAL,
response [11] OCTET STRING 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 END

View File

@ -1,4 +1,4 @@
# $Id$ # $Id: Makefile.in 2842 2009-12-29 19:10:52Z badlop $
CC = @CC@ CC = @CC@
CFLAGS = @CFLAGS@ CFLAGS = @CFLAGS@
@ -20,18 +20,23 @@ ifdef debug
endif endif
OUTDIR = .. OUTDIR = ..
SOURCES = $(wildcard *.erl) ELDAPv3.erl SOURCES = $(wildcard *.erl) ELDAPv3.erl eldap_filter_yecc.erl
BEAMS = $(addprefix $(OUTDIR)/,$(SOURCES:.erl=.beam)) BEAMS = $(addprefix $(OUTDIR)/,$(SOURCES:.erl=.beam))
all: $(BEAMS) ELDAPv3.beam all: $(BEAMS) ELDAPv3.beam eldap_filter_yecc.beam
ELDAPv3.beam: ELDAPv3.erl ELDAPv3.beam: ELDAPv3.erl
ELDAPv3.erl: ELDAPv3.asn ELDAPv3.erl: ELDAPv3.asn
@ERLC@ $(ASN_FLAGS) -W $(EFLAGS) $< @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) $< @ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
clean: clean:
@ -39,6 +44,8 @@ clean:
rm -f ELDAPv3.erl rm -f ELDAPv3.erl
rm -f ELDAPv3.hrl rm -f ELDAPv3.hrl
rm -f ELDAPv3.beam rm -f ELDAPv3.beam
rm -f eldap_filter_yecc.erl
rm -f eldap_filter_yecc.beam
rm -f $(BEAMS) rm -f $(BEAMS)
distclean: clean distclean: clean

View File

@ -4,7 +4,7 @@ include ..\Makefile.inc
EFLAGS = -I .. -pz .. EFLAGS = -I .. -pz ..
OUTDIR = .. 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 ASN_FLAGS = -bber_bin +optimize +driver
@ -15,11 +15,16 @@ Clean :
-@erase ELDAPv3.erl -@erase ELDAPv3.erl
-@erase ELDAPv3.hrl -@erase ELDAPv3.hrl
-@erase ELDAPv3.beam -@erase ELDAPv3.beam
-@erase eldap_filter_yecc.erl
-@erase eldap_filter_yecc.beam
-@erase $(BEAMS) -@erase $(BEAMS)
ELDAPv3.erl : ELDAPv3.asn ELDAPv3.erl : ELDAPv3.asn
erlc $(ASN_FLAGS) -W $(EFLAGS) 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 $(OUTDIR)\eldap.beam : eldap.erl ELDAPv3.erl
erlc -W $(EFLAGS) -o $(OUTDIR) eldap.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 $(OUTDIR)\eldap_pool.beam : eldap_pool.erl
erlc -W $(EFLAGS) -o $(OUTDIR) 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

View File

@ -33,8 +33,11 @@
%%% Modified by Alexey Shchepin <alexey@sevcom.net> %%% 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 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> %%% Modified by Christophe Romain <christophe.romain@process-one.net>
%%% Improve error case handling %%% Improve error case handling
@ -71,9 +74,9 @@
-export([baseObject/0,singleLevel/0,wholeSubtree/0,close/1, -export([baseObject/0,singleLevel/0,wholeSubtree/0,close/1,
equalityMatch/2,greaterOrEqual/2,lessOrEqual/2, 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, '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]). -export([get_status/1]).
%% gen_fsm callbacks %% gen_fsm callbacks
@ -127,9 +130,10 @@ start_link(Name) ->
Reg_name = list_to_atom("eldap_" ++ Name), Reg_name = list_to_atom("eldap_" ++ Name),
gen_fsm:start_link({local, Reg_name}, ?MODULE, [], []). 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), 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. %%% Get status of connection.
@ -239,6 +243,10 @@ modify_dn(Handle, Entry, NewRDN, DelOldRDN, NewSup)
{modify_dn, Entry, NewRDN, bool_p(DelOldRDN), optional(NewSup)}, {modify_dn, Entry, NewRDN, bool_p(DelOldRDN), optional(NewSup)},
?CALL_TIMEOUT). ?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. %%% Bind.
@ -374,6 +382,29 @@ substrings(Type, SubStr) when is_list(Type), is_list(SubStr) ->
{substrings,#'SubstringFilter'{type = Type, {substrings,#'SubstringFilter'{type = Type,
substrings = Ss}}. 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(Pid) when is_pid(Pid) -> Pid;
get_handle(Atom) when is_atom(Atom) -> Atom; 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([]) -> init([]) ->
case get_config() of case get_config() of
{ok, Hosts, Rootdn, Passwd, Encrypt} -> {ok, Hosts, Rootdn, Passwd, Opts} ->
init({Hosts, Rootdn, Passwd, Encrypt}); init({Hosts, Rootdn, Passwd, Opts});
{error, Reason} -> {error, Reason} ->
{stop, Reason} {stop, Reason}
end; end;
init({Hosts, Port, Rootdn, Passwd, Encrypt}) -> init({Hosts, Port, Rootdn, Passwd, Opts}) ->
catch ssl:start(), catch ssl:start(),
{X1,X2,X3} = erlang:now(), ssl:seed(randoms:get_string()),
ssl:seed(integer_to_list(X1) ++ integer_to_list(X2) ++ integer_to_list(X3)), Encrypt = case proplists:get_value(encrypt, Opts) of
tls -> tls;
_ -> none
end,
PortTemp = case Port of PortTemp = case Port of
undefined -> undefined ->
case Encrypt of case Encrypt of
@ -414,7 +448,14 @@ init({Hosts, Port, Rootdn, Passwd, Encrypt}) ->
end; end;
PT -> PT PT -> PT
end, 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, {ok, connecting, #eldap{hosts = Hosts,
port = PortTemp, port = PortTemp,
rootdn = Rootdn, rootdn = Rootdn,
@ -671,6 +712,16 @@ gen_req({modify_dn, Entry, NewRDN, DelOldRDN, NewSup}) ->
deleteoldrdn = DelOldRDN, deleteoldrdn = DelOldRDN,
newSuperior = NewSup}}; 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}) -> gen_req({bind, RootDN, Passwd}) ->
{bindRequest, {bindRequest,
#'BindRequest'{version = ?LDAP_VERSION, #'BindRequest'{version = ?LDAP_VERSION,
@ -745,6 +796,11 @@ recvd_packet(Pkt, S) ->
cancel_timer(Timer), cancel_timer(Timer),
Reply = check_bind_reply(Result, From), Reply = check_bind_reply(Result, From),
{reply, Reply, From, S#eldap{dict = New_dict}}; {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} -> {OtherName, OtherResult} ->
New_dict = dict:erase(Id, Dict), New_dict = dict:erase(Id, Dict),
cancel_timer(Timer), cancel_timer(Timer),
@ -769,6 +825,15 @@ check_bind_reply(#'BindResponse'{resultCode = Reason}, _From) ->
check_bind_reply(Other, _From) -> check_bind_reply(Other, _From) ->
{error, Other}. {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) -> get_op_rec(Id, Dict) ->
case dict:find(Id, Dict) of case dict:find(Id, Dict) of
{ok, [{Timer, _Command, From, Name}|Res]} -> {ok, [{Timer, _Command, From, Name}|Res]} ->
@ -904,7 +969,7 @@ connect_bind(S) ->
tls -> tls ->
SockMod = ssl, SockMod = ssl,
SslOpts = [{packet, asn1}, {active, true}, {keepalive, true}, SslOpts = [{packet, asn1}, {active, true}, {keepalive, true},
binary], binary | S#eldap.tls_options],
ssl:connect(Host, S#eldap.port, SslOpts); ssl:connect(Host, S#eldap.port, SslOpts);
%% starttls -> %% TODO: Implement STARTTLS; %% starttls -> %% TODO: Implement STARTTLS;
_ -> _ ->
@ -973,6 +1038,8 @@ v_filter({lessOrEqual,AV}) -> {lessOrEqual,AV};
v_filter({approxMatch,AV}) -> {approxMatch,AV}; v_filter({approxMatch,AV}) -> {approxMatch,AV};
v_filter({present,A}) -> {present,A}; v_filter({present,A}) -> {present,A};
v_filter({substrings,S}) when is_record(S,'SubstringFilter') -> {substrings,S}; 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_filter(_Filter) -> throw({error,concat(["unknown filter: ",_Filter])}).
v_modifications(Mods) -> v_modifications(Mods) ->
@ -1018,8 +1085,8 @@ get_config() ->
case file:consult(File) of case file:consult(File) of
{ok, Entries} -> {ok, Entries} ->
case catch parse(Entries) of case catch parse(Entries) of
{ok, Hosts, Port, Rootdn, Passwd, Encrypt} -> {ok, Hosts, Port, Rootdn, Passwd, Opts} ->
{ok, Hosts, Port, Rootdn, Passwd, Encrypt}; {ok, Hosts, Port, Rootdn, Passwd, Opts};
{error, Reason} -> {error, Reason} ->
{error, Reason}; {error, Reason};
{'EXIT', Reason} -> {'EXIT', Reason} ->
@ -1035,7 +1102,7 @@ parse(Entries) ->
get_integer(port, Entries), get_integer(port, Entries),
get_list(rootdn, Entries), get_list(rootdn, Entries),
get_list(passwd, Entries), get_list(passwd, Entries),
get_atom(encrypt, Entries)}. get_list(options, Entries)}.
get_integer(Key, List) -> get_integer(Key, List) ->
case lists:keysearch(Key, 1, List) of 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)}) throw({error, "No Entry in Config for " ++ atom_to_list(Key)})
end. end.
get_atom(Key, List) -> %% get_atom(Key, List) ->
case lists:keysearch(Key, 1, List) of %% case lists:keysearch(Key, 1, List) of
{value, {Key, Value}} when is_atom(Value) -> %% {value, {Key, Value}} when is_atom(Value) ->
Value; %% Value;
{value, {Key, _Value}} -> %% {value, {Key, _Value}} ->
throw({error, "Bad Value in Config for " ++ atom_to_list(Key)}); %% throw({error, "Bad Value in Config for " ++ atom_to_list(Key)});
false -> %% false ->
throw({error, "No Entry in Config for " ++ atom_to_list(Key)}) %% throw({error, "No Entry in Config for " ++ atom_to_list(Key)})
end. %% end.
get_hosts(Key, List) -> get_hosts(Key, List) ->
lists:map(fun({Key1, {A,B,C,D}}) when is_integer(A), lists:map(fun({Key1, {A,B,C,D}}) when is_integer(A),

View File

@ -3,7 +3,7 @@
%%% Purpose: Converts String Representation of %%% Purpose: Converts String Representation of
%%% LDAP Search Filter (RFC 2254) %%% LDAP Search Filter (RFC 2254)
%%% to eldap's representation of filter %%% 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 %%% ejabberd, Copyright (C) 2002-2010 ProcessOne
@ -24,19 +24,17 @@
%%% 02111-1307 USA %%% 02111-1307 USA
%%% %%%
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
-module(eldap_filter). -module(eldap_filter).
%%%====================== %% TODO: remove this when new regexp module will be used
%%% Export functions -compile({nowarn_deprecated_function, {regexp, sub, 3}}).
%%%======================
-export([parse/1, -export([parse/1, parse/2, do_sub/2]).
parse/2,
do_sub/2
]).
%%%------------------------------------------------------------------------- %%====================================================================
%% API
%%====================================================================
%%%-------------------------------------------------------------------
%%% Arity: parse/1 %%% Arity: parse/1
%%% Function: parse(RFC2254_Filter) -> {ok, EldapFilter} | %%% Function: parse(RFC2254_Filter) -> {ok, EldapFilter} |
%%% {error, bad_filter} %%% {error, bad_filter}
@ -51,11 +49,11 @@
%%% %%%
%%% {ok,{'and',[{'not',{lessOrEqual,{'AttributeValueAssertion',"uid","100"}}}, %%% {ok,{'and',[{'not',{lessOrEqual,{'AttributeValueAssertion',"uid","100"}}},
%%% {present,"mail"}]}} %%% {present,"mail"}]}}
%%%------------------------------------------------------------------------- %%%-------------------------------------------------------------------
parse(RFC2254_Filter) -> parse(L) when is_list(L) ->
parse(RFC2254_Filter, []). parse(L, []).
%%%------------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% Arity: parse/2 %%% Arity: parse/2
%%% Function: parse(RFC2254_Filter, [SubstValue |...]) -> %%% Function: parse(RFC2254_Filter, [SubstValue |...]) ->
%%% {ok, EldapFilter} | %%% {ok, EldapFilter} |
@ -81,135 +79,53 @@ parse(RFC2254_Filter) ->
%%% {equalityMatch,{'AttributeValueAssertion', %%% {equalityMatch,{'AttributeValueAssertion',
%%% "jid", %%% "jid",
%%% "xramtsov@gmail.com"}}]}} %%% "xramtsov@gmail.com"}}]}}
%%%-------------------------------------------------------------------------- %%%-------------------------------------------------------------------
parse(RFC2254_Filter, ListOfSubValues) -> parse(L, SList) when is_list(L), is_list(SList) ->
case catch convert_filter(parse_filter(RFC2254_Filter), ListOfSubValues) of case catch eldap_filter_yecc:parse(scan(L, SList)) of
[EldapFilter] when is_tuple(EldapFilter) -> {error, {_, _, Msg}} ->
{ok, EldapFilter}; {error, Msg};
{regexp, Error} -> {ok, Result} ->
{error, Error}; {ok, Result};
_ -> {regexp, Err} ->
{error, bad_filter} {error, Err}
end. end.
%%%========================== %%====================================================================
%%% Internal functions %% Internal functions
%%%========================== %%====================================================================
-define(do_scan(L), scan(Rest, [], [{L, 1} | check(Buf, S) ++ Result], L, S)).
%%%---------------------- scan(L, SList) ->
%%% split/1,4 scan(L, "", [], undefined, SList).
%%%----------------------
split(Filter) ->
split(Filter, 0, [], []).
split([], _, _, Result) -> scan("=*)" ++ Rest, Buf, Result, '(', S) ->
Result; 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) -> check([], _) ->
NewNum = case H of [];
$( -> Num + 1; check(Buf, S) ->
$) -> Num - 1; [{str, 1, do_sub(lists:reverse(Buf), S)}].
_ -> 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
%%%--------------------
-define(MAX_RECURSION, 100). -define(MAX_RECURSION, 100).
@ -234,9 +150,9 @@ do_sub(S, {RegExp, New}, Iter) ->
{ok, NewS, _} when Iter =< ?MAX_RECURSION -> {ok, NewS, _} when Iter =< ?MAX_RECURSION ->
do_sub(NewS, {RegExp, New}, Iter+1); do_sub(NewS, {RegExp, New}, Iter+1);
{ok, _, _} when Iter > ?MAX_RECURSION -> {ok, _, _} when Iter > ?MAX_RECURSION ->
throw({regexp, max_substitute_recursion}); erlang:error(max_substitute_recursion);
_ -> _ ->
throw({regexp, bad_regexp}) erlang:error(bad_regexp)
end; end;
do_sub(S, {_, _, N}, _) when N<1 -> do_sub(S, {_, _, N}, _) when N<1 ->
@ -251,52 +167,11 @@ do_sub(S, {RegExp, New, Times}, Iter) ->
{ok, NewS, _} -> {ok, NewS, _} ->
NewS; NewS;
_ -> _ ->
throw({regexp, bad_regexp}) erlang:error(bad_regexp)
end. 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) -> replace_amps(String) ->
lists:foldl( lists:flatmap(
fun(X, Acc) -> fun($&) -> "\\&";
if (Chr) -> [Chr]
X == $& -> end, String).
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.

View 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).

View File

@ -31,7 +31,8 @@
-export([ -export([
start_link/7, start_link/7,
bind/3, bind/3,
search/2 search/2,
modify_passwd/3
]). ]).
-include("ejabberd.hrl"). -include("ejabberd.hrl").
@ -45,12 +46,17 @@ bind(PoolName, DN, Passwd) ->
search(PoolName, Opts) -> search(PoolName, Opts) ->
do_request(PoolName, {search, [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), PoolName = make_id(Name),
pg2:create(PoolName), pg2:create(PoolName),
lists:foreach(fun(Host) -> lists:foreach(
fun(Host) ->
ID = erlang:ref_to_list(make_ref()), 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} -> {ok, Pid} ->
pg2:join(PoolName, Pid); pg2:join(PoolName, Pid);
_ -> _ ->

View File

@ -41,6 +41,8 @@ typedef struct {
XML_Parser parser; XML_Parser parser;
} expat_data; } expat_data;
static XML_Memory_Handling_Suite ms = {driver_alloc, driver_realloc, driver_free};
void *erlXML_StartElementHandler(expat_data *d, void *erlXML_StartElementHandler(expat_data *d,
const XML_Char *name, const XML_Char *name,
const XML_Char **atts) 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)); expat_data* d = (expat_data*)driver_alloc(sizeof(expat_data));
d->port = port; d->port = port;
d->parser = XML_ParserCreate("UTF-8"); d->parser = XML_ParserCreate_MM("UTF-8", &ms, NULL);
XML_SetUserData(d->parser, d); XML_SetUserData(d->parser, d);
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY); set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);

View File

@ -27,8 +27,15 @@
-module(extauth). -module(extauth).
-author('leifj@it.su.se'). -author('leifj@it.su.se').
-export([start/2, stop/1, init/2, -export([start/2,
check_password/3, set_password/3, is_user_exists/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"). -include("ejabberd.hrl").
@ -36,16 +43,29 @@
-define(CALL_TIMEOUT, 10000). % Timeout is in milliseconds: 10 seconds == 10000 -define(CALL_TIMEOUT, 10000). % Timeout is in milliseconds: 10 seconds == 10000
start(Host, ExtPrg) -> 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) -> init(ProcessName, ExtPrg) ->
register(gen_mod:get_module_proc(Host, eauth), self()), register(ProcessName, self()),
process_flag(trap_exit,true), process_flag(trap_exit,true),
Port = open_port({spawn, ExtPrg}, [{packet,2}]), Port = open_port({spawn, ExtPrg}, [{packet,2}]),
loop(Port, ?INIT_TIMEOUT). loop(Port, ?INIT_TIMEOUT).
stop(Host) -> 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) -> check_password(User, Server, Password) ->
call_port(Server, ["auth", User, Server, Password]). call_port(Server, ["auth", User, Server, Password]).
@ -56,14 +76,38 @@ is_user_exists(User, Server) ->
set_password(User, Server, Password) -> set_password(User, Server, Password) ->
call_port(Server, ["setpass", 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) -> call_port(Server, Msg) ->
LServer = jlib:nameprep(Server), 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 receive
{eauth,Result} -> {eauth,Result} ->
Result Result
end. 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) -> loop(Port, Timeout) ->
receive receive
{call, Caller, Msg} -> {call, Caller, Msg} ->

View File

@ -22,6 +22,7 @@
-define(NS_DISCO_ITEMS, "http://jabber.org/protocol/disco#items"). -define(NS_DISCO_ITEMS, "http://jabber.org/protocol/disco#items").
-define(NS_DISCO_INFO, "http://jabber.org/protocol/disco#info"). -define(NS_DISCO_INFO, "http://jabber.org/protocol/disco#info").
-define(NS_VCARD, "vcard-temp"). -define(NS_VCARD, "vcard-temp").
-define(NS_VCARD_UPDATE, "vcard-temp:x:update").
-define(NS_AUTH, "jabber:iq:auth"). -define(NS_AUTH, "jabber:iq:auth").
-define(NS_AUTH_ERROR, "jabber:iq:auth:error"). -define(NS_AUTH_ERROR, "jabber:iq:auth:error").
-define(NS_REGISTER, "jabber:iq:register"). -define(NS_REGISTER, "jabber:iq:register").
@ -140,6 +141,8 @@
?STANZA_ERROR("407", "auth", "subscription-required")). ?STANZA_ERROR("407", "auth", "subscription-required")).
-define(ERR_UNEXPECTED_REQUEST, -define(ERR_UNEXPECTED_REQUEST,
?STANZA_ERROR("400", "wait", "unexpected-request")). ?STANZA_ERROR("400", "wait", "unexpected-request")).
-define(ERR_UNEXPECTED_REQUEST_CANCEL,
?STANZA_ERROR("401", "cancel", "unexpected-request")).
%-define(ERR_, %-define(ERR_,
% ?STANZA_ERROR("", "", "")). % ?STANZA_ERROR("", "", "")).

View File

@ -1,7 +1,7 @@
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% File : mod_adhoc.erl %%% File : mod_adhoc.erl
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se> %%% 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> %%% Created : 15 Nov 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%% %%%
%%% %%%

View File

@ -33,6 +33,10 @@
-behaviour(gen_mod). -behaviour(gen_mod).
-export([read_caps/1, -export([read_caps/1,
caps_stream_features/2,
disco_features/5,
disco_identity/5,
disco_info/5,
get_features/1]). get_features/1]).
%% gen_mod callbacks %% gen_mod callbacks
@ -56,7 +60,7 @@
-define(PROCNAME, ejabberd_mod_caps). -define(PROCNAME, ejabberd_mod_caps).
-record(caps, {node, version, exts}). -record(caps, {node, version, hash, exts}).
-record(caps_features, {node_pair, features = []}). -record(caps_features, {node_pair, features = []}).
-record(state, {host}). -record(state, {host}).
@ -115,8 +119,10 @@ read_caps([{xmlelement, "c", Attrs, _Els} | Tail], Result) ->
?NS_CAPS -> ?NS_CAPS ->
Node = xml:get_attr_s("node", Attrs), Node = xml:get_attr_s("node", Attrs),
Version = xml:get_attr_s("ver", Attrs), Version = xml:get_attr_s("ver", Attrs),
Hash = xml:get_attr_s("hash", Attrs),
Exts = string:tokens(xml:get_attr_s("ext", 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) read_caps(Tail, Result)
end; end;
@ -152,6 +158,41 @@ user_send_packet(#jid{luser = User, lserver = Server} = From,
user_send_packet(_From, _To, _Packet) -> user_send_packet(_From, _To, _Packet) ->
ok. 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 %% gen_server callbacks
%%==================================================================== %%====================================================================
@ -163,6 +204,16 @@ init([Host, _Opts]) ->
mnesia:add_table_copy(caps_features, node(), disc_copies), mnesia:add_table_copy(caps_features, node(), disc_copies),
ejabberd_hooks:add(user_send_packet, Host, ejabberd_hooks:add(user_send_packet, Host,
?MODULE, user_send_packet, 75), ?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}}. {ok, #state{host = Host}}.
handle_call(stop, _From, State) -> handle_call(stop, _From, State) ->
@ -180,6 +231,16 @@ terminate(_Reason, State) ->
Host = State#state.host, Host = State#state.host,
ejabberd_hooks:delete(user_send_packet, Host, ejabberd_hooks:delete(user_send_packet, Host,
?MODULE, user_send_packet, 75), ?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. ok.
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
@ -214,16 +275,21 @@ feature_request(_Host, _From, _Caps, []) ->
feature_response(#iq{type = result, feature_response(#iq{type = result,
sub_el = [{xmlelement, _, _, Els}]}, sub_el = [{xmlelement, _, _, Els}]},
Host, From, Caps, [SubNode | SubNodes]) -> Host, From, Caps, [SubNode | SubNodes]) ->
BinaryNode = node_to_binary(Caps#caps.node, SubNode),
case check_hash(Caps, Els) of
true ->
Features = lists:flatmap( Features = lists:flatmap(
fun({xmlelement, "feature", FAttrs, _}) -> fun({xmlelement, "feature", FAttrs, _}) ->
[xml:get_attr_s("var", FAttrs)]; [xml:get_attr_s("var", FAttrs)];
(_) -> (_) ->
[] []
end, Els), end, Els),
BinaryNode = node_to_binary(Caps#caps.node, SubNode),
mnesia:dirty_write( mnesia:dirty_write(
#caps_features{node_pair = BinaryNode, #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_request(Host, From, Caps, SubNodes);
feature_response(timeout, _Host, _From, _Caps, _SubNodes) -> feature_response(timeout, _Host, _From, _Caps, _SubNodes) ->
ok; ok;
@ -239,3 +305,171 @@ node_to_binary(Node, SubNode) ->
features_to_binary(L) -> [list_to_binary(I) || I <- L]. features_to_binary(L) -> [list_to_binary(I) || I <- L].
binary_to_features(L) -> [binary_to_list(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)].

View File

@ -237,7 +237,23 @@ get_local_features(Acc, From, #jid{lserver = LServer} = _To, Node, _Lang) ->
?INFO_RESULT(Allow, [?NS_COMMANDS]); ?INFO_RESULT(Allow, [?NS_COMMANDS]);
["config", _] -> ["config", _] ->
?INFO_RESULT(Allow, [?NS_COMMANDS]); ?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]); ?INFO_RESULT(Allow, [?NS_COMMANDS]);
_ -> _ ->
Acc Acc
@ -449,7 +465,23 @@ get_local_items(Acc, From, #jid{lserver = LServer} = To, Node, Lang) ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
["config", _] -> ["config", _] ->
?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN}); ?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}); ?ITEMS_RESULT(Allow, LNode, {error, ?ERR_FORBIDDEN});
_ -> _ ->
Acc Acc

View File

@ -172,7 +172,7 @@ process_local_iq_info(From, To, #iq{type = Type, lang = Lang,
[{"xmlns", ?NS_DISCO_INFO} | ANode], [{"xmlns", ?NS_DISCO_INFO} | ANode],
Identity ++ Identity ++
Info ++ Info ++
lists:map(fun feature_to_xml/1, Features) features_to_xml(Features)
}]}; }]};
{error, Error} -> {error, Error} ->
IQ#iq{type = error, sub_el = [SubEl, Error]} IQ#iq{type = error, sub_el = [SubEl, Error]}
@ -209,10 +209,16 @@ get_local_features(Acc, _From, _To, _Node, _Lang) ->
end. end.
feature_to_xml({{Feature, _Host}}) -> features_to_xml(FeatureList) ->
feature_to_xml(Feature); %% Avoid duplicating features
feature_to_xml(Feature) when is_list(Feature) -> [{xmlelement, "feature", [{"var", Feat}], []} ||
{xmlelement, "feature", [{"var", Feature}], []}. Feat <- lists:usort(
lists:map(
fun({{Feature, _Host}}) ->
Feature;
(Feature) when is_list(Feature) ->
Feature
end, FeatureList))].
domain_to_xml({Domain}) -> domain_to_xml({Domain}) ->
{xmlelement, "item", [{"jid", 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 -> set ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
get -> get ->
case is_presence_subscribed(From, To) of
true ->
Host = To#jid.lserver, Host = To#jid.lserver,
Node = xml:get_tag_attr_s("node", SubEl), Node = xml:get_tag_attr_s("node", SubEl),
case ejabberd_hooks:run_fold(disco_sm_items, 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} -> {error, Error} ->
IQ#iq{type = error, sub_el = [SubEl, Error]} IQ#iq{type = error, sub_el = [SubEl, Error]}
end;
false ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]}
end end
end. end.
get_sm_items({error, _Error} = Acc, _From, _To, _Node, _Lang) -> get_sm_items({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
Acc; Acc;
get_sm_items(Acc, get_sm_items(Acc, From,
#jid{luser = LFrom, lserver = LSFrom}, #jid{user = User, server = Server} = To,
#jid{user = User, server = Server, luser = LTo, lserver = LSTo} = _To,
[], _Lang) -> [], _Lang) ->
Items = case Acc of Items = case Acc of
{result, Its} -> Its; {result, Its} -> Its;
empty -> [] empty -> []
end, end,
Items1 = case {LFrom, LSFrom} of Items1 = case is_presence_subscribed(From, To) of
{LTo, LSTo} -> get_user_resources(User, Server); true ->
_ -> [] get_user_resources(User, Server);
_ ->
[]
end, end,
{result, Items ++ Items1}; {result, Items ++ Items1};
@ -314,11 +326,25 @@ get_sm_items(empty, From, To, _Node, _Lang) ->
{error, ?ERR_NOT_ALLOWED} {error, ?ERR_NOT_ALLOWED}
end. 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) -> process_sm_iq_info(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
case Type of case Type of
set -> set ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
get -> get ->
case is_presence_subscribed(From, To) of
true ->
Host = To#jid.lserver, Host = To#jid.lserver,
Node = xml:get_tag_attr_s("node", SubEl), Node = xml:get_tag_attr_s("node", SubEl),
Identity = ejabberd_hooks:run_fold(disco_sm_identity, 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", sub_el = [{xmlelement, "query",
[{"xmlns", ?NS_DISCO_INFO} | ANode], [{"xmlns", ?NS_DISCO_INFO} | ANode],
Identity ++ Identity ++
lists:map(fun feature_to_xml/1, Features) features_to_xml(Features)
}]}; }]};
{error, Error} -> {error, Error} ->
IQ#iq{type = error, sub_el = [SubEl, Error]} IQ#iq{type = error, sub_el = [SubEl, Error]}
end;
false ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]}
end end
end. end.
get_sm_identity(Acc, _From, _To, _Node, _Lang) -> get_sm_identity(Acc, _From, #jid{luser = LUser, lserver=LServer}, _Node, _Lang) ->
Acc. 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) -> get_sm_features(empty, From, To, _Node, _Lang) ->
#jid{luser = LFrom, lserver = LSFrom} = From, #jid{luser = LFrom, lserver = LSFrom} = From,
@ -373,7 +409,13 @@ get_user_resources(User, Server) ->
%%% Support for: XEP-0157 Contact Addresses for XMPP Services %%% 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), Serverinfo_fields = get_fields_xml(Host, Module),
[{xmlelement, "x", [{xmlelement, "x",
[{"xmlns", ?NS_XDATA}, {"type", "result"}], [{"xmlns", ?NS_XDATA}, {"type", "result"}],
@ -387,8 +429,8 @@ get_info(_A, Host, Module, Node, _Lang) when Node == [] ->
++ Serverinfo_fields ++ Serverinfo_fields
}]; }];
get_info(_, _, _, _Node, _) -> get_info(Acc, _, _, _Node, _) ->
[]. Acc.
get_fields_xml(Host, Module) -> get_fields_xml(Host, Module) ->
Fields = gen_mod:get_module_opt(Host, ?MODULE, server_info, []), Fields = gen_mod:get_module_opt(Host, ?MODULE, server_info, []),

View File

@ -64,15 +64,15 @@ static int iconv_erl_control(ErlDrvData drv_data,
ei_decode_version(buf, &index, &i); ei_decode_version(buf, &index, &i);
ei_decode_tuple_header(buf, &index, &i); ei_decode_tuple_header(buf, &index, &i);
ei_get_type(buf, &index, &i, &size); ei_get_type(buf, &index, &i, &size);
from = malloc(size + 1); from = driver_alloc(size + 1);
ei_decode_string(buf, &index, from); ei_decode_string(buf, &index, from);
ei_get_type(buf, &index, &i, &size); ei_get_type(buf, &index, &i, &size);
to = malloc(size + 1); to = driver_alloc(size + 1);
ei_decode_string(buf, &index, to); ei_decode_string(buf, &index, to);
ei_get_type(buf, &index, &i, &size); ei_get_type(buf, &index, &i, &size);
stmp = string = malloc(size + 1); stmp = string = driver_alloc(size + 1);
ei_decode_string(buf, &index, string); ei_decode_string(buf, &index, string);
/* Special mode: parse as UTF-8 if possible; otherwise assume it's /* 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)); *rbuf = (char*)(b = driver_alloc_binary(size));
memcpy(b->orig_bytes, string, size); memcpy(b->orig_bytes, string, size);
free(from); driver_free(from);
free(to); driver_free(to);
free(string); driver_free(string);
return size; return size;
} }
@ -102,7 +102,7 @@ static int iconv_erl_control(ErlDrvData drv_data,
outleft = avail = 4*size; outleft = avail = 4*size;
inleft = size; inleft = size;
rtmp = rstring = malloc(avail); rtmp = rstring = driver_alloc(avail);
while (inleft > 0) { while (inleft > 0) {
if (iconv(cd, &stmp, &inleft, &rtmp, &outleft) == (size_t) -1) { if (iconv(cd, &stmp, &inleft, &rtmp, &outleft) == (size_t) -1) {
if (invalid_utf8_as_latin1 && (*stmp & 0x80) && outleft >= 2) { 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)); *rbuf = (char*)(b = driver_alloc_binary(size));
memcpy(b->orig_bytes, rstring, size); memcpy(b->orig_bytes, rstring, size);
free(from); driver_free(from);
free(to); driver_free(to);
free(string); driver_free(string);
free(rstring); driver_free(rstring);
iconv_close(cd); iconv_close(cd);
return size; return size;

View File

@ -1,7 +1,7 @@
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% File : mod_last.erl %%% File : mod_last.erl
%%% Author : Alexey Shchepin <alexey@process-one.net> %%% 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> %%% 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, [User, Server, UserListRecord,
{From, To, {From, To,
{xmlelement, "presence", [], []}}, {xmlelement, "presence", [], []}},
out]) of in]) of
allow -> allow ->
get_last(IQ, SubEl, User, Server); get_last(IQ, SubEl, User, Server);
deny -> deny ->

View File

@ -1,7 +1,7 @@
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% File : mod_last_odbc.erl %%% File : mod_last_odbc.erl
%%% Author : Alexey Shchepin <alexey@process-one.net> %%% 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> %%% 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, [User, Server, UserListRecord,
{From, To, {From, To,
{xmlelement, "presence", [], []}}, {xmlelement, "presence", [], []}},
out]) of in]) of
allow -> allow ->
get_last(IQ, SubEl, User, Server); get_last(IQ, SubEl, User, Server);
deny -> deny ->

View File

@ -1,7 +1,7 @@
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% File : mod_muc.erl %%% File : mod_muc.erl
%%% Author : Alexey Shchepin <alexey@process-one.net> %%% 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> %%% 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", ErrText = "Access denied by service policy",
Err = jlib:make_error_reply(Packet, Err = jlib:make_error_reply(Packet,
?ERRT_FORBIDDEN(Lang, ErrText)), ?ERRT_FORBIDDEN(Lang, ErrText)),
ejabberd_router:route(To, From, Err) ejabberd_router:route_error(To, From, Err, Packet)
end. end.

View File

@ -860,7 +860,9 @@ roomconfig_to_string(Options, Lang, FileFormat) ->
T -> T ->
case Opt of case Opt of
password -> "<div class=\"rcoe\">" ++ OptText ++ "</div>"; 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 ++ "\"" _ -> "\"" ++ T ++ "\""
end end
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(public_list) -> "Make participants list public";
get_roomconfig_text(password_protected) -> "Make room password protected"; get_roomconfig_text(password_protected) -> "Make room password protected";
get_roomconfig_text(password) -> "Password"; 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(members_only) -> "Make room members-only";
get_roomconfig_text(moderated) -> "Make room moderated"; get_roomconfig_text(moderated) -> "Make room moderated";
get_roomconfig_text(members_by_default) -> "Default users as participants"; 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_query_users) -> "Allow users to query other users";
get_roomconfig_text(allow_user_invites) -> "Allow users to send invites"; get_roomconfig_text(allow_user_invites) -> "Allow users to send invites";
get_roomconfig_text(logging) -> "Enable logging"; 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. get_roomconfig_text(_) -> undefined.
%% Users = [{JID, Nick, Role}] %% Users = [{JID, Nick, Role}]

View File

@ -162,7 +162,7 @@ normal_state({route, From, "",
trunc(gen_mod:get_module_opt( trunc(gen_mod:get_module_opt(
StateData#state.server_host, StateData#state.server_host,
mod_muc, min_message_interval, 0) * 1000000), mod_muc, min_message_interval, 0) * 1000000),
Size = lists:flatlength(xml:element_to_string(Packet)), Size = element_size(Packet),
{MessageShaper, MessageShaperInterval} = {MessageShaper, MessageShaperInterval} =
shaper:update(Activity#activity.message_shaper, Size), shaper:update(Activity#activity.message_shaper, Size),
if if
@ -593,35 +593,8 @@ handle_event(_Event, StateName, StateData) ->
%% {stop, Reason, Reply, NewStateData} %% {stop, Reason, Reply, NewStateData}
%%---------------------------------------------------------------------- %%----------------------------------------------------------------------
handle_sync_event({get_disco_item, JID, Lang}, _From, StateName, StateData) -> handle_sync_event({get_disco_item, JID, Lang}, _From, StateName, StateData) ->
FAffiliation = get_affiliation(JID, StateData), Reply = get_roomdesc_reply(JID, StateData,
FRole = get_role(JID, StateData), get_roomdesc_tail(StateData, Lang)),
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, Reply, StateName, StateData}; {reply, Reply, StateName, StateData};
handle_sync_event(get_config, _From, StateName, StateData) -> handle_sync_event(get_config, _From, StateName, StateData) ->
{reply, {ok, StateData#state.config}, StateName, StateData}; {reply, {ok, StateData#state.config}, StateName, StateData};
@ -1000,6 +973,18 @@ is_user_online(JID, StateData) ->
LJID = jlib:jid_tolower(JID), LJID = jlib:jid_tolower(JID),
?DICT:is_key(LJID, StateData#state.users). ?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 %%% Handle IQ queries of vCard
@ -1409,7 +1394,7 @@ prepare_room_queue(StateData) ->
{{value, {message, From}}, _RoomQueue} -> {{value, {message, From}}, _RoomQueue} ->
Activity = get_user_activity(From, StateData), Activity = get_user_activity(From, StateData),
Packet = Activity#activity.message, Packet = Activity#activity.message,
Size = lists:flatlength(xml:element_to_string(Packet)), Size = element_size(Packet),
{RoomShaper, RoomShaperInterval} = {RoomShaper, RoomShaperInterval} =
shaper:update(StateData#state.room_shaper, Size), shaper:update(StateData#state.room_shaper, Size),
erlang:send_after( erlang:send_after(
@ -1420,7 +1405,7 @@ prepare_room_queue(StateData) ->
{{value, {presence, From}}, _RoomQueue} -> {{value, {presence, From}}, _RoomQueue} ->
Activity = get_user_activity(From, StateData), Activity = get_user_activity(From, StateData),
{_Nick, Packet} = Activity#activity.presence, {_Nick, Packet} = Activity#activity.presence,
Size = lists:flatlength(xml:element_to_string(Packet)), Size = element_size(Packet),
{RoomShaper, RoomShaperInterval} = {RoomShaper, RoomShaperInterval} =
shaper:update(StateData#state.room_shaper, Size), shaper:update(StateData#state.room_shaper, Size),
erlang:send_after( erlang:send_after(
@ -1902,10 +1887,18 @@ send_new_presence(NJID, Reason, StateData) ->
false -> false ->
[] []
end, 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( Packet = xml:append_subtags(
Presence, Presence,
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}], [{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
[{xmlelement, "item", ItemAttrs, ItemEls} | Status]}]), [{xmlelement, "item", ItemAttrs, ItemEls} | Status2]}]),
ejabberd_router:route( ejabberd_router:route(
jlib:jid_replace_resource(StateData#state.jid, Nick), jlib:jid_replace_resource(StateData#state.jid, Nick),
Info#user.jid, Info#user.jid,
@ -2083,7 +2076,7 @@ add_message_to_history(FromNick, FromJID, Packet, StateData) ->
jlib:jid_replace_resource(StateData#state.jid, FromNick), jlib:jid_replace_resource(StateData#state.jid, FromNick),
StateData#state.jid, StateData#state.jid,
TSPacket), TSPacket),
Size = lists:flatlength(xml:element_to_string(SPacket)), Size = element_size(SPacket),
Q1 = lqueue_in({FromNick, TSPacket, HaveSubject, TimeStamp, Size}, Q1 = lqueue_in({FromNick, TSPacket, HaveSubject, TimeStamp, Size},
StateData#state.history), StateData#state.history),
add_to_log(text, {FromNick, Packet}, StateData), add_to_log(text, {FromNick, Packet}, StateData),
@ -3329,28 +3322,16 @@ process_iq_disco_items(_From, set, _Lang, _StateData) ->
{error, ?ERR_NOT_ALLOWED}; {error, ?ERR_NOT_ALLOWED};
process_iq_disco_items(From, get, _Lang, StateData) -> process_iq_disco_items(From, get, _Lang, StateData) ->
FAffiliation = get_affiliation(From, StateData), case (StateData#state.config)#config.public_list of
FRole = get_role(From, StateData),
case ((StateData#state.config)#config.public_list == true) orelse
(FRole /= none) orelse
(FAffiliation == admin) orelse
(FAffiliation == owner) of
true -> true ->
UList = {result, get_mucroom_disco_items(StateData), StateData};
lists:map( _ ->
fun({_LJID, Info}) -> case is_occupant_or_admin(From, StateData) of
Nick = Info#user.nick, true ->
{xmlelement, "item", {result, get_mucroom_disco_items(StateData), StateData};
[{"jid", jlib:jid_to_string(
{StateData#state.room,
StateData#state.host,
Nick})},
{"name", Nick}], []}
end,
?DICT:to_list(StateData#state.users)),
{result, UList, StateData};
_ -> _ ->
{error, ?ERR_FORBIDDEN} {error, ?ERR_FORBIDDEN}
end
end. end.
process_iq_captcha(_From, get, _Lang, _SubEl, _StateData) -> process_iq_captcha(_From, get, _Lang, _SubEl, _StateData) ->
@ -3372,6 +3353,38 @@ get_title(StateData) ->
Name Name
end. 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 % Invitation support
@ -3577,3 +3590,6 @@ tab_count_user(JID) ->
_ -> _ ->
0 0
end. end.
element_size(El) ->
size(xml:element_to_binary(El)).

View File

@ -126,8 +126,7 @@ loop(Host, AccessMaxOfflineMsgs) ->
M#offline_msg.timestamp))]}, M#offline_msg.timestamp))]},
XML = XML =
ejabberd_odbc:escape( ejabberd_odbc:escape(
lists:flatten( xml:element_to_binary(Packet)),
xml:element_to_string(Packet))),
odbc_queries:add_spool_sql(Username, XML) odbc_queries:add_spool_sql(Username, XML)
end, Msgs), end, Msgs),
case catch odbc_queries:add_spool(Host, Query) of case catch odbc_queries:add_spool(Host, Query) of

View File

@ -726,11 +726,11 @@ raw_to_item({SType, SValue, SAction, SOrder, SMatchAll, SMatchIQ,
"d" -> deny "d" -> deny
end, end,
Order = list_to_integer(SOrder), Order = list_to_integer(SOrder),
MatchAll = SMatchAll == "1" orelse SMatchAll == "t", MatchAll = ejabberd_odbc:to_bool(SMatchAll),
MatchIQ = SMatchIQ == "1" orelse SMatchIQ == "t" , MatchIQ = ejabberd_odbc:to_bool(SMatchIQ),
MatchMessage = SMatchMessage == "1" orelse SMatchMessage == "t", MatchMessage = ejabberd_odbc:to_bool(SMatchMessage),
MatchPresenceIn = SMatchPresenceIn == "1" orelse SMatchPresenceIn == "t", MatchPresenceIn = ejabberd_odbc:to_bool(SMatchPresenceIn),
MatchPresenceOut = SMatchPresenceOut == "1" orelse SMatchPresenceOut == "t", MatchPresenceOut = ejabberd_odbc:to_bool(SMatchPresenceOut),
#listitem{type = Type, #listitem{type = Type,
value = Value, value = Value,
action = Action, action = Action,

View File

@ -92,7 +92,7 @@ set_data(LUser, LServer, El) ->
Username = ejabberd_odbc:escape(LUser), Username = ejabberd_odbc:escape(LUser),
LXMLNS = ejabberd_odbc:escape(XMLNS), LXMLNS = ejabberd_odbc:escape(XMLNS),
SData = ejabberd_odbc:escape( 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) odbc_queries:set_private_data(LServer, Username, LXMLNS, SData)
end; end;
_ -> _ ->

View File

@ -73,8 +73,7 @@
disco_sm_items/5 disco_sm_items/5
]). ]).
%% exported iq handlers %% exported iq handlers
-export([iq_local/3, -export([iq_sm/3
iq_sm/3
]). ]).
%% exports for console debug manual use %% exports for console debug manual use
@ -183,9 +182,8 @@ init([ServerHost, Opts]) ->
pubsub_index:init(Host, 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(Host, config), [set, named_table]),
ets:new(gen_mod:get_module_proc(ServerHost, 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), {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), 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), {nodetree, NodeTree}),
ets:insert(gen_mod:get_module_proc(Host, config), {plugins, Plugins}), 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), {ignore_pep_from_offline, PepOffline}),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {host, Host}), 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(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_local_identity, ServerHost, ?MODULE, disco_local_identity, 75),
ejabberd_hooks:add(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75), ejabberd_hooks:add(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75),
ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 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(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 80),
ejabberd_hooks:add(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50), 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(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50),
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE, remove_user, 50), ejabberd_hooks:add(remove_user, ServerHost, ?MODULE, remove_user, 50),
ejabberd_hooks:add(anonymous_purge_hook, 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 case lists:member(?PEPNODE, Plugins) of
true -> true ->
ejabberd_hooks:add(feature_check_packet, ServerHost, ?MODULE, feature_check_packet, 75), 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_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75),
ejabberd_hooks:add(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75), ejabberd_hooks:add(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
ejabberd_hooks:add(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75), ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
gen_iq_handler:add_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB, ?MODULE, iq_local, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB, ?MODULE, iq_sm, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB_OWNER, ?MODULE, iq_local, IQDisc); gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER, ?MODULE, iq_sm, IQDisc);
false -> false ->
ok ok
end, end,
@ -585,15 +581,12 @@ send_loop(State) ->
%% disco hooks handling functions %% 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) -> 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) -> disco_local_identity(Acc, _From, _To, _Node, _Lang) ->
Acc. Acc.
@ -614,76 +607,116 @@ disco_local_items(Acc, _From, _To, [], _Lang) ->
disco_local_items(Acc, _From, _To, _Node, _Lang) -> disco_local_items(Acc, _From, _To, _Node, _Lang) ->
Acc. Acc.
disco_sm_identity(Acc, _From, To, [], _Lang) -> disco_sm_identity(Acc, From, To, Node, Lang) when is_list(Node) ->
Acc ++ [identity(To#jid.lserver)]; 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) -> disco_sm_identity(Acc, From, To, Node, _Lang) ->
LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)), disco_identity(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From) ++ Acc.
Acc ++ case node_disco_identity(LOwner, From, Node) of
{result, I} -> I;
_ -> []
end.
disco_sm_features(Acc, _From, _To, [], _Lang) -> disco_identity(_Host, <<>>, _From) ->
Acc; [{xmlelement, "identity", [{"category", "pubsub"}, {"type", "pep"}], []}];
disco_sm_features(Acc, From, To, Node, _Lang) -> disco_identity(Host, Node, From) ->
LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)), Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) ->
Features = node_disco_features(LOwner, From, Node), case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
case {Acc, Features} of {result, _} ->
{{result, AccFeatures}, {result, AddFeatures}} -> {result, [{xmlelement, "identity", [{"category", "pubsub"}, {"type", "pep"}], []},
{result, AccFeatures++AddFeatures}; {xmlelement, "identity",
{_, {result, AddFeatures}} -> [{"category", "pubsub"},
{result, AddFeatures}; {"type", "leaf"}
{_, _} -> | case get_option(Options, title) of
Acc false -> [];
end. [Title] -> [{"name", Title}]
end],
disco_sm_items(Acc, From, To, [], _Lang) -> []}]};
Host = To#jid.lserver, _ -> {result, []}
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
end end
end, end,
case transaction(Host, Node, Action, sync_dirty) of 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 _ -> 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. end.
%% ------- %% -------
@ -859,25 +892,23 @@ terminate(_Reason, #state{host = Host,
case lists:member(?PEPNODE, Plugins) of case lists:member(?PEPNODE, Plugins) of
true -> true ->
ejabberd_hooks:delete(feature_check_packet, ServerHost, ?MODULE, feature_check_packet, 75), 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_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75),
ejabberd_hooks:delete(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75), ejabberd_hooks:delete(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
ejabberd_hooks:delete(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75), ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
gen_iq_handler:remove_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB), gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB),
gen_iq_handler:remove_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB_OWNER); gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER);
false -> false ->
ok ok
end, end,
ejabberd_hooks:delete(sm_remove_connection_hook, ServerHost, ?MODULE, on_user_offline, 75), 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_local_identity, ServerHost, ?MODULE, disco_local_identity, 75),
ejabberd_hooks:delete(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75), ejabberd_hooks:delete(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75),
ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 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(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 80),
ejabberd_hooks:delete(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50), 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(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50),
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50), ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50),
ejabberd_hooks:delete(anonymous_purge_hook, 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), mod_disco:unregister_feature(ServerHost, ?NS_PUBSUB),
gen_mod:get_module_proc(ServerHost, ?LOOPNAME) ! stop, gen_mod:get_module_proc(ServerHost, ?LOOPNAME) ! stop,
terminate_plugins(Host, ServerHost, Plugins, TreePlugin). 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) ->
node_disco_info(Host, Node, From, true, true). node_disco_info(Host, Node, From, true, true).
node_disco_identity(Host, Node, From) -> %node_disco_identity(Host, Node, From) ->
node_disco_info(Host, Node, From, true, false). % node_disco_info(Host, Node, From, true, false).
node_disco_features(Host, Node, From) -> %node_disco_features(Host, Node, From) ->
node_disco_info(Host, Node, From, false, true). % node_disco_info(Host, Node, From, false, true).
node_disco_info(Host, Node, From, Identity, Features) -> node_disco_info(Host, Node, From, Identity, Features) ->
Action = Action =
fun(#pubsub_node{type = Type, id = NodeId}) -> fun(#pubsub_node{type = Type, id = NodeId}) ->
@ -1121,17 +1152,15 @@ iq_disco_items(Host, Item, From) ->
{result, []}; {result, []};
[SNode] -> [SNode] ->
Node = string_to_node(SNode), Node = string_to_node(SNode),
Action = Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) ->
fun(#pubsub_node{type = Type, id = NodeId}) -> NodeItems = case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
% TODO call get_items/6 instead for access control (EJAB-1033) {result, R} -> R;
NodeItems = case node_call(Type, get_items, [NodeId, From]) of
{result, I} -> I;
_ -> [] _ -> []
end, end,
Nodes = lists:map( Nodes = lists:map(
fun(#pubsub_node{nodeid = {_, SubNode}, options = Options}) -> fun(#pubsub_node{nodeid = {_, SubNode}, options = SubOptions}) ->
Attrs = Attrs =
case get_option(Options, title) of case get_option(SubOptions, title) of
false -> false ->
[{"jid", Host} |nodeAttr(SubNode)]; [{"jid", Host} |nodeAttr(SubNode)];
Title -> Title ->
@ -1152,24 +1181,6 @@ iq_disco_items(Host, Item, From) ->
end end
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) -> iq_sm(From, To, #iq{type = Type, sub_el = SubEl, xmlns = XMLNS, lang = Lang} = IQ) ->
ServerHost = To#jid.lserver, ServerHost = To#jid.lserver,
LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)), LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)),
@ -1737,9 +1748,17 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
{result, true} -> {result, true} ->
case tree_call(Host, create_node, [Host, Node, Type, Owner, NodeOptions, Parents]) of case tree_call(Host, create_node, [Host, Node, Type, Owner, NodeOptions, Parents]) of
{ok, NodeId} -> {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}} -> {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 ->
Error Error
end; end;
@ -1751,20 +1770,15 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
[{xmlelement, "create", nodeAttr(Node), [{xmlelement, "create", nodeAttr(Node),
[]}]}], []}]}],
case transaction(CreateNode, transaction) of case transaction(CreateNode, transaction) of
{result, {Result, broadcast}} -> {result, {NodeId, SubsByDepth, {Result, broadcast}}} ->
%%Lang = "en", %% TODO: fix broadcast_created_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth),
%%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))]}]),
case Result of case Result of
default -> {result, Reply}; default -> {result, Reply};
_ -> {result, Result} _ -> {result, Result}
end; end;
{result, default} -> {result, {_NodeId, _SubsByDepth, default}} ->
{result, Reply}; {result, Reply};
{result, Result} -> {result, {_NodeId, _SubsByDepth, Result}} ->
{result, Result}; {result, Result};
Error -> Error ->
%% in case we change transaction to sync_dirty... %% in case we change transaction to sync_dirty...
@ -1866,7 +1880,7 @@ subscribe_node(Host, Node, From, JID, Configuration) ->
error -> {"", "", ""}; error -> {"", "", ""};
J -> jlib:jid_tolower(J) J -> jlib:jid_tolower(J)
end, 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), Features = features(Type),
SubscribeFeature = lists:member("subscribe", Features), SubscribeFeature = lists:member("subscribe", Features),
OptionsFeature = lists:member("subscription-options", Features), OptionsFeature = lists:member("subscription-options", Features),
@ -1875,21 +1889,7 @@ subscribe_node(Host, Node, From, JID, Configuration) ->
AccessModel = get_option(Options, access_model), AccessModel = get_option(Options, access_model),
SendLast = get_option(Options, send_last_published_item), SendLast = get_option(Options, send_last_published_item),
AllowedGroups = get_option(Options, roster_groups_allowed, []), AllowedGroups = get_option(Options, roster_groups_allowed, []),
{PresenceSubscription, RosterGroup} = {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, Subscriber, Owners, AccessModel, AllowedGroups),
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,
if if
not SubscribeFeature -> not SubscribeFeature ->
%% Node does not support subscriptions %% Node does not support subscriptions
@ -2054,7 +2054,7 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
PluginPayload -> PluginPayload PluginPayload -> PluginPayload
end, end,
broadcast_publish_item(Host, Node, NodeId, Type, Options, Removed, ItemId, jlib:jid_tolower(Publisher), BroadcastPayload), 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 case Result of
default -> {result, Reply}; default -> {result, Reply};
_ -> {result, Result} _ -> {result, Result}
@ -2064,14 +2064,14 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
Type = TNode#pubsub_node.type, Type = TNode#pubsub_node.type,
Options = TNode#pubsub_node.options, Options = TNode#pubsub_node.options,
broadcast_retract_items(Host, Node, NodeId, Type, Options, Removed), 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, Reply};
{result, {TNode, {Result, Removed}}} -> {result, {TNode, {Result, Removed}}} ->
NodeId = TNode#pubsub_node.id, NodeId = TNode#pubsub_node.id,
Type = TNode#pubsub_node.type, Type = TNode#pubsub_node.type,
Options = TNode#pubsub_node.options, Options = TNode#pubsub_node.options,
broadcast_retract_items(Host, Node, NodeId, Type, Options, Removed), 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, Result};
{result, {_, default}} -> {result, {_, default}} ->
{result, Reply}; {result, Reply};
@ -2232,20 +2232,13 @@ get_items(Host, Node, From, SubId, SMaxItems, ItemIDs) ->
{error, Error} -> {error, Error} ->
{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), Features = features(Type),
RetreiveFeature = lists:member("retrieve-items", Features), RetreiveFeature = lists:member("retrieve-items", Features),
PersistentFeature = lists:member("persistent-items", Features), PersistentFeature = lists:member("persistent-items", Features),
AccessModel = get_option(Options, access_model), AccessModel = get_option(Options, access_model),
AllowedGroups = get_option(Options, roster_groups_allowed, []), AllowedGroups = get_option(Options, roster_groups_allowed, []),
{PresenceSubscription, RosterGroup} = {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups),
case Host of
{OUser, OServer, _} ->
get_roster_info(OUser, OServer,
jlib:jid_tolower(From), AllowedGroups);
_ ->
{true, true}
end,
if if
not RetreiveFeature -> not RetreiveFeature ->
%% Item Retrieval Not Supported %% Item Retrieval Not Supported
@ -2295,6 +2288,12 @@ get_item(Host, Node, ItemId) ->
{result, {_, Items}} -> Items; {result, {_, Items}} -> Items;
Error -> Error Error -> Error
end. 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() %% @spec (Host, Node, NodeId, Type, LJID, Number) -> any()
%% Host = pubsubHost() %% Host = pubsubHost()
@ -2310,10 +2309,10 @@ send_items(Host, Node, NodeId, Type, LJID, last) ->
undefined -> undefined ->
send_items(Host, Node, NodeId, Type, LJID, 1); send_items(Host, Node, NodeId, Type, LJID, 1);
LastItem -> LastItem ->
{ModifNow, ModifLjid} = LastItem#pubsub_item.modification, {ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
Stanza = event_stanza_with_delay( Stanza = event_stanza_with_delay(
[{xmlelement, "items", nodeAttr(Node), [{xmlelement, "items", nodeAttr(Node),
itemsEls([LastItem])}], ModifNow, ModifLjid), itemsEls([LastItem])}], ModifNow, ModifUSR),
ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza) ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza)
end; end;
send_items(Host, Node, NodeId, Type, LJID, Number) -> send_items(Host, Node, NodeId, Type, LJID, Number) ->
@ -2330,10 +2329,10 @@ send_items(Host, Node, NodeId, Type, LJID, Number) ->
end, end,
Stanza = case ToSend of Stanza = case ToSend of
[LastItem] -> [LastItem] ->
{ModifNow, ModifLjid} = LastItem#pubsub_item.modification, {ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
event_stanza_with_delay( event_stanza_with_delay(
[{xmlelement, "items", nodeAttr(Node), [{xmlelement, "items", nodeAttr(Node),
itemsEls(ToSend)}], ModifNow, ModifLjid); itemsEls(ToSend)}], ModifNow, ModifUSR);
_ -> _ ->
event_stanza( event_stanza(
[{xmlelement, "items", nodeAttr(Node), [{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) -> Action = fun(#pubsub_node{owners = Owners, type = Type, id = NodeId}=N) ->
case lists:member(Owner, Owners) of case lists:member(Owner, Owners) of
true -> true ->
OwnerJID = jlib:make_jid(Owner),
FilteredEntities = case Owners of
[Owner] -> [E || E <- Entities, element(1, E) =/= OwnerJID];
_ -> Entities
end,
lists:foreach( lists:foreach(
fun({JID, Affiliation}) -> fun({JID, Affiliation}) ->
node_call(Type, set_affiliation, [NodeId, JID, Affiliation]), node_call(Type, set_affiliation, [NodeId, JID, Affiliation]),
@ -2464,7 +2468,7 @@ set_affiliations(Host, Node, From, EntitiesEls) ->
_ -> _ ->
ok ok
end end
end, Entities), end, FilteredEntities),
{result, []}; {result, []};
_ -> _ ->
{error, ?ERR_FORBIDDEN} {error, ?ERR_FORBIDDEN}
@ -2767,8 +2771,24 @@ set_subscriptions(Host, Node, From, EntitiesEls) ->
end end
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) %% @spec (OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, SubscriberResource}, AllowedGroups)
%% -> {PresenceSubscription, RosterGroup} %% -> {PresenceSubscription, RosterGroup}
get_roster_info(_, _, {"", "", _}, _) ->
{false, false};
get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, AllowedGroups) -> get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, AllowedGroups) ->
{Subscription, Groups} = {Subscription, Groups} =
ejabberd_hooks:run_fold( ejabberd_hooks:run_fold(
@ -2780,7 +2800,9 @@ get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, A
RosterGroup = lists:any(fun(Group) -> RosterGroup = lists:any(fun(Group) ->
lists:member(Group, AllowedGroups) lists:member(Group, AllowedGroups)
end, Groups), 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 %% @spec (AffiliationStr) -> Affiliation
%% AffiliationStr = string() %% AffiliationStr = string()
@ -2868,15 +2890,66 @@ node_to_deliver(LJID, NodeOptions) ->
presence_can_deliver(LJID, PresenceDelivery). presence_can_deliver(LJID, PresenceDelivery).
presence_can_deliver(_, false) -> true; 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 case mnesia:dirty_match_object({session, '_', '_', {User, Server}, '_', '_'}) of
[] -> false; [] -> false;
Ss -> Ss ->
lists:foldl(fun({session, _, _, _, undefined, _}, Acc) -> Acc; lists:foldl(fun(_, true) -> true;
({session, _, _, _, _Priority, _}, _Acc) -> true ({session, _, _ , _, undefined, _}, _Acc) -> false;
({session, _, {_, _, R}, _, _Priority, _}, _Acc) ->
case Resource of
[] -> true;
R -> true;
_ -> false
end
end, false, Ss) end, false, Ss)
end. 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() %% @spec (Payload) -> int()
%% Payload = term() %% Payload = term()
%% @doc <p>Count occurence of XML elements in payload.</p> %% @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(Els) ->
event_stanza_withmoreels(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), 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).
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} {result, false}
end. 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) -> broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) ->
case get_option(NodeOptions, notify_config) of case get_option(NodeOptions, notify_config) of
true -> true ->
@ -3137,18 +3217,23 @@ subscribed_nodes_by_jid(NotifyType, SubsByDepth) ->
case is_to_deliver(LJID, NotifyType, Depth, NodeOptions, SubOptions) of case is_to_deliver(LJID, NotifyType, Depth, NodeOptions, SubOptions) of
true -> true ->
%% If is to deliver : %% 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, %% check if the JIDs co-accumulator contains the Subscription Jid,
false -> false ->
%% - if not, %% - if not,
%% - add the Jid to JIDs list co-accumulator ; %% - add the Jid to JIDs list co-accumulator ;
%% - create a tuple of the Jid, NodeId, and SubID (as list), %% - create a tuple of the Jid, NodeId, and SubID (as list),
%% and add the tuple to the Recipients list co-accumulator %% and add the tuple to the Recipients list co-accumulator
{[LJID | JIDs], [{LJID, NodeName, [SubID]} | Recipients]}; {[JIDToDeliver | JIDsAcc], [{JIDToDeliver, NodeName, [SubID]} | RecipientsAcc]};
true -> true ->
%% - if the JIDs co-accumulator contains the Jid %% - if the JIDs co-accumulator contains the Jid
%% get the tuple containing the Jid from the Recipient list co-accumulator %% 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 %% delete the tuple from the Recipients list
% v1 : Recipients1 = lists:keydelete(LJID, 1, Recipients), % v1 : Recipients1 = lists:keydelete(LJID, 1, Recipients),
% v2 : Recipients1 = lists:keyreplace(LJID, 1, Recipients, {LJID, NodeId1, [SubID | SubIDs]}), % 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.1 : {JIDs, lists:append(Recipients1, [{LJID, NodeId1, lists:append(SubIDs, [SubID])}])}
% v1.2 : {JIDs, [{LJID, NodeId1, [SubID | SubIDs]} | Recipients1]} % v1.2 : {JIDs, [{LJID, NodeId1, [SubID | SubIDs]} | Recipients1]}
% v2: {JIDs, 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; end;
false -> false ->
{JIDs, Recipients} {JIDs, Recipients}
end end
end, Acc, Subs) end, Acc, Subs)
end, end,
DepthsToDeliver = fun({Depth, SubsByNode}, Acc) -> DepthsToDeliver = fun({Depth, SubsByNode}, Acc1) ->
lists:foldl(fun({Node, Subs}, Acc2) -> lists:foldl(fun({Node, Subs}, Acc2) ->
NodesToDeliver(Depth, Node, Subs, Acc2) NodesToDeliver(Depth, Node, Subs, Acc2)
end, Acc, SubsByNode) end, Acc1, SubsByNode)
end, end,
{_, JIDSubs} = lists:foldl(DepthsToDeliver, {[], []}, SubsByDepth), {_, JIDSubs} = lists:foldl(DepthsToDeliver, {[], []}, SubsByDepth),
JIDSubs. JIDSubs.
@ -3494,18 +3581,18 @@ is_last_item_cache_enabled(Host) ->
_ -> false _ -> false
end. end.
set_cached_item({_, ServerHost, _}, NodeId, ItemId, Payload) -> set_cached_item({_, ServerHost, _}, NodeId, ItemId, Publisher, Payload) ->
set_cached_item(ServerHost, NodeId, ItemId, Payload); set_cached_item(ServerHost, NodeId, ItemId, Publisher, Payload);
set_cached_item(Host, NodeId, ItemId, Payload) -> set_cached_item(Host, NodeId, ItemId, Publisher, Payload) ->
case is_last_item_cache_enabled(Host) of 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 _ -> ok
end. end.
unset_cached_item({_, ServerHost, _}, NodeId) -> unset_cached_item({_, ServerHost, _}, NodeId) ->
unset_cached_item(ServerHost, NodeId); unset_cached_item(ServerHost, NodeId);
unset_cached_item(Host, NodeId) -> unset_cached_item(Host, NodeId) ->
case is_last_item_cache_enabled(Host) of 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 _ -> ok
end. end.
get_cached_item({_, ServerHost, _}, NodeId) -> get_cached_item({_, ServerHost, _}, NodeId) ->
@ -3513,9 +3600,10 @@ get_cached_item({_, ServerHost, _}, NodeId) ->
get_cached_item(Host, NodeId) -> get_cached_item(Host, NodeId) ->
case is_last_item_cache_enabled(Host) of case is_last_item_cache_enabled(Host) of
true -> true ->
case catch ets:lookup(gen_mod:get_module_proc(Host, last_items), NodeId) of case mnesia:dirty_read({pubsub_last_item, NodeId}) of
[{NodeId, {ItemId, Payload}}] -> [{pubsub_last_item, NodeId, ItemId, Creation, Payload}] ->
#pubsub_item{itemid = {ItemId, NodeId}, payload = Payload}; #pubsub_item{itemid = {ItemId, NodeId}, payload = Payload,
creation = Creation, modification = Creation};
_ -> _ ->
undefined undefined
end; end;
@ -3669,6 +3757,10 @@ transaction(Host, Node, Action, Trans) ->
Error Error
end end
end, Trans). end, Trans).
transaction(Host, Action, Trans) ->
transaction(fun() ->
{result, lists:foldl(Action, [], tree_call(Host, get_nodes, [Host]))}
end, Trans).
transaction(Fun, Trans) -> transaction(Fun, Trans) ->
case catch mnesia:Trans(Fun) of case catch mnesia:Trans(Fun) of

View File

@ -73,8 +73,7 @@
disco_sm_items/5 disco_sm_items/5
]). ]).
%% exported iq handlers %% exported iq handlers
-export([iq_local/3, -export([iq_sm/3
iq_sm/3
]). ]).
%% exports for console debug manual use %% exports for console debug manual use
@ -183,9 +182,8 @@ init([ServerHost, Opts]) ->
pubsub_index:init(Host, 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(Host, config), [set, named_table]),
ets:new(gen_mod:get_module_proc(ServerHost, 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), {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), 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), {nodetree, NodeTree}),
ets:insert(gen_mod:get_module_proc(Host, config), {plugins, Plugins}), 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), {ignore_pep_from_offline, PepOffline}),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {host, Host}), 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(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_local_identity, ServerHost, ?MODULE, disco_local_identity, 75),
ejabberd_hooks:add(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75), ejabberd_hooks:add(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75),
ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 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(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 80),
ejabberd_hooks:add(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50), 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(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50),
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE, remove_user, 50), ejabberd_hooks:add(remove_user, ServerHost, ?MODULE, remove_user, 50),
ejabberd_hooks:add(anonymous_purge_hook, 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 case lists:member(?PEPNODE, Plugins) of
true -> true ->
ejabberd_hooks:add(feature_check_packet, ServerHost, ?MODULE, feature_check_packet, 75), 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_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75),
ejabberd_hooks:add(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75), ejabberd_hooks:add(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
ejabberd_hooks:add(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75), ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
gen_iq_handler:add_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB, ?MODULE, iq_local, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB, ?MODULE, iq_sm, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB_OWNER, ?MODULE, iq_local, IQDisc); gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER, ?MODULE, iq_sm, IQDisc);
false -> false ->
ok ok
end, end,
@ -388,15 +384,12 @@ send_loop(State) ->
%% disco hooks handling functions %% 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) -> 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) -> disco_local_identity(Acc, _From, _To, _Node, _Lang) ->
Acc. Acc.
@ -417,76 +410,120 @@ disco_local_items(Acc, _From, _To, [], _Lang) ->
disco_local_items(Acc, _From, _To, _Node, _Lang) -> disco_local_items(Acc, _From, _To, _Node, _Lang) ->
Acc. Acc.
disco_sm_identity(Acc, _From, To, [], _Lang) -> disco_sm_identity(Acc, From, To, Node, Lang) when is_list(Node) ->
Acc ++ [identity(To#jid.lserver)]; 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) -> disco_sm_identity(Acc, From, To, Node, _Lang) ->
LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)), disco_identity(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From) ++ Acc.
Acc ++ case node_disco_identity(LOwner, From, Node) of
{result, I} -> I;
_ -> []
end.
disco_sm_features(Acc, _From, _To, [], _Lang) -> disco_identity(_Host, <<>>, _From) ->
Acc; [{xmlelement, "identity", [{"category", "pubsub"}, {"type", "pep"}], []}];
disco_sm_features(Acc, From, To, Node, _Lang) -> disco_identity(Host, Node, From) ->
LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)), Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) ->
Features = node_disco_features(LOwner, From, Node), Owners = node_owners_call(Type, Idx),
case {Acc, Features} of case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
{{result, AccFeatures}, {result, AddFeatures}} -> {result, _} ->
{result, AccFeatures++AddFeatures}; {result, [{xmlelement, "identity", [{"category", "pubsub"}, {"type", "pep"}], []},
{_, {result, AddFeatures}} -> {xmlelement, "identity",
{result, AddFeatures}; [{"category", "pubsub"},
{_, _} -> {"type", "leaf"}
Acc | case get_option(Options, title) of
end. false -> [];
[Title] -> [{"name", Title}]
disco_sm_items(Acc, From, To, [], _Lang) -> end],
Host = To#jid.lserver, []}]};
case tree_action(Host, get_subnodes, [Host, <<>>, From]) of _ -> {result, []}
[] ->
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
end end
end, end,
case transaction(Host, Node, Action, sync_dirty) of 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 _ -> 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. end.
%% ------- %% -------
@ -662,25 +699,23 @@ terminate(_Reason, #state{host = Host,
case lists:member(?PEPNODE, Plugins) of case lists:member(?PEPNODE, Plugins) of
true -> true ->
ejabberd_hooks:delete(feature_check_packet, ServerHost, ?MODULE, feature_check_packet, 75), 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_sm_identity, ServerHost, ?MODULE, disco_sm_identity, 75),
ejabberd_hooks:delete(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75), ejabberd_hooks:delete(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
ejabberd_hooks:delete(disco_local_items, ServerHost, ?MODULE, disco_local_items, 75), ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
gen_iq_handler:remove_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB), gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB),
gen_iq_handler:remove_iq_handler(ejabberd_local, ServerHost, ?NS_PUBSUB_OWNER); gen_iq_handler:remove_iq_handler(ejabberd_sm, ServerHost, ?NS_PUBSUB_OWNER);
false -> false ->
ok ok
end, end,
ejabberd_hooks:delete(sm_remove_connection_hook, ServerHost, ?MODULE, on_user_offline, 75), 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_local_identity, ServerHost, ?MODULE, disco_local_identity, 75),
ejabberd_hooks:delete(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75), ejabberd_hooks:delete(disco_local_features, ServerHost, ?MODULE, disco_local_features, 75),
ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 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(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 80),
ejabberd_hooks:delete(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50), 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(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50),
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50), ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50),
ejabberd_hooks:delete(anonymous_purge_hook, 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), mod_disco:unregister_feature(ServerHost, ?NS_PUBSUB),
gen_mod:get_module_proc(ServerHost, ?LOOPNAME) ! stop, gen_mod:get_module_proc(ServerHost, ?LOOPNAME) ! stop,
terminate_plugins(Host, ServerHost, Plugins, TreePlugin). 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) ->
node_disco_info(Host, Node, From, true, true). node_disco_info(Host, Node, From, true, true).
node_disco_identity(Host, Node, From) -> %node_disco_identity(Host, Node, From) ->
node_disco_info(Host, Node, From, true, false). % node_disco_info(Host, Node, From, true, false).
node_disco_features(Host, Node, From) -> %node_disco_features(Host, Node, From) ->
node_disco_info(Host, Node, From, false, true). % node_disco_info(Host, Node, From, false, true).
node_disco_info(Host, Node, From, Identity, Features) -> node_disco_info(Host, Node, From, Identity, Features) ->
Action = Action =
fun(#pubsub_node{type = Type, id = NodeId}) -> fun(#pubsub_node{type = Type, id = NodeId}) ->
@ -927,17 +962,16 @@ iq_disco_items(Host, Item, From, RSM) ->
{result, []}; {result, []};
[SNode] -> [SNode] ->
Node = string_to_node(SNode), Node = string_to_node(SNode),
Action = Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) ->
fun(#pubsub_node{type = Type, id = NodeId}) -> Owners = node_owners_call(Type, Idx),
%% TODO call get_items/6 instead for access control (EJAB-1033) {NodeItems, RsmOut} = case get_allowed_items_call(Host, Idx, From, Type, Options, Owners, RSM) of
{NodeItems, RsmOut} = case node_call(Type, get_items, [NodeId, From, RSM]) of {result, R} -> R;
{result, I} -> I;
_ -> {[], none} _ -> {[], none}
end, end,
Nodes = lists:map( Nodes = lists:map(
fun(#pubsub_node{nodeid = {_, SubNode}, options = Options}) -> fun(#pubsub_node{nodeid = {_, SubNode}, options = SubOptions}) ->
Attrs = Attrs =
case get_option(Options, title) of case get_option(SubOptions, title) of
false -> false ->
[{"jid", Host} |nodeAttr(SubNode)]; [{"jid", Host} |nodeAttr(SubNode)];
Title -> Title ->
@ -958,24 +992,6 @@ iq_disco_items(Host, Item, From, RSM) ->
end end
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) -> iq_sm(From, To, #iq{type = Type, sub_el = SubEl, xmlns = XMLNS, lang = Lang} = IQ) ->
ServerHost = To#jid.lserver, ServerHost = To#jid.lserver,
LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)), LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)),
@ -1547,9 +1563,17 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
{result, true} -> {result, true} ->
case tree_call(Host, create_node, [Host, Node, Type, Owner, NodeOptions, Parents]) of case tree_call(Host, create_node, [Host, Node, Type, Owner, NodeOptions, Parents]) of
{ok, NodeId} -> {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}} -> {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 ->
Error Error
end; end;
@ -1561,20 +1585,15 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
[{xmlelement, "create", nodeAttr(Node), [{xmlelement, "create", nodeAttr(Node),
[]}]}], []}]}],
case transaction(Host, CreateNode, transaction) of case transaction(Host, CreateNode, transaction) of
{result, {Result, broadcast}} -> {result, {NodeId, SubsByDepth, {Result, broadcast}}} ->
%%Lang = "en", %% TODO: fix broadcast_created_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth),
%%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))]}]),
case Result of case Result of
default -> {result, Reply}; default -> {result, Reply};
_ -> {result, Result} _ -> {result, Result}
end; end;
{result, default} -> {result, {_NodeId, _SubsByDepth, default}} ->
{result, Reply}; {result, Reply};
{result, Result} -> {result, {_NodeId, _SubsByDepth, Result}} ->
{result, Result}; {result, Result};
Error -> Error ->
%% in case we change transaction to sync_dirty... %% 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), AccessModel = get_option(Options, access_model),
SendLast = get_option(Options, send_last_published_item), SendLast = get_option(Options, send_last_published_item),
AllowedGroups = get_option(Options, roster_groups_allowed, []), AllowedGroups = get_option(Options, roster_groups_allowed, []),
{PresenceSubscription, RosterGroup} = Owners = node_owners_call(Type, NodeId),
case Host of {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, Subscriber, Owners, AccessModel, AllowedGroups),
{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,
if if
not SubscribeFeature -> not SubscribeFeature ->
%% Node does not support subscriptions %% Node does not support subscriptions
@ -1868,7 +1870,7 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
PluginPayload -> PluginPayload PluginPayload -> PluginPayload
end, end,
broadcast_publish_item(Host, Node, NodeId, Type, Options, Removed, ItemId, jlib:jid_tolower(Publisher), BroadcastPayload), 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 case Result of
default -> {result, Reply}; default -> {result, Reply};
_ -> {result, Result} _ -> {result, Result}
@ -1878,14 +1880,14 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
Type = TNode#pubsub_node.type, Type = TNode#pubsub_node.type,
Options = TNode#pubsub_node.options, Options = TNode#pubsub_node.options,
broadcast_retract_items(Host, Node, NodeId, Type, Options, Removed), 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, Reply};
{result, {TNode, {Result, Removed}}} -> {result, {TNode, {Result, Removed}}} ->
NodeId = TNode#pubsub_node.id, NodeId = TNode#pubsub_node.id,
Type = TNode#pubsub_node.type, Type = TNode#pubsub_node.type,
Options = TNode#pubsub_node.options, Options = TNode#pubsub_node.options,
broadcast_retract_items(Host, Node, NodeId, Type, Options, Removed), 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, Result};
{result, {_, default}} -> {result, {_, default}} ->
{result, Reply}; {result, Reply};
@ -2052,14 +2054,8 @@ get_items(Host, Node, From, SubId, SMaxItems, ItemIDs, RSM) ->
PersistentFeature = lists:member("persistent-items", Features), PersistentFeature = lists:member("persistent-items", Features),
AccessModel = get_option(Options, access_model), AccessModel = get_option(Options, access_model),
AllowedGroups = get_option(Options, roster_groups_allowed, []), AllowedGroups = get_option(Options, roster_groups_allowed, []),
{PresenceSubscription, RosterGroup} = Owners = node_owners_call(Type, NodeId),
case Host of {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups),
{OUser, OServer, _} ->
get_roster_info(OUser, OServer,
jlib:jid_tolower(From), AllowedGroups);
_ ->
{true, true}
end,
if if
not RetreiveFeature -> not RetreiveFeature ->
%% Item Retrieval Not Supported %% Item Retrieval Not Supported
@ -2110,6 +2106,17 @@ get_item(Host, Node, ItemId) ->
{result, {_, Items}} -> Items; {result, {_, Items}} -> Items;
Error -> Error Error -> Error
end. 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() %% @spec (Host, Node, NodeId, Type, LJID, Number) -> any()
%% Host = pubsubHost() %% 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 % 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 case node_action(Host, Type, get_last_items, [NodeId, LJID, 1]) of
{result, [LastItem]} -> {result, [LastItem]} ->
{ModifNow, ModifLjid} = LastItem#pubsub_item.modification, {ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
event_stanza_with_delay( event_stanza_with_delay(
[{xmlelement, "items", nodeAttr(Node), [{xmlelement, "items", nodeAttr(Node),
itemsEls([LastItem])}], ModifNow, ModifLjid); itemsEls([LastItem])}], ModifNow, ModifUSR);
_ -> _ ->
event_stanza( event_stanza(
[{xmlelement, "items", nodeAttr(Node), [{xmlelement, "items", nodeAttr(Node),
itemsEls([])}]) itemsEls([])}])
end; end;
LastItem -> LastItem ->
{ModifNow, ModifLjid} = LastItem#pubsub_item.modification, {ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
event_stanza_with_delay( event_stanza_with_delay(
[{xmlelement, "items", nodeAttr(Node), [{xmlelement, "items", nodeAttr(Node),
itemsEls([LastItem])}], ModifNow, ModifLjid) itemsEls([LastItem])}], ModifNow, ModifUSR)
end, end,
ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza); ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza);
send_items(Host, Node, NodeId, Type, LJID, Number) -> send_items(Host, Node, NodeId, Type, LJID, Number) ->
@ -2156,10 +2163,10 @@ send_items(Host, Node, NodeId, Type, LJID, Number) ->
end, end,
Stanza = case ToSend of Stanza = case ToSend of
[LastItem] -> [LastItem] ->
{ModifNow, ModifLjid} = LastItem#pubsub_item.modification, {ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
event_stanza_with_delay( event_stanza_with_delay(
[{xmlelement, "items", nodeAttr(Node), [{xmlelement, "items", nodeAttr(Node),
itemsEls(ToSend)}], ModifNow, ModifLjid); itemsEls(ToSend)}], ModifNow, ModifUSR);
_ -> _ ->
event_stanza( event_stanza(
[{xmlelement, "items", nodeAttr(Node), [{xmlelement, "items", nodeAttr(Node),
@ -2268,12 +2275,18 @@ set_affiliations(Host, Node, From, EntitiesEls) ->
{error, ?ERR_BAD_REQUEST}; {error, ?ERR_BAD_REQUEST};
_ -> _ ->
Action = fun(#pubsub_node{type = Type, id = NodeId}) -> 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 -> true ->
OwnerJID = jlib:make_jid(Owner),
FilteredEntities = case Owners of
[Owner] -> [E || E <- Entities, element(1, E) =/= OwnerJID];
_ -> Entities
end,
lists:foreach( lists:foreach(
fun({JID, Affiliation}) -> fun({JID, Affiliation}) ->
node_call(Type, set_affiliation, [NodeId, JID, Affiliation]) node_call(Type, set_affiliation, [NodeId, JID, Affiliation])
end, Entities), end, FilteredEntities),
{result, []}; {result, []};
_ -> _ ->
{error, ?ERR_FORBIDDEN} {error, ?ERR_FORBIDDEN}
@ -2576,8 +2589,24 @@ set_subscriptions(Host, Node, From, EntitiesEls) ->
end end
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) %% @spec (OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, SubscriberResource}, AllowedGroups)
%% -> {PresenceSubscription, RosterGroup} %% -> {PresenceSubscription, RosterGroup}
get_roster_info(_, _, {"", "", _}, _) ->
{false, false};
get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, AllowedGroups) -> get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, AllowedGroups) ->
{Subscription, Groups} = {Subscription, Groups} =
ejabberd_hooks:run_fold( ejabberd_hooks:run_fold(
@ -2589,7 +2618,9 @@ get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, A
RosterGroup = lists:any(fun(Group) -> RosterGroup = lists:any(fun(Group) ->
lists:member(Group, AllowedGroups) lists:member(Group, AllowedGroups)
end, Groups), 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 %% @spec (AffiliationStr) -> Affiliation
%% AffiliationStr = string() %% AffiliationStr = string()
@ -2677,15 +2708,66 @@ node_to_deliver(LJID, NodeOptions) ->
presence_can_deliver(LJID, PresenceDelivery). presence_can_deliver(LJID, PresenceDelivery).
presence_can_deliver(_, false) -> true; 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 case mnesia:dirty_match_object({session, '_', '_', {User, Server}, '_', '_'}) of
[] -> false; [] -> false;
Ss -> Ss ->
lists:foldl(fun({session, _, _, _, undefined, _}, Acc) -> Acc; lists:foldl(fun(_, true) -> true;
({session, _, _, _, _Priority, _}, _Acc) -> true ({session, _, _ , _, undefined, _}, _Acc) -> false;
({session, _, {_, _, R}, _, _Priority, _}, _Acc) ->
case Resource of
[] -> true;
R -> true;
_ -> false
end
end, false, Ss) end, false, Ss)
end. 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() %% @spec (Payload) -> int()
%% Payload = term() %% Payload = term()
%% @doc <p>Count occurence of XML elements in payload.</p> %% @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(Els) ->
event_stanza_withmoreels(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), 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).
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} {result, false}
end. 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) -> broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) ->
case get_option(NodeOptions, notify_config) of case get_option(NodeOptions, notify_config) of
true -> true ->
@ -2946,18 +3035,23 @@ subscribed_nodes_by_jid(NotifyType, SubsByDepth) ->
case is_to_deliver(LJID, NotifyType, Depth, NodeOptions, SubOptions) of case is_to_deliver(LJID, NotifyType, Depth, NodeOptions, SubOptions) of
true -> true ->
%% If is to deliver : %% 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, %% check if the JIDs co-accumulator contains the Subscription Jid,
false -> false ->
%% - if not, %% - if not,
%% - add the Jid to JIDs list co-accumulator ; %% - add the Jid to JIDs list co-accumulator ;
%% - create a tuple of the Jid, NodeId, and SubID (as list), %% - create a tuple of the Jid, NodeId, and SubID (as list),
%% and add the tuple to the Recipients list co-accumulator %% and add the tuple to the Recipients list co-accumulator
{[LJID | JIDs], [{LJID, NodeName, [SubID]} | Recipients]}; {[JIDToDeliver | JIDsAcc], [{JIDToDeliver, NodeName, [SubID]} | RecipientsAcc]};
true -> true ->
%% - if the JIDs co-accumulator contains the Jid %% - if the JIDs co-accumulator contains the Jid
%% get the tuple containing the Jid from the Recipient list co-accumulator %% 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 %% delete the tuple from the Recipients list
% v1 : Recipients1 = lists:keydelete(LJID, 1, Recipients), % v1 : Recipients1 = lists:keydelete(LJID, 1, Recipients),
% v2 : Recipients1 = lists:keyreplace(LJID, 1, Recipients, {LJID, NodeId1, [SubID | SubIDs]}), % 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.1 : {JIDs, lists:append(Recipients1, [{LJID, NodeId1, lists:append(SubIDs, [SubID])}])}
% v1.2 : {JIDs, [{LJID, NodeId1, [SubID | SubIDs]} | Recipients1]} % v1.2 : {JIDs, [{LJID, NodeId1, [SubID | SubIDs]} | Recipients1]}
% v2: {JIDs, 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; end;
false -> false ->
{JIDs, Recipients} {JIDs, Recipients}
end end
end, Acc, Subs) end, Acc, Subs)
end, end,
DepthsToDeliver = fun({Depth, SubsByNode}, Acc) -> DepthsToDeliver = fun({Depth, SubsByNode}, Acc1) ->
lists:foldl(fun({Node, Subs}, Acc2) -> lists:foldl(fun({Node, Subs}, Acc2) ->
NodesToDeliver(Depth, Node, Subs, Acc2) NodesToDeliver(Depth, Node, Subs, Acc2)
end, Acc, SubsByNode) end, Acc1, SubsByNode)
end, end,
{_, JIDSubs} = lists:foldl(DepthsToDeliver, {[], []}, SubsByDepth), {_, JIDSubs} = lists:foldl(DepthsToDeliver, {[], []}, SubsByDepth),
JIDSubs. JIDSubs.
@ -3327,18 +3423,18 @@ is_last_item_cache_enabled(Host) ->
_ -> false _ -> false
end. end.
set_cached_item({_, ServerHost, _}, NodeId, ItemId, Payload) -> set_cached_item({_, ServerHost, _}, NodeId, ItemId, Publisher, Payload) ->
set_cached_item(ServerHost, NodeId, ItemId, Payload); set_cached_item(ServerHost, NodeId, ItemId, Publisher, Payload);
set_cached_item(Host, NodeId, ItemId, Payload) -> set_cached_item(Host, NodeId, ItemId, Publisher, Payload) ->
case is_last_item_cache_enabled(Host) of 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 _ -> ok
end. end.
unset_cached_item({_, ServerHost, _}, NodeId) -> unset_cached_item({_, ServerHost, _}, NodeId) ->
unset_cached_item(ServerHost, NodeId); unset_cached_item(ServerHost, NodeId);
unset_cached_item(Host, NodeId) -> unset_cached_item(Host, NodeId) ->
case is_last_item_cache_enabled(Host) of 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 _ -> ok
end. end.
get_cached_item({_, ServerHost, _}, NodeId) -> get_cached_item({_, ServerHost, _}, NodeId) ->
@ -3346,9 +3442,10 @@ get_cached_item({_, ServerHost, _}, NodeId) ->
get_cached_item(Host, NodeId) -> get_cached_item(Host, NodeId) ->
case is_last_item_cache_enabled(Host) of case is_last_item_cache_enabled(Host) of
true -> true ->
case catch ets:lookup(gen_mod:get_module_proc(Host, last_items), NodeId) of case mnesia:dirty_read({pubsub_last_item, NodeId}) of
[{NodeId, {ItemId, Payload}}] -> [{pubsub_last_item, NodeId, ItemId, Creation, Payload}] ->
#pubsub_item{itemid = {ItemId, NodeId}, payload = Payload}; #pubsub_item{itemid = {ItemId, NodeId}, payload = Payload,
creation = Creation, modification = Creation};
_ -> _ ->
undefined undefined
end; end;
@ -3508,6 +3605,10 @@ transaction(Host, Node, Action, Trans) ->
Error Error
end end
end, Trans). 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(Host, Fun, Trans) ->
transaction_retry(Host, Fun, Trans, 2). transaction_retry(Host, Fun, Trans, 2).

View File

@ -379,7 +379,7 @@ unsubscribe_node(NodeId, Sender, Subscriber, SubId) ->
%% {error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; %% {error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
%% Requesting entity is not a subscriber %% Requesting entity is not a subscriber
Subscriptions == [] -> Subscriptions == [] ->
{error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST, "not-subscribed")}; {error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST_CANCEL, "not-subscribed")};
%% Subid supplied, so use that. %% Subid supplied, so use that.
SubIdExists -> SubIdExists ->
Sub = first_in_list(fun(S) -> Sub = first_in_list(fun(S) ->
@ -393,7 +393,7 @@ unsubscribe_node(NodeId, Sender, Subscriber, SubId) ->
delete_subscriptions(SubKey, NodeId, [S], SubState), delete_subscriptions(SubKey, NodeId, [S], SubState),
{result, default}; {result, default};
false -> false ->
{error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST, "not-subscribed")} {error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST_CANCEL, "not-subscribed")}
end; end;
%% Asking to remove all subscriptions to the given node %% Asking to remove all subscriptions to the given node
SubId == all -> SubId == all ->

View File

@ -379,7 +379,7 @@ unsubscribe_node(NodeId, Sender, Subscriber, SubId) ->
%% {error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; %% {error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
%% Requesting entity is not a subscriber %% Requesting entity is not a subscriber
Subscriptions == [] -> Subscriptions == [] ->
{error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST, "not-subscribed")}; {error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST_CANCEL, "not-subscribed")};
%% Subid supplied, so use that. %% Subid supplied, so use that.
SubIdExists -> SubIdExists ->
Sub = first_in_list(fun(S) -> Sub = first_in_list(fun(S) ->
@ -393,7 +393,7 @@ unsubscribe_node(NodeId, Sender, Subscriber, SubId) ->
delete_subscription(SubKey, NodeId, S, Affiliation, Subscriptions), delete_subscription(SubKey, NodeId, S, Affiliation, Subscriptions),
{result, default}; {result, default};
false -> false ->
{error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST, "not-subscribed")} {error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST_CANCEL, "not-subscribed")}
end; end;
%% Asking to remove all subscriptions to the given node %% Asking to remove all subscriptions to the given node
SubId == all -> SubId == all ->

View File

@ -135,3 +135,12 @@
%% <p>This is the format of the <tt>subscriptions</tt> table. The type of the %% <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> %% table is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
-record(pubsub_subscription, {subid, options}). -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}).

View File

@ -1,5 +1,5 @@
--- mod_pubsub.erl 2010-03-11 15:55:35.000000000 +0100 --- mod_pubsub.erl 2010-08-02 16:07:28.000000000 +0200
+++ mod_pubsub_odbc.erl 2010-03-11 15:59:40.000000000 +0100 +++ mod_pubsub_odbc.erl 2010-08-02 17:04:37.000000000 +0200
@@ -42,7 +42,7 @@ @@ -42,7 +42,7 @@
%%% 6.2.3.1, 6.2.3.5, and 6.3. For information on subscription leases see %%% 6.2.3.1, 6.2.3.5, and 6.3. For information on subscription leases see
%%% XEP-0060 section 12.18. %%% XEP-0060 section 12.18.
@ -22,7 +22,7 @@
%% exports for hooks %% exports for hooks
-export([presence_probe/3, -export([presence_probe/3,
@@ -104,7 +104,7 @@ @@ -103,7 +103,7 @@
string_to_affiliation/1, string_to_affiliation/1,
extended_error/2, extended_error/2,
extended_error/3, extended_error/3,
@ -31,7 +31,7 @@
]). ]).
%% API and gen_server callbacks %% API and gen_server callbacks
@@ -123,7 +123,7 @@ @@ -122,7 +122,7 @@
-export([send_loop/1 -export([send_loop/1
]). ]).
@ -40,7 +40,7 @@
-define(LOOPNAME, ejabberd_mod_pubsub_loop). -define(LOOPNAME, ejabberd_mod_pubsub_loop).
-define(PLUGIN_PREFIX, "node_"). -define(PLUGIN_PREFIX, "node_").
-define(TREE_PREFIX, "nodetree_"). -define(TREE_PREFIX, "nodetree_").
@@ -221,8 +221,6 @@ @@ -217,8 +217,6 @@
ok ok
end, end,
ejabberd_router:register_route(Host), ejabberd_router:register_route(Host),
@ -49,7 +49,7 @@
init_nodes(Host, ServerHost, NodeTree, Plugins), init_nodes(Host, ServerHost, NodeTree, Plugins),
State = #state{host = Host, State = #state{host = Host,
server_host = ServerHost, server_host = ServerHost,
@@ -281,207 +279,14 @@ @@ -277,207 +275,14 @@
init_nodes(Host, ServerHost, _NodeTree, Plugins) -> init_nodes(Host, ServerHost, _NodeTree, Plugins) ->
%% TODO, this call should be done plugin side %% TODO, this call should be done plugin side
@ -260,7 +260,7 @@
send_loop(State) -> send_loop(State) ->
receive receive
{presence, JID, Pid} -> {presence, JID, Pid} ->
@@ -492,17 +297,15 @@ @@ -488,17 +293,15 @@
%% for each node From is subscribed to %% for each node From is subscribed to
%% and if the node is so configured, send the last published item to From %% and if the node is so configured, send the last published item to From
lists:foreach(fun(PType) -> lists:foreach(fun(PType) ->
@ -284,7 +284,54 @@
true -> true ->
% resource not concerned about that subscription % resource not concerned about that subscription
ok 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) -> lists:foreach(fun(PType) ->
{result, Subscriptions} = node_action(Host, PType, get_entity_subscriptions, [Host, Entity]), {result, Subscriptions} = node_action(Host, PType, get_entity_subscriptions, [Host, Entity]),
lists:foreach(fun lists:foreach(fun
@ -297,7 +344,7 @@
true -> true ->
node_action(Host, PType, unsubscribe_node, [NodeId, Entity, JID, all]); node_action(Host, PType, unsubscribe_node, [NodeId, Entity, JID, all]);
false -> false ->
@@ -921,7 +724,8 @@ @@ -952,7 +759,8 @@
sub_el = SubEl} = IQ -> sub_el = SubEl} = IQ ->
{xmlelement, _, QAttrs, _} = SubEl, {xmlelement, _, QAttrs, _} = SubEl,
Node = xml:get_attr_s("node", QAttrs), Node = xml:get_attr_s("node", QAttrs),
@ -307,7 +354,7 @@
{result, IQRes} -> {result, IQRes} ->
jlib:iq_to_xml( jlib:iq_to_xml(
IQ#iq{type = result, IQ#iq{type = result,
@@ -1034,7 +838,7 @@ @@ -1065,7 +873,7 @@
[] -> [] ->
["leaf"]; %% No sub-nodes: it's a leaf node ["leaf"]; %% No sub-nodes: it's a leaf node
_ -> _ ->
@ -316,7 +363,7 @@
{result, []} -> ["collection"]; {result, []} -> ["collection"];
{result, _} -> ["leaf", "collection"]; {result, _} -> ["leaf", "collection"];
_ -> [] _ -> []
@@ -1050,8 +854,9 @@ @@ -1081,8 +889,9 @@
[]; [];
true -> true ->
[{xmlelement, "feature", [{"var", ?NS_PUBSUB}], []} | [{xmlelement, "feature", [{"var", ?NS_PUBSUB}], []} |
@ -328,7 +375,7 @@
end, features(Type))] end, features(Type))]
end, end,
%% TODO: add meta-data info (spec section 5.4) %% 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_PUBSUB}], []},
{xmlelement, "feature", [{"var", ?NS_COMMANDS}], []}, {xmlelement, "feature", [{"var", ?NS_COMMANDS}], []},
{xmlelement, "feature", [{"var", ?NS_VCARD}], []}] ++ {xmlelement, "feature", [{"var", ?NS_VCARD}], []}] ++
@ -340,7 +387,7 @@
end, features(Host, Node))}; end, features(Host, Node))};
<<?NS_COMMANDS>> -> <<?NS_COMMANDS>> ->
command_disco_info(Host, Node, From); command_disco_info(Host, Node, From);
@@ -1091,7 +897,7 @@ @@ -1122,7 +932,7 @@
node_disco_info(Host, Node, From) node_disco_info(Host, Node, From)
end. end.
@ -349,7 +396,7 @@
case tree_action(Host, get_subnodes, [Host, <<>>, From]) of case tree_action(Host, get_subnodes, [Host, <<>>, From]) of
Nodes when is_list(Nodes) -> Nodes when is_list(Nodes) ->
{result, lists:map( {result, lists:map(
@@ -1108,14 +914,14 @@ @@ -1139,23 +949,24 @@
Other -> Other ->
Other Other
end; end;
@ -367,21 +414,20 @@
case string:tokens(Item, "!") of case string:tokens(Item, "!") of
[_SNode, _ItemID] -> [_SNode, _ItemID] ->
{result, []}; {result, []};
@@ -1123,10 +929,10 @@ [SNode] ->
Node = string_to_node(SNode), Node = string_to_node(SNode),
Action = - Action = fun(#pubsub_node{id = Idx, type = Type, options = Options, owners = Owners}) ->
fun(#pubsub_node{type = Type, id = NodeId}) -> - NodeItems = case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
- % TODO call get_items/6 instead for access control (EJAB-1033) + Action = fun(#pubsub_node{id = Idx, type = Type, options = Options}) ->
- NodeItems = case node_call(Type, get_items, [NodeId, From]) of + Owners = node_owners_call(Type, Idx),
+ %% TODO call get_items/6 instead for access control (EJAB-1033) + {NodeItems, RsmOut} = case get_allowed_items_call(Host, Idx, From, Type, Options, Owners, RSM) of
+ {NodeItems, RsmOut} = case node_call(Type, get_items, [NodeId, From, RSM]) of {result, R} -> R;
{result, I} -> I;
- _ -> [] - _ -> []
+ _ -> {[], none} + _ -> {[], none}
end, end,
Nodes = lists:map( Nodes = lists:map(
fun(#pubsub_node{nodeid = {_, SubNode}, options = Options}) -> fun(#pubsub_node{nodeid = {_, SubNode}, options = SubOptions}) ->
@@ -1144,7 +950,7 @@ @@ -1173,7 +984,7 @@
{result, Name} = node_call(Type, get_item_name, [Host, Node, RN]), {result, Name} = node_call(Type, get_item_name, [Host, Node, RN]),
{xmlelement, "item", [{"jid", Host}, {"name", Name}], []} {xmlelement, "item", [{"jid", Host}, {"name", Name}], []}
end, NodeItems), end, NodeItems),
@ -390,7 +436,7 @@
end, end,
case transaction(Host, Node, Action, sync_dirty) of case transaction(Host, Node, Action, sync_dirty) of
{result, {_, Result}} -> {result, Result}; {result, {_, Result}} -> {result, Result};
@@ -1273,7 +1079,8 @@ @@ -1284,7 +1095,8 @@
(_, Acc) -> (_, Acc) ->
Acc Acc
end, [], xml:remove_cdata(Els)), end, [], xml:remove_cdata(Els)),
@ -400,7 +446,7 @@
{get, "subscriptions"} -> {get, "subscriptions"} ->
get_subscriptions(Host, Node, From, Plugins); get_subscriptions(Host, Node, From, Plugins);
{get, "affiliations"} -> {get, "affiliations"} ->
@@ -1296,7 +1103,9 @@ @@ -1307,7 +1119,9 @@
iq_pubsub_owner(Host, ServerHost, From, IQType, SubEl, Lang) -> iq_pubsub_owner(Host, ServerHost, From, IQType, SubEl, Lang) ->
{xmlelement, _, _, SubEls} = SubEl, {xmlelement, _, _, SubEls} = SubEl,
@ -411,7 +457,7 @@
case Action of case Action of
[{xmlelement, Name, Attrs, Els}] -> [{xmlelement, Name, Attrs, Els}] ->
Node = string_to_node(xml:get_attr_s("node", Attrs)), Node = string_to_node(xml:get_attr_s("node", Attrs)),
@@ -1426,7 +1235,8 @@ @@ -1437,7 +1251,8 @@
_ -> [] _ -> []
end end
end, end,
@ -421,7 +467,7 @@
sync_dirty) of sync_dirty) of
{result, Res} -> Res; {result, Res} -> Res;
Err -> Err Err -> Err
@@ -1465,7 +1275,7 @@ @@ -1476,7 +1291,7 @@
%%% authorization handling %%% authorization handling
@ -430,7 +476,7 @@
Lang = "en", %% TODO fix Lang = "en", %% TODO fix
Stanza = {xmlelement, "message", Stanza = {xmlelement, "message",
[], [],
@@ -1494,7 +1304,7 @@ @@ -1505,7 +1320,7 @@
[{xmlelement, "value", [], [{xmlcdata, "false"}]}]}]}]}, [{xmlelement, "value", [], [{xmlcdata, "false"}]}]}]}]},
lists:foreach(fun(Owner) -> lists:foreach(fun(Owner) ->
ejabberd_router:route(service_jid(Host), jlib:make_jid(Owner), Stanza) ejabberd_router:route(service_jid(Host), jlib:make_jid(Owner), Stanza)
@ -439,7 +485,7 @@
find_authorization_response(Packet) -> find_authorization_response(Packet) ->
{xmlelement, _Name, _Attrs, Els} = Packet, {xmlelement, _Name, _Attrs, Els} = Packet,
@@ -1558,8 +1368,8 @@ @@ -1569,8 +1384,8 @@
"true" -> true; "true" -> true;
_ -> false _ -> false
end, end,
@ -450,16 +496,16 @@
{result, Subscriptions} = node_call(Type, get_subscriptions, [NodeId, Subscriber]), {result, Subscriptions} = node_call(Type, get_subscriptions, [NodeId, Subscriber]),
if if
not IsApprover -> not IsApprover ->
@@ -1750,7 +1560,7 @@ @@ -1769,7 +1584,7 @@
Reply = [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}], Reply = [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}],
[{xmlelement, "create", nodeAttr(Node), [{xmlelement, "create", nodeAttr(Node),
[]}]}], []}]}],
- case transaction(CreateNode, transaction) of - case transaction(CreateNode, transaction) of
+ case transaction(Host, CreateNode, transaction) of + case transaction(Host, CreateNode, transaction) of
{result, {Result, broadcast}} -> {result, {NodeId, SubsByDepth, {Result, broadcast}}} ->
%%Lang = "en", %% TODO: fix broadcast_created_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth),
%%OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), case Result of
@@ -1858,7 +1668,7 @@ @@ -1872,7 +1687,7 @@
%%<li>The node does not exist.</li> %%<li>The node does not exist.</li>
%%</ul> %%</ul>
subscribe_node(Host, Node, From, JID, Configuration) -> subscribe_node(Host, Node, From, JID, Configuration) ->
@ -468,33 +514,24 @@
{result, GoodSubOpts} -> GoodSubOpts; {result, GoodSubOpts} -> GoodSubOpts;
_ -> invalid _ -> invalid
end, end,
@@ -1866,7 +1676,7 @@ @@ -1880,7 +1695,7 @@
error -> {"", "", ""}; error -> {"", "", ""};
J -> jlib:jid_tolower(J) J -> jlib:jid_tolower(J)
end, 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}) -> + Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId}) ->
Features = features(Type), Features = features(Type),
SubscribeFeature = lists:member("subscribe", Features), SubscribeFeature = lists:member("subscribe", Features),
OptionsFeature = lists:member("subscription-options", Features), OptionsFeature = lists:member("subscription-options", Features),
@@ -1885,9 +1695,13 @@ @@ -1889,6 +1704,7 @@
{"", "", ""} -> AccessModel = get_option(Options, access_model),
{false, false}; SendLast = get_option(Options, send_last_published_item),
_ -> AllowedGroups = get_option(Options, roster_groups_allowed, []),
- {OU, OS, _} = Owner, + Owners = node_owners_call(Type, NodeId),
- get_roster_info(OU, OS, {PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, Subscriber, Owners, AccessModel, AllowedGroups),
- Subscriber, AllowedGroups)
+ case node_owners_call(Type, NodeId) of
+ [{OU, OS, _}|_] ->
+ get_roster_info(OU, OS,
+ Subscriber, AllowedGroups);
+ _ ->
+ {false, false}
+ end
end
end,
if if
@@ -2218,7 +2032,7 @@ not SubscribeFeature ->
@@ -2218,7 +2034,7 @@
%% <p>The permission are not checked in this function.</p> %% <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 %% @todo We probably need to check that the user doing the query has the right
%% to read the items. %% to read the items.
@ -503,7 +540,22 @@
MaxItems = MaxItems =
if if
SMaxItems == "" -> get_max_items_node(Host); 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, node_call(Type, get_items,
[NodeId, From, [NodeId, From,
AccessModel, PresenceSubscription, RosterGroup, AccessModel, PresenceSubscription, RosterGroup,
@ -517,7 +569,7 @@
SendItems = case ItemIDs of SendItems = case ItemIDs of
[] -> [] ->
Items; Items;
@@ -2274,7 +2088,8 @@ @@ -2267,7 +2084,8 @@
%% number of items sent to MaxItems: %% number of items sent to MaxItems:
{result, [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}], {result, [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}],
[{xmlelement, "items", nodeAttr(Node), [{xmlelement, "items", nodeAttr(Node),
@ -527,7 +579,24 @@
Error -> Error ->
Error Error
end 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> %% @doc <p>Resend the items of a node to the user.</p>
%% @todo use cache-last-item feature %% @todo use cache-last-item feature
send_items(Host, Node, NodeId, Type, LJID, last) -> 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 + % 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 + case node_action(Host, Type, get_last_items, [NodeId, LJID, 1]) of
+ {result, [LastItem]} -> + {result, [LastItem]} ->
+ {ModifNow, ModifLjid} = LastItem#pubsub_item.modification, + {ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
+ event_stanza_with_delay( + event_stanza_with_delay(
+ [{xmlelement, "items", nodeAttr(Node), + [{xmlelement, "items", nodeAttr(Node),
+ itemsEls([LastItem])}], ModifNow, ModifLjid); + itemsEls([LastItem])}], ModifNow, ModifUSR);
+ _ -> + _ ->
+ event_stanza( + event_stanza(
+ [{xmlelement, "items", nodeAttr(Node), + [{xmlelement, "items", nodeAttr(Node),
+ itemsEls([])}]) + itemsEls([])}])
+ end; + end;
LastItem -> LastItem ->
{ModifNow, ModifLjid} = LastItem#pubsub_item.modification, {ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
- Stanza = event_stanza_with_delay( - Stanza = event_stanza_with_delay(
+ event_stanza_with_delay( + event_stanza_with_delay(
[{xmlelement, "items", nodeAttr(Node), [{xmlelement, "items", nodeAttr(Node),
- itemsEls([LastItem])}], ModifNow, ModifLjid), - itemsEls([LastItem])}], ModifNow, ModifUSR),
- ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza) - ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza)
- end; - end;
+ itemsEls([LastItem])}], ModifNow, ModifLjid) + itemsEls([LastItem])}], ModifNow, ModifUSR)
+ end, + end,
+ ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza); + ejabberd_router:route(service_jid(Host), jlib:make_jid(LJID), Stanza);
send_items(Host, Node, NodeId, Type, LJID, Number) -> send_items(Host, Node, NodeId, Type, LJID, Number) ->
ToSend = case node_action(Host, Type, get_items, [NodeId, LJID]) of ToSend = case node_action(Host, Type, get_items, [NodeId, LJID]) of
{result, []} -> {result, []} ->
@@ -2441,29 +2267,12 @@ @@ -2440,7 +2274,8 @@
error -> error ->
{error, ?ERR_BAD_REQUEST}; {error, ?ERR_BAD_REQUEST};
_ -> _ ->
- Action = fun(#pubsub_node{owners = Owners, type = Type, id = NodeId}=N) -> - 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}) -> + 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 -> true ->
OwnerJID = jlib:make_jid(Owner),
@@ -2450,24 +2285,7 @@
end,
lists:foreach( lists:foreach(
fun({JID, Affiliation}) -> fun({JID, Affiliation}) ->
- node_call(Type, set_affiliation, [NodeId, JID, Affiliation]), - node_call(Type, set_affiliation, [NodeId, JID, Affiliation]),
@ -591,10 +663,10 @@
- ok - ok
- end - end
+ node_call(Type, set_affiliation, [NodeId, JID, Affiliation]) + node_call(Type, set_affiliation, [NodeId, JID, Affiliation])
end, Entities), end, FilteredEntities),
{result, []}; {result, []};
_ -> _ ->
@@ -2516,11 +2325,11 @@ @@ -2520,11 +2338,11 @@
end. end.
read_sub(Subscriber, Node, NodeID, SubID, Lang) -> read_sub(Subscriber, Node, NodeID, SubID, Lang) ->
@ -608,7 +680,7 @@
OptionsEl = {xmlelement, "options", [{"jid", jlib:jid_to_string(Subscriber)}, OptionsEl = {xmlelement, "options", [{"jid", jlib:jid_to_string(Subscriber)},
{"subid", SubID}|nodeAttr(Node)], {"subid", SubID}|nodeAttr(Node)],
[XdataEl]}, [XdataEl]},
@@ -2546,7 +2355,7 @@ @@ -2550,7 +2368,7 @@
end. end.
set_options_helper(Configuration, JID, NodeID, SubID, Type) -> set_options_helper(Configuration, JID, NodeID, SubID, Type) ->
@ -617,7 +689,7 @@
{result, GoodSubOpts} -> GoodSubOpts; {result, GoodSubOpts} -> GoodSubOpts;
_ -> invalid _ -> invalid
end, end,
@@ -2575,7 +2384,7 @@ @@ -2579,7 +2397,7 @@
write_sub(_Subscriber, _NodeID, _SubID, invalid) -> write_sub(_Subscriber, _NodeID, _SubID, invalid) ->
{error, extended_error(?ERR_BAD_REQUEST, "invalid-options")}; {error, extended_error(?ERR_BAD_REQUEST, "invalid-options")};
write_sub(Subscriber, NodeID, SubID, Options) -> write_sub(Subscriber, NodeID, SubID, Options) ->
@ -626,7 +698,7 @@
{error, notfound} -> {error, notfound} ->
{error, extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; {error, extended_error(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
{result, _} -> {result, _} ->
@@ -2743,8 +2552,8 @@ @@ -2747,8 +2565,8 @@
{"subscription", subscription_to_string(Sub)} | nodeAttr(Node)], []}]}]}, {"subscription", subscription_to_string(Sub)} | nodeAttr(Node)], []}]}]},
ejabberd_router:route(service_jid(Host), jlib:make_jid(JID), Stanza) ejabberd_router:route(service_jid(Host), jlib:make_jid(JID), Stanza)
end, end,
@ -637,7 +709,7 @@
true -> true ->
Result = lists:foldl(fun({JID, Subscription, SubId}, Acc) -> Result = lists:foldl(fun({JID, Subscription, SubId}, Acc) ->
@@ -3023,7 +2832,7 @@ @@ -3103,7 +2921,7 @@
{Depth, [{N, get_node_subs(N)} || N <- Nodes]} {Depth, [{N, get_node_subs(N)} || N <- Nodes]}
end, tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]))} end, tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]))}
end, end,
@ -646,7 +718,7 @@
{result, CollSubs} -> CollSubs; {result, CollSubs} -> CollSubs;
_ -> [] _ -> []
end. end.
@@ -3037,9 +2846,9 @@ @@ -3117,9 +2935,9 @@
get_options_for_subs(NodeID, Subs) -> get_options_for_subs(NodeID, Subs) ->
lists:foldl(fun({JID, subscribed, SubID}, Acc) -> lists:foldl(fun({JID, subscribed, SubID}, Acc) ->
@ -658,7 +730,7 @@
_ -> Acc _ -> Acc
end; end;
(_, Acc) -> (_, Acc) ->
@@ -3236,6 +3045,30 @@ @@ -3323,6 +3141,30 @@
Result Result
end. end.
@ -689,7 +761,7 @@
%% @spec (Host, Options) -> MaxItems %% @spec (Host, Options) -> MaxItems
%% Host = host() %% Host = host()
%% Options = [Option] %% Options = [Option]
@@ -3631,7 +3464,13 @@ @@ -3719,7 +3561,13 @@
tree_action(Host, Function, Args) -> tree_action(Host, Function, Args) ->
?DEBUG("tree_action ~p ~p ~p",[Host,Function,Args]), ?DEBUG("tree_action ~p ~p ~p",[Host,Function,Args]),
Fun = fun() -> tree_call(Host, Function, Args) end, Fun = fun() -> tree_call(Host, Function, Args) end,
@ -704,7 +776,7 @@
%% @doc <p>node plugin call.</p> %% @doc <p>node plugin call.</p>
node_call(Type, Function, Args) -> node_call(Type, Function, Args) ->
@@ -3651,13 +3490,13 @@ @@ -3739,13 +3587,13 @@
node_action(Host, Type, Function, Args) -> node_action(Host, Type, Function, Args) ->
?DEBUG("node_action ~p ~p ~p ~p",[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 case tree_call(Host, get_node, [Host, Node]) of
N when is_record(N, pubsub_node) -> N when is_record(N, pubsub_node) ->
case Action(N) of case Action(N) of
@@ -3670,8 +3509,14 @@ @@ -3757,13 +3605,19 @@
Error
end end
end, Trans). 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) -> -transaction(Fun, Trans) ->
- case catch mnesia:Trans(Fun) of - case catch mnesia:Trans(Fun) of
@ -737,7 +816,7 @@
{result, Result} -> {result, Result}; {result, Result} -> {result, Result};
{error, Error} -> {error, Error}; {error, Error} -> {error, Error};
{atomic, {result, Result}} -> {result, Result}; {atomic, {result, Result}} -> {result, Result};
@@ -3679,6 +3524,15 @@ @@ -3771,6 +3625,15 @@
{aborted, Reason} -> {aborted, Reason} ->
?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]), ?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]),
{error, ?ERR_INTERNAL_SERVER_ERROR}; {error, ?ERR_INTERNAL_SERVER_ERROR};
@ -753,7 +832,7 @@
{'EXIT', Reason} -> {'EXIT', Reason} ->
?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]), ?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]),
{error, ?ERR_INTERNAL_SERVER_ERROR}; {error, ?ERR_INTERNAL_SERVER_ERROR};
@@ -3687,6 +3541,17 @@ @@ -3779,6 +3642,17 @@
{error, ?ERR_INTERNAL_SERVER_ERROR} {error, ?ERR_INTERNAL_SERVER_ERROR}
end. end.

View File

@ -31,7 +31,7 @@
-export([start/2, -export([start/2,
stop/1, stop/1,
stream_feature_register/1, stream_feature_register/2,
unauthenticated_iq_register/4, unauthenticated_iq_register/4,
try_register/5, try_register/5,
process_iq/3]). process_iq/3]).
@ -65,7 +65,7 @@ stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_REGISTER). gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_REGISTER).
stream_feature_register(Acc) -> stream_feature_register(Acc, _Host) ->
[{xmlelement, "register", [{xmlelement, "register",
[{"xmlns", ?NS_FEATURE_IQREGISTER}], []} | Acc]. [{"xmlns", ?NS_FEATURE_IQREGISTER}], []} | Acc].
@ -187,6 +187,18 @@ process_iq(From, To,
sub_el = [SubEl, ?ERR_BAD_REQUEST]} sub_el = [SubEl, ?ERR_BAD_REQUEST]}
end; end;
get -> 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, IQ#iq{type = result,
sub_el = [{xmlelement, sub_el = [{xmlelement,
"query", "query",
@ -197,8 +209,9 @@ process_iq(From, To,
Lang, Lang,
"Choose a username and password " "Choose a username and password "
"to register with this server")}]}, "to register with this server")}]},
{xmlelement, "username", [], []}, {xmlelement, "username", [], UsernameSubels},
{xmlelement, "password", [], []}]}]} {xmlelement, "password", [], []}
| QuerySubels]}]}
end. end.
%% @doc Try to change password and return IQ response %% @doc Try to change password and return IQ response

View File

@ -655,6 +655,7 @@ push_user_to_members(User, Server, Subscription) ->
UserGroups = get_user_displayed_groups(LUser, LServer, GroupsOpts), UserGroups = get_user_displayed_groups(LUser, LServer, GroupsOpts),
lists:foreach( lists:foreach(
fun(Group) -> fun(Group) ->
remove_user_from_group(LServer, {LUser, LServer}, Group),
GroupOpts = proplists:get_value(Group, GroupsOpts, []), GroupOpts = proplists:get_value(Group, GroupsOpts, []),
GroupName = proplists:get_value(name, GroupOpts, Group), GroupName = proplists:get_value(name, GroupOpts, Group),
lists:foreach( lists:foreach(

View File

@ -57,10 +57,7 @@ process_local_iq(#jid{user = User, server = Server, resource = Resource}, _To,
get_ip({User, Server, Resource}, IQ); get_ip({User, Server, Resource}, IQ);
process_local_iq(_From, _To, #iq{type = 'set', sub_el = SubEl} = IQ) -> process_local_iq(_From, _To, #iq{type = 'set', sub_el = SubEl} = IQ) ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; 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]}.
process_sm_iq(#jid{user = User, server = Server, resource = Resource}, 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]}; IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]};
process_sm_iq(_From, _To, #iq{type = 'set', sub_el = SubEl} = IQ) -> process_sm_iq(_From, _To, #iq{type = 'set', sub_el = SubEl} = IQ) ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; 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]}.
get_ip({User, Server, Resource}, get_ip({User, Server, Resource},
#iq{sub_el = {xmlelement, Name, Attrs, _} = SubEl} = IQ) -> #iq{sub_el = {xmlelement, Name, Attrs, _} = SubEl} = IQ) ->

View File

@ -267,7 +267,8 @@ set_vcard(User, LServer, VCARD) ->
orgunit = OrgUnit, lorgunit = LOrgUnit orgunit = OrgUnit, lorgunit = LOrgUnit
}) })
end, end,
mnesia:transaction(F) mnesia:transaction(F),
ejabberd_hooks:run(vcard_set, LServer, [LUser, LServer, VCARD])
end. end.
-define(TLFIELD(Type, Label, Var), -define(TLFIELD(Type, Label, Var),

View File

@ -62,7 +62,7 @@
servers, servers,
backups, backups,
port, port,
encrypt, tls_options,
dn, dn,
base, base,
password, password,
@ -181,7 +181,7 @@ init([Host, Opts]) ->
State#state.port, State#state.port,
State#state.dn, State#state.dn,
State#state.password, State#state.password,
State#state.encrypt), State#state.tls_options),
case State#state.search of case State#state.search of
true -> true ->
ejabberd_router:register_route(State#state.myhost); ejabberd_router:register_route(State#state.myhost);
@ -686,6 +686,11 @@ parse_options(Host, Opts) ->
ejabberd_config:get_local_option({ldap_encrypt, Host}); ejabberd_config:get_local_option({ldap_encrypt, Host});
E -> E E -> E
end, 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 LDAPPortTemp = case gen_mod:get_opt(ldap_port, Opts, undefined) of
undefined -> undefined ->
ejabberd_config:get_local_option({ldap_port, Host}); ejabberd_config:get_local_option({ldap_port, Host});
@ -766,7 +771,8 @@ parse_options(Host, Opts) ->
servers = LDAPServers, servers = LDAPServers,
backups = LDAPBackups, backups = LDAPBackups,
port = LDAPPort, port = LDAPPort,
encrypt = LDAPEncrypt, tls_options = [{encrypt, LDAPEncrypt},
{tls_verify, LDAPTLSVerify}],
dn = RootDN, dn = RootDN,
base = LDAPBase, base = LDAPBase,
password = Password, password = Password,

View File

@ -216,7 +216,7 @@ set_vcard(User, LServer, VCARD) ->
Username = ejabberd_odbc:escape(User), Username = ejabberd_odbc:escape(User),
LUsername = ejabberd_odbc:escape(LUser), LUsername = ejabberd_odbc:escape(LUser),
SVCARD = ejabberd_odbc:escape( SVCARD = ejabberd_odbc:escape(
lists:flatten(xml:element_to_string(VCARD))), xml:element_to_binary(VCARD)),
SFN = ejabberd_odbc:escape(FN), SFN = ejabberd_odbc:escape(FN),
SLFN = ejabberd_odbc:escape(LFN), SLFN = ejabberd_odbc:escape(LFN),
@ -247,7 +247,9 @@ set_vcard(User, LServer, VCARD) ->
SLLocality, SLMiddle, SLNickname, SLLocality, SLMiddle, SLNickname,
SLOrgName, SLOrgUnit, SLocality, SLOrgName, SLOrgUnit, SLocality,
SMiddle, SNickname, SOrgName, SMiddle, SNickname, SOrgName,
SOrgUnit, SVCARD, Username) SOrgUnit, SVCARD, Username),
ejabberd_hooks:run(vcard_set, LServer, [LUser, LServer, VCARD])
end. end.
-define(TLFIELD(Type, Label, Var), -define(TLFIELD(Type, Label, Var),

121
src/mod_vcard_xupdate.erl Normal file
View 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}.

View File

@ -36,6 +36,10 @@
{"Change Password","Canviar password"}. {"Change Password","Canviar password"}.
{"Change User Password","Canviar Password d'Usuari"}. {"Change User Password","Canviar Password d'Usuari"}.
{"Chatroom configuration modified","Configuració de la sala de xat modificada"}. {"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"}. {"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 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"}. {"Choose modules to stop","Selecciona mòduls a detindre"}.
@ -246,6 +250,7 @@
{"Protocol","Protocol"}. {"Protocol","Protocol"}.
{"Publish-Subscribe","Publicar-subscriure't"}. {"Publish-Subscribe","Publicar-subscriure't"}.
{"PubSub subscriber request","Petició de subscriptor PubSub"}. {"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"}. {"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 and disc copy","Còpia en RAM i disc"}.
{"RAM copy","Còpia en RAM"}. {"RAM copy","Còpia en RAM"}.
@ -298,6 +303,7 @@
{"Shut Down Service","Apager el Servei"}. {"Shut Down Service","Apager el Servei"}.
{"~s invites you to the room ~s","~s et convida a la sala ~s"}. {"~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 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"}. {"Specify the publisher model","Especificar el model del publicant"}.
{"~s's Offline Messages Queue","~s's cua de missatges offline"}. {"~s's Offline Messages Queue","~s's cua de missatges offline"}.
{"Start","Iniciar"}. {"Start","Iniciar"}.

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,10 @@
{"Change Password","Změnit heslo"}. {"Change Password","Změnit heslo"}.
{"Change User Password","Změnit heslo uživatele"}. {"Change User Password","Změnit heslo uživatele"}.
{"Chatroom configuration modified","Nastavení diskuzní místnosti bylo změněno"}. {"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"}. {"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 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"}. {"Choose modules to stop","Vyberte moduly, které mají být zastaveny"}.
@ -246,6 +250,7 @@
{"Protocol","Protokol"}. {"Protocol","Protokol"}.
{"Publish-Subscribe","Publish-Subscribe"}. {"Publish-Subscribe","Publish-Subscribe"}.
{"PubSub subscriber request","Žádost odběratele PubSub"}. {"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é"}. {"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 and disc copy","Kopie RAM a disku"}.
{"RAM copy","Kopie RAM"}. {"RAM copy","Kopie RAM"}.
@ -298,6 +303,7 @@
{"Shut Down Service","Vypnout službu"}. {"Shut Down Service","Vypnout službu"}.
{"~s invites you to the room ~s","~s vás zve do místnosti ~s"}. {"~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 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í"}. {"Specify the publisher model","Specifikovat model pro publikování"}.
{"~s's Offline Messages Queue","Fronta offline zpráv uživatele ~s"}. {"~s's Offline Messages Queue","Fronta offline zpráv uživatele ~s"}.
{"Start Modules at ","Spustit moduly na "}. {"Start Modules at ","Spustit moduly na "}.

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,20 @@
{"Access Configuration","Zugangskonfiguration"}. {"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 Control Lists","Zugangskontroll-Listen (ACL)"}. {"Access Control Lists","Zugangskontroll-Listen (ACL)"}.
{"Access denied by service policy","Zugang aufgrund der Dienstrichtlinien verweigert"}. {"Access denied by service policy","Zugang aufgrund der Dienstrichtlinien verweigert"}.
{"Access rules","Zugangsregeln"}. {"Access rules","Zugangsregeln"}.
{"Access Rules","Zugangsregeln"}. {"Access Rules","Zugangsregeln"}.
{"Action on user","Aktion auf Benutzer"}. {"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 New","Neuen hinzufügen"}.
{"Add User","Benutzer hinzufügen"}. {"Add User","Benutzer hinzufügen"}.
{"Administration of ","Administration von "}. {"Administration of ","Administration von "}.
{"Administration","Verwaltung"}. {"Administration","Verwaltung"}.
{"Administrator privileges required","Administratorenrechte benötigt"}. {"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"}. {"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 change the subject","Erlaube Benutzern das Thema zu ändern"}.
{"Allow users to query other users","Erlaube Benutzern andere Benutzer abzufragen"}. {"Allow users to query other users","Erlaube Benutzern andere Benutzer abzufragen"}.
{"Allow users to send invites","Erlaube Benutzern Einladungen zu senden"}. {"Allow users to send invites","Erlaube Benutzern Einladungen zu senden"}.
@ -28,85 +28,89 @@
{"April","April"}. {"April","April"}.
{"August","August"}. {"August","August"}.
{"Backup","Datensicherung"}. {"Backup","Datensicherung"}.
{"Backup Management","Datensicherungsmanagement"}. {"Backup Management","Datensicherungsverwaltung"}.
{"Backup of ","Sicherung von "}. {"Backup of ","Sicherung von "}.
{"Backup to File at ","Datensicherung in die Datei "}. {"Backup to File at ","Datensicherung in die Datei "}.
{"Bad format","Ungültiges Format"}. {"Bad format","Ungültiges Format"}.
{"Birthday","Geburtsdatum"}. {"Birthday","Geburtsdatum"}.
{"Change Password","Passwort ändern"}. {"Change Password","Passwort ändern"}.
{"Change User Password","Benutzer Passwort ändern"}. {"Change User Password","Benutzer-Passwort ändern"}.
{"Chatroom configuration modified","Chatraum Konfiguration geändert"}. {"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"}. {"Chatrooms","Chaträume"}.
{"Choose a username and password to register with this server","Wählen sie zum Registrieren einen Benutzernamen und ein Passwort"}. {"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 modules to stop","Wähle zu stoppende Module"}.
{"Choose storage type of tables","Wähle Speichertyp der Tabellen"}. {"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"}. {"City","Stadt"}.
{"Commands","Befehle"}. {"Commands","Befehle"}.
{"Conference room does not exist","Konferenzraum existiert nicht"}. {"Conference room does not exist","Konferenzraum existiert nicht"}.
{"Configuration","Konfiguration"}. {"Configuration","Konfiguration"}.
{"Configuration of room ~s","Konfiguration für Raum ~s"}. {"Configuration of room ~s","Konfiguration für Raum ~s"}.
{"Connected Resources:","Verbundene Resourcen"}. {"Connected Resources:","Verbundene Ressourcen"}.
{"Connections parameters","Verbindungsparameter"}. {"Connections parameters","Verbindungsparameter"}.
{"Country","Land"}. {"Country","Land"}.
{"CPU Time:","CPU Zeit:"}. {"CPU Time:","CPU-Zeit:"}.
{"Database","Datenbank"}. {"Database","Datenbank"}.
{"Database Tables at ","Datenbank Tabellen bei "}. {"Database Tables at ","Datenbanktabellen auf "}.
{"Database Tables Configuration at ","Datenbank Tabellen Konfiguration bei "}. {"Database Tables Configuration at ","Datenbanktabellen-Konfiguration auf "}.
{"December","Dezember"}. {"December","Dezember"}.
{"Default users as participants","Standardbenutzer als Teilnehmer"}. {"Default users as participants","Standardbenutzer als Teilnehmer"}.
{"Delete","Löschen"}. {"Delete","Löschen"}.
{"Delete message of the day","Lösche Nachricht des Tages"}. {"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 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"}. {"Delete User","Benutzer löschen"}.
{"Deliver event notifications","Versende Ereignisbenachrichtigungen"}. {"Deliver event notifications","Ereignisbenachrichtigung zustellen"}.
{"Deliver payloads with event notifications","Versende Nutzlast mit Ereignisbenachrichtigungen"}. {"Deliver payloads with event notifications","Nachrichten mit mit Ereignis-Benachrichtigungen zustellen"}.
{"Description:","Beschreibung:"}. {"Description:","Beschreibung:"}.
{"Disc only copy","Festplatten Kopie"}. {"Disc only copy","Nur auf Festplatte"}.
{"Displayed Groups:","Angezeigte Gruppen:"}. {"Displayed Groups:","Angezeigte Gruppen:"}.
{"Dump Backup to Text File at ","Ausgabe der Sicherung in diese Textdatei "}. {"Dump Backup to Text File at ","Ausgabe der Sicherung in diese Textdatei "}.
{"Dump to Text File","Ausgabe in Textdatei"}. {"Dump to Text File","Ausgabe in Textdatei"}.
{"Edit Properties","Einstellungen ändern"}. {"Edit Properties","Einstellungen ändern"}.
{"ejabberd IRC module","ejabberd IRC Modul"}. {"ejabberd IRC module","ejabberd IRC-Modul"}.
{"ejabberd MUC module","ejabberd MUC Modul"}. {"ejabberd MUC module","ejabberd MUC-Modul"}.
{"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe Modul"}. {"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe-Modul"}.
{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams Modul"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5-Bytestreams-Modul"}.
{"ejabberd vCard module","ejabberd vCard Modul"}. {"ejabberd vCard module","ejabberd vCard-Modul"}.
{"ejabberd virtual hosts","ejabberd virtuelle Hosts"}. {"ejabberd virtual hosts","ejabberd virtuelle Hosts"}.
{"ejabberd Web Admin","ejabberd Web Admin"}. {"ejabberd Web Admin","ejabberd Web-Admin"}.
{"Elements","Elemente"}. {"Elements","Elemente"}.
{"Email","E-Mail"}. {"Email","E-Mail"}.
{"Enable logging","Log-Funktion aktivieren"}. {"Enable logging","Protokollierung aktivieren"}.
{"Encoding for server ~b","Kodierung für Server ~b"}. {"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 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 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 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 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 file","Geben Sie den Pfad zur jabberd14-Spool-Datei ein"}.
{"Enter path to text file","Geben sie den Pfad zur Textdatei 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 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 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"}. {"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"}. {"Erlang Jabber Server","Erlang Jabber-Server"}.
{"Error","Fehler"}. {"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\"}]."}. {"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 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:"}. {"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"}. {"Family Name","Nachname"}.
{"February","Februar"}. {"February","Februar"}.
{"Fill in fields to search for any matching Jabber User","Felder ausfüllen, um nach passenden Jabber Benutzern 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)"}. {"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"}. {"Friday","Freitag"}.
{"From ~s","Von ~s"}. {"From ~s","Von ~s"}.
{"From","Von"}. {"From","Von"}.
{"Full Name","Gesamter Name"}. {"Full Name","Vollständiger Name"}.
{"Get Number of Online Users","Anzahl der angemeldeten Benutzer abrufen"}. {"Get Number of Online Users","Anzahl der angemeldeten Benutzer abrufen"}.
{"Get Number of Registered Users","Anzahl der registrierten Benutzer abrufen"}. {"Get Number of Registered Users","Anzahl der registrierten Benutzer abrufen"}.
{"Get User Last Login Time","letzte Anmeldezeit abrufen"}. {"Get User Last Login Time","letzte Anmeldezeit abrufen"}.
{"Get User Password","Benutzer Passwort abrufen"}. {"Get User Password","Benutzer-Passwort abrufen"}.
{"Get User Statistics","Benutzer Statistik abrufen"}. {"Get User Statistics","Benutzer-Statistiken abrufen"}.
{"Group ","Gruppe "}. {"Group ","Gruppe "}.
{"Groups","Gruppen"}. {"Groups","Gruppen"}.
{"has been banned","wurde gebannt"}. {"has been banned","wurde gebannt"}.
@ -123,8 +127,8 @@
{"Import User from File at ","Benutzer aus dieser Datei importieren "}. {"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 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 data from jabberd14 spool directory:","Importiere Benutzer von jabberd14 Spool Verzeichnis:"}.
{"Import Users from Dir at ","Benutzer vom Verzeichnis importieren "}. {"Import Users from Dir at ","Benutzer importieren aus dem Verzeichnis "}.
{"Import Users From jabberd14 Spool Files","Importiere Benutzer von jabberd14 Spool Dateien"}. {"Import Users From jabberd14 Spool Files","Importiere Benutzer aus jabberd14-Spool-Dateien"}.
{"Improper message type","Unzulässiger Nachrichtentyp"}. {"Improper message type","Unzulässiger Nachrichtentyp"}.
{"Incorrect password","Falsches Passwort"}. {"Incorrect password","Falsches Passwort"}.
{"Invalid affiliation: ~s","Ungültige Mitgliedschaft: ~s"}. {"Invalid affiliation: ~s","Ungültige Mitgliedschaft: ~s"}.
@ -136,13 +140,13 @@
{"IRC settings","IRC Einstellungen"}. {"IRC settings","IRC Einstellungen"}.
{"IRC Transport","IRC Transport"}. {"IRC Transport","IRC Transport"}.
{"IRC username","IRC Benutzername"}. {"IRC username","IRC Benutzername"}.
{"IRC Username","IRC Benutzername"}. {"IRC Username","IRC-Benutzername"}.
{"is now known as","ist nun bekannt als"}. {"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","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 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"}. {"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","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"}. {"January","Januar"}.
{"Join IRC channel","IRC Channel beitreten"}. {"Join IRC channel","IRC Channel beitreten"}.
{"joins the room","kommt in den Raum"}. {"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"}. {"Join the IRC channel in this Jabber ID: ~s","Den IRC Channel mit dieser Jabber ID beitreten: ~s"}.
{"July","Juli"}. {"July","Juli"}.
{"June","Juni"}. {"June","Juni"}.
{"Last Activity","Letzte Aktion"}. {"Last Activity","Letzte Aktivität"}.
{"Last login","Letzte Anmeldung"}. {"Last login","Letzte Anmeldung"}.
{"Last month","Letzter Monat"}. {"Last month","Letzter Monat"}.
{"Last year","Letztes Jahr"}. {"Last year","Letztes Jahr"}.
@ -162,8 +166,8 @@
{"Make participants list public","Teilnehmerliste öffentlich machen"}. {"Make participants list public","Teilnehmerliste öffentlich machen"}.
{"Make room captcha protected","Raum mit Verifizierung (Captcha) versehen"}. {"Make room captcha protected","Raum mit Verifizierung (Captcha) versehen"}.
{"Make room members-only","Raum nur für Mitglieder zugänglich machen"}. {"Make room members-only","Raum nur für Mitglieder zugänglich machen"}.
{"Make room moderated","Raum modieriert machen"}. {"Make room moderated","Raum moderiert machen"}.
{"Make room password protected","Raum passwortgeschützt machen"}. {"Make room password protected","Raum mit Passwort schützen"}.
{"Make room persistent","Raum persistent machen"}. {"Make room persistent","Raum persistent machen"}.
{"Make room public searchable","Raum öffentlich suchbar machen"}. {"Make room public searchable","Raum öffentlich suchbar machen"}.
{"March","März"}. {"March","März"}.
@ -187,11 +191,11 @@
{"Name","Vorname"}. {"Name","Vorname"}.
{"Never","Nie"}. {"Never","Nie"}.
{"Nickname Registration at ","Registrieren des Spitznamens bei"}. {"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"}. {"Nickname","Spitzname"}.
{"No body provided for announce message","Kein Text für die Ankündigung angegeben"}. {"No body provided for announce message","Kein Text für die Ankündigung angegeben"}.
{"No Data","Keine Daten"}. {"No Data","Keine Daten"}.
{"Node ID","Knoten ID"}. {"Node ID","Knoten-ID"}.
{"Node ","Knoten "}. {"Node ","Knoten "}.
{"Node not found","Knoten nicht gefunden"}. {"Node not found","Knoten nicht gefunden"}.
{"Nodes","Knoten"}. {"Nodes","Knoten"}.
@ -200,15 +204,15 @@
{"No resource provided","Keine Ressource angegeben"}. {"No resource provided","Keine Ressource angegeben"}.
{"Not Found","nicht gefunden"}. {"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 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"}. {"Notify subscribers when the node is deleted","Abonnenten benachrichtigen, wenn der Knoten gelöscht wird"}.
{"November","November"}. {"November","November"}.
{"Number of occupants","Anzahl der Teilnehmer"}. {"Number of occupants","Anzahl der Teilnehmer"}.
{"Number of online users","Anzahl der angemeldeten Benutzer"}. {"Number of online users","Anzahl der angemeldeten Benutzer"}.
{"Number of registered users","Anzahl der registrierten Benutzer"}. {"Number of registered users","Anzahl der registrierten Benutzer"}.
{"October","Oktober"}. {"October","Oktober"}.
{"Offline Messages:","Offline Nachrichten:"}. {"Offline Messages:","Offline-Nachrichten:"}.
{"Offline Messages","Offline Nachrichten"}. {"Offline Messages","Offline-Nachrichten"}.
{"OK","OK"}. {"OK","OK"}.
{"Online","Angemeldet"}. {"Online","Angemeldet"}.
{"Online Users:","Angemeldete Benutzer:"}. {"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 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 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 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"}. {"Options","Optionen"}.
{"Organization Name","Firmenname"}. {"Organization Name","Organisation"}.
{"Organization Unit","Abteilung"}. {"Organization Unit","Abteilung"}.
{"Outgoing s2s Connections:","Ausgehende s2s Verbindungen:"}. {"Outgoing s2s Connections:","Ausgehende s2s-Verbindungen:"}.
{"Outgoing s2s Connections","Ausgehende s2s Verbindungen"}. {"Outgoing s2s Connections","Ausgehende s2s-Verbindungen"}.
{"Outgoing s2s Servers:","Ausgehende s2s Server:"}. {"Outgoing s2s Servers:","Ausgehende s2s-Server:"}.
{"Owner privileges required","Besitzerrechte benötigt"}. {"Owner privileges required","Besitzerrechte benötigt"}.
{"Packet","Paket"}. {"Packet","Paket"}.
{"Password ~b","Passwort ~b"}. {"Password ~b","Passwort ~b"}.
@ -233,24 +237,25 @@
{"Password Verification","Passwort bestätigen"}. {"Password Verification","Passwort bestätigen"}.
{"Path to Dir","Pfad zum Verzeichnis"}. {"Path to Dir","Pfad zum Verzeichnis"}.
{"Path to File","Pfad zur Datei"}. {"Path to File","Pfad zur Datei"}.
{"Pending","schwebend"}. {"Pending","anhängig"}.
{"Period: ","Zeitraum: "}. {"Period: ","Zeitraum: "}.
{"Persist items to storage","Einträge dauerhaft speichern"}. {"Persist items to storage","Einträge dauerhaft speichern"}.
{"Ping","Ping"}. {"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"}. {"Pong","Pong"}.
{"Port ~b","Port ~b"}. {"Port ~b","Port ~b"}.
{"Port","Port"}. {"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, "}. {"private, ","privat, "}.
{"Protocol","Protokoll"}. {"Protocol","Protokoll"}.
{"Publish-Subscribe","Publish-Subscribe"}. {"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"}. {"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 and disc copy","RAM und Festplatte"}.
{"RAM copy","RAM Kopie"}. {"RAM copy","Nur RAM"}.
{"Raw","Unformatiert"}. {"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"}. {"Recipient is not in the conference room","Der Empfänger ist nicht im Raum"}.
{"Registered Users:","Registrierte Benutzer:"}. {"Registered Users:","Registrierte Benutzer:"}.
{"Registered Users","Registrierte Benutzer"}. {"Registered Users","Registrierte Benutzer"}.
@ -264,11 +269,11 @@
{"Restart","Neustart"}. {"Restart","Neustart"}.
{"Restart Service","Dienst neustarten"}. {"Restart Service","Dienst neustarten"}.
{"Restore Backup from File at ","Datenwiederherstellung aus der Datei "}. {"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 binary backup immediately:","Stelle binäre Sicherung sofort wieder her:"}.
{"Restore plain text backup immediately:","Stelle Klartext-Sicherung sofort wieder her:"}. {"Restore plain text backup immediately:","Stelle Klartext-Sicherung sofort wieder her:"}.
{"Restore","Wiederherstellung"}. {"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 creation is denied by service policy","Anlegen des Raumes aufgrund der Dienstrichtlinien verweigert"}.
{"Room description","Raum Beschreibung"}. {"Room description","Raum Beschreibung"}.
{"Room Occupants","Teilnehmer in diesem Raum"}. {"Room Occupants","Teilnehmer in diesem Raum"}.
@ -277,11 +282,11 @@
{"Roster","Kontaktliste"}. {"Roster","Kontaktliste"}.
{"Roster of ","Kontaktliste von "}. {"Roster of ","Kontaktliste von "}.
{"Roster size","Kontaktlistengröße"}. {"Roster size","Kontaktlistengröße"}.
{"RPC Call Error","RPC Abruf-Fehler"}. {"RPC Call Error","Fehler bei RPC-Aufruf"}.
{"Running Nodes","Aktive Knoten"}. {"Running Nodes","Aktive Knoten"}.
{"~s access rule configuration","~s Zugangsregel Konfiguration"}. {"~s access rule configuration","~s Zugangsregel-Konfiguration"}.
{"Saturday","Samstag"}. {"Saturday","Samstag"}.
{"Script check","Script Überprüfung"}. {"Script check","Script-Überprüfung"}.
{"Search Results for ","Suchergebnisse für "}. {"Search Results for ","Suchergebnisse für "}.
{"Search users in ","Benutzer suchen in "}. {"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"}. {"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 Integral Table","Vollständige Tabelle anzeigen"}.
{"Show Ordinary Table","Normale Tabelle anzeigen"}. {"Show Ordinary Table","Normale Tabelle anzeigen"}.
{"Shut Down Service","Dienst herunterfahren"}. {"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 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"}. {"Specify the publisher model","Geben sie das Publikationsmodell an"}.
{"~s's Offline Messages Queue","~s's Offline Nachrichten Warteschlange"}. {"~s's Offline Messages Queue","~s's Offline-Nachrichten-Warteschlange"}.
{"Start","Anfang"}. {"Start Modules at ","Starte Module auf "}.
{"Start Modules at ","Starte Module bei "}.
{"Start Modules","Module starten"}. {"Start Modules","Module starten"}.
{"Start","Starten"}.
{"Statistics of ~p","Statistiken von ~p"}. {"Statistics of ~p","Statistiken von ~p"}.
{"Statistics","Statistik"}. {"Statistics","Statistik"}.
{"Stop Modules at ","Stoppe Module bei "}. {"Stop Modules at ","Stoppe Module auf "}.
{"Stop Modules","Module stoppen"}. {"Stop Modules","Module stoppen"}.
{"Stopped Nodes","Inaktive Knoten"}. {"Stopped Nodes","Inaktive Knoten"}.
{"Stop","Stop"}. {"Stop","Stoppen"}.
{"Storage Type","Speichertyp"}. {"Storage Type","Speichertyp"}.
{"Store binary backup:","Speichere binäre Sicherung:"}. {"Store binary backup:","Speichere binäre Sicherung:"}.
{"Store plain text backup:","Speichere Klartext-Sicherung:"}. {"Store plain text backup:","Speichere Klartext-Sicherung:"}.
{"Subject","Thema"}. {"Subject","Betreff"}.
{"Submit","Senden"}. {"Submit","Senden"}.
{"Submitted","Gesendet"}. {"Submitted","Gesendet"}.
{"Subscriber Address","Abonnenten Adresse"}. {"Subscriber Address","Abonnenten-Adresse"}.
{"Subscription","Abonnement"}. {"Subscription","Abonnement"}.
{"Sunday","Sonntag"}. {"Sunday","Sonntag"}.
{"That nickname is already in use by another occupant","Dieser Spitzname wird bereits von einem Teilnehmer genutzt"}. {"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"}. {"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 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 collections with which a node is affiliated","Sammlungen, mit denen ein Knoten verknüpft ist"}.
{"the password is","das Passwort ist"}. {"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 gekickt, da er eine Fehlernachricht gesendet hat"}. {"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 gekickt, da er eine Fehlernachricht an einen anderen Teilnehmer 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 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"}. {"This room is not anonymous","Dieser Raum ist nicht anonym"}.
{"Thursday","Donnerstag"}. {"Thursday","Donnerstag"}.
{"Time delay","Zeitverzögerung"}. {"Time delay","Zeitverzögerung"}.
{"Time","Zeit"}. {"Time","Zeit"}.
{"To","An"}.
{"To ~s","An ~s"}. {"To ~s","An ~s"}.
{"To","Zu"}.
{"Traffic rate limit is exceeded","Datenratenlimit wurde überschritten"}. {"Traffic rate limit is exceeded","Datenratenlimit wurde überschritten"}.
{"Transactions Aborted:","Vorgänge abgebrochen:"}. {"Transactions Aborted:","Abgebrochene Transaktionen:"}.
{"Transactions Committed:","Vorgänge durchgeführt:"}. {"Transactions Committed:","Durchgeführte Transaktionen:"}.
{"Transactions Logged:","Vorgänge protokolliert:"}. {"Transactions Logged:","Protokollierte Transaktionen:"}.
{"Transactions Restarted:","Vorgänge neu gestartet:"}. {"Transactions Restarted:","Neugestartete Transaktionen:"}.
{"Tuesday","Dienstag"}. {"Tuesday","Dienstag"}.
{"Unable to generate a captcha","Konnte Verifizierung nicht erstellen"}. {"Unable to generate a captcha","Konnte Verifizierung (Captcha) nicht erstellen"}.
{"Unauthorized","Nicht berechtigt"}. {"Unauthorized","Nicht berechtigt"}.
{"Update","Aktualisieren"}. {"Update","Aktualisieren"}.
{"Update ","Aktualisierung "}. {"Update ","Aktualisierung "}.
@ -350,12 +356,12 @@
{"Use of STARTTLS required","Verwendung von STARTTLS erforderlich"}. {"Use of STARTTLS required","Verwendung von STARTTLS erforderlich"}.
{"User ","Benutzer "}. {"User ","Benutzer "}.
{"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 are not allowed to register accounts so quickly","Benutzer dürfen Konten nicht so schnell registrieren"}.
{"Users","Benutzer"}. {"Users","Benutzer"}.
{"Users Last Activity","Letzte Benutzeraktivität"}. {"Users Last Activity","Letzte Benutzeraktivität"}.
{"Validate","Validieren"}. {"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 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"}. {"Visitors are not allowed to send messages to all occupants","Besucher dürfen nicht an alle Teilnehmer Nachrichten verschicken"}.
{"Wednesday","Mittwoch"}. {"Wednesday","Mittwoch"}.
@ -363,9 +369,9 @@
{"Whether to allow subscriptions","Ob Abonnements erlaubt sind"}. {"Whether to allow subscriptions","Ob Abonnements erlaubt sind"}.
{"You have been banned from this room","Sie wurden aus diesem Raum verbannt"}. {"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 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 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 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"}. {"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"}. {"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"}.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,10 @@
{"Change Password","Αλλαγή κωδικού"}. {"Change Password","Αλλαγή κωδικού"}.
{"Change User Password","Αλλαγή Κωδικού Πρόσβασης Χρήστη"}. {"Change User Password","Αλλαγή Κωδικού Πρόσβασης Χρήστη"}.
{"Chatroom configuration modified","Διαμόρφωση Αίθουσaς σύνεδριασης τροποποιηθηκε"}. {"Chatroom configuration modified","Διαμόρφωση Αίθουσaς σύνεδριασης τροποποιηθηκε"}.
{"Chatroom is created","Η αίθουσα σύνεδριασης δημιουργήθηκε"}.
{"Chatroom is destroyed","Η αίθουσα σύνεδριασης διαγράφηκε"}.
{"Chatroom is started","Η αίθουσα σύνεδριασης έχει ξεκινήσει"}.
{"Chatroom is stopped","Η αίθουσα σύνεδριασης έχει σταματήσει"}.
{"Chatrooms","Αίθουσες σύνεδριασης"}. {"Chatrooms","Αίθουσες σύνεδριασης"}.
{"Choose a username and password to register with this server","Επιλέξτε ένα όνομα χρήστη και κωδικό πρόσβασης για να εγγραφείτε σε αυτό τον διακομιστή"}. {"Choose a username and password to register with this server","Επιλέξτε ένα όνομα χρήστη και κωδικό πρόσβασης για να εγγραφείτε σε αυτό τον διακομιστή"}.
{"Choose modules to stop","Επιλέξτε modules για να σταματήσουν"}. {"Choose modules to stop","Επιλέξτε modules για να σταματήσουν"}.
@ -246,6 +250,7 @@
{"Protocol","Πρωτόκολλο"}. {"Protocol","Πρωτόκολλο"}.
{"Publish-Subscribe","Δημοσίευση-Εγγραφή"}. {"Publish-Subscribe","Δημοσίευση-Εγγραφή"}.
{"PubSub subscriber request","Αίτηση συνδρομητή Δημοσίευσης-Εγγραφής"}. {"PubSub subscriber request","Αίτηση συνδρομητή Δημοσίευσης-Εγγραφής"}.
{"Purge all items when the relevant publisher goes offline","Διαγραφή όλων των στοιχείων όταν ο σχετικός εκδότης αποσυνδέεται"}.
{"Queries to the conference members are not allowed in this room","Ερωτήματα πρώς τα μέλη της διασκέψεως δεν επιτρέπονται σε αυτήν την αίθουσα"}. {"Queries to the conference members are not allowed in this room","Ερωτήματα πρώς τα μέλη της διασκέψεως δεν επιτρέπονται σε αυτήν την αίθουσα"}.
{"RAM and disc copy","Αντίγραφο μόνο σε RAM καί δίσκο"}. {"RAM and disc copy","Αντίγραφο μόνο σε RAM καί δίσκο"}.
{"RAM copy","Αντίγραφο σε RAM"}. {"RAM copy","Αντίγραφο σε RAM"}.
@ -298,6 +303,7 @@
{"Shut Down Service","Κλείσιμο Υπηρεσίας"}. {"Shut Down Service","Κλείσιμο Υπηρεσίας"}.
{"~s invites you to the room ~s","~s σας προσκαλεί στην αίθουσα ~s"}. {"~s invites you to the room ~s","~s σας προσκαλεί στην αίθουσα ~s"}.
{"Specify the access model","Καθορίστε το μοντέλο πρόσβασης"}. {"Specify the access model","Καθορίστε το μοντέλο πρόσβασης"}.
{"Specify the event message type","Καθορίστε τον τύπο μηνύματος συμβάντος"}.
{"Specify the publisher model","Καθορίστε το μοντέλο εκδότη"}. {"Specify the publisher model","Καθορίστε το μοντέλο εκδότη"}.
{"~s's Offline Messages Queue","Η Σειρά Χωρίς Σύνδεση Μηνύματων τού ~s"}. {"~s's Offline Messages Queue","Η Σειρά Χωρίς Σύνδεση Μηνύματων τού ~s"}.
{"Start Modules at ","Εκκίνηση Modules στο "}. {"Start Modules at ","Εκκίνηση Modules στο "}.

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,10 @@
{"Change Password","Ŝanĝu pasvorton"}. {"Change Password","Ŝanĝu pasvorton"}.
{"Change User Password","Ŝanĝu pasvorton de uzanto"}. {"Change User Password","Ŝanĝu pasvorton de uzanto"}.
{"Chatroom configuration modified","Agordo de babilejo ŝanĝita"}. {"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"}. {"Chatrooms","Babilejoj"}.
{"Choose a username and password to register with this server","Elektu uzantnomon kaj pasvorton por registri je ĉi tiu servilo"}. {"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"}. {"Choose modules to stop","Elektu modulojn por fini"}.
@ -246,6 +250,7 @@
{"Protocol","Protokolo"}. {"Protocol","Protokolo"}.
{"Publish-Subscribe","Public-Abonado"}. {"Publish-Subscribe","Public-Abonado"}.
{"PubSub subscriber request","PubAbo abonpeto"}. {"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"}. {"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 and disc copy","RAM- kaj disk-kopio"}.
{"RAM copy","RAM-kopio"}. {"RAM copy","RAM-kopio"}.
@ -298,6 +303,7 @@
{"Shut Down Service","Haltigu Servon"}. {"Shut Down Service","Haltigu Servon"}.
{"~s invites you to the room ~s","~s invitas vin al la babilejo ~s"}. {"~s invites you to the room ~s","~s invitas vin al la babilejo ~s"}.
{"Specify the access model","Specifu atingo-modelon"}. {"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"}. {"Specify the publisher model","Enmetu publikadan modelon"}.
{"~s's Offline Messages Queue","Mesaĝo-atendovico de ~s"}. {"~s's Offline Messages Queue","Mesaĝo-atendovico de ~s"}.
{"Start Modules at ","Startu modulojn je "}. {"Start Modules at ","Startu modulojn je "}.

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,10 @@
{"Change Password","Cambiar contraseña"}. {"Change Password","Cambiar contraseña"}.
{"Change User Password","Cambiar contraseña de usuario"}. {"Change User Password","Cambiar contraseña de usuario"}.
{"Chatroom configuration modified","Configuración de la sala modificada"}. {"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"}. {"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 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"}. {"Choose modules to stop","Selecciona módulos a detener"}.
@ -246,6 +250,7 @@
{"Protocol","Protocolo"}. {"Protocol","Protocolo"}.
{"Publish-Subscribe","Servicio de Publicar-Subscribir"}. {"Publish-Subscribe","Servicio de Publicar-Subscribir"}.
{"PubSub subscriber request","Petición de subscriptor de PubSub"}. {"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"}. {"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 and disc copy","Copia en RAM y disco"}.
{"RAM copy","Copia en RAM"}. {"RAM copy","Copia en RAM"}.
@ -298,6 +303,7 @@
{"Shut Down Service","Detener el servicio"}. {"Shut Down Service","Detener el servicio"}.
{"~s invites you to the room ~s","~s te invita a la sala ~s"}. {"~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 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"}. {"Specify the publisher model","Especificar el modelo del publicante"}.
{"~s's Offline Messages Queue","Cola de mensajes diferidos de ~s"}. {"~s's Offline Messages Queue","Cola de mensajes diferidos de ~s"}.
{"Start","Iniciar"}. {"Start","Iniciar"}.

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,10 @@
{"Change Password","Modifier le mot de passe"}. {"Change Password","Modifier le mot de passe"}.
{"Change User Password","Changer le mot de passe de l'utilisateur"}. {"Change User Password","Changer le mot de passe de l'utilisateur"}.
{"Chatroom configuration modified","Configuration du salon modifiée"}. {"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"}. {"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 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"}. {"Choose modules to stop","Sélectionnez les modules à arrêter"}.
@ -246,6 +250,7 @@
{"Protocol","Protocole"}. {"Protocol","Protocole"}.
{"Publish-Subscribe","Publication-Abonnement"}. {"Publish-Subscribe","Publication-Abonnement"}.
{"PubSub subscriber request","Demande d'abonnement PubSub"}. {"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"}. {"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 and disc copy","Copie en mémoire vive (RAM) et sur disque"}.
{"RAM copy","Copie en mémoire vive (RAM)"}. {"RAM copy","Copie en mémoire vive (RAM)"}.
@ -298,6 +303,7 @@
{"Shut Down Service","Arrêter le service"}. {"Shut Down Service","Arrêter le service"}.
{"~s invites you to the room ~s","~s vous a invité dans la salle de discussion ~s"}. {"~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 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"}. {"Specify the publisher model","Définir le modèle de publication"}.
{"~s's Offline Messages Queue","~s messages en file d'attente"}. {"~s's Offline Messages Queue","~s messages en file d'attente"}.
{"Start","Démarrer"}. {"Start","Démarrer"}.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,10 @@
{"Change Password","Modificare la password"}. {"Change Password","Modificare la password"}.
{"Change User Password","Cambiare la password dell'utente"}. {"Change User Password","Cambiare la password dell'utente"}.
{"Chatroom configuration modified","Configurazione della stanza modificata"}. {"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"}. {"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 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"}. {"Choose modules to stop","Selezionare i moduli da arrestare"}.
@ -246,6 +250,7 @@
{"Protocol","Protocollo"}. {"Protocol","Protocollo"}.
{"Publish-Subscribe","Pubblicazione-Iscrizione"}. {"Publish-Subscribe","Pubblicazione-Iscrizione"}.
{"PubSub subscriber request","Richiesta di iscrizione per PubSub"}. {"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"}. {"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 and disc copy","Copia in memoria (RAM) e su disco"}.
{"RAM copy","Copia in memoria (RAM)"}. {"RAM copy","Copia in memoria (RAM)"}.
@ -298,6 +303,7 @@
{"Shut Down Service","Terminare il servizio"}. {"Shut Down Service","Terminare il servizio"}.
{"~s invites you to the room ~s","~s ti invita nella stanza ~s"}. {"~s invites you to the room ~s","~s ti invita nella stanza ~s"}.
{"Specify the access model","Specificare il modello di accesso"}. {"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"}. {"Specify the publisher model","Definire il modello di pubblicazione"}.
{"~s's Offline Messages Queue","Coda di ~s messaggi offline"}. {"~s's Offline Messages Queue","Coda di ~s messaggi offline"}.
{"Start","Avviare"}. {"Start","Avviare"}.

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,10 @@
{"Change Password","パスワードの変更"}. {"Change Password","パスワードの変更"}.
{"Change User Password","パスワードを変更"}. {"Change User Password","パスワードを変更"}.
{"Chatroom configuration modified","チャットルームの設定を変更しました"}. {"Chatroom configuration modified","チャットルームの設定を変更しました"}.
{"Chatroom is created","チャットルームは作られました。"}.
{"Chatroom is destroyed","チャットルームは削除されました。"}.
{"Chatroom is started","チャットルームが開始しました。"}.
{"Chatroom is stopped","チャットルームが停止しました。"}.
{"Chatrooms","チャットルーム"}. {"Chatrooms","チャットルーム"}.
{"Choose a username and password to register with this server","サーバーに登録するユーザー名とパスワードを選択して下さい"}. {"Choose a username and password to register with this server","サーバーに登録するユーザー名とパスワードを選択して下さい"}.
{"Choose modules to stop","停止するモジュールの選択"}. {"Choose modules to stop","停止するモジュールの選択"}.
@ -246,6 +250,7 @@
{"Protocol","プロトコル"}. {"Protocol","プロトコル"}.
{"Publish-Subscribe","Publish-Subscribe"}. {"Publish-Subscribe","Publish-Subscribe"}.
{"PubSub subscriber request","PubSub 購読リクエスト"}. {"PubSub subscriber request","PubSub 購読リクエスト"}.
{"Purge all items when the relevant publisher goes offline","公開者がオフラインになる時に、全てののアイテムを削除"}.
{"Queries to the conference members are not allowed in this room","このチャットルームではカンファレンスメンバーへのクエリーは禁止されています"}. {"Queries to the conference members are not allowed in this room","このチャットルームではカンファレンスメンバーへのクエリーは禁止されています"}.
{"RAM and disc copy","RAM, ディスクコピー"}. {"RAM and disc copy","RAM, ディスクコピー"}.
{"RAM copy","RAMコピー"}. {"RAM copy","RAMコピー"}.
@ -298,6 +303,7 @@
{"Shut Down Service","サービスを停止"}. {"Shut Down Service","サービスを停止"}.
{"~s invites you to the room ~s","~s はあなたをチャットルーム ~s に招待しています"}. {"~s invites you to the room ~s","~s はあなたをチャットルーム ~s に招待しています"}.
{"Specify the access model","アクセスモデルを設定する"}. {"Specify the access model","アクセスモデルを設定する"}.
{"Specify the event message type","イベントメッセージ種別を設定"}.
{"Specify the publisher model","公開モデルを指定する"}. {"Specify the publisher model","公開モデルを指定する"}.
{"~s's Offline Messages Queue","~s's オフラインメッセージキュー"}. {"~s's Offline Messages Queue","~s's オフラインメッセージキュー"}.
{"Start Modules at ","モジュールの開始"}. {"Start Modules at ","モジュールの開始"}.

File diff suppressed because it is too large Load Diff

View File

@ -25,8 +25,8 @@
{"Announcements","Mededelingen"}. {"Announcements","Mededelingen"}.
{"anyone","iedereen"}. {"anyone","iedereen"}.
{"A password is required to enter this room","U hebt een wachtwoord nodig om deze chatruimte te kunnen betreden"}. {"A password is required to enter this room","U hebt een wachtwoord nodig om deze chatruimte te kunnen betreden"}.
{"April","april"}. {"April","April"}.
{"August","augustus"}. {"August","Augustus"}.
{"Backup","Backup"}. {"Backup","Backup"}.
{"Backup Management","Backup"}. {"Backup Management","Backup"}.
{"Backup of ","Backup maken van "}. {"Backup of ","Backup maken van "}.
@ -36,6 +36,10 @@
{"Change Password","Wachtwoord wijzigen"}. {"Change Password","Wachtwoord wijzigen"}.
{"Change User Password","Verander Gebruikerswachtwoord"}. {"Change User Password","Verander Gebruikerswachtwoord"}.
{"Chatroom configuration modified","De instellingen van de chatruimte werden veranderd"}. {"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"}. {"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 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"}. {"Choose modules to stop","Selecteer de modules die u wilt stoppen"}.
@ -53,7 +57,7 @@
{"Database","Database"}. {"Database","Database"}.
{"Database Tables at ","Databasetabellen van "}. {"Database Tables at ","Databasetabellen van "}.
{"Database Tables Configuration at ","Instellingen van databasetabellen op "}. {"Database Tables Configuration at ","Instellingen van databasetabellen op "}.
{"December","december"}. {"December","December"}.
{"Default users as participants","Gebruikers standaard instellen als deelnemers"}. {"Default users as participants","Gebruikers standaard instellen als deelnemers"}.
{"Delete message of the day","Bericht van de dag verwijderen"}. {"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"}. {"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 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):"}. {"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"}. {"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 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.)."}. {"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 ~s","Van ~s"}.
{"From","Van"}. {"From","Van"}.
{"Full Name","Volledige naam"}. {"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"}. {"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","Jabber ID"}.
{"Jabber ID ~s is invalid","De Jabber ID ~s is ongeldig"}. {"Jabber ID ~s is invalid","De Jabber ID ~s is ongeldig"}.
{"January","januari"}. {"January","Januari"}.
{"Join IRC channel","Ga IRC kanaal binnen"}. {"Join IRC channel","Ga IRC kanaal binnen"}.
{"joins the room","betrad de chatruimte"}. {"joins the room","betrad de chatruimte"}.
{"Join the IRC channel here.","Ga het IRC kanaal binnen"}. {"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"}. {"Join the IRC channel in this Jabber ID: ~s","Ga het IRC kanaal van deze Jabber ID binnen: ~s"}.
{"July","juli"}. {"July","Juli"}.
{"June","juni"}. {"June","Juni"}.
{"Last Activity","Laatste activiteit"}. {"Last Activity","Laatste activiteit"}.
{"Last login","Laatste Aanmelding"}. {"Last login","Laatste Aanmelding"}.
{"Last month","Afgelopen maand"}. {"Last month","Afgelopen maand"}.
@ -166,11 +170,11 @@
{"Make room password protected","Chatruimte beveiligen met een wachtwoord"}. {"Make room password protected","Chatruimte beveiligen met een wachtwoord"}.
{"Make room persistent","Chatruimte blijvend maken"}. {"Make room persistent","Chatruimte blijvend maken"}.
{"Make room public searchable","Chatruimte doorzoekbaar maken"}. {"Make room public searchable","Chatruimte doorzoekbaar maken"}.
{"March","maart"}. {"March","Maart"}.
{"Maximum Number of Occupants","Maximum aantal aanwezigen"}. {"Maximum Number of Occupants","Maximum aantal aanwezigen"}.
{"Max # of items to persist","Maximum aantal in het geheugen te bewaren items"}. {"Max # of items to persist","Maximum aantal in het geheugen te bewaren items"}.
{"Max payload size in bytes","Maximumgrootte van bericht in bytes"}. {"Max payload size in bytes","Maximumgrootte van bericht in bytes"}.
{"May","mei"}. {"May","Mei"}.
{"Members:","Groepsleden:"}. {"Members:","Groepsleden:"}.
{"Membership is required to enter this room","U moet lid zijn om deze chatruimte te kunnen betreden"}. {"Membership is required to enter this room","U moet lid zijn om deze chatruimte te kunnen betreden"}.
{"Memory","Geheugen"}. {"Memory","Geheugen"}.
@ -182,7 +186,7 @@
{"Module","Module"}. {"Module","Module"}.
{"Modules at ","Modules op "}. {"Modules at ","Modules op "}.
{"Modules","Modules"}. {"Modules","Modules"}.
{"Monday","maandag"}. {"Monday","Maandag"}.
{"Name:","Naam:"}. {"Name:","Naam:"}.
{"Name","Naam"}. {"Name","Naam"}.
{"Never","Nooit"}. {"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 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 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"}. {"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 occupants","Aantal aanwezigen"}.
{"Number of online users","Aantal Aanwezige Gebruikers"}. {"Number of online users","Aantal Aanwezige Gebruikers"}.
{"Number of registered users","Aantal Geregistreerde Gebruikers"}. {"Number of registered users","Aantal Geregistreerde Gebruikers"}.
{"October","oktober"}. {"October","Oktober"}.
{"Offline Messages:","Offline berichten:"}. {"Offline Messages:","Offline berichten:"}.
{"Offline Messages","Offline berichten"}. {"Offline Messages","Offline berichten"}.
{"OK","OK"}. {"OK","OK"}.
@ -246,6 +250,7 @@
{"Protocol","Protocol"}. {"Protocol","Protocol"}.
{"Publish-Subscribe","Publish-Subscribe"}. {"Publish-Subscribe","Publish-Subscribe"}.
{"PubSub subscriber request","PubSub abonnee verzoek"}. {"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"}. {"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 and disc copy","RAM en harde schijf"}.
{"RAM copy","RAM"}. {"RAM copy","RAM"}.
@ -280,7 +285,7 @@
{"RPC Call Error","RPC-oproepfout"}. {"RPC Call Error","RPC-oproepfout"}.
{"Running Nodes","Draaiende nodes"}. {"Running Nodes","Draaiende nodes"}.
{"~s access rule configuration","Access rules op ~s"}. {"~s access rule configuration","Access rules op ~s"}.
{"Saturday","zaterdag"}. {"Saturday","Zaterdag"}.
{"Script check","Controle van script"}. {"Script check","Controle van script"}.
{"Search Results for ","Zoekresultaten voor "}. {"Search Results for ","Zoekresultaten voor "}.
{"Search users in ","Gebruikers zoeken in "}. {"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 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","Mededeling verzenden naar alle gebruikers"}.
{"Send announcement to all users on all hosts","Stuur aankondiging aan alle gebruikers op alle hosts"}. {"Send announcement to all users on all hosts","Stuur aankondiging aan alle gebruikers op alle hosts"}.
{"September","september"}. {"September","September"}.
{"Server ~b","Server ~b"}. {"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 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"}. {"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"}. {"Shut Down Service","Stop Service"}.
{"~s invites you to the room ~s","~s nodigt je uit voor het groepsgesprek ~s"}. {"~s invites you to the room ~s","~s nodigt je uit voor het groepsgesprek ~s"}.
{"Specify the access model","Geef toegangsmodel"}. {"Specify the access model","Geef toegangsmodel"}.
{"Specify the event message type","Geef type van eventbericht"}.
{"Specify the publisher model","Publicatietype opgeven"}. {"Specify the publisher model","Publicatietype opgeven"}.
{"~s's Offline Messages Queue","offline berichten van ~s"}. {"~s's Offline Messages Queue","offline berichten van ~s"}.
{"Start Modules at ","Modules starten op "}. {"Start Modules at ","Modules starten op "}.
@ -317,17 +323,17 @@
{"Submit","Verzenden"}. {"Submit","Verzenden"}.
{"Subscriber Address","Abonnee Adres"}. {"Subscriber Address","Abonnee Adres"}.
{"Subscription","Inschrijving"}. {"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 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"}. {"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 collections with which a node is affiliated","De collecties waar een node mee is gerelateerd"}.
{"the password is","het wachtwoord is"}. {"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","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 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 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"}. {"This room is not anonymous","Deze chatruimte is niet anoniem"}.
{"Thursday","donderdag"}. {"Thursday","Donderdag"}.
{"Time delay","Vertraging"}. {"Time delay","Vertraging"}.
{"Time","Tijd"}. {"Time","Tijd"}.
{"To","Aan"}. {"To","Aan"}.
@ -337,7 +343,7 @@
{"Transactions Committed:","Bevestigde transacties:"}. {"Transactions Committed:","Bevestigde transacties:"}.
{"Transactions Logged:","Gelogde transacties:"}. {"Transactions Logged:","Gelogde transacties:"}.
{"Transactions Restarted:","Herstarte transacties:"}. {"Transactions Restarted:","Herstarte transacties:"}.
{"Tuesday","dinsdag"}. {"Tuesday","Dinsdag"}.
{"Unable to generate a captcha","Het generen van een gautomatiseerde Turing test is mislukt"}. {"Unable to generate a captcha","Het generen van een gautomatiseerde Turing test is mislukt"}.
{"Unauthorized","Niet geautoriseerd"}. {"Unauthorized","Niet geautoriseerd"}.
{"Update","Bijwerken"}. {"Update","Bijwerken"}.
@ -358,7 +364,7 @@
{"vCard User Search","Gebruikers zoeken"}. {"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 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"}. {"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"}. {"When to send the last published item","Wanneer het laatst gepubliceerde item verzonden moet worden"}.
{"Whether to allow subscriptions","Abonnementsaanvraag toestaan"}. {"Whether to allow subscriptions","Abonnementsaanvraag toestaan"}.
{"You have been banned from this room","U werd verbannen uit deze chatruimte"}. {"You have been banned from this room","U werd verbannen uit deze chatruimte"}.

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,10 @@
{"Change Password","Endre Passord"}. {"Change Password","Endre Passord"}.
{"Change User Password","Endre Brukers Passord"}. {"Change User Password","Endre Brukers Passord"}.
{"Chatroom configuration modified","Samtalerommets konfigurasjon er endret"}. {"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"}. {"Chatrooms","Samtalerom"}.
{"Choose a username and password to register with this server","Velg et brukernavn og passord for å registrere på "}. {"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"}. {"Choose modules to stop","Velg hvilke moduler som skal stoppes"}.
@ -246,6 +250,7 @@
{"Protocol","Protokoll"}. {"Protocol","Protokoll"}.
{"Publish-Subscribe","Publish-Subscribe"}. {"Publish-Subscribe","Publish-Subscribe"}.
{"PubSub subscriber request","PubSub abonements forespørsel"}. {"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"}. {"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 and disc copy","RAM og diskkopi"}.
{"RAM copy","RAM kopi"}. {"RAM copy","RAM kopi"}.
@ -298,6 +303,7 @@
{"Shut Down Service","Avslutt Tjeneste"}. {"Shut Down Service","Avslutt Tjeneste"}.
{"~s invites you to the room ~s","~s inviterer deg til rommet ~s"}. {"~s invites you to the room ~s","~s inviterer deg til rommet ~s"}.
{"Specify the access model","Spesifiser aksess modellen"}. {"Specify the access model","Spesifiser aksess modellen"}.
{"Specify the event message type","Spesifiser hendelsesbeskjed type"}.
{"Specify the publisher model","Angi publiserings modell"}. {"Specify the publisher model","Angi publiserings modell"}.
{"~s's Offline Messages Queue","~ss kø for Frakoblede Meldinger"}. {"~s's Offline Messages Queue","~ss kø for Frakoblede Meldinger"}.
{"Start Modules at ","Start Moduler på "}. {"Start Modules at ","Start Moduler på "}.

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,10 @@
{"Change Password","Zmień hasło"}. {"Change Password","Zmień hasło"}.
{"Change User Password","Zmień hasło użytkownika"}. {"Change User Password","Zmień hasło użytkownika"}.
{"Chatroom configuration modified","Konfiguracja pokoju zmodyfikowana"}. {"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"}. {"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 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"}. {"Choose modules to stop","Wybierz moduły do zatrzymania"}.
@ -246,6 +250,7 @@
{"Protocol","Protokół"}. {"Protocol","Protokół"}.
{"Publish-Subscribe","PubSub"}. {"Publish-Subscribe","PubSub"}.
{"PubSub subscriber request","Żądanie subskrybcji 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"}. {"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 and disc copy","Kopia na dysku i w pamięci RAM"}.
{"RAM copy","Kopia w pamięci RAM"}. {"RAM copy","Kopia w pamięci RAM"}.

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,10 @@
{"Change Password","Mudar senha"}. {"Change Password","Mudar senha"}.
{"Change User Password","Alterar Senha do Usuário"}. {"Change User Password","Alterar Senha do Usuário"}.
{"Chatroom configuration modified","Configuração da sala de bate-papo modificada"}. {"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"}. {"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 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"}. {"Choose modules to stop","Selecione módulos a parar"}.
@ -246,6 +250,7 @@
{"Protocol","Porta"}. {"Protocol","Porta"}.
{"Publish-Subscribe","Publicação de Tópico"}. {"Publish-Subscribe","Publicação de Tópico"}.
{"PubSub subscriber request","PubSub requisição de assinante"}. {"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"}. {"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 and disc copy","Copia em RAM y disco"}.
{"RAM copy","Copia em RAM"}. {"RAM copy","Copia em RAM"}.
@ -298,6 +303,7 @@
{"Shut Down Service","Parar Serviço"}. {"Shut Down Service","Parar Serviço"}.
{"~s invites you to the room ~s","~s convidou você para a sala ~s"}. {"~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 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"}. {"Specify the publisher model","Especificar o modelo do publicante"}.
{"~s's Offline Messages Queue","~s's Fila de Mensagens Offline"}. {"~s's Offline Messages Queue","~s's Fila de Mensagens Offline"}.
{"Start","Iniciar"}. {"Start","Iniciar"}.

File diff suppressed because it is too large Load Diff

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