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

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

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

2
README
View File

@ -20,7 +20,7 @@ To compile ejabberd you need:
- GNU Iconv 1.8 or higher, for the IRC Transport
(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

View File

@ -356,7 +356,7 @@ Don&#X2019;t use R14A or R14B because <A HREF="http://www.erlang.org/cgi-bin/ezm
</LI><LI CLASS="li-itemize">PAM library. Optional. For Pluggable Authentication Modules (PAM). See section <A HREF="#pam">3.1.4</A>.
</LI><LI CLASS="li-itemize">GNU Iconv 1.8 or higher, for the IRC Transport (mod_irc). Optional. Not needed on systems with GNU Libc. See section <A HREF="#modirc">3.3.8</A>.
</LI><LI CLASS="li-itemize">ImageMagick&#X2019;s Convert program. Optional. For CAPTCHA challenges. See section <A HREF="#captcha">3.1.8</A>.
</LI><LI CLASS="li-itemize">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>&#XA0;&#XA0;<A HREF="#download">Download Source Code</A></H3><!--SEC END --><P> <A NAME="download"></A>
</P><P>Released versions of <TT>ejabberd</TT> are available in the ProcessOne <TT>ejabberd</TT> downloads page:
@ -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>

View File

@ -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,15 +2636,16 @@ 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
of IQ queries of a namespace with this discipline. In addition, the processing
of this queue is done in parallel with that of other packets. This discipline
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
\titem{\{queues, N\}} N separate queues are created to process the
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}

View File

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

View File

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

View File

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

View File

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

View File

@ -6,7 +6,7 @@
%%% Created : 29 Aug 2010 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
%%% 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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, [

View File

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

View File

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

View File

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

View File

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

View File

@ -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()]),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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},
{ok, wait_for_stream, StateData, ?C2S_OPEN_TIMEOUT}
case get_jid_from_opts(Opts) of
{ok, #jid{user = U, server = Server, resource = R} = JID} ->
?GEN_FSM:send_event(self(), open_session),
{ok, wait_for_session, StateData#state{
user = U,
server = Server,
resource = R,
jid = JID,
lang = ""}};
_ ->
{ok, wait_for_stream, StateData, ?C2S_OPEN_TIMEOUT}
end
end;
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,45 +603,55 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
"(~w) Accepted legacy authentication for ~s by ~p",
[StateData#state.socket,
jlib:jid_to_string(JID), AuthModule]),
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,
StateData#state.server,
{[], []},
[U, StateData#state.server]),
LJID = jlib:jid_tolower(
jlib:jid_remove_resource(JID)),
Fs1 = [LJID | Fs],
Ts1 = [LJID | Ts],
PrivList =
ejabberd_hooks:run_fold(
privacy_get_user_list, StateData#state.server,
#userlist{},
[U, StateData#state.server]),
NewStateData = StateData#state{
user = U,
resource = R,
jid = JID,
sid = SID,
conn = Conn,
auth_module = AuthModule,
pres_f = ?SETS:from_list(Fs1),
pres_t = ?SETS:from_list(Ts1),
privacy_list = PrivList},
DebugFlag = ejabberd_hooks:run_fold(c2s_debug_start_hook,
NewStateData#state.server,
false,
[self(), NewStateData]),
maybe_migrate(session_established, NewStateData#state{debug=DebugFlag});
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),
Res1 = jlib:make_result_iq_reply(El),
Res = setelement(4, Res1, []),
send_element(StateData, Res),
change_shaper(StateData, JID),
{Fs, Ts} = ejabberd_hooks:run_fold(
roster_get_subscription_lists,
StateData#state.server,
{[], []},
[U, StateData#state.server]),
LJID = jlib:jid_tolower(
jlib:jid_remove_resource(JID)),
Fs1 = [LJID | Fs],
Ts1 = [LJID | Ts],
PrivList =
ejabberd_hooks:run_fold(
privacy_get_user_list,
StateData#state.server,
#userlist{},
[U, StateData#state.server]),
NewStateData =
StateData#state{
user = U,
resource = R,
jid = JID,
sid = SID,
conn = Conn,
auth_module = AuthModule,
pres_f = ?SETS:from_list(Fs1),
pres_t = ?SETS:from_list(Ts1),
privacy_list = PrivList},
DebugFlag = ejabberd_hooks:run_fold(
c2s_debug_start_hook,
NewStateData#state.server,
false,
[self(), NewStateData]),
maybe_migrate(session_established,
NewStateData#state{debug=DebugFlag})
end;
_ ->
?INFO_MSG(
"(~w) Failed legacy authentication for ~s",
@ -711,21 +743,30 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
Mech,
ClientIn) of
{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]),
fsm_next_state(wait_for_stream,
StateData#state{
streamid = new_id(),
authenticated = true,
auth_module = AuthModule,
user = U });
catch (StateData#state.sockmod):reset_stream(
StateData#state.socket),
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 })
end;
{continue, ServerOut, NewSASLState} ->
send_element(StateData,
{xmlelement, "challenge",
@ -865,20 +906,29 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
ClientIn) of
{ok, Props} ->
catch (StateData#state.sockmod):reset_stream(
StateData#state.socket),
send_element(StateData,
{xmlelement, "success",
[{"xmlns", ?NS_SASL}], []}),
StateData#state.socket),
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]),
fsm_next_state(wait_for_stream,
StateData#state{
streamid = new_id(),
authenticated = true,
auth_module = AuthModule,
user = U});
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})
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),
Presence = StateData#state.pres_last,
Priority =
case Presence of
undefined ->
undefined;
_ ->
get_priority_from_presence(Presence)
end,
ejabberd_sm:open_session(SID, U, S, R, Priority, Info),
StateData2 = change_reception(PackedStateData, true),
StateData3 = start_keepalive_timer(StateData2),
erlang:garbage_collect(),
case ejabberd_cluster:get_node_new({U, S}) of
Node ->
ok;
NewNode ->
After = ejabberd_cluster:rehash_timeout(),
migrate(self(), NewNode, After)
end,
fsm_next_state(StateName, PackedStateData);
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.

View File

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

View File

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

View File

@ -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)),
@ -96,18 +97,18 @@ create_captcha(SID, From, To, Lang, Args)
OOB = {xmlelement, "x", [{"xmlns", ?NS_OOB}],
[{xmlelement, "url", [], [{xmlcdata, get_url(Id)}]}]},
Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE, {remove_id, Id}),
ets:insert(captcha, #captcha{id=Id, pid=self(), key=Key,
ets:insert(captcha, #captcha{id=Id, pid=self(), key=Key,
tref=Tref, args=Args}),
{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 ->
?CRITICAL_MSG("Captcha is enabled in the option captcha_cmd, "
"but it can't generate images.", []);
case create_image() of
{ok, _, _, _} ->
ok;
_Err ->
?CRITICAL_MSG("Captcha is enabled in the option captcha_cmd, "
"but it can't generate images.", []),
throw({error, captcha_cmd_enabled_but_fails})
end;
false ->
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).

View File

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

View File

@ -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),

View File

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

View File

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

View File

@ -5,7 +5,7 @@
%%% Created : 14 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% 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)

View File

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

View File

@ -5,7 +5,7 @@
%%% Created : 11 Jan 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% 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

View File

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

View File

@ -5,7 +5,7 @@
%%% Created : 23 Aug 2006 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% 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

View File

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

View File

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

View File

@ -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,23 +125,35 @@ 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,
timer = TRef}).
module = Module,
function = Function,
timer = TRef}).
register_iq_handler(Host, XMLNS, Module, Fun) ->
ejabberd_local ! {register_iq_handler, Host, XMLNS, Module, Fun}.

View File

@ -5,7 +5,7 @@
%%% Created : 23 Oct 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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, _} ->

View File

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

View File

@ -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,
[] -> [];
[#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) ->
case catch do_route(From, To, Packet) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, To, Packet}]);
_ ->
ok
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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#*-}"
KERNEL_OPTS=""
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

View File

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

View File

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

View File

@ -6,7 +6,7 @@
%%% Author: Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
%%% 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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
/*
* ejabberd, Copyright (C) 2002-2010 ProcessOne
* ejabberd, Copyright (C) 2002-2011 ProcessOne
*
* This program is free software; you can redistribute it and/or
* 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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
%%%----------------------------------------------------------------------
%%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% 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
View File

@ -0,0 +1,420 @@
%%%-------------------------------------------------------------------
%%% File : mod_ack.erl
%%% Author : Mickael Remond <mremond@process-one.net>
%%% Description : Implements reliable message delivery
%%% Note: this module depends on mod_caps
%%% Created : 12 Mar 2010 by Mickael Remond <mremond@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
%%%-------------------------------------------------------------------
-module(mod_ack).
-behaviour(gen_server).
-behaviour(gen_mod).
%% API
-export([start/2, stop/1, start_link/2]).
-export([user_send_packet/3,
offline_message/3,
delayed_message/3,
remove_connection/3,
feature_inspect_packet/4]).
%% gen_server callbacks
-export([init/1,
handle_info/2,
handle_call/3,
handle_cast/2,
terminate/2,
code_change/3]).
-include("jlib.hrl").
-include("ejabberd.hrl").
-define(PROCNAME, ejabberd_mod_ack).
-define(ACK_TIMEOUT, 60). %% seconds
-define(DICT, dict).
-ifndef(NS_RECEIPTS).
-define(NS_RECEIPTS, "urn:xmpp:receipts").
-endif.
-ifndef(NS_PING).
-define(NS_PING, "urn:xmpp:ping").
-endif.
-ifndef(NS_P1_PUSHED).
-define(NS_P1_PUSHED, "p1:pushed").
-endif.
-record(state, {host, timers = ?DICT:new(), timeout}).
%%====================================================================
%% API
%%====================================================================
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec =
{Proc,
{?MODULE, start_link, [Host, Opts]},
transient,
1000,
worker,
[?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:call(Proc, stop),
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
%% TODO: Make ack on server receive optional ?
user_send_packet(From, To, {xmlelement, "message", _Attrs, _Els} = Packet) ->
case has_receipt_request(Packet) of
{true, _} ->
process_ack_request("on-sender-server", From, To, Packet);
false ->
case has_receipt_response(Packet) of
{true, ID} ->
Server = From#jid.lserver,
del_timer(Server, {message, ID}, From);
false ->
do_nothing
end
end;
user_send_packet(From, _To, {xmlelement, "iq", Attrs, _Els}) ->
case xml:get_attr_s("id", Attrs) of
"" ->
ok;
ID ->
Server = From#jid.lserver,
del_timer(Server, {iq, ID}, From)
end;
user_send_packet(_From, _To, _Packet) ->
do_nothing.
offline_message(From, To, Packet) ->
process_ack_request("offline", From, To, Packet),
ok.
delayed_message(From, To, Packet) ->
process_ack_request("delayed", From, To, Packet),
ok.
feature_inspect_packet(JID, Server,
{xmlelement, "presence", _, _} = Pres,
{xmlelement, "message", Attrs, _} = El) ->
HasReceipts = has_receipt_request(El),
ReceiptsSupported = are_receipts_supported(Pres),
?DEBUG("feature_inspect_packet:~n"
"** JID: ~p~n"
"** Has receipts: ~p~n"
"** Receipts supported: ~p~n"
"** Pres: ~p~n"
"** El: ~p",
[JID, HasReceipts, ReceiptsSupported, Pres, El]),
Type = xml:get_attr_s("type", Attrs),
case HasReceipts of
_ when Type == "error" ->
ok;
{true, ID} ->
case {jlib:string_to_jid(xml:get_attr_s("from", Attrs)),
jlib:string_to_jid(xml:get_attr_s("to", Attrs))} of
{#jid{} = From, #jid{} = To} ->
Pkt = {From, To, El},
case ReceiptsSupported of
true ->
add_timer(Server, {message, ID}, JID, Pkt);
false ->
ping(From, To, Server, JID, El);
unknown ->
process_ack_request("unreliable", From, To, El)
end;
_ ->
?WARNING_MSG("message doesn't have 'from' or 'to'"
" attribute:~n** El: ~p", [El])
end;
_ ->
ok
end;
feature_inspect_packet(_User, _Server, _Pres, _El) ->
ok.
remove_connection({_, C2SPid}, #jid{lserver = Host}, _Info) ->
gen_server:cast(gen_mod:get_module_proc(Host, ?PROCNAME), {del, C2SPid}).
%%====================================================================
%% gen_server callbacks
%%====================================================================
init([Host, Opts]) ->
Timeout = timer:seconds(gen_mod:get_opt(timeout, Opts, ?ACK_TIMEOUT)),
ejabberd_hooks:add(user_send_packet, Host,
?MODULE, user_send_packet, 20),
ejabberd_hooks:add(offline_message_hook, Host,
?MODULE, offline_message, 20),
ejabberd_hooks:add(delayed_message_hook, Host,
?MODULE, delayed_message, 20),
ejabberd_hooks:add(feature_inspect_packet, Host,
?MODULE, feature_inspect_packet, 150),
ejabberd_hooks:add(sm_remove_connection_hook, Host,
?MODULE, remove_connection, 20),
ejabberd_hooks:add(sm_remove_migrated_connection_hook, Host,
?MODULE, remove_connection, 20),
{ok, #state{host = Host, timeout = Timeout}}.
handle_call(stop, _From, State) ->
{stop, normal, ok, State};
handle_call(_Req, _From, State) ->
{reply, {error, badarg}, State}.
handle_cast({add, ID, Pid, Packet}, State) ->
TRef = erlang:start_timer(State#state.timeout, self(), {ID, Pid}),
Timers = insert(Pid, ID, {TRef, Packet}, State#state.timers),
{noreply, State#state{timers = Timers}};
handle_cast({del, ID, Pid}, State) ->
case lookup(Pid, ID, State#state.timers) of
{ok, {TRef, {From, To, {xmlelement, _, Attrs, _}}}} ->
cancel_timer(TRef),
Timers = delete(Pid, ID, State#state.timers),
case ID of
{iq, _} ->
MsgID = xml:get_attr_s("id", Attrs),
Message = {xmlelement, "message", [{"id", MsgID}],
[{xmlelement, "received",
[{"xmlns", ?NS_RECEIPTS}, {"id", MsgID}], []}]},
ejabberd_router:route(To, From, Message);
_ ->
ok
end,
{noreply, State#state{timers = Timers}};
error ->
{noreply, State}
end;
handle_cast({del, Pid}, State) ->
lists:foreach(
fun({_, _, {TRef, {From, To, El}}}) ->
cancel_timer(TRef),
El1 = xml:remove_subtags(El, "x", {"xmlns", ?NS_P1_PUSHED}),
El2 = xml:append_subtags(
El1, [{xmlelement, "x", [{"xmlns", ?NS_P1_PUSHED}], []}]),
?DEBUG("Resending message:~n"
"** From: ~p~n"
"** To: ~p~n"
"** El: ~p",
[From, To, El2]),
ejabberd_router:route(From, To, El2)
end, to_list(Pid, State#state.timers)),
Timers = delete(Pid, State#state.timers),
{noreply, State#state{timers = Timers}};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({timeout, _TRef, {ID, Pid}}, State) ->
case lookup(Pid, ID, State#state.timers) of
{ok, _} ->
MRef = erlang:monitor(process, Pid),
catch ejabberd_c2s:stop(Pid),
receive
{'DOWN', MRef, process, Pid, _Reason}->
ok
after 5 ->
catch exit(Pid, kill)
end,
erlang:demonitor(MRef, [flush]),
handle_cast({del, Pid}, State);
error ->
{noreply, State}
end;
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, State) ->
Host = State#state.host,
ejabberd_hooks:delete(user_send_packet, Host,
?MODULE, user_send_packet, 20),
ejabberd_hooks:delete(offline_message_hook, Host,
?MODULE, offline_message, 20),
ejabberd_hooks:delete(delayed_message_hook, Host,
?MODULE, delayed_message, 20),
ejabberd_hooks:delete(feature_inspect_packet, Host,
?MODULE, feature_inspect_packet, 150),
ejabberd_hooks:delete(sm_remove_connection_hook, Host,
?MODULE, remove_connection, 20),
ejabberd_hooks:delete(sm_remove_migrated_connection_hook, Host,
?MODULE, remove_connection, 20),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%====================================================================
%% Internal functions
%%====================================================================
process_ack_request(AckTagName,
#jid{lserver=LServer} = From, To,
{xmlelement, "message", _Attrs, _Els} = Packet) ->
case has_receipt_request(Packet) of
{true, ID} ->
BareTo = jlib:jid_remove_resource(To),
Message = {xmlelement, "message", [{"id", ID}],
[{xmlelement, AckTagName,
[{"xmlns", ?NS_RECEIPTS},
{"server", LServer}, {"id", ID}], []}]},
ejabberd_router:route(BareTo, From, Message);
false ->
do_nothing
end.
has_receipt_request(Packet) ->
has_receipt(Packet, "request").
has_receipt_response(Packet) ->
has_receipt(Packet, "received").
has_receipt({xmlelement, "message", MsgAttrs, _} = Packet, Type) ->
case xml:get_attr_s("id", MsgAttrs) of
"" ->
case Type of
"request" -> false; %% Message must have an ID to ask a request for ack.
"received" ->
case xml:get_subtag(Packet, "received") of
false ->
false;
{xmlelement, _Name, Attrs, _Els} ->
case xml:get_attr_s("xmlns", Attrs) of
?NS_RECEIPTS ->
case xml:get_attr_s("id", Attrs) of
"" -> false;
SubTagID -> {true, SubTagID}
end;
_ -> false
end
end
end;
ID ->
case xml:get_subtag(Packet, Type) of
false ->
false;
{xmlelement, _Name, Attrs, _Els} ->
case xml:get_attr_s("xmlns", Attrs) of
?NS_RECEIPTS ->
case xml:get_attr_s("id", Attrs) of
"" -> {true, ID};
SubTagID -> {true, SubTagID}
end;
_ ->
false
end
end
end.
are_receipts_supported(undefined) ->
unknown;
are_receipts_supported({xmlelement, "presence", _, Els}) ->
case mod_caps:read_caps(Els) of
nothing ->
unknown;
Caps ->
lists:member(?NS_RECEIPTS, mod_caps:get_features(Caps))
end.
ping(From, To, Server, JID, El) ->
ID = randoms:get_string(),
add_timer(Server, {iq, ID}, JID, {From, To, El}),
ejabberd_router:route(jlib:make_jid("", Server, ""), JID,
{xmlelement, "iq",
[{"type", "get"}, {"id", ID}],
[{xmlelement, "query", [{"xmlns", ?NS_PING}], []}]}).
add_timer(Host, ID, JID, Packet) ->
{U, S, R} = jlib:jid_tolower(JID),
C2SPid = ejabberd_sm:get_session_pid(U, S, R),
gen_server:cast(gen_mod:get_module_proc(Host, ?PROCNAME),
{add, ID, C2SPid, Packet}).
del_timer(Host, ID, JID) ->
{U, S, R} = jlib:jid_tolower(JID),
C2SPid = ejabberd_sm:get_session_pid(U, S, R),
gen_server:cast(gen_mod:get_module_proc(Host, ?PROCNAME),
{del, ID, C2SPid}).
cancel_timer(TRef) ->
case erlang:cancel_timer(TRef) of
false ->
receive
{timeout, TRef, _} ->
ok
after 0 ->
ok
end;
_ ->
ok
end.
lookup(Pid, Key, Queue) ->
case ?DICT:find(Pid, Queue) of
{ok, Treap} ->
case treap:lookup(Key, Treap) of
{ok, _, Val} ->
{ok, Val};
error ->
error
end;
error ->
error
end.
insert(Pid, Key, Val, Queue) ->
Treap = case ?DICT:find(Pid, Queue) of
{ok, Treap1} ->
Treap1;
error ->
nil
end,
?DICT:store(Pid, treap:insert(Key, now(), Val, Treap), Queue).
delete(Pid, Key, Queue) ->
case ?DICT:find(Pid, Queue) of
{ok, Treap} ->
NewTreap = treap:delete(Key, Treap),
case treap:is_empty(NewTreap) of
true ->
?DICT:erase(Pid, Queue);
false ->
?DICT:store(Pid, NewTreap, Queue)
end;
error ->
Queue
end.
delete(Pid, Queue) ->
?DICT:erase(Pid, Queue).
to_list(Pid, Queue) ->
case ?DICT:find(Pid, Queue) of
{ok, Treap} ->
treap:to_list(Treap);
error ->
[]
end.

View File

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

View File

@ -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
View File

@ -0,0 +1,333 @@
%%%----------------------------------------------------------------------
%%% File : mod_blocking.erl
%%% Author : Stephan Maka
%%% Purpose : XEP-0191: Simple Communications Blocking
%%% Created : 24 Aug 2008 by Stephan Maka <stephan@spaceboyz.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
%%%----------------------------------------------------------------------
-module(mod_blocking).
-behaviour(gen_mod).
-export([start/2, stop/1,
process_iq/3,
process_iq_set/4,
process_iq_get/5]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("mod_privacy.hrl").
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
ejabberd_hooks:add(privacy_iq_get, Host,
?MODULE, process_iq_get, 40),
ejabberd_hooks:add(privacy_iq_set, Host,
?MODULE, process_iq_set, 40),
mod_disco:register_feature(Host, ?NS_BLOCKING),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING,
?MODULE, process_iq, IQDisc).
stop(Host) ->
ejabberd_hooks:delete(privacy_iq_get, Host,
?MODULE, process_iq_get, 40),
ejabberd_hooks:delete(privacy_iq_set, Host,
?MODULE, process_iq_set, 40),
mod_disco:unregister_feature(Host, ?NS_BLOCKING),
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING).
process_iq(_From, _To, IQ) ->
SubEl = IQ#iq.sub_el,
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}.
process_iq_get(_, From, _To,
#iq{xmlns = ?NS_BLOCKING,
sub_el = {xmlelement, "blocklist", _, _}},
_) ->
#jid{luser = LUser, lserver = LServer} = From,
process_blocklist_get(LUser, LServer);
process_iq_get(Acc, _, _, _, _) ->
Acc.
process_iq_set(_, From, _To, #iq{xmlns = ?NS_BLOCKING,
sub_el = {xmlelement, SubElName, _, SubEls}}) ->
#jid{luser = LUser, lserver = LServer} = From,
case {SubElName, xml:remove_cdata(SubEls)} of
{"block", []} ->
{error, ?ERR_BAD_REQUEST};
{"block", Els} ->
JIDs = parse_blocklist_items(Els, []),
process_blocklist_block(LUser, LServer, JIDs);
{"unblock", []} ->
process_blocklist_unblock_all(LUser, LServer);
{"unblock", Els} ->
JIDs = parse_blocklist_items(Els, []),
process_blocklist_unblock(LUser, LServer, JIDs);
_ ->
{error, ?ERR_BAD_REQUEST}
end;
process_iq_set(Acc, _, _, _) ->
Acc.
is_list_needdb(Items) ->
lists:any(
fun(X) ->
case X#listitem.type of
subscription -> true;
group -> true;
_ -> false
end
end, Items).
list_to_blocklist_jids([], JIDs) ->
JIDs;
list_to_blocklist_jids([#listitem{type = jid,
action = deny,
value = JID} = Item | Items], JIDs) ->
case Item of
#listitem{match_all = true} ->
Match = true;
#listitem{match_iq = true,
match_message = true,
match_presence_in = true,
match_presence_out = true} ->
Match = true;
_ ->
Match = false
end,
if
Match ->
list_to_blocklist_jids(Items, [JID | JIDs]);
true ->
list_to_blocklist_jids(Items, JIDs)
end;
% Skip Privacy List items than cannot be mapped to Blocking items
list_to_blocklist_jids([_ | Items], JIDs) ->
list_to_blocklist_jids(Items, JIDs).
parse_blocklist_items([], JIDs) ->
JIDs;
parse_blocklist_items([{xmlelement, "item", Attrs, _} | Els], JIDs) ->
case xml:get_attr("jid", Attrs) of
{value, JID1} ->
JID = jlib:jid_tolower(jlib:string_to_jid(JID1)),
parse_blocklist_items(Els, [JID | JIDs]);
false ->
% Tolerate missing jid attribute
parse_blocklist_items(Els, JIDs)
end;
parse_blocklist_items([_ | Els], JIDs) ->
% Tolerate unknown elements
parse_blocklist_items(Els, JIDs).
process_blocklist_block(LUser, LServer, JIDs) ->
F =
fun() ->
case mnesia:wread({privacy, {LUser, LServer}}) of
[] ->
% No lists yet
P = #privacy{us = {LUser, LServer}},
% TODO: i18n here:
NewDefault = "Blocked contacts",
NewLists1 = [],
List = [];
[#privacy{default = Default,
lists = Lists} = P] ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
% Default list exists
NewDefault = Default,
NewLists1 = lists:keydelete(Default, 1, Lists);
false ->
% No default list yet, create one
% TODO: i18n here:
NewDefault = "Blocked contacts",
NewLists1 = Lists,
List = []
end
end,
AlreadyBlocked = list_to_blocklist_jids(List, []),
NewList =
lists:foldr(fun(JID, List1) ->
case lists:member(JID, AlreadyBlocked) of
true ->
List1;
false ->
[#listitem{type = jid,
value = JID,
action = deny,
order = 0,
match_all = true
} | List1]
end
end, List, JIDs),
NewLists = [{NewDefault, NewList} | NewLists1],
mnesia:write(P#privacy{default = NewDefault,
lists = NewLists}),
{ok, NewDefault, NewList}
end,
case mnesia:transaction(F) of
{atomic, {error, _} = Error} ->
Error;
{atomic, {ok, Default, List}} ->
broadcast_list_update(LUser, LServer, Default, List),
broadcast_blocklist_event(LUser, LServer, {block, JIDs}),
{result, []};
_ ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
end.
process_blocklist_unblock_all(LUser, LServer) ->
F =
fun() ->
case mnesia:read({privacy, {LUser, LServer}}) of
[] ->
% No lists, nothing to unblock
ok;
[#privacy{default = Default,
lists = Lists} = P] ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
% Default list, remove all deny items
NewList =
lists:filter(
fun(#listitem{action = A}) ->
A =/= deny
end, List),
NewLists1 = lists:keydelete(Default, 1, Lists),
NewLists = [{Default, NewList} | NewLists1],
mnesia:write(P#privacy{lists = NewLists}),
{ok, Default, NewList};
false ->
% No default list, nothing to unblock
ok
end
end
end,
case mnesia:transaction(F) of
{atomic, {error, _} = Error} ->
Error;
{atomic, ok} ->
{result, []};
{atomic, {ok, Default, List}} ->
broadcast_list_update(LUser, LServer, Default, List),
broadcast_blocklist_event(LUser, LServer, unblock_all),
{result, []};
_ ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
end.
process_blocklist_unblock(LUser, LServer, JIDs) ->
F =
fun() ->
case mnesia:read({privacy, {LUser, LServer}}) of
[] ->
% No lists, nothing to unblock
ok;
[#privacy{default = Default,
lists = Lists} = P] ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
% Default list, remove matching deny items
NewList =
lists:filter(
fun(#listitem{action = deny,
type = jid,
value = JID}) ->
not(lists:member(JID, JIDs));
(_) ->
true
end, List),
NewLists1 = lists:keydelete(Default, 1, Lists),
NewLists = [{Default, NewList} | NewLists1],
mnesia:write(P#privacy{lists = NewLists}),
{ok, Default, NewList};
false ->
% No default list, nothing to unblock
ok
end
end
end,
case mnesia:transaction(F) of
{atomic, {error, _} = Error} ->
Error;
{atomic, ok} ->
{result, []};
{atomic, {ok, Default, List}} ->
broadcast_list_update(LUser, LServer, Default, List),
broadcast_blocklist_event(LUser, LServer, {unblock, JIDs}),
{result, []};
_ ->
{error, ?ERR_INTERNAL_SERVER_ERROR}
end.
broadcast_list_update(LUser, LServer, Name, List) ->
NeedDb = is_list_needdb(List),
ejabberd_router:route(
jlib:make_jid(LUser, LServer, ""),
jlib:make_jid(LUser, LServer, ""),
{xmlelement, "broadcast", [],
[{privacy_list,
#userlist{name = Name, list = List, needdb = NeedDb},
Name}]}).
broadcast_blocklist_event(LUser, LServer, Event) ->
JID = jlib:make_jid(LUser, LServer, ""),
ejabberd_router:route(
JID, JID,
{xmlelement, "broadcast", [],
[{blocking, Event}]}).
process_blocklist_get(LUser, LServer) ->
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
{'EXIT', _Reason} ->
{error, ?ERR_INTERNAL_SERVER_ERROR};
[] ->
{result, [{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}], []}]};
[#privacy{default = Default, lists = Lists}] ->
case lists:keysearch(Default, 1, Lists) of
{value, {_, List}} ->
JIDs = list_to_blocklist_jids(List, []),
Items = lists:map(
fun(JID) ->
?DEBUG("JID: ~p",[JID]),
{xmlelement, "item",
[{"jid", jlib:jid_to_string(JID)}], []}
end, JIDs),
{result,
[{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}],
Items}]};
_ ->
{result, [{xmlelement, "blocklist", [{"xmlns", ?NS_BLOCKING}], []}]}
end
end.

View File

@ -5,7 +5,7 @@
%%% Created : 7 Oct 2006 by Magnus Henoch <henoch@dtek.chalmers.se>
%%%
%%%
%%% 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@
%%% Created : 15 Feb 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% 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})

View File

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

View File

@ -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{},

View File

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

View File

@ -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(
Host, ServerHost, Access,
Room, HistorySize,
RoomShaper, From,
Nick, DefRoomOpts),
register_room(Host, Room, Pid),
mod_muc_room:route(Pid, From, Nick, Packet),
ok;
case start_new_room(
Host, ServerHost, Access,
Room, HistorySize,
RoomShaper, From,
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) ->

View File

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

View File

@ -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;
_ ->
captcha_required
WList = (StateData#state.config)#config.captcha_whitelist,
#jid{luser = U, lserver = S, lresource = R} = From,
case ?SETS:is_element({U, S, R}, WList) of
true ->
true;
false ->
case ?SETS:is_element({U, S, ""}, WList) of
true ->
true;
false ->
case ?SETS:is_element({"", S, ""}, WList) of
true ->
true;
false ->
captcha_required
end
end
end
end;
_ ->
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}

View File

@ -1,6 +1,6 @@
%%%----------------------------------------------------------------------
%%%
%%% ejabberd, Copyright (C) 2002-2010 ProcessOne
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% 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,

View File

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

View File

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

View File

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