25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-12-24 17:29:28 +01:00

Merge branch '2.2.x' of git+ssh://gitorious.process-one.net/+ejabberd-developers/ejabberd/maincustomers into 2.2.x

This commit is contained in:
Mickaël Rémond 2011-07-15 20:21:13 +02:00
commit 1dfd9fd568
254 changed files with 8744 additions and 1749 deletions

2
README
View File

@ -20,7 +20,7 @@ To compile ejabberd you need:
- GNU Iconv 1.8 or higher, for the IRC Transport - GNU Iconv 1.8 or higher, for the IRC Transport
(mod_irc). Optional. Not needed on systems with GNU Libc. (mod_irc). Optional. Not needed on systems with GNU Libc.
- ImageMagick's Convert program. Optional. For CAPTCHA challenges. - ImageMagick's Convert program. Optional. For CAPTCHA challenges.
- exmpp 0.9.2 or higher. Optional. For import/export XEP-0227 files. - exmpp 0.9.6 or higher. Optional. For import/export XEP-0227 files.
1. Compile and install on *nix systems 1. Compile and install on *nix systems

View File

@ -356,7 +356,7 @@ Don&#X2019;t use R14A or R14B because <A HREF="http://www.erlang.org/cgi-bin/ezm
</LI><LI CLASS="li-itemize">PAM library. Optional. For Pluggable Authentication Modules (PAM). See section <A HREF="#pam">3.1.4</A>. </LI><LI CLASS="li-itemize">PAM library. Optional. For Pluggable Authentication Modules (PAM). See section <A HREF="#pam">3.1.4</A>.
</LI><LI CLASS="li-itemize">GNU Iconv 1.8 or higher, for the IRC Transport (mod_irc). Optional. Not needed on systems with GNU Libc. See section <A HREF="#modirc">3.3.8</A>. </LI><LI CLASS="li-itemize">GNU Iconv 1.8 or higher, for the IRC Transport (mod_irc). Optional. Not needed on systems with GNU Libc. See section <A HREF="#modirc">3.3.8</A>.
</LI><LI CLASS="li-itemize">ImageMagick&#X2019;s Convert program. Optional. For CAPTCHA challenges. See section <A HREF="#captcha">3.1.8</A>. </LI><LI CLASS="li-itemize">ImageMagick&#X2019;s Convert program. Optional. For CAPTCHA challenges. See section <A HREF="#captcha">3.1.8</A>.
</LI><LI CLASS="li-itemize">exmpp 0.9.2 or higher. Optional. For import/export user data with <A HREF="http://xmpp.org/extensions/xep-0227.html">XEP-0227</A> XML files. </LI><LI CLASS="li-itemize">exmpp 0.9.6 or higher. Optional. For import/export user data with <A HREF="http://xmpp.org/extensions/xep-0227.html">XEP-0227</A> XML files.
</LI></UL><P> <A NAME="download"></A> </P><!--TOC subsection Download Source Code--> </LI></UL><P> <A NAME="download"></A> </P><!--TOC subsection Download Source Code-->
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc10">2.4.2</A>&#XA0;&#XA0;<A HREF="#download">Download Source Code</A></H3><!--SEC END --><P> <A NAME="download"></A> <H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc10">2.4.2</A>&#XA0;&#XA0;<A HREF="#download">Download Source Code</A></H3><!--SEC END --><P> <A NAME="download"></A>
</P><P>Released versions of <TT>ejabberd</TT> are available in the ProcessOne <TT>ejabberd</TT> downloads page: </P><P>Released versions of <TT>ejabberd</TT> are available in the ProcessOne <TT>ejabberd</TT> downloads page:
@ -2994,7 +2994,7 @@ The default option value is an empty list: <TT>[]</TT>.
This option sets the minimum informational entropy for passwords. The value <TT>Entropy</TT> This option sets the minimum informational entropy for passwords. The value <TT>Entropy</TT>
is a number of bits of entropy. The recommended minimum is 32 bits. is a number of bits of entropy. The recommended minimum is 32 bits.
The default is 0, i.e. no checks are performed. The default is 0, i.e. no checks are performed.
</DD><DT CLASS="dt-description"><B><TT>{welcome_message, Message}</TT></B></DT><DD CLASS="dd-description"> Set a welcome message that </DD><DT CLASS="dt-description"><B><TT>{welcome_message, {Subject, Body}}</TT></B></DT><DD CLASS="dd-description"> Set a welcome message that
is sent to each newly registered account. The first string is the subject, and is sent to each newly registered account. The first string is the subject, and
the second string is the message body. the second string is the message body.
In the body you can set a newline with the characters: <CODE>\n</CODE> In the body you can set a newline with the characters: <CODE>\n</CODE>

View File

