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:
commit
1dfd9fd568
2
README
2
README
@ -20,7 +20,7 @@ To compile ejabberd you need:
|
||||
- GNU Iconv 1.8 or higher, for the IRC Transport
|
||||
(mod_irc). Optional. Not needed on systems with GNU Libc.
|
||||
- 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
|
||||
|
@ -356,7 +356,7 @@ Don’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">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’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-->
|
||||
<H3 CLASS="subsection"><!--SEC ANCHOR --><A NAME="htoc10">2.4.2</A>  <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:
|
||||
@ -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>
|
||||
is a number of bits of entropy. The recommended minimum is 32 bits.
|
||||
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
|
||||
the second string is the message body.
|
||||
In the body you can set a newline with the characters: <CODE>\n</CODE>
|
||||
|
@ -66,6 +66,7 @@
|
||||
\newcommand{\module}[1]{\texttt{#1}}
|
||||
\newcommand{\modadhoc}{\module{mod\_adhoc}}
|
||||
\newcommand{\modannounce}{\module{mod\_announce}}
|
||||
\newcommand{\modblocking}{\module{mod\_blocking}}
|
||||
\newcommand{\modcaps}{\module{mod\_caps}}
|
||||
\newcommand{\modconfigure}{\module{mod\_configure}}
|
||||
\newcommand{\moddisco}{\module{mod\_disco}}
|
||||
@ -80,6 +81,7 @@
|
||||
\newcommand{\modoffline}{\module{mod\_offline}}
|
||||
\newcommand{\modofflineodbc}{\module{mod\_offline\_odbc}}
|
||||
\newcommand{\modping}{\module{mod\_ping}}
|
||||
\newcommand{\modprescounter}{\module{mod\_pres\_counter}}
|
||||
\newcommand{\modprivacy}{\module{mod\_privacy}}
|
||||
\newcommand{\modprivacyodbc}{\module{mod\_privacy\_odbc}}
|
||||
\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 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 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}
|
||||
|
||||
\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 3478 listens for STUN requests over UDP.
|
||||
\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.
|
||||
\end{itemize}
|
||||
\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, [
|
||||
web_admin,
|
||||
http_bind,
|
||||
tls, {certfile, "/etc/ejabberd/server.pem"},
|
||||
]}
|
||||
]
|
||||
@ -1635,11 +1638,14 @@ The configurable options are:
|
||||
\titem{\{captcha\_cmd, Path\}}
|
||||
Full path to a script that generates the image.
|
||||
The default value is an empty string: \term{""}
|
||||
\titem{\{captcha\_host, Host\}}
|
||||
Host part of the URL sent to the user.
|
||||
You can include the port number.
|
||||
The URL sent to the user is formed by: \term{http://Host/captcha/}
|
||||
The default value is the first hostname configured.
|
||||
\titem{\{captcha\_host, ProtocolHostPort\}}
|
||||
ProtocolHostPort is a string with the host, and optionally the Protocol and Port number.
|
||||
It must identify where ejabberd listens for CAPTCHA requests.
|
||||
The URL sent to the user is formed by: \term{Protocol://Host:Port/captcha/}
|
||||
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}
|
||||
|
||||
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_host, "example.org:5280"}.
|
||||
%% {captcha_host, "https://example.org:443"}.
|
||||
%% {captcha_host, "http://example.com"}.
|
||||
|
||||
{listen,
|
||||
[
|
||||
@ -1863,6 +1871,7 @@ The following LDAP servers are tested with \ejabberd{}:
|
||||
\item \footahref{http://www.microsoft.com/activedirectory/}{Active Directory}
|
||||
(see section~\ref{ad})
|
||||
\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
|
||||
success with a not-listed server so that we can list it here.
|
||||
\end{itemize}
|
||||
@ -2526,6 +2535,7 @@ The following table lists all modules included in \ejabberd{}.
|
||||
\hline
|
||||
\hline \modadhoc{} & Ad-Hoc Commands (\xepref{0050}) & \\
|
||||
\hline \ahrefloc{modannounce}{\modannounce{}} & Manage announcements & recommends \modadhoc{} \\
|
||||
\hline \modblocking{} & Simple Communications Blocking (\xepref{0191}) & \modprivacy{} \\
|
||||
\hline \modcaps{} & Entity Capabilities (\xepref{0115}) & \\
|
||||
\hline \modconfigure{} & Server configuration using Ad-Hoc & \modadhoc{} \\
|
||||
\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}{\modofflineodbc{}} & Offline message storage (\xepref{0160}) & supported DB (*) \\
|
||||
\hline \ahrefloc{modping}{\modping{}} & XMPP Ping and periodic keepalives (\xepref{0199}) & \\
|
||||
\hline \ahrefloc{modprivacy}{\modprivacy{}} & Blocking Communication (XMPP IM) & \\
|
||||
\hline \ahrefloc{modprivacy}{\modprivacyodbc{}} & Blocking Communication (XMPP IM) & supported DB (*) \\
|
||||
\hline \ahrefloc{modprescounter}{\modprivacy{}} & Detect presence subscription flood & \\
|
||||
\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}{\modprivateodbc{}} & Private XML Storage (\xepref{0049}) & supported DB (*) \\
|
||||
\hline \ahrefloc{modproxy}{\modproxy{}} & SOCKS5 Bytestreams (\xepref{0065}) & \\
|
||||
@ -2625,7 +2636,8 @@ The syntax is:
|
||||
Possible \term{Value} are:
|
||||
\begin{description}
|
||||
\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
|
||||
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
|
||||
@ -2633,7 +2645,7 @@ Possible \term{Value} are:
|
||||
of this queue is done in parallel with that of other packets. This discipline
|
||||
is most recommended.
|
||||
\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.
|
||||
\titem{parallel} For every packet with this discipline a separate Erlang process
|
||||
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}
|
||||
|
||||
\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{}}
|
||||
\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:
|
||||
\begin{description}
|
||||
\titem{\{access, AccessName\}} \ind{options!access}This option can be configured to specify
|
||||
rules to restrict registration. If a rule returns `deny' on the requested
|
||||
user name, registration for that user name is denied. (there are no
|
||||
restrictions by default).
|
||||
\titem{\{access, AccessName\}} \ind{options!access}
|
||||
Specify rules to restrict what usernames can be registered and unregistered.
|
||||
If a rule returns `deny' on the requested username,
|
||||
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{}
|
||||
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
|
||||
@ -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}
|
||||
is a number of bits of entropy. The recommended minimum is 32 bits.
|
||||
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
|
||||
the second string is the message body.
|
||||
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[Description] The description of the group. This parameter does not affect
|
||||
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.
|
||||
To put as members all the registered users in the virtual hosts,
|
||||
you can use the special directive: @all@.
|
||||
Note that this directive is designed for a small server with just a few hundred users.
|
||||
The special member directive \term{@all@}
|
||||
represents all the registered users in the virtual host;
|
||||
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
|
||||
group's members.
|
||||
\end{description}
|
||||
@ -4964,6 +5012,9 @@ The command line parameters:
|
||||
If using \term{-sname}, specify either this option or \term{ERL\_INETRC}.
|
||||
\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.
|
||||
\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}
|
||||
Starts the Erlang system detached from the system console.
|
||||
Useful for running daemons and backgrounds processes.
|
||||
@ -5376,6 +5427,12 @@ The Erlang command-line parameter used internally is, for example:
|
||||
\begin{verbatim}
|
||||
erl ... -kernel inet_dist_listen_min 4370 inet_dist_listen_max 4375
|
||||
\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}
|
||||
|
@ -213,6 +213,7 @@ install: all
|
||||
sed -e "s*@ctlscriptpath@*$(SBINDIR)*" \
|
||||
-e "s*@installuser@*$(INIT_USER)*" ejabberd.init.template \
|
||||
> ejabberd.init
|
||||
chmod 755 ejabberd.init
|
||||
#
|
||||
# Binary Erlang files
|
||||
install -d $(BEAMDIR)
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -6,7 +6,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -6,7 +6,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -6,7 +6,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -475,10 +475,14 @@
|
||||
%%{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"}.
|
||||
|
||||
%%
|
||||
%% Limit CAPTCHA calls per minute for JID/IP to avoid DoS.
|
||||
%%
|
||||
%%{captcha_limit, 5}.
|
||||
|
||||
%%%. =======
|
||||
%%%' MODULES
|
||||
@ -490,6 +494,7 @@
|
||||
[
|
||||
{mod_adhoc, []},
|
||||
{mod_announce, [{access, announce}]}, % recommends mod_adhoc
|
||||
{mod_blocking,[]}, % requires mod_privacy
|
||||
{mod_caps, []},
|
||||
{mod_configure,[]}, % requires mod_adhoc
|
||||
{mod_disco, []},
|
||||
@ -511,6 +516,7 @@
|
||||
%%{mod_muc_log,[]},
|
||||
{mod_offline, [{access_max_user_messages, max_user_offline_messages}]},
|
||||
{mod_ping, []},
|
||||
%%{mod_pres_counter,[{count, 5}, {interval, 60}]},
|
||||
{mod_privacy, []},
|
||||
{mod_private, []},
|
||||
%%{mod_proxy65,[]},
|
||||
@ -545,9 +551,17 @@
|
||||
%%
|
||||
%%{registration_watchers, ["admin1@example.org"]},
|
||||
|
||||
%%
|
||||
%% Only clients in the server machine can register accounts
|
||||
%%
|
||||
{ip_access, [{allow, "127.0.0.0/8"},
|
||||
{deny, "0.0.0.0/0"}]},
|
||||
|
||||
%%
|
||||
%% Local c2s or remote s2s users cannot register accounts
|
||||
%%
|
||||
%%{access_from, deny},
|
||||
|
||||
{access, register}
|
||||
]},
|
||||
%%{mod_register_web, [
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -1,6 +1,20 @@
|
||||
#! /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 nounset
|
||||
|
||||
DIR=@ctlscriptpath@
|
||||
CTL="$DIR"/ejabberdctl
|
||||
@ -32,14 +46,17 @@ case "$1" in
|
||||
su - $USER -c "$CTL stopped"
|
||||
echo "done."
|
||||
;;
|
||||
|
||||
status)
|
||||
test -x "$CTL" || exit 0
|
||||
echo "Getting ejabberd status..."
|
||||
su - $USER -c "$CTL status"
|
||||
;;
|
||||
force-reload|restart)
|
||||
"$0" stop
|
||||
"$0" start
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|force-reload}"
|
||||
echo "Usage: $0 {start|stop|restart|force-reload|status}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% 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:fprof_start(),
|
||||
maybe_add_nameservers(),
|
||||
{ok, Pid} = ejabberd_cluster:start(),
|
||||
start_modules(),
|
||||
ejabberd_cluster:announce(),
|
||||
ejabberd_cluster:announce(Pid),
|
||||
ejabberd_node_groups:start(),
|
||||
ejabberd_listener:start_listeners(),
|
||||
?INFO_MSG("ejabberd ~s is started in the node ~p", [?VERSION, node()]),
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% 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),
|
||||
case AuthModule == ?MODULE of
|
||||
true ->
|
||||
ejabberd_hooks:run(register_user, LServer, [LUser, LServer]),
|
||||
US = {LUser, LServer},
|
||||
mnesia:async_dirty(
|
||||
fun() -> mnesia:write(#anonymous{us = US, sid=SID})
|
||||
@ -231,8 +232,8 @@ try_register(_User, _Server, _Password) ->
|
||||
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
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@ -194,7 +194,7 @@ stop(FsmRef) ->
|
||||
?GEN_FSM:send_event(FsmRef, closed).
|
||||
|
||||
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
|
||||
@ -231,6 +231,12 @@ init([{SockMod, Socket}, Opts, FSMLimitOpts]) ->
|
||||
(_) -> false
|
||||
end, Opts),
|
||||
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
|
||||
{value, {_, IP1}} ->
|
||||
IP1;
|
||||
@ -246,7 +252,7 @@ init([{SockMod, Socket}, Opts, FSMLimitOpts]) ->
|
||||
false ->
|
||||
Socket1 =
|
||||
if
|
||||
TLSEnabled ->
|
||||
TLSEnabled andalso SockMod /= ejabberd_frontend_socket ->
|
||||
SockMod:starttls(Socket, TLSOpts);
|
||||
true ->
|
||||
Socket
|
||||
@ -265,8 +271,20 @@ init([{SockMod, Socket}, Opts, FSMLimitOpts]) ->
|
||||
access = Access,
|
||||
shaper = Shaper,
|
||||
ip = IP,
|
||||
redirect = Redirect,
|
||||
fsm_limit_opts = FSMLimitOpts},
|
||||
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;
|
||||
init([StateName, StateData, _FSMLimitOpts]) ->
|
||||
MRef = (StateData#state.sockmod):monitor(StateData#state.socket),
|
||||
@ -282,6 +300,7 @@ init([StateName, StateData, _FSMLimitOpts]) ->
|
||||
El ->
|
||||
get_priority_from_presence(El)
|
||||
end,
|
||||
ejabberd_sm:drop_session(StateData#state.sid),
|
||||
ejabberd_sm:open_session(
|
||||
SID,
|
||||
StateData#state.user,
|
||||
@ -289,8 +308,11 @@ init([StateName, StateData, _FSMLimitOpts]) ->
|
||||
StateData#state.resource,
|
||||
Priority,
|
||||
Info),
|
||||
%%ejabberd_sm:drop_session(StateData#state.sid),
|
||||
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 ->
|
||||
{ok, StateName, StateData#state{socket_monitor = MRef}}
|
||||
end.
|
||||
@ -581,15 +603,20 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
"(~w) Accepted legacy authentication for ~s by ~p",
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(JID), AuthModule]),
|
||||
case need_redirect(StateData#state{user = U}) of
|
||||
{true, Host} ->
|
||||
?INFO_MSG("(~w) Redirecting ~s to ~s",
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(JID), Host]),
|
||||
send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
|
||||
send_trailer(StateData),
|
||||
{stop, normal, StateData};
|
||||
false ->
|
||||
SID = {now(), self()},
|
||||
Conn = get_conn_type(StateData),
|
||||
%% Info = [{ip, StateData#state.ip}, {conn, Conn},
|
||||
%% {auth_module, AuthModule}],
|
||||
Res1 = jlib:make_result_iq_reply(El),
|
||||
Res = setelement(4, Res1, []),
|
||||
send_element(StateData, Res),
|
||||
%% ejabberd_sm:open_session(
|
||||
%% SID, U, StateData#state.server, R, Info),
|
||||
change_shaper(StateData, JID),
|
||||
{Fs, Ts} = ejabberd_hooks:run_fold(
|
||||
roster_get_subscription_lists,
|
||||
@ -602,10 +629,12 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
Ts1 = [LJID | Ts],
|
||||
PrivList =
|
||||
ejabberd_hooks:run_fold(
|
||||
privacy_get_user_list, StateData#state.server,
|
||||
privacy_get_user_list,
|
||||
StateData#state.server,
|
||||
#userlist{},
|
||||
[U, StateData#state.server]),
|
||||
NewStateData = StateData#state{
|
||||
NewStateData =
|
||||
StateData#state{
|
||||
user = U,
|
||||
resource = R,
|
||||
jid = JID,
|
||||
@ -615,11 +644,14 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
|
||||
pres_f = ?SETS:from_list(Fs1),
|
||||
pres_t = ?SETS:from_list(Ts1),
|
||||
privacy_list = PrivList},
|
||||
DebugFlag = ejabberd_hooks:run_fold(c2s_debug_start_hook,
|
||||
DebugFlag = ejabberd_hooks:run_fold(
|
||||
c2s_debug_start_hook,
|
||||
NewStateData#state.server,
|
||||
false,
|
||||
[self(), NewStateData]),
|
||||
maybe_migrate(session_established, NewStateData#state{debug=DebugFlag});
|
||||
maybe_migrate(session_established,
|
||||
NewStateData#state{debug=DebugFlag})
|
||||
end;
|
||||
_ ->
|
||||
?INFO_MSG(
|
||||
"(~w) Failed legacy authentication for ~s",
|
||||
@ -713,19 +745,28 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
{ok, Props} ->
|
||||
catch (StateData#state.sockmod):reset_stream(
|
||||
StateData#state.socket),
|
||||
send_element(StateData,
|
||||
{xmlelement, "success",
|
||||
[{"xmlns", ?NS_SASL}], []}),
|
||||
U = xml:get_attr_s(username, Props),
|
||||
AuthModule = xml:get_attr_s(auth_module, Props),
|
||||
?INFO_MSG("(~w) Accepted authentication for ~s by ~p",
|
||||
[StateData#state.socket, U, AuthModule]),
|
||||
case need_redirect(StateData#state{user = U}) of
|
||||
{true, Host} ->
|
||||
?INFO_MSG("(~w) Redirecting ~s to ~s",
|
||||
[StateData#state.socket, U, Host]),
|
||||
send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
|
||||
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 });
|
||||
user = U })
|
||||
end;
|
||||
{continue, ServerOut, NewSASLState} ->
|
||||
send_element(StateData,
|
||||
{xmlelement, "challenge",
|
||||
@ -866,19 +907,28 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
|
||||
{ok, Props} ->
|
||||
catch (StateData#state.sockmod):reset_stream(
|
||||
StateData#state.socket),
|
||||
send_element(StateData,
|
||||
{xmlelement, "success",
|
||||
[{"xmlns", ?NS_SASL}], []}),
|
||||
U = xml:get_attr_s(username, Props),
|
||||
AuthModule = xml:get_attr_s(auth_module, Props),
|
||||
?INFO_MSG("(~w) Accepted authentication for ~s by ~p",
|
||||
[StateData#state.socket, U, AuthModule]),
|
||||
case need_redirect(StateData#state{user = U}) of
|
||||
{true, Host} ->
|
||||
?INFO_MSG("(~w) Redirecting ~s to ~s",
|
||||
[StateData#state.socket, U, Host]),
|
||||
send_element(StateData, ?SERR_SEE_OTHER_HOST(Host)),
|
||||
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});
|
||||
user = U})
|
||||
end;
|
||||
{continue, ServerOut, NewSASLState} ->
|
||||
send_element(StateData,
|
||||
{xmlelement, "challenge",
|
||||
@ -1045,6 +1095,11 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
|
||||
fsm_next_state(wait_for_session, StateData)
|
||||
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) ->
|
||||
{stop, normal, StateData};
|
||||
|
||||
@ -1172,7 +1227,9 @@ session_established2(El, StateData) ->
|
||||
end;
|
||||
"iq" ->
|
||||
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(
|
||||
user_send_packet,
|
||||
Server,
|
||||
@ -1230,9 +1287,6 @@ session_established2(El, StateData) ->
|
||||
%% {next_state, NextStateName, NextStateData, Timeout} |
|
||||
%% {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) ->
|
||||
NewStateData = roster_change(IJID, ISubscription, StateData),
|
||||
fsm_next_state(StateName, NewStateData);
|
||||
@ -1433,6 +1487,9 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
send_element(StateData, PrivPushEl),
|
||||
{false, Attrs, StateData#state{privacy_list = NewPL}}
|
||||
end;
|
||||
[{blocking, What}] ->
|
||||
route_blocking(What, StateData),
|
||||
{false, Attrs, StateData};
|
||||
_ ->
|
||||
{false, Attrs, StateData}
|
||||
end;
|
||||
@ -1622,6 +1679,12 @@ handle_info({force_update_presence, LUser}, StateName,
|
||||
StateData
|
||||
end,
|
||||
{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) ->
|
||||
Recipients = ejabberd_hooks:run_fold(
|
||||
c2s_broadcast_recipients, StateData#state.server,
|
||||
@ -1698,7 +1761,7 @@ terminate(_Reason, StateName, StateData) ->
|
||||
presence_broadcast(
|
||||
StateData, From, StateData#state.pres_i, Packet);
|
||||
rebinded ->
|
||||
ejabberd_sm:close_session(
|
||||
ejabberd_sm:close_migrated_session(
|
||||
StateData#state.sid,
|
||||
StateData#state.user,
|
||||
StateData#state.server,
|
||||
@ -1969,24 +2032,12 @@ process_presence_probe(From, To, StateData) ->
|
||||
(E) ->
|
||||
[E]
|
||||
end, PresEls),
|
||||
{xmlelement, "presence", PresAttrs,
|
||||
[{xmlelement, "show", [],
|
||||
[{xmlcdata,
|
||||
StateData#state.oor_show}]},
|
||||
{xmlelement, "status", [],
|
||||
[{xmlcdata,
|
||||
StateData#state.oor_status}]}]
|
||||
++ PresEls1}
|
||||
make_oor_presence(
|
||||
StateData, PresAttrs, PresEls1)
|
||||
end
|
||||
end,
|
||||
Timestamp = StateData#state.pres_timestamp,
|
||||
Packet1 = xml:append_subtags(
|
||||
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)]),
|
||||
Packet1 = maybe_add_delay(Packet, utc, To, "", Timestamp),
|
||||
case ejabberd_hooks:run_fold(
|
||||
privacy_check_packet, StateData#state.server,
|
||||
allow,
|
||||
@ -2422,7 +2473,7 @@ resend_offline_messages(StateData) ->
|
||||
Rs when is_list(Rs) ->
|
||||
lists:foreach(
|
||||
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
|
||||
allow ->
|
||||
true;
|
||||
@ -2431,11 +2482,11 @@ resend_offline_messages(StateData) ->
|
||||
end,
|
||||
if
|
||||
Pass ->
|
||||
Attrs2 = jlib:replace_from_to_attrs(
|
||||
jlib:jid_to_string(From),
|
||||
jlib:jid_to_string(To),
|
||||
Attrs),
|
||||
FixedPacket = {xmlelement, Name, Attrs2, Els},
|
||||
%% Attrs2 = jlib:replace_from_to_attrs(
|
||||
%% jlib:jid_to_string(From),
|
||||
%% jlib:jid_to_string(To),
|
||||
%% Attrs),
|
||||
%% FixedPacket = {xmlelement, Name, Attrs2, Els},
|
||||
%% Use route instead of send_element to go through standard workflow
|
||||
ejabberd_router:route(From, To, Packet);
|
||||
%% send_element(StateData, FixedPacket),
|
||||
@ -2526,23 +2577,25 @@ peerip(SockMod, Socket) ->
|
||||
|
||||
maybe_migrate(StateName, StateData) ->
|
||||
PackedStateData = pack(StateData),
|
||||
case ejabberd_cluster:get_node({StateData#state.user,
|
||||
StateData#state.server}) of
|
||||
#state{user = U, server = S, resource = R, sid = SID} = StateData,
|
||||
case ejabberd_cluster:get_node({jlib:nodeprep(U), jlib:nameprep(S)}) of
|
||||
Node when Node == node() ->
|
||||
Conn = get_conn_type(StateData),
|
||||
Info = [{ip, StateData#state.ip}, {conn, Conn},
|
||||
{auth_module, StateData#state.auth_module}],
|
||||
#state{user = U, server = S, resource = R, sid = SID} = StateData,
|
||||
ejabberd_sm:open_session(SID, U, S, R, Info),
|
||||
erlang:garbage_collect(),
|
||||
case ejabberd_cluster:get_node_new({U, S}) of
|
||||
Node ->
|
||||
ok;
|
||||
NewNode ->
|
||||
After = ejabberd_cluster:rehash_timeout(),
|
||||
migrate(self(), NewNode, After)
|
||||
Presence = StateData#state.pres_last,
|
||||
Priority =
|
||||
case Presence of
|
||||
undefined ->
|
||||
undefined;
|
||||
_ ->
|
||||
get_priority_from_presence(Presence)
|
||||
end,
|
||||
fsm_next_state(StateName, PackedStateData);
|
||||
ejabberd_sm:open_session(SID, U, S, R, Priority, Info),
|
||||
StateData2 = change_reception(PackedStateData, true),
|
||||
StateData3 = start_keepalive_timer(StateData2),
|
||||
erlang:garbage_collect(),
|
||||
fsm_next_state(StateName, StateData3);
|
||||
Node ->
|
||||
fsm_migrate(StateName, PackedStateData, Node, 0)
|
||||
end.
|
||||
@ -2627,12 +2680,7 @@ change_reception(#state{reception = true} = StateData, false) ->
|
||||
"" ->
|
||||
ok;
|
||||
_ ->
|
||||
Packet =
|
||||
{xmlelement, "presence", [],
|
||||
[{xmlelement, "show", [],
|
||||
[{xmlcdata, StateData#state.oor_show}]},
|
||||
{xmlelement, "status", [],
|
||||
[{xmlcdata, StateData#state.oor_status}]}]},
|
||||
Packet = make_oor_presence(StateData),
|
||||
update_priority(0, Packet, StateData#state{reception = false}),
|
||||
presence_broadcast_to_trusted(
|
||||
StateData,
|
||||
@ -2775,7 +2823,14 @@ send_out_of_reception_message(StateData, From, To,
|
||||
CBody = utf8_cut(Body, 100),
|
||||
case StateData#state.oor_send_from of
|
||||
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
|
||||
end;
|
||||
true ->
|
||||
@ -2810,6 +2865,23 @@ send_out_of_reception_message(StateData, From, To,
|
||||
send_out_of_reception_message(StateData, _From, _To, _Packet) ->
|
||||
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 + 1).
|
||||
|
||||
@ -2825,6 +2897,47 @@ utf8_cut([C | S], Cur, Prev, Bytes) ->
|
||||
utf8_cut(S, [C | Cur], Cur, Bytes - 1)
|
||||
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) ->
|
||||
erlang:cancel_timer(Timer),
|
||||
@ -2873,17 +2986,13 @@ enqueue(StateData, From, To, Packet) ->
|
||||
StateData#state{pres_queue = NewQueue}
|
||||
end;
|
||||
true ->
|
||||
CleanPacket = xml:remove_subtags(
|
||||
xml:remove_subtags(Packet, "x", {"xmlns", ?NS_P1_PUSHED}),
|
||||
"x", {"xmlns", ?NS_DELAY91}),
|
||||
CleanPacket = xml:remove_subtags(Packet, "x", {"xmlns", ?NS_P1_PUSHED}),
|
||||
Packet2 =
|
||||
case CleanPacket of
|
||||
{xmlelement, "message" = Name, Attrs, Els} ->
|
||||
{xmlelement, Name, Attrs,
|
||||
Els ++
|
||||
[jlib:timestamp_to_xml(
|
||||
calendar:now_to_universal_time(now())),
|
||||
{xmlelement, "x", [{"xmlns", ?NS_P1_PUSHED}], []}]};
|
||||
{xmlelement, "message", _, _} ->
|
||||
xml:append_subtags(
|
||||
maybe_add_delay(CleanPacket, utc, To, ""),
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_P1_PUSHED}], []}]);
|
||||
_ ->
|
||||
Packet
|
||||
end,
|
||||
@ -3025,15 +3134,6 @@ rebind(StateData, JID, StreamID) ->
|
||||
[StateData#state.socket,
|
||||
jlib:jid_to_string(JID)]),
|
||||
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 =
|
||||
NewStateData#state{
|
||||
socket = StateData#state.socket,
|
||||
@ -3044,22 +3144,11 @@ rebind(StateData, JID, StreamID) ->
|
||||
keepalive_timer = StateData#state.keepalive_timer,
|
||||
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,
|
||||
{xmlelement, "rebind",
|
||||
[{"xmlns", ?NS_P1_REBIND}],
|
||||
[]}),
|
||||
StateData3 = change_reception(StateData2, true),
|
||||
StateData4 = start_keepalive_timer(StateData3),
|
||||
fsm_next_state(session_established,
|
||||
StateData4)
|
||||
maybe_migrate(session_established, StateData2)
|
||||
after 1000 ->
|
||||
send_element(StateData,
|
||||
{xmlelement, "failure",
|
||||
@ -3221,6 +3310,33 @@ check_x_attachment1([El | Els]) ->
|
||||
check_x_attachment1(Els)
|
||||
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) ->
|
||||
%% First test previous version attribute:
|
||||
@ -3233,6 +3349,7 @@ send_from(El) ->
|
||||
case xml:get_path_s(El, [{elem, "body"}, {attr, "from"}]) of
|
||||
"jid" -> jid;
|
||||
"username" -> username;
|
||||
"name" -> name;
|
||||
"none" -> none;
|
||||
_ -> jid
|
||||
end
|
||||
@ -3260,6 +3377,51 @@ bounce_messages() ->
|
||||
ok
|
||||
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
|
||||
%%%----------------------------------------------------------------------
|
||||
@ -3333,3 +3495,39 @@ flash_policy_string() ->
|
||||
++ ToPortsString ++
|
||||
"\"/>\n"
|
||||
"</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.
|
||||
|
@ -57,6 +57,7 @@
|
||||
conn = unknown,
|
||||
auth_module = unknown,
|
||||
ip,
|
||||
redirect = false,
|
||||
aux_fields = [],
|
||||
fsm_limit_opts,
|
||||
lang,
|
||||
|
@ -6,7 +6,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% 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,
|
||||
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,
|
||||
create_captcha_x/4, create_captcha_x/5]).
|
||||
create_captcha_x/5, create_captcha_x/6]).
|
||||
|
||||
-include("jlib.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
@ -50,8 +50,9 @@
|
||||
-define(CAPTCHA_TEXT(Lang), translate:translate(Lang, "Enter the text you see")).
|
||||
-define(CAPTCHA_LIFETIME, 120000). % two minutes
|
||||
-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}).
|
||||
|
||||
%%====================================================================
|
||||
@ -64,10 +65,10 @@
|
||||
start_link() ->
|
||||
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),
|
||||
is_record(From, jid), is_record(To, jid) ->
|
||||
case create_image() of
|
||||
case create_image(Limiter) of
|
||||
{ok, Type, Key, Image} ->
|
||||
Id = randoms:get_string() ++ "-" ++ ejabberd_cluster:node_id(),
|
||||
B64Image = jlib:encode_base64(binary_to_list(Image)),
|
||||
@ -99,15 +100,15 @@ create_captcha(SID, From, To, Lang, Args)
|
||||
ets:insert(captcha, #captcha{id=Id, pid=self(), key=Key,
|
||||
tref=Tref, args=Args}),
|
||||
{ok, Id, [Body, OOB, Captcha, Data]};
|
||||
_Err ->
|
||||
error
|
||||
Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
create_captcha_x(SID, To, Lang, HeadEls) ->
|
||||
create_captcha_x(SID, To, Lang, HeadEls, []).
|
||||
create_captcha_x(SID, To, Lang, Limiter, HeadEls) ->
|
||||
create_captcha_x(SID, To, Lang, Limiter, HeadEls, []).
|
||||
|
||||
create_captcha_x(SID, To, Lang, HeadEls, TailEls) ->
|
||||
case create_image() of
|
||||
create_captcha_x(SID, To, Lang, Limiter, HeadEls, TailEls) ->
|
||||
case create_image(Limiter) of
|
||||
{ok, Type, Key, Image} ->
|
||||
Id = randoms:get_string() ++ "-" ++ ejabberd_cluster:node_id(),
|
||||
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}),
|
||||
ets:insert(captcha, #captcha{id=Id, key=Key, tref=Tref}),
|
||||
{ok, [Captcha, Data]};
|
||||
_ ->
|
||||
error
|
||||
Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
%% @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)
|
||||
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
|
||||
{ok, #captcha{key=Key}} ->
|
||||
case create_image(Key) of
|
||||
case create_image(Addr, Key) of
|
||||
{ok, Type, _, Img} ->
|
||||
{200,
|
||||
[{"Content-Type", Type},
|
||||
{"Cache-Control", "no-cache"},
|
||||
{"Last-Modified", httpd_util:rfc1123_date()}],
|
||||
Img};
|
||||
{error, limit} ->
|
||||
ejabberd_web:error(not_allowed);
|
||||
_ ->
|
||||
ejabberd_web:error(not_found)
|
||||
end;
|
||||
@ -288,6 +292,20 @@ init([]) ->
|
||||
check_captcha_setup(),
|
||||
{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) ->
|
||||
{reply, bad_request, State}.
|
||||
|
||||
@ -329,11 +347,22 @@ code_change(_OldVsn, State, _Extra) ->
|
||||
%% Reason = atom()
|
||||
%%--------------------------------------------------------------------
|
||||
create_image() ->
|
||||
create_image(undefined).
|
||||
|
||||
create_image(Limiter) ->
|
||||
%% Six numbers from 1 to 9.
|
||||
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(),
|
||||
Cmd = lists:flatten(io_lib:format("~s ~s", [FileName, Key])),
|
||||
case cmd(Cmd) of
|
||||
@ -363,20 +392,82 @@ get_prog_name() ->
|
||||
case ejabberd_config:get_local_option(captcha_cmd) of
|
||||
FileName when is_list(FileName) ->
|
||||
FileName;
|
||||
_ ->
|
||||
Value when (Value == undefined) or (Value == "") ->
|
||||
?DEBUG("The option captcha_cmd is not configured, but some "
|
||||
"module wants to use the CAPTCHA feature.", []),
|
||||
throw({error, option_not_configured_captcha_cmd})
|
||||
false
|
||||
end.
|
||||
|
||||
get_url(Str) ->
|
||||
case ejabberd_config:get_local_option(captcha_host) of
|
||||
Host when is_list(Host) ->
|
||||
CaptchaHost = ejabberd_config:get_local_option(captcha_host),
|
||||
case string:tokens(CaptchaHost, ":") of
|
||||
[Host] ->
|
||||
"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
|
||||
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}
|
||||
%% Cmd = string()
|
||||
@ -425,28 +516,23 @@ return(Port, TRef, Result) ->
|
||||
catch port_close(Port),
|
||||
Result.
|
||||
|
||||
is_feature_enabled() ->
|
||||
try get_prog_name() of
|
||||
Prog when is_list(Prog) -> true
|
||||
catch
|
||||
_:_ -> false
|
||||
end.
|
||||
|
||||
is_feature_available() ->
|
||||
case is_feature_enabled() of
|
||||
false -> false;
|
||||
true ->
|
||||
case create_image() of
|
||||
{ok, _, _, _} -> true;
|
||||
_Error -> false
|
||||
end
|
||||
case get_prog_name() of
|
||||
Prog when is_list(Prog) -> true;
|
||||
false -> false
|
||||
end.
|
||||
|
||||
check_captcha_setup() ->
|
||||
case is_feature_enabled() andalso not is_feature_available() of
|
||||
case is_feature_available() of
|
||||
true ->
|
||||
case create_image() of
|
||||
{ok, _, _, _} ->
|
||||
ok;
|
||||
_Err ->
|
||||
?CRITICAL_MSG("Captcha is enabled in the option captcha_cmd, "
|
||||
"but it can't generate images.", []);
|
||||
"but it can't generate images.", []),
|
||||
throw({error, captcha_cmd_enabled_but_fails})
|
||||
end;
|
||||
false ->
|
||||
ok
|
||||
end.
|
||||
@ -498,3 +584,21 @@ do_check_captcha(Id, ProvidedKey) ->
|
||||
_ ->
|
||||
captcha_not_found
|
||||
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).
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -10,8 +10,8 @@
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API
|
||||
-export([start_link/0, get_node/1, get_node_new/1, announce/0, shutdown/0,
|
||||
node_id/0, get_node_by_id/1, get_nodes/0, rehash_timeout/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, start/0]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
@ -22,15 +22,28 @@
|
||||
-define(HASHTBL, nodes_hash).
|
||||
-define(HASHTBL_NEW, nodes_hash_new).
|
||||
-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, {}).
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
start() ->
|
||||
ChildSpec = {?MODULE,
|
||||
{?MODULE, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[?MODULE]},
|
||||
supervisor:start_child(ejabberd_sup, ChildSpec).
|
||||
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
gen_server:start_link(?MODULE, [], []).
|
||||
|
||||
get_node(Key) ->
|
||||
Hash = erlang:phash2(Key),
|
||||
@ -44,8 +57,8 @@ get_nodes() ->
|
||||
%% TODO
|
||||
mnesia:system_info(running_db_nodes).
|
||||
|
||||
announce() ->
|
||||
gen_server:call(?MODULE, announce, infinity).
|
||||
announce(Pid) ->
|
||||
gen_server:call(Pid, announce, infinity).
|
||||
|
||||
node_id() ->
|
||||
integer_to_list(erlang:phash2(node())).
|
||||
@ -80,13 +93,15 @@ shutdown() ->
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
init([]) ->
|
||||
{A, B, C} = now(),
|
||||
random:seed(A, B, C),
|
||||
net_kernel:monitor_nodes(true, [{node_type, visible}]),
|
||||
ets:new(?HASHTBL, [named_table, public, ordered_set]),
|
||||
ets:new(?HASHTBL_NEW, [named_table, public, ordered_set]),
|
||||
register_node(),
|
||||
AllNodes = mnesia:system_info(running_db_nodes),
|
||||
AllNodes = get_nodes(),
|
||||
OtherNodes = case AllNodes of
|
||||
[_] ->
|
||||
[_MyNode] ->
|
||||
AllNodes;
|
||||
_ ->
|
||||
AllNodes -- [node()]
|
||||
@ -96,35 +111,59 @@ init([]) ->
|
||||
{ok, #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] ->
|
||||
ok;
|
||||
register(?MODULE, self()),
|
||||
global:del_lock(?LOCK);
|
||||
Nodes ->
|
||||
OtherNodes = Nodes -- [node()],
|
||||
lists:foreach(
|
||||
fun(Node) ->
|
||||
{?MODULE, Node} ! {node_ready, node()}
|
||||
end, OtherNodes),
|
||||
?INFO_MSG("waiting for migration from nodes: ~w",
|
||||
[OtherNodes]),
|
||||
timer:sleep(?REHASH_TIMEOUT),
|
||||
append_node(?HASHTBL, node())
|
||||
{_Res, BadNodes} = gen_server:multi_call(
|
||||
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,
|
||||
{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) ->
|
||||
Reply = ok,
|
||||
{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) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({node_ready, Node}, State) ->
|
||||
?INFO_MSG("node ~p is ready, starting migration", [Node]),
|
||||
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),
|
||||
handle_info(del_lock, State) ->
|
||||
global:del_lock(?LOCK),
|
||||
{noreply, State};
|
||||
handle_info({node_down, Node}, State) ->
|
||||
delete_node(?HASHTBL, Node),
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% 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);
|
||||
{captcha_host, Host} ->
|
||||
add_option(captcha_host, Host, State);
|
||||
{captcha_limit, Limit} ->
|
||||
add_option(captcha_limit, Limit, State);
|
||||
{ejabberdctl_access_commands, ACs} ->
|
||||
add_option(ejabberdctl_access_commands, ACs, State);
|
||||
{loglevel, Loglevel} ->
|
||||
@ -438,6 +440,8 @@ process_term(Term, State) ->
|
||||
State;
|
||||
{max_fsm_queue, N} ->
|
||||
add_option(max_fsm_queue, N, State);
|
||||
{hostname, Host} ->
|
||||
add_option(hostname, Host, State);
|
||||
{_Opt, _Val} ->
|
||||
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
|
||||
State, State#state.hosts)
|
||||
|
@ -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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@ -34,10 +34,12 @@
|
||||
|
||||
-export([route/3,
|
||||
route_iq/4,
|
||||
route_iq/5,
|
||||
process_iq_reply/3,
|
||||
register_iq_handler/4,
|
||||
register_iq_handler/5,
|
||||
register_iq_response_handler/4,
|
||||
register_iq_response_handler/5,
|
||||
unregister_iq_handler/2,
|
||||
unregister_iq_response_handler/2,
|
||||
refresh_iq_handlers/0,
|
||||
@ -123,19 +125,31 @@ route(From, To, Packet) ->
|
||||
ok
|
||||
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 ->
|
||||
ID = ejabberd_router:make_id(),
|
||||
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});
|
||||
true ->
|
||||
jlib:iq_to_xml(IQ)
|
||||
end,
|
||||
ejabberd_router:route(From, To, Packet).
|
||||
|
||||
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) ->
|
||||
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,
|
||||
module = Module,
|
||||
function = Function,
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -9,7 +9,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@ -203,9 +203,9 @@ migrate(After) ->
|
||||
['$$']}]),
|
||||
lists:foreach(
|
||||
fun([FromTo, Pid]) ->
|
||||
case ejabberd_cluster:get_node_new(FromTo) of
|
||||
case ejabberd_cluster:get_node(FromTo) of
|
||||
Node when Node /= node() ->
|
||||
ejabberd_s2s_out:stop_connection(Pid, After * 2);
|
||||
ejabberd_s2s_out:stop_connection(Pid, After);
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% 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
|
||||
StateData#state.use_v10 ->
|
||||
{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 ->
|
||||
{next_state, wait_for_features, StateData#state{db_enabled = false}, ?FSMTIMEOUT};
|
||||
{NSProvided, DB, _} ->
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@ -37,9 +37,11 @@
|
||||
open_session/6,
|
||||
close_session/4,
|
||||
close_migrated_session/4,
|
||||
drop_session/1,
|
||||
check_in_subscription/6,
|
||||
bounce_offline_message/3,
|
||||
disconnect_removed_user/2,
|
||||
get_user_sessions/2,
|
||||
get_user_resources/2,
|
||||
set_presence/7,
|
||||
unset_presence/6,
|
||||
@ -59,6 +61,7 @@
|
||||
get_session_pid/3,
|
||||
get_user_info/3,
|
||||
get_user_ip/3,
|
||||
node_up/1,
|
||||
migrate/1
|
||||
]).
|
||||
|
||||
@ -107,27 +110,37 @@ open_session(SID, User, Server, Resource, Priority, Info) ->
|
||||
[SID, JID, Info]).
|
||||
|
||||
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),
|
||||
ejabberd_hooks:run(sm_remove_connection_hook, JID#jid.lserver,
|
||||
[SID, JID, Info]).
|
||||
|
||||
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),
|
||||
ejabberd_hooks:run(sm_remove_migrated_connection_hook, JID#jid.lserver,
|
||||
[SID, JID, Info]).
|
||||
|
||||
do_close_session(SID, User, Server, Resource) ->
|
||||
do_close_session(SID) ->
|
||||
Info = case mnesia:dirty_read({session, SID}) of
|
||||
[] -> [];
|
||||
[#session{info=I}] -> I
|
||||
end,
|
||||
drop_session(SID),
|
||||
Info.
|
||||
|
||||
drop_session(SID) ->
|
||||
F = fun() ->
|
||||
mnesia:delete({session, SID})
|
||||
end,
|
||||
mnesia:sync_dirty(F),
|
||||
Info.
|
||||
mnesia:sync_dirty(F).
|
||||
|
||||
check_in_subscription(Acc, User, Server, _JID, _Type, _Reason) ->
|
||||
case ejabberd_auth:is_user_exists(User, Server) of
|
||||
@ -148,6 +161,18 @@ disconnect_removed_user(User, Server) ->
|
||||
{xmlelement, "broadcast", [],
|
||||
[{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) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
@ -299,14 +324,33 @@ migrate(After) ->
|
||||
['$$']}]),
|
||||
lists:foreach(
|
||||
fun([US, Pid]) ->
|
||||
case ejabberd_cluster:get_node_new(US) of
|
||||
case ejabberd_cluster:get_node(US) of
|
||||
Node when Node /= node() ->
|
||||
ejabberd_c2s:migrate(Pid, Node, After);
|
||||
ejabberd_c2s:migrate(Pid, Node, random:uniform(After));
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
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
|
||||
%%====================================================================
|
||||
@ -328,6 +372,7 @@ init([]) ->
|
||||
mnesia:add_table_index(session, us),
|
||||
mnesia:add_table_copy(session, node(), ram_copies),
|
||||
ets:new(sm_iqtable, [named_table]),
|
||||
ejabberd_hooks:add(node_up, ?MODULE, node_up, 100),
|
||||
ejabberd_hooks:add(node_hash_update, ?MODULE, migrate, 100),
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
@ -339,7 +384,7 @@ init([]) ->
|
||||
ejabberd_sm, disconnect_removed_user, 100)
|
||||
end, ?MYHOSTS),
|
||||
ejabberd_commands:register_commands(commands()),
|
||||
|
||||
start_dispatchers(),
|
||||
{ok, #state{}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
@ -370,13 +415,19 @@ handle_cast(_Msg, State) ->
|
||||
%% {stop, Reason, State}
|
||||
%% Description: Handling all non call/cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_info({route, From, To, Packet}, State) ->
|
||||
handle_info({route, From, To, Packet} = Msg, State) ->
|
||||
case get_proc_num() of
|
||||
N when N > 1 ->
|
||||
#jid{luser = U, lserver = S} = To,
|
||||
get_proc_by_hash({U, S}) ! Msg;
|
||||
_ ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("~p~nwhen processing: ~p",
|
||||
[Reason, {From, To, Packet}]);
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_info({register_iq_handler, Host, XMLNS, Module, Function}, State) ->
|
||||
@ -405,8 +456,10 @@ handle_info(_Info, State) ->
|
||||
%% The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
terminate(_Reason, _State) ->
|
||||
ejabberd_hooks:delete(node_up, ?MODULE, node_up, 100),
|
||||
ejabberd_hooks:delete(node_hash_update, ?MODULE, migrate, 100),
|
||||
ejabberd_commands:unregister_commands(commands()),
|
||||
stop_dispatchers(),
|
||||
ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
@ -420,7 +473,7 @@ code_change(_OldVsn, State, _Extra) ->
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
set_session(SID, User, Server, Resource, Priority, Info) ->
|
||||
set_session({_, Pid} = SID, User, Server, Resource, Priority, Info) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
LResource = jlib:resourceprep(Resource),
|
||||
@ -433,7 +486,31 @@ set_session(SID, User, Server, Resource, Priority, Info) ->
|
||||
priority = Priority,
|
||||
info = Info})
|
||||
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),
|
||||
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
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@ -178,13 +178,6 @@ init([]) ->
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
Cluster =
|
||||
{ejabberd_cluster,
|
||||
{ejabberd_cluster, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_cluster]},
|
||||
CacheTabSupervisor =
|
||||
{cache_tab_sup,
|
||||
{cache_tab_sup, start_link, []},
|
||||
@ -194,7 +187,6 @@ init([]) ->
|
||||
[cache_tab_sup]},
|
||||
{ok, {{one_for_one, 10, 1},
|
||||
[Hooks,
|
||||
Cluster,
|
||||
SystemMonitor,
|
||||
Router,
|
||||
SM,
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -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
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
|
@ -50,12 +50,23 @@
|
||||
#
|
||||
#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
|
||||
#
|
||||
# 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
|
||||
# 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
|
||||
# not have to worry about allowing a huge number of them.
|
||||
#
|
||||
|
@ -76,10 +76,12 @@ fi
|
||||
NAME=-name
|
||||
[ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && NAME=-sname
|
||||
|
||||
if [ "$FIREWALL_WINDOW" = "" ] ; then
|
||||
KERNEL_OPTS=""
|
||||
else
|
||||
KERNEL_OPTS="-kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}"
|
||||
if [ "$FIREWALL_WINDOW" != "" ] ; then
|
||||
KERNEL_OPTS="${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
|
||||
|
||||
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS"
|
||||
@ -321,13 +323,21 @@ ctlexec ()
|
||||
{
|
||||
CONN_NAME=$1; shift
|
||||
COMMAND=$@
|
||||
$EXEC_CMD "$ERL \
|
||||
|
||||
CTLEXEC="$ERL \
|
||||
$NAME ${CONN_NAME} \
|
||||
-noinput \
|
||||
-hidden \
|
||||
-pa $EJABBERD_EBIN_PATH \
|
||||
$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
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -6,7 +6,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -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
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@ -35,6 +35,7 @@
|
||||
#define PARSE_FINAL_COMMAND 1
|
||||
|
||||
ei_x_buff event_buf;
|
||||
ei_x_buff xmlns_buf;
|
||||
|
||||
typedef struct {
|
||||
ErlDrvPort port;
|
||||
@ -43,6 +44,32 @@ typedef struct {
|
||||
|
||||
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,
|
||||
const XML_Char *name,
|
||||
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_long(&event_buf, XML_START);
|
||||
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) {}
|
||||
|
||||
@ -64,7 +94,7 @@ void *erlXML_StartElementHandler(expat_data *d,
|
||||
for (i = 0; atts[i]; i += 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]);
|
||||
}
|
||||
}
|
||||
@ -80,7 +110,7 @@ void *erlXML_EndElementHandler(expat_data *d,
|
||||
ei_x_encode_list_header(&event_buf, 1);
|
||||
ei_x_encode_tuple_header(&event_buf, 2);
|
||||
ei_x_encode_long(&event_buf, XML_END);
|
||||
ei_x_encode_string(&event_buf, name);
|
||||
encode_name(name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -95,12 +125,45 @@ void *erlXML_CharacterDataHandler(expat_data *d,
|
||||
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)
|
||||
{
|
||||
expat_data* d = (expat_data*)driver_alloc(sizeof(expat_data));
|
||||
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);
|
||||
|
||||
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
|
||||
@ -112,6 +175,11 @@ static ErlDrvData expat_erl_start(ErlDrvPort port, char *buff)
|
||||
XML_SetCharacterDataHandler(
|
||||
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;
|
||||
}
|
||||
@ -138,6 +206,7 @@ static int expat_erl_control(ErlDrvData drv_data,
|
||||
case PARSE_COMMAND:
|
||||
case PARSE_FINAL_COMMAND:
|
||||
ei_x_new_with_version(&event_buf);
|
||||
ei_x_new(&xmlns_buf);
|
||||
#ifdef ENABLE_FLASH_HACK
|
||||
/* 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);
|
||||
|
||||
ei_x_free(&event_buf);
|
||||
ei_x_free(&xmlns_buf);
|
||||
|
||||
*rbuf = (char *)b;
|
||||
return size;
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@ -45,16 +45,23 @@
|
||||
start(Host, ExtPrg) ->
|
||||
lists:foreach(
|
||||
fun(This) ->
|
||||
spawn(?MODULE, init, [get_process_name(Host, This), ExtPrg])
|
||||
start_instance(get_process_name(Host, This), ExtPrg)
|
||||
end,
|
||||
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) ->
|
||||
register(ProcessName, self()),
|
||||
process_flag(trap_exit,true),
|
||||
Port = open_port({spawn, ExtPrg}, [{packet,2}]),
|
||||
loop(Port, ?INIT_TIMEOUT).
|
||||
loop(Port, ?INIT_TIMEOUT, ProcessName, ExtPrg).
|
||||
|
||||
stop(Host) ->
|
||||
lists:foreach(
|
||||
@ -108,23 +115,27 @@ get_instances(Server) ->
|
||||
_ -> 1
|
||||
end.
|
||||
|
||||
loop(Port, Timeout) ->
|
||||
loop(Port, Timeout, ProcessName, ExtPrg) ->
|
||||
receive
|
||||
{call, Caller, Msg} ->
|
||||
Port ! {self(), {command, encode(Msg)}},
|
||||
port_command(Port, encode(Msg)),
|
||||
receive
|
||||
{Port, {data, 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} ->
|
||||
?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
|
||||
Timeout ->
|
||||
?ERROR_MSG("extauth call '~p' didn't receive response", [Msg]),
|
||||
Caller ! {eauth, false}
|
||||
end,
|
||||
loop(Port, ?CALL_TIMEOUT);
|
||||
Caller ! {eauth, false},
|
||||
Pid = restart_instance(ProcessName, ExtPrg),
|
||||
flush_buffer_and_forward_messages(Pid),
|
||||
exit(port_terminated)
|
||||
end;
|
||||
stop ->
|
||||
Port ! {self(), close},
|
||||
receive
|
||||
@ -132,10 +143,21 @@ loop(Port, Timeout) ->
|
||||
exit(normal)
|
||||
end;
|
||||
{'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)
|
||||
end.
|
||||
|
||||
flush_buffer_and_forward_messages(Pid) ->
|
||||
receive
|
||||
Message ->
|
||||
Pid ! Message,
|
||||
flush_buffer_and_forward_messages(Pid)
|
||||
after 0 ->
|
||||
true
|
||||
end.
|
||||
|
||||
join(List, Sep) ->
|
||||
lists:foldl(fun(A, "") -> A;
|
||||
(A, Acc) -> Acc ++ Sep ++ A
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@ -70,11 +70,24 @@ start_module(Host, Module, Opts) ->
|
||||
catch Class:Reason ->
|
||||
del_module_mnesia(Host, Module),
|
||||
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]),
|
||||
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.
|
||||
|
||||
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.
|
||||
stop_module(Host, Module) ->
|
||||
case stop_module_keep_config(Host, Module) of
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
115
src/jlib.hrl
115
src/jlib.hrl
@ -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
|
||||
%%% 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_VER, "urn:xmpp:features:rosterver").
|
||||
-define(NS_PRIVACY, "jabber:iq:privacy").
|
||||
-define(NS_BLOCKING, "urn:xmpp:blocking").
|
||||
-define(NS_PRIVATE, "jabber:iq:private").
|
||||
-define(NS_VERSION, "jabber:iq:version").
|
||||
-define(NS_TIME90, "jabber:iq:time"). % TODO: Remove once XEP-0090 is Obsolete
|
||||
@ -206,117 +207,117 @@
|
||||
?ERRT_CONFLICT(Lang, "Resource conflict")).
|
||||
|
||||
|
||||
-define(STREAM_ERROR(Condition),
|
||||
-define(STREAM_ERROR(Condition, Cdata),
|
||||
{xmlelement, "stream:error",
|
||||
[],
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []}]}).
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}],
|
||||
[{xmlcdata, Cdata}]}]}).
|
||||
|
||||
-define(SERR_BAD_FORMAT,
|
||||
?STREAM_ERROR("bad-format")).
|
||||
?STREAM_ERROR("bad-format", "")).
|
||||
-define(SERR_BAD_NAMESPACE_PREFIX,
|
||||
?STREAM_ERROR("bad-namespace-prefix")).
|
||||
?STREAM_ERROR("bad-namespace-prefix", "")).
|
||||
-define(SERR_CONFLICT,
|
||||
?STREAM_ERROR("conflict")).
|
||||
?STREAM_ERROR("conflict", "")).
|
||||
-define(SERR_CONNECTION_TIMEOUT,
|
||||
?STREAM_ERROR("connection-timeout")).
|
||||
?STREAM_ERROR("connection-timeout", "")).
|
||||
-define(SERR_HOST_GONE,
|
||||
?STREAM_ERROR("host-gone")).
|
||||
?STREAM_ERROR("host-gone", "")).
|
||||
-define(SERR_HOST_UNKNOWN,
|
||||
?STREAM_ERROR("host-unknown")).
|
||||
?STREAM_ERROR("host-unknown", "")).
|
||||
-define(SERR_IMPROPER_ADDRESSING,
|
||||
?STREAM_ERROR("improper-addressing")).
|
||||
?STREAM_ERROR("improper-addressing", "")).
|
||||
-define(SERR_INTERNAL_SERVER_ERROR,
|
||||
?STREAM_ERROR("internal-server-error")).
|
||||
?STREAM_ERROR("internal-server-error", "")).
|
||||
-define(SERR_INVALID_FROM,
|
||||
?STREAM_ERROR("invalid-from")).
|
||||
?STREAM_ERROR("invalid-from", "")).
|
||||
-define(SERR_INVALID_ID,
|
||||
?STREAM_ERROR("invalid-id")).
|
||||
?STREAM_ERROR("invalid-id", "")).
|
||||
-define(SERR_INVALID_NAMESPACE,
|
||||
?STREAM_ERROR("invalid-namespace")).
|
||||
?STREAM_ERROR("invalid-namespace", "")).
|
||||
-define(SERR_INVALID_XML,
|
||||
?STREAM_ERROR("invalid-xml")).
|
||||
?STREAM_ERROR("invalid-xml", "")).
|
||||
-define(SERR_NOT_AUTHORIZED,
|
||||
?STREAM_ERROR("not-authorized")).
|
||||
?STREAM_ERROR("not-authorized", "")).
|
||||
-define(SERR_POLICY_VIOLATION,
|
||||
?STREAM_ERROR("policy-violation")).
|
||||
?STREAM_ERROR("policy-violation", "")).
|
||||
-define(SERR_REMOTE_CONNECTION_FAILED,
|
||||
?STREAM_ERROR("remote-connection-failed")).
|
||||
?STREAM_ERROR("remote-connection-failed", "")).
|
||||
-define(SERR_RESOURSE_CONSTRAINT,
|
||||
?STREAM_ERROR("resource-constraint")).
|
||||
?STREAM_ERROR("resource-constraint", "")).
|
||||
-define(SERR_RESTRICTED_XML,
|
||||
?STREAM_ERROR("restricted-xml")).
|
||||
% TODO: include hostname or IP
|
||||
-define(SERR_SEE_OTHER_HOST,
|
||||
?STREAM_ERROR("see-other-host")).
|
||||
?STREAM_ERROR("restricted-xml", "")).
|
||||
-define(SERR_SEE_OTHER_HOST(Host),
|
||||
?STREAM_ERROR("see-other-host", Host)).
|
||||
-define(SERR_SYSTEM_SHUTDOWN,
|
||||
?STREAM_ERROR("system-shutdown")).
|
||||
?STREAM_ERROR("system-shutdown", "")).
|
||||
-define(SERR_UNSUPPORTED_ENCODING,
|
||||
?STREAM_ERROR("unsupported-encoding")).
|
||||
?STREAM_ERROR("unsupported-encoding", "")).
|
||||
-define(SERR_UNSUPPORTED_STANZA_TYPE,
|
||||
?STREAM_ERROR("unsupported-stanza-type")).
|
||||
?STREAM_ERROR("unsupported-stanza-type", "")).
|
||||
-define(SERR_UNSUPPORTED_VERSION,
|
||||
?STREAM_ERROR("unsupported-version")).
|
||||
?STREAM_ERROR("unsupported-version", "")).
|
||||
-define(SERR_XML_NOT_WELL_FORMED,
|
||||
?STREAM_ERROR("xml-not-well-formed")).
|
||||
?STREAM_ERROR("xml-not-well-formed", "")).
|
||||
%-define(SERR_,
|
||||
% ?STREAM_ERROR("")).
|
||||
% ?STREAM_ERROR("", "")).
|
||||
|
||||
-define(STREAM_ERRORT(Condition, Lang, Text),
|
||||
-define(STREAM_ERRORT(Condition, Cdata, Lang, Text),
|
||||
{xmlelement, "stream:error",
|
||||
[],
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []},
|
||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}],
|
||||
[{xmlcdata, Cdata}]},
|
||||
{xmlelement, "text", [{"xml:lang", Lang}, {"xmlns", ?NS_STREAMS}],
|
||||
[{xmlcdata, translate:translate(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),
|
||||
?STREAM_ERRORT("bad-namespace-prefix", Lang, Text)).
|
||||
?STREAM_ERRORT("bad-namespace-prefix", "", Lang, Text)).
|
||||
-define(SERRT_CONFLICT(Lang, Text),
|
||||
?STREAM_ERRORT("conflict", Lang, Text)).
|
||||
?STREAM_ERRORT("conflict", "", 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),
|
||||
?STREAM_ERRORT("host-gone", Lang, Text)).
|
||||
?STREAM_ERRORT("host-gone", "", 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),
|
||||
?STREAM_ERRORT("improper-addressing", Lang, Text)).
|
||||
?STREAM_ERRORT("improper-addressing", "", 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),
|
||||
?STREAM_ERRORT("invalid-from", Lang, Text)).
|
||||
?STREAM_ERRORT("invalid-from", "", 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),
|
||||
?STREAM_ERRORT("invalid-namespace", Lang, Text)).
|
||||
?STREAM_ERRORT("invalid-namespace", "", 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),
|
||||
?STREAM_ERRORT("not-authorized", Lang, Text)).
|
||||
?STREAM_ERRORT("not-authorized", "", 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),
|
||||
?STREAM_ERRORT("remote-connection-failed", Lang, Text)).
|
||||
?STREAM_ERRORT("remote-connection-failed", "", 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),
|
||||
?STREAM_ERRORT("restricted-xml", Lang, Text)).
|
||||
% TODO: include hostname or IP
|
||||
-define(SERRT_SEE_OTHER_HOST(Lang, Text),
|
||||
?STREAM_ERRORT("see-other-host", Lang, Text)).
|
||||
?STREAM_ERRORT("restricted-xml", "", Lang, Text)).
|
||||
-define(SERRT_SEE_OTHER_HOST(Host, Lang, Text),
|
||||
?STREAM_ERRORT("see-other-host", Host, 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),
|
||||
?STREAM_ERRORT("unsupported-encoding", Lang, Text)).
|
||||
?STREAM_ERRORT("unsupported-encoding", "", 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),
|
||||
?STREAM_ERRORT("unsupported-version", Lang, Text)).
|
||||
?STREAM_ERRORT("unsupported-version", "", 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),
|
||||
% ?STREAM_ERRORT("", Lang, Text)).
|
||||
% ?STREAM_ERRORT("", "", Lang, Text)).
|
||||
|
||||
|
||||
-record(jid, {user, server, resource,
|
||||
|
420
src/mod_ack.erl
Normal file
420
src/mod_ack.erl
Normal 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.
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
333
src/mod_blocking.erl
Normal file
333
src/mod_blocking.erl
Normal 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.
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -7,7 +7,7 @@
|
||||
%%% {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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -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
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@ -425,7 +425,7 @@ iq_get_vcard(Lang) ->
|
||||
[{xmlcdata, ?EJABBERD_URI}]},
|
||||
{xmlelement, "DESC", [],
|
||||
[{xmlcdata, translate:translate(Lang, "ejabberd IRC module") ++
|
||||
"\nCopyright (c) 2003-2010 Alexey Shchepin"}]}].
|
||||
"\nCopyright (c) 2003-2011 ProcessOne"}]}].
|
||||
|
||||
command_items(Host, Lang) ->
|
||||
lists:map(fun({Node, Name, _Function})
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@ -219,6 +219,7 @@ handle_info({route_chan, Channel, Resource,
|
||||
NewStateData =
|
||||
case xml:get_attr_s("type", Attrs) of
|
||||
"unavailable" ->
|
||||
send_stanza_unavailable(Channel, StateData),
|
||||
S1 = ?SEND(io_lib:format("PART #~s\r\n", [Channel])),
|
||||
S1#state{channels =
|
||||
dict:erase(Channel, S1#state.channels)};
|
||||
@ -656,13 +657,9 @@ terminate(_Reason, _StateName, FullStateData) ->
|
||||
bounce_messages("Server Connect Failed"),
|
||||
lists:foreach(
|
||||
fun(Chan) ->
|
||||
ejabberd_router:route(
|
||||
jlib:make_jid(
|
||||
lists:concat([Chan, "%", StateData#state.server]),
|
||||
StateData#state.host, StateData#state.nick),
|
||||
StateData#state.user,
|
||||
{xmlelement, "presence", [{"type", "error"}],
|
||||
[Error]})
|
||||
Stanza = {xmlelement, "presence", [{"type", "error"}],
|
||||
[Error]},
|
||||
send_stanza(Chan, StateData, Stanza)
|
||||
end, dict:fetch_keys(StateData#state.channels)),
|
||||
case StateData#state.socket of
|
||||
undefined ->
|
||||
@ -672,6 +669,30 @@ terminate(_Reason, _StateName, FullStateData) ->
|
||||
end,
|
||||
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
|
||||
%%%----------------------------------------------------------------------
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% 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,
|
||||
{none, []}, [User, Server, From]),
|
||||
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(
|
||||
privacy_get_user_list, Server,
|
||||
#userlist{},
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@ -42,6 +42,7 @@
|
||||
process_iq_disco_items/4,
|
||||
broadcast_service_message/2,
|
||||
register_room/3,
|
||||
node_up/1,
|
||||
migrate/1,
|
||||
get_vh_rooms/1,
|
||||
can_use_nick/3]).
|
||||
@ -175,14 +176,33 @@ migrate(After) ->
|
||||
['$$']}]),
|
||||
lists:foreach(
|
||||
fun([NameHost, Pid]) ->
|
||||
case ejabberd_cluster:get_node_new(NameHost) of
|
||||
case ejabberd_cluster:get_node(NameHost) of
|
||||
Node when Node /= node() ->
|
||||
mod_muc_room:migrate(Pid, Node, After);
|
||||
mod_muc_room:migrate(Pid, Node, random:uniform(After));
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
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
|
||||
%%====================================================================
|
||||
@ -219,6 +239,7 @@ init([Host, Opts]) ->
|
||||
DefRoomOpts = gen_mod:get_opt(default_room_options, Opts, []),
|
||||
RoomShaper = gen_mod:get_opt(room_shaper, Opts, none),
|
||||
ejabberd_router:register_route(MyHost),
|
||||
ejabberd_hooks:add(node_up, ?MODULE, node_up, 100),
|
||||
ejabberd_hooks:add(node_hash_update, ?MODULE, migrate, 100),
|
||||
load_permanent_rooms(MyHost, Host,
|
||||
{Access, AccessCreate, AccessAdmin, AccessPersistent},
|
||||
@ -306,7 +327,15 @@ handle_info({room_destroyed, RoomHost, Pid}, State) ->
|
||||
mnesia:delete_object(#muc_online_room{name_host = RoomHost,
|
||||
pid = Pid})
|
||||
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};
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
@ -319,6 +348,7 @@ handle_info(_Info, State) ->
|
||||
%% The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
terminate(_Reason, State) ->
|
||||
ejabberd_hooks:delete(node_up, ?MODULE, node_up, 100),
|
||||
ejabberd_hooks:delete(node_hash_update, ?MODULE, migrate, 100),
|
||||
ejabberd_router:unregister_route(State#state.host),
|
||||
ok.
|
||||
@ -507,14 +537,20 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
|
||||
AccessCreate, From,
|
||||
Room) of
|
||||
true ->
|
||||
{ok, Pid} = start_new_room(
|
||||
case start_new_room(
|
||||
Host, ServerHost, Access,
|
||||
Room, HistorySize,
|
||||
RoomShaper, From,
|
||||
Nick, DefRoomOpts),
|
||||
register_room(Host, Room, Pid),
|
||||
Nick, DefRoomOpts) of
|
||||
{ok, Pid} ->
|
||||
mod_muc_room:route(Pid, From, Nick, Packet),
|
||||
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 ->
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
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},
|
||||
pid = Pid})
|
||||
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) ->
|
||||
[{xmlelement, "identity",
|
||||
@ -847,7 +904,7 @@ iq_get_vcard(Lang) ->
|
||||
[{xmlcdata, ?EJABBERD_URI}]},
|
||||
{xmlelement, "DESC", [],
|
||||
[{xmlcdata, translate:translate(Lang, "ejabberd MUC module") ++
|
||||
"\nCopyright (c) 2003-2010 Alexey Shchepin"}]}].
|
||||
"\nCopyright (c) 2003-2011 ProcessOne"}]}].
|
||||
|
||||
|
||||
broadcast_service_message(Host, Msg) ->
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% 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).
|
||||
|
||||
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
|
||||
@ -597,9 +597,6 @@ handle_event(destroy, StateName, StateData) ->
|
||||
handle_event({set_affiliations, Affiliations}, StateName, StateData) ->
|
||||
{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) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
@ -716,6 +713,13 @@ handle_info({captcha_failed, From}, normal_state, StateData) ->
|
||||
StateData
|
||||
end,
|
||||
{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) ->
|
||||
{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
|
||||
%% (e.g., a room owner, room admin, or service-level admin)
|
||||
%% 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) ->
|
||||
case get_service_affiliation(JID, StateData) of
|
||||
owner ->
|
||||
@ -1665,15 +1666,25 @@ add_new_user(From, Nick, {xmlelement, _, Attrs, Els} = Packet, StateData) ->
|
||||
SID = xml:get_attr_s("id", Attrs),
|
||||
RoomJID = StateData#state.jid,
|
||||
To = jlib:jid_replace_resource(RoomJID, Nick),
|
||||
Limiter = {From#jid.luser, From#jid.lserver},
|
||||
case ejabberd_captcha:create_captcha(
|
||||
SID, RoomJID, To, Lang, From) of
|
||||
SID, RoomJID, To, Lang, Limiter, From) of
|
||||
{ok, ID, CaptchaEls} ->
|
||||
MsgPkt = {xmlelement, "message", [{"id", ID}], CaptchaEls},
|
||||
Robots = ?DICT:store(From,
|
||||
{Nick, Packet}, StateData#state.robots),
|
||||
ejabberd_router:route(RoomJID, From, MsgPkt),
|
||||
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",
|
||||
Err = jlib:make_error_reply(
|
||||
Packet, ?ERRT_INTERNAL_SERVER_ERROR(Lang, ErrText)),
|
||||
@ -1725,7 +1736,24 @@ check_captcha(Affiliation, From, StateData) ->
|
||||
{ok, passed} ->
|
||||
true;
|
||||
_ ->
|
||||
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;
|
||||
_ ->
|
||||
true
|
||||
@ -2525,6 +2553,11 @@ can_change_ra(_FAffiliation, _FRole,
|
||||
%% A room owner tries to add as persistent owner a
|
||||
%% participant that is already owner because he is MUC admin
|
||||
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,
|
||||
TAffiliation, _TRole,
|
||||
affiliation, Value, _ServiceAf)
|
||||
@ -2699,14 +2732,21 @@ send_kickban_presence(JID, Reason, Code, NewAffiliation, StateData) ->
|
||||
end, LJIDs).
|
||||
|
||||
send_kickban_presence1(UJID, Reason, Code, Affiliation, StateData) ->
|
||||
{ok, #user{jid = _RealJID,
|
||||
{ok, #user{jid = RealJID,
|
||||
nick = Nick}} =
|
||||
?DICT:find(jlib:jid_tolower(UJID), StateData#state.users),
|
||||
SAffiliation = affiliation_to_list(Affiliation),
|
||||
BannedJIDString = jlib:jid_to_string(RealJID),
|
||||
lists:foreach(
|
||||
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},
|
||||
{"role", "none"}],
|
||||
{"role", "none"}] ++ JidAttrList,
|
||||
ItemEls = case Reason of
|
||||
"" ->
|
||||
[];
|
||||
@ -2907,6 +2947,13 @@ is_password_settings_correct(XEl, StateData) ->
|
||||
-define(PRIVATEXFIELD(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) ->
|
||||
DefRoomOpts = gen_mod:get_module_opt(RoomState#state.server_host, mod_muc, default_room_options, []),
|
||||
RoomState2 = set_opts(DefRoomOpts, RoomState),
|
||||
@ -3027,6 +3074,9 @@ get_config(Lang, StateData, From) ->
|
||||
Config#config.captcha_protected)];
|
||||
false -> []
|
||||
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(
|
||||
StateData#state.server_host, From) of
|
||||
allow ->
|
||||
@ -3096,6 +3146,18 @@ set_config(XEl, StateData) ->
|
||||
-define(SET_STRING_XOPT(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) ->
|
||||
Config;
|
||||
@ -3153,6 +3215,9 @@ set_xoption([{"muc#roomconfig_maxusers", [Val]} | Opts], Config) ->
|
||||
end;
|
||||
set_xoption([{"muc#roomconfig_enablelogging", [Val]} | Opts], Config) ->
|
||||
?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) ->
|
||||
%% Ignore our FORM_TYPE
|
||||
set_xoption(Opts, Config);
|
||||
@ -3222,6 +3287,7 @@ set_opts([{Opt, Val} | Opts], StateData) ->
|
||||
password -> StateData#state{config = (StateData#state.config)#config{password = Val}};
|
||||
anonymous -> StateData#state{config = (StateData#state.config)#config{anonymous = 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 ->
|
||||
ServiceMaxUsers = get_service_max_users(StateData),
|
||||
MaxUsers = if
|
||||
@ -3266,6 +3332,8 @@ make_opts(StateData) ->
|
||||
?MAKE_CONFIG_OPT(anonymous),
|
||||
?MAKE_CONFIG_OPT(logging),
|
||||
?MAKE_CONFIG_OPT(max_users),
|
||||
{captcha_whitelist,
|
||||
?SETS:to_list((StateData#state.config)#config.captcha_whitelist)},
|
||||
{affiliations, ?DICT:to_list(StateData#state.affiliations)},
|
||||
{subject, StateData#state.subject},
|
||||
{subject_author, StateData#state.subject_author}
|
||||
|
@ -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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@ -45,7 +45,8 @@
|
||||
password = "",
|
||||
anonymous = true,
|
||||
max_users = ?MAX_USERS_DEFAULT,
|
||||
logging = false
|
||||
logging = false,
|
||||
captcha_whitelist = ?SETS:empty()
|
||||
}).
|
||||
|
||||
-record(user, {jid,
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
@ -5,7 +5,7 @@
|
||||
%%% 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
|
||||
%%% 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),
|
||||
PingInterval = gen_mod:get_opt(ping_interval, Opts, ?DEFAULT_PING_INTERVAL),
|
||||
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),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PING,
|
||||
?MODULE, iq_ping, IQDisc),
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user