@ -66,6 +66,7 @@
\newcommand{\module}[1]{\texttt{#1}} \newcommand{\module}[1]{\texttt{#1}}
\newcommand{\modadhoc}{\module{mod\_adhoc}} \newcommand{\modadhoc}{\module{mod\_adhoc}}
\newcommand{\modannounce}{\module{mod\_announce}} \newcommand{\modannounce}{\module{mod\_announce}}
\newcommand{\modblocking}{\module{mod\_blocking}}
\newcommand{\modcaps}{\module{mod\_caps}} \newcommand{\modcaps}{\module{mod\_caps}}
\newcommand{\modconfigure}{\module{mod\_configure}} \newcommand{\modconfigure}{\module{mod\_configure}}
\newcommand{\moddisco}{\module{mod\_disco}} \newcommand{\moddisco}{\module{mod\_disco}}
@ -80,6 +81,7 @@
\newcommand{\modoffline}{\module{mod\_offline}} \newcommand{\modoffline}{\module{mod\_offline}}
\newcommand{\modofflineodbc}{\module{mod\_offline\_odbc}} \newcommand{\modofflineodbc}{\module{mod\_offline\_odbc}}
\newcommand{\modping}{\module{mod\_ping}} \newcommand{\modping}{\module{mod\_ping}}
\newcommand{\modprescounter}{\module{mod\_pres\_counter}}
\newcommand{\modprivacy}{\module{mod\_privacy}} \newcommand{\modprivacy}{\module{mod\_privacy}}
\newcommand{\modprivacyodbc}{\module{mod\_privacy\_odbc}} \newcommand{\modprivacyodbc}{\module{mod\_privacy\_odbc}}
\newcommand{\modprivate}{\module{mod\_private}} \newcommand{\modprivate}{\module{mod\_private}}
@ -321,7 +323,7 @@ To compile \ejabberd{} on a `Unix-like' operating system, you need:
\item PAM library. Optional. For Pluggable Authentication Modules (PAM). See section \ref{pam}. \item PAM library. Optional. For Pluggable Authentication Modules (PAM). See section \ref{pam}.
\item GNU Iconv 1.8 or higher, for the IRC Transport (mod\_irc). Optional. Not needed on systems with GNU Libc. See section \ref{modirc}. \item GNU Iconv 1.8 or higher, for the IRC Transport (mod\_irc). Optional. Not needed on systems with GNU Libc. See section \ref{modirc}.
\item ImageMagick's Convert program. Optional. For CAPTCHA challenges. See section \ref{captcha}. \item ImageMagick's Convert program. Optional. For CAPTCHA challenges. See section \ref{captcha}.
\item exmpp 0.9.2 or higher. Optional. For import/export user data with \xepref{0227} XML files. \item exmpp 0.9.6 or higher. Optional. For import/export user data with \xepref{0227} XML files.
\end{itemize} \end{itemize}
\makesubsection{download}{Download Source Code} \makesubsection{download}{Download Source Code}
@ -1030,7 +1032,7 @@ However, the c2s and s2s connections to the domain \term{example.com} use the fi
\item Port 5269 listens for s2s connections with STARTTLS. The socket is set for IPv6 instead of IPv4. \item Port 5269 listens for s2s connections with STARTTLS. The socket is set for IPv6 instead of IPv4.
\item Port 3478 listens for STUN requests over UDP. \item Port 3478 listens for STUN requests over UDP.
\item Port 5280 listens for HTTP requests, and serves the HTTP Poll service. \item Port 5280 listens for HTTP requests, and serves the HTTP Poll service.
\item Port 5281 listens for HTTP requests, and serves the Web Admin using HTTPS as explained in \item Port 5281 listens for HTTP requests, using HTTPS to serve HTTP-Bind (BOSH) and the Web Admin as explained in
section~\ref{webadmin}. The socket only listens connections to the IP address 127.0.0.1. section~\ref{webadmin}. The socket only listens connections to the IP address 127.0.0.1.
\end{itemize} \end{itemize}
\begin{verbatim} \begin{verbatim}
@ -1059,6 +1061,7 @@ However, the c2s and s2s connections to the domain \term{example.com} use the fi
]}, ]},
{{5281, "127.0.0.1"}, ejabberd_http, [ {{5281, "127.0.0.1"}, ejabberd_http, [
web_admin, web_admin,
http_bind,
tls, {certfile, "/etc/ejabberd/server.pem"}, tls, {certfile, "/etc/ejabberd/server.pem"},
]} ]}
] ]
@ -1635,11 +1638,14 @@ The configurable options are:
\titem{\{captcha\_cmd, Path\}} \titem{\{captcha\_cmd, Path\}}
Full path to a script that generates the image. Full path to a script that generates the image.
The default value is an empty string: \term{""} The default value is an empty string: \term{""}
\titem{\{captcha\_host, Host\}} \titem{\{captcha\_host, ProtocolHostPort\}}
Host part of the URL sent to the user. ProtocolHostPort is a string with the host, and optionally the Protocol and Port number.
You can include the port number. It must identify where ejabberd listens for CAPTCHA requests.
The URL sent to the user is formed by: \term{http://Host/captcha/} The URL sent to the user is formed by: \term{Protocol://Host:Port/captcha/}
The default value is the first hostname configured. The default value is: protocol \term{http}, the first hostname configured, and port \term{80}.
If you specify a port number that does not match exactly an ejabberd listener
(because you are using a reverse proxy or other port-forwarding tool),
then you must specify the transfer protocol, as seen in the example below.
\end{description} \end{description}
Additionally, an \term{ejabberd\_http} listener must be enabled with the \term{captcha} option. Additionally, an \term{ejabberd\_http} listener must be enabled with the \term{captcha} option.
@ -1651,6 +1657,8 @@ Example configuration:
{captcha_cmd, "/lib/ejabberd/priv/bin/captcha.sh"}. {captcha_cmd, "/lib/ejabberd/priv/bin/captcha.sh"}.
{captcha_host, "example.org:5280"}. {captcha_host, "example.org:5280"}.
%% {captcha_host, "https://example.org:443"}.
%% {captcha_host, "http://example.com"}.
{listen, {listen,
[ [
@ -1863,6 +1871,7 @@ The following LDAP servers are tested with \ejabberd{}:
\item \footahref{http://www.microsoft.com/activedirectory/}{Active Directory} \item \footahref{http://www.microsoft.com/activedirectory/}{Active Directory}
(see section~\ref{ad}) (see section~\ref{ad})
\item \footahref{http://www.openldap.org/}{OpenLDAP} \item \footahref{http://www.openldap.org/}{OpenLDAP}
\item \footahref{http://www.communigate.com/}{CommuniGate Pro}
\item Normally any LDAP compatible server should work; inform us about your \item Normally any LDAP compatible server should work; inform us about your
success with a not-listed server so that we can list it here. success with a not-listed server so that we can list it here.
\end{itemize} \end{itemize}
@ -2526,6 +2535,7 @@ The following table lists all modules included in \ejabberd{}.
\hline \hline
\hline \modadhoc{} & Ad-Hoc Commands (\xepref{0050}) & \\ \hline \modadhoc{} & Ad-Hoc Commands (\xepref{0050}) & \\
\hline \ahrefloc{modannounce}{\modannounce{}} & Manage announcements & recommends \modadhoc{} \\ \hline \ahrefloc{modannounce}{\modannounce{}} & Manage announcements & recommends \modadhoc{} \\
\hline \modblocking{} & Simple Communications Blocking (\xepref{0191}) & \modprivacy{} \\
\hline \modcaps{} & Entity Capabilities (\xepref{0115}) & \\ \hline \modcaps{} & Entity Capabilities (\xepref{0115}) & \\
\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}) & \\
@ -2540,8 +2550,9 @@ The following table lists all modules included in \ejabberd{}.
\hline \ahrefloc{modoffline}{\modoffline{}} & Offline message storage (\xepref{0160}) & \\ \hline \ahrefloc{modoffline}{\modoffline{}} & Offline message storage (\xepref{0160}) & \\
\hline \ahrefloc{modoffline}{\modofflineodbc{}} & Offline message storage (\xepref{0160}) & supported DB (*) \\ \hline \ahrefloc{modoffline}{\modofflineodbc{}} & Offline message storage (\xepref{0160}) & supported DB (*) \\
\hline \ahrefloc{modping}{\modping{}} & XMPP Ping and periodic keepalives (\xepref{0199}) & \\ \hline \ahrefloc{modping}{\modping{}} & XMPP Ping and periodic keepalives (\xepref{0199}) & \\
\hline \ahrefloc{modprivacy}{\modprivacy{}} & Blocking Communication (XMPP IM) & \\ \hline \ahrefloc{modprescounter}{\modprivacy{}} & Detect presence subscription flood & \\
\hline \ahrefloc{modprivacy}{\modprivacyodbc{}} & Blocking Communication (XMPP IM) & supported DB (*) \\ \hline \ahrefloc{modprivacy}{\modprivacy{}} & Blocking Communication (\xepref{0016}) & \\
\hline \ahrefloc{modprivacy}{\modprivacyodbc{}} & Blocking Communication (\xepref{0016}) & supported DB (*) \\
\hline \ahrefloc{modprivate}{\modprivate{}} & Private XML Storage (\xepref{0049}) & \\ \hline \ahrefloc{modprivate}{\modprivate{}} & Private XML Storage (\xepref{0049}) & \\
\hline \ahrefloc{modprivate}{\modprivateodbc{}} & Private XML Storage (\xepref{0049}) & supported DB (*) \\ \hline \ahrefloc{modprivate}{\modprivateodbc{}} & Private XML Storage (\xepref{0049}) & supported DB (*) \\
\hline \ahrefloc{modproxy}{\modproxy{}} & SOCKS5 Bytestreams (\xepref{0065}) & \\ \hline \ahrefloc{modproxy}{\modproxy{}} & SOCKS5 Bytestreams (\xepref{0065}) & \\
@ -2625,15 +2636,16 @@ The syntax is:
Possible \term{Value} are: Possible \term{Value} are:
\begin{description} \begin{description}
\titem{no\_queue} All queries of a namespace with this processing discipline are \titem{no\_queue} All queries of a namespace with this processing discipline are
processed immediately. This also means that no other packets can be processed processed directly. This means that the XMPP connection that sends this IQ query gets blocked:
no other packets can be processed
until this one has been completely processed. Hence this discipline is not until this one has been completely processed. Hence this discipline is not
recommended if the processing of a query can take a relatively long time. recommended if the processing of a query can take a relatively long time.
\titem{one\_queue} In this case a separate queue is created for the processing \titem{one\_queue} In this case a separate queue is created for the processing
of IQ queries of a namespace with this discipline. In addition, the processing of IQ queries of a namespace with this discipline. In addition, the processing
of this queue is done in parallel with that of other packets. This discipline of this queue is done in parallel with that of other packets. This discipline
is most recommended. is most recommended.
\titem{\{queues, N\}} N separate queues are created to process the \titem{\{queues, N\}} N separate queues are created to process the
queries. The queries are thus process in parallel, but in a queries. The queries are thus processed in parallel, but in a
controlled way. controlled way.
\titem{parallel} For every packet with this discipline a separate Erlang process \titem{parallel} For every packet with this discipline a separate Erlang process
is spawned. Consequently, all these packets are processed in parallel. is spawned. Consequently, all these packets are processed in parallel.
@ -3564,6 +3576,39 @@ and if a client does not answer to the ping in less than 32 seconds, its connect
]}. ]}.
\end{verbatim} \end{verbatim}
\makesubsection{modprescounter}{\modprescounter{}}
\ind{modules!\modprescounter{}}
This module detects flood/spam in presence subscription stanza traffic.
If a user sends or receives more of those stanzas in a time interval,
the exceeding stanzas are silently dropped, and warning is logged.
Configuration options:
\begin{description}
\titem{\{count, StanzaNumber\}}\ind{options!count}
The number of subscription presence stanzas
(subscribe, unsubscribe, subscribed, unsubscribed)
allowed for any direction (input or output)
per time interval.
Please note that two users subscribing to each other usually generate
4 stanzas, so the recommended value is 4 or more.
The default value is: 5.
\titem{\{interval, Seconds\}}\ind{options!interval}
The time interval defined in seconds.
The default value is 60.
\end{description}
This example enables the module, and allows up to 5 presence subscription stanzas
to be sent or received by the users in 60 seconds:
\begin{verbatim}
{modules,
[
...
{mod_pres_counter, [{count, 5}, {interval, 60}]},
...
]}.
\end{verbatim}
\makesubsection{modprivacy}{\modprivacy{}} \makesubsection{modprivacy}{\modprivacy{}}
\ind{modules!\modprivacy{}}\ind{Blocking Communication}\ind{Privacy Rules}\ind{protocols!RFC 3921: XMPP IM} \ind{modules!\modprivacy{}}\ind{Blocking Communication}\ind{Privacy Rules}\ind{protocols!RFC 3921: XMPP IM}
@ -3788,10 +3833,11 @@ enables end users to use a \XMPP{} client to:
Options: Options:
\begin{description} \begin{description}
\titem{\{access, AccessName\}} \ind{options!access}This option can be configured to specify \titem{\{access, AccessName\}} \ind{options!access}
rules to restrict registration. If a rule returns `deny' on the requested Specify rules to restrict what usernames can be registered and unregistered.
user name, registration for that user name is denied. (there are no If a rule returns `deny' on the requested username,
restrictions by default). registration and unregistration of that user name is denied.
There are no restrictions by default.
\titem{\{access\_from, AccessName\}} \ind{options!access\_from}By default, \ejabberd{} \titem{\{access\_from, AccessName\}} \ind{options!access\_from}By default, \ejabberd{}
doesn't allow to register new accounts from s2s or existing c2s sessions. You can doesn't allow to register new accounts from s2s or existing c2s sessions. You can
change it by defining access rule in this option. Use with care: allowing registration change it by defining access rule in this option. Use with care: allowing registration
@ -3808,7 +3854,7 @@ Protect registrations with CAPTCHA (see section \ref{captcha}). The default is \
This option sets the minimum informational entropy for passwords. The value \term{Entropy} This option sets the minimum informational entropy for passwords. The value \term{Entropy}
is a number of bits of entropy. The recommended minimum is 32 bits. is a number of bits of entropy. The recommended minimum is 32 bits.
The default is 0, i.e. no checks are performed. The default is 0, i.e. no checks are performed.
\titem{\{welcome\_message, Message\}} \ind{options!welcomem}Set a welcome message that \titem{\{welcome\_message, \{Subject, Body\}\}} \ind{options!welcomem}Set a welcome message that
is sent to each newly registered account. The first string is the subject, and is sent to each newly registered account. The first string is the subject, and
the second string is the message body. the second string is the message body.
In the body you can set a newline with the characters: \verb|\n| In the body you can set a newline with the characters: \verb|\n|
@ -4029,11 +4075,13 @@ has a unique identification and the following parameters:
\item[Name] The name of the group, which will be displayed in the roster. \item[Name] The name of the group, which will be displayed in the roster.
\item[Description] The description of the group. This parameter does not affect \item[Description] The description of the group. This parameter does not affect
anything. anything.
\item[Members] A list of full JIDs of group members, entered one per line in \item[Members] A list of JIDs of group members, entered one per line in
the Web Admin. the Web Admin.
To put as members all the registered users in the virtual hosts, The special member directive \term{@all@}
you can use the special directive: @all@. represents all the registered users in the virtual host;
Note that this directive is designed for a small server with just a few hundred users. which is only recommended for a small server with just a few hundred users.
The special member directive \term{@online@}
represents the online users in the virtual host.
\item[Displayed groups] A list of groups that will be in the rosters of this \item[Displayed groups] A list of groups that will be in the rosters of this
group's members. group's members.
\end{description} \end{description}
@ -4964,6 +5012,9 @@ The command line parameters:
If using \term{-sname}, specify either this option or \term{ERL\_INETRC}. If using \term{-sname}, specify either this option or \term{ERL\_INETRC}.
\titem{-kernel inet\_dist\_listen\_min 4200 inet\_dist\_listen\_min 4210} \titem{-kernel inet\_dist\_listen\_min 4200 inet\_dist\_listen\_min 4210}
Define the first and last ports that \term{epmd} (section \ref{epmd}) can listen to. Define the first and last ports that \term{epmd} (section \ref{epmd}) can listen to.
\titem{-kernel inet\_dist\_use\_interface "\{ 127,0,0,1 \}"}
Define the IP address where this Erlang node listens for other nodes
connections (see section \ref{epmd}).
\titem{-detached} \titem{-detached}
Starts the Erlang system detached from the system console. Starts the Erlang system detached from the system console.
Useful for running daemons and backgrounds processes. Useful for running daemons and backgrounds processes.
@ -5376,6 +5427,12 @@ The Erlang command-line parameter used internally is, for example:
\begin{verbatim} \begin{verbatim}
erl ... -kernel inet_dist_listen_min 4370 inet_dist_listen_max 4375 erl ... -kernel inet_dist_listen_min 4370 inet_dist_listen_max 4375
\end{verbatim} \end{verbatim}
It is also possible to configure in \term{ejabberdctl.cfg}
the network interface where the Erlang node will listen and accept connections.
The Erlang command-line parameter used internally is, for example:
\begin{verbatim}
erl ... -kernel inet_dist_use_interface "{127,0,0,1}"
\end{verbatim}
\makesection{cookie}{Erlang Cookie} \makesection{cookie}{Erlang Cookie}

View File

@ -213,6 +213,7 @@ install: all
sed -e "s*@ctlscriptpath@*$(SBINDIR)*" \ sed -e "s*@ctlscriptpath@*$(SBINDIR)*" \
-e "s*@installuser@*$(INIT_USER)*" ejabberd.init.template \ -e "s*@installuser@*$(INIT_USER)*" ejabberd.init.template \
> ejabberd.init > ejabberd.init
chmod 755 ejabberd.init
# #
# Binary Erlang files # Binary Erlang files
install -d $(BEAMDIR) install -d $(BEAMDIR)

View File

@ -5,7 +5,7 @@
%%% Created : 18 Jan 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 18 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 31 Oct 2005 by Magnus Henoch <henoch@dtek.chalmers.se> %%% Created : 31 Oct 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -1,6 +1,6 @@
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -6,7 +6,7 @@
%%% Created : 29 Aug 2010 by Evgeniy Khramtsov <ekhramtsov@process-one.net> %%% Created : 29 Aug 2010 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -6,7 +6,7 @@
%%% Created : 30 Aug 2010 by Evgeniy Khramtsov <ekhramtsov@process-one.net> %%% Created : 30 Aug 2010 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 27 Jan 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 27 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -6,7 +6,7 @@
%%% Created : 23 Aug 2005 by Magnus Henoch <henoch@dtek.chalmers.se> %%% Created : 23 Aug 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 11 Mar 2003 by Alexey Shchepin <alexey@sevcom.net> %%% Created : 11 Mar 2003 by Alexey Shchepin <alexey@sevcom.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -475,10 +475,14 @@
%%{captcha_cmd, "/lib/ejabberd/priv/bin/captcha.sh"}. %%{captcha_cmd, "/lib/ejabberd/priv/bin/captcha.sh"}.
%% %%
%% Host part of the URL sent to the user. %% Host for the URL and port where ejabberd listens for CAPTCHA requests.
%% %%
%%{captcha_host, "example.org:5280"}. %%{captcha_host, "example.org:5280"}.
%%
%% Limit CAPTCHA calls per minute for JID/IP to avoid DoS.
%%
%%{captcha_limit, 5}.
%%%. ======= %%%. =======
%%%' MODULES %%%' MODULES
@ -490,6 +494,7 @@
[ [
{mod_adhoc, []}, {mod_adhoc, []},
{mod_announce, [{access, announce}]}, % recommends mod_adhoc {mod_announce, [{access, announce}]}, % recommends mod_adhoc
{mod_blocking,[]}, % requires mod_privacy
{mod_caps, []}, {mod_caps, []},
{mod_configure,[]}, % requires mod_adhoc {mod_configure,[]}, % requires mod_adhoc
{mod_disco, []}, {mod_disco, []},
@ -511,6 +516,7 @@
%%{mod_muc_log,[]}, %%{mod_muc_log,[]},
{mod_offline, [{access_max_user_messages, max_user_offline_messages}]}, {mod_offline, [{access_max_user_messages, max_user_offline_messages}]},
{mod_ping, []}, {mod_ping, []},
%%{mod_pres_counter,[{count, 5}, {interval, 60}]},
{mod_privacy, []}, {mod_privacy, []},
{mod_private, []}, {mod_private, []},
%%{mod_proxy65,[]}, %%{mod_proxy65,[]},
@ -545,9 +551,17 @@
%% %%
%%{registration_watchers, ["admin1@example.org"]}, %%{registration_watchers, ["admin1@example.org"]},
%%
%% Only clients in the server machine can register accounts
%%
{ip_access, [{allow, "127.0.0.0/8"}, {ip_access, [{allow, "127.0.0.0/8"},
{deny, "0.0.0.0/0"}]}, {deny, "0.0.0.0/0"}]},
%%
%% Local c2s or remote s2s users cannot register accounts
%%
%%{access_from, deny},
{access, register} {access, register}
]}, ]},
%%{mod_register_web, [ %%{mod_register_web, [

View File

@ -5,7 +5,7 @@
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net> %%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -1,6 +1,6 @@
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -1,6 +1,20 @@
#! /bin/sh #! /bin/sh
### BEGIN INIT INFO
# Provides: ejabberd
# Required-Start: $remote_fs $network $named $time
# Required-Stop: $remote_fs $network $named $time
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Starts ejabberd jabber server
# Description: Starts ejabberd jabber server, an XMPP
# compliant server written in Erlang.
### END INIT INFO
# chkconfig: 2345 90 10
# description: ejabberd XMPP server
set -o errexit set -o errexit
set -o nounset
DIR=@ctlscriptpath@ DIR=@ctlscriptpath@
CTL="$DIR"/ejabberdctl CTL="$DIR"/ejabberdctl
@ -32,14 +46,17 @@ case "$1" in
su - $USER -c "$CTL stopped" su - $USER -c "$CTL stopped"
echo "done." echo "done."
;; ;;
status)
test -x "$CTL" || exit 0
echo "Getting ejabberd status..."
su - $USER -c "$CTL status"
;;
force-reload|restart) force-reload|restart)
"$0" stop "$0" stop
"$0" start "$0" start
;; ;;
*) *)
echo "Usage: $0 {start|stop|restart|force-reload}" echo "Usage: $0 {start|stop|restart|force-reload|status}"
exit 1 exit 1
esac esac

View File

@ -5,7 +5,7 @@
%%% Created : 7 May 2006 by Mickael Remond <mremond@process-one.net> %%% Created : 7 May 2006 by Mickael Remond <mremond@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -67,8 +67,9 @@ start(normal, _Args) ->
%ejabberd_debug:eprof_start(), %ejabberd_debug:eprof_start(),
%ejabberd_debug:fprof_start(), %ejabberd_debug:fprof_start(),
maybe_add_nameservers(), maybe_add_nameservers(),
{ok, Pid} = ejabberd_cluster:start(),
start_modules(), start_modules(),
ejabberd_cluster:announce(), ejabberd_cluster:announce(Pid),
ejabberd_node_groups:start(), ejabberd_node_groups:start(),
ejabberd_listener:start_listeners(), ejabberd_listener:start_listeners(),
?INFO_MSG("ejabberd ~s is started in the node ~p", [?VERSION, node()]), ?INFO_MSG("ejabberd ~s is started in the node ~p", [?VERSION, node()]),

View File

@ -5,7 +5,7 @@
%%% Created : 23 Nov 2002 by Alexey Shchepin <alexey@process-one.net> %%% Created : 23 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 17 Feb 2006 by Mickael Remond <mremond@process-one.net> %%% Created : 17 Feb 2006 by Mickael Remond <mremond@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -155,6 +155,7 @@ register_connection(SID, #jid{luser = LUser, lserver = LServer}, Info) ->
AuthModule = xml:get_attr_s(auth_module, Info), AuthModule = xml:get_attr_s(auth_module, Info),
case AuthModule == ?MODULE of case AuthModule == ?MODULE of
true -> true ->
ejabberd_hooks:run(register_user, LServer, [LUser, LServer]),
US = {LUser, LServer}, US = {LUser, LServer},
mnesia:async_dirty( mnesia:async_dirty(
fun() -> mnesia:write(#anonymous{us = US, sid=SID}) fun() -> mnesia:write(#anonymous{us = US, sid=SID})
@ -231,8 +232,8 @@ try_register(_User, _Server, _Password) ->
dirty_get_registered_users() -> dirty_get_registered_users() ->
[]. [].
get_vh_registered_users(_Server) -> get_vh_registered_users(Server) ->
[]. [{U, S} || {U, S, _R} <- ejabberd_sm:get_vh_session_list(Server)].
%% Return password of permanent user or false for anonymous users %% Return password of permanent user or false for anonymous users

View File

@ -5,7 +5,7 @@
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net> %%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net> %%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net> %%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net> %%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 5 Jul 2007 by Evgeniy Khramtsov <xram@jabber.ru> %%% Created : 5 Jul 2007 by Evgeniy Khramtsov <xram@jabber.ru>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net> %%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -194,7 +194,7 @@ stop(FsmRef) ->
?GEN_FSM:send_event(FsmRef, closed). ?GEN_FSM:send_event(FsmRef, closed).
migrate(FsmRef, Node, After) -> migrate(FsmRef, Node, After) ->
?GEN_FSM:send_all_state_event(FsmRef, {migrate, Node, After}). erlang:send_after(After, FsmRef, {migrate, Node}).
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm %%% Callback functions from gen_fsm
@ -231,6 +231,12 @@ init([{SockMod, Socket}, Opts, FSMLimitOpts]) ->
(_) -> false (_) -> false
end, Opts), end, Opts),
TLSOpts = [verify_none | TLSOpts1], TLSOpts = [verify_none | TLSOpts1],
Redirect = case lists:keysearch(redirect, 1, Opts) of
{value, {_, true}} ->
true;
_ ->
false
end,
IP = case lists:keysearch(frontend_ip, 1, Opts) of IP = case lists:keysearch(frontend_ip, 1, Opts) of
{value, {_, IP1}} -> {value, {_, IP1}} ->
IP1; IP1;
@ -246,7 +252,7 @@ init([{SockMod, Socket}, Opts, FSMLimitOpts]) ->
false -> false ->
Socket1 = Socket1 =
if if
TLSEnabled -> TLSEnabled andalso SockMod /= ejabberd_frontend_socket ->
SockMod:starttls(Socket, TLSOpts); SockMod:starttls(Socket, TLSOpts);
true -> true ->
Socket Socket
@ -265,8 +271,20 @@ init([{SockMod, Socket}, Opts, FSMLimitOpts]) ->
access = Access, access = Access,
shaper = Shaper, shaper = Shaper,
ip = IP, ip = IP,
redirect = Redirect,
fsm_limit_opts = FSMLimitOpts}, fsm_limit_opts = FSMLimitOpts},
{ok, wait_for_stream, StateData, ?C2S_OPEN_TIMEOUT} case get_jid_from_opts(Opts) of
{ok, #jid{user = U, server = Server, resource = R} = JID} ->
?GEN_FSM:send_event(self(), open_session),
{ok, wait_for_session, StateData#state{
user = U,
server = Server,
resource = R,
jid = JID,
lang = ""}};
_ ->
{ok, wait_for_stream, StateData, ?C2S_OPEN_TIMEOUT}
end
end; end;
init([StateName, StateData, _FSMLimitOpts]) -> init([StateName, StateData, _FSMLimitOpts]) ->
MRef = (StateData#state.sockmod):monitor(StateData#state.socket), MRef = (StateData#state.sockmod):monitor(StateData#state.socket),
@ -282,6 +300,7 @@ init([StateName, StateData, _FSMLimitOpts]) ->
El -> El ->
get_priority_from_presence(El) get_priority_from_presence(El)
end, end,
ejabberd_sm:drop_session(StateData#state.sid),
ejabberd_sm:open_session( ejabberd_sm:open_session(
SID, SID,
StateData#state.user, StateData#state.user,
@ -289,8 +308,11 @@ init([StateName, StateData, _FSMLimitOpts]) ->
StateData#state.resource, StateData#state.resource,
Priority, Priority,
Info), Info),
%%ejabberd_sm:drop_session(StateData#state.sid),
NewStateData = StateData#state{sid = SID, socket_monitor = MRef}, NewStateData = StateData#state{sid = SID, socket_monitor = MRef},
{ok, StateName, NewStateData}; StateData2 = change_reception(NewStateData, true),
StateData3 = start_keepalive_timer(StateData2),
{ok, StateName, StateData3};
true -> true ->
{ok, StateName, StateData#state{socket_monitor = MRef}} {ok, StateName, StateData#state{socket_monitor = MRef}}
end. end.
@ -581,45 +603,55 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
"(~w) Accepted legacy authentication for ~s by ~p", "(~w) Accepted legacy authentication for ~s by ~p",
[StateData#state.socket, [StateData#state.socket,
jlib:jid_to_string(JID), AuthModule]), jlib:jid_to_string(JID), AuthModule]),
SID = {now(), self()}, case need_redirect(StateData#state{user = U}) of
Conn = get_conn_type(StateData), {true, Host} ->
%% Info = [{ip, StateData#state.ip}, {conn, Conn}, ?INFO_MSG("(~w) Redirecting ~s to ~s",
%% {auth_module, AuthModule}], [StateData#state.socket,
Res1 = jlib:make_result_iq_reply(El), jlib:jid_to_string(JID), Host]),
Res = setelement(4, Res1, []), send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
send_element(StateData, Res), send_trailer(StateData),
%% ejabberd_sm:open_session( {stop, normal, StateData};
%% SID, U, StateData#state.server, R, Info), false ->
change_shaper(StateData, JID), SID = {now(), self()},
{Fs, Ts} = ejabberd_hooks:run_fold( Conn = get_conn_type(StateData),
roster_get_subscription_lists, Res1 = jlib:make_result_iq_reply(El),
StateData#state.server, Res = setelement(4, Res1, []),
{[], []}, send_element(StateData, Res),
[U, StateData#state.server]), change_shaper(StateData, JID),
LJID = jlib:jid_tolower( {Fs, Ts} = ejabberd_hooks:run_fold(
jlib:jid_remove_resource(JID)), roster_get_subscription_lists,
Fs1 = [LJID | Fs], StateData#state.server,
Ts1 = [LJID | Ts], {[], []},
PrivList = [U, StateData#state.server]),
ejabberd_hooks:run_fold( LJID = jlib:jid_tolower(
privacy_get_user_list, StateData#state.server, jlib:jid_remove_resource(JID)),
#userlist{}, Fs1 = [LJID | Fs],
[U, StateData#state.server]), Ts1 = [LJID | Ts],
NewStateData = StateData#state{ PrivList =
user = U, ejabberd_hooks:run_fold(
resource = R, privacy_get_user_list,
jid = JID, StateData#state.server,
sid = SID, #userlist{},
conn = Conn, [U, StateData#state.server]),
auth_module = AuthModule, NewStateData =
pres_f = ?SETS:from_list(Fs1), StateData#state{
pres_t = ?SETS:from_list(Ts1), user = U,
privacy_list = PrivList}, resource = R,
DebugFlag = ejabberd_hooks:run_fold(c2s_debug_start_hook, jid = JID,
NewStateData#state.server, sid = SID,
false, conn = Conn,
[self(), NewStateData]), auth_module = AuthModule,
maybe_migrate(session_established, NewStateData#state{debug=DebugFlag}); pres_f = ?SETS:from_list(Fs1),
pres_t = ?SETS:from_list(Ts1),
privacy_list = PrivList},
DebugFlag = ejabberd_hooks:run_fold(
c2s_debug_start_hook,
NewStateData#state.server,
false,
[self(), NewStateData]),
maybe_migrate(session_established,
NewStateData#state{debug=DebugFlag})
end;
_ -> _ ->
?INFO_MSG( ?INFO_MSG(
"(~w) Failed legacy authentication for ~s", "(~w) Failed legacy authentication for ~s",
@ -711,21 +743,30 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
Mech, Mech,
ClientIn) of ClientIn) of
{ok, Props} -> {ok, Props} ->
catch (StateData#state.sockmod):reset_stream( catch (StateData#state.sockmod):reset_stream(
StateData#state.socket), StateData#state.socket),
send_element(StateData, U = xml:get_attr_s(username, Props),
{xmlelement, "success", AuthModule = xml:get_attr_s(auth_module, Props),
[{"xmlns", ?NS_SASL}], []}), ?INFO_MSG("(~w) Accepted authentication for ~s by ~p",
U = xml:get_attr_s(username, Props), [StateData#state.socket, U, AuthModule]),
AuthModule = xml:get_attr_s(auth_module, Props), case need_redirect(StateData#state{user = U}) of
?INFO_MSG("(~w) Accepted authentication for ~s by ~p", {true, Host} ->
[StateData#state.socket, U, AuthModule]), ?INFO_MSG("(~w) Redirecting ~s to ~s",
fsm_next_state(wait_for_stream, [StateData#state.socket, U, Host]),
StateData#state{ send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
streamid = new_id(), send_trailer(StateData),
authenticated = true, {stop, normal, StateData};
auth_module = AuthModule, false ->
user = U }); send_element(StateData,
{xmlelement, "success",
[{"xmlns", ?NS_SASL}], []}),
fsm_next_state(wait_for_stream,
StateData#state{
streamid = new_id(),
authenticated = true,
auth_module = AuthModule,
user = U })
end;
{continue, ServerOut, NewSASLState} -> {continue, ServerOut, NewSASLState} ->
send_element(StateData, send_element(StateData,
{xmlelement, "challenge", {xmlelement, "challenge",
@ -865,20 +906,29 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
ClientIn) of ClientIn) of
{ok, Props} -> {ok, Props} ->
catch (StateData#state.sockmod):reset_stream( catch (StateData#state.sockmod):reset_stream(
StateData#state.socket), StateData#state.socket),
send_element(StateData,
{xmlelement, "success",
[{"xmlns", ?NS_SASL}], []}),
U = xml:get_attr_s(username, Props), U = xml:get_attr_s(username, Props),
AuthModule = xml:get_attr_s(auth_module, Props), AuthModule = xml:get_attr_s(auth_module, Props),
?INFO_MSG("(~w) Accepted authentication for ~s by ~p", ?INFO_MSG("(~w) Accepted authentication for ~s by ~p",
[StateData#state.socket, U, AuthModule]), [StateData#state.socket, U, AuthModule]),
fsm_next_state(wait_for_stream, case need_redirect(StateData#state{user = U}) of
StateData#state{ {true, Host} ->
streamid = new_id(), ?INFO_MSG("(~w) Redirecting ~s to ~s",
authenticated = true, [StateData#state.socket, U, Host]),
auth_module = AuthModule, send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
user = U}); send_trailer(StateData),
{stop, normal, StateData};
false ->
send_element(StateData,
{xmlelement, "success",
[{"xmlns", ?NS_SASL}], []}),
fsm_next_state(wait_for_stream,
StateData#state{
streamid = new_id(),
authenticated = true,
auth_module = AuthModule,
user = U})
end;
{continue, ServerOut, NewSASLState} -> {continue, ServerOut, NewSASLState} ->
send_element(StateData, send_element(StateData,
{xmlelement, "challenge", {xmlelement, "challenge",
@ -1045,6 +1095,11 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
fsm_next_state(wait_for_session, StateData) fsm_next_state(wait_for_session, StateData)
end; end;
wait_for_session(open_session, StateData) ->
El = {xmlelement, "iq", [{"type", "set"}, {"id", "session"}],
[{xmlelement, "session", [{"xmlns", ?NS_SESSION}], []}]},
wait_for_session({xmlstreamelement, El}, StateData);
wait_for_session(timeout, StateData) -> wait_for_session(timeout, StateData) ->
{stop, normal, StateData}; {stop, normal, StateData};
@ -1172,7 +1227,9 @@ session_established2(El, StateData) ->
end; end;
"iq" -> "iq" ->
case jlib:iq_query_info(NewEl) of case jlib:iq_query_info(NewEl) of
#iq{xmlns = ?NS_PRIVACY} = IQ -> #iq{xmlns = Xmlns} = IQ
when Xmlns == ?NS_PRIVACY;
Xmlns == ?NS_BLOCKING ->
ejabberd_hooks:run( ejabberd_hooks:run(
user_send_packet, user_send_packet,
Server, Server,
@ -1230,9 +1287,6 @@ session_established2(El, StateData) ->
%% {next_state, NextStateName, NextStateData, Timeout} | %% {next_state, NextStateName, NextStateData, Timeout} |
%% {stop, Reason, NewStateData} %% {stop, Reason, NewStateData}
%%---------------------------------------------------------------------- %%----------------------------------------------------------------------
handle_event({migrate, Node, After}, StateName, StateData) when Node /= node() ->
fsm_migrate(StateName, StateData, Node, After * 2);
handle_event({add_rosteritem, IJID, ISubscription}, StateName, StateData) -> handle_event({add_rosteritem, IJID, ISubscription}, StateName, StateData) ->
NewStateData = roster_change(IJID, ISubscription, StateData), NewStateData = roster_change(IJID, ISubscription, StateData),
fsm_next_state(StateName, NewStateData); fsm_next_state(StateName, NewStateData);
@ -1433,6 +1487,9 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
send_element(StateData, PrivPushEl), send_element(StateData, PrivPushEl),
{false, Attrs, StateData#state{privacy_list = NewPL}} {false, Attrs, StateData#state{privacy_list = NewPL}}
end; end;
[{blocking, What}] ->
route_blocking(What, StateData),
{false, Attrs, StateData};
_ -> _ ->
{false, Attrs, StateData} {false, Attrs, StateData}
end; end;
@ -1622,6 +1679,12 @@ handle_info({force_update_presence, LUser}, StateName,
StateData StateData
end, end,
{next_state, StateName, NewStateData}; {next_state, StateName, NewStateData};
handle_info({migrate, Node}, StateName, StateData) ->
if Node /= node() ->
fsm_migrate(StateName, StateData, Node, 0);
true ->
fsm_next_state(StateName, StateData)
end;
handle_info({broadcast, Type, From, Packet}, StateName, StateData) -> handle_info({broadcast, Type, From, Packet}, StateName, StateData) ->
Recipients = ejabberd_hooks:run_fold( Recipients = ejabberd_hooks:run_fold(
c2s_broadcast_recipients, StateData#state.server, c2s_broadcast_recipients, StateData#state.server,
@ -1698,7 +1761,7 @@ terminate(_Reason, StateName, StateData) ->
presence_broadcast( presence_broadcast(
StateData, From, StateData#state.pres_i, Packet); StateData, From, StateData#state.pres_i, Packet);
rebinded -> rebinded ->
ejabberd_sm:close_session( ejabberd_sm:close_migrated_session(
StateData#state.sid, StateData#state.sid,
StateData#state.user, StateData#state.user,
StateData#state.server, StateData#state.server,
@ -1969,24 +2032,12 @@ process_presence_probe(From, To, StateData) ->
(E) -> (E) ->
[E] [E]
end, PresEls), end, PresEls),
{xmlelement, "presence", PresAttrs, make_oor_presence(
[{xmlelement, "show", [], StateData, PresAttrs, PresEls1)
[{xmlcdata,
StateData#state.oor_show}]},
{xmlelement, "status", [],
[{xmlcdata,
StateData#state.oor_status}]}]
++ PresEls1}
end end
end, end,
Timestamp = StateData#state.pres_timestamp, Timestamp = StateData#state.pres_timestamp,
Packet1 = xml:append_subtags( Packet1 = maybe_add_delay(Packet, utc, To, "", Timestamp),
xml:remove_subtags(
Packet, "x", {"xmlns", ?NS_DELAY91}),
%% To is the one sending the presence (the target of the probe)
[jlib:timestamp_to_xml(Timestamp, utc, To, ""),
%% TODO: Delete the next line once XEP-0091 is Obsolete
jlib:timestamp_to_xml(Timestamp)]),
case ejabberd_hooks:run_fold( case ejabberd_hooks:run_fold(
privacy_check_packet, StateData#state.server, privacy_check_packet, StateData#state.server,
allow, allow,
@ -2422,7 +2473,7 @@ resend_offline_messages(StateData) ->
Rs when is_list(Rs) -> Rs when is_list(Rs) ->
lists:foreach( lists:foreach(
fun({route, fun({route,
From, To, {xmlelement, Name, Attrs, Els} = Packet}) -> From, To, {xmlelement, _Name, _Attrs, _Els} = Packet}) ->
Pass = case privacy_check_packet(StateData, From, To, Packet, in) of Pass = case privacy_check_packet(StateData, From, To, Packet, in) of
allow -> allow ->
true; true;
@ -2431,11 +2482,11 @@ resend_offline_messages(StateData) ->
end, end,
if if
Pass -> Pass ->
Attrs2 = jlib:replace_from_to_attrs( %% Attrs2 = jlib:replace_from_to_attrs(
jlib:jid_to_string(From), %% jlib:jid_to_string(From),
jlib:jid_to_string(To), %% jlib:jid_to_string(To),
Attrs), %% Attrs),
FixedPacket = {xmlelement, Name, Attrs2, Els}, %% FixedPacket = {xmlelement, Name, Attrs2, Els},
%% Use route instead of send_element to go through standard workflow %% Use route instead of send_element to go through standard workflow
ejabberd_router:route(From, To, Packet); ejabberd_router:route(From, To, Packet);
%% send_element(StateData, FixedPacket), %% send_element(StateData, FixedPacket),
@ -2526,23 +2577,25 @@ peerip(SockMod, Socket) ->
maybe_migrate(StateName, StateData) -> maybe_migrate(StateName, StateData) ->
PackedStateData = pack(StateData), PackedStateData = pack(StateData),
case ejabberd_cluster:get_node({StateData#state.user, #state{user = U, server = S, resource = R, sid = SID} = StateData,
StateData#state.server}) of case ejabberd_cluster:get_node({jlib:nodeprep(U), jlib:nameprep(S)}) of
Node when Node == node() -> Node when Node == node() ->
Conn = get_conn_type(StateData), Conn = get_conn_type(StateData),
Info = [{ip, StateData#state.ip}, {conn, Conn}, Info = [{ip, StateData#state.ip}, {conn, Conn},
{auth_module, StateData#state.auth_module}], {auth_module, StateData#state.auth_module}],
#state{user = U, server = S, resource = R, sid = SID} = StateData, Presence = StateData#state.pres_last,
ejabberd_sm:open_session(SID, U, S, R, Info), Priority =
case Presence of
undefined ->
undefined;
_ ->
get_priority_from_presence(Presence)
end,
ejabberd_sm:open_session(SID, U, S, R, Priority, Info),
StateData2 = change_reception(PackedStateData, true),
StateData3 = start_keepalive_timer(StateData2),
erlang:garbage_collect(), erlang:garbage_collect(),
case ejabberd_cluster:get_node_new({U, S}) of fsm_next_state(StateName, StateData3);
Node ->
ok;
NewNode ->
After = ejabberd_cluster:rehash_timeout(),
migrate(self(), NewNode, After)
end,
fsm_next_state(StateName, PackedStateData);
Node -> Node ->
fsm_migrate(StateName, PackedStateData, Node, 0) fsm_migrate(StateName, PackedStateData, Node, 0)
end. end.
@ -2627,12 +2680,7 @@ change_reception(#state{reception = true} = StateData, false) ->
"" -> "" ->
ok; ok;
_ -> _ ->
Packet = Packet = make_oor_presence(StateData),
{xmlelement, "presence", [],
[{xmlelement, "show", [],
[{xmlcdata, StateData#state.oor_show}]},
{xmlelement, "status", [],
[{xmlcdata, StateData#state.oor_status}]}]},
update_priority(0, Packet, StateData#state{reception = false}), update_priority(0, Packet, StateData#state{reception = false}),
presence_broadcast_to_trusted( presence_broadcast_to_trusted(
StateData, StateData,
@ -2775,7 +2823,14 @@ send_out_of_reception_message(StateData, From, To,
CBody = utf8_cut(Body, 100), CBody = utf8_cut(Body, 100),
case StateData#state.oor_send_from of case StateData#state.oor_send_from of
jid -> SFrom ++ ": " ++ CBody; jid -> SFrom ++ ": " ++ CBody;
username -> BFrom#jid.user ++ ": " ++ CBody; username ->
UnescapedFrom =
unescape(BFrom#jid.user),
UnescapedFrom ++ ": " ++ CBody;
name ->
Name = get_roster_name(
StateData, BFrom),
Name ++ ": " ++ CBody;
_ -> CBody _ -> CBody
end; end;
true -> true ->
@ -2810,6 +2865,23 @@ send_out_of_reception_message(StateData, From, To,
send_out_of_reception_message(StateData, _From, _To, _Packet) -> send_out_of_reception_message(StateData, _From, _To, _Packet) ->
StateData. StateData.
make_oor_presence(StateData) ->
make_oor_presence(StateData, [], []).
make_oor_presence(StateData, PresenceAttrs, PresenceEls) ->
ShowEl =
case StateData#state.oor_show of
"available" -> [];
_ ->
[{xmlelement, "show", [],
[{xmlcdata, StateData#state.oor_show}]}]
end,
{xmlelement, "presence", PresenceAttrs,
ShowEl ++
[{xmlelement, "status", [],
[{xmlcdata, StateData#state.oor_status}]}]
++ PresenceEls}.
utf8_cut(S, Bytes) -> utf8_cut(S, Bytes) ->
utf8_cut(S, [], [], Bytes + 1). utf8_cut(S, [], [], Bytes + 1).
@ -2825,6 +2897,47 @@ utf8_cut([C | S], Cur, Prev, Bytes) ->
utf8_cut(S, [C | Cur], Cur, Bytes - 1) utf8_cut(S, [C | Cur], Cur, Bytes - 1)
end. end.
-include("mod_roster.hrl").
get_roster_name(StateData, JID) ->
User = StateData#state.user,
Server = StateData#state.server,
RosterItems = ejabberd_hooks:run_fold(
roster_get, Server, [], [{User, Server}]),
JUser = JID#jid.luser,
JServer = JID#jid.lserver,
Item =
lists:foldl(
fun(_, Res = #roster{}) ->
Res;
(I, false) ->
case I#roster.jid of
{JUser, JServer, _} ->
I;
_ ->
false
end
end, false, RosterItems),
case Item of
false ->
unescape(JID#jid.user);
#roster{} ->
Item#roster.name
end.
unescape("") -> "";
unescape("\\20" ++ S) -> [$\s | unescape(S)];
unescape("\\22" ++ S) -> [$" | unescape(S)];
unescape("\\26" ++ S) -> [$& | unescape(S)];
unescape("\\27" ++ S) -> [$' | unescape(S)];
unescape("\\2f" ++ S) -> [$/ | unescape(S)];
unescape("\\3a" ++ S) -> [$: | unescape(S)];
unescape("\\3c" ++ S) -> [$< | unescape(S)];
unescape("\\3e" ++ S) -> [$> | unescape(S)];
unescape("\\40" ++ S) -> [$@ | unescape(S)];
unescape("\\5c" ++ S) -> [$\\ | unescape(S)];
unescape([C | S]) -> [C | unescape(S)].
cancel_timer(Timer) -> cancel_timer(Timer) ->
erlang:cancel_timer(Timer), erlang:cancel_timer(Timer),
@ -2873,17 +2986,13 @@ enqueue(StateData, From, To, Packet) ->
StateData#state{pres_queue = NewQueue} StateData#state{pres_queue = NewQueue}
end; end;
true -> true ->
CleanPacket = xml:remove_subtags( CleanPacket = xml:remove_subtags(Packet, "x", {"xmlns", ?NS_P1_PUSHED}),
xml:remove_subtags(Packet, "x", {"xmlns", ?NS_P1_PUSHED}),
"x", {"xmlns", ?NS_DELAY91}),
Packet2 = Packet2 =
case CleanPacket of case CleanPacket of
{xmlelement, "message" = Name, Attrs, Els} -> {xmlelement, "message", _, _} ->
{xmlelement, Name, Attrs, xml:append_subtags(
Els ++ maybe_add_delay(CleanPacket, utc, To, ""),
[jlib:timestamp_to_xml( [{xmlelement, "x", [{"xmlns", ?NS_P1_PUSHED}], []}]);
calendar:now_to_universal_time(now())),
{xmlelement, "x", [{"xmlns", ?NS_P1_PUSHED}], []}]};
_ -> _ ->
Packet Packet
end, end,
@ -3025,15 +3134,6 @@ rebind(StateData, JID, StreamID) ->
[StateData#state.socket, [StateData#state.socket,
jlib:jid_to_string(JID)]), jlib:jid_to_string(JID)]),
SID = {now(), self()}, SID = {now(), self()},
Conn = get_conn_type(NewStateData),
Info = [{ip, StateData#state.ip}, {conn, Conn},
{auth_module, NewStateData#state.auth_module}],
ejabberd_sm:open_session(
SID,
NewStateData#state.user,
NewStateData#state.server,
NewStateData#state.resource,
Info),
StateData2 = StateData2 =
NewStateData#state{ NewStateData#state{
socket = StateData#state.socket, socket = StateData#state.socket,
@ -3044,22 +3144,11 @@ rebind(StateData, JID, StreamID) ->
keepalive_timer = StateData#state.keepalive_timer, keepalive_timer = StateData#state.keepalive_timer,
ack_timer = undefined ack_timer = undefined
}, },
Presence = StateData2#state.pres_last,
case Presence of
undefined ->
ok;
_ ->
NewPriority = get_priority_from_presence(Presence),
update_priority(NewPriority, Presence, StateData2)
end,
send_element(StateData2, send_element(StateData2,
{xmlelement, "rebind", {xmlelement, "rebind",
[{"xmlns", ?NS_P1_REBIND}], [{"xmlns", ?NS_P1_REBIND}],
[]}), []}),
StateData3 = change_reception(StateData2, true), maybe_migrate(session_established, StateData2)
StateData4 = start_keepalive_timer(StateData3),
fsm_next_state(session_established,
StateData4)
after 1000 -> after 1000 ->
send_element(StateData, send_element(StateData,
{xmlelement, "failure", {xmlelement, "failure",
@ -3221,6 +3310,33 @@ check_x_attachment1([El | Els]) ->
check_x_attachment1(Els) check_x_attachment1(Els)
end. end.
%% TODO: Delete XEP-0091 stuff once it is Obsolete
maybe_add_delay(El, TZ, From, Desc) ->
maybe_add_delay(El, TZ, From, Desc, calendar:now_to_universal_time(now())).
maybe_add_delay({xmlelement, _, _, Els} = El, TZ, From, Desc, TimeStamp) ->
HasOldTS = lists:any(
fun({xmlelement, "x", Attrs, _}) ->
xml:get_attr_s("xmlns", Attrs) == ?NS_DELAY91;
(_) ->
false
end, Els),
HasNewTS = lists:any(
fun({xmlelement, "delay", Attrs, _}) ->
xml:get_attr_s("xmlns", Attrs) == ?NS_DELAY;
(_) ->
false
end, Els),
El1 = if not HasOldTS ->
xml:append_subtags(El, [jlib:timestamp_to_xml(TimeStamp)]);
true ->
El
end,
if not HasNewTS ->
xml:append_subtags(
El1, [jlib:timestamp_to_xml(TimeStamp, TZ, From, Desc)]);
true ->
El1
end.
send_from(El) -> send_from(El) ->
%% First test previous version attribute: %% First test previous version attribute:
@ -3233,6 +3349,7 @@ send_from(El) ->
case xml:get_path_s(El, [{elem, "body"}, {attr, "from"}]) of case xml:get_path_s(El, [{elem, "body"}, {attr, "from"}]) of
"jid" -> jid; "jid" -> jid;
"username" -> username; "username" -> username;
"name" -> name;
"none" -> none; "none" -> none;
_ -> jid _ -> jid
end end
@ -3260,6 +3377,51 @@ bounce_messages() ->
ok ok
end. end.
%%%----------------------------------------------------------------------
%%% XEP-0191
%%%----------------------------------------------------------------------
route_blocking(What, StateData) ->
SubEl =
case What of
{block, JIDs} ->
{xmlelement, "block",
[{"xmlns", ?NS_BLOCKING}],
lists:map(
fun(JID) ->
{xmlelement, "item",
[{"jid", jlib:jid_to_string(JID)}],
[]}
end, JIDs)};
{unblock, JIDs} ->
{xmlelement, "unblock",
[{"xmlns", ?NS_BLOCKING}],
lists:map(
fun(JID) ->
{xmlelement, "item",
[{"jid", jlib:jid_to_string(JID)}],
[]}
end, JIDs)};
unblock_all ->
{xmlelement, "unblock",
[{"xmlns", ?NS_BLOCKING}], []}
end,
PrivPushIQ =
#iq{type = set, xmlns = ?NS_BLOCKING,
id = "push",
sub_el = [SubEl]},
PrivPushEl =
jlib:replace_from_to(
jlib:jid_remove_resource(
StateData#state.jid),
StateData#state.jid,
jlib:iq_to_xml(PrivPushIQ)),
send_element(StateData, PrivPushEl),
%% No need to replace active privacy list here,
%% blocking pushes are always accompanied by
%% Privacy List pushes
ok.
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% JID Set memory footprint reduction code %%% JID Set memory footprint reduction code
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
@ -3333,3 +3495,39 @@ flash_policy_string() ->
++ ToPortsString ++ ++ ToPortsString ++
"\"/>\n" "\"/>\n"
"</cross-domain-policy>\n\0". "</cross-domain-policy>\n\0".
need_redirect(#state{redirect = true, user = User, server = Server}) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
case ejabberd_cluster:get_node({LUser, LServer}) of
Node when node() == Node ->
false;
Node ->
case rpc:call(Node, ejabberd_config,
get_local_option, [hostname], 5000) of
Host when is_list(Host) ->
{true, Host};
_ ->
false
end
end;
need_redirect(_) ->
false.
get_jid_from_opts(Opts) ->
case lists:keysearch(jid, 1, Opts) of
{value, {_, JIDValue}} ->
JID = case JIDValue of
{_U, _S, _R} ->
jlib:make_jid(JIDValue);
_ when is_binary(JIDValue) ->
jlib:string_to_jid(binary_to_list(JIDValue));
_ when is_list(JIDValue) ->
jlib:string_to_jid(JIDValue);
_ ->
JIDValue
end,
{ok, JID};
_ ->
error
end.

View File

@ -57,6 +57,7 @@
conn = unknown, conn = unknown,
auth_module = unknown, auth_module = unknown,
ip, ip,
redirect = false,
aux_fields = [], aux_fields = [],
fsm_limit_opts, fsm_limit_opts,
lang, lang,

View File

@ -6,7 +6,7 @@
%%% Created : 2 Nov 2007 by Mickael Remond <mremond@process-one.net> %%% Created : 2 Nov 2007 by Mickael Remond <mremond@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 26 Apr 2008 by Evgeniy Khramtsov <xramtsov@gmail.com> %%% Created : 26 Apr 2008 by Evgeniy Khramtsov <xramtsov@gmail.com>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -35,9 +35,9 @@
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]). terminate/2, code_change/3]).
-export([create_captcha/5, build_captcha_html/2, check_captcha/2, -export([create_captcha/6, build_captcha_html/2, check_captcha/2,
process_reply/1, process/2, is_feature_available/0, process_reply/1, process/2, is_feature_available/0,
create_captcha_x/4, create_captcha_x/5]). create_captcha_x/5, create_captcha_x/6]).
-include("jlib.hrl"). -include("jlib.hrl").
-include("ejabberd.hrl"). -include("ejabberd.hrl").
@ -50,8 +50,9 @@
-define(CAPTCHA_TEXT(Lang), translate:translate(Lang, "Enter the text you see")). -define(CAPTCHA_TEXT(Lang), translate:translate(Lang, "Enter the text you see")).
-define(CAPTCHA_LIFETIME, 120000). % two minutes -define(CAPTCHA_LIFETIME, 120000). % two minutes
-define(RPC_TIMEOUT, 5000). -define(RPC_TIMEOUT, 5000).
-define(LIMIT_PERIOD, 60*1000*1000). % one minute
-record(state, {}). -record(state, {limits = treap:empty()}).
-record(captcha, {id, pid, key, tref, args}). -record(captcha, {id, pid, key, tref, args}).
%%==================================================================== %%====================================================================
@ -64,10 +65,10 @@
start_link() -> start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
create_captcha(SID, From, To, Lang, Args) create_captcha(SID, From, To, Lang, Limiter, Args)
when is_list(Lang), is_list(SID), when is_list(Lang), is_list(SID),
is_record(From, jid), is_record(To, jid) -> is_record(From, jid), is_record(To, jid) ->
case create_image() of case create_image(Limiter) of
{ok, Type, Key, Image} -> {ok, Type, Key, Image} ->
Id = randoms:get_string() ++ "-" ++ ejabberd_cluster:node_id(), Id = randoms:get_string() ++ "-" ++ ejabberd_cluster:node_id(),
B64Image = jlib:encode_base64(binary_to_list(Image)), B64Image = jlib:encode_base64(binary_to_list(Image)),
@ -96,18 +97,18 @@ create_captcha(SID, From, To, Lang, Args)
OOB = {xmlelement, "x", [{"xmlns", ?NS_OOB}], OOB = {xmlelement, "x", [{"xmlns", ?NS_OOB}],
[{xmlelement, "url", [], [{xmlcdata, get_url(Id)}]}]}, [{xmlelement, "url", [], [{xmlcdata, get_url(Id)}]}]},
Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}), Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}),
ets:insert(captcha, #captcha{id=Id, pid=self(), key=Key, ets:insert(captcha, #captcha{id=Id, pid=self(), key=Key,
tref=Tref, args=Args}), tref=Tref, args=Args}),
{ok, Id, [Body, OOB, Captcha, Data]}; {ok, Id, [Body, OOB, Captcha, Data]};
_Err -> Err ->
error Err
end. end.
create_captcha_x(SID, To, Lang, HeadEls) -> create_captcha_x(SID, To, Lang, Limiter, HeadEls) ->
create_captcha_x(SID, To, Lang, HeadEls, []). create_captcha_x(SID, To, Lang, Limiter, HeadEls, []).
create_captcha_x(SID, To, Lang, HeadEls, TailEls) -> create_captcha_x(SID, To, Lang, Limiter, HeadEls, TailEls) ->
case create_image() of case create_image(Limiter) of
{ok, Type, Key, Image} -> {ok, Type, Key, Image} ->
Id = randoms:get_string() ++ "-" ++ ejabberd_cluster:node_id(), Id = randoms:get_string() ++ "-" ++ ejabberd_cluster:node_id(),
B64Image = jlib:encode_base64(binary_to_list(Image)), B64Image = jlib:encode_base64(binary_to_list(Image)),
@ -144,8 +145,8 @@ create_captcha_x(SID, To, Lang, HeadEls, TailEls) ->
Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}), Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}),
ets:insert(captcha, #captcha{id=Id, key=Key, tref=Tref}), ets:insert(captcha, #captcha{id=Id, key=Key, tref=Tref}),
{ok, [Captcha, Data]}; {ok, [Captcha, Data]};
_ -> Err ->
error Err
end. end.
%% @spec (Id::string(), Lang::string()) -> {FormEl, {ImgEl, TextEl, IdEl, KeyEl}} | captcha_not_found %% @spec (Id::string(), Lang::string()) -> {FormEl, {ImgEl, TextEl, IdEl, KeyEl}} | captcha_not_found
@ -242,16 +243,19 @@ process(_Handlers, #request{method='GET', lang=Lang, path=[_, Id]}) ->
ejabberd_web:error(not_found) ejabberd_web:error(not_found)
end; end;
process(_Handlers, #request{method='GET', path=[_, Id, "image"]}) -> process(_Handlers, #request{method='GET', path=[_, Id, "image"], ip = IP}) ->
{Addr, _Port} = IP,
case lookup_captcha(Id) of case lookup_captcha(Id) of
{ok, #captcha{key=Key}} -> {ok, #captcha{key=Key}} ->
case create_image(Key) of case create_image(Addr, Key) of
{ok, Type, _, Img} -> {ok, Type, _, Img} ->
{200, {200,
[{"Content-Type", Type}, [{"Content-Type", Type},
{"Cache-Control", "no-cache"}, {"Cache-Control", "no-cache"},
{"Last-Modified", httpd_util:rfc1123_date()}], {"Last-Modified", httpd_util:rfc1123_date()}],
Img}; Img};
{error, limit} ->
ejabberd_web:error(not_allowed);
_ -> _ ->
ejabberd_web:error(not_found) ejabberd_web:error(not_found)
end; end;
@ -288,6 +292,20 @@ init([]) ->
check_captcha_setup(), check_captcha_setup(),
{ok, #state{}}. {ok, #state{}}.
handle_call({is_limited, Limiter, RateLimit}, _From, State) ->
NowPriority = now_priority(),
CleanPriority = NowPriority + ?LIMIT_PERIOD,
Limits = clean_treap(State#state.limits, CleanPriority),
case treap:lookup(Limiter, Limits) of
{ok, _, Rate} when Rate >= RateLimit ->
{reply, true, State#state{limits = Limits}};
{ok, Priority, Rate} ->
NewLimits = treap:insert(Limiter, Priority, Rate+1, Limits),
{reply, false, State#state{limits = NewLimits}};
_ ->
NewLimits = treap:insert(Limiter, NowPriority, 1, Limits),
{reply, false, State#state{limits = NewLimits}}
end;
handle_call(_Request, _From, State) -> handle_call(_Request, _From, State) ->
{reply, bad_request, State}. {reply, bad_request, State}.
@ -329,11 +347,22 @@ code_change(_OldVsn, State, _Extra) ->
%% Reason = atom() %% Reason = atom()
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
create_image() -> create_image() ->
create_image(undefined).
create_image(Limiter) ->
%% Six numbers from 1 to 9. %% Six numbers from 1 to 9.
Key = string:substr(randoms:get_string(), 1, 6), Key = string:substr(randoms:get_string(), 1, 6),
create_image(Key). create_image(Limiter, Key).
create_image(Key) -> create_image(Limiter, Key) ->
case is_limited(Limiter) of
true ->
{error, limit};
false ->
do_create_image(Key)
end.
do_create_image(Key) ->
FileName = get_prog_name(), FileName = get_prog_name(),
Cmd = lists:flatten(io_lib:format("~s ~s", [FileName, Key])), Cmd = lists:flatten(io_lib:format("~s ~s", [FileName, Key])),
case cmd(Cmd) of case cmd(Cmd) of
@ -363,20 +392,82 @@ get_prog_name() ->
case ejabberd_config:get_local_option(captcha_cmd) of case ejabberd_config:get_local_option(captcha_cmd) of
FileName when is_list(FileName) -> FileName when is_list(FileName) ->
FileName; FileName;
_ -> Value when (Value == undefined) or (Value == "") ->
?DEBUG("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}) false
end. end.
get_url(Str) -> get_url(Str) ->
case ejabberd_config:get_local_option(captcha_host) of CaptchaHost = ejabberd_config:get_local_option(captcha_host),
Host when is_list(Host) -> case string:tokens(CaptchaHost, ":") of
[Host] ->
"http://" ++ Host ++ "/captcha/" ++ Str; "http://" ++ Host ++ "/captcha/" ++ Str;
["http"++_ = TransferProt, Host] ->
TransferProt ++ ":" ++ Host ++ "/captcha/" ++ Str;
[Host, PortString] ->
TransferProt = atom_to_list(get_transfer_protocol(PortString)),
TransferProt ++ "://" ++ Host ++ ":" ++ PortString ++ "/captcha/" ++ Str;
[TransferProt, Host, PortString] ->
TransferProt ++ ":" ++ Host ++ ":" ++ PortString ++ "/captcha/" ++ Str;
_ -> _ ->
"http://" ++ ?MYNAME ++ "/captcha/" ++ Str "http://" ++ ?MYNAME ++ "/captcha/" ++ Str
end. end.
get_transfer_protocol(PortString) ->
PortNumber = list_to_integer(PortString),
PortListeners = get_port_listeners(PortNumber),
get_captcha_transfer_protocol(PortListeners).
get_port_listeners(PortNumber) ->
AllListeners = ejabberd_config:get_local_option(listen),
lists:filter(
fun({{Port, _Ip, _Netp}, _Module1, _Opts1}) when Port == PortNumber ->
true;
(_) ->
false
end,
AllListeners).
get_captcha_transfer_protocol([]) ->
throw("The port number mentioned in captcha_host is not "
"a ejabberd_http listener with 'captcha' option. "
"Change the port number or specify http:// in that option.");
get_captcha_transfer_protocol([{{_Port, _Ip, tcp}, ejabberd_http, Opts}
| Listeners]) ->
case lists:member(captcha, Opts) of
true ->
case lists:member(tls, Opts) of
true ->
https;
false ->
http
end;
false ->
get_captcha_transfer_protocol(Listeners)
end;
get_captcha_transfer_protocol([_ | Listeners]) ->
get_captcha_transfer_protocol(Listeners).
is_limited(undefined) ->
false;
is_limited(Limiter) ->
case ejabberd_config:get_local_option(captcha_limit) of
Int when is_integer(Int), Int > 0 ->
case catch gen_server:call(?MODULE, {is_limited, Limiter, Int},
5000) of
true ->
true;
false ->
false;
Err ->
?ERROR_MSG("Call failed: ~p", [Err]),
false
end;
_ ->
false
end.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Function: cmd(Cmd) -> Data | {error, Reason} %% Function: cmd(Cmd) -> Data | {error, Reason}
%% Cmd = string() %% Cmd = string()
@ -425,28 +516,23 @@ return(Port, TRef, Result) ->
catch port_close(Port), catch port_close(Port),
Result. Result.
is_feature_enabled() ->
try get_prog_name() of
Prog when is_list(Prog) -> true
catch
_:_ -> false
end.
is_feature_available() -> is_feature_available() ->
case is_feature_enabled() of case get_prog_name() of
false -> false; Prog when is_list(Prog) -> true;
true -> false -> false
case create_image() of
{ok, _, _, _} -> true;
_Error -> false
end
end. end.
check_captcha_setup() -> check_captcha_setup() ->
case is_feature_enabled() andalso not is_feature_available() of case is_feature_available() of
true -> true ->
?CRITICAL_MSG("Captcha is enabled in the option captcha_cmd, " case create_image() of
"but it can't generate images.", []); {ok, _, _, _} ->
ok;
_Err ->
?CRITICAL_MSG("Captcha is enabled in the option captcha_cmd, "
"but it can't generate images.", []),
throw({error, captcha_cmd_enabled_but_fails})
end;
false -> false ->
ok ok
end. end.
@ -498,3 +584,21 @@ do_check_captcha(Id, ProvidedKey) ->
_ -> _ ->
captcha_not_found captcha_not_found
end. end.
clean_treap(Treap, CleanPriority) ->
case treap:is_empty(Treap) of
true ->
Treap;
false ->
{_Key, Priority, _Value} = treap:get_root(Treap),
if
Priority > CleanPriority ->
clean_treap(treap:delete_root(Treap), CleanPriority);
true ->
Treap
end
end.
now_priority() ->
{MSec, Sec, USec} = now(),
-((MSec*1000000 + Sec)*1000000 + USec).

View File

@ -5,7 +5,7 @@
%%% Created : 27 Feb 2008 by Mickael Remond <mremond@process-one.net> %%% Created : 27 Feb 2008 by Mickael Remond <mremond@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -10,8 +10,8 @@
-behaviour(gen_server). -behaviour(gen_server).
%% API %% API
-export([start_link/0, get_node/1, get_node_new/1, announce/0, shutdown/0, -export([start_link/0, get_node/1, get_node_new/1, announce/1, shutdown/0,
node_id/0, get_node_by_id/1, get_nodes/0, rehash_timeout/0]). node_id/0, get_node_by_id/1, get_nodes/0, rehash_timeout/0, start/0]).
%% gen_server callbacks %% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@ -22,15 +22,28 @@
-define(HASHTBL, nodes_hash). -define(HASHTBL, nodes_hash).
-define(HASHTBL_NEW, nodes_hash_new). -define(HASHTBL_NEW, nodes_hash_new).
-define(POINTS, 64). -define(POINTS, 64).
-define(REHASH_TIMEOUT, 30000). -define(REHASH_TIMEOUT, timer:seconds(30)).
-define(MIGRATE_TIMEOUT, timer:minutes(2)).
%%-define(REHASH_TIMEOUT, timer:seconds(10)).
%%-define(MIGRATE_TIMEOUT, timer:seconds(5)).
-define(LOCK, {migrate, node()}).
-record(state, {}). -record(state, {}).
%%==================================================================== %%====================================================================
%% API %% API
%%==================================================================== %%====================================================================
start() ->
ChildSpec = {?MODULE,
{?MODULE, start_link, []},
permanent,
brutal_kill,
worker,
[?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
start_link() -> start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). gen_server:start_link(?MODULE, [], []).
get_node(Key) -> get_node(Key) ->
Hash = erlang:phash2(Key), Hash = erlang:phash2(Key),
@ -44,8 +57,8 @@ get_nodes() ->
%% TODO %% TODO
mnesia:system_info(running_db_nodes). mnesia:system_info(running_db_nodes).
announce() -> announce(Pid) ->
gen_server:call(?MODULE, announce, infinity). gen_server:call(Pid, announce, infinity).
node_id() -> node_id() ->
integer_to_list(erlang:phash2(node())). integer_to_list(erlang:phash2(node())).
@ -80,13 +93,15 @@ shutdown() ->
%% gen_server callbacks %% gen_server callbacks
%%==================================================================== %%====================================================================
init([]) -> init([]) ->
{A, B, C} = now(),
random:seed(A, B, C),
net_kernel:monitor_nodes(true, [{node_type, visible}]), net_kernel:monitor_nodes(true, [{node_type, visible}]),
ets:new(?HASHTBL, [named_table, public, ordered_set]), ets:new(?HASHTBL, [named_table, public, ordered_set]),
ets:new(?HASHTBL_NEW, [named_table, public, ordered_set]), ets:new(?HASHTBL_NEW, [named_table, public, ordered_set]),
register_node(), register_node(),
AllNodes = mnesia:system_info(running_db_nodes), AllNodes = get_nodes(),
OtherNodes = case AllNodes of OtherNodes = case AllNodes of
[_] -> [_MyNode] ->
AllNodes; AllNodes;
_ -> _ ->
AllNodes -- [node()] AllNodes -- [node()]
@ -96,35 +111,59 @@ init([]) ->
{ok, #state{}}. {ok, #state{}}.
handle_call(announce, _From, State) -> handle_call(announce, _From, State) ->
case mnesia:system_info(running_db_nodes) of case global:set_lock(?LOCK, get_nodes(), 0) of
false ->
?INFO_MSG("Another node is recently attached to "
"the cluster and is being rebalanced. "
"Waiting for the rebalancing to be completed "
"before starting this node. "
"This may take serveral minutes. "
"Please, be patient.", []),
global:set_lock(?LOCK, get_nodes(), infinity);
true ->
ok
end,
case get_nodes() of
[_MyNode] -> [_MyNode] ->
ok; register(?MODULE, self()),
global:del_lock(?LOCK);
Nodes -> Nodes ->
OtherNodes = Nodes -- [node()], OtherNodes = Nodes -- [node()],
lists:foreach(
fun(Node) ->
{?MODULE, Node} ! {node_ready, node()}
end, OtherNodes),
?INFO_MSG("waiting for migration from nodes: ~w", ?INFO_MSG("waiting for migration from nodes: ~w",
[OtherNodes]), [OtherNodes]),
timer:sleep(?REHASH_TIMEOUT), {_Res, BadNodes} = gen_server:multi_call(
append_node(?HASHTBL, node()) OtherNodes, ?MODULE,
{node_ready, node()}, ?REHASH_TIMEOUT),
append_node(?HASHTBL, node()),
register(?MODULE, self()),
case OtherNodes -- BadNodes of
[] ->
global:del_lock(?LOCK);
WorkingNodes ->
gen_server:abcast(WorkingNodes, ?MODULE, {node_ready, node()}),
erlang:send_after(?MIGRATE_TIMEOUT, self(), del_lock)
end
end, end,
{reply, ok, State}; {reply, ok, State};
handle_call({node_ready, Node}, _From, State) ->
?INFO_MSG("node ~p is ready, preparing migration", [Node]),
append_node(?HASHTBL_NEW, Node),
ejabberd_hooks:run(node_up, [Node]),
{reply, ok, State};
handle_call(_Request, _From, State) -> handle_call(_Request, _From, State) ->
Reply = ok, Reply = ok,
{reply, Reply, State}. {reply, Reply, State}.
handle_cast({node_ready, Node}, State) ->
?INFO_MSG("adding node ~p to hash and starting migration", [Node]),
append_node(?HASHTBL, Node),
ejabberd_hooks:run(node_hash_update, [?MIGRATE_TIMEOUT]),
{noreply, State};
handle_cast(_Msg, State) -> handle_cast(_Msg, State) ->
{noreply, State}. {noreply, State}.
handle_info({node_ready, Node}, State) -> handle_info(del_lock, State) ->
?INFO_MSG("node ~p is ready, starting migration", [Node]), global:del_lock(?LOCK),
append_node(?HASHTBL_NEW, Node),
ejabberd_hooks:run(node_hash_update, [?REHASH_TIMEOUT]),
timer:sleep(?REHASH_TIMEOUT),
?INFO_MSG("adding node ~p to hash", [Node]),
append_node(?HASHTBL, Node),
{noreply, State}; {noreply, State};
handle_info({node_down, Node}, State) -> handle_info({node_down, Node}, State) ->
delete_node(?HASHTBL, Node), delete_node(?HASHTBL, Node),

View File

@ -5,7 +5,7 @@
%%% Created : 20 May 2008 by Badlop <badlop@process-one.net> %%% Created : 20 May 2008 by Badlop <badlop@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -1,6 +1,6 @@
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 14 Dec 2002 by Alexey Shchepin <alexey@process-one.net> %%% Created : 14 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -431,6 +431,8 @@ process_term(Term, State) ->
add_option(captcha_cmd, Cmd, State); add_option(captcha_cmd, Cmd, State);
{captcha_host, Host} -> {captcha_host, Host} ->
add_option(captcha_host, Host, State); add_option(captcha_host, Host, State);
{captcha_limit, Limit} ->
add_option(captcha_limit, Limit, State);
{ejabberdctl_access_commands, ACs} -> {ejabberdctl_access_commands, ACs} ->
add_option(ejabberdctl_access_commands, ACs, State); add_option(ejabberdctl_access_commands, ACs, State);
{loglevel, Loglevel} -> {loglevel, Loglevel} ->
@ -438,6 +440,8 @@ process_term(Term, State) ->
State; State;
{max_fsm_queue, N} -> {max_fsm_queue, N} ->
add_option(max_fsm_queue, N, State); add_option(max_fsm_queue, N, State);
{hostname, Host} ->
add_option(hostname, Host, State);
{_Opt, _Val} -> {_Opt, _Val} ->
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end, lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
State, State#state.hosts) State, State#state.hosts)

View File

@ -1,6 +1,6 @@
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 11 Jan 2004 by Alexey Shchepin <alexey@process-one.net> %%% Created : 11 Jan 2004 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -1,6 +1,6 @@
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 23 Aug 2006 by Alexey Shchepin <alexey@process-one.net> %%% Created : 23 Aug 2006 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 8 Aug 2004 by Alexey Shchepin <alexey@process-one.net> %%% Created : 8 Aug 2004 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net> %%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 30 Nov 2002 by Alexey Shchepin <alexey@process-one.net> %%% Created : 30 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -34,10 +34,12 @@
-export([route/3, -export([route/3,
route_iq/4, route_iq/4,
route_iq/5,
process_iq_reply/3, process_iq_reply/3,
register_iq_handler/4, register_iq_handler/4,
register_iq_handler/5, register_iq_handler/5,
register_iq_response_handler/4, register_iq_response_handler/4,
register_iq_response_handler/5,
unregister_iq_handler/2, unregister_iq_handler/2,
unregister_iq_response_handler/2, unregister_iq_response_handler/2,
refresh_iq_handlers/0, refresh_iq_handlers/0,
@ -123,23 +125,35 @@ route(From, To, Packet) ->
ok ok
end. end.
route_iq(From, To, #iq{type = Type} = IQ, F) when is_function(F) -> route_iq(From, To, IQ, F) ->
route_iq(From, To, IQ, F, undefined).
route_iq(From, To, #iq{type = Type} = IQ, F, Timeout) when is_function(F) ->
Packet = if Type == set; Type == get -> Packet = if Type == set; Type == get ->
ID = ejabberd_router:make_id(), ID = ejabberd_router:make_id(),
Host = From#jid.lserver, Host = From#jid.lserver,
register_iq_response_handler(Host, ID, undefined, F), register_iq_response_handler(Host, ID, undefined, F, Timeout),
jlib:iq_to_xml(IQ#iq{id = ID}); jlib:iq_to_xml(IQ#iq{id = ID});
true -> true ->
jlib:iq_to_xml(IQ) jlib:iq_to_xml(IQ)
end, end,
ejabberd_router:route(From, To, Packet). ejabberd_router:route(From, To, Packet).
register_iq_response_handler(_Host, ID, Module, Function) -> register_iq_response_handler(Host, ID, Module, Function) ->
TRef = erlang:start_timer(?IQ_TIMEOUT, ejabberd_local, ID), register_iq_response_handler(Host, ID, Module, Function, undefined).
register_iq_response_handler(_Host, ID, Module, Function, Timeout0) ->
Timeout = case Timeout0 of
undefined ->
?IQ_TIMEOUT;
N when is_integer(N), N > 0 ->
N
end,
TRef = erlang:start_timer(Timeout, ejabberd_local, ID),
ets:insert(iq_response, #iq_response{id = ID, ets:insert(iq_response, #iq_response{id = ID,
module = Module, module = Module,
function = Function, function = Function,
timer = TRef}). timer = TRef}).
register_iq_handler(Host, XMLNS, Module, Fun) -> register_iq_handler(Host, XMLNS, Module, Fun) ->
ejabberd_local ! {register_iq_handler, Host, XMLNS, Module, Fun}. ejabberd_local ! {register_iq_handler, Host, XMLNS, Module, Fun}.

View File

@ -5,7 +5,7 @@
%%% Created : 23 Oct 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 23 Oct 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -9,7 +9,7 @@
%%% Created : 29 Nov 2006 by Mickael Remond <mremond@process-one.net> %%% Created : 29 Nov 2006 by Mickael Remond <mremond@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 1 Nov 2006 by Alexey Shchepin <alexey@process-one.net> %%% Created : 1 Nov 2006 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 17 Jul 2008 by Pablo Polvorin <pablo.polvorin@process-one.net> %%% Created : 17 Jul 2008 by Pablo Polvorin <pablo.polvorin@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 10 Nov 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 10 Nov 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 27 Nov 2002 by Alexey Shchepin <alexey@process-one.net> %%% Created : 27 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 7 Dec 2002 by Alexey Shchepin <alexey@process-one.net> %%% Created : 7 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -203,9 +203,9 @@ migrate(After) ->
['$$']}]), ['$$']}]),
lists:foreach( lists:foreach(
fun([FromTo, Pid]) -> fun([FromTo, Pid]) ->
case ejabberd_cluster:get_node_new(FromTo) of case ejabberd_cluster:get_node(FromTo) of
Node when Node /= node() -> Node when Node /= node() ->
ejabberd_s2s_out:stop_connection(Pid, After * 2); ejabberd_s2s_out:stop_connection(Pid, After);
_ -> _ ->
ok ok
end end

View File

@ -5,7 +5,7 @@
%%% Created : 6 Dec 2002 by Alexey Shchepin <alexey@process-one.net> %%% Created : 6 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 6 Dec 2002 by Alexey Shchepin <alexey@process-one.net> %%% Created : 6 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -322,6 +322,10 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
{"jabber:server", "jabber:server:dialback", true} when {"jabber:server", "jabber:server:dialback", true} when
StateData#state.use_v10 -> StateData#state.use_v10 ->
{next_state, wait_for_features, StateData, ?FSMTIMEOUT}; {next_state, wait_for_features, StateData, ?FSMTIMEOUT};
%% Clause added to handle Tigase's workaround for an old ejabberd bug:
{"jabber:server", "jabber:server:dialback", true} when
not StateData#state.use_v10 ->
send_db_request(StateData);
{"jabber:server", "", true} when StateData#state.use_v10 -> {"jabber:server", "", true} when StateData#state.use_v10 ->
{next_state, wait_for_features, StateData#state{db_enabled = false}, ?FSMTIMEOUT}; {next_state, wait_for_features, StateData#state{db_enabled = false}, ?FSMTIMEOUT};
{NSProvided, DB, _} -> {NSProvided, DB, _} ->

View File

@ -5,7 +5,7 @@
%%% Created : 6 Dec 2002 by Alexey Shchepin <alexey@process-one.net> %%% Created : 6 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 24 Nov 2002 by Alexey Shchepin <alexey@process-one.net> %%% Created : 24 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -37,9 +37,11 @@
open_session/6, open_session/6,
close_session/4, close_session/4,
close_migrated_session/4, close_migrated_session/4,
drop_session/1,
check_in_subscription/6, check_in_subscription/6,
bounce_offline_message/3, bounce_offline_message/3,
disconnect_removed_user/2, disconnect_removed_user/2,
get_user_sessions/2,
get_user_resources/2, get_user_resources/2,
set_presence/7, set_presence/7,
unset_presence/6, unset_presence/6,
@ -59,6 +61,7 @@
get_session_pid/3, get_session_pid/3,
get_user_info/3, get_user_info/3,
get_user_ip/3, get_user_ip/3,
node_up/1,
migrate/1 migrate/1
]). ]).
@ -107,27 +110,37 @@ open_session(SID, User, Server, Resource, Priority, Info) ->
[SID, JID, Info]). [SID, JID, Info]).
close_session(SID, User, Server, Resource) -> close_session(SID, User, Server, Resource) ->
Info = do_close_session(SID, User, Server, Resource), Info = do_close_session(SID),
US = {jlib:nodeprep(User), jlib:nameprep(Server)},
case ejabberd_cluster:get_node_new(US) of
Node when Node /= node() ->
rpc:cast(Node, ?MODULE, drop_session, [SID]);
_ ->
ok
end,
JID = jlib:make_jid(User, Server, Resource), JID = jlib:make_jid(User, Server, Resource),
ejabberd_hooks:run(sm_remove_connection_hook, JID#jid.lserver, ejabberd_hooks:run(sm_remove_connection_hook, JID#jid.lserver,
[SID, JID, Info]). [SID, JID, Info]).
close_migrated_session(SID, User, Server, Resource) -> close_migrated_session(SID, User, Server, Resource) ->
Info = do_close_session(SID, User, Server, Resource), Info = do_close_session(SID),
JID = jlib:make_jid(User, Server, Resource), JID = jlib:make_jid(User, Server, Resource),
ejabberd_hooks:run(sm_remove_migrated_connection_hook, JID#jid.lserver, ejabberd_hooks:run(sm_remove_migrated_connection_hook, JID#jid.lserver,
[SID, JID, Info]). [SID, JID, Info]).
do_close_session(SID, User, Server, Resource) -> do_close_session(SID) ->
Info = case mnesia:dirty_read({session, SID}) of Info = case mnesia:dirty_read({session, SID}) of
[] -> []; [] -> [];
[#session{info=I}] -> I [#session{info=I}] -> I
end, end,
drop_session(SID),
Info.
drop_session(SID) ->
F = fun() -> F = fun() ->
mnesia:delete({session, SID}) mnesia:delete({session, SID})
end, end,
mnesia:sync_dirty(F), mnesia:sync_dirty(F).
Info.
check_in_subscription(Acc, User, Server, _JID, _Type, _Reason) -> check_in_subscription(Acc, User, Server, _JID, _Type, _Reason) ->
case ejabberd_auth:is_user_exists(User, Server) of case ejabberd_auth:is_user_exists(User, Server) of
@ -148,6 +161,18 @@ disconnect_removed_user(User, Server) ->
{xmlelement, "broadcast", [], {xmlelement, "broadcast", [],
[{exit, "User removed"}]}). [{exit, "User removed"}]}).
get_user_sessions(User, Server) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
US = {LUser, LServer},
case ejabberd_cluster:get_node({LUser, LServer}) of
Node when Node == node() ->
catch mnesia:dirty_index_read(session, US, #session.us);
Node ->
catch rpc:call(Node, mnesia, dirty_index_read,
[session, US, #session.us], 5000)
end.
get_user_resources(User, Server) -> get_user_resources(User, Server) ->
LUser = jlib:nodeprep(User), LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server), LServer = jlib:nameprep(Server),
@ -299,14 +324,33 @@ migrate(After) ->
['$$']}]), ['$$']}]),
lists:foreach( lists:foreach(
fun([US, Pid]) -> fun([US, Pid]) ->
case ejabberd_cluster:get_node_new(US) of case ejabberd_cluster:get_node(US) of
Node when Node /= node() -> Node when Node /= node() ->
ejabberd_c2s:migrate(Pid, Node, After); ejabberd_c2s:migrate(Pid, Node, random:uniform(After));
_ -> _ ->
ok ok
end end
end, Ss). end, Ss).
node_up(_Node) ->
copy_sessions(mnesia:dirty_first(session)).
copy_sessions('$end_of_table') ->
ok;
copy_sessions(Key) ->
case mnesia:dirty_read(session, Key) of
[#session{us = US} = Session] ->
case ejabberd_cluster:get_node_new(US) of
Node when node() /= Node ->
rpc:cast(Node, mnesia, dirty_write, [Session]);
_ ->
ok
end;
_ ->
ok
end,
copy_sessions(mnesia:dirty_next(session, Key)).
%%==================================================================== %%====================================================================
%% gen_server callbacks %% gen_server callbacks
%%==================================================================== %%====================================================================
@ -328,6 +372,7 @@ init([]) ->
mnesia:add_table_index(session, us), mnesia:add_table_index(session, us),
mnesia:add_table_copy(session, node(), ram_copies), mnesia:add_table_copy(session, node(), ram_copies),
ets:new(sm_iqtable, [named_table]), ets:new(sm_iqtable, [named_table]),
ejabberd_hooks:add(node_up, ?MODULE, node_up, 100),
ejabberd_hooks:add(node_hash_update, ?MODULE, migrate, 100), ejabberd_hooks:add(node_hash_update, ?MODULE, migrate, 100),
lists:foreach( lists:foreach(
fun(Host) -> fun(Host) ->
@ -339,7 +384,7 @@ init([]) ->
ejabberd_sm, disconnect_removed_user, 100) ejabberd_sm, disconnect_removed_user, 100)
end, ?MYHOSTS), end, ?MYHOSTS),
ejabberd_commands:register_commands(commands()), ejabberd_commands:register_commands(commands()),
start_dispatchers(),
{ok, #state{}}. {ok, #state{}}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -370,13 +415,19 @@ handle_cast(_Msg, State) ->
%% {stop, Reason, State} %% {stop, Reason, State}
%% Description: Handling all non call/cast messages %% Description: Handling all non call/cast messages
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
handle_info({route, From, To, Packet}, State) -> handle_info({route, From, To, Packet} = Msg, State) ->
case catch do_route(From, To, Packet) of case get_proc_num() of
{'EXIT', Reason} -> N when N > 1 ->
?ERROR_MSG("~p~nwhen processing: ~p", #jid{luser = U, lserver = S} = To,
[Reason, {From, To, Packet}]); get_proc_by_hash({U, S}) ! Msg;
_ -> _ ->
ok case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end
end, end,
{noreply, State}; {noreply, State};
handle_info({register_iq_handler, Host, XMLNS, Module, Function}, State) -> handle_info({register_iq_handler, Host, XMLNS, Module, Function}, State) ->
@ -405,8 +456,10 @@ handle_info(_Info, State) ->
%% The return value is ignored. %% The return value is ignored.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
terminate(_Reason, _State) -> terminate(_Reason, _State) ->
ejabberd_hooks:delete(node_up, ?MODULE, node_up, 100),
ejabberd_hooks:delete(node_hash_update, ?MODULE, migrate, 100), ejabberd_hooks:delete(node_hash_update, ?MODULE, migrate, 100),
ejabberd_commands:unregister_commands(commands()), ejabberd_commands:unregister_commands(commands()),
stop_dispatchers(),
ok. ok.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -420,7 +473,7 @@ code_change(_OldVsn, State, _Extra) ->
%%% Internal functions %%% Internal functions
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
set_session(SID, User, Server, Resource, Priority, Info) -> set_session({_, Pid} = SID, User, Server, Resource, Priority, Info) ->
LUser = jlib:nodeprep(User), LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server), LServer = jlib:nameprep(Server),
LResource = jlib:resourceprep(Resource), LResource = jlib:resourceprep(Resource),
@ -433,7 +486,31 @@ set_session(SID, User, Server, Resource, Priority, Info) ->
priority = Priority, priority = Priority,
info = Info}) info = Info})
end, end,
mnesia:sync_dirty(F). mnesia:sync_dirty(F),
case ejabberd_cluster:get_node_new(US) of
Node when node() /= Node ->
%% New node has just been added. But we may miss session records
%% copy procedure, so we copy the session record manually just
%% to make sure
rpc:cast(Node, mnesia, dirty_write,
[#session{sid = SID,
usr = USR,
us = US,
priority = Priority,
info = Info}]),
case ejabberd_cluster:get_node(US) of
Node when node() /= Node ->
%% Migration to new node has completed, and seems like
%% we missed it, so we migrate the session pid manually.
%% It is not a problem if we have already got migration
%% notification: dups are just ignored by the c2s pid.
ejabberd_c2s:migrate(Pid, Node, 0);
_ ->
ok
end;
_ ->
ok
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -805,6 +882,55 @@ user_resources(User, Server) ->
Resources = get_user_resources(User, Server), Resources = get_user_resources(User, Server),
lists:sort(Resources). lists:sort(Resources).
get_proc_num() ->
erlang:system_info(logical_processors).
get_proc_by_hash(Term) ->
N = erlang:phash2(Term, get_proc_num()) + 1,
get_proc(N).
get_proc(N) ->
list_to_atom(atom_to_list(?MODULE) ++ "_" ++ integer_to_list(N)).
start_dispatchers() ->
case get_proc_num() of
N when N > 1 ->
lists:foreach(
fun(I) ->
Pid = spawn(fun dispatch/0),
erlang:register(get_proc(I), Pid)
end, lists:seq(1, N));
_ ->
ok
end.
stop_dispatchers() ->
case get_proc_num() of
N when N > 1 ->
lists:foreach(
fun(I) ->
get_proc(I) ! stop
end, lists:seq(1, N));
_ ->
ok
end.
dispatch() ->
receive
{route, From, To, Packet} ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
end,
dispatch();
stop ->
stopped;
_ ->
dispatch()
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Update Mnesia tables %%% Update Mnesia tables

View File

@ -5,7 +5,7 @@
%%% Created : 23 Aug 2006 by Alexey Shchepin <alexey@process-one.net> %%% Created : 23 Aug 2006 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -178,13 +178,6 @@ init([]) ->
infinity, infinity,
supervisor, supervisor,
[ejabberd_tmp_sup]}, [ejabberd_tmp_sup]},
Cluster =
{ejabberd_cluster,
{ejabberd_cluster, start_link, []},
permanent,
brutal_kill,
worker,
[ejabberd_cluster]},
CacheTabSupervisor = CacheTabSupervisor =
{cache_tab_sup, {cache_tab_sup,
{cache_tab_sup, start_link, []}, {cache_tab_sup, start_link, []},
@ -194,7 +187,6 @@ init([]) ->
[cache_tab_sup]}, [cache_tab_sup]},
{ok, {{one_for_one, 10, 1}, {ok, {{one_for_one, 10, 1},
[Hooks, [Hooks,
Cluster,
SystemMonitor, SystemMonitor,
Router, Router,
SM, SM,

View File

@ -5,7 +5,7 @@
%%% Created : 21 Mar 2007 by Alexey Shchepin <alexey@process-one.net> %%% Created : 21 Mar 2007 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 18 Jul 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 18 Jul 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 27 Jan 2006 by Alexey Shchepin <alexey@process-one.net> %%% Created : 27 Jan 2006 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 19 Jan 2006 by Alexey Shchepin <alexey@process-one.net> %%% Created : 19 Jan 2006 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -1,5 +1,5 @@
/* /*
* ejabberd, Copyright (C) 2002-2010 ProcessOne * ejabberd, Copyright (C) 2002-2011 ProcessOne
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as

View File

@ -50,12 +50,23 @@
# #
#FIREWALL_WINDOW= #FIREWALL_WINDOW=
#.
#' INET_DIST_INTERFACE: IP address where this Erlang node listens other nodes
#
# This communication is used by ejabberdctl command line tool,
# and in a cluster of several ejabberd nodes.
# Notice that the IP address must be specified in the Erlang syntax.
#
# Default: {127,0,0,1}
#
INET_DIST_INTERFACE={127,0,0,1}
#. #.
#' ERL_PROCESSES: Maximum number of Erlang processes #' ERL_PROCESSES: Maximum number of Erlang processes
# #
# Erlang consumes a lot of lightweight processes. If there is a lot of activity # Erlang consumes a lot of lightweight processes. If there is a lot of activity
# on ejabberd so that the maximum number of processes is reached, people will # on ejabberd so that the maximum number of processes is reached, people will
# experiment greater latency times. As these processes are implemented in # experience greater latency times. As these processes are implemented in
# Erlang, and therefore not related to the operating system processes, you do # Erlang, and therefore not related to the operating system processes, you do
# not have to worry about allowing a huge number of them. # not have to worry about allowing a huge number of them.
# #

View File

@ -76,10 +76,12 @@ fi
NAME=-name NAME=-name
[ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && NAME=-sname [ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && NAME=-sname
if [ "$FIREWALL_WINDOW" = "" ] ; then KERNEL_OPTS=""
KERNEL_OPTS="" if [ "$FIREWALL_WINDOW" != "" ] ; then
else KERNEL_OPTS="${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
if [ "$INET_DIST_INTERFACE" != "" ] ; then
KERNEL_OPTS="${KERNEL_OPTS} -kernel inet_dist_use_interface \"${INET_DIST_INTERFACE}\""
fi fi
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS" ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS"
@ -321,13 +323,21 @@ ctlexec ()
{ {
CONN_NAME=$1; shift CONN_NAME=$1; shift
COMMAND=$@ COMMAND=$@
$EXEC_CMD "$ERL \
CTLEXEC="$ERL \
$NAME ${CONN_NAME} \ $NAME ${CONN_NAME} \
-noinput \ -noinput \
-hidden \ -hidden \
-pa $EJABBERD_EBIN_PATH \ -pa $EJABBERD_EBIN_PATH \
$KERNEL_OPTS \ $KERNEL_OPTS \
-s ejabberd_ctl -extra $ERLANG_NODE $COMMAND" -s ejabberd_ctl -extra $ERLANG_NODE"
# quote input from the command line
for i in $COMMAND; do
CTLEXEC="$CTLEXEC '$i'";
done
$EXEC_CMD "$CTLEXEC"
} }
# display ctl usage # display ctl usage

View File

@ -5,7 +5,7 @@
%%% Created : 22 Aug 2005 by Alexey Shchepin <alexey@process-one.net> %%% Created : 22 Aug 2005 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -1,6 +1,6 @@
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -6,7 +6,7 @@
%%% Author: Evgeniy Khramtsov <ekhramtsov@process-one.net> %%% Author: Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 12 Nov 2006 by Evgeniy Khramtsov <xram@jabber.ru> %%% Created : 12 Nov 2006 by Evgeniy Khramtsov <xram@jabber.ru>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 12 Oct 2006 by Mickael Remond <mremond@process-one.net> %%% Created : 12 Oct 2006 by Mickael Remond <mremond@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -1,5 +1,5 @@
/* /*
* ejabberd, Copyright (C) 2002-2010 ProcessOne * ejabberd, Copyright (C) 2002-2011 ProcessOne
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
@ -35,6 +35,7 @@
#define PARSE_FINAL_COMMAND 1 #define PARSE_FINAL_COMMAND 1
ei_x_buff event_buf; ei_x_buff event_buf;
ei_x_buff xmlns_buf;
typedef struct { typedef struct {
ErlDrvPort port; ErlDrvPort port;
@ -43,6 +44,32 @@ typedef struct {
static XML_Memory_Handling_Suite ms = {driver_alloc, driver_realloc, driver_free}; static XML_Memory_Handling_Suite ms = {driver_alloc, driver_realloc, driver_free};
void encode_name(const XML_Char *name)
{
char *name_start;
char *prefix_start;
char *buf;
int name_len, prefix_len, buf_len;
if ((name_start = strchr(name, '\n'))) {
if ((prefix_start = strchr(name_start+1, '\n'))) {
name_len = prefix_start - name_start;
prefix_len = strlen(prefix_start+1);
buf_len = prefix_len + name_len;
buf = driver_alloc(buf_len);
memcpy(buf, prefix_start+1, prefix_len);
memcpy(buf+prefix_len, name_start, name_len);
buf[prefix_len] = ':';
ei_x_encode_string_len(&event_buf, buf, buf_len);
driver_free(buf);
} else {
ei_x_encode_string(&event_buf, name_start+1);
};
} else {
ei_x_encode_string(&event_buf, name);
}
}
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)
@ -53,7 +80,10 @@ void *erlXML_StartElementHandler(expat_data *d,
ei_x_encode_tuple_header(&event_buf, 2); ei_x_encode_tuple_header(&event_buf, 2);
ei_x_encode_long(&event_buf, XML_START); ei_x_encode_long(&event_buf, XML_START);
ei_x_encode_tuple_header(&event_buf, 2); ei_x_encode_tuple_header(&event_buf, 2);
ei_x_encode_string(&event_buf, name); encode_name(name);
ei_x_append(&event_buf, &xmlns_buf);
ei_x_free(&xmlns_buf);
ei_x_new(&xmlns_buf);
for (i = 0; atts[i]; i += 2) {} for (i = 0; atts[i]; i += 2) {}
@ -64,7 +94,7 @@ void *erlXML_StartElementHandler(expat_data *d,
for (i = 0; atts[i]; i += 2) for (i = 0; atts[i]; i += 2)
{ {
ei_x_encode_tuple_header(&event_buf, 2); ei_x_encode_tuple_header(&event_buf, 2);
ei_x_encode_string(&event_buf, atts[i]); encode_name(atts[i]);
ei_x_encode_string(&event_buf, atts[i+1]); ei_x_encode_string(&event_buf, atts[i+1]);
} }
} }
@ -80,7 +110,7 @@ void *erlXML_EndElementHandler(expat_data *d,
ei_x_encode_list_header(&event_buf, 1); ei_x_encode_list_header(&event_buf, 1);
ei_x_encode_tuple_header(&event_buf, 2); ei_x_encode_tuple_header(&event_buf, 2);
ei_x_encode_long(&event_buf, XML_END); ei_x_encode_long(&event_buf, XML_END);
ei_x_encode_string(&event_buf, name); encode_name(name);
return NULL; return NULL;
} }
@ -95,12 +125,45 @@ void *erlXML_CharacterDataHandler(expat_data *d,
return NULL; return NULL;
} }
void *erlXML_StartNamespaceDeclHandler(expat_data *d,
const XML_Char *prefix,
const XML_Char *uri)
{
int prefix_len;
char *buf;
/* From the expat documentation:
"For a default namespace declaration (xmlns='...'),
the prefix will be null ...
... The URI will be null for the case where
the default namespace is being unset."
FIXME: I'm not quite sure what all that means */
if (uri == NULL)
return NULL;
ei_x_encode_list_header(&xmlns_buf, 1);
ei_x_encode_tuple_header(&xmlns_buf, 2);
if (prefix) {
prefix_len = strlen(prefix);
buf = driver_alloc(7 + prefix_len);
strcpy(buf, "xmlns:");
strcpy(buf+6, prefix);
ei_x_encode_string(&xmlns_buf, buf);
driver_free(buf);
} else {
ei_x_encode_string(&xmlns_buf, "xmlns");
};
ei_x_encode_string(&xmlns_buf, uri);
return NULL;
}
static ErlDrvData expat_erl_start(ErlDrvPort port, char *buff) 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_MM("UTF-8", &ms, NULL); d->parser = XML_ParserCreate_MM("UTF-8", &ms, "\n");
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);
@ -112,6 +175,11 @@ static ErlDrvData expat_erl_start(ErlDrvPort port, char *buff)
XML_SetCharacterDataHandler( XML_SetCharacterDataHandler(
d->parser, (XML_CharacterDataHandler)erlXML_CharacterDataHandler); d->parser, (XML_CharacterDataHandler)erlXML_CharacterDataHandler);
XML_SetStartNamespaceDeclHandler(
d->parser, (XML_StartNamespaceDeclHandler) erlXML_StartNamespaceDeclHandler);
XML_SetReturnNSTriplet(d->parser, 1);
XML_SetDefaultHandler(d->parser, NULL);
return (ErlDrvData)d; return (ErlDrvData)d;
} }
@ -138,6 +206,7 @@ static int expat_erl_control(ErlDrvData drv_data,
case PARSE_COMMAND: case PARSE_COMMAND:
case PARSE_FINAL_COMMAND: case PARSE_FINAL_COMMAND:
ei_x_new_with_version(&event_buf); ei_x_new_with_version(&event_buf);
ei_x_new(&xmlns_buf);
#ifdef ENABLE_FLASH_HACK #ifdef ENABLE_FLASH_HACK
/* Flash hack - Flash clients send a null byte after the stanza. Remove that... */ /* Flash hack - Flash clients send a null byte after the stanza. Remove that... */
{ {
@ -190,6 +259,7 @@ static int expat_erl_control(ErlDrvData drv_data,
memcpy(b->orig_bytes, event_buf.buff, size); memcpy(b->orig_bytes, event_buf.buff, size);
ei_x_free(&event_buf); ei_x_free(&event_buf);
ei_x_free(&xmlns_buf);
*rbuf = (char *)b; *rbuf = (char *)b;
return size; return size;

View File

@ -5,7 +5,7 @@
%%% Created : 30 Jul 2004 by Leif Johansson <leifj@it.su.se> %%% Created : 30 Jul 2004 by Leif Johansson <leifj@it.su.se>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -45,16 +45,23 @@
start(Host, ExtPrg) -> start(Host, ExtPrg) ->
lists:foreach( lists:foreach(
fun(This) -> fun(This) ->
spawn(?MODULE, init, [get_process_name(Host, This), ExtPrg]) start_instance(get_process_name(Host, This), ExtPrg)
end, end,
lists:seq(0, get_instances(Host)-1) lists:seq(0, get_instances(Host)-1)
). ).
start_instance(ProcessName, ExtPrg) ->
spawn(?MODULE, init, [ProcessName, ExtPrg]).
restart_instance(ProcessName, ExtPrg) ->
unregister(ProcessName),
start_instance(ProcessName, ExtPrg).
init(ProcessName, ExtPrg) -> init(ProcessName, ExtPrg) ->
register(ProcessName, 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, ProcessName, ExtPrg).
stop(Host) -> stop(Host) ->
lists:foreach( lists:foreach(
@ -108,23 +115,27 @@ get_instances(Server) ->
_ -> 1 _ -> 1
end. end.
loop(Port, Timeout) -> loop(Port, Timeout, ProcessName, ExtPrg) ->
receive receive
{call, Caller, Msg} -> {call, Caller, Msg} ->
Port ! {self(), {command, encode(Msg)}}, port_command(Port, encode(Msg)),
receive receive
{Port, {data, Data}} -> {Port, {data, Data}} ->
?DEBUG("extauth call '~p' received data response:~n~p", [Msg, Data]), ?DEBUG("extauth call '~p' received data response:~n~p", [Msg, Data]),
Caller ! {eauth, decode(Data)}; Caller ! {eauth, decode(Data)},
loop(Port, ?CALL_TIMEOUT, ProcessName, ExtPrg);
{Port, Other} -> {Port, Other} ->
?ERROR_MSG("extauth call '~p' received strange response:~n~p", [Msg, Other]), ?ERROR_MSG("extauth call '~p' received strange response:~n~p", [Msg, Other]),
Caller ! {eauth, false} Caller ! {eauth, false},
loop(Port, ?CALL_TIMEOUT, ProcessName, ExtPrg)
after after
Timeout -> Timeout ->
?ERROR_MSG("extauth call '~p' didn't receive response", [Msg]), ?ERROR_MSG("extauth call '~p' didn't receive response", [Msg]),
Caller ! {eauth, false} Caller ! {eauth, false},
end, Pid = restart_instance(ProcessName, ExtPrg),
loop(Port, ?CALL_TIMEOUT); flush_buffer_and_forward_messages(Pid),
exit(port_terminated)
end;
stop -> stop ->
Port ! {self(), close}, Port ! {self(), close},
receive receive
@ -132,10 +143,21 @@ loop(Port, Timeout) ->
exit(normal) exit(normal)
end; end;
{'EXIT', Port, Reason} -> {'EXIT', Port, Reason} ->
?CRITICAL_MSG("~p ~n", [Reason]), ?CRITICAL_MSG("extauth script has exitted abruptly with reason '~p'", [Reason]),
Pid = restart_instance(ProcessName, ExtPrg),
flush_buffer_and_forward_messages(Pid),
exit(port_terminated) exit(port_terminated)
end. end.
flush_buffer_and_forward_messages(Pid) ->
receive
Message ->
Pid ! Message,
flush_buffer_and_forward_messages(Pid)
after 0 ->
true
end.
join(List, Sep) -> join(List, Sep) ->
lists:foldl(fun(A, "") -> A; lists:foldl(fun(A, "") -> A;
(A, Acc) -> Acc ++ Sep ++ A (A, Acc) -> Acc ++ Sep ++ A

View File

@ -5,7 +5,7 @@
%%% Created : 22 Jan 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 22 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 24 Jan 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 24 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -70,11 +70,24 @@ start_module(Host, Module, Opts) ->
catch Class:Reason -> catch Class:Reason ->
del_module_mnesia(Host, Module), del_module_mnesia(Host, Module),
ets:delete(ejabberd_modules, {Module, Host}), ets:delete(ejabberd_modules, {Module, Host}),
?ERROR_MSG("Problem starting the module ~p for host ~p with options:~n ~p~n ~p: ~p", ErrorText = io_lib:format("Problem starting the module ~p for host ~p ~n options: ~p~n ~p: ~p",
[Module, Host, Opts, Class, Reason]), [Module, Host, Opts, Class, Reason]),
erlang:raise(Class, Reason, erlang:get_stacktrace()) ?CRITICAL_MSG(ErrorText, []),
case is_app_running(ejabberd) of
true ->
erlang:raise(Class, Reason, erlang:get_stacktrace());
false ->
?CRITICAL_MSG("ejabberd initialization was aborted because a module start failed.", []),
timer:sleep(3000),
erlang:halt(lists:flatten(ErrorText))
end
end. end.
is_app_running(AppName) ->
%% Use a high timeout to prevent a false positive in a high load system
Timeout = 15000,
lists:keymember(AppName, 1, application:which_applications(Timeout)).
%% @doc Stop the module in a host, and forget its configuration. %% @doc Stop the module in a host, and forget its configuration.
stop_module(Host, Module) -> stop_module(Host, Module) ->
case stop_module_keep_config(Host, Module) of case stop_module_keep_config(Host, Module) of

View File

@ -5,7 +5,7 @@
%%% Created : 10 Apr 2004 by Alexey Shchepin <alexey@process-one.net> %%% Created : 10 Apr 2004 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 2 Feb 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 2 Feb 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 23 Nov 2002 by Alexey Shchepin <alexey@process-one.net> %%% Created : 23 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -1,6 +1,6 @@
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -30,6 +30,7 @@
-define(NS_ROSTER, "jabber:iq:roster"). -define(NS_ROSTER, "jabber:iq:roster").
-define(NS_ROSTER_VER, "urn:xmpp:features:rosterver"). -define(NS_ROSTER_VER, "urn:xmpp:features:rosterver").
-define(NS_PRIVACY, "jabber:iq:privacy"). -define(NS_PRIVACY, "jabber:iq:privacy").
-define(NS_BLOCKING, "urn:xmpp:blocking").
-define(NS_PRIVATE, "jabber:iq:private"). -define(NS_PRIVATE, "jabber:iq:private").
-define(NS_VERSION, "jabber:iq:version"). -define(NS_VERSION, "jabber:iq:version").
-define(NS_TIME90, "jabber:iq:time"). % TODO: Remove once XEP-0090 is Obsolete -define(NS_TIME90, "jabber:iq:time"). % TODO: Remove once XEP-0090 is Obsolete
@ -206,117 +207,117 @@
?ERRT_CONFLICT(Lang, "Resource conflict")). ?ERRT_CONFLICT(Lang, "Resource conflict")).
-define(STREAM_ERROR(Condition), -define(STREAM_ERROR(Condition, Cdata),
{xmlelement, "stream:error", {xmlelement, "stream:error",
[], [],
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []}]}). [{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}],
[{xmlcdata, Cdata}]}]}).
-define(SERR_BAD_FORMAT, -define(SERR_BAD_FORMAT,
?STREAM_ERROR("bad-format")). ?STREAM_ERROR("bad-format", "")).
-define(SERR_BAD_NAMESPACE_PREFIX, -define(SERR_BAD_NAMESPACE_PREFIX,
?STREAM_ERROR("bad-namespace-prefix")). ?STREAM_ERROR("bad-namespace-prefix", "")).
-define(SERR_CONFLICT, -define(SERR_CONFLICT,
?STREAM_ERROR("conflict")). ?STREAM_ERROR("conflict", "")).
-define(SERR_CONNECTION_TIMEOUT, -define(SERR_CONNECTION_TIMEOUT,
?STREAM_ERROR("connection-timeout")). ?STREAM_ERROR("connection-timeout", "")).
-define(SERR_HOST_GONE, -define(SERR_HOST_GONE,
?STREAM_ERROR("host-gone")). ?STREAM_ERROR("host-gone", "")).
-define(SERR_HOST_UNKNOWN, -define(SERR_HOST_UNKNOWN,
?STREAM_ERROR("host-unknown")). ?STREAM_ERROR("host-unknown", "")).
-define(SERR_IMPROPER_ADDRESSING, -define(SERR_IMPROPER_ADDRESSING,
?STREAM_ERROR("improper-addressing")). ?STREAM_ERROR("improper-addressing", "")).
-define(SERR_INTERNAL_SERVER_ERROR, -define(SERR_INTERNAL_SERVER_ERROR,
?STREAM_ERROR("internal-server-error")). ?STREAM_ERROR("internal-server-error", "")).
-define(SERR_INVALID_FROM, -define(SERR_INVALID_FROM,
?STREAM_ERROR("invalid-from")). ?STREAM_ERROR("invalid-from", "")).
-define(SERR_INVALID_ID, -define(SERR_INVALID_ID,
?STREAM_ERROR("invalid-id")). ?STREAM_ERROR("invalid-id", "")).
-define(SERR_INVALID_NAMESPACE, -define(SERR_INVALID_NAMESPACE,
?STREAM_ERROR("invalid-namespace")). ?STREAM_ERROR("invalid-namespace", "")).
-define(SERR_INVALID_XML, -define(SERR_INVALID_XML,
?STREAM_ERROR("invalid-xml")). ?STREAM_ERROR("invalid-xml", "")).
-define(SERR_NOT_AUTHORIZED, -define(SERR_NOT_AUTHORIZED,
?STREAM_ERROR("not-authorized")). ?STREAM_ERROR("not-authorized", "")).
-define(SERR_POLICY_VIOLATION, -define(SERR_POLICY_VIOLATION,
?STREAM_ERROR("policy-violation")). ?STREAM_ERROR("policy-violation", "")).
-define(SERR_REMOTE_CONNECTION_FAILED, -define(SERR_REMOTE_CONNECTION_FAILED,
?STREAM_ERROR("remote-connection-failed")). ?STREAM_ERROR("remote-connection-failed", "")).
-define(SERR_RESOURSE_CONSTRAINT, -define(SERR_RESOURSE_CONSTRAINT,
?STREAM_ERROR("resource-constraint")). ?STREAM_ERROR("resource-constraint", "")).
-define(SERR_RESTRICTED_XML, -define(SERR_RESTRICTED_XML,
?STREAM_ERROR("restricted-xml")). ?STREAM_ERROR("restricted-xml", "")).
% TODO: include hostname or IP -define(SERR_SEE_OTHER_HOST(Host),
-define(SERR_SEE_OTHER_HOST, ?STREAM_ERROR("see-other-host", Host)).
?STREAM_ERROR("see-other-host")).
-define(SERR_SYSTEM_SHUTDOWN, -define(SERR_SYSTEM_SHUTDOWN,
?STREAM_ERROR("system-shutdown")). ?STREAM_ERROR("system-shutdown", "")).
-define(SERR_UNSUPPORTED_ENCODING, -define(SERR_UNSUPPORTED_ENCODING,
?STREAM_ERROR("unsupported-encoding")). ?STREAM_ERROR("unsupported-encoding", "")).
-define(SERR_UNSUPPORTED_STANZA_TYPE, -define(SERR_UNSUPPORTED_STANZA_TYPE,
?STREAM_ERROR("unsupported-stanza-type")). ?STREAM_ERROR("unsupported-stanza-type", "")).
-define(SERR_UNSUPPORTED_VERSION, -define(SERR_UNSUPPORTED_VERSION,
?STREAM_ERROR("unsupported-version")). ?STREAM_ERROR("unsupported-version", "")).
-define(SERR_XML_NOT_WELL_FORMED, -define(SERR_XML_NOT_WELL_FORMED,
?STREAM_ERROR("xml-not-well-formed")). ?STREAM_ERROR("xml-not-well-formed", "")).
%-define(SERR_, %-define(SERR_,
% ?STREAM_ERROR("")). % ?STREAM_ERROR("", "")).
-define(STREAM_ERRORT(Condition, Lang, Text), -define(STREAM_ERRORT(Condition, Cdata, Lang, Text),
{xmlelement, "stream:error", {xmlelement, "stream:error",
[], [],
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []}, [{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}],
[{xmlcdata, Cdata}]},
{xmlelement, "text", [{"xml:lang", Lang}, {"xmlns", ?NS_STREAMS}], {xmlelement, "text", [{"xml:lang", Lang}, {"xmlns", ?NS_STREAMS}],
[{xmlcdata, translate:translate(Lang, Text)}]}]}). [{xmlcdata, translate:translate(Lang, Text)}]}]}).
-define(SERRT_BAD_FORMAT(Lang, Text), -define(SERRT_BAD_FORMAT(Lang, Text),
?STREAM_ERRORT("bad-format", Lang, Text)). ?STREAM_ERRORT("bad-format", "", Lang, Text)).
-define(SERRT_BAD_NAMESPACE_PREFIX(Lang, Text), -define(SERRT_BAD_NAMESPACE_PREFIX(Lang, Text),
?STREAM_ERRORT("bad-namespace-prefix", Lang, Text)). ?STREAM_ERRORT("bad-namespace-prefix", "", Lang, Text)).
-define(SERRT_CONFLICT(Lang, Text), -define(SERRT_CONFLICT(Lang, Text),
?STREAM_ERRORT("conflict", Lang, Text)). ?STREAM_ERRORT("conflict", "", Lang, Text)).
-define(SERRT_CONNECTION_TIMEOUT(Lang, Text), -define(SERRT_CONNECTION_TIMEOUT(Lang, Text),
?STREAM_ERRORT("connection-timeout", Lang, Text)). ?STREAM_ERRORT("connection-timeout", "", Lang, Text)).
-define(SERRT_HOST_GONE(Lang, Text), -define(SERRT_HOST_GONE(Lang, Text),
?STREAM_ERRORT("host-gone", Lang, Text)). ?STREAM_ERRORT("host-gone", "", Lang, Text)).
-define(SERRT_HOST_UNKNOWN(Lang, Text), -define(SERRT_HOST_UNKNOWN(Lang, Text),
?STREAM_ERRORT("host-unknown", Lang, Text)). ?STREAM_ERRORT("host-unknown", "", Lang, Text)).
-define(SERRT_IMPROPER_ADDRESSING(Lang, Text), -define(SERRT_IMPROPER_ADDRESSING(Lang, Text),
?STREAM_ERRORT("improper-addressing", Lang, Text)). ?STREAM_ERRORT("improper-addressing", "", Lang, Text)).
-define(SERRT_INTERNAL_SERVER_ERROR(Lang, Text), -define(SERRT_INTERNAL_SERVER_ERROR(Lang, Text),
?STREAM_ERRORT("internal-server-error", Lang, Text)). ?STREAM_ERRORT("internal-server-error", "", Lang, Text)).
-define(SERRT_INVALID_FROM(Lang, Text), -define(SERRT_INVALID_FROM(Lang, Text),
?STREAM_ERRORT("invalid-from", Lang, Text)). ?STREAM_ERRORT("invalid-from", "", Lang, Text)).
-define(SERRT_INVALID_ID(Lang, Text), -define(SERRT_INVALID_ID(Lang, Text),
?STREAM_ERRORT("invalid-id", Lang, Text)). ?STREAM_ERRORT("invalid-id", "", Lang, Text)).
-define(SERRT_INVALID_NAMESPACE(Lang, Text), -define(SERRT_INVALID_NAMESPACE(Lang, Text),
?STREAM_ERRORT("invalid-namespace", Lang, Text)). ?STREAM_ERRORT("invalid-namespace", "", Lang, Text)).
-define(SERRT_INVALID_XML(Lang, Text), -define(SERRT_INVALID_XML(Lang, Text),
?STREAM_ERRORT("invalid-xml", Lang, Text)). ?STREAM_ERRORT("invalid-xml", "", Lang, Text)).
-define(SERRT_NOT_AUTHORIZED(Lang, Text), -define(SERRT_NOT_AUTHORIZED(Lang, Text),
?STREAM_ERRORT("not-authorized", Lang, Text)). ?STREAM_ERRORT("not-authorized", "", Lang, Text)).
-define(SERRT_POLICY_VIOLATION(Lang, Text), -define(SERRT_POLICY_VIOLATION(Lang, Text),
?STREAM_ERRORT("policy-violation", Lang, Text)). ?STREAM_ERRORT("policy-violation", "", Lang, Text)).
-define(SERRT_REMOTE_CONNECTION_FAILED(Lang, Text), -define(SERRT_REMOTE_CONNECTION_FAILED(Lang, Text),
?STREAM_ERRORT("remote-connection-failed", Lang, Text)). ?STREAM_ERRORT("remote-connection-failed", "", Lang, Text)).
-define(SERRT_RESOURSE_CONSTRAINT(Lang, Text), -define(SERRT_RESOURSE_CONSTRAINT(Lang, Text),
?STREAM_ERRORT("resource-constraint", Lang, Text)). ?STREAM_ERRORT("resource-constraint", "", Lang, Text)).
-define(SERRT_RESTRICTED_XML(Lang, Text), -define(SERRT_RESTRICTED_XML(Lang, Text),
?STREAM_ERRORT("restricted-xml", Lang, Text)). ?STREAM_ERRORT("restricted-xml", "", Lang, Text)).
% TODO: include hostname or IP -define(SERRT_SEE_OTHER_HOST(Host, Lang, Text),
-define(SERRT_SEE_OTHER_HOST(Lang, Text), ?STREAM_ERRORT("see-other-host", Host, Lang, Text)).
?STREAM_ERRORT("see-other-host", Lang, Text)).
-define(SERRT_SYSTEM_SHUTDOWN(Lang, Text), -define(SERRT_SYSTEM_SHUTDOWN(Lang, Text),
?STREAM_ERRORT("system-shutdown", Lang, Text)). ?STREAM_ERRORT("system-shutdown", "", Lang, Text)).
-define(SERRT_UNSUPPORTED_ENCODING(Lang, Text), -define(SERRT_UNSUPPORTED_ENCODING(Lang, Text),
?STREAM_ERRORT("unsupported-encoding", Lang, Text)). ?STREAM_ERRORT("unsupported-encoding", "", Lang, Text)).
-define(SERRT_UNSUPPORTED_STANZA_TYPE(Lang, Text), -define(SERRT_UNSUPPORTED_STANZA_TYPE(Lang, Text),
?STREAM_ERRORT("unsupported-stanza-type", Lang, Text)). ?STREAM_ERRORT("unsupported-stanza-type", "", Lang, Text)).
-define(SERRT_UNSUPPORTED_VERSION(Lang, Text), -define(SERRT_UNSUPPORTED_VERSION(Lang, Text),
?STREAM_ERRORT("unsupported-version", Lang, Text)). ?STREAM_ERRORT("unsupported-version", "", Lang, Text)).
-define(SERRT_XML_NOT_WELL_FORMED(Lang, Text), -define(SERRT_XML_NOT_WELL_FORMED(Lang, Text),
?STREAM_ERRORT("xml-not-well-formed", Lang, Text)). ?STREAM_ERRORT("xml-not-well-formed", "", Lang, Text)).
%-define(SERRT_(Lang, Text), %-define(SERRT_(Lang, Text),
% ?STREAM_ERRORT("", Lang, Text)). % ?STREAM_ERRORT("", "", Lang, Text)).
-record(jid, {user, server, resource, -record(jid, {user, server, resource,

420
src/mod_ack.erl Normal file
View File

@ -0,0 +1,420 @@
%%%-------------------------------------------------------------------
%%% File : mod_ack.erl
%%% Author : Mickael Remond <mremond@process-one.net>
%%% Description : Implements reliable message delivery
%%% Note: this module depends on mod_caps
%%% Created : 12 Mar 2010 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(mod_ack).
-behaviour(gen_server).
-behaviour(gen_mod).
%% API
-export([start/2, stop/1, start_link/2]).
-export([user_send_packet/3,
offline_message/3,
delayed_message/3,
remove_connection/3,
feature_inspect_packet/4]).
%% gen_server callbacks
-export([init/1,
handle_info/2,
handle_call/3,
handle_cast/2,
terminate/2,
code_change/3]).
-include("jlib.hrl").
-include("ejabberd.hrl").
-define(PROCNAME, ejabberd_mod_ack).
-define(ACK_TIMEOUT, 60). %% seconds
-define(DICT, dict).
-ifndef(NS_RECEIPTS).
-define(NS_RECEIPTS, "urn:xmpp:receipts").
-endif.
-ifndef(NS_PING).
-define(NS_PING, "urn:xmpp:ping").
-endif.
-ifndef(NS_P1_PUSHED).
-define(NS_P1_PUSHED, "p1:pushed").
-endif.
-record(state, {host, timers = ?DICT:new(), timeout}).
%%====================================================================
%% API
%%====================================================================
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec =
{Proc,
{?MODULE, start_link, [Host, Opts]},
transient,
1000,
worker,
[?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:call(Proc, stop),
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
%% TODO: Make ack on server receive optional ?
user_send_packet(From, To, {xmlelement, "message", _Attrs, _Els} = Packet) ->
case has_receipt_request(Packet) of
{true, _} ->
process_ack_request("on-sender-server", From, To, Packet);
false ->
case has_receipt_response(Packet) of
{true, ID} ->
Server = From#jid.lserver,
del_timer(Server, {message, ID}, From);
false ->
do_nothing
end
end;
user_send_packet(From, _To, {xmlelement, "iq", Attrs, _Els}) ->
case xml:get_attr_s("id", Attrs) of
"" ->
ok;
ID ->
Server = From#jid.lserver,
del_timer(Server, {iq, ID}, From)
end;
user_send_packet(_From, _To, _Packet) ->
do_nothing.
offline_message(From, To, Packet) ->
process_ack_request("offline", From, To, Packet),
ok.
delayed_message(From, To, Packet) ->
process_ack_request("delayed", From, To, Packet),
ok.
feature_inspect_packet(JID, Server,
{xmlelement, "presence", _, _} = Pres,
{xmlelement, "message", Attrs, _} = El) ->
HasReceipts = has_receipt_request(El),
ReceiptsSupported = are_receipts_supported(Pres),
?DEBUG("feature_inspect_packet:~n"
"** JID: ~p~n"
"** Has receipts: ~p~n"
"** Receipts supported: ~p~n"
"** Pres: ~p~n"
"** El: ~p",
[JID, HasReceipts, ReceiptsSupported, Pres, El]),
Type = xml:get_attr_s("type", Attrs),
case HasReceipts of
_ when Type == "error" ->
ok;
{true, ID} ->
case {jlib:string_to_jid(xml:get_attr_s("from", Attrs)),
jlib:string_to_jid(xml:get_attr_s("to", Attrs))} of
{#jid{} = From, #jid{} = To} ->
Pkt = {From, To, El},
case ReceiptsSupported of
true ->
add_timer(Server, {message, ID}, JID, Pkt);
false ->
ping(From, To, Server, JID, El);
unknown ->
process_ack_request("unreliable", From, To, El)
end;
_ ->
?WARNING_MSG("message doesn't have 'from' or 'to'"
" attribute:~n** El: ~p", [El])
end;
_ ->
ok
end;
feature_inspect_packet(_User, _Server, _Pres, _El) ->
ok.
remove_connection({_, C2SPid}, #jid{lserver = Host}, _Info) ->
gen_server:cast(gen_mod:get_module_proc(Host, ?PROCNAME), {del, C2SPid}).
%%====================================================================
%% gen_server callbacks
%%====================================================================
init([Host, Opts]) ->
Timeout = timer:seconds(gen_mod:get_opt(timeout, Opts, ?ACK_TIMEOUT)),
ejabberd_hooks:add(user_send_packet, Host,
?MODULE, user_send_packet, 20),
ejabberd_hooks:add(offline_message_hook, Host,
?MODULE, offline_message, 20),
ejabberd_hooks:add(delayed_message_hook, Host,
?MODULE, delayed_message, 20),
ejabberd_hooks:add(feature_inspect_packet, Host,
?MODULE, feature_inspect_packet, 150),
ejabberd_hooks:add(sm_remove_connection_hook, Host,
?MODULE, remove_connection, 20),
ejabberd_hooks:add(sm_remove_migrated_connection_hook, Host,
?MODULE, remove_connection, 20),
{ok, #state{host = Host, timeout = Timeout}}.
handle_call(stop, _From, State) ->
{stop, normal, ok, State};
handle_call(_Req, _From, State) ->
{reply, {error, badarg}, State}.
handle_cast({add, ID, Pid, Packet}, State) ->
TRef = erlang:start_timer(State#state.timeout, self(), {ID, Pid}),
Timers = insert(Pid, ID, {TRef, Packet}, State#state.timers),
{noreply, State#state{timers = Timers}};
handle_cast({del, ID, Pid}, State) ->
case lookup(Pid, ID, State#state.timers) of
{ok, {TRef, {From, To, {xmlelement, _, Attrs, _}}}} ->
cancel_timer(TRef),
Timers = delete(Pid, ID, State#state.timers),
case ID of
{iq, _} ->
MsgID = xml:get_attr_s("id", Attrs),
Message = {xmlelement, "message", [{"id", MsgID}],
[{xmlelement, "received",
[{"xmlns", ?NS_RECEIPTS}, {"id", MsgID}], []}]},
ejabberd_router:route(To, From, Message);
_ ->
ok
end,
{noreply, State#state{timers = Timers}};
error ->
{noreply, State}
end;
handle_cast({del, Pid}, State) ->
lists:foreach(
fun({_, _, {TRef, {From, To, El}}}) ->
cancel_timer(TRef),
El1 = xml:remove_subtags(El, "x", {"xmlns", ?NS_P1_PUSHED}),
El2 = xml:append_subtags(
El1, [{xmlelement, "x", [{"xmlns", ?NS_P1_PUSHED}], []}]),
?DEBUG("Resending message:~n"
"** From: ~p~n"
"** To: ~p~n"
"** El: ~p",
[From, To, El2]),
ejabberd_router:route(From, To, El2)
end, to_list(Pid, State#state.timers)),
Timers = delete(Pid, State#state.timers),
{noreply, State#state{timers = Timers}};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({timeout, _TRef, {ID, Pid}}, State) ->
case lookup(Pid, ID, State#state.timers) of
{ok, _} ->
MRef = erlang:monitor(process, Pid),
catch ejabberd_c2s:stop(Pid),
receive
{'DOWN', MRef, process, Pid, _Reason}->
ok
after 5 ->
catch exit(Pid, kill)
end,
erlang:demonitor(MRef, [flush]),
handle_cast({del, Pid}, State);
error ->
{noreply, State}
end;
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, State) ->
Host = State#state.host,
ejabberd_hooks:delete(user_send_packet, Host,
?MODULE, user_send_packet, 20),
ejabberd_hooks:delete(offline_message_hook, Host,
?MODULE, offline_message, 20),
ejabberd_hooks:delete(delayed_message_hook, Host,
?MODULE, delayed_message, 20),
ejabberd_hooks:delete(feature_inspect_packet, Host,
?MODULE, feature_inspect_packet, 150),
ejabberd_hooks:delete(sm_remove_connection_hook, Host,
?MODULE, remove_connection, 20),
ejabberd_hooks:delete(sm_remove_migrated_connection_hook, Host,
?MODULE, remove_connection, 20),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%====================================================================
%% Internal functions
%%====================================================================
process_ack_request(AckTagName,
#jid{lserver=LServer} = From, To,
{xmlelement, "message", _Attrs, _Els} = Packet) ->
case has_receipt_request(Packet) of
{true, ID} ->
BareTo = jlib:jid_remove_resource(To),
Message = {xmlelement, "message", [{"id", ID}],
[{xmlelement, AckTagName,
[{"xmlns", ?NS_RECEIPTS},
{"server", LServer}, {"id", ID}], []}]},
ejabberd_router:route(BareTo, From, Message);
false ->
do_nothing
end.
has_receipt_request(Packet) ->
has_receipt(Packet, "request").
has_receipt_response(Packet) ->
has_receipt(Packet, "received").
has_receipt({xmlelement, "message", MsgAttrs, _} = Packet, Type) ->
case xml:get_attr_s("id", MsgAttrs) of
"" ->
case Type of
"request" -> false; %% Message must have an ID to ask a request for ack.
"received" ->
case xml:get_subtag(Packet, "received") of
false ->
false;
{xmlelement, _Name, Attrs, _Els} ->
case xml:get_attr_s("xmlns", Attrs) of
?NS_RECEIPTS ->
case xml:get_attr_s("id", Attrs) of
"" -> false;
SubTagID -> {true, SubTagID}
end;
_ -> false
end
end
end;
ID ->
case xml:get_subtag(Packet, Type) of
false ->
false;
{xmlelement, _Name, Attrs, _Els} ->
case xml:get_attr_s("xmlns", Attrs) of
?NS_RECEIPTS ->
case xml:get_attr_s("id", Attrs) of
"" -> {true, ID};
SubTagID -> {true, SubTagID}
end;
_ ->
false
end
end
end.
are_receipts_supported(undefined) ->
unknown;
are_receipts_supported({xmlelement, "presence", _, Els}) ->
case mod_caps:read_caps(Els) of
nothing ->
unknown;
Caps ->
lists:member(?NS_RECEIPTS, mod_caps:get_features(Caps))
end.
ping(From, To, Server, JID, El) ->
ID = randoms:get_string(),
add_timer(Server, {iq, ID}, JID, {From, To, El}),
ejabberd_router:route(jlib:make_jid("", Server, ""), JID,
{xmlelement, "iq",
[{"type", "get"}, {"id", ID}],
[{xmlelement, "query", [{"xmlns", ?NS_PING}], []}]}).
add_timer(Host, ID, JID, Packet) ->
{U, S, R} = jlib:jid_tolower(JID),
C2SPid = ejabberd_sm:get_session_pid(U, S, R),
gen_server:cast(gen_mod:get_module_proc(Host, ?PROCNAME),
{add, ID, C2SPid, Packet}).
del_timer(Host, ID, JID) ->
{U, S, R} = jlib:jid_tolower(JID),
C2SPid = ejabberd_sm:get_session_pid(U, S, R),
gen_server:cast(gen_mod:get_module_proc(Host, ?PROCNAME),
{del, ID, C2SPid}).
cancel_timer(TRef) ->
case erlang:cancel_timer(TRef) of
false ->
receive
{timeout, TRef, _} ->
ok
after 0 ->
ok
end;
_ ->
ok
end.
lookup(Pid, Key, Queue) ->
case ?DICT:find(Pid, Queue) of
{ok, Treap} ->
case treap:lookup(Key, Treap) of
{ok, _, Val} ->
{ok, Val};
error ->
error
end;
error ->
error
end.
insert(Pid, Key, Val, Queue) ->
Treap = case ?DICT:find(Pid, Queue) of
{ok, Treap1} ->
Treap1;
error ->
nil
end,
?DICT:store(Pid, treap:insert(Key, now(), Val, Treap), Queue).
delete(Pid, Key, Queue) ->
case ?DICT:find(Pid, Queue) of
{ok, Treap} ->
NewTreap = treap:delete(Key, Treap),
case treap:is_empty(NewTreap) of
true ->
?DICT:erase(Pid, Queue);
false ->
?DICT:store(Pid, NewTreap, Queue)
end;
error ->
Queue
end.
delete(Pid, Queue) ->
?DICT:erase(Pid, Queue).
to_list(Pid, Queue) ->
case ?DICT:find(Pid, Queue) of
{ok, Treap} ->
treap:to_list(Treap);
error ->
[]
end.

View File

@ -5,7 +5,7 @@
%%% Created : 15 Nov 2005 by Magnus Henoch <henoch@dtek.chalmers.se> %%% Created : 15 Nov 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 11 Aug 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 11 Aug 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

333
src/mod_blocking.erl Normal file
View File

@ -0,0 +1,333 @@
%%%----------------------------------------------------------------------
%%% File : mod_blocking.erl
%%% Author : Stephan Maka
%%% Purpose : XEP-0191: Simple Communications Blocking
%%% Created : 24 Aug 2008 by Stephan Maka <stephan@spaceboyz.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 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(mod_blocking).
-behaviour(gen_mod).
-export([start/2, stop/1,
process_iq/3,
process_iq_set/4,
process_iq_get/5]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("mod_privacy.hrl").
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
ejabberd_hooks:add(privacy_iq_get, Host,
?MODULE, process_iq_get, 40),
ejabberd_hooks:add(privacy_iq_set, Host,
?MODULE, process_iq_set, 40),
mod_disco:register_feature(Host, ?NS_BLOCKING),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING,
?MODULE, process_iq, IQDisc).
stop(Host) ->
ejabberd_hooks:delete(privacy_iq_get, Host,
?MODULE, process_iq_get, 40),
ejabberd_hooks:delete(privacy_iq_set, Host,
?MODULE, process_iq_set, 40),
mod_disco:unregister_feature(Host, ?NS_BLOCKING),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING).
process_iq(_From, _To, IQ) ->
SubEl = IQ#iq.sub_el,
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}.
process_iq_get(_, From, _To,
#iq{xmlns = ?NS_BLOCKING,
sub_el = {xmlelement, "blocklist", _, _}},
_) ->
#jid{luser = LUser, lserver = LServer} = From,
process_blocklist_get(LUser, LServer);
process_iq_get(Acc, _, _, _, _) ->
Acc.
process_iq_set(_, From, _To, #iq{xmlns = ?NS_BLOCKING,
sub_el = {xmlelement, SubElName, _, SubEls}}) ->
#jid{luser = LUser, lserver = LServer} = From,
case {SubElName, xml:remove_cdata(SubEls)} of
{"block", []} ->
{error, ?ERR_BAD_REQUEST};
{"block", Els} ->
JIDs = parse_blocklist_items(Els, []),
process_blocklist_block(LUser, LServer, JIDs);
{"unblock", []} ->
process_blocklist_unblock_all(LUser, LServer);
{"unblock", Els} ->
JIDs = parse_blocklist_items(Els, []),
process_blocklist_unblock(LUser, LServer, JIDs);
_ ->
{error, ?ERR_BAD_REQUEST}
end;
process_iq_set(Acc, _, _, _) ->
Acc.
is_list_needdb(Items) ->
lists:any(
fun(X) ->
case X#listitem.type of
subscription -> true;
group -> true;
_ -> false
end
end, Items).
list_to_blocklist_jids([], JIDs) ->
JIDs;
list_to_blocklist_jids([#listitem{type = jid,
action = deny,
value = JID} = Item | Items], JIDs) ->
case Item of
#listitem{match_all = true} ->
Match = true;
#listitem{match_iq = true,
match_message = true,
match_presence_in = true,
match_presence_out = true} ->
Match = true;
_ ->
Match = false
end,
if
Match ->
list_to_blocklist_jids(Items, [JID | JIDs]);
true ->
list_to_blocklist_jids(Items, JIDs)
end;
% Skip Privacy List items than cannot be mapped to Blocking items
list_to_blocklist_jids([_ | Items], JIDs) ->
list_to_blocklist_jids(Items, JIDs).
parse_blocklist_items([], JIDs) ->
JIDs;
parse_blocklist_items([{xmlelement, "item", Attrs, _} | Els], JIDs) ->
case xml:get_attr("jid", Attrs) of
{value, JID1} ->
JID = jlib:jid_tolower(jlib:string_to_jid(JID1)),
parse_blocklist_items(Els, [JID | JIDs]);
false ->
% Tolerate missing jid attribute
parse_blocklist_items(Els, JIDs)
end;
parse_blocklist_items([_ | Els], JIDs) ->
% Tolerate unknown elements
parse_blocklist_items(Els, JIDs).
process_blocklist_block(LUser, LServer, JIDs) ->
F =
fun() ->
case mnesia:wread({privacy, {LUser, LServer}}) of
[] ->
% No lists yet
P = #privacy{us = {LUser, LServer}},
% TODO: i18n here:
NewDefault = "Blocked contacts",
NewLists1 = [],
List = [];
[#privacy{default = Default,
lists = Lists} = P] ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
% Default list exists
NewDefault = Default,
NewLists1 = lists:keydelete(Default, 1, Lists);
false ->
% No default list yet, create one
% TODO: i18n here:
NewDefault = "Blocked contacts",
NewLists1 = Lists,
List = []
end
end,
AlreadyBlocked = list_to_blocklist_jids(List, []),
NewList =
lists:foldr(fun(JID, List1) ->
case lists:member(JID, AlreadyBlocked) of
true ->
List1;
false ->
[#listitem{type = jid,
value = JID,
action = deny,
order = 0,
match_all = true
} | List1]
end
end, List, JIDs),
NewLists = [{NewDefault, NewList} | NewLists1],
mnesia:write(P#privacy{default = NewDefault,
lists = NewLists}),
{ok, NewDefault, NewList}
end,
case mnesia:transaction(F) of
{atomic, {error, _} = Error} ->
Error;
{atomic, {ok, Default, List}} ->
broadcast_list_update(LUser, LServer, Default, List),
broadcast_blocklist_event(LUser, LServer, {block, JIDs}),
{result, []};
_ ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
end.
process_blocklist_unblock_all(LUser, LServer) ->
F =
fun() ->
case mnesia:read({privacy, {LUser, LServer}}) of
[] ->
% No lists, nothing to unblock
ok;
[#privacy{default = Default,
lists = Lists} = P] ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
% Default list, remove all deny items
NewList =
lists:filter(
fun(#listitem{action = A}) ->
A =/= deny
end, List),
NewLists1 = lists:keydelete(Default, 1, Lists),
NewLists = [{Default, NewList} | NewLists1],
mnesia:write(P#privacy{lists = NewLists}),
{ok, Default, NewList};
false ->
% No default list, nothing to unblock
ok
end
end
end,
case mnesia:transaction(F) of
{atomic, {error, _} = Error} ->
Error;
{atomic, ok} ->
{result, []};
{atomic, {ok, Default, List}} ->
broadcast_list_update(LUser, LServer, Default, List),
broadcast_blocklist_event(LUser, LServer, unblock_all),
{result, []};
_ ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
end.
process_blocklist_unblock(LUser, LServer, JIDs) ->
F =
fun() ->
case mnesia:read({privacy, {LUser, LServer}}) of
[] ->
% No lists, nothing to unblock
ok;
[#privacy{default = Default,
lists = Lists} = P] ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
% Default list, remove matching deny items
NewList =
lists:filter(
fun(#listitem{action = deny,
type = jid,
value = JID}) ->
not(lists:member(JID, JIDs));
(_) ->
true
end, List),
NewLists1 = lists:keydelete(Default, 1, Lists),
NewLists = [{Default, NewList} | NewLists1],
mnesia:write(P#privacy{lists = NewLists}),
{ok, Default, NewList};
false ->
% No default list, nothing to unblock
ok
end
end
end,
case mnesia:transaction(F) of
{atomic, {error, _} = Error} ->
Error;
{atomic, ok} ->
{result, []};
{atomic, {ok, Default, List}} ->
broadcast_list_update(LUser, LServer, Default, List),
broadcast_blocklist_event(LUser, LServer, {unblock, JIDs}),
{result, []};
_ ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
end.
broadcast_list_update(LUser, LServer, Name, List) ->
NeedDb = is_list_needdb(List),
ejabberd_router:route(
jlib:make_jid(LUser, LServer, ""),
jlib:make_jid(LUser, LServer, ""),
{xmlelement, "broadcast", [],
[{privacy_list,
#userlist{name = Name, list = List, needdb = NeedDb},
Name}]}).
broadcast_blocklist_event(LUser, LServer, Event) ->
JID = jlib:make_jid(LUser, LServer, ""),
ejabberd_router:route(
JID, JID,
{xmlelement, "broadcast", [],
[{blocking, Event}]}).
process_blocklist_get(LUser, LServer) ->
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
{'EXIT', _Reason} ->
{error, ?ERR_INTERNAL_SERVER_ERROR};
[] ->
{result, [{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}], []}]};
[#privacy{default = Default, lists = Lists}] ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
JIDs = list_to_blocklist_jids(List, []),
Items = lists:map(
fun(JID) ->
?DEBUG("JID: ~p",[JID]),
{xmlelement, "item",
[{"jid", jlib:jid_to_string(JID)}], []}
end, JIDs),
{result,
[{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}],
Items}]};
_ ->
{result, [{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}], []}]}
end
end.

View File

@ -5,7 +5,7 @@
%%% Created : 7 Oct 2006 by Magnus Henoch <henoch@dtek.chalmers.se> %%% Created : 7 Oct 2006 by Magnus Henoch <henoch@dtek.chalmers.se>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 19 Jan 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 19 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 26 Oct 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 26 Oct 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 1 Jan 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 1 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 15 Jan 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 15 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -7,7 +7,7 @@
%%% {mod_ip_blacklist, []} %%% {mod_ip_blacklist, []}
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 16 Feb 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 16 Feb 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -1,5 +1,5 @@
/* /*
* ejabberd, Copyright (C) 2002-2010 ProcessOne * ejabberd, Copyright (C) 2002-2011 ProcessOne
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 15 Feb 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 15 Feb 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -425,7 +425,7 @@ iq_get_vcard(Lang) ->
[{xmlcdata, ?EJABBERD_URI}]}, [{xmlcdata, ?EJABBERD_URI}]},
{xmlelement, "DESC", [], {xmlelement, "DESC", [],
[{xmlcdata, translate:translate(Lang, "ejabberd IRC module") ++ [{xmlcdata, translate:translate(Lang, "ejabberd IRC module") ++
"\nCopyright (c) 2003-2010 Alexey Shchepin"}]}]. "\nCopyright (c) 2003-2011 ProcessOne"}]}].
command_items(Host, Lang) -> command_items(Host, Lang) ->
lists:map(fun({Node, Name, _Function}) lists:map(fun({Node, Name, _Function})

View File

@ -5,7 +5,7 @@
%%% Created : 15 Feb 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 15 Feb 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -219,6 +219,7 @@ handle_info({route_chan, Channel, Resource,
NewStateData = NewStateData =
case xml:get_attr_s("type", Attrs) of case xml:get_attr_s("type", Attrs) of
"unavailable" -> "unavailable" ->
send_stanza_unavailable(Channel, StateData),
S1 = ?SEND(io_lib:format("PART #~s\r\n", [Channel])), S1 = ?SEND(io_lib:format("PART #~s\r\n", [Channel])),
S1#state{channels = S1#state{channels =
dict:erase(Channel, S1#state.channels)}; dict:erase(Channel, S1#state.channels)};
@ -656,13 +657,9 @@ terminate(_Reason, _StateName, FullStateData) ->
bounce_messages("Server Connect Failed"), bounce_messages("Server Connect Failed"),
lists:foreach( lists:foreach(
fun(Chan) -> fun(Chan) ->
ejabberd_router:route( Stanza = {xmlelement, "presence", [{"type", "error"}],
jlib:make_jid( [Error]},
lists:concat([Chan, "%", StateData#state.server]), send_stanza(Chan, StateData, Stanza)
StateData#state.host, StateData#state.nick),
StateData#state.user,
{xmlelement, "presence", [{"type", "error"}],
[Error]})
end, dict:fetch_keys(StateData#state.channels)), end, dict:fetch_keys(StateData#state.channels)),
case StateData#state.socket of case StateData#state.socket of
undefined -> undefined ->
@ -672,6 +669,30 @@ terminate(_Reason, _StateName, FullStateData) ->
end, end,
ok. ok.
send_stanza(Chan, StateData, Stanza) ->
ejabberd_router:route(
jlib:make_jid(
lists:concat([Chan, "%", StateData#state.server]),
StateData#state.host, StateData#state.nick),
StateData#state.user,
Stanza).
send_stanza_unavailable(Chan, StateData) ->
Affiliation = "member", % this is a simplification
Role = "none",
Stanza =
{xmlelement, "presence", [{"type", "unavailable"}],
[{xmlelement, "x", [{"xmlns", ?NS_MUC_USER}],
[{xmlelement, "item",
[{"affiliation", Affiliation},
{"role", Role}],
[]},
{xmlelement, "status",
[{"code", "110"}],
[]}
]}]},
send_stanza(Chan, StateData, Stanza).
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% Internal functions %%% Internal functions
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------

View File

@ -5,7 +5,7 @@
%%% Created : 24 Oct 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 24 Oct 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -116,7 +116,9 @@ process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
roster_get_jid_info, Server, roster_get_jid_info, Server,
{none, []}, [User, Server, From]), {none, []}, [User, Server, From]),
if if
(Subscription == both) or (Subscription == from) -> (Subscription == both) or (Subscription == from)
or ((From#jid.luser == To#jid.luser)
and (From#jid.lserver == To#jid.lserver)) ->
UserListRecord = ejabberd_hooks:run_fold( UserListRecord = ejabberd_hooks:run_fold(
privacy_get_user_list, Server, privacy_get_user_list, Server,
#userlist{}, #userlist{},

View File

@ -5,7 +5,7 @@
%%% Created : 24 Oct 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 24 Oct 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 19 Mar 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 19 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -42,6 +42,7 @@
process_iq_disco_items/4, process_iq_disco_items/4,
broadcast_service_message/2, broadcast_service_message/2,
register_room/3, register_room/3,
node_up/1,
migrate/1, migrate/1,
get_vh_rooms/1, get_vh_rooms/1,
can_use_nick/3]). can_use_nick/3]).
@ -175,14 +176,33 @@ migrate(After) ->
['$$']}]), ['$$']}]),
lists:foreach( lists:foreach(
fun([NameHost, Pid]) -> fun([NameHost, Pid]) ->
case ejabberd_cluster:get_node_new(NameHost) of case ejabberd_cluster:get_node(NameHost) of
Node when Node /= node() -> Node when Node /= node() ->
mod_muc_room:migrate(Pid, Node, After); mod_muc_room:migrate(Pid, Node, random:uniform(After));
_ -> _ ->
ok ok
end end
end, Rs). end, Rs).
node_up(_Node) ->
copy_rooms(mnesia:dirty_first(muc_online_room)).
copy_rooms('$end_of_table') ->
ok;
copy_rooms(Key) ->
case mnesia:dirty_read(muc_online_room, Key) of
[#muc_online_room{name_host = NameHost} = Room] ->
case ejabberd_cluster:get_node_new(NameHost) of
Node when node() /= Node ->
rpc:cast(Node, mnesia, dirty_write, [Room]);
_ ->
ok
end;
_ ->
ok
end,
copy_rooms(mnesia:dirty_next(muc_online_room, Key)).
%%==================================================================== %%====================================================================
%% gen_server callbacks %% gen_server callbacks
%%==================================================================== %%====================================================================
@ -219,6 +239,7 @@ init([Host, Opts]) ->
DefRoomOpts = gen_mod:get_opt(default_room_options, Opts, []), DefRoomOpts = gen_mod:get_opt(default_room_options, Opts, []),
RoomShaper = gen_mod:get_opt(room_shaper, Opts, none), RoomShaper = gen_mod:get_opt(room_shaper, Opts, none),
ejabberd_router:register_route(MyHost), ejabberd_router:register_route(MyHost),
ejabberd_hooks:add(node_up, ?MODULE, node_up, 100),
ejabberd_hooks:add(node_hash_update, ?MODULE, migrate, 100), ejabberd_hooks:add(node_hash_update, ?MODULE, migrate, 100),
load_permanent_rooms(MyHost, Host, load_permanent_rooms(MyHost, Host,
{Access, AccessCreate, AccessAdmin, AccessPersistent}, {Access, AccessCreate, AccessAdmin, AccessPersistent},
@ -306,7 +327,15 @@ handle_info({room_destroyed, RoomHost, Pid}, State) ->
mnesia:delete_object(#muc_online_room{name_host = RoomHost, mnesia:delete_object(#muc_online_room{name_host = RoomHost,
pid = Pid}) pid = Pid})
end, end,
mnesia:sync_dirty(F), mnesia:async_dirty(F),
case ejabberd_cluster:get_node_new(RoomHost) of
Node when Node /= node() ->
rpc:cast(Node, mnesia, dirty_delete_object,
[#muc_online_room{name_host = RoomHost,
pid = Pid}]);
_ ->
ok
end,
{noreply, State}; {noreply, State};
handle_info(_Info, State) -> handle_info(_Info, State) ->
{noreply, State}. {noreply, State}.
@ -319,6 +348,7 @@ handle_info(_Info, State) ->
%% The return value is ignored. %% The return value is ignored.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
terminate(_Reason, State) -> terminate(_Reason, State) ->
ejabberd_hooks:delete(node_up, ?MODULE, node_up, 100),
ejabberd_hooks:delete(node_hash_update, ?MODULE, migrate, 100), ejabberd_hooks:delete(node_hash_update, ?MODULE, migrate, 100),
ejabberd_router:unregister_route(State#state.host), ejabberd_router:unregister_route(State#state.host),
ok. ok.
@ -507,14 +537,20 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
AccessCreate, From, AccessCreate, From,
Room) of Room) of
true -> true ->
{ok, Pid} = start_new_room( case start_new_room(
Host, ServerHost, Access, Host, ServerHost, Access,
Room, HistorySize, Room, HistorySize,
RoomShaper, From, RoomShaper, From,
Nick, DefRoomOpts), Nick, DefRoomOpts) of
register_room(Host, Room, Pid), {ok, Pid} ->
mod_muc_room:route(Pid, From, Nick, Packet), mod_muc_room:route(Pid, From, Nick, Packet),
ok; register_room(Host, Room, Pid),
ok;
_Err ->
Err = jlib:make_error_reply(
Packet, ?ERR_INTERNAL_SERVER_ERROR),
ejabberd_router:route(To, From, Err)
end;
false -> false ->
Lang = xml:get_attr_s("xml:lang", Attrs), Lang = xml:get_attr_s("xml:lang", Attrs),
ErrText = "Room creation is denied by service policy", ErrText = "Room creation is denied by service policy",
@ -603,7 +639,28 @@ register_room(Host, Room, Pid) ->
mnesia:write(#muc_online_room{name_host = {Room, Host}, mnesia:write(#muc_online_room{name_host = {Room, Host},
pid = Pid}) pid = Pid})
end, end,
mnesia:sync_dirty(F). mnesia:async_dirty(F),
case ejabberd_cluster:get_node_new({Room, Host}) of
Node when Node /= node() ->
%% New node has just been added. But we may miss MUC records
%% copy procedure, so we copy the MUC record manually just
%% to make sure
rpc:cast(Node, mnesia, dirty_write,
[#muc_online_room{name_host = {Room, Host},
pid = Pid}]),
case ejabberd_cluster:get_node({Room, Host}) of
Node when node() /= Node ->
%% Migration to new node has completed, and seems like
%% we missed it, so we migrate the MUC room pid manually.
%% It is not a problem if we have already got migration
%% notification: dups are just ignored by the MUC room pid.
mod_muc_room:migrate(Pid, Node, 0);
_ ->
ok
end;
_ ->
ok
end.
iq_disco_info(Lang) -> iq_disco_info(Lang) ->
[{xmlelement, "identity", [{xmlelement, "identity",
@ -847,7 +904,7 @@ iq_get_vcard(Lang) ->
[{xmlcdata, ?EJABBERD_URI}]}, [{xmlcdata, ?EJABBERD_URI}]},
{xmlelement, "DESC", [], {xmlelement, "DESC", [],
[{xmlcdata, translate:translate(Lang, "ejabberd MUC module") ++ [{xmlcdata, translate:translate(Lang, "ejabberd MUC module") ++
"\nCopyright (c) 2003-2010 Alexey Shchepin"}]}]. "\nCopyright (c) 2003-2011 ProcessOne"}]}].
broadcast_service_message(Host, Msg) -> broadcast_service_message(Host, Msg) ->

View File

@ -5,7 +5,7 @@
%%% Created : 12 Mar 2006 by Alexey Shchepin <alexey@process-one.net> %%% Created : 12 Mar 2006 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 19 Mar 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 19 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -110,7 +110,7 @@ start_link(StateName, StateData) ->
?GEN_FSM:start_link(?MODULE, [StateName, StateData], ?FSMOPTS). ?GEN_FSM:start_link(?MODULE, [StateName, StateData], ?FSMOPTS).
migrate(FsmRef, Node, After) -> migrate(FsmRef, Node, After) ->
?GEN_FSM:send_all_state_event(FsmRef, {migrate, Node, After}). erlang:send_after(After, FsmRef, {migrate, Node}).
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm %%% Callback functions from gen_fsm
@ -597,9 +597,6 @@ handle_event(destroy, StateName, StateData) ->
handle_event({set_affiliations, Affiliations}, StateName, StateData) -> handle_event({set_affiliations, Affiliations}, StateName, StateData) ->
{next_state, StateName, StateData#state{affiliations = Affiliations}}; {next_state, StateName, StateData#state{affiliations = Affiliations}};
handle_event({migrate, Node, After}, StateName, StateData) when Node /= node() ->
{migrate, StateData,
{Node, ?MODULE, start, [StateName, StateData]}, After * 2};
handle_event(_Event, StateName, StateData) -> handle_event(_Event, StateName, StateData) ->
{next_state, StateName, StateData}. {next_state, StateName, StateData}.
@ -716,6 +713,13 @@ handle_info({captcha_failed, From}, normal_state, StateData) ->
StateData StateData
end, end,
{next_state, normal_state, NewState}; {next_state, normal_state, NewState};
handle_info({migrate, Node}, StateName, StateData) ->
if Node /= node() ->
{migrate, StateData,
{Node, ?MODULE, start, [StateName, StateData]}, 0};
true ->
{next_state, StateName, StateData}
end;
handle_info(_Info, StateName, StateData) -> handle_info(_Info, StateName, StateData) ->
{next_state, StateName, StateData}. {next_state, StateName, StateData}.
@ -870,9 +874,6 @@ process_groupchat_message(From, {xmlelement, "message", Attrs, _Els} = Packet,
%% an implementation MAY allow users with certain privileges %% an implementation MAY allow users with certain privileges
%% (e.g., a room owner, room admin, or service-level admin) %% (e.g., a room owner, room admin, or service-level admin)
%% to send messages to the room even if those users are not occupants. %% to send messages to the room even if those users are not occupants.
%%
%% Check the mod_muc option access_message_nonparticipant and wether this JID
%% is allowed or denied
is_user_allowed_message_nonparticipant(JID, StateData) -> is_user_allowed_message_nonparticipant(JID, StateData) ->
case get_service_affiliation(JID, StateData) of case get_service_affiliation(JID, StateData) of
owner -> owner ->
@ -1665,15 +1666,25 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
SID = xml:get_attr_s("id", Attrs), SID = xml:get_attr_s("id", Attrs),
RoomJID = StateData#state.jid, RoomJID = StateData#state.jid,
To = jlib:jid_replace_resource(RoomJID, Nick), To = jlib:jid_replace_resource(RoomJID, Nick),
Limiter = {From#jid.luser, From#jid.lserver},
case ejabberd_captcha:create_captcha( case ejabberd_captcha:create_captcha(
SID, RoomJID, To, Lang, From) of SID, RoomJID, To, Lang, Limiter, From) of
{ok, ID, CaptchaEls} -> {ok, ID, CaptchaEls} ->
MsgPkt = {xmlelement, "message", [{"id", ID}], CaptchaEls}, MsgPkt = {xmlelement, "message", [{"id", ID}], CaptchaEls},
Robots = ?DICT:store(From, Robots = ?DICT:store(From,
{Nick, Packet}, StateData#state.robots), {Nick, Packet}, StateData#state.robots),
ejabberd_router:route(RoomJID, From, MsgPkt), ejabberd_router:route(RoomJID, From, MsgPkt),
StateData#state{robots = Robots}; StateData#state{robots = Robots};
error -> {error, limit} ->
ErrText = "Too many CAPTCHA requests",
Err = jlib:make_error_reply(
Packet, ?ERRT_RESOURCE_CONSTRAINT(Lang, ErrText)),
ejabberd_router:route( % TODO: s/Nick/""/
jlib:jid_replace_resource(
StateData#state.jid, Nick),
From, Err),
StateData;
_ ->
ErrText = "Unable to generate a captcha", ErrText = "Unable to generate a captcha",
Err = jlib:make_error_reply( Err = jlib:make_error_reply(
Packet, ?ERRT_INTERNAL_SERVER_ERROR(Lang, ErrText)), Packet, ?ERRT_INTERNAL_SERVER_ERROR(Lang, ErrText)),
@ -1725,7 +1736,24 @@ check_captcha(Affiliation, From, StateData) ->
{ok, passed} -> {ok, passed} ->
true; true;
_ -> _ ->
captcha_required WList = (StateData#state.config)#config.captcha_whitelist,
#jid{luser = U, lserver = S, lresource = R} = From,
case ?SETS:is_element({U, S, R}, WList) of
true ->
true;
false ->
case ?SETS:is_element({U, S, ""}, WList) of
true ->
true;
false ->
case ?SETS:is_element({"", S, ""}, WList) of
true ->
true;
false ->
captcha_required
end
end
end
end; end;
_ -> _ ->
true true
@ -2525,6 +2553,11 @@ can_change_ra(_FAffiliation, _FRole,
%% A room owner tries to add as persistent owner a %% A room owner tries to add as persistent owner a
%% participant that is already owner because he is MUC admin %% participant that is already owner because he is MUC admin
true; true;
can_change_ra(_FAffiliation, _FRole,
_TAffiliation, _TRole,
_RoleorAffiliation, _Value, owner) ->
%% Nobody can decrease MUC admin's role/affiliation
false;
can_change_ra(_FAffiliation, _FRole, can_change_ra(_FAffiliation, _FRole,
TAffiliation, _TRole, TAffiliation, _TRole,
affiliation, Value, _ServiceAf) affiliation, Value, _ServiceAf)
@ -2699,14 +2732,21 @@ send_kickban_presence(JID, Reason, Code, NewAffiliation, StateData) ->
end, LJIDs). end, LJIDs).
send_kickban_presence1(UJID, Reason, Code, Affiliation, StateData) -> send_kickban_presence1(UJID, Reason, Code, Affiliation, StateData) ->
{ok, #user{jid = _RealJID, {ok, #user{jid = RealJID,
nick = Nick}} = nick = Nick}} =
?DICT:find(jlib:jid_tolower(UJID), StateData#state.users), ?DICT:find(jlib:jid_tolower(UJID), StateData#state.users),
SAffiliation = affiliation_to_list(Affiliation), SAffiliation = affiliation_to_list(Affiliation),
BannedJIDString = jlib:jid_to_string(RealJID),
lists:foreach( lists:foreach(
fun({_LJID, Info}) -> fun({_LJID, Info}) ->
JidAttrList = case (Info#user.role == moderator) orelse
((StateData#state.config)#config.anonymous
== false) of
true -> [{"jid", BannedJIDString}];
false -> []
end,
ItemAttrs = [{"affiliation", SAffiliation}, ItemAttrs = [{"affiliation", SAffiliation},
{"role", "none"}], {"role", "none"}] ++ JidAttrList,
ItemEls = case Reason of ItemEls = case Reason of
"" -> "" ->
[]; [];
@ -2907,6 +2947,13 @@ is_password_settings_correct(XEl, StateData) ->
-define(PRIVATEXFIELD(Label, Var, Val), -define(PRIVATEXFIELD(Label, Var, Val),
?XFIELD("text-private", Label, Var, Val)). ?XFIELD("text-private", Label, Var, Val)).
-define(JIDMULTIXFIELD(Label, Var, JIDList),
{xmlelement, "field", [{"type", "jid-multi"},
{"label", translate:translate(Lang, Label)},
{"var", Var}],
[{xmlelement, "value", [], [{xmlcdata, jlib:jid_to_string(JID)}]}
|| JID <- JIDList]}).
get_default_room_maxusers(RoomState) -> get_default_room_maxusers(RoomState) ->
DefRoomOpts = gen_mod:get_module_opt(RoomState#state.server_host, mod_muc, default_room_options, []), DefRoomOpts = gen_mod:get_module_opt(RoomState#state.server_host, mod_muc, default_room_options, []),
RoomState2 = set_opts(DefRoomOpts, RoomState), RoomState2 = set_opts(DefRoomOpts, RoomState),
@ -3027,6 +3074,9 @@ get_config(Lang, StateData, From) ->
Config#config.captcha_protected)]; Config#config.captcha_protected)];
false -> [] false -> []
end ++ end ++
[?JIDMULTIXFIELD("Exclude Jabber IDs from CAPTCHA challenge",
"muc#roomconfig_captcha_whitelist",
?SETS:to_list(Config#config.captcha_whitelist))] ++
case mod_muc_log:check_access_log( case mod_muc_log:check_access_log(
StateData#state.server_host, From) of StateData#state.server_host, From) of
allow -> allow ->
@ -3096,6 +3146,18 @@ set_config(XEl, StateData) ->
-define(SET_STRING_XOPT(Opt, Val), -define(SET_STRING_XOPT(Opt, Val),
set_xoption(Opts, Config#config{Opt = Val})). set_xoption(Opts, Config#config{Opt = Val})).
-define(SET_JIDMULTI_XOPT(Opt, Vals),
begin
Set = lists:foldl(
fun({U, S, R}, Set1) ->
?SETS:add_element({U, S, R}, Set1);
(#jid{luser = U, lserver = S, lresource = R}, Set1) ->
?SETS:add_element({U, S, R}, Set1);
(_, Set1) ->
Set1
end, ?SETS:empty(), Vals),
set_xoption(Opts, Config#config{Opt = Set})
end).
set_xoption([], Config) -> set_xoption([], Config) ->
Config; Config;
@ -3153,6 +3215,9 @@ set_xoption([{"muc#roomconfig_maxusers", [Val]} | Opts], Config) ->
end; end;
set_xoption([{"muc#roomconfig_enablelogging", [Val]} | Opts], Config) -> set_xoption([{"muc#roomconfig_enablelogging", [Val]} | Opts], Config) ->
?SET_BOOL_XOPT(logging, Val); ?SET_BOOL_XOPT(logging, Val);
set_xoption([{"muc#roomconfig_captcha_whitelist", Vals} | Opts], Config) ->
JIDs = [jlib:string_to_jid(Val) || Val <- Vals],
?SET_JIDMULTI_XOPT(captcha_whitelist, JIDs);
set_xoption([{"FORM_TYPE", _} | Opts], Config) -> set_xoption([{"FORM_TYPE", _} | Opts], Config) ->
%% Ignore our FORM_TYPE %% Ignore our FORM_TYPE
set_xoption(Opts, Config); set_xoption(Opts, Config);
@ -3222,6 +3287,7 @@ set_opts([{Opt, Val} | Opts], StateData) ->
password -> StateData#state{config = (StateData#state.config)#config{password = Val}}; password -> StateData#state{config = (StateData#state.config)#config{password = Val}};
anonymous -> StateData#state{config = (StateData#state.config)#config{anonymous = Val}}; anonymous -> StateData#state{config = (StateData#state.config)#config{anonymous = Val}};
logging -> StateData#state{config = (StateData#state.config)#config{logging = Val}}; logging -> StateData#state{config = (StateData#state.config)#config{logging = Val}};
captcha_whitelist -> StateData#state{config = (StateData#state.config)#config{captcha_whitelist = ?SETS:from_list(Val)}};
max_users -> max_users ->
ServiceMaxUsers = get_service_max_users(StateData), ServiceMaxUsers = get_service_max_users(StateData),
MaxUsers = if MaxUsers = if
@ -3266,6 +3332,8 @@ make_opts(StateData) ->
?MAKE_CONFIG_OPT(anonymous), ?MAKE_CONFIG_OPT(anonymous),
?MAKE_CONFIG_OPT(logging), ?MAKE_CONFIG_OPT(logging),
?MAKE_CONFIG_OPT(max_users), ?MAKE_CONFIG_OPT(max_users),
{captcha_whitelist,
?SETS:to_list((StateData#state.config)#config.captcha_whitelist)},
{affiliations, ?DICT:to_list(StateData#state.affiliations)}, {affiliations, ?DICT:to_list(StateData#state.affiliations)},
{subject, StateData#state.subject}, {subject, StateData#state.subject},
{subject_author, StateData#state.subject_author} {subject_author, StateData#state.subject_author}

View File

@ -1,6 +1,6 @@
%%%---------------------------------------------------------------------- %%%----------------------------------------------------------------------
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -45,7 +45,8 @@
password = "", password = "",
anonymous = true, anonymous = true,
max_users = ?MAX_USERS_DEFAULT, max_users = ?MAX_USERS_DEFAULT,
logging = false logging = false,
captcha_whitelist = ?SETS:empty()
}). }).
-record(user, {jid, -record(user, {jid,

View File

@ -5,7 +5,7 @@
%%% Created : 5 Jan 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 5 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 5 Jan 2003 by Alexey Shchepin <alexey@process-one.net> %%% Created : 5 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as

View File

@ -5,7 +5,7 @@
%%% Created : 11 Jul 2009 by Brian Cully <bjc@kublai.com> %%% Created : 11 Jul 2009 by Brian Cully <bjc@kublai.com>
%%% %%%
%%% %%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%% %%%
%%% This program is free software; you can redistribute it and/or %%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as %%% modify it under the terms of the GNU General Public License as
@ -95,7 +95,7 @@ init([Host, Opts]) ->
SendPings = gen_mod:get_opt(send_pings, Opts, ?DEFAULT_SEND_PINGS), SendPings = gen_mod:get_opt(send_pings, Opts, ?DEFAULT_SEND_PINGS),
PingInterval = gen_mod:get_opt(ping_interval, Opts, ?DEFAULT_PING_INTERVAL), PingInterval = gen_mod:get_opt(ping_interval, Opts, ?DEFAULT_PING_INTERVAL),
TimeoutAction = gen_mod:get_opt(timeout_action, Opts, none), TimeoutAction = gen_mod:get_opt(timeout_action, Opts, none),
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), IQDisc = gen_mod:get_opt(iqdisc, Opts, no_queue),
mod_disco:register_feature(Host, ?NS_PING), mod_disco:register_feature(Host, ?NS_PING),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PING, gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PING,
?MODULE, iq_ping, IQDisc), ?MODULE, iq_ping, IQDisc),

Some files were not shown because too many files have changed in this diff Show More