Compare commits

...

109 Commits

Author SHA1 Message Date
Badlop 8c942e31e0 Update ejabberd version number to 3.0.0-alpha-5 2011-12-23 12:32:59 +01:00
Badlop 9912e1b548 Support undefinition of ssl:seed in R15B 2011-12-23 12:19:18 +01:00
Badlop 09a5fafc8a Always allow packets from user's server and bare jid (thanks to Alexey Shchepin) 2011-12-23 11:55:01 +01:00
Alexey Shchepin 64036bc121 Corrected mod_blocking hooks return value, activate "Blocked Contacts" privacy list after it is changed (EJAB-1519) (thanks to Karim Gemayel) 2011-12-21 19:00:17 +02:00
Badlop 33973677f2 Fix update with stripped beams (thanks to Jose M Herrero)(EJAB-1520) 2011-12-21 12:59:56 +01:00
Badlop c4c0b45ced Support Erlang/OTP R15B driver (EJAB-1521) 2011-12-21 12:13:44 +01:00
Evgeniy Khramtsov 4e9c81b66a Get rid of +driver option because it is incompatible with R15B 2011-12-19 16:01:52 +10:00
Badlop 7a53b66e46 Preliminary support for groupname@vhost in Displayed Groups (EJAB-506) 2011-12-15 17:28:37 +01:00
Badlop d5ebef1362 Fix command "update" to return response in the expected format 2011-12-08 19:40:29 +01:00
Badlop 26b814e367 Don't reveal invitee's resource when room informs invitor 2011-12-08 16:54:25 +01:00
Badlop f675ae1e8c Add callback function print_state/1 for behavior p1_fsm 2011-12-08 12:58:52 +01:00
Badlop b03828d2f7 Fix a recent commit 2011-12-08 12:55:50 +01:00
Pablo Polvorin df2e050780 Prevent overload of incomming s2s connections
Three changes were introduced:

1) ejabberd_s2s_in now uses p1_fsm instead of gen_fsm.  And uses
   the {max_queue, N} option to kill the process if its input
   queue grows too much.
2) If a ejabberd_s2s_in process is overload and killed, the server
   that originated that connection is not allowed to connect back
   to us for X seconds (set to 60seconds on the source)
3) The list of blocked (both statically and dynamically by the above
   method) host is now also checked for hosts authenticating by
   starttls+sasl. Previusly it was only used during dialback.
2011-12-02 15:36:51 -03:00
Pablo Polvorin 089331caf0 Fix bug on s2s shaper when TLS is used
The shaper was not enabled if the remote server authenticates
using a certificate instead of dialback.
2011-12-01 13:22:36 -03:00
Christophe Romain db834ca421 add hooks for node creation/deletion (EJAB-1470) 2011-11-29 15:48:49 +01:00
Badlop 5594e1470f Use httpc instead of the deprecated http 2011-11-26 17:20:13 +01:00
Badlop f7076f49b6 Avoid a possible race condition 2011-11-26 17:04:18 +01:00
Janusz Dziemidowicz 5c40d38b69 Remove some leftovers of XEP-0018.
Support for XEP-0018 was dropped long ago, but there are some
leftovers. Remove them.
2011-11-17 18:07:57 +01:00
Badlop e2763b7d11 Comment section 6.3.1 because it has no content at all 2011-11-15 16:49:50 +01:00
Evgeniy Khramtsov 80188d3a52 Fix presence problem after first unavailable (thanks to Christopher Faulet) (EJAB-1466) 2011-11-09 15:11:50 +10:00
Badlop 6575f899b6 Support to update modules in R14B04 and higher 2011-11-03 19:56:38 +01:00
Badlop 3cd9cd0e36 Only verify if passwd table is SCRAMmed when starting module. Some format fixes. 2011-10-28 16:43:07 +02:00
Badlop 576dda5c87 Fix previous commit about language name 2011-10-20 17:33:33 +02:00
Badlop 35a71fc493 Fix extraction of language name from PO files 2011-10-20 15:46:17 +02:00
Badlop 1a3abf15c0 Use the 'host' option also for static_modules, instead of 'prefix' 2011-10-20 11:13:36 +02:00
Badlop c7c59766b2 When a vhost is removed, remove also its accounts and rooms (EJAB-1371) 2011-10-20 11:13:23 +02:00
Badlop e681f4d4cf Remove workaround, as now the correct #roster format is received 2011-10-10 17:15:16 +02:00
Badlop b2a6eccded Revert "Update ejabberd version number to 3.0.0-alpha-4"
This reverts commit a6e5fb269c.
2011-10-04 11:19:04 +02:00
Badlop a6e5fb269c Update ejabberd version number to 3.0.0-alpha-4 2011-09-26 18:07:01 +02:00
Badlop 96c99b6427 Added relevant changes of 'master' branch. 2011-09-26 18:03:30 +02:00
Janusz Dziemidowicz 3fd5513549 Decrease CPU usage caused by tls:send with large data.
Sending one large chunk of data with tls:send eats lots of
CPU power and blocks whole Erlang emulator. This is caused by the
fact that encrypted output is read from memory BIO in 1k chunks.
Memory BIO, after reading data, shifts the remaining part.
If large chunks of data (few MB) is sent and then read in 1k
chunks, then a _lot_ of shifting is performed eating CPU.

The solution is to simply allocate binary of the needed size
(amount of data in memory BIO can be retrieved with
BIO_ctrl_pending) and then issue only one read that reads the
whole data.
2011-09-25 00:56:15 +02:00
Badlop 804549415d Fix mod_muc_log crash when first log entry is room being destroyed (EJAB-1499) 2011-09-23 17:02:00 +02:00
Christophe Romain fa08db7091 fix broken PEP after EJAB-1498 (thanks to Karim Gemayel) 2011-09-22 20:49:56 +02:00
Badlop df0754ade9 ejabberdctl doesn't support parameters with blankspaces (EJAB-1458)
Revert "Escape output from ctlexec() to erl script (thanks to Dan Scott)(EJAB-1399)"

This reverts commit 6dea2d2307.
2011-09-22 16:18:44 +02:00
Christophe Romain d3c4eab46f Fix Denial of Service when user sends malformed publish stanza (thanks to Oleg Smirnov) (EJAB-1498) 2011-09-21 14:40:47 +02:00
Badlop 1c62edc53c added svg mimetype to default content types (thanks to Markus Kohlhase) 2011-09-20 16:50:32 +02:00
Badlop 17099eaaca Add support for @online@ to add_user_to_group 2011-09-20 16:45:08 +02:00
Evgeniy Khramtsov f3f80ea0ea New option support: ldap_deref_aliases (EJAB-639)
Conflicts:

	src/ejabberd_auth_ldap.erl
2011-09-19 17:03:36 +10:00
Badlop 8b45ee5208 Use NS_XBOSH instead of NS_BOSH (thanks to Janusz Dziemidowicz)
There are two, exactly the same defines for urn:xmpp:xbosh
namespace: NS_BOSH and NS_XBOSH. Both are used in http_bind
module. Stick with NS_XBOSH everywhere.
2011-09-16 01:17:26 +02:00
Janusz Dziemidowicz 1dc1795f2a Update http_bind module to latest XEP-0124 and XEP-0206
Only cosmetic changes, version bumped to 1.10 and added
'restartlogic' attribute.
2011-09-16 01:17:23 +02:00
Badlop a435c4b123 No need to explicitly skip old useless tables, simply fix keep_tables 2011-09-14 16:19:23 +02:00
Badlop 151994c8d5 Avoid crash when performing admin action (thanks to Evgeniy Khramtsov) 2011-09-09 11:29:46 +02:00
Badlop 1aa36cb05c Fixed support for multi-resource occupants (EJAB-305) 2011-09-08 16:18:22 +02:00
Badlop e6e146c0f7 Improve room config change logging, due to allow_private_messages_from_visitors 2011-09-08 12:35:59 +02:00
Badlop adc37a8cc6 Support to import XML files without password-format attribute 2011-09-08 11:43:21 +02:00
Badlop b017702a15 Clarification about system user of ejabberd.init 2011-09-06 13:40:04 +02:00
Badlop d6d58d13f7 Fix SCRAM to support attributes which value has only one character 2011-09-05 23:37:57 +02:00
Badlop 8bcde56409 Fix typo in the Guide about user_regexp 2011-09-05 19:36:32 +02:00
Badlop e1e75b2852 Fix support to negotiate zlib compression after starttls 2011-09-05 16:53:04 +02:00
Badlop d6d3190b47 Fix SASL auth error conditions 2011-09-05 16:29:30 +02:00
Badlop fed00a0c17 Fix SCRAM auth of uppercase passwords (EJAB-1196) 2011-09-05 13:04:26 +02:00
Badlop 4fcff0b6b9 Replace calls of OTP's Binary, since they would require R14 2011-09-04 15:19:10 +02:00
Badlop d1f1958dd4 Replace the full ejabberd_zlib into a simple exmpp_compress interface (EJAB-991) 2011-09-03 01:04:33 +02:00
Badlop adc4a7f67c Fix zlib compression connection (EJAB-1448) 2011-09-03 00:06:53 +02:00
Badlop ad6218cdf2 Use the syntax PRIMARY KEY (field(int)) only for MySQL (EJAB-1337) 2011-09-02 13:50:03 +02:00
Badlop edda5e1747 Updated mysql schema with current dump (EJAB-1474) 2011-09-02 13:15:03 +02:00
Christophe Romain 2f5dc0d35d enable pubsub#deliver_notification checking (thanks to Karim Gemayel)(EJAB-1453) 2011-08-31 16:42:44 +02:00
Badlop dbe71153dc Explicitly skip useless ejabberd 2.0* mnesia tables when restoring backup
Reference:
http://www.ejabberd.im/node/4867
2011-08-29 12:38:42 +02:00
Badlop 8ddbf1c1c5 Allow multiple entry with same nick to MUC rooms (thanks to Magnus Henoch)(EJAB-305) 2011-08-23 21:52:08 +02:00
Badlop 7d3633e91c Fix more Dialyzer warnings 2011-08-22 22:26:54 +02:00
Badlop fef8c3a1f3 New option resource_conflict defines server action (thanks to Lee Boynton)(EJAB-650) 2011-08-17 19:44:56 +02:00
Badlop 7355e810a8 Fix problems detected by Dialyzer 2011-08-17 19:25:59 +02:00
Badlop 6eb5b0d298 Fix ODBC account counting (thanks to Artem Sh)(EJAB-1491) 2011-08-16 19:29:40 +02:00
Badlop e895b12aea Fix support for odbc + gen_storage + static_modules 2011-08-16 19:29:38 +02:00
Badlop afad6c75a0 Provide stacktrace in log report when module start fails 2011-08-16 19:29:36 +02:00
Badlop 4a4a7afe22 Fix SCRAM code (EJAB-1196) 2011-08-16 19:29:30 +02:00
Badlop 24f5c964cd New SASL authentication method: SCRAM-SHA-1 (thanks to Stephen Röttger)(EJAB-1196) 2011-08-16 00:28:25 +02:00
Badlop 1b7cc33a7f Add comment about ping requests sent even to non-supporting clients 2011-08-04 18:32:45 +02:00
Badlop aa8c9bf0a5 Name and groups must be binaries, not strings 2011-07-28 16:58:58 +02:00
Badlop 92887e96bd Return #roster elements in the expected format 2011-07-28 16:58:40 +02:00
Badlop 4f101c318f Fix JID format, relevant when contact is in roster and shared roster 2011-07-28 16:05:18 +02:00
Badlop 4fcbd5894e Send presence from shared roster contacts to new logged user (EJAB-1412) 2011-07-28 16:05:15 +02:00
Badlop cd7beb9492 External auth should call storage auth only when cache is enabled 2011-07-27 13:19:53 +02:00
Badlop 91fc4a6fbb Verify username provided in registration web form 2011-07-27 12:44:43 +02:00
Badlop 158aa06760 Handle case when ejabberd_auth_* says the account exists 2011-07-27 12:44:41 +02:00
Badlop 9c2f0681f0 More fix Host usage when running webadmin_page_* 2011-07-21 13:36:16 +02:00
Badlop cd2d207877 Remove unused variable 2011-07-19 22:14:59 +02:00
Badlop 5ab3561700 Support for: Room option to ignore private messages from visitors 2011-07-19 22:14:58 +02:00
Badlop 19ab0174e4 Support for static_modules in mod_muc and mod_muc_log 2011-07-19 22:14:56 +02:00
Badlop 82551036c2 Fix Host usage when running webadmin_page_* (thanks to Shaun Kruger) 2011-07-19 12:23:05 +02:00
Badlop d09acc8231 Fix flash client patch 2011-07-18 21:56:23 +02:00
Badlop 98a78624cc Include status 110 in presence to new occupant (EJAB-740) 2011-07-16 19:31:44 +02:00
Badlop d923c70082 Document static_modules, prefix, and which modules don't support static 2011-07-15 02:51:01 +02:00
Badlop 06d4889586 Support static_modules in mod_vcard, vcard_xupdate, version, http_bind and http_fileserver 2011-07-15 02:50:45 +02:00
Badlop ff8bd0c2ec Support static_modules in mod_caps, ping, privacy, private, roster, stats and time 2011-07-15 02:50:04 +02:00
Badlop 12d6f3364b Support static_modules in mod_blocking, configure, pres_counter, register, shared_roster and sic 2011-07-15 02:49:48 +02:00
Badlop 86859e5234 Try first to get table of the specific Host, otherwise try 'global' 2011-07-15 02:49:35 +02:00
Badlop b0755c7cfb Use #roster record instead of direct tuple 2011-07-13 17:12:51 +02:00
Evgeniy Khramtsov c5cf9b4e6d Document ldap_tls_cacertfile and ldap_tls_depth options (EJAB-1299) 2011-07-13 15:57:24 +10:00
Evgeniy Khramtsov 8760eef677 Add ldap_tls_cacertfile and ldap_tls_depth options (EJAB-1299) 2011-07-13 15:44:31 +10:00
Badlop 0b1f3ca148 Fix support for static_modules in mod_adhoc, announce, disco, last, offline 2011-07-11 19:47:11 +02:00
Badlop 0d7e69a65e Implement dirty_get_registered_users in ejabberd_auth_storage 2011-07-11 19:47:05 +02:00
Badlop 466481d126 Improve static_modules support in hooks (EJAB-1478)
Also removed code that calls a hook for both Host and for global.
2011-07-11 19:47:03 +02:00
Badlop b8d74ab2a9 Fix translatable strings 2011-07-11 17:25:05 +02:00
Badlop 1d8a69d94f Room option to ignore private messages from visitors (thanks to Maxim Ignatenko) 2011-07-11 17:25:02 +02:00
Alexey Shchepin 72e3e0e6ad Ported flash clients patch from 2.1.x 2011-07-08 18:17:05 +03:00
Badlop 3ccbf1115c Fix read of global ldap_servers option (EJAB-1309) 2011-07-06 20:49:32 +02:00
Badlop c1e6c7d821 Document that ldap_servers can contain {host, VHost} 2011-07-06 20:47:53 +02:00
Badlop 01c160251a Fix reference to 'admin' ACL name (thanks to Samuel Wejeus)(EJAB-1476) 2011-07-05 16:32:48 +02:00
Evgeniy Khramtsov 8d4945aa7f Match ldap_uidattr_format case-insensitively (EJAB-1449) 2011-07-04 21:45:42 +10:00
Juan Pablo Carlino 06e3503976 is_user_in_group/3 must check if user belongs to given Group from Host specified on third argument.
* is_user_in_group/3 was checking if given user (passed on first parameter as
a bare jid) belonged to group of the server derived from this jid.
2011-07-01 12:52:07 -03:00
Badlop 1d244c7d9b Comment in the cfg file the correct node name used in ejabberdctl.template 2011-06-28 00:31:31 +02:00
Pablo Polvorin d3d17e8041 Fix namespace of returned conference participants <item> 2011-06-23 12:31:54 -03:00
Badlop bf5938ee1d Provide list of registered pids/ports 2011-06-23 16:26:39 +02:00
Badlop 8ad81a3db8 No need to have the default "localhost" in the table "hosts" 2011-06-23 16:26:36 +02:00
Badlop ef67ec4286 start/stop_modules/methods accept atom or list of atoms 2011-06-22 12:32:52 +02:00
Evgeniy Khramtsov b2f06226b5 Log an error when an LDAP filter is incorrect (EJAB-1395) 2011-06-15 18:11:08 +02:00
Badlop 84b0e6333d Clarify in mod_register_web example what host to put in the URL 2011-06-11 16:42:02 +02:00
Badlop 9c9f278a96 Fix crash when user publishes an item (EJAB-1457) 2011-06-01 23:29:45 +02:00
93 changed files with 2771 additions and 1514 deletions
-2
View File
@@ -12,8 +12,6 @@ To compile ejabberd you need:
Avoid R14A and R14B.
- exmpp 0.9.6 or higher
- OpenSSL 0.9.8 or higher, for STARTTLS, SASL and SSL encryption.
- Zlib 1.2.3 or higher, for Stream Compression support
(XEP-0138). Optional.
- Erlang mysql library. Optional. MySQL authentication/storage.
- Erlang pgsql library. Optional. PostgreSQL authentication/storage.
- PAM library. Optional. For Pluggable Authentication Modules (PAM).
+168 -80
View File
@@ -112,10 +112,11 @@
\newcommand{\iqdiscitem}[1]{\titem{\{iqdisc, Discipline\}} \ind{options!iqdisc}This specifies
the processing discipline for #1 IQ queries (see section~\ref{modiqdiscoption}).}
\newcommand{\hostitem}[1]{
\titem{\{host, HostName\}} \ind{options!host} This option defines the Jabber ID of the
service. If the \texttt{host} option is not specified, the Jabber ID will be the
hostname of the virtual host with the prefix `\jid{#1.}'. The keyword "@HOST@"
is replaced at start time with the real virtual host name.
\titem{\{host, HostName\}} \ind{options!host}
This option defines the Jabber ID of the service.
If the option is not specified, the Jabber ID of the service will be the
hostname of the virtual host preceded with `\jid{#1.}'.
For more details see \ref{modhostoption}.
}
\newcommand{\backend}[1]{\titem{\{backend, mnesia|odbc\}} \ind{options!backend}
Specify the backend used to store the tables: #1.
@@ -250,8 +251,9 @@ and for example it doesn't read the file ejabberdctl.cfg.
On a *nix system, if you want ejabberd to be started as daemon at boot time,
copy \term{ejabberd.init} from the 'bin' directory to something like \term{/etc/init.d/ejabberd}
(depending on your distribution).
Create a system user called \term{ejabberd};
it will be used by the script to start the server.
Create a system user called \term{ejabberd},
give it write access to the directories \term{database/} and \term{logs/}, and set that as home;
the script will start the server with that user.
Then you can call \term{/etc/inid.d/ejabberd start} as root to start the server.
If \term{ejabberd} doesn't start correctly in Windows,
@@ -317,7 +319,6 @@ To compile \ejabberd{} on a `Unix-like' operating system, you need:
Don't use R14A or R14B because \footahref{http://www.erlang.org/cgi-bin/ezmlm-cgi/4/54598}{they have a bug}.
\item exmpp 0.9.6 or higher
\item OpenSSL 0.9.8 or higher, for STARTTLS, SASL and SSL encryption.
\item Zlib 1.2.3 or higher, for Stream Compression support (\xepref{0138}). Optional.
\item Erlang mysql library. Optional. For MySQL authentication or storage. See section \ref{compilemysql}.
\item Erlang pgsql library. Optional. For PostgreSQL authentication or storage. See section \ref{compilepgsql}.
\item PAM library. Optional. For Pluggable Authentication Modules (PAM). See section \ref{pam}.
@@ -393,6 +394,9 @@ Some options that you may be interested in modifying:
\titem{--disable-transient-supervisors}
Disable the use of Erlang/OTP supervision for transient processes.
\titem{--enable-flash-hack}
Enable support for non-standard XML socket clients of Adobe Flash 8 and lower.
\end{description}
\makesubsection{install}{Install}
@@ -520,7 +524,6 @@ To compile \ejabberd{} on a Microsoft Windows system, you need:
\item \footahref{http://support.process-one.net/doc/display/EXMPP}{exmpp 0.9.5 or higher}
\item \footahref{http://www.slproweb.com/products/Win32OpenSSL.html}{Shining Light OpenSSL 0.9.8d or higher}
(to enable SSL connections)
\item \footahref{http://www.zlib.net/}{Zlib 1.2.3 or higher}
\end{itemize}
@@ -531,8 +534,6 @@ We assume that we will try to put as much library as possible into \verb|C:\sdk\
\begin{enumerate}
\item Install Erlang emulator (for example, into \verb|C:\sdk\erl5.6.5|).
\item Install OpenSSL in \verb|C:\sdk\OpenSSL| and add \verb|C:\sdk\OpenSSL\lib\VC| to your path or copy the binaries to your system directory.
\item Install ZLib in \verb|C:\sdk\gnuWin32|. Copy
\verb|C:\sdk\GnuWin32\bin\zlib1.dll| to your system directory.
\item Make sure the you can access Erlang binaries from your path. For example: \verb|set PATH=%PATH%;"C:\sdk\erl5.6.5\bin"|
\item Depending on how you end up actually installing the library you might need to check and tweak the paths in the file configure.erl.
\item While in the directory \verb|ejabberd\src| run:
@@ -565,8 +566,8 @@ ejabberdctl register admin1 example.org FgT5bk3
\end{enumerate}
\item Edit the \ejabberd{} configuration file to give administration rights to the XMPP account you created:
\begin{verbatim}
{acl, admins, {user, "admin1", "example.org"}}.
{access, configure, [{allow, admins}]}.
{acl, admin, {user, "admin1", "example.org"}}.
{access, configure, [{allow, admin}]}.
\end{verbatim}
You can grant administrative privileges to many XMPP accounts,
and also to accounts in other XMPP servers.
@@ -1221,12 +1222,41 @@ When the storage is configured for ODBC, the ODBC server is
configured with the \term{odbc\_server} option, see
\ref{mysql} for MySQL, \ref{pgsql} for PostgreSQL, \ref{mssql} for MSSQL, and \ref{odbc} for generic ODBC.
The option \term{\{auth\_password\_format, plain|scram\}}
defines in what format the users passwords are stored:
\begin{description}
\titem{plain}
The password is stored as plain text in the database.
This is risky because the passwords can be read if your database gets compromised.
This is the default value.
This format allows clients to authenticate using:
the old Jabber Non-SASL (\xepref{0078}), \term{SASL PLAIN},
\term{SASL DIGEST-MD5}, and \term{SASL SCRAM-SHA-1}.
\titem{scram}
The password is not stored, only some information that allows to verify the hash provided by the client.
It is impossible to obtain the original plain password from the stored information;
for this reason, when this value is configured it cannot be changed to \term{plain} anymore.
This format allows clients to authenticate using: \term{SASL PLAIN} and \term{SASL SCRAM-SHA-1}.
\end{description}
The option \option{resource\_conflict} defines the action when a client attempts to
login to an account with a resource that is already connected.
The option syntax is:
\esyntax{\{resource\_conflict, setresource|closenew|closeold\}.}
The possible values match exactly the three possibilities described in
\footahref{http://tools.ietf.org/html/rfc6120\#section-7.7.2.2}{XMPP Core: section 7.7.2.2}.
The default value is \term{closeold}.
If the client uses old Jabber Non-SASL authentication (\xepref{0078}),
then this option is not respected, and the action performed is \term{closeold}.
Examples:
\begin{itemize}
\item To use internal Mnesia storage on all virtual hosts:
\item To use internal Mnesia storage with hashed passwords on all virtual hosts:
\begin{verbatim}
{auth_method, storage}.
{auth_storage, mnesia}.
{auth_password_format, scram}.
\end{verbatim}
\item To use ODBC storage on all virtual hosts:
\begin{verbatim}
@@ -1533,10 +1563,10 @@ declarations of ACLs in the configuration file have the following syntax:
{acl, tests, {user_regexp, "^test[0-9]*$"}}.
\end{verbatim}
%$
\titem{\{user\_regexp, UserRegexp, Server\}} Matches any user with a name
\titem{\{user\_regexp, Regexp, Server\}} Matches any user with a name
that matches \term{Regexp} at server \term{Server}. Example:
\begin{verbatim}
{acl, tests, {user_Userregexp, "^test", "example.org"}}.
{acl, tests, {user_regexp, "^test", "example.org"}}.
\end{verbatim}
\titem{\{server\_regexp, Regexp\}} Matches any JID from the server that
matches \term{Regexp}. Example:
@@ -2291,8 +2321,11 @@ one for authentication and other for regular calls.
Parameters:
\begin{description}
\titem{\{ldap\_servers, [Servers, ...]\}} \ind{options!ldap\_server}List of IP addresses or DNS names of your
LDAP servers. This option is required.
\titem{\{ldap\_servers, [Servers, ...]|\{host, VHost\}\}} \ind{options!ldap\_server}
This option can contain a list of IP addresses or DNS names of your LDAP servers.
If you are configuring multiple vhosts, you can configure in this option that some of
them must use the same LDAP connections as another vhost.
This option is required.
\titem{\{ldap\_encrypt, none|tls\}} \ind{options!ldap\_encrypt}Type of connection encryption to the LDAP server.
Allowed values are: \term{none}, \term{tls}.
The value \term{tls} enables encryption by using LDAP over SSL.
@@ -2303,6 +2336,16 @@ This option specifies whether to verify LDAP server certificate or not when TLS
When \term{hard} is enabled \ejabberd{} doesn't proceed if a certificate is invalid.
When \term{soft} is enabled \ejabberd{} proceeds even if check fails.
The default is \term{false} which means no checks are performed.
\titem{\{ldap\_tls\_cacertfile, Path\}} \ind{options!ldap\_tls\_cacertfile}
Path to file containing PEM encoded CA certificates. This option is needed
(and required) when TLS verification is enabled.
\titem{\{ldap\_tls\_depth, Number\}} \ind{options!ldap\_tls\_depth}
Specifies the maximum verification depth when TLS verification is enabled,
i.e. how far in a chain of certificates the verification process can proceed
before the verification is considered to fail.
Peer certificate = 0, CA certificate = 1, higher level CA certificate = 2, etc.
The value 2 thus means that a chain can at most contain peer cert,
CA cert, next CA cert, and an additional CA cert. The default value is 1.
\titem{\{ldap\_port, Number\}} \ind{options!ldap\_port}Port to connect to your LDAP server.
The default port is~389 if encryption is disabled; and 636 if encryption is enabled.
If you configure a value, it is stored in \ejabberd{}'s database.
@@ -2312,6 +2355,7 @@ the value previously stored in the database will be used instead of the default
is~\term{""} which means `anonymous connection'.
\titem{\{ldap\_password, Password\}} \ind{options!ldap\_password}Bind password. The default
value is \term{""}.
\titem{\{ldap\_deref\_aliases, never|always|finding|searching\}} \ind{options!ldap\_deref\_aliases} Whether or not to dereference aliases. The default is \term{never}.
\end{description}
Example:
@@ -2321,6 +2365,12 @@ Example:
{ldap_port, 389}.
{ldap_rootdn, "cn=Manager,dc=domain,dc=org"}.
{ldap_password, "secret"}.
{host_config, "example3.org", [
{ldap_servers, ["ldap3.example.org"]}.
]}.
{host_config, "example4.org", [
{ldap_servers, {host, "example3.org"}}
]}.
\end{verbatim}
\makesubsubsection{ldapauth}{Authentication}
@@ -2540,37 +2590,52 @@ configuration is shown below:
\makesection{modules}{Modules Configuration}
\ind{modules}
The option \term{modules} defines the list of modules that will be loaded after
\ejabberd{}'s startup. Each entry in the list is a tuple in which the first
element is the name of a module and the second is a list of options for that
module.
The option \term{modules} defines the modules that will be started
for each virtual host, and the module options.
The syntax is:
\esyntax{\{modules, [ \{ModuleName, ModuleOptions\}, ...]\}.}
Each module is started for each virtual host with the same options.
If you want a module to be started in a specific host with a different configuration,
use \term{host\_config} with \term{add} as explained in section~\ref{virtualhost}.
If you configure ejabberd to serve millions of virtual hosts,
you may want that each module is started only once, and let it serve all the hosts.
Those modules are started statically when ejabberd starts,
and cannot be reconfigured until the next server start.
The option \term{static\_modules} defines the modules that will be started statically.
The syntax is:
\esyntax{\{static\_modules, [ \{ModuleName, ModuleOptions\}, ...]\}.}
Examples:
\begin{itemize}
\item In this example only the module \modecho{} is loaded and no module
options are specified between the square brackets:
\item Start three modules for each vhost.
They can be reconfigured or stopped dynamically, for example with WebAdmin.
Remark that, besides the last entry, all entries end with a comma:
\begin{verbatim}
{modules,
[
{mod_echo, []}
{mod_echo, [{host, "echodynamic.@HOST@"}]},
{mod_time, []},
{mod_version, []}
]}.
\end{verbatim}
\item In the second example the modules \modecho{}, \modtime{}, and
\modversion{} are loaded without options. Remark that, besides the last entry,
all entries end with a comma:
\item Start MUC for each vhost, and the other three modules for all the vhosts:
\begin{verbatim}
{modules,
[
{mod_echo, []},
{mod_muc, []}
]}.
{static_modules,
[
{mod_echo, [{host, "echostatic.@HOST@"}]}
{mod_time, []},
{mod_version, []}
]}.
\end{verbatim}
\end{itemize}
\makesubsection{modoverview}{Modules Overview}
\ind{modules!overview}\ind{XMPP compliancy}
@@ -2578,48 +2643,49 @@ The following table lists all modules included in \ejabberd{}.
\begin{table}[H]
\centering
\begin{tabular}{|l|l|l|}
\hline {\bf Module} & {\bf Feature} & {\bf Dependencies} \\
\begin{tabular}{|l|l|l|l|}
\hline {\bf Module} & {\bf Feature} & {\bf St} & {\bf Dependencies} \\
\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}) & \\
\hline \ahrefloc{modecho}{\modecho{}} & Echoes XMPP stanzas & \\
\hline \ahrefloc{modhttpbind}{\modhttpbind{}} & XMPP over Bosh service (HTTP Binding) & \\
\hline \ahrefloc{modhttpfileserver}{\modhttpfileserver{}} & Small HTTP file server & \\
\hline \ahrefloc{modlast}{\modlast{}} & Last Activity (\xepref{0012}) & \\
\hline \ahrefloc{modmuc}{\modmuc{}} & Multi-User Chat (\xepref{0045}) & \\
\hline \ahrefloc{modmuclog}{\modmuclog{}} & Multi-User Chat room logging & \modmuc{} \\
\hline \ahrefloc{modmulticast}{\modmulticast{}} & Multicast Service (\xepref{0033}) & \\
\hline \ahrefloc{modoffline}{\modoffline{}} & Offline message storage (\xepref{0160}) & \\
\hline \ahrefloc{modping}{\modping{}} & XMPP Ping and periodic keepalives (\xepref{0199}) & \\
\hline \ahrefloc{modprescounter}{\modprivacy{}} & Detect presence subscription flood & \\
\hline \ahrefloc{modprivacy}{\modprivacy{}} & Blocking Communication (XMPP IM) & \\
\hline \ahrefloc{modprivate}{\modprivate{}} & Private XML Storage (\xepref{0049}) & \\
\hline \ahrefloc{modproxy}{\modproxy{}} & SOCKS5 Bytestreams (\xepref{0065}) & \\
\hline \ahrefloc{modpubsub}{\modpubsub{}} & Pub-Sub (\xepref{0060}), PEP (\xepref{0163}) & \modcaps{} \\
\hline \ahrefloc{modpubsub}{\modpubsubodbc{}} & Pub-Sub (\xepref{0060}), PEP (\xepref{0163}) & supported DB (*) and \modcaps{} \\
\hline \ahrefloc{modregister}{\modregister{}} & In-Band Registration (\xepref{0077}) & \\
\hline \ahrefloc{modregisterweb}{\modregisterweb{}} & Web for Account Registrations & \\
\hline \ahrefloc{modroster}{\modroster{}} & Roster management (XMPP IM) & \\
\hline \ahrefloc{modservicelog}{\modservicelog{}} & Copy user messages to logger service & \\
\hline \ahrefloc{modsharedroster}{\modsharedroster{}} & Shared roster management & \modroster{} \\
\hline \ahrefloc{modsharedrosterldap}{\modsharedrosterldap{}} & LDAP Shared roster management & \modroster{} \\
\hline \ahrefloc{modsic}{\modsic{}} & Server IP Check (\xepref{0279}) & \\
\hline \ahrefloc{modstats}{\modstats{}} & Statistics Gathering (\xepref{0039}) & \\
\hline \ahrefloc{modtime}{\modtime{}} & Entity Time (\xepref{0202}) & \\
\hline \ahrefloc{modvcard}{\modvcard{}} & vcard-temp (\xepref{0054}) & \\
\hline \ahrefloc{modvcardldap}{\modvcardldap{}} & vcard-temp (\xepref{0054}) & LDAP server \\
\hline \ahrefloc{modvcardxupdate}{\modvcardxupdate{}} & vCard-Based Avatars (\xepref{0153}) & \modvcard{} \\
\hline \ahrefloc{modversion}{\modversion{}} & Software Version (\xepref{0092}) & \\
\hline \modadhoc{} & Ad-Hoc Commands (\xepref{0050}) & & \\
\hline \ahrefloc{modannounce}{\modannounce{}} & Manage announcements& & recommends \modadhoc{} \\
\hline \modblocking{} & Simple Communic. 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}) & & \\
\hline \ahrefloc{modecho}{\modecho{}} & Echoes XMPP stanzas & & \\
\hline \ahrefloc{modhttpbind}{\modhttpbind{}} & XMPP over Bosh service (HTTP Binding) & & \\
\hline \ahrefloc{modhttpfileserver}{\modhttpfileserver{}} & Small HTTP file server & & \\
\hline \ahrefloc{modlast}{\modlast{}} & Last Activity (\xepref{0012}) & & \\
\hline \ahrefloc{modmuc}{\modmuc{}} & Multi-User Chat (\xepref{0045}) & & \\
\hline \ahrefloc{modmuclog}{\modmuclog{}} & Multi-User Chat room logging & & \modmuc{} \\
\hline \ahrefloc{modmulticast}{\modmulticast{}} & Multicast Service (\xepref{0033}) & - & \\
\hline \ahrefloc{modoffline}{\modoffline{}} & Offline message storage (\xepref{0160}) & & \\
\hline \ahrefloc{modping}{\modping{}} & XMPP Ping and keepalives (\xepref{0199}) & & \\
\hline \ahrefloc{modprescounter}{\modprescounter{}} & Detect presence subscription flood & & \\
\hline \ahrefloc{modprivacy}{\modprivacy{}} & Blocking Communication (XMPP IM) & & \\
\hline \ahrefloc{modprivate}{\modprivate{}} & Private XML Storage (\xepref{0049}) & & \\
\hline \ahrefloc{modproxy}{\modproxy{}} & SOCKS5 Bytestreams (\xepref{0065}) & - & \\
\hline \ahrefloc{modpubsub}{\modpubsub{}} & Pub-Sub (\xepref{0060}), PEP (\xepref{0163}) & - & \modcaps{} \\
\hline \ahrefloc{modpubsub}{\modpubsubodbc{}} & Pub-Sub (\xepref{0060}), PEP (\xepref{0163}) & - & DB (*) and \modcaps{} \\
\hline \ahrefloc{modregister}{\modregister{}} & In-Band Registration (\xepref{0077}) & & \\
\hline \ahrefloc{modregisterweb}{\modregisterweb{}} & Web for Account Registrations & & \\
\hline \ahrefloc{modroster}{\modroster{}} & Roster management (XMPP IM) & & \\
\hline \ahrefloc{modservicelog}{\modservicelog{}} & Copy user messages to logger service & - & \\
\hline \ahrefloc{modsharedroster}{\modsharedroster{}} & Shared roster management & & \modroster{} \\
\hline \ahrefloc{modsharedrosterldap}{\modsharedrosterldap{}} & LDAP Shared roster management & - & \modroster{} \\
\hline \ahrefloc{modsic}{\modsic{}} & Server IP Check (\xepref{0279}) & & \\
\hline \ahrefloc{modstats}{\modstats{}} & Statistics Gathering (\xepref{0039}) & & \\
\hline \ahrefloc{modtime}{\modtime{}} & Entity Time (\xepref{0202}) & & \\
\hline \ahrefloc{modvcard}{\modvcard{}} & vcard-temp (\xepref{0054}) & & \\
\hline \ahrefloc{modvcardldap}{\modvcardldap{}} & vcard-temp (\xepref{0054}) & - & LDAP server \\
\hline \ahrefloc{modvcardxupdate}{\modvcardxupdate{}} & vCard-Based Avatars (\xepref{0153}) & & \modvcard{} \\
\hline \ahrefloc{modversion}{\modversion{}} & Software Version (\xepref{0092}) & & \\
\hline
\end{tabular}
\end{table}
\begin{itemize}
\item St -: This means that the module cannot be configured to start statically, only dynamically.
\item (*) This module requires a supported database. For a list of supported databases, see section~\ref{database}.
\end{itemize}
@@ -2709,6 +2775,7 @@ The syntax is:
If you include the keyword "@HOST@" in the HostName,
it is replaced at start time with the real virtual host string.
If you configure the module to be started statically, then the keyword @HOST@ is mandatory.
This example configures
the \ind{modules!\modecho{}}echo module to provide its echoing service
@@ -2733,6 +2800,20 @@ the "@HOST@" keyword must be used:
]}.
\end{verbatim}
This example configures
the \ind{modules!\modecho{}}echo module to provide its echoing service
in Jabber IDs like
\jid{mirror.example.org}, \jid{mirror.host2.com}, \jid{mirror.shakespeare.lit}:
\begin{verbatim}
{hosts, ["example.org", "host2.com", "shakespeare.lit"]}.
{static_modules,
[
...
{mod_echo, [{host, "mirror.@HOST@"}]},
...
]}.
\end{verbatim}
\makesubsection{modannounce}{\modannounce{}}
\ind{modules!\modannounce{}}\ind{MOTD}\ind{message of the day}\ind{announcements}
@@ -2781,7 +2862,7 @@ Examples:
\begin{itemize}
\item Only administrators can send announcements:
\begin{verbatim}
{access, announce, [{allow, admins}]}.
{access, announce, [{allow, admin}]}.
{modules,
[
@@ -2795,9 +2876,9 @@ Examples:
\begin{verbatim}
{acl, direction, {user, "big_boss", "example.org"}}.
{acl, direction, {user, "assistant", "example.org"}}.
{acl, admins, {user, "admin", "example.org"}}.
{acl, admin, {user, "admin", "example.org"}}.
{access, announce, [{allow, admins},
{access, announce, [{allow, admin},
{allow, direction}]}.
{modules,
@@ -3201,6 +3282,7 @@ Module options:
\begin{description}
\titem{\{allow\_change\_subj, true|false\}} Allow occupants to change the subject.
\titem{\{allow\_private\_messages, true|false\}} Occupants can send private messages to other occupants.
\titem{\{allow\_private\_messages\_from\_visitors, anyone|moderators|nobody\}} Visitors can send private messages to other occupants.
\titem{\{allow\_query\_users, true|false\}} Occupants can send IQ queries to other occupants.
\titem{\{allow\_user\_invites, false|true\}} Allow occupants to send invitations.
\titem{\{allow\_visitor\_nickchange, true|false\}} Allow visitors to
@@ -3445,10 +3527,10 @@ Examples:
be stored in /var/www/muclogs, and the local time will be used. Finally, the
top link will be the default \verb|<a href="/">Home</a>|.
\begin{verbatim}
{acl, admins, {user, "admin1", "example.org"}}.
{acl, admins, {user, "admin2", "example.net"}}.
{acl, admin, {user, "admin1", "example.org"}}.
{acl, admin, {user, "admin2", "example.net"}}.
{access, muc_log, [{allow, admins},
{access, muc_log, [{allow, admin},
{deny, all}]}.
{modules,
@@ -3969,6 +4051,8 @@ Options:
This example configuration shows how to enable the module and the web handler:
\begin{verbatim}
{hosts, ["localhost", "example.org", "example.com"]}.
{listen, [
...
{5281, ejabberd_http, [
@@ -3987,7 +4071,8 @@ This example configuration shows how to enable the module and the web handler:
]}.
\end{verbatim}
The users can visit this page: https://localhost:5281/register/
For example, the users of the host \term{example.org} can visit the page:
\ns{https://example.org:5281/register/}
It is important to include the last / character in the URL,
otherwise the subpages URL will be incorrect.
@@ -4116,8 +4201,9 @@ has a unique identification and the following parameters:
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.
\item[Displayed groups]
A list of groups that will be in the rosters of this group's members.
A group of other vhost can be identified with \term{groupid@vhost}
\end{description}
Examples:
@@ -4688,8 +4774,9 @@ The \modvcardldap{} module has
its own optional parameters. The first group of parameters has the same
meaning as the top-level LDAP parameters to set the authentication method:
\option{ldap\_servers}, \option{ldap\_port}, \option{ldap\_rootdn},
\option{ldap\_password}, \option{ldap\_base}, \option{ldap\_uids}, and
\option{ldap\_filter}. See section~\ref{ldapauth} for detailed information
\option{ldap\_password}, \option{ldap\_base}, \option{ldap\_uids},
\option{ldap\_deref\_aliases} and \option{ldap\_filter}.
See section~\ref{ldapauth} for detailed information
about these options. If one of these options is not set, \ejabberd{} will look
for the top-level option with the same name.
@@ -5262,11 +5349,11 @@ Example configurations:
administer the virtual host \jid{example.com}.
The account `\jid{reviewer@example.com}' can browse that vhost in read-only mode.
\begin{verbatim}
{acl, admins, {user, "admin", "example.net"}}.
{host_config, "example.com", [{acl, admins, {user, "admin", "example.com"}}]}.
{acl, admin, {user, "admin", "example.net"}}.
{host_config, "example.com", [{acl, admin, {user, "admin", "example.com"}}]}.
{host_config, "example.com", [{acl, viewers, {user, "reviewer", "example.com"}}]}.
{access, configure, [{allow, admins}]}.
{access, configure, [{allow, admin}]}.
{access, webadmin_view, [{allow, viewers}]}.
{hosts, ["example.org"]}.
@@ -5682,7 +5769,8 @@ domain.
\makesection{servicelb}{Service Load-Balancing}
\ind{component load-balancing}
\makesubsection{componentlb}{Components Load-Balancing}
% This section never had content, should it?
% \makesubsection{componentlb}{Components Load-Balancing}
\makesubsection{domainlb}{Domain Load-Balancing Algorithm}
\ind{options!domain\_balancing}
+4
View File
@@ -43,6 +43,7 @@ ejabberd 3.0.0 includes three major changes:
* Massive Hosting (abbreviated MH) is preliminary implemented in ejabberd,
but it is still incomplete, untested and undocumented.
New option static_modules.
This is a more detailed list of changes since ejabberd 2.1.x releases:
@@ -71,6 +72,7 @@ This is a more detailed list of changes since ejabberd 2.1.x releases:
* XEP support
- Removed support for the deprecated XEP-0018 Invisible Presence (EJAB-810)
- Update http_bind to XEP-0124 1.10 and XEP-0206 1.3
- mod_multicast: service for XEP-0033: Extended Stanza Addressing (EJAB-265)
- New ejabberd router for multicast packets (XEP-0033) (EJAB-329)
- ejabberd_c2s uses XEP-0033 if mod_multicast is enabled (EJAB-267)
@@ -93,7 +95,9 @@ This is a more detailed list of changes since ejabberd 2.1.x releases:
- Bugfix: external contacts were offline after unavailable presence (EJAB-943)
- Don't offer SASL auth before doing TLS if TLS is required
- Experimental support for GSSAPI auth, requires esasl library (EJAB-831)
- Replaced the full ejabberd_zlib into a simple exmpp_compress interface (EJAB-991)
- Use binaries instead of lists where possible (EJAB-17)
- Webadmin: provide list of registered pids/ports
- Windows ejabberdctl.cmd only handles up to 4 arguments (EJAB-1216)
- ejabberdctl: when starting, check it isn't already running
+7 -2
View File
@@ -64,10 +64,15 @@ ifeq (@pam@, pam)
INSTALL_EPAM=install -m 750 $(O_USER) epam $(PBINDIR)
endif
ifeq (@flash_hack@, true)
ERLC_FLAGS+=-DENABLE_FLASH_HACK
CPPFLAGS+=-DENABLE_FLASH_HACK
endif
prefix = @prefix@
exec_prefix = @exec_prefix@
SUBDIRS = stun @mod_pubsub@ @mod_muc@ @mod_proxy65@ @eldap@ @pam@ @web@ @tls@ @odbc@ @ejabberd_zlib@
SUBDIRS = stun @mod_pubsub@ @mod_muc@ @mod_proxy65@ @eldap@ @pam@ @web@ @tls@ @odbc@
ERLSHLIBS =
ERLBEHAVS = cyrsasl.erl gen_mod.erl p1_fsm.erl
SOURCES_ALL = $(wildcard *.erl)
@@ -158,7 +163,7 @@ mostlyclean-recursive maintainer-clean-recursive:
@ERLC@ -W $(EFLAGS) $*.erl
$(ERLSHLIBS): %.so: %.c
$(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) \
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(LIBS) \
$(subst ../,,$(subst .so,.c,$@)) \
$(ERLANG_LIBS) \
$(ERLANG_CFLAGS) \
-7
View File
@@ -69,9 +69,6 @@ release : build release_clean
mkdir $(SRC_DIR)\tls
copy tls\*.erl $(SRC_DIR)\tls
copy tls\*.c $(SRC_DIR)\tls
mkdir $(SRC_DIR)\ejabberd_zlib
copy ejabberd_zlib\*.erl $(SRC_DIR)\ejabberd_zlib
copy ejabberd_zlib\*.c $(SRC_DIR)\ejabberd_zlib
mkdir $(SRC_DIR)\web
copy web\*.erl $(SRC_DIR)\web
mkdir $(SRC_DIR)\odbc
@@ -103,8 +100,6 @@ all-recursive :
nmake -nologo -f Makefile.win32
cd ..\tls
nmake -nologo -f Makefile.win32
cd ..\ejabberd_zlib
nmake -nologo -f Makefile.win32
cd ..\web
nmake -nologo -f Makefile.win32
cd ..\odbc
@@ -142,8 +137,6 @@ clean-recursive :
nmake -nologo -f Makefile.win32 clean
cd ..\tls
nmake -nologo -f Makefile.win32 clean
cd ..\ejabberd_zlib
nmake -nologo -f Makefile.win32 clean
cd ..\web
nmake -nologo -f Makefile.win32 clean
cd ..\odbc
-36
View File
@@ -1,39 +1,3 @@
AC_DEFUN([AM_WITH_ZLIB],
[ AC_ARG_WITH(zlib,
[AC_HELP_STRING([--with-zlib=PREFIX], [prefix where zlib is installed])])
if test x"$ejabberd_zlib" != x; then
ZLIB_CFLAGS=
ZLIB_LIBS=
if test x"$with_zlib" != x; then
ZLIB_CFLAGS="-I$with_zlib/include"
ZLIB_LIBS="-L$with_zlib/lib"
fi
AC_CHECK_LIB(z, gzgets,
[ ZLIB_LIBS="$ZLIB_LIBS -lz"
zlib_found=yes ],
[ zlib_found=no ],
"$ZLIB_LIBS")
if test $zlib_found = no; then
AC_MSG_ERROR([Could not find development files of zlib library. Install them or disable `ejabberd_zlib' with: --disable-ejabberd_zlib])
fi
zlib_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $ZLIB_CFLAGS"
zlib_save_CPPFLAGS="$CFLAGS"
CPPFLAGS="$CPPFLAGS $ZLIB_CFLAGS"
AC_CHECK_HEADERS(zlib.h, , zlib_found=no)
if test $zlib_found = no; then
AC_MSG_ERROR([Could not find zlib.h. Install it or disable `ejabberd_zlib' with: --disable-ejabberd_zlib])
fi
CFLAGS="$zlib_save_CFLAGS"
CPPFLAGS="$zlib_save_CPPFLAGS"
AC_SUBST(ZLIB_CFLAGS)
AC_SUBST(ZLIB_LIBS)
fi
])
AC_DEFUN([AM_WITH_PAM],
[ AC_ARG_WITH(pam,
[AC_HELP_STRING([--with-pam=PREFIX], [prefix where PAM is installed])])
+10 -6
View File
@@ -36,10 +36,6 @@ AC_MOD_ENABLE(odbc, no)
AC_MOD_ENABLE(tls, yes)
AC_MOD_ENABLE(web, yes)
AC_MOD_ENABLE(ejabberd_zlib, yes)
#locating zlib
AM_WITH_ZLIB
AC_MOD_ENABLE(pam, no)
#locating PAM
AM_WITH_PAM
@@ -62,6 +58,15 @@ AC_ARG_ENABLE(roster_gateway_workaround,
esac],[roster_gateway_workaround=false])
AC_SUBST(roster_gateway_workaround)
AC_ARG_ENABLE(flash_hack,
[AC_HELP_STRING([--enable-flash-hack], [support Adobe Flash client XML (default: no)])],
[case "${enableval}" in
yes) flash_hack=true ;;
no) flash_hack=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-flash-hack) ;;
esac],[flash_hack=false])
AC_SUBST(flash_hack)
AC_ARG_ENABLE(mssql,
[AC_HELP_STRING([--enable-mssql], [use Microsoft SQL Server database (default: no, requires --enable-odbc)])],
[case "${enableval}" in
@@ -98,8 +103,7 @@ AC_CONFIG_FILES([Makefile
$make_web
stun/Makefile
$make_tls
$make_odbc
$make_ejabberd_zlib])
$make_odbc])
#openssl
AM_WITH_OPENSSL
# If ssl is kerberized it need krb5.h
+23 -12
View File
@@ -43,7 +43,7 @@
%% Require_Plain = bool().
%% Registry entry of a supported SASL mechanism.
-record(sasl_mechanism, {mechanism, module, require_plain_password}).
-record(sasl_mechanism, {mechanism, module, password_type}).
%% @type saslstate() = {sasl_state, Service, Myname, Realm, GetPassword, CheckPassword, CheckPasswordDigest, Mech_Mod, Mech_State}
%% Service = string()
@@ -76,6 +76,7 @@ start() ->
{keypos, #sasl_mechanism.mechanism}]),
cyrsasl_plain:start([]),
cyrsasl_digest:start([]),
cyrsasl_scram:start([]),
cyrsasl_anonymous:start([]),
maybe_try_start_gssapi(),
ok.
@@ -101,11 +102,11 @@ try_start_gssapi() ->
%% Module = atom()
%% Require_Plain = bool()
register_mechanism(Mechanism, Module, RequirePlainPassword) ->
register_mechanism(Mechanism, Module, PasswordType) ->
ets:insert(sasl_mechanism,
#sasl_mechanism{mechanism = Mechanism,
module = Module,
require_plain_password = RequirePlainPassword}).
password_type = PasswordType}).
% TODO use callbacks
%-include("ejabberd.hrl").
@@ -153,17 +154,20 @@ check_credentials(_State, Props) ->
%% Mechanism = string()
listmech(Host) ->
RequirePlainPassword = ejabberd_auth:plain_password_required(Host),
Mechs = ets:select(sasl_mechanism,
[{#sasl_mechanism{mechanism = '$1',
require_plain_password = '$2',
password_type = '$2',
_ = '_'},
if
RequirePlainPassword ->
[{'==', '$2', false}];
true ->
[]
case catch ejabberd_auth:store_type(Host) of
external ->
[{'==', '$2', plain}];
scram ->
[{'/=', '$2', digest}];
{'EXIT',{undef,[{Module,store_type,[]} | _]}} ->
?WARNING_MSG("~p doesn't implement the function store_type/0", [Module]),
[];
_Else ->
[]
end,
['$1']}]),
filter_anonymous(Host, Mechs).
@@ -228,7 +232,7 @@ server_start(State, Mech, ClientIn) ->
%% @spec (State, ClientIn) -> Ok | Continue | Error
%% State = saslstate()
%% ClientIn = string()
%% Ok = {ok, Props}
%% Ok = {ok, Props} | {ok, Props, ServerOut}
%% Props = [Prop]
%% Prop = [{Key, Value}]
%% Key = atom()
@@ -252,6 +256,13 @@ server_step(State, ClientIn) ->
{error, Error} ->
{error, Error}
end;
{ok, Props, ServerOut} ->
case check_credentials(State, Props) of
ok ->
{ok, Props, ServerOut};
{error, Error} ->
{error, Error}
end;
{continue, ServerOut, NewMechState} ->
{continue, ServerOut,
State#sasl_state{mech_state = NewMechState}};
+1 -1
View File
@@ -42,7 +42,7 @@
%% Opts = term()
start(_Opts) ->
cyrsasl:register_mechanism("ANONYMOUS", ?MODULE, false),
cyrsasl:register_mechanism("ANONYMOUS", ?MODULE, plain),
ok.
%% @spec () -> ok
+3 -3
View File
@@ -53,7 +53,7 @@
%% Opts = term()
start(_Opts) ->
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE, true).
cyrsasl:register_mechanism("DIGEST-MD5", ?MODULE, digest).
%% @spec () -> ok
@@ -91,7 +91,7 @@ mech_step(#state{step = 1, nonce = Nonce} = State, _) ->
mech_step(#state{step = 3, nonce = Nonce} = State, ClientIn) ->
case parse(ClientIn) of
bad ->
{error, 'bad-protocol'};
{error, 'malformed-request'};
KeyVals ->
DigestURI = proplists:get_value("digest-uri", KeyVals, ""),
UserName = proplists:get_value("username", KeyVals, ""),
@@ -136,7 +136,7 @@ mech_step(#state{step = 5,
{auth_module, AuthModule}]};
mech_step(A, B) ->
?DEBUG("SASL DIGEST: A ~p B ~p", [A,B]),
{error, 'bad-protocol'}.
{error, 'malformed-request'}.
%% @spec (S) -> [{Key, Value}] | bad
%% S = string()
+2 -2
View File
@@ -42,7 +42,7 @@
%% Opts = term()
start(_Opts) ->
cyrsasl:register_mechanism("PLAIN", ?MODULE, false),
cyrsasl:register_mechanism("PLAIN", ?MODULE, plain),
ok.
%% @spec () -> ok
@@ -77,7 +77,7 @@ mech_step(State, ClientIn) ->
{error, 'not-authorized', "", User}
end;
_ ->
{error, 'bad-protocol'}
{error, 'malformed-request'}
end.
prepare(ClientIn) ->
+191
View File
@@ -0,0 +1,191 @@
%%%----------------------------------------------------------------------
%%% File : cyrsasl_scram.erl
%%% Author : Stephen Röttger <stephen.roettger@googlemail.com>
%%% Purpose : SASL SCRAM authentication
%%% Created : 7 Aug 2011 by Stephen Röttger <stephen.roettger@googlemail.com>
%%%
%%%
%%% 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(cyrsasl_scram).
-author('stephen.roettger@googlemail.com').
-export([start/1,
stop/0,
mech_new/1,
mech_step/2]).
-include("ejabberd.hrl").
-include("cyrsasl.hrl").
-behaviour(cyrsasl).
-record(state, {step, stored_key, server_key, username, get_password, check_password,
auth_message, client_nonce, server_nonce}).
-define(SALT_LENGTH, 16).
-define(NONCE_LENGTH, 16).
start(_Opts) ->
cyrsasl:register_mechanism("SCRAM-SHA-1", ?MODULE, scram).
stop() ->
ok.
mech_new(#sasl_params{get_password=GetPassword}) ->
{ok, #state{step = 2, get_password = GetPassword}}.
mech_step(#state{step = 2} = State, ClientIn) ->
case string:tokens(ClientIn, ",") of
[CBind, UserNameAttribute, ClientNonceAttribute] when (CBind == "y") or (CBind == "n") ->
case parse_attribute(UserNameAttribute) of
{error, Reason} ->
{error, Reason};
{_, EscapedUserName} ->
case unescape_username(EscapedUserName) of
error ->
{error, 'malformed-request', "Error in username encoding", EscapedUserName};
UserName ->
case parse_attribute(ClientNonceAttribute) of
{$r, ClientNonce} ->
case (State#state.get_password)(UserName) of
{false, _} ->
{error, 'not-authorized', "", UserName};
{Ret, _AuthModule} ->
{StoredKey, ServerKey, Salt, IterationCount} = if
is_tuple(Ret) ->
Ret;
true ->
TempSalt = crypto:rand_bytes(?SALT_LENGTH),
SaltedPassword = scram:salted_password(Ret, TempSalt, ?SCRAM_DEFAULT_ITERATION_COUNT),
{scram:stored_key(scram:client_key(SaltedPassword)),
scram:server_key(SaltedPassword), TempSalt, ?SCRAM_DEFAULT_ITERATION_COUNT}
end,
ClientFirstMessageBare = string:substr(ClientIn, string:str(ClientIn, "n=")),
ServerNonce = base64:encode_to_string(crypto:rand_bytes(?NONCE_LENGTH)),
ServerFirstMessage = "r=" ++ ClientNonce ++ ServerNonce ++ "," ++
"s=" ++ base64:encode_to_string(Salt) ++ "," ++
"i=" ++ integer_to_list(IterationCount),
{continue,
ServerFirstMessage,
State#state{step = 4, stored_key = StoredKey, server_key = ServerKey,
auth_message = ClientFirstMessageBare ++ "," ++ ServerFirstMessage,
client_nonce = ClientNonce, server_nonce = ServerNonce, username = UserName}}
end;
_Else ->
{error, 'malformed-request'}
end
end
end;
_Else ->
{error, 'malformed-request'}
end;
mech_step(#state{step = 4} = State, ClientIn) ->
case string:tokens(ClientIn, ",") of
[GS2ChannelBindingAttribute, NonceAttribute, ClientProofAttribute] ->
case parse_attribute(GS2ChannelBindingAttribute) of
{$c, CVal} when (CVal == "biws") or (CVal == "eSws") ->
%% biws is base64 for n,, => channelbinding not supported
%% eSws is base64 for y,, => channelbinding supported by client only
Nonce = State#state.client_nonce ++ State#state.server_nonce,
case parse_attribute(NonceAttribute) of
{$r, CompareNonce} when CompareNonce == Nonce ->
case parse_attribute(ClientProofAttribute) of
{$p, ClientProofB64} ->
ClientProof = base64:decode(ClientProofB64),
AuthMessage = State#state.auth_message ++ "," ++ string:substr(ClientIn, 1, string:str(ClientIn, ",p=")-1),
ClientSignature = scram:client_signature(State#state.stored_key, AuthMessage),
ClientKey = scram:client_key(ClientProof, ClientSignature),
CompareStoredKey = scram:stored_key(ClientKey),
if CompareStoredKey == State#state.stored_key ->
ServerSignature = scram:server_signature(State#state.server_key, AuthMessage),
{ok, [{username, State#state.username}], "v=" ++ base64:encode_to_string(ServerSignature)};
true ->
{error, 'not-authorized', "", State#state.username}
end;
_Else ->
{error, 'malformed-request', "Bad protocol", State#state.username}
end;
{$r, _} ->
{error, 'malformed-request', "Bad nonce", State#state.username};
_Else ->
{error, 'malformed-request', "Bad protocol", State#state.username}
end;
_Else ->
{error, 'malformed-request', "Bad protocol", State#state.username}
end;
_Else ->
{error, 'malformed-request', "Bad protocol", State#state.username}
end.
parse_attribute(Attribute) ->
AttributeLen = string:len(Attribute),
if
AttributeLen >= 3 ->
SecondChar = lists:nth(2, Attribute),
case is_alpha(lists:nth(1, Attribute)) of
true ->
if
SecondChar == $= ->
String = string:substr(Attribute, 3),
{lists:nth(1, Attribute), String};
true ->
{error, 'malformed-request', "Second char not equal sign", ""}
end;
_Else ->
{error, 'malformed-request', "First char not a letter", ""}
end;
true ->
{error, 'malformed-request', "Attribute too short", ""}
end.
unescape_username("") ->
"";
unescape_username(EscapedUsername) ->
Pos = string:str(EscapedUsername, "="),
if
Pos == 0 ->
EscapedUsername;
true ->
Start = string:substr(EscapedUsername, 1, Pos-1),
End = string:substr(EscapedUsername, Pos),
EndLen = string:len(End),
if
EndLen < 3 ->
error;
true ->
case string:substr(End, 1, 3) of
"=2C" ->
Start ++ "," ++ unescape_username(string:substr(End, 4));
"=3D" ->
Start ++ "=" ++ unescape_username(string:substr(End, 4));
_Else ->
error
end
end
end.
is_alpha(Char) when Char >= $a, Char =< $z ->
true;
is_alpha(Char) when Char >= $A, Char =< $Z ->
true;
is_alpha(_) ->
false.
+2 -2
View File
@@ -1,6 +1,6 @@
{application, ejabberd,
[{description, "ejabberd"},
{vsn, "3.0.0-alpha-x"},
{vsn, "3.0.0-alpha-5"},
{modules, [acl,
adhoc,
configure,
@@ -8,6 +8,7 @@
cyrsasl,
cyrsasl_digest,
cyrsasl_plain,
cyrsasl_scram,
ejabberd_admin,
ejabberd_app,
ejabberd_auth_anonymous,
@@ -47,7 +48,6 @@
ejabberd_update,
ejabberd_web_admin,
ejabberd_web,
ejabberd_zlib,
ejd2odbc,
eldap,
eldap_filter,
+15
View File
@@ -295,6 +295,15 @@
%%
%%{host_config, "public.example.org", [{auth_method, [internal, anonymous]}]}.
%%
%% auth_password_format: Format of storing users passwords
%% The default format is plain text.
%% If you change to hashed scram, you can never go back to plain.
%% This option is only supported by the 'storage' auth_method.
%%
{auth_password_format, plain}.
%%{auth_password_format, scram}.
%%%. ==============
%%%' DATABASE SETUP
@@ -611,6 +620,12 @@
%%
{static_modules,
[
%% {mod_adhoc, []},
%% {mod_announce, []},
%% {mod_disco, []},
%% {mod_echo, [{prefix, "echo"}]},
%% {mod_last, []},
%% {mod_offline, []}
]}.
+10
View File
@@ -32,6 +32,13 @@
-define(CONFIG_PATH, "ejabberd.cfg").
-define(LOG_PATH, "ejabberd.log").
-ifdef(ENABLE_FLASH_HACK).
-define(FLASH_HACK, true).
-else.
-define(FLASH_HACK, false).
-endif.
-define(EJABBERD_URI, "http://www.process-one.net/en/ejabberd/").
-define(S2STIMEOUT, 600000).
@@ -40,6 +47,9 @@
%%-define(DBGFSM, true).
-record(scram, {storedkey, serverkey, salt, iterationcount}).
-define(SCRAM_DEFAULT_ITERATION_COUNT, 4096).
%% ---------------------------------
%% Logging mechanism
+4 -3
View File
@@ -312,14 +312,15 @@ update_list() ->
[atom_to_list(Beam) || Beam <- UpdatedBeams].
update("all") ->
[update_module(ModStr) || ModStr <- update_list()];
[update_module(ModStr) || ModStr <- update_list()],
{ok, []};
update(ModStr) ->
update_module(ModStr).
update_module(ModuleNameString) ->
ModuleName = list_to_atom(ModuleNameString),
case ejabberd_update:update([ModuleName]) of
{ok, Res} -> {ok, io_lib:format("Updated: ~p", [Res])};
{ok, _Res} -> {ok, []};
{error, Reason} -> {error, Reason}
end.
@@ -453,7 +454,7 @@ restore(Path) ->
%% Obsolete tables or tables created by module who are no longer used are not
%% restored and are ignored.
keep_tables() ->
lists:flatten([acl, passwd, config, local_config, disco_publish,
lists:flatten([acl, passwd, config, local_config,
keep_modules_tables()]).
%% Returns the list of modules tables in use, according to the list of actually
+47 -21
View File
@@ -49,17 +49,16 @@
is_user_exists_in_other_modules/3,
remove_user/2,
remove_user/3,
remove_host/1,
plain_password_required/1,
store_type/1,
entropy/1
]).
-export([start/1
,start_module/2
,stop_module/2
,stop/1
,start_modules/2
,start_method/2
,stop_method/2
,start_methods/2
,stop_modules/2
]).
-export([auth_modules/1]).
@@ -81,32 +80,58 @@ start() ->
lists:foreach(fun start/1, ?MYHOSTS).
start(Host) ->
ejabberd_hooks:add(remove_host, list_to_binary(Host), ?MODULE, remove_host, 150),
start_modules(Host, auth_modules(Host)).
stop(Host) ->
ejabberd_hooks:delete(remove_host, list_to_binary(Host), ?MODULE, remove_host, 150),
stop_modules(Host, auth_modules(Host)).
remove_host(HostB) when is_binary(HostB) ->
lists:foreach(
fun({Username, Host}) ->
ejabberd_auth:remove_user(Username, Host)
end,
ejabberd_auth:get_vh_registered_users(binary_to_list(HostB))).
start_modules(Host, Modules) when is_list(Modules) ->
lists:foreach(fun (M) -> start_module(Host, M) end, Modules).
start_module(Host, Module) when is_atom(Module) ->
lists:foreach(fun (M) -> start_modules(Host, M) end, Modules);
start_modules(Host, Module) when is_atom(Module) ->
Module:start(Host).
stop_module(Host, Module) when is_atom(Module) ->
stop_modules(Host, Modules) when is_list(Modules) ->
lists:foreach(fun (M) -> stop_modules(Host, M) end, Modules);
stop_modules(Host, Module) when is_atom(Module) ->
Module:stop(Host).
start_methods(Host, Methods) when is_list(Methods) ->
lists:foreach(fun (M) -> start_method(Host, M) end, Methods).
start_method(Host, Method) when is_atom(Method) ->
start_module(Host, module_name(Method)).
stop_method(Host, Method) when is_atom(Method) ->
stop_module(Host, module_name(Method)).
%% @spec (Server) -> bool()
%% Server = string()
%% This is only executed by ejabberd_c2s for non-SASL auth client
plain_password_required(Server) when is_list(Server) ->
lists:any(
fun(M) ->
M:plain_password_required()
end, auth_modules(Server)).
%% @spec (Server) -> bool()
%% Server = string()
store_type(Server) ->
lists:foldl(
fun(_, external) ->
external;
(M, scram) ->
case M:store_type() of
external ->
external;
_Else ->
scram
end;
(M, plain) ->
M:store_type()
end, plain, auth_modules(Server)).
%% @spec (User, Server, Password) -> bool()
%% User = string()
%% Server = string()
@@ -338,8 +363,10 @@ get_password_s(User, Server) when is_list(User), is_list(Server) ->
case get_password(User, Server) of
false ->
"";
Password ->
Password
Password when is_list(Password) ->
Password;
_ ->
""
end.
%% @spec (User, Server) -> {Password, AuthModule} | {false, none}
@@ -491,9 +518,8 @@ auth_modules() ->
%% Server = string()
%% @doc Return the list of authenticated modules for a given host.
auth_modules(Server) when is_list(Server) ->
LServer = exmpp_stringprep:nameprep(Server),
Method = ejabberd_config:get_local_option({auth_method, ejabberd:normalize_host(LServer)}),
auth_modules(Server) ->
Method = ejabberd_config:get_local_option({auth_method, Server}),
Methods = if
Method == undefined -> [];
is_list(Method) -> Method;
+4
View File
@@ -52,6 +52,7 @@
is_user_exists/2,
remove_user/2,
remove_user/3,
store_type/0,
plain_password_required/0]).
-include_lib("exmpp/include/exmpp.hrl").
@@ -360,6 +361,9 @@ remove_user(_User, _Server, _Password) ->
plain_password_required() ->
false.
store_type() ->
plain.
update_tables() ->
case catch mnesia:table_info(anonymous, local_content) of
false ->
+28 -4
View File
@@ -44,6 +44,7 @@
is_user_exists/2,
remove_user/2,
remove_user/3,
store_type/0,
plain_password_required/0
]).
@@ -94,6 +95,9 @@ check_cache_last_options(Server) ->
plain_password_required() ->
true.
store_type() ->
external.
%% @spec (User, Server, Password) -> bool()
%% User = string()
%% Server = string()
@@ -146,19 +150,39 @@ dirty_get_registered_users() ->
ejabberd_auth_storage:dirty_get_registered_users().
get_vh_registered_users(Server) ->
ejabberd_auth_storage:get_vh_registered_users(Server).
case check_cache_last_options(Server) of
cache ->
ejabberd_auth_storage:get_vh_registered_users(Server);
no_cache ->
[]
end.
get_vh_registered_users(Server, Data) ->
ejabberd_auth_storage:get_vh_registered_users(Server, Data).
case check_cache_last_options(Server) of
cache ->
ejabberd_auth_storage:get_vh_registered_users(Server, Data);
no_cache ->
[]
end.
%% @spec (Server) -> nil()
%% Server = string()
get_vh_registered_users_number(Server) ->
ejabberd_auth_storage:get_vh_registered_users_number(Server).
case check_cache_last_options(Server) of
cache ->
ejabberd_auth_storage:get_vh_registered_users_number(Server);
no_cache ->
0
end.
get_vh_registered_users_number(Server, Data) ->
ejabberd_auth_storage:get_vh_registered_users_number(Server, Data).
case check_cache_last_options(Server) of
cache ->
ejabberd_auth_storage:get_vh_registered_users_number(Server, Data);
no_cache ->
0
end.
%% @spec (User, Server) -> bool()
%% User = string()
+54 -23
View File
@@ -54,6 +54,7 @@
is_user_exists/2,
remove_user/2,
remove_user/3,
store_type/0,
plain_password_required/0
]).
@@ -73,6 +74,7 @@
uids,
ufilter,
lfilter, %% Local filter (performed by ejabberd, not LDAP)
deref_aliases,
dn_filter,
dn_filter_attrs
}).
@@ -99,25 +101,31 @@ handle_info(_Info, State) ->
start(Host) ->
?DEBUG("Starting ~p for ~p.", [?MODULE, Host]),
case ejabberd_config:get_host_option(Host, ldap_servers) of
undefined -> check_bad_config(Host);
{host, _Host} -> ok;
_ ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
Option = case ejabberd_config:get_host_option(Host, ldap_servers) of
undefined -> check_local_config(Host);
{host, _Host} -> nothing;
_ ->
{start, Host}
end,
case Option of
nothing -> ok;
{start, Host2} ->
Proc = gen_mod:get_module_proc(Host2, ?MODULE),
ChildSpec = {
Proc, {?MODULE, start_link, [Host]},
transient, 1000, worker, [?MODULE]
},
Proc, {?MODULE, start_link, [Host2]},
transient, 1000, worker, [?MODULE]
},
supervisor:start_child(ejabberd_sup, ChildSpec)
end.
check_bad_config(Host) ->
check_local_config(Host) ->
case ejabberd_config:get_local_option({ldap_servers, Host}) of
undefined ->
?ERROR_MSG("Can't start ~p for host ~p: missing ldap_servers configuration",
[?MODULE, Host]),
?ERROR_MSG("Can't start ~p for host ~p: missing ldap_servers "
"configuration", [?MODULE, Host]),
{error, bad_config};
_ -> ok
X when is_list(X)->
{start, Host}
end.
%% @spec (Host) -> term()
@@ -173,6 +181,9 @@ init(Host) ->
plain_password_required() ->
true.
store_type() ->
external.
%% @spec (User, Server, Password) -> bool()
%% User = string()
%% Server = string()
@@ -342,10 +353,12 @@ get_vh_registered_users_ldap(Server) ->
ResAttrs = result_attrs(State),
case eldap_filter:parse(SearchFilter) of
{ok, EldapFilter} ->
case eldap_pool:search(Eldap_ID, [{base, State#state.base},
{filter, EldapFilter},
{timeout, ?LDAP_SEARCH_TIMEOUT},
{attributes, ResAttrs}]) of
case eldap_pool:search(Eldap_ID,
[{base, State#state.base},
{filter, EldapFilter},
{timeout, ?LDAP_SEARCH_TIMEOUT},
{deref_aliases, State#state.deref_aliases},
{attributes, ResAttrs}]) of
#eldap_search_result{entries = Entries} ->
lists:flatmap(
fun(#eldap_entry{attributes = Attrs,
@@ -401,9 +414,11 @@ find_user_dn(User, Server, State) ->
UserFilter = build_ufilter(State, Server),
case eldap_filter:parse(UserFilter, [{"%u", User}]) of
{ok, Filter} ->
case eldap_pool:search(State#state.eldap_id, [{base, State#state.base},
{filter, Filter},
{attributes, ResAttrs}]) of
case eldap_pool:search(State#state.eldap_id,
[{base, State#state.base},
{filter, Filter},
{deref_aliases, State#state.deref_aliases},
{attributes, ResAttrs}]) of
#eldap_search_result{entries = [#eldap_entry{attributes = Attrs,
object_name = DN} | _]} ->
dn_filter(DN, Server, Attrs, State);
@@ -443,6 +458,7 @@ is_valid_dn(DN, Server, Attrs, State) ->
case eldap_pool:search(State#state.eldap_id,
[{base, State#state.base},
{filter, EldapFilter},
{deref_aliases, State#state.deref_aliases},
{attributes, ["dn"]}]) of
#eldap_search_result{entries = [_|_]} ->
DN;
@@ -512,6 +528,8 @@ parse_options(Host) ->
end,
LDAPEncrypt = ejabberd_config:get_local_option({ldap_encrypt, Host}),
LDAPTLSVerify = ejabberd_config:get_local_option({ldap_tls_verify, Host}),
LDAPTLSCAFile = ejabberd_config:get_local_option({ldap_tls_cacertfile, Host}),
LDAPTLSDepth = ejabberd_config:get_local_option({ldap_tls_depth, Host}),
LDAPPort = case ejabberd_config:get_local_option({ldap_port, Host}) of
undefined -> case LDAPEncrypt of
tls -> ?LDAPS_PORT;
@@ -530,11 +548,15 @@ parse_options(Host) ->
end,
UIDs = case ejabberd_config:get_local_option({ldap_uids, Host}) of
undefined -> [{"uid", "%u"}];
UI -> UI
UI -> eldap_utils:uids_domain_subst(Host, UI)
end,
SubFilter = lists:flatten(eldap_utils:generate_subfilter(UIDs)),
UserFilter = case ejabberd_config:get_local_option({ldap_filter, Host}) of
undefined -> "";
F -> F
"" -> "";
F ->
eldap_utils:check_filter(F),
"(&" ++ SubFilter ++ F ++ ")"
end,
LDAPBase = ejabberd_config:get_local_option({ldap_base, Host}),
{DNFilter, DNFilterAttrs} =
@@ -546,7 +568,13 @@ parse_options(Host) ->
{DNF, DNFA} ->
{DNF, DNFA}
end,
LocalFilter = ejabberd_config:get_local_option({ldap_local_filter, Host}),
eldap_utils:check_filter(DNFilter),
LocalFilter = ejabberd_config:get_local_option({ldap_local_filter, Host}),
DerefAliases = case ejabberd_config:get_local_option(
{ldap_deref_aliases, Host}) of
undefined -> never;
Val -> Val
end,
#state{host = Host,
eldap_id = Eldap_ID,
bind_eldap_id = Bind_Eldap_ID,
@@ -554,13 +582,16 @@ parse_options(Host) ->
backups = LDAPBackups,
port = LDAPPort,
tls_options = [{encrypt, LDAPEncrypt},
{tls_verify, LDAPTLSVerify}],
{tls_verify, LDAPTLSVerify},
{tls_cacertfile, LDAPTLSCAFile},
{tls_depth, LDAPTLSDepth}],
dn = RootDN,
password = Password,
base = LDAPBase,
uids = UIDs,
ufilter = UserFilter,
lfilter = LocalFilter,
deref_aliases = DerefAliases,
dn_filter = DNFilter,
dn_filter_attrs = DNFilterAttrs
}.
+4
View File
@@ -40,6 +40,7 @@
is_user_exists/2,
remove_user/2,
remove_user/3,
store_type/0,
plain_password_required/0
]).
@@ -171,6 +172,9 @@ remove_user(_User, _Server, _Password) ->
plain_password_required() ->
true.
store_type() ->
external.
%%====================================================================
%% Internal functions
%%====================================================================
+187 -19
View File
@@ -44,10 +44,27 @@
%%% user_host = {Username::string(), Host::string()}
%%% password = string()
%%%
%%% 3.0.0-beta / mnesia / passwd
%%% user_host = {Username::string(), Host::string()}
%%% password = string()
%%% storedkey = base64 binary()
%%% serverkey = base64 binary()
%%% iterationcount = integer()
%%% salt = base64 binary()
%%%
%%% 3.0.0-alpha / odbc / passwd
%%% user = varchar150
%%% host = varchar150
%%% password = text
%%%
%%% 3.0.0-beta / odbc / passwd
%%% user = varchar150
%%% host = varchar150
%%% password = base64 text
%%% storedkey = base64 text
%%% serverkey = base64 text
%%% iterationcount = integer
%%% salt = base64 text
-module(ejabberd_auth_storage).
-author('alexey@process-one.net').
@@ -69,14 +86,17 @@
is_user_exists/2,
remove_user/2,
remove_user/3,
store_type/0,
plain_password_required/0
]).
-include("ejabberd.hrl").
-record(passwd, {user_host, password}).
-record(passwd, {user_host, password, storedkey, serverkey, salt, iterationcount}).
-record(reg_users_counter, {vhost, count}).
-define(SALT_LENGTH, 16).
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
@@ -95,9 +115,14 @@ start(Host) ->
[{odbc_host, Host},
{disc_copies, [node()]},
{attributes, record_info(fields, passwd)},
{types, [{user_host, {text, text}}]}
{types, [{user_host, {text, text}},
{storedkey, binary},
{serverkey, binary},
{salt, binary},
{iterationcount, int}]}
]),
update_table(Host, Backend),
maybe_scram_passwords(HostB),
mnesia:create_table(reg_users_counter,
[{ram_copies, [node()]},
{attributes, record_info(fields, reg_users_counter)}]),
@@ -120,7 +145,16 @@ update_reg_users_counter_table(Server) ->
%% @spec () -> bool()
plain_password_required() ->
false.
case is_option_scram(?MYNAME) of
false -> false;
true -> true
end.
store_type() ->
case is_option_scram(?MYNAME) of
false -> plain; %% allows: PLAIN DIGEST-MD5 SCRAM
true -> scram %% allows: PLAIN SCRAM
end.
%% @spec (User, Server, Password) -> bool()
%% User = string()
@@ -132,6 +166,8 @@ check_password(User, Server, Password) ->
LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer},
case catch gen_storage:dirty_read(LServer, {passwd, US}) of
[#passwd{password = ""} = Passwd] ->
is_password_scram_valid(Password, Passwd);
[#passwd{password = Password}] ->
Password /= "";
_ ->
@@ -150,6 +186,19 @@ check_password(User, Server, Password, Digest, DigestGen) ->
LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer},
case catch gen_storage:dirty_read(LServer, {passwd, US}) of
[#passwd{password = ""} = Passwd] ->
Storedkey = base64:decode(Passwd#passwd.storedkey),
DigRes = if
Digest /= "" ->
Digest == DigestGen(Storedkey);
true ->
false
end,
if DigRes ->
true;
true ->
(Storedkey == Password) and (Password /= "")
end;
[#passwd{password = Passwd}] ->
DigRes = if
Digest /= "" ->
@@ -182,9 +231,11 @@ set_password(User, Server, Password) ->
US ->
%% TODO: why is this a transaction?
F = fun() ->
gen_storage:write(LServer,
#passwd{user_host = US,
password = Password})
Passwd = case is_option_scram(LServer) and (Password /= "") of
true -> password_to_scram(Password, #passwd{user_host=US});
false -> #passwd{user_host = US, password = Password}
end,
gen_storage:write(LServer, Passwd)
end,
{atomic, ok} = gen_storage:transaction(LServer, passwd, F),
ok
@@ -207,9 +258,11 @@ try_register(User, Server, Password) ->
F = fun() ->
case gen_storage:read(LServer, {passwd, US}) of
[] ->
gen_storage:write(LServer,
#passwd{user_host = US,
password = Password}),
Passwd = case is_option_scram(LServer) and (Password /= "") of
true -> password_to_scram(Password, #passwd{user_host=US});
false -> #passwd{user_host = US, password = Password}
end,
gen_storage:write(LServer, Passwd),
mnesia:dirty_update_counter(
reg_users_counter,
exmpp_jid:prep_domain(exmpp_jid:parse(Server)), 1),
@@ -228,8 +281,12 @@ try_register(User, Server, Password) ->
%% @doc Get all registered users in Mnesia.
dirty_get_registered_users() ->
%% TODO:
exit(not_implemented).
lists:foldl(
fun(HostB, Res) ->
get_vh_registered_users(binary_to_list(HostB)) ++ Res
end,
[],
gen_storage:all_table_hosts(passwd)).
%% @spec (Server) -> [{LUser, LServer}]
%% Server = string()
@@ -348,9 +405,14 @@ get_password(User, Server) ->
LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer},
case catch gen_storage:dirty_read(LServer, passwd, US) of
[#passwd{password = Password}] ->
[#passwd{password = ""} = Passwd] ->
{base64:decode(Passwd#passwd.storedkey),
base64:decode(Passwd#passwd.serverkey),
base64:decode(Passwd#passwd.salt),
Passwd#passwd.iterationcount};
[#passwd{password = Password}] ->
Password;
_ ->
_ ->
false
end
catch
@@ -369,8 +431,8 @@ get_password_s(User, Server) ->
LServer = exmpp_stringprep:nameprep(Server),
US = {LUser, LServer},
case catch gen_storage:dirty_read(LServer, passwd, US) of
[#passwd{password = Password}] ->
Password;
[#passwd{password = Password}] ->
Password;
_ ->
[]
end
@@ -437,13 +499,21 @@ remove_user(User, Server, Password) ->
US = {LUser, LServer},
F = fun() ->
case gen_storage:read(LServer, {passwd, US}) of
[#passwd{password = ""} = Passwd] ->
case is_password_scram_valid(Password, Passwd) of
true ->
gen_storage:delete(LServer, {passwd, US}),
mnesia:dirty_update_counter(reg_users_counter,
LServer, -1),
ok;
false ->
not_allowed
end;
[#passwd{password = Password}] ->
gen_storage:delete(LServer, {passwd, US}),
mnesia:dirty_update_counter(reg_users_counter,
exmpp_jid:prep_domain(exmpp_jid:parse(Server)), -1),
ok;
[_] ->
not_allowed;
_ ->
not_exists
end
@@ -459,13 +529,111 @@ remove_user(User, Server, Password) ->
bad_request
end.
%%%
%%% SCRAM
%%%
%% The passwords are stored scrammed in the table either if the option says so,
%% or if at least the first password is empty.
action_password_format(Host) ->
OptionScram = is_option_scram(Host),
case {OptionScram, get_format_first_element(Host)} of
{true, scram} -> scram;
{true, any} -> scram;
{true, plain} -> must_scram;
{false, plain} -> plain;
{false, any} -> plain;
{false, scram} ->
set_option_password_format(scram),
forced_scram
end.
get_format_first_element(HostB) ->
case gen_storage:dirty_select(HostB, passwd, []) of
[] -> any;
[#passwd{password = ""} | _] -> scram;
[#passwd{} | _] -> plain
end.
is_option_scram(Host) when is_list(Host) ->
scram == ejabberd_config:get_local_option({auth_password_format, Host}).
set_option_password_format(Value) ->
?ERROR_MSG("Some passwords are stored in the database as SCRAM bits, "
"but the option 'auth_password_format' is not configured 'scram'. "
"The option will now be considered to be 'scram'.", []),
ejabberd_config:add_local_option({auth_password_format, ?MYNAME}, Value).
maybe_scram_passwords(HostB) ->
case action_password_format(binary_to_list(HostB)) of
must_scram -> scram_passwords(HostB);
_ -> ok
end.
scram_passwords(HostB) ->
Backend =
case ejabberd_config:get_local_option({auth_storage, binary_to_list(HostB)}) of
undefined -> mnesia;
B -> B
end,
scram_passwords(HostB, Backend).
scram_passwords(HostB, mnesia) ->
?INFO_MSG("Converting the passwords stored in mnesia for host ~p into SCRAM bits", [HostB]),
gen_storage_migration:migrate_mnesia(
HostB, passwd,
[{passwd, [user_host, password, storedkey, serverkey, salt, iterationcount],
fun(#passwd{password = Password} = Passwd) ->
password_to_scram(Password, Passwd)
end}]);
scram_passwords(HostB, odbc) ->
?INFO_MSG("Converting the passwords stored in odbc for host ~p into SCRAM bits", [HostB]),
gen_storage_migration:migrate_odbc(
HostB, [passwd],
[{"passwd", ["user", "host", "password", "storedkey", "serverkey", "salt", "iterationcount"],
fun(_, User, Host2, Password, _Storedkey, _Serverkey, _Iterationcount, _Salt) ->
password_to_scram(Password, #passwd{user_host = {User, Host2}})
end}]).
password_to_scram(Password, Passwd) ->
password_to_scram(Password, Passwd, ?SCRAM_DEFAULT_ITERATION_COUNT).
password_to_scram(Password, Passwd, IterationCount) ->
Salt = crypto:rand_bytes(?SALT_LENGTH),
SaltedPassword = scram:salted_password(Password, Salt, IterationCount),
StoredKey = scram:stored_key(scram:client_key(SaltedPassword)),
ServerKey = scram:server_key(SaltedPassword),
Passwd#passwd{password = "",
storedkey = base64:encode(StoredKey),
salt = base64:encode(Salt),
iterationcount = IterationCount,
serverkey = base64:encode(ServerKey)}.
is_password_scram_valid(Password, Passwd) ->
IterationCount = Passwd#passwd.iterationcount,
Salt = base64:decode(Passwd#passwd.salt),
SaltedPassword = scram:salted_password(Password, Salt, IterationCount),
StoredKey = scram:stored_key(scram:client_key(SaltedPassword)),
(base64:decode(Passwd#passwd.storedkey) == StoredKey).
update_table(Host, mnesia) ->
gen_storage_migration:migrate_mnesia(
Host, passwd,
[{passwd, [us, password],
fun({passwd, {User, _Host}, Password}) ->
#passwd{user_host = {User, Host},
password = Password}
case is_list(Password) of
true ->
#passwd{user_host = {User, Host},
password = Password};
false ->
#passwd{user_host = {User, Host},
password = "",
storedkey = Password#scram.storedkey,
serverkey = Password#scram.serverkey,
salt = Password#scram.salt,
iterationcount = Password#scram.iterationcount}
end
end}]);
update_table(Host, odbc) ->
gen_storage_migration:migrate_odbc(
+129 -18
View File
@@ -114,7 +114,8 @@
ip,
aux_fields = [],
fsm_limit_opts,
lang}).
lang,
flash_connection = false}).
%-define(DBGFSM, true).
@@ -151,6 +152,14 @@
"id='~s' from='~s'~s~s>"
).
-define(FLASH_STREAM_HEADER,
"<?xml version='1.0'?>"
"<flash:stream xmlns='jabber:client' "
"xmlns:stream='http://etherx.jabber.org/streams' "
"id='~s' from='~s'~s~s>"
).
-define(NS_FLASH_STREAM, "http://www.jabber.com/streams/flash").
-define(INVALID_NS_ERR, exmpp_stream:error('invalid-namespace')).
-define(INVALID_XML_ERR, exmpp_stream:error('xml-not-well-formed')).
-define(HOST_UNKNOWN_ERR, exmpp_stream:error('host-unknown')).
@@ -344,7 +353,8 @@ get_subscribed(FsmRef) ->
%% {stop, Reason, NewStateData}
%%----------------------------------------------------------------------
wait_for_stream({xmlstreamstart, #xmlel{ns = NS} = Opening}, StateData) ->
wait_for_stream({xmlstreamstart, #xmlel{ns = NS, name = Name} = Opening},
StateData) ->
DefaultLang = case ?MYLANG of
undefined ->
"en";
@@ -353,11 +363,19 @@ wait_for_stream({xmlstreamstart, #xmlel{ns = NS} = Opening}, StateData) ->
end,
Header = exmpp_stream:opening_reply(Opening,
StateData#state.streamid, DefaultLang),
case NS of
?NS_XMPP ->
case {NS, exmpp_xml:is_ns_declared_here(Opening, ?NS_FLASH_STREAM),
?FLASH_HACK,
StateData#state.flash_connection} of
{_, true, true, false} ->
%% Flash client connecting - attention!
%% Some of them don't provide an xmlns:stream attribute -
%% compensate for that.
wait_for_stream({xmlstreamstart, Opening#xmlel{ns = ?NS_XMPP}},
StateData#state{flash_connection = true});
{?NS_XMPP, _, _, _} ->
ServerB = exmpp_stringprep:nameprep(
exmpp_stream:get_receiving_entity(Opening)),
Server = binary_to_list(ServerB),
Server = binary_to_list(ServerB),
case ?IS_MY_HOST(Server) of
true ->
Lang = case exmpp_stream:get_lang(Opening) of
@@ -518,11 +536,17 @@ wait_for_stream({xmlstreamstart, #xmlel{ns = NS} = Opening}, StateData) ->
send_trailer(StateData),
{stop, normal, StateData}
end;
_ ->
send_header(StateData, ?MYNAME, "", DefaultLang),
send_element(StateData, ?INVALID_NS_ERR),
send_trailer(StateData),
{stop, normal, StateData}
_ ->
case Name of
"policy-file-request" ->
send_text(StateData, flash_policy_string()),
{stop, normal, StateData};
_ ->
send_header(StateData, ?MYNAME, "", DefaultLang),
send_element(StateData, ?INVALID_NS_ERR),
send_trailer(StateData),
{stop, normal, StateData}
end
end;
wait_for_stream(timeout, StateData) ->
@@ -764,9 +788,11 @@ wait_for_feature_request({xmlstreamelement, #xmlel{ns = NS, name = Name} = El},
fsm_next_state(wait_for_feature_request, StateData);
<<"zlib">> ->
Socket = StateData#state.socket,
Compressed = exmpp_xml:node_to_list(
exmpp_server_compression:compressed(), [?DEFAULT_NS], ?PREFIXED_NS),
ZlibSocket = (StateData#state.sockmod):compress(
Socket,
exmpp_server_compression:compressed()),
Compressed),
fsm_next_state(wait_for_stream,
StateData#state{socket = ZlibSocket,
streamid = new_id()
@@ -820,6 +846,21 @@ wait_for_sasl_response({xmlstreamelement, #xmlel{ns = NS, name = Name} = El},
StateData#state.socket),
send_element(StateData, exmpp_server_sasl:success()),
U = proplists:get_value(username, Props),
AuthModule = proplists:get_value(auth_module, Props),
?INFO_MSG("(~w) Accepted authentication for ~s by ~s",
[StateData#state.socket, U, AuthModule]),
fsm_next_state(wait_for_stream,
StateData#state{
streamid = new_id(),
authenticated = true,
auth_module = AuthModule,
user = list_to_binary(U)});
{ok, Props, ServerOut} ->
catch (StateData#state.sockmod):reset_stream(
StateData#state.socket),
send_element(StateData, exmpp_server_sasl:success(ServerOut)),
U = proplists:get_value(username, Props),
AuthModule = proplists:get_value(auth_module, Props),
?INFO_MSG("(~w) Accepted authentication for ~s by ~s",
[StateData#state.socket, U, AuthModule]),
@@ -868,6 +909,29 @@ wait_for_sasl_response(closed, StateData) ->
{stop, normal, StateData}.
resource_conflict_action(U, S, R) ->
OptionRaw = case ejabberd_sm:is_existing_resource(U, S, R) of
true ->
ejabberd_config:get_local_option({resource_conflict,binary_to_list(S)});
false ->
acceptnew
end,
Option = case OptionRaw of
setresource -> setresource;
closeold -> acceptnew; %% ejabberd_sm will close old session
closenew -> closenew;
acceptnew -> acceptnew;
_ -> acceptnew %% default ejabberd behavior
end,
case Option of
acceptnew ->
{accept_resource, R};
closenew ->
closenew;
setresource ->
Rnew = lists:concat([randoms:get_string() | tuple_to_list(now())]),
{accept_resource, Rnew}
end.
wait_for_bind({xmlstreamelement, El}, StateData) ->
try
@@ -884,11 +948,17 @@ wait_for_bind({xmlstreamelement, El}, StateData) ->
%%send_element(StateData,
%% exmpp_stream:features([exmpp_server_session:feature()
%% | RosterVersioningFeature])),
JID = exmpp_jid:make(StateData#state.user, StateData#state.server, R),
Res = exmpp_server_binding:bind(El, JID),
send_element(StateData, Res),
fsm_next_state(wait_for_session,
case resource_conflict_action(StateData#state.user, StateData#state.server, list_to_binary(R)) of
closenew ->
send_element(StateData, ?SERRT_CONFLICT), %% (Lang, "Replaced by new connection")),
fsm_next_state(wait_for_bind, StateData);
{accept_resource, R2} ->
JID = exmpp_jid:make(StateData#state.user, StateData#state.server, R2),
Res = exmpp_server_binding:bind(El, JID),
send_element(StateData, Res),
fsm_next_state(wait_for_session,
StateData#state{resource = exmpp_jid:resource(JID), jid = JID})
end
catch
throw:{stringprep, resourceprep, _, _} ->
Err = exmpp_server_binding:error(El, 'bad-request'),
@@ -1565,8 +1635,15 @@ change_shaper(StateData, JID) ->
send_text(StateData, Text) when StateData#state.xml_socket ->
?DEBUG("Send Text on stream = ~p", [lists:flatten(Text)]),
Text1 =
if ?FLASH_HACK and StateData#state.flash_connection ->
%% send a null byte after each stanza to Flash clients
[Text, 0];
true ->
Text
end,
(StateData#state.sockmod):send_xml(StateData#state.socket,
{xmlstreamraw, Text});
{xmlstreamraw, Text1});
send_text(StateData, Text) ->
?DEBUG("Send XML on stream = ~s", [Text]),
(StateData#state.sockmod):send(StateData#state.socket, Text).
@@ -1579,6 +1656,15 @@ send_element(StateData, El) when StateData#state.xml_socket ->
send_element(StateData, El) ->
send_text(StateData, exmpp_stanza:to_iolist(El)).
send_header(StateData,Server, Version, Lang)
when StateData#state.flash_connection ->
Header = io_lib:format(?FLASH_STREAM_HEADER,
[StateData#state.streamid,
Server,
Version,
Lang]),
send_text(StateData, Header);
send_header(StateData, Server, Version, Lang)
when StateData#state.xml_socket ->
VersionAttr =
@@ -1781,9 +1867,9 @@ presence_update(From, Packet, StateData) ->
update_priority(NewPriority, Packet, StateData),
FromUnavail = (StateData#state.pres_last == undefined),
?DEBUG("from unavail = ~p~n", [FromUnavail]),
NewStateData = StateData#state{pres_last = Packet,
pres_timestamp = Timestamp},
NewState =
NewStateData = StateData#state{pres_last = Packet,
pres_timestamp = Timestamp},
if
FromUnavail ->
ejabberd_hooks:run(user_available_hook,
@@ -2361,3 +2447,28 @@ pack_string(String, Pack) ->
none ->
{String, gb_trees:insert(String, String, Pack)}
end.
%% @spec () -> string()
%% @doc Build the content of a Flash policy file.
%% It specifies as domain "*".
%% It specifies as to-ports the ports that serve ejabberd_c2s.
flash_policy_string() ->
Listen = ejabberd_config:get_local_option(listen),
ClientPortsDeep = ["," ++ integer_to_list(Port)
|| {{Port,_,_}, ejabberd_c2s, _Opts} <- Listen],
%% NOTE: The function string:join/2 was introduced in Erlang/OTP R12B-0
%% so it can't be used yet in ejabberd.
ToPortsString = case lists:flatten(ClientPortsDeep) of
[$, | Tail] -> Tail;
_ -> []
end,
"<?xml version=\"1.0\"?>\n"
"<!DOCTYPE cross-domain-policy SYSTEM "
"\"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">\n"
"<cross-domain-policy>\n"
" <allow-access-from domain=\"*\" to-ports=\""
++ ToPortsString ++
"\"/>\n"
"</cross-domain-policy>\n\0".
+40 -42
View File
@@ -119,27 +119,14 @@ delete_dist(Hook, Host, Node, Module, Function, Seq) ->
%% @doc Run the calls of this hook in order, don't care about function results.
%% If a call returns stop, no more calls are performed.
run(Hook, Args) ->
runx(Hook, global, Args).
run(Hook, global, Args).
run(Hook, Host, Args) when is_binary(Host) ->
case runx(Hook, Host, Args) of
stop -> stop;
_ -> runx(Hook, global, Args)
end;
run(Hook, Host, Args) when Host == global ->
runx(Hook, Host, Args).
runx(Hook, Host, Args) when is_binary(Host) orelse is_atom(Host) ->
case ets:lookup(hooks, {Hook, ejabberd:normalize_host(Host)}) of
[{_, Ls}] ->
run1(Ls, Hook, Args);
[] ->
case ets:lookup(hooks, {Hook, global}) of
[{_, Ls}] ->
run1(Ls, Hook, Args);
[] ->
ok
end
run(Hook, Host, Args) ->
case get_registered_hooks_for(Hook, Host) of
[] ->
ok;
Ls ->
run1(Ls, Hook, Args)
end.
%% @spec (Hook::atom(), Val, Args) -> Val | stopped | NewVal
@@ -149,29 +136,16 @@ runx(Hook, Host, Args) when is_binary(Host) orelse is_atom(Host) ->
%% If a call returns 'stop', no more calls are performed and 'stopped' is returned.
%% If a call returns {stopped, NewVal}, no more calls are performed and NewVal is returned.
run_fold(Hook, Val, Args) ->
run_foldx(Hook, global, Val, Args).
run_fold(Hook, global, Val, Args).
%% @spec (Hook::atom(), Host, Val, Args) -> Val | stopped | NewVal
%% Host = global | binary()
run_fold(Hook, Host, Val, Args) when is_binary(Host) ->
case run_foldx(Hook, Host, Val, Args) of
stopped -> stopped;
Val2 -> run_foldx(Hook, global, Val2, Args)
end;
run_fold(Hook, Host, Val, Args) when Host == global ->
run_foldx(Hook, Host, Val, Args).
run_foldx(Hook, Host, Val, Args) ->
case ets:lookup(hooks, {Hook, ejabberd:normalize_host(Host)}) of
[{_, Ls}] ->
run_fold1(Ls, Hook, Val, Args);
[] ->
case ets:lookup(hooks, {Hook, global}) of
[{_, Ls}] ->
run_fold1(Ls, Hook, Val, Args);
[] ->
Val
end
run_fold(Hook, Host, Val, Args) ->
case get_registered_hooks_for(Hook, Host) of
[] ->
Val;
Ls ->
run_fold1(Ls, Hook, Val, Args)
end.
%%%----------------------------------------------------------------------
@@ -240,7 +214,10 @@ handle_call({delete, Hook, Host, Module, Function, Seq}, _From, State) ->
Reply = case ets:lookup(hooks, {Hook, NHost}) of
[{_, Ls}] ->
NewLs = lists:delete({Seq, Module, Function}, Ls),
ets:insert(hooks, {{Hook, NHost}, NewLs}),
case NewLs of
[] -> ets:delete(hooks, {Hook, NHost});
_ -> ets:insert(hooks, {{Hook, NHost}, NewLs})
end,
ok;
[] ->
ok
@@ -250,7 +227,10 @@ handle_call({delete, Hook, Host, Node, Module, Function, Seq}, _From, State) ->
Reply = case ets:lookup(hooks, {Hook, Host}) of
[{_, Ls}] ->
NewLs = lists:delete({Seq, Node, Module, Function}, Ls),
ets:insert(hooks, {{Hook, Host}, NewLs}),
case NewLs of
[] -> ets:delete(hooks, {Hook, Host});
_ -> ets:insert(hooks, {{Hook, Host}, NewLs})
end,
ok;
[] ->
ok
@@ -374,3 +354,21 @@ run_fold1([{_Seq, Module, Function} | Ls], Hook, Val, Args) ->
NewVal ->
run_fold1(Ls, Hook, NewVal, Args)
end.
get_registered_hooks_for(Hook, global) ->
case ets:lookup(hooks, {Hook, global}) of
[{_, Ls}] ->
Ls;
[] ->
[]
end;
get_registered_hooks_for(Hook, Host) ->
GlobalHooks = get_registered_hooks_for(Hook, global),
HostHooks = case ets:lookup(hooks, {Hook, ejabberd:normalize_host(Host)}) of
[{_, Ls}] ->
Ls;
[] ->
[]
end,
lists:umerge(GlobalHooks, HostHooks).
+11 -25
View File
@@ -7,6 +7,8 @@
%%% Database schema (version / storage / table)
%%%
%%% The table 'hosts' keeps the dynamic hosts (not defined in ejabberd.cfg)
%%%
%%% 3.0.0-alpha-x / mnesia / hosts
%%% host = string()
%%% clusterid = integer()
@@ -99,13 +101,18 @@ update_host_conf(Host, Config) when is_list(Host), is_list(Config) ->
case registered(Host) of
false -> {error, host_process_not_registered};
true ->
remove(Host),
remove_host_info(Host),
?MODULE:register(Host, Config)
end.
%% Removes a vhost from the system,
%% XXX deleting all ODBC data.
remove(Host) when is_list(Host) ->
HostB = list_to_binary(Host),
ejabberd_hooks:run(remove_host, HostB, [HostB]),
remove_host_info(Host).
remove_host_info(Host) ->
true = exmpp_stringprep:is_node(Host),
ID = get_clusterid(),
gen_storage:dirty_delete_where(
@@ -213,11 +220,6 @@ handle_info(timeout, State = #state{state=wait_odbc,backend=Backend,odbc_wait_ti
{clusterid, int},
{config, text}]}]),
%% Now let's add the default vhost: "localhost"
gen_storage:dirty_write(HostB, #hosts{host = Host,
clusterid = 1,
config = ""}),
self() ! reload,
timer:send_interval(?RELOAD_INTERVAL, reload),
{noreply, State#state{state=running,odbc_wait_time=0}};
@@ -349,10 +351,7 @@ start_host(Host) when is_list(Host) ->
gen_mod:start_module(Host, Module, Args)
end, Modules)
end,
case auth_method(Host) of
{host_method, HostMethod} ->
ejabberd_auth:start_method(Host, HostMethod)
end,
ejabberd_auth:start(Host),
ok.
@@ -369,10 +368,7 @@ stop_host(Host) when is_list(Host) ->
lists:foreach(fun(Module) ->
gen_mod:stop_module_keep_config(Host, Module)
end, gen_mod:loaded_modules(Host)),
case auth_method(Host) of
{host_method, Method} ->
ejabberd_auth:stop_method(Host, Method)
end.
ejabberd_auth:stop(Host).
%% Get the current vhost list from a variety of sources (ODBC, internal)
get_hosts(ejabberd) -> ?MYHOSTS;
@@ -420,16 +416,6 @@ diff_hosts(NewHosts, OldHosts) ->
AddHosts = NewHosts -- OldHosts,
{AddHosts,RemoveHosts}.
%% XXX - this should be part of auth, not hosts.
auth_method(Host) ->
case ejabberd_config:get_host_option(Host, auth_method) of
undefined ->
[Default] = ejabberd_config:get_host_option(global, auth_method),
{host_method, Default};
Other ->
{host_method, Other}
end.
configure_static_hosts() ->
?DEBUG("Node startup - configuring hosts: ~p", [?MYHOSTS]),
%% Add a null configuration for all MYHOSTS - this ensures
+38 -8
View File
@@ -159,21 +159,23 @@ process_element(El,State) ->
add_user(El, Domain) ->
User = exmpp_xml:get_attribute(El,<<"name">>,none),
PasswordFormat = exmpp_xml:get_attribute(El,<<"password-format">>,<<"plaintext">>),
Password = exmpp_xml:get_attribute(El,<<"password">>,none),
add_user(El, Domain, User, Password).
add_user(El, Domain, User, PasswordFormat, Password).
%% @spec (El::xmlel(), Domain::string(), User::binary(), Password::binary() | none)
%% @spec (El::xmlel(), Domain::string(), User::binary(), PasswordFormat::binary(), Password::binary() | none)
%% -> ok | {error, ErrorText::string()}
%% @doc Add a new user to the database.
%% If user already exists, it will be only updated.
add_user(El, Domain, User, none) ->
add_user(El, Domain, User, <<"plaintext">>, none) ->
io:format("Account ~s@~s will not be created, updating it...~n",
[User, Domain]),
io:format(""),
populate_user_with_elements(El, Domain, User),
ok;
add_user(El, Domain, User, Password) ->
case create_user(User,Password,Domain) of
add_user(El, Domain, User, <<"scram">> = PasswordFormat, Password) ->
Password2 = prepare_password(PasswordFormat, Password, El),
case create_user(User,Password2,Domain) of
ok ->
populate_user_with_elements(El, Domain, User),
ok;
@@ -188,6 +190,21 @@ add_user(El, Domain, User, Password) ->
{error, Other}
end.
prepare_password(<<"plaintext">>, PasswordBinary, _El) ->
?BTL(PasswordBinary);
prepare_password(<<"scram">>, none, El) ->
ScramEl = exmpp_xml:get_element(El, 'scram-hash'),
#scram{storedkey = base64:decode(exmpp_xml:get_attribute(
ScramEl, <<"stored-key">>, none)),
serverkey = base64:decode(exmpp_xml:get_attribute(
ScramEl, <<"server-key">>, none)),
salt = base64:decode(exmpp_xml:get_attribute(
ScramEl, <<"salt">>, none)),
iterationcount = list_to_integer(exmpp_xml:get_attribute_as_list(
ScramEl, <<"iteration-count">>,
?SCRAM_DEFAULT_ITERATION_COUNT))
}.
populate_user_with_elements(El, Domain, User) ->
exmpp_xml:foreach(
fun (_,Child) ->
@@ -199,7 +216,7 @@ populate_user_with_elements(El, Domain, User) ->
%% -> ok | {atomic, exists} | {error, not_allowed}
%% @doc Create a new user
create_user(User,Password,Domain) ->
case ejabberd_auth:try_register(?BTL(User),?BTL(Domain),?BTL(Password)) of
case ejabberd_auth:try_register(?BTL(User),?BTL(Domain),Password) of
{atomic,ok} -> ok;
{atomic, exists} -> {atomic, exists};
{error, not_allowed} -> {error, not_allowed};
@@ -482,10 +499,23 @@ export_user(Fd, Username, Host) ->
%% @spec (Username::string(), Host::string()) -> string()
extract_user(Username, Host) ->
Password = ejabberd_auth:get_password_s(Username, Host),
Password = ejabberd_auth:get_password(Username, Host),
PasswordStr = build_password_string(Password),
UserInfo = [extract_user_info(InfoName, Username, Host) || InfoName <- [roster, offline, private, vcard]],
UserInfoString = lists:flatten(UserInfo),
io_lib:format("<user name='~s' password='~s'>~s</user>", [Username, Password, UserInfoString]).
io_lib:format("<user name='~s' ~s ~s</user>",
[Username, PasswordStr, UserInfoString]).
build_password_string({StoredKey, ServerKey, Salt, IterationCount}) ->
io_lib:format("password-format='scram'>"
"<scram-hash stored-key='~s' server-key='~s' "
"salt='~s' iteration-count='~w'/> ",
[base64:encode_to_string(StoredKey),
base64:encode_to_string(ServerKey),
base64:encode_to_string(Salt),
IterationCount]);
build_password_string(Password) when is_list(Password) ->
io_lib:format("password-format='plaintext' password='~s'>", [Password]).
%% @spec (InfoName::atom(), Username::string(), Host::string()) -> string()
extract_user_info(roster, Username, Host) ->
+38 -1
View File
@@ -42,7 +42,11 @@
allow_host/2,
incoming_s2s_number/0,
outgoing_s2s_number/0,
migrate/1
migrate/1,
clean_temporarily_blocked_table/0,
list_temporarily_blocked_hosts/0,
external_host_overloaded/1,
is_temporarly_blocked/1
]).
%% gen_server callbacks
@@ -65,9 +69,14 @@
-define(PREFIXED_NS,
[{?NS_XMPP, ?NS_XMPP_pfx}, {?NS_DIALBACK, ?NS_DIALBACK_pfx}]).
-define(S2S_OVERLOAD_BLOCK_PERIOD, 60).
%% once a server is temporarly blocked, it stay blocked for 60 seconds
-record(s2s, {fromto, pid, key}).
-record(state, {}).
-record(temporarily_blocked, {host, timestamp}).
%%====================================================================
%% API
%%====================================================================
@@ -98,6 +107,31 @@ route(From, To, Packet) ->
ok
end.
clean_temporarily_blocked_table() ->
mnesia:clear_table(temporarily_blocked).
list_temporarily_blocked_hosts() ->
ets:tab2list(temporarily_blocked).
external_host_overloaded(Host) ->
?INFO_MSG("Disabling connections from ~s for ~s seconds", [Host, ?S2S_OVERLOAD_BLOCK_PERIOD]),
mnesia:transaction( fun() ->
mnesia:write(#temporarily_blocked{host = Host, timestamp = now()})
end).
is_temporarly_blocked(Host) ->
case mnesia:dirty_read(temporarily_blocked, Host) of
[] -> false;
[#temporarily_blocked{timestamp = T}=Entry] ->
case timer:now_diff(now(), T) of
N when N > ?S2S_OVERLOAD_BLOCK_PERIOD * 1000 * 1000 ->
mnesia:dirty_delete_object(Entry),
false;
_ ->
true
end
end.
remove_connection(FromTo, Pid, Key) ->
case catch mnesia:dirty_match_object(s2s, #s2s{fromto = FromTo,
pid = Pid,
@@ -248,6 +282,7 @@ init([]) ->
mnesia:add_table_copy(s2s, node(), ram_copies),
ejabberd_hooks:add(node_hash_update, ?MODULE, migrate, 100),
ejabberd_commands:register_commands(commands()),
mnesia:create_table(temporarily_blocked, [{ram_copies, [node()]}, {attributes, record_info(fields, temporarily_blocked)}]),
{ok, #state{}}.
%%--------------------------------------------------------------------
@@ -576,6 +611,8 @@ update_tables() ->
%% Check if host is in blacklist or white list
allow_host(MyServer, S2SHost) ->
allow_host2(MyServer, S2SHost) andalso (not is_temporarly_blocked(S2SHost)).
allow_host2(MyServer, S2SHost) ->
Hosts = ?MYHOSTS,
case lists:dropwhile(
fun(ParentDomain) ->
+37 -5
View File
@@ -27,7 +27,7 @@
-module(ejabberd_s2s_in).
-author('alexey@process-one.net').
-behaviour(gen_fsm).
-behaviour(p1_fsm).
%% External exports
-export([start/2,
@@ -44,6 +44,7 @@
handle_sync_event/4,
code_change/4,
handle_info/3,
print_state/1,
terminate/3]).
-include_lib("exmpp/include/exmpp.hrl").
@@ -80,10 +81,12 @@
-define(FSMOPTS, []).
-endif.
-define(FSMLIMITS, [{max_queue, 2000}]). %% if queue grows more than this, we shutdown this connection.
%% Module start with or without supervisor:
-ifdef(NO_TRANSIENT_SUPERVISORS).
-define(SUPERVISOR_START, gen_fsm:start(ejabberd_s2s_in, [SockData, Opts],
?FSMOPTS)).
-define(SUPERVISOR_START, p1_fsm:start(ejabberd_s2s_in, [SockData, Opts],
?FSMOPTS ++ ?FSMLIMITS)).
-else.
-define(SUPERVISOR_START, supervisor:start_child(ejabberd_s2s_in_sup,
[SockData, Opts])).
@@ -102,7 +105,7 @@ start(SockData, Opts) ->
?SUPERVISOR_START.
start_link(SockData, Opts) ->
gen_fsm:start_link(ejabberd_s2s_in, [SockData, Opts], ?FSMOPTS).
p1_fsm:start_link(ejabberd_s2s_in, [SockData, Opts], ?FSMOPTS ++ ?FSMLIMITS).
socket_type() ->
xml_stream.
@@ -322,14 +325,20 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
_ ->
false
end,
AllowRemoteHost = ejabberd_s2s:allow_host("", AuthDomain),
if
AuthRes ->
AuthRes andalso AllowRemoteHost ->
(StateData#state.sockmod):reset_stream(
StateData#state.socket),
send_element(StateData,
exmpp_server_sasl:success()),
?DEBUG("(~w) Accepted s2s authentication for ~s",
[StateData#state.socket, AuthDomain]),
%% acess rules are first checked against the globally defined ones, that have precedence over
%% domain-specific ones.. http://www.process-one.net/docs/ejabberd/guide_en.html#AccessRights
%% since there is allways a shaper defined globally for s2s, it doesn't matter the actual
%% local host, since the globall one will be used, even if this domain has a special rule
change_shaper(StateData, "", exmpp_jid:make(AuthDomain)),
{next_state, wait_for_stream,
StateData#state{streamid = new_id(),
authenticated = true,
@@ -617,9 +626,32 @@ handle_info(_, StateName, StateData) ->
%%----------------------------------------------------------------------
terminate(Reason, _StateName, StateData) ->
?DEBUG("terminated: ~p", [Reason]),
case Reason of
{process_limit, _} ->
[ejabberd_s2s:external_host_overloaded(Host) || Host <- get_external_hosts(StateData)];
_ ->
ok
end,
(StateData#state.sockmod):close(StateData#state.socket),
ok.
get_external_hosts(StateData) ->
case StateData#state.authenticated of
true ->
[StateData#state.auth_domain];
false ->
Connections = StateData#state.connections,
[D || {{D, _}, established} <- dict:to_list(Connections)]
end.
%%----------------------------------------------------------------------
%% Func: print_state/1
%% Purpose: Prepare the state to be printed on error log
%% Returns: State to print
%%----------------------------------------------------------------------
print_state(State) ->
State.
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
+14 -6
View File
@@ -59,6 +59,7 @@
get_session_pid/1,
get_user_info/3,
get_user_ip/1,
is_existing_resource/3,
migrate/1
]).
@@ -727,16 +728,12 @@ check_for_sessions_to_replace(JID) ->
check_max_sessions(JID).
check_existing_resources(JID) ->
USR = {exmpp_jid:prep_node(JID),
exmpp_jid:prep_domain(JID),
exmpp_jid:prep_resource(JID)},
%% A connection exist with the same resource. We replace it:
SIDs = mnesia:dirty_select(
session,
[{#session{sid = '$1', usr = USR, _ = '_'}, [], ['$1']}]),
SIDs = get_resource_sessions(JID),
if
SIDs == [] -> ok;
true ->
%% A connection exist with the same resource. We replace it:
MaxSID = lists:max(SIDs),
lists:foreach(
fun({_, Pid} = S) when S /= MaxSID ->
@@ -745,6 +742,17 @@ check_existing_resources(JID) ->
end, SIDs)
end.
is_existing_resource(U, S, R) ->
[] /= get_resource_sessions(exmpp_jid:make(U, S, R)).
get_resource_sessions(JID) ->
USR = {exmpp_jid:prep_node(JID),
exmpp_jid:prep_domain(JID),
exmpp_jid:prep_resource(JID)},
mnesia:dirty_select(
session,
[{#session{sid = '$1', usr = USR, _ = '_'}, [], ['$1']}]).
check_max_sessions(JID) ->
%% If the max number of sessions for a given is reached, we replace the
%% first one
+22 -6
View File
@@ -41,10 +41,10 @@ update() ->
case update_info() of
{ok, Dir, _UpdatedBeams, _Script, LowLevelScript, _Check} ->
Eval =
release_handler_1:eval_script(
eval_script(
LowLevelScript, [],
[{ejabberd, "", filename:join(Dir, "..")}]),
?INFO_MSG("eval: ~p~n", [Eval]),
?DEBUG("eval: ~p~n", [Eval]),
Eval;
{error, Reason} ->
{error, Reason}
@@ -58,15 +58,25 @@ update(ModulesToUpdate) ->
[A || A <- UpdatedBeamsAll, B <- ModulesToUpdate, A == B],
{_, LowLevelScript, _} = build_script(Dir, UpdatedBeamsNow),
Eval =
release_handler_1:eval_script(
eval_script(
LowLevelScript, [],
[{ejabberd, "", filename:join(Dir, "..")}]),
?INFO_MSG("eval: ~p~n", [Eval]),
?DEBUG("eval: ~p~n", [Eval]),
Eval;
{error, Reason} ->
{error, Reason}
end.
%% OTP R14B03 and older provided release_handler_1:eval_script/3
%% But OTP R14B04 and newer provide release_handler_1:eval_script/5
eval_script(Script, Apps, LibDirs) ->
case lists:member({eval_script, 5}, release_handler_1:module_info(exports)) of
true ->
release_handler_1:eval_script(Script, Apps, LibDirs, [], []);
false ->
release_handler_1:eval_script(Script, Apps, LibDirs)
end.
%% Get information about the modified modules
update_info() ->
Dir = filename:dirname(code:which(ejabberd)),
@@ -118,8 +128,10 @@ get_new_version(Module) ->
get_current_version(Module) ->
Attrs = Module:module_info(attributes),
{value, {vsn, CurVsn}} = lists:keysearch(vsn, 1, Attrs),
CurVsn.
case lists:keysearch(vsn, 1, Attrs) of
{value, {vsn, CurVsn}} -> CurVsn;
_ -> unknown_version
end.
%% @spec(Dir::string(), UpdatedBeams::[atom()]) -> {Script,LowLevelScript,Check}
build_script(Dir, UpdatedBeams) ->
@@ -134,6 +146,10 @@ build_script(Dir, UpdatedBeams) ->
?DEBUG("script: ~p~n", [Script]),
?DEBUG("low level script: ~p~n", [LowLevelScript]),
?DEBUG("check: ~p~n", [Check]);
{ok, []} ->
?DEBUG("script: ~p~n", [Script]),
?DEBUG("low level script: ~p~n", [LowLevelScript]),
?DEBUG("check: ~p~n", [Check]);
_ ->
?ERROR_MSG("script: ~p~n", [Script]),
?ERROR_MSG("low level script: ~p~n", [LowLevelScript]),
+112
View File
@@ -0,0 +1,112 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_zlib.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose : Interface to exmpp_compress
%%% Created : 19 Jan 2006 by Alexey Shchepin <alexey@process-one.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(ejabberd_zlib).
-author('alexey@process-one.net').
-export([start/0, start_link/0,
enable_zlib/2, disable_zlib/1,
send/2,
recv/2, recv/3, recv_data/2,
setopts/2,
get_sockmod/1,
sockname/1, peername/1,
controlling_process/2,
close/1]).
-define(DEFLATE, 1).
-define(INFLATE, 2).
%% Copied from exmpp/src/core/exmpp_compress.erl
-record(compress_socket, {socket,
packet_mode = binary,
port
}).
start() ->
exmpp_compress:start().
start_link() ->
exmpp_compress:start_link().
enable_zlib(SockMod, Socket) ->
try
ZlibSock = exmpp_compress:enable_compression({SockMod, Socket},
[{compress_method, zlib}]),
{ok, ZlibSock}
catch
Exception ->
{error, Exception}
end.
disable_zlib(ZlibSock) ->
exmpp_compress:disable_compression(ZlibSock).
recv(Socket, Length) ->
recv(Socket, Length, infinity).
recv(ZlibSock, _Length, Timeout) ->
exmpp_compress:recv(ZlibSock, Timeout).
recv_data(#compress_socket{socket = {SockMod, Socket}} = ZlibSock, Packet) ->
case SockMod of
gen_tcp ->
recv_data2(ZlibSock, Packet);
_ ->
case SockMod:recv_data(Socket, Packet) of
{ok, Packet2} ->
recv_data2(ZlibSock, Packet2);
Error ->
Error
end
end.
recv_data2(ZlibSock, Packet) ->
exmpp_compress:recv_data(ZlibSock, Packet).
send(ZlibSock, Packet) ->
exmpp_compress:send(ZlibSock, Packet).
setopts(ZlibSock, Opts) ->
exmpp_compress:setopts(ZlibSock, Opts).
get_sockmod(#compress_socket{socket = {SockMod, _Port}}) ->
SockMod.
sockname(ZlibSock) ->
exmpp_compress:sockname(ZlibSock).
peername(ZlibSock) ->
exmpp_compress:peername(ZlibSock).
controlling_process(ZlibSock, Pid) ->
exmpp_compress:controlling_process(ZlibSock, Pid).
close(ZlibSock) ->
exmpp_compress:close(ZlibSock).
-81
View File
@@ -1,81 +0,0 @@
CC = @CC@
CFLAGS = @CFLAGS@
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
ZLIB_CFLAGS = @ZLIB_CFLAGS@
ZLIB_LIBS = @ZLIB_LIBS@
ERLANG_CFLAGS = @ERLANG_CFLAGS@
ERLANG_LIBS = @ERLANG_LIBS@
# Assume Linux-style dynamic library flags
DYNAMIC_LIB_CFLAGS = -fpic -shared
ifeq ($(shell uname),Darwin)
DYNAMIC_LIB_CFLAGS = -fPIC -bundle -flat_namespace -undefined suppress
endif
ifeq ($(shell uname),SunOs)
DYNAMIC_LIB_CFLAGS = -KPIC -G -z text
endif
EFLAGS += -I ..
EFLAGS += -pz ..
# make debug=true to compile Erlang module with debug informations.
ifdef debug
EFLAGS+=+debug_info +export_all
endif
ERLSHLIBS = ../ejabberd_zlib_drv.so
OUTDIR = ..
SOURCES = $(wildcard *.erl)
BEAMS = $(addprefix $(OUTDIR)/,$(SOURCES:.erl=.beam))
all: $(BEAMS) $(ERLSHLIBS)
$(OUTDIR)/%.beam: %.erl
@ERLC@ -W $(EFLAGS) -o $(OUTDIR) $<
#all: $(ERLSHLIBS)
# erl -s make all report "{outdir, \"..\"}" -noinput -s erlang halt
$(ERLSHLIBS): ../%.so: %.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) \
$(subst ../,,$(subst .so,.c,$@)) \
$(LIBS) \
$(ZLIB_LIBS) \
$(ZLIB_CFLAGS) \
$(ERLANG_LIBS) \
$(ERLANG_CFLAGS) \
-o $@ \
$(DYNAMIC_LIB_CFLAGS)
clean:
rm -f $(BEAMS) $(ERLSHLIBS)
distclean: clean
rm -f Makefile
TAGS:
etags *.erl
DEVDOCDIR=../../doc/devdoc
DDTDIR=..
HTMLS = $(SOURCES:%.erl=../../doc/devdoc/%.html)
ERLHTMLS = $(SOURCES:%.erl=../../doc/devdoc/%.erl.html)
SVGS = $(SOURCES:%.erl=../../doc/devdoc/%.svg)
devdoc: $(HTMLS) $(ERLHTMLS) $(SVGS)
$(DEVDOCDIR)/%.erl.html: %.erl
@ERL@ -noshell -pa $(DEVDOCDIR) -run escobar_run file $< $(DDTDIR) -s init stop
$(DEVDOCDIR)/%.html: %.erl
@ERL@ -noshell -run edoc_run file $< \
'[{dir,"$(DDTDIR)"},{packages,false},{todo,true},{private,true},{def,{vsn,"$(VSN)"}},{stylesheet,"process-one.css"},{overview,"overview.edoc"}]' -s init stop
$(DEVDOCDIR)/%.svg: %.erl
@ERL@ -noshell -pa $(DEVDOCDIR) -run funrelg dir $< $(DDTDIR) -s init stop
-36
View File
@@ -1,36 +0,0 @@
include ..\Makefile.inc
EFLAGS = -I .. -pz ..
OUTDIR = ..
BEAMS = ..\ejabberd_zlib.beam
SOURCE = ejabberd_zlib_drv.c
OBJECT = ejabberd_zlib_drv.o
DLL = $(OUTDIR)\ejabberd_zlib_drv.dll
ALL : $(DLL) $(BEAMS)
CLEAN :
-@erase $(DLL)
-@erase $(OUTDIR)\ejabberd_zlib_drv.exp
-@erase $(OUTDIR)\ejabberd_zlib_drv.lib
-@erase $(OBJECT)
-@erase $(BEAMS)
$(OUTDIR)\ejabberd_zlib.beam : ejabberd_zlib.erl
erlc -W $(EFLAGS) -o $(OUTDIR) ejabberd_zlib.erl
CC=cl.exe
CC_FLAGS=-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT -MD -Ox -I"$(ERLANG_DIR)\usr\include" -I"$(EI_DIR)\include" -I"$(ZLIB_DIR)\include"
LD=link.exe
LD_FLAGS=-release -nologo -incremental:no -dll "$(EI_DIR)\lib\ei_md.lib" "$(EI_DIR)\lib\erl_interface_md.lib" "$(ZLIB_LIB)" MSVCRT.LIB kernel32.lib advapi32.lib gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib
$(DLL) : $(OBJECT)
$(LD) $(LD_FLAGS) -out:$(DLL) $(OBJECT)
$(OBJECT) : $(SOURCE)
$(CC) $(CC_FLAGS) -c -Fo$(OBJECT) $(SOURCE)
-193
View File
@@ -1,193 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_zlib.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose : Interface to zlib
%%% Created : 19 Jan 2006 by Alexey Shchepin <alexey@process-one.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(ejabberd_zlib).
-author('alexey@process-one.net').
-behaviour(gen_server).
-export([start/0, start_link/0,
enable_zlib/2, disable_zlib/1,
send/2,
recv/2, recv/3, recv_data/2,
setopts/2,
sockname/1, peername/1,
get_sockmod/1,
controlling_process/2,
close/1]).
%% Internal exports, call-back functions.
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
code_change/3,
terminate/2]).
-define(DEFLATE, 1).
-define(INFLATE, 2).
-record(zlibsock, {sockmod, socket, zlibport}).
start() ->
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init([]) ->
case erl_ddll:load_driver(ejabberd:get_so_path(), ejabberd_zlib_drv) of
ok -> ok;
{error, already_loaded} -> ok
end,
Port = open_port({spawn, "ejabberd_zlib_drv"}, [binary]),
{ok, Port}.
%%% --------------------------------------------------------
%%% The call-back functions.
%%% --------------------------------------------------------
handle_call(_, _, State) ->
{noreply, State}.
handle_cast(_, State) ->
{noreply, State}.
handle_info({'EXIT', Port, Reason}, Port) ->
{stop, {port_died, Reason}, Port};
handle_info({'EXIT', _Pid, _Reason}, Port) ->
{noreply, Port};
handle_info(_, State) ->
{noreply, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
terminate(_Reason, Port) ->
Port ! {self, close},
ok.
enable_zlib(SockMod, Socket) ->
case erl_ddll:load_driver(ejabberd:get_so_path(), ejabberd_zlib_drv) of
ok -> ok;
{error, already_loaded} -> ok
end,
Port = open_port({spawn, "ejabberd_zlib_drv"}, [binary]),
{ok, #zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}}.
disable_zlib(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}) ->
port_close(Port),
{SockMod, Socket}.
recv(Socket, Length) ->
recv(Socket, Length, infinity).
recv(#zlibsock{sockmod = SockMod, socket = Socket} = ZlibSock,
Length, Timeout) ->
case SockMod:recv(Socket, Length, Timeout) of
{ok, Packet} ->
recv_data(ZlibSock, Packet);
{error, _Reason} = Error ->
Error
end.
recv_data(#zlibsock{sockmod = SockMod, socket = Socket} = ZlibSock, Packet) ->
case SockMod of
gen_tcp ->
recv_data2(ZlibSock, Packet);
_ ->
case SockMod:recv_data(Socket, Packet) of
{ok, Packet2} ->
recv_data2(ZlibSock, Packet2);
Error ->
Error
end
end.
recv_data2(ZlibSock, Packet) ->
case catch recv_data1(ZlibSock, Packet) of
{'EXIT', Reason} ->
{error, Reason};
Res ->
Res
end.
recv_data1(#zlibsock{zlibport = Port} = _ZlibSock, Packet) ->
case port_control(Port, ?INFLATE, Packet) of
<<0, In/binary>> ->
{ok, In};
<<1, Error/binary>> ->
{error, binary_to_list(Error)}
end.
send(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port},
Packet) ->
case port_control(Port, ?DEFLATE, Packet) of
<<0, Out/binary>> ->
SockMod:send(Socket, Out);
<<1, Error/binary>> ->
{error, binary_to_list(Error)}
end.
setopts(#zlibsock{sockmod = SockMod, socket = Socket}, Opts) ->
case SockMod of
gen_tcp ->
inet:setopts(Socket, Opts);
_ ->
SockMod:setopts(Socket, Opts)
end.
sockname(#zlibsock{sockmod = SockMod, socket = Socket}) ->
case SockMod of
gen_tcp ->
inet:sockname(Socket);
_ ->
SockMod:sockname(Socket)
end.
get_sockmod(#zlibsock{sockmod = SockMod}) ->
SockMod.
peername(#zlibsock{sockmod = SockMod, socket = Socket}) ->
case SockMod of
gen_tcp ->
inet:peername(Socket);
_ ->
SockMod:peername(Socket)
end.
controlling_process(#zlibsock{sockmod = SockMod, socket = Socket}, Pid) ->
SockMod:controlling_process(Socket, Pid).
close(#zlibsock{sockmod = SockMod, socket = Socket, zlibport = Port}) ->
SockMod:close(Socket),
port_close(Port).
-189
View File
@@ -1,189 +0,0 @@
/*
* 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
*
*/
#include <stdio.h>
#include <string.h>
#include <erl_driver.h>
#include <zlib.h>
#define BUF_SIZE 1024
typedef struct {
ErlDrvPort port;
z_stream *d_stream;
z_stream *i_stream;
} ejabberd_zlib_data;
static ErlDrvData ejabberd_zlib_drv_start(ErlDrvPort port, char *buff)
{
ejabberd_zlib_data *d =
(ejabberd_zlib_data *)driver_alloc(sizeof(ejabberd_zlib_data));
d->port = port;
d->d_stream = (z_stream *)malloc(sizeof(z_stream));
d->d_stream->zalloc = (alloc_func)0;
d->d_stream->zfree = (free_func)0;
d->d_stream->opaque = (voidpf)0;
deflateInit(d->d_stream, Z_DEFAULT_COMPRESSION);
d->i_stream = (z_stream *)malloc(sizeof(z_stream));
d->i_stream->zalloc = (alloc_func)0;
d->i_stream->zfree = (free_func)0;
d->i_stream->opaque = (voidpf)0;
inflateInit(d->i_stream);
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
return (ErlDrvData)d;
}
static void ejabberd_zlib_drv_stop(ErlDrvData handle)
{
ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle;
deflateEnd(d->d_stream);
free(d->d_stream);
inflateEnd(d->i_stream);
free(d->i_stream);
driver_free((char *)handle);
}
#define DEFLATE 1
#define INFLATE 2
#define die_unless(cond, errstr) \
if (!(cond)) \
{ \
rlen = strlen(errstr) + 1; \
b = driver_realloc_binary(b, rlen); \
b->orig_bytes[0] = 1; \
strncpy(b->orig_bytes + 1, errstr, rlen - 1); \
*rbuf = (char *)b; \
return rlen; \
}
static int ejabberd_zlib_drv_control(ErlDrvData handle,
unsigned int command,
char *buf, int len,
char **rbuf, int rlen)
{
ejabberd_zlib_data *d = (ejabberd_zlib_data *)handle;
int err;
int size;
ErlDrvBinary *b;
switch (command)
{
case DEFLATE:
size = BUF_SIZE + 1;
rlen = 1;
b = driver_alloc_binary(size);
b->orig_bytes[0] = 0;
d->d_stream->next_in = (unsigned char *)buf;
d->d_stream->avail_in = len;
d->d_stream->avail_out = 0;
err = Z_OK;
while (err == Z_OK && d->d_stream->avail_out == 0)
{
d->d_stream->next_out = (unsigned char *)b->orig_bytes + rlen;
d->d_stream->avail_out = BUF_SIZE;
err = deflate(d->d_stream, Z_SYNC_FLUSH);
die_unless((err == Z_OK) || (err == Z_STREAM_END),
"Deflate error");
rlen += (BUF_SIZE - d->d_stream->avail_out);
size += (BUF_SIZE - d->d_stream->avail_out);
b = driver_realloc_binary(b, size);
}
b = driver_realloc_binary(b, rlen);
*rbuf = (char *)b;
return rlen;
case INFLATE:
size = BUF_SIZE + 1;
rlen = 1;
b = driver_alloc_binary(size);
b->orig_bytes[0] = 0;
if (len > 0) {
d->i_stream->next_in = (unsigned char *)buf;
d->i_stream->avail_in = len;
d->i_stream->avail_out = 0;
err = Z_OK;
while (err == Z_OK && d->i_stream->avail_out == 0)
{
d->i_stream->next_out = (unsigned char *)b->orig_bytes + rlen;
d->i_stream->avail_out = BUF_SIZE;
err = inflate(d->i_stream, Z_SYNC_FLUSH);
die_unless((err == Z_OK) || (err == Z_STREAM_END),
"Inflate error");
rlen += (BUF_SIZE - d->i_stream->avail_out);
size += (BUF_SIZE - d->i_stream->avail_out);
b = driver_realloc_binary(b, size);
}
}
b = driver_realloc_binary(b, rlen);
*rbuf = (char *)b;
return rlen;
}
b = driver_alloc_binary(1);
b->orig_bytes[0] = 0;
*rbuf = (char *)b;
return 1;
}
ErlDrvEntry ejabberd_zlib_driver_entry = {
NULL, /* F_PTR init, N/A */
ejabberd_zlib_drv_start, /* L_PTR start, called when port is opened */
ejabberd_zlib_drv_stop, /* F_PTR stop, called when port is closed */
NULL, /* F_PTR output, called when erlang has sent */
NULL, /* F_PTR ready_input, called when input descriptor ready */
NULL, /* F_PTR ready_output, called when output descriptor ready */
"ejabberd_zlib_drv", /* char *driver_name, the argument to open_port */
NULL, /* F_PTR finish, called when unloaded */
NULL, /* handle */
ejabberd_zlib_drv_control, /* F_PTR control, port_command callback */
NULL, /* F_PTR timeout, reserved */
NULL /* F_PTR outputv, reserved */
};
DRIVER_INIT(ejabberd_zlib_drv) /* must match name in driver_entry */
{
return &ejabberd_zlib_driver_entry;
}
+2 -2
View File
@@ -133,9 +133,9 @@ INET_DIST_INTERFACE={127,0,0,1}
# The same as previous, but erlang will use long hostname
# (see erl (1) manual for details)
#
# Default: ejabberd
# Default: ejabberd@localhost
#
#ERLANG_NODE=ejabberd
#ERLANG_NODE=ejabberd@localhost
#.
#' EJABBERD_PID_PATH: ejabberd PID file
+2 -10
View File
@@ -328,21 +328,13 @@ ctlexec ()
{
CONN_NAME=$1; shift
COMMAND=$@
CTLEXEC="$ERL \
$EXEC_CMD "$ERL \
$NAME ${CONN_NAME} \
-noinput \
-hidden \
-pa $EJABBERD_EBIN_PATH \
$KERNEL_OPTS \
-s ejabberd_ctl -extra $ERLANG_NODE"
# quote input from the command line
for i in $COMMAND; do
CTLEXEC="$CTLEXEC '$i'";
done
$EXEC_CMD "$CTLEXEC"
-s ejabberd_ctl -extra $ERLANG_NODE $COMMAND"
}
# display ctl usage
+1 -1
View File
@@ -4,7 +4,7 @@ CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
ASN_FLAGS = -bber_bin +optimize +driver
ASN_FLAGS = -bber_bin +optimize
ERLANG_CFLAGS = @ERLANG_CFLAGS@
ERLANG_LIBS = @ERLANG_LIBS@
+1 -1
View File
@@ -6,7 +6,7 @@ EFLAGS = -I .. -pz ..
OUTDIR = ..
BEAMS = ..\eldap.beam ..\eldap_filter.beam ..\eldap_pool.beam ..\eldap_utils.beam ..\eldap_filter_yecc.beam
ASN_FLAGS = -bber_bin +optimize +driver
ASN_FLAGS = -bber_bin +optimize
ALL : $(BEAMS)
+47 -18
View File
@@ -322,6 +322,14 @@ parse_search_args([{timeout, Timeout}|T],A) when is_integer(Timeout) ->
parse_search_args(T,A#eldap_search{timeout = Timeout});
parse_search_args([{limit, Limit}|T],A) when is_integer(Limit) ->
parse_search_args(T,A#eldap_search{limit = Limit});
parse_search_args([{deref_aliases, never}|T],A) ->
parse_search_args(T,A#eldap_search{deref_aliases = neverDerefAliases});
parse_search_args([{deref_aliases, searching}|T],A) ->
parse_search_args(T,A#eldap_search{deref_aliases = derefInSearching});
parse_search_args([{deref_aliases, finding}|T],A) ->
parse_search_args(T,A#eldap_search{deref_aliases = derefFindingBaseObj});
parse_search_args([{deref_aliases, always}|T],A) ->
parse_search_args(T,A#eldap_search{deref_aliases = derefAlways});
parse_search_args([H|_],_) ->
throw({error,{unknown_arg, H}});
parse_search_args([],A) ->
@@ -430,7 +438,7 @@ init([]) ->
end;
init({Hosts, Port, Rootdn, Passwd, Opts}) ->
catch ssl:start(),
ssl:seed(randoms:get_string()),
catch ssl:seed(randoms:get_string()),
Encrypt = case proplists:get_value(encrypt, Opts) of
tls -> tls;
_ -> none
@@ -445,14 +453,29 @@ init({Hosts, Port, Rootdn, Passwd, Opts}) ->
end;
PT -> PT
end,
TLSOpts = case proplists:get_value(tls_verify, Opts) of
soft ->
[{verify, 1}];
hard ->
[{verify, 2}];
_ ->
[{verify, 0}]
end,
CacertOpts = case proplists:get_value(tls_cacertfile, Opts) of
[_|_] = Path -> [{cacertfile, Path}];
_ -> []
end,
DepthOpts = case proplists:get_value(tls_depth, Opts) of
Depth when is_integer(Depth), Depth >= 0 ->
[{depth, Depth}];
_ -> []
end,
Verify = proplists:get_value(tls_verify, Opts),
TLSOpts = if (Verify == hard orelse Verify == soft)
andalso CacertOpts == [] ->
?WARNING_MSG("TLS verification is enabled "
"but no CA certfiles configured, so "
"verification is disabled.", []),
[];
Verify == soft ->
[{verify, 1}] ++ CacertOpts ++ DepthOpts;
Verify == hard ->
[{verify, 2}] ++ CacertOpts ++ DepthOpts;
true ->
[]
end,
{ok, connecting, #eldap{hosts = Hosts,
port = PortTemp,
rootdn = Rootdn,
@@ -684,7 +707,7 @@ gen_req({search, A}) ->
{searchRequest,
#'SearchRequest'{baseObject = A#eldap_search.base,
scope = v_scope(A#eldap_search.scope),
derefAliases = neverDerefAliases,
derefAliases = A#eldap_search.deref_aliases,
sizeLimit = A#eldap_search.limit,
timeLimit = v_timeout(A#eldap_search.timeout),
typesOnly = v_bool(A#eldap_search.types_only),
@@ -957,18 +980,21 @@ polish([], Res, Ref) ->
connect_bind(S) ->
Host = next_host(S#eldap.host, S#eldap.hosts),
?INFO_MSG("LDAP connection on ~s:~p", [Host, S#eldap.port]),
Opts = if S#eldap.tls == tls ->
[{packet, asn1}, {active, true}, {keepalive, true},
binary | S#eldap.tls_options];
true ->
[{packet, asn1}, {active, true}, {keepalive, true},
{send_timeout, ?SEND_TIMEOUT}, binary]
end,
SocketData = case S#eldap.tls of
tls ->
SockMod = ssl,
SslOpts = [{packet, asn1}, {active, true}, {keepalive, true},
binary | S#eldap.tls_options],
ssl:connect(Host, S#eldap.port, SslOpts);
ssl:connect(Host, S#eldap.port, Opts);
%% starttls -> %% TODO: Implement STARTTLS;
_ ->
SockMod = gen_tcp,
TcpOpts = [{packet, asn1}, {active, true}, {keepalive, true},
{send_timeout, ?SEND_TIMEOUT}, binary],
gen_tcp:connect(Host, S#eldap.port, TcpOpts)
gen_tcp:connect(Host, S#eldap.port, Opts)
end,
case SocketData of
{ok, Socket} ->
@@ -986,8 +1012,11 @@ connect_bind(S) ->
{ok, connecting, NewS#eldap{host = Host}}
end;
{error, Reason} ->
?ERROR_MSG("LDAP connection failed on ~s:~p~nReason: ~p",
[Host, S#eldap.port, Reason]),
?ERROR_MSG("LDAP connection failed:~n"
"** Server: ~s:~p~n"
"** Reason: ~p~n"
"** Socket options: ~p",
[Host, S#eldap.port, Reason, Opts]),
NewS = close_and_retry(S),
{ok, connecting, NewS#eldap{host = Host}}
end.
+1
View File
@@ -28,6 +28,7 @@
limit = 0,
attributes = [],
types_only = false,
deref_aliases = neverDerefAliases,
timeout = 0}).
+2
View File
@@ -79,6 +79,8 @@ parse(L) when is_list(L) ->
%%%-------------------------------------------------------------------
parse(L, SList) when is_list(L), is_list(SList) ->
case catch eldap_filter_yecc:parse(scan(L, SList)) of
{'EXIT', _} = Err ->
{error, Err};
{error, {_, _, Msg}} ->
{error, Msg};
{ok, Result} ->
+23 -3
View File
@@ -35,8 +35,11 @@
make_filter/2,
get_state/2,
case_insensitive_match/2,
check_filter/1,
uids_domain_subst/2]).
-include("ejabberd.hrl").
%% Generate an 'or' LDAP query on one or several attributes
%% If there is only one attribute
generate_subfilter([UID]) ->
@@ -91,9 +94,13 @@ get_user_part(String, Pattern) ->
{'EXIT', _} ->
{error, badmatch};
Result ->
case re:replace(Pattern, "%u", Result, [{return, list}]) of
String -> {ok, Result};
_ -> {error, badmatch}
case string:to_lower(
re:replace(Pattern, "%u", Result, [{return, list}])) ==
string:to_lower(String) of
true ->
{ok, Result};
false ->
{error, badmatch}
end
end.
@@ -144,3 +151,16 @@ uids_domain_subst(Host, UIDs) ->
(A) -> A
end,
UIDs).
check_filter(undefined) ->
ok;
check_filter(Filter) ->
case eldap_filter:parse(Filter) of
{ok, _} ->
ok;
Err ->
?ERROR_MSG("failed to parse LDAP filter:~n"
"** Filter: ~p~n"
"** Reason: ~p",
[Filter, Err])
end.
+31 -16
View File
@@ -40,7 +40,7 @@
loaded_modules_with_opts/1,
get_hosts/2,
get_module_proc/2,
expand_host_name/3,
get_module_proc_existing/2,
is_loaded/2]).
-export([behaviour_info/1]).
@@ -85,8 +85,8 @@ start_module2(Host, Module, Opts) ->
catch Class:Reason ->
del_module_mnesia(Host, Module),
ets:delete(ejabberd_modules, {Module, Host}),
ErrorText = io_lib:format("Problem starting the module ~p for host ~p ~n options: ~p~n ~p: ~p",
[Module, Host, Opts, Class, Reason]),
ErrorText = io_lib:format("Problem starting the module ~p for host ~p ~n options: ~p~n ~p: ~p~n stacktarce: ~p",
[Module, Host, Opts, Class, Reason, erlang:get_stacktrace()]),
?CRITICAL_MSG(ErrorText, []),
case is_app_running(ejabberd) of
true ->
@@ -194,7 +194,13 @@ get_module_opt(Host, Module, Opt, Default) ->
OptsList = ets:lookup(ejabberd_modules, {Module, Host}),
case OptsList of
[] ->
Default;
OptsList2 = ets:lookup(ejabberd_modules, {Module, global}),
case OptsList2 of
[] ->
Default;
[#ejabberd_module{opts = Opts} | _] ->
get_opt(Opt, Opts, Default)
end;
[#ejabberd_module{opts = Opts} | _] ->
get_opt(Opt, Opts, Default)
end.
@@ -204,8 +210,14 @@ get_module_opt_host(Host, Module, Default) ->
re:replace(Val, "@HOST@", Host, [global,{return,list}]).
get_opt_host(Host, Opts, Default) ->
Val = get_opt(host, Opts, Default),
re:replace(Val, "@HOST@", Host, [global,{return,list}]).
case Host of
global ->
Val = get_opt(host, Opts, Default),
{global, re:replace(Val, ".@HOST@", "", [global,{return,list}])};
Host ->
Val = get_opt(host, Opts, Default),
re:replace(Val, "@HOST@", Host, [global,{return,list}])
end.
loaded_modules(Host) ->
ets:select(ejabberd_modules,
@@ -260,6 +272,15 @@ get_hosts(Opts, Prefix) ->
Hosts
end.
get_module_proc_existing(Host, Base) ->
Proc = get_module_proc(Host, Base),
%% If the process doesn't exist for Host, it may exist for global
case {whereis(Proc), Host == global} of
{undefined, false} -> get_module_proc(global, Base);
{undefined, true} -> not_existing;
{_, _} -> Proc
end.
get_module_proc(Host, Base) when is_binary(Host) ->
get_module_proc(binary_to_list(Host), Base);
get_module_proc(global, Base) ->
@@ -269,14 +290,8 @@ get_module_proc(Host, {frontend, Base}) ->
get_module_proc(Host, Base) ->
list_to_atom(atom_to_list(Base) ++ "_" ++ Host).
%% @spec(Host::string() | global, Module::atom()) -> true | false
%% @doc Check if the module is loaded in this host (or global), or not.
is_loaded(Host, Module) ->
ets:member(ejabberd_modules, {Module, Host}).
expand_host_name(Host, Opts, DefaultPrefix) ->
case Host of
global ->
{global, gen_mod:get_opt(prefix, Opts, DefaultPrefix)};
_ ->
gen_mod:get_opt_host(Host, Opts, DefaultPrefix ++ ".@HOST@")
end.
ets:member(ejabberd_modules, {Module, Host})
orelse ets:member(ejabberd_modules, {Module, global}).
+8 -3
View File
@@ -597,10 +597,15 @@ async_dirty(Host, Tab, Fun) ->
get_table(Host, Tab) when is_list(Host) ->
get_table(list_to_binary(Host), Tab);
get_table(Host, Tab) ->
case mnesia:dirty_read(table, {Host, Tab}) of
[T] ->
case {mnesia:dirty_read(table, {Host, Tab}), Host} of
{[T], _} ->
T;
_ ->
{_, Host} when is_binary(Host) ->
Prefix = lists:nth(1, string:tokens(binary_to_list(Host), ".")),
get_table({global, Prefix}, Tab);
{_, {global, _Prefix}} ->
get_table(global, Tab);
{_, global} ->
catch throw(error123),
Stacktrace = erlang:get_stacktrace(),
error_logger:error_msg("gen_storage: Table ~p not found on ~p~nStacktrace: ~p", [Tab, Host, Stacktrace]),
+8 -5
View File
@@ -32,8 +32,9 @@ migrate_mnesia(Host, Table, Migrations) ->
end
end, DifferentTableName).
migrate_mnesia1(Host, Table, {OldTable, OldAttributes, MigrateFun}) ->
HostB = list_to_binary(Host),
migrate_mnesia1(Host, Table, {OldTable, OldAttributes, MigrateFun}) when is_list(Host) ->
migrate_mnesia1(list_to_binary(Host), Table, {OldTable, OldAttributes, MigrateFun});
migrate_mnesia1(HostB, Table, {OldTable, OldAttributes, MigrateFun}) ->
case (catch mnesia:table_info(OldTable, attributes)) of
OldAttributes ->
if
@@ -110,9 +111,12 @@ migrate_mnesia1(Host, Table, {OldTable, OldAttributes, MigrateFun}) ->
end.
migrate_odbc(HostStr, Tables, Migrations) when is_list(HostStr) ->
migrate_odbc(list_to_binary(HostStr), Tables, Migrations);
migrate_odbc(Host, Tables, Migrations) ->
HostStr = binary_to_list(Host),
try ejabberd_odbc:sql_transaction(
Host,
HostStr,
fun() ->
lists:foreach(
fun(Migration) ->
@@ -145,10 +149,9 @@ migrate_odbc1(Host, Tables, {OldTablesColumns, MigrateFun}) ->
ignored
end.
migrate_odbc2(Host, Tables, OldTable, OldTables, OldColumns, OldColumnsAll, OldTablesA, ColumnsT, MigrateFun)
migrate_odbc2(HostB, Tables, OldTable, OldTables, OldColumns, OldColumnsAll, OldTablesA, ColumnsT, MigrateFun)
when ColumnsT == OldColumnsAll ->
?INFO_MSG("Migrating ODBC table ~p to gen_storage tables ~p", [OldTable, Tables]),
HostB = list_to_binary(Host),
%% rename old tables to *_old
lists:foreach(fun(OldTable1) ->
+17 -6
View File
@@ -162,12 +162,7 @@ create_table(#tabdef{name = Tab,
end, {"", []}, Attributes),
TabS = atom_to_list(Tab),
PKey = case TableType of
%% This 105 limits the size of fields in the primary key.
%% That prevents MySQL from complaining when setting the
%% last_activity key (text, text) with this error:
%% #42000Specified key was too long; max key length is 1000bytes"
%% Similarly for rosteritem and other tables, maybe also PgSQL.
set -> [", PRIMARY KEY (", string:join(K, "(105), "), "(105))"];
set -> primary_key_string(Host, K);
bag -> []
end,
case odbc_command(Host,
@@ -192,6 +187,22 @@ create_table(#tabdef{name = Tab,
{aborted, Reason}
end.
%% This 105 limits the size of fields in the primary key.
%% That prevents MySQL from complaining when setting the
%% last_activity key (text, text) with this error:
%% #42000Specified key was too long; max key length is 1000bytes"
%% Similarly for rosteritem and other tables.
primary_key_string(Host, K) ->
Dbtype = ejabberd_odbc:db_type(Host),
case Dbtype of
mysql ->
[", PRIMARY KEY (", string:join(K, "(105), "), "(105))"];
odbc ->
[", PRIMARY KEY (", string:join(K, ", "), ")"];
pgsql ->
[", PRIMARY KEY (", string:join(K, ", "), ")"]
end.
type_to_sql_type(Type, false = _NoTextKeys) ->
type_to_sql_type(Type);
type_to_sql_type(Type, true = _NoTextKeys) ->
+3 -2
View File
@@ -47,8 +47,9 @@
-include("ejabberd.hrl").
-include("adhoc.hrl").
start(Host, Opts) ->
HostB = list_to_binary(Host),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
gen_iq_handler:add_iq_handler(ejabberd_local, HostB, ?NS_ADHOC,
+7 -6
View File
@@ -57,8 +57,9 @@
-define(NS_ADMINL(Sub), ["http:","jabber.org","protocol","admin", Sub]).
tokenize(Node) -> string:tokens(Node, "/#").
start(Host, _Opts) ->
HostB = list_to_binary(Host),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, _Opts) ->
mnesia:create_table(motd, [{disc_copies, [node()]},
{attributes, record_info(fields, motd)}]),
mnesia:create_table(motd_users, [{disc_copies, [node()]},
@@ -73,7 +74,7 @@ start(Host, _Opts) ->
ejabberd_hooks:add(adhoc_local_commands, HostB, ?MODULE, announce_commands, 50),
ejabberd_hooks:add(user_available_hook, HostB,
?MODULE, send_motd, 50),
register(gen_mod:get_module_proc(Host, ?PROCNAME),
register(gen_mod:get_module_proc(HostB, ?PROCNAME),
proc_lib:spawn(?MODULE, init, [])).
init() ->
@@ -135,7 +136,7 @@ announce(From, To, Packet) ->
case {exmpp_jid:prep_node(To), exmpp_jid:prep_resource(To)} of
{undefined, Res} ->
Name = Packet#xmlel.name,
Proc = gen_mod:get_module_proc(exmpp_jid:prep_domain_as_list(To), ?PROCNAME),
Proc = gen_mod:get_module_proc_existing(exmpp_jid:prep_domain_as_list(To), ?PROCNAME),
case {Res, Name} of
{<<"announce/all">>, 'message'} ->
Proc ! {announce_all, From, To, Packet},
@@ -541,7 +542,7 @@ handle_adhoc_form(From, To,
node = Node,
sessionid = SessionID},
Fields) ->
LServer = exmpp_jid:prep_domain_as_list(To),
LServerB = exmpp_jid:prep_domain(To),
Confirm = case lists:keysearch("confirm", 1, Fields) of
{value, {"confirm", ["true"]}} ->
true;
@@ -582,7 +583,7 @@ handle_adhoc_form(From, To,
[]
end},
Proc = gen_mod:get_module_proc(LServer, ?PROCNAME),
Proc = gen_mod:get_module_proc_existing(LServerB, ?PROCNAME),
case {Node, Body} of
{?NS_ADMIN_s ++ "#delete-motd", _} ->
if Confirm ->
+22 -14
View File
@@ -38,8 +38,9 @@
-include_lib("exmpp/include/exmpp.hrl").
-include("mod_privacy.hrl").
start(Host, Opts) ->
HostB = list_to_binary(Host),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
ejabberd_hooks:add(privacy_iq_get, HostB,
?MODULE, process_iq_get, 40),
@@ -66,9 +67,9 @@ process_iq_get(_, From, _To, #iq{ns = ?NS_BLOCKING, payload = SubEl}, _) ->
true ->
LUser = exmpp_jid:prep_node(From),
LServer = exmpp_jid:prep_domain(From),
process_blocklist_get(LUser, LServer);
{stop, process_blocklist_get(LUser, LServer)};
false ->
{error, 'bad-request'}
{stop, {error, 'bad-request'}}
end;
process_iq_get(Acc, _, _, _, _) ->
@@ -79,7 +80,7 @@ process_iq_set(_, From, _To, #iq{ns = ?NS_BLOCKING,
children = SubEls}}) ->
LUser = exmpp_jid:prep_node(From),
LServer = exmpp_jid:prep_domain(From),
case {SubElName, exmpp_xml:remove_cdata_from_list(SubEls)} of
Res = case {SubElName, exmpp_xml:remove_cdata_from_list(SubEls)} of
{block, []} ->
{error, 'bad-request'};
{block, Els} ->
@@ -92,7 +93,8 @@ process_iq_set(_, From, _To, #iq{ns = ?NS_BLOCKING,
process_blocklist_unblock(LUser, LServer, JIDs);
_ ->
{error, 'bad-request'}
end;
end,
{stop, Res};
process_iq_set(Acc, _, _, _) ->
Acc.
@@ -198,9 +200,10 @@ process_blocklist_block(LUser, LServer, JIDs) ->
%% [{'=', user_host, {LUser, LServer}},
%% {'=', name, Default}]),
List = list_data_to_items(Data),
broadcast_list_update(LUser, LServer, Default, List),
UserList = make_userlist(Default, List),
broadcast_list_update(LUser, LServer, Default, UserList),
broadcast_blocklist_event(LUser, LServer, {block, JIDs}),
{result, []};
{result, [], UserList};
Error ->
?DEBUG("Error ~n~p", [Error]),
{error, 'internal-server-error'}
@@ -259,9 +262,10 @@ process_blocklist_unblock_all(LUser, LServer) ->
{result, []};
{atomic, {ok, Default, Data}} ->
List = list_data_to_items(Data),
broadcast_list_update(LUser, LServer, Default, List),
UserList = make_userlist(Default, List),
broadcast_list_update(LUser, LServer, Default, UserList),
broadcast_blocklist_event(LUser, LServer, unblock_all),
{result, []};
{result, [], UserList};
_ ->
{error, 'internal-server-error'}
end.
@@ -304,17 +308,21 @@ process_blocklist_unblock(LUser, LServer, JIDs) ->
{result, []};
{atomic, {ok, Default, Data}} ->
List = list_data_to_items(Data),
broadcast_list_update(LUser, LServer, Default, List),
UserList = make_userlist(Default, List),
broadcast_list_update(LUser, LServer, Default, UserList),
broadcast_blocklist_event(LUser, LServer, {unblock, JIDs}),
{result, []};
{result, [], UserList};
_ ->
{error, 'internal-server-error'}
end.
broadcast_list_update(LUser, LServer, Name, List) ->
make_userlist(Name, List) ->
NeedDb = is_list_needdb(List),
#userlist{name = Name, list = List, needdb = NeedDb}.
broadcast_list_update(LUser, LServer, Name, UserList) ->
JID = exmpp_jid:make(LUser, LServer),
ListString = lists:flatten(io_lib:format("~p.", [#userlist{name = Name, list = List, needdb = NeedDb}])),
ListString = lists:flatten(io_lib:format("~p.", [UserList])),
ejabberd_router:route(
JID,
JID,
+9 -8
View File
@@ -92,11 +92,13 @@ 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),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
Proc = gen_mod:get_module_proc(HostB, ?PROCNAME),
ChildSpec =
{Proc,
{?MODULE, start_link, [Host, Opts]},
{?MODULE, start_link, [HostB, Opts]},
transient,
1000,
worker,
@@ -225,7 +227,7 @@ c2s_presence_in(C2SState, {From, To, #xmlel{attrs = Attrs, children = Els}}) ->
Subscription = ejabberd_c2s:get_subscription(From, C2SState),
Insert = ((Type == <<>>) or (Type == <<"available">>))
and ((Subscription == both) or (Subscription == to)),
Delete = (Type == <<"unavailable">>) or (Type == <<"error">>) or (Type == <<"invisible">>),
Delete = (Type == <<"unavailable">>) or (Type == <<"error">>),
if Insert or Delete ->
LFrom = exmpp_jid:to_lower(From),
Rs = case ejabberd_c2s:get_aux_field(caps_resources, C2SState) of
@@ -284,7 +286,7 @@ c2s_broadcast_recipients(Acc, _, _, _, _) ->
%%====================================================================
%% gen_server callbacks
%%====================================================================
init([Host, Opts]) ->
init([HostB, Opts]) ->
case catch mnesia:table_info(caps_features, storage_type) of
{'EXIT', _} ->
ok;
@@ -301,7 +303,6 @@ init([Host, Opts]) ->
MaxSize = gen_mod:get_opt(cache_size, Opts, 1000),
LifeTime = gen_mod:get_opt(cache_life_time, Opts, timer:hours(24) div 1000),
cache_tab:new(caps_features, [{max_size, MaxSize}, {life_time, LifeTime}]),
HostB = list_to_binary(Host),
ejabberd_hooks:add(c2s_presence_in, HostB,
?MODULE, c2s_presence_in, 75),
ejabberd_hooks:add(c2s_broadcast_recipients, HostB,
@@ -320,7 +321,7 @@ init([Host, Opts]) ->
?MODULE, disco_identity, 75),
ejabberd_hooks:add(disco_info, HostB,
?MODULE, disco_info, 75),
{ok, #state{host = Host}}.
{ok, #state{host = HostB}}.
handle_call(stop, _From, State) ->
{stop, normal, ok, State};
@@ -334,7 +335,7 @@ handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, State) ->
HostB = list_to_binary(State#state.host),
HostB = State#state.host,
ejabberd_hooks:delete(c2s_presence_in, HostB,
?MODULE, c2s_presence_in, 75),
ejabberd_hooks:delete(c2s_broadcast_recipients, HostB,
+3 -2
View File
@@ -56,8 +56,9 @@
%% Copied from ejabberd_sm.erl
-record(session, {sid, usr, us, priority, info}).
start(Host, _Opts) ->
HostB = list_to_binary(Host),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, _Opts) ->
ejabberd_hooks:add(disco_local_items, HostB, ?MODULE, get_local_items, 50),
ejabberd_hooks:add(disco_local_features, HostB, ?MODULE, get_local_features, 50),
ejabberd_hooks:add(disco_local_identity, HostB, ?MODULE, get_local_identity, 50),
+11 -10
View File
@@ -51,10 +51,12 @@
-include_lib("exmpp/include/exmpp.hrl").
-include("ejabberd.hrl").
-include("mod_roster.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
start(Host, Opts) ->
HostB = list_to_binary(Host),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
ejabberd_local:refresh_iq_handlers(),
@@ -71,11 +73,10 @@ start(Host, Opts) ->
catch ets:new(disco_features, [named_table, ordered_set, public]),
register_feature(HostB, "iq"),
register_feature(HostB, "presence"),
register_feature(HostB, "presence-invisible"),
catch ets:new(disco_extra_domains, [named_table, ordered_set, public]),
ExtraDomains = gen_mod:get_opt(extra_domains, Opts, []),
lists:foreach(fun(Domain) -> register_extra_domain(list_to_binary(Host), Domain) end,
lists:foreach(fun(Domain) -> register_extra_domain(HostB, Domain) end,
ExtraDomains),
catch ets:new(disco_sm_features, [named_table, ordered_set, public]),
catch ets:new(disco_sm_nodes, [named_table, ordered_set, public]),
@@ -106,7 +107,7 @@ stop(Host) ->
ok.
register_feature(HostB, Feature) when is_binary(HostB) ->
register_feature(HostB, Feature) ->
catch ets:new(disco_features, [named_table, ordered_set, public]),
ets:insert(disco_features, {{Feature, HostB}}).
@@ -114,11 +115,11 @@ unregister_feature(HostB, Feature) ->
catch ets:new(disco_features, [named_table, ordered_set, public]),
ets:delete(disco_features, {Feature, HostB}).
register_extra_domain(HostB, Domain) when is_binary(HostB) ->
register_extra_domain(HostB, Domain) ->
catch ets:new(disco_extra_domains, [named_table, ordered_set, public]),
ets:insert(disco_extra_domains, {{Domain, HostB}}).
unregister_extra_domain(HostB, Domain) when is_binary(HostB) ->
unregister_extra_domain(HostB, Domain) ->
catch ets:new(disco_extra_domains, [named_table, ordered_set, public]),
ets:delete(disco_extra_domains, {Domain, HostB}).
@@ -242,14 +243,14 @@ get_local_services(Acc, _From, To, <<>>, _Lang) ->
empty -> []
end,
Host = exmpp_jid:prep_domain_as_list(To),
NHost = ejabberd:normalize_host(Host),
NHostB = list_to_binary(ejabberd:normalize_host(Host)),
{result,
lists:usort(
lists:map(fun domain_to_xml/1,
get_vh_services(Host) ++
ets:select(disco_extra_domains,
ets:fun2ms(fun ({{Service, H}})
when H =:= NHost;
when H =:= NHostB;
H =:= global ->
Service
end)))
@@ -315,7 +316,7 @@ is_presence_subscribed(From, To) ->
Server = exmpp_jid:prep_domain(From),
LUser = exmpp_jid:prep_node(To),
LServer = exmpp_jid:prep_domain(To),
lists:any(fun({roster, _, _, {TUser, TServer, _}, _, S, _, _, _, _}) ->
lists:any(fun(#roster{jid = {TUser, TServer, _}, subscription = S}) ->
if
LUser == TUser, LServer == TServer, S/=none ->
true;
+1 -1
View File
@@ -86,7 +86,7 @@ stop(Host) ->
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([Host, Opts]) ->
MyHost = gen_mod:expand_host_name(Host, Opts, "echo"),
MyHost = gen_mod:get_opt_host(Host, Opts, "echo.@HOST@"),
ClientVersion = gen_mod:get_opt(client_version, Opts, false),
ejabberd_router:register_route(MyHost),
{ok, #state{host = MyHost, client_version = ClientVersion}}.
+16 -9
View File
@@ -33,6 +33,7 @@
%% API:
-export([start/2,
preinit/2,
init/1,
stop/1]).
-export([update_bl_c2s/0]).
@@ -49,14 +50,20 @@
-record(bl_c2s, {ip}).
%% Start once for all vhost
start(Host, Opts) ->
case whereis(?PROCNAME) of
undefined ->
?DEBUG("Starting mod_ip_blacklist ~p ~p~n", [Host, Opts]),
register(?PROCNAME,
spawn(?MODULE, init, [#state{}]));
_ ->
ok
start(_Host, _Opts) ->
Pid = spawn(?MODULE, preinit, [self(), #state{}]),
receive {ok, Pid, PreinitResult} ->
PreinitResult
end.
preinit(Parent, State) ->
Pid = self(),
try register(?PROCNAME, Pid) of
true ->
Parent ! {ok, Pid, true},
init(State)
catch error:_ ->
Parent ! {ok, Pid, true}
end.
%% TODO:
@@ -85,7 +92,7 @@ loop(_State) ->
%% TODO: Support comment lines starting by %
update_bl_c2s() ->
?INFO_MSG("Updating C2S Blacklist", []),
case http:request(?BLC2S) of
case httpc:request(?BLC2S) of
{ok, {{_Version, 200, _Reason}, _Headers, Body}} ->
IPs = string:tokens(Body,"\n"),
ets:delete_all_objects(bl_c2s),
+16 -11
View File
@@ -77,17 +77,18 @@
-record(last_activity, {user_host, timestamp, status}).
start(Host, Opts) ->
HostB = list_to_binary(Host),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
Backend = gen_mod:get_opt(backend, Opts, mnesia),
gen_storage:create_table(Backend, HostB, last_activity,
[{disc_copies, [node()]},
{odbc_host, Host},
{odbc_host, HostB},
{attributes, record_info(fields, last_activity)},
{types, [{user_host, {text, text}},
{timestamp, bigint}]}]),
update_table(Host, Backend),
update_table(HostB, Backend),
gen_iq_handler:add_iq_handler(ejabberd_local, HostB, ?NS_LAST_ACTIVITY,
?MODULE, process_local_iq, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_LAST_ACTIVITY,
@@ -97,8 +98,9 @@ start(Host, Opts) ->
ejabberd_hooks:add(unset_presence_hook, HostB,
?MODULE, on_presence_update, 50).
stop(Host) ->
HostB = list_to_binary(Host),
stop(Host) when is_list(Host) ->
stop(list_to_binary(Host));
stop(HostB) ->
ejabberd_hooks:delete(remove_user, HostB,
?MODULE, remove_user, 50),
ejabberd_hooks:delete(unset_presence_hook, HostB,
@@ -252,9 +254,12 @@ remove_user(User, Server) when is_binary(User), is_binary(Server) ->
ok
end.
update_table(Host, mnesia) ->
update_table(global, Storage) ->
[update_table(HostB, Storage) || HostB <- ejabberd_hosts:get_hosts(ejabberd)];
update_table(HostB, mnesia) ->
gen_storage_migration:migrate_mnesia(
Host, last_activity,
HostB, last_activity,
[{last_activity, [us, timestamp, status],
fun({last_activity, {U, S}, Timestamp, Status}) ->
U1 = case U of
@@ -266,14 +271,14 @@ update_table(Host, mnesia) ->
timestamp = Timestamp,
status = list_to_binary(Status)}
end}]);
update_table(Host, odbc) ->
update_table(HostB, odbc) ->
gen_storage_migration:migrate_odbc(
Host, [last_activity],
HostB, [last_activity],
[{"last", ["username", "seconds", "state"],
fun(_, Username, STimeStamp, Status) ->
case catch list_to_integer(STimeStamp) of
TimeStamp when is_integer(TimeStamp) ->
[#last_activity{user_host = {Username, Host},
[#last_activity{user_host = {Username, HostB},
timestamp = TimeStamp,
status = Status}];
_ ->
+57 -24
View File
@@ -91,6 +91,7 @@
register_room/3,
migrate/1,
get_vh_rooms/1,
remove_host/1,
can_use_nick/3]).
%% gen_server callbacks
@@ -128,12 +129,14 @@ start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
start(Host, Opts) ->
start_supervisor(Host),
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
start_supervisor(HostB),
Proc = gen_mod:get_module_proc(HostB, ?PROCNAME),
ChildSpec =
{Proc,
{?MODULE, start_link, [Host, Opts]},
{?MODULE, start_link, [HostB, Opts]},
temporary,
1000,
worker,
@@ -154,7 +157,7 @@ stop(Host) ->
%% So the message sending must be catched
room_destroyed(Host, Room, Pid, ServerHost) when is_binary(Host),
is_binary(Room) ->
catch gen_mod:get_module_proc(ServerHost, ?PROCNAME) !
catch gen_mod:get_module_proc_existing(ServerHost, ?PROCNAME) !
{room_destroyed, {Room, Host}, Pid},
ok.
@@ -162,7 +165,7 @@ room_destroyed(Host, Room, Pid, ServerHost) when is_binary(Host),
%% If Opts = default, the default room options are used.
%% Else use the passed options as defined in mod_muc_room.
create_room(Host, Name, From, Nick, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
Proc = gen_mod:get_module_proc_existing(Host, ?PROCNAME),
RoomHost = gen_mod:get_module_opt_host(Host, ?MODULE, "conference.@HOST@"),
Node = ejabberd_cluster:get_node({Name, RoomHost}),
gen_server:call({Proc, Node}, {create, Name, From, Nick, Opts}).
@@ -255,7 +258,7 @@ forget_room(Host, Name) when is_binary(Host), is_binary(Name) ->
end,
gen_storage:transaction(Host, muc_room_opt, F).
process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) when is_binary(Host) ->
process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) ->
Rsm = jlib:rsm_decode(IQ),
Res = exmpp_iq:result(IQ, #xmlel{ns = ?NS_DISCO_ITEMS,
name = 'query',
@@ -296,6 +299,16 @@ migrate(After) ->
end
end, Rs).
remove_host(MyHostB) when is_binary(MyHostB) ->
Host = gen_mod:get_module_opt_host(binary_to_list(MyHostB), ?MODULE, "conference.@HOST@"),
?INFO_MSG("Removing rooms of MUC service ~p", [Host]),
lists:foreach(
fun(#muc_online_room{name_host = {NameB, HostB}, pid = Pid}) ->
gen_fsm:send_all_state_event(Pid, destroy),
forget_room(HostB, NameB)
end,
get_vh_rooms(list_to_binary(Host))).
%%====================================================================
%% gen_server callbacks
%%====================================================================
@@ -308,8 +321,8 @@ migrate(After) ->
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([Host, Opts]) ->
MyHost_L = gen_mod:expand_host_name(Host, Opts, "conference"),
MyHost = list_to_binary(MyHost_L),
MyHostStr = gen_mod:get_opt_host(Host, Opts, "conference.@HOST@"),
MyHost = l2b(MyHostStr),
Backend = gen_mod:get_opt(backend, Opts, mnesia),
gen_storage:create_table(Backend, MyHost, muc_room_opt,
[{disc_copies, [node()]},
@@ -339,11 +352,11 @@ init([Host, Opts]) ->
{pid, pid}]}]),
%% If ejabberd stops abruptly, ODBC table keeps obsolete data. Let's clean:
gen_storage:dirty_delete_where(MyHost, muc_online_room,
[{'=', name_host, {'_', MyHost}}]),
[{'=', name_host, {'_', MyHost}}]),
gen_storage:add_table_copy(MyHost, muc_online_room, node(), ram_copies),
catch ets:new(muc_online_users, [bag, named_table, public, {keypos, 2}]),
gen_storage:add_table_index(MyHost, muc_registered, nick),
update_tables(MyHost_L, Backend),
update_tables(MyHost, Backend),
Access = gen_mod:get_opt(access, Opts, all),
AccessCreate = gen_mod:get_opt(access_create, Opts, all),
AccessAdmin = gen_mod:get_opt(access_admin, Opts, none),
@@ -351,8 +364,9 @@ init([Host, Opts]) ->
HistorySize = gen_mod:get_opt(history_size, Opts, 20),
DefRoomOpts = gen_mod:get_opt(default_room_options, Opts, []),
RoomShaper = gen_mod:get_opt(room_shaper, Opts, none),
ejabberd_router:register_route(MyHost_L),
ejabberd_router:register_route(MyHostStr),
ejabberd_hooks:add(node_hash_update, ?MODULE, migrate, 100),
ejabberd_hooks:add(remove_host, Host, ?MODULE, remove_host, 50),
load_permanent_rooms(MyHost, Host,
{Access, AccessCreate, AccessAdmin, AccessPersistent},
HistorySize,
@@ -413,12 +427,12 @@ handle_cast(_Msg, State) ->
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({route, From, To, Packet},
#state{host = Host,
server_host = ServerHost,
#state{server_host = ServerHost,
access = Access,
default_room_opts = DefRoomOpts,
history_size = HistorySize,
room_shaper = RoomShaper} = State) ->
Host = exmpp_jid:domain(To),
US = {exmpp_jid:prep_node(To), exmpp_jid:prep_domain(To)},
case ejabberd_cluster:get_node(US) of
Node when Node == node() ->
@@ -430,7 +444,7 @@ handle_info({route, From, To, Packet},
ok
end;
Node ->
Proc = gen_mod:get_module_proc(ServerHost, ?PROCNAME),
Proc = gen_mod:get_module_proc_existing(ServerHost, ?PROCNAME),
{Proc, Node} ! {route, From, To, Packet}
end,
{noreply, State};
@@ -454,6 +468,7 @@ handle_info(_Info, State) ->
%%--------------------------------------------------------------------
terminate(_Reason, State) ->
ejabberd_hooks:delete(node_hash_update, ?MODULE, migrate, 100),
ejabberd_hooks:delete(remove_host, State#state.server_host, ?MODULE, remove_host, 50),
ejabberd_router:unregister_route(binary_to_list(State#state.host)),
ok.
@@ -500,6 +515,9 @@ do_route(Host, ServerHost, Access, HistorySize, RoomShaper,
ejabberd_router:route_error(To, From, Err, Packet)
end.
l2b(global) -> global;
l2b(String) when is_list(String) -> list_to_binary(String);
l2b(Other) -> Other.
do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
From, To, Packet, DefRoomOpts) ->
@@ -516,7 +534,7 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
case exmpp_iq:xmlel_to_iq(Packet) of
#iq{type = get, ns = ?NS_DISCO_INFO = XMLNS,
payload = _SubEl, lang = Lang} = IQ ->
ServerHostB = list_to_binary(ServerHost),
ServerHostB = l2b(ServerHost),
Info = ejabberd_hooks:run_fold(
disco_info, ServerHostB, [],
[ServerHost, ?MODULE, <<>>, ""]),
@@ -661,10 +679,20 @@ check_user_can_create_room(ServerHost, AccessCreate, From, RoomID) ->
end.
load_permanent_rooms(Host, ServerHost, Access, HistorySize, RoomShaper) ->
load_permanent_rooms({global, Prefix}, global, Access, HistorySize, RoomShaper) ->
timer:sleep(2000), %% Wait for ejabberd_hosts to start
lists:foreach(
fun(ServerHost) ->
Host = list_to_binary(Prefix++"."++ServerHost),
load_permanent_rooms(Host, global, Access, HistorySize, RoomShaper)
end,
?MYHOSTS);
load_permanent_rooms(Host, ServerHost1, Access, HistorySize, RoomShaper) ->
F = fun() ->
Rs = gen_storage:select(Host, muc_room_opt,
[{'=', name_host, {'_', Host}}]),
[{'=', opt, persistent},
{'=', name_host, {'_', Host}}]),
Names = lists:foldl(
fun(#muc_room_opt{name_host = {Room, _}}, Names) ->
sets:add_element(Room, Names)
@@ -678,7 +706,7 @@ load_permanent_rooms(Host, ServerHost, Access, HistorySize, RoomShaper) ->
Opts = restore_room_internal(Host, Room),
{ok, Pid} = mod_muc_room:start(
Host,
ServerHost,
ServerHost1,
Access,
Room,
HistorySize,
@@ -1029,10 +1057,12 @@ get_vh_rooms(Host) when is_binary(Host) ->
%%%%% Database migration from ejabberd 2.1.x
%%%%%
update_tables(Host, mnesia) ->
HostB = list_to_binary(Host),
update_tables(global, Storage) ->
[update_tables(HostB, Storage) || HostB <- ejabberd_hosts:get_hosts(ejabberd)];
update_tables(HostB, mnesia) ->
gen_storage_migration:migrate_mnesia(
Host, muc_registered,
HostB, muc_registered,
[{muc_registered, [us_host, nick],
fun({muc_registered, {{UserS, ServerS}, HostS}, NickS}) ->
#muc_registered{user_host = {exmpp_jid:make(UserS, ServerS),
@@ -1040,7 +1070,7 @@ update_tables(Host, mnesia) ->
nick = list_to_binary(NickS)}
end}]),
gen_storage_migration:migrate_mnesia(
Host, muc_room_opt,
HostB, muc_room_opt,
[{muc_room, [name_host, opts],
fun({muc_room, {NameString, HostString}, Options}) ->
NameHost = {list_to_binary(NameString),
@@ -1066,4 +1096,7 @@ update_tables(Host, mnesia) ->
opt = Opt,
val = Val})
end, Options)
end}]).
end}]);
update_tables(_HostB, odbc) ->
ok.
+3 -1
View File
@@ -877,6 +877,7 @@ roomconfig_to_string(Options, Lang, FileFormat) ->
max_users -> "<div class=\"rcot\">" ++ OptText ++ ": \"" ++ htmlize(integer_to_list(T), FileFormat) ++ "\"</div>";
title -> "<div class=\"rcot\">" ++ OptText ++ ": \"" ++ htmlize(T, FileFormat) ++ "\"</div>";
description -> "<div class=\"rcot\">" ++ OptText ++ ": \"" ++ htmlize(T, FileFormat) ++ "\"</div>";
allow_private_messages_from_visitors -> "<div class=\"rcot\">" ++ OptText ++ ": \"" ++ htmlize(?T(atom_to_list(T)), FileFormat) ++ "\"</div>";
_ -> "\"" ++ T ++ "\""
end
end,
@@ -898,6 +899,7 @@ get_roomconfig_text(moderated) -> "Make room moderated";
get_roomconfig_text(members_by_default) -> "Default users as participants";
get_roomconfig_text(allow_change_subj) -> "Allow users to change the subject";
get_roomconfig_text(allow_private_messages) -> "Allow users to send private messages";
get_roomconfig_text(allow_private_messages_from_visitors) -> "Allow visitors to send private messages to";
get_roomconfig_text(allow_query_users) -> "Allow users to query other users";
get_roomconfig_text(allow_user_invites) -> "Allow users to send invites";
get_roomconfig_text(logging) -> "Enable logging";
@@ -957,7 +959,7 @@ get_room_state(RoomName, MucService) ->
RoomPid = R#muc_online_room.pid,
get_room_state(RoomPid);
[] ->
room_not_found
#state{}
end.
get_room_state(RoomPid) ->
+272 -89
View File
@@ -451,26 +451,42 @@ normal_state({route, From, ToNick,
ToNick),
From, Err);
_ ->
case find_jid_by_nick(ToNick, StateData) of
ToJIDs = find_jids_by_nick(ToNick, StateData),
case ToJIDs of
false ->
ErrText = "Recipient is not in the room",
ErrText = "Recipient is not in the conference room",
Err = exmpp_stanza:reply_with_error(Packet,
exmpp_stanza:error(Packet#xmlel.ns, 'item-not-found',
{Lang, translate:translate(Lang, ErrText)})),
exmpp_stanza:error(Packet#xmlel.ns, 'item-not-found',
{Lang, translate:translate(Lang, ErrText)})),
ejabberd_router:route(
jid_replace_resource(
StateData#state.jid,
ToNick),
From, Err);
ToJID ->
{ok, #user{nick = FromNick}} =
?DICT:find(jlib:short_prepd_jid(From),
StateData#state.users),
ejabberd_router:route(
jid_replace_resource(
StateData#state.jid,
FromNick),
ToJID, Packet)
_ ->
SrcIsVisitor = is_visitor(From, StateData),
DstIsModerator = is_moderator(hd(ToJIDs), StateData),
PmFromVisitors = (StateData#state.config)#config.allow_private_messages_from_visitors,
if SrcIsVisitor == false;
PmFromVisitors == anyone;
(PmFromVisitors == moderators) and (DstIsModerator) ->
{ok, #user{nick = FromNick}} =
?DICT:find(jlib:jid_tolower(From),
StateData#state.users),
FromNickJID = jid_replace_resource(StateData#state.jid, FromNick),
[ejabberd_router:route(FromNickJID, ToJID, Packet) || ToJID <- ToJIDs];
true ->
ErrText = "It is not allowed to send private messages",
Err = exmpp_stanza:reply_with_error(Packet,
exmpp_stanza:error(Packet#xmlel.ns, 'forbidden',
{Lang, translate:translate(Lang, ErrText)})),
ejabberd_router:route(
jid_replace_resource(
StateData#state.jid,
ToNick),
From, Err)
end
end
end;
{true, false} ->
@@ -934,7 +950,10 @@ process_presence(From, Nick, #xmlel{name = 'presence'} = Packet,
true ->
NewState =
add_user_presence_un(From, Packet, StateData),
send_new_presence(From, NewState),
case ?DICT:find(Nick, StateData#state.nicks) of
{ok, [_, _ | _]} -> ok;
_ -> send_new_presence(From, NewState)
end,
Reason = case exmpp_xml:get_element(Packet, 'status') of
undefined -> <<>>;
Status_el -> exmpp_xml:get_cdata(Status_el)
@@ -958,7 +977,7 @@ process_presence(From, Nick, #xmlel{name = 'presence'} = Packet,
true ->
case is_nick_change(From, Nick, StateData) of
true ->
case {is_nick_exists(Nick, StateData),
case {nick_collision(From, Nick, StateData),
mod_muc:can_use_nick(
StateData#state.host, From, Nick),
{(StateData#state.config)#config.allow_visitor_nickchange,
@@ -1277,21 +1296,31 @@ set_role(JID, Role, StateData) ->
[]
end
end,
Users = case Role of
none ->
lists:foldl(fun(J, Us) ->
?DICT:erase(J,
Us)
end, StateData#state.users, LJIDs);
_ ->
lists:foldl(fun(J, Us) ->
{ok, User} = ?DICT:find(J, Us),
?DICT:store(J,
User#user{role = Role},
Us)
end, StateData#state.users, LJIDs)
end,
StateData#state{users = Users}.
{Users, Nicks}
= case Role of
none ->
lists:foldl(fun(J, {Us, Ns}) ->
NewNs =
case ?DICT:find(J, Us) of
{ok, #user{nick = Nick}} ->
?DICT:erase(Nick, Ns);
_ ->
Ns
end,
{?DICT:erase(J, Us), NewNs}
end,
{StateData#state.users, StateData#state.nicks},
LJIDs);
_ ->
{lists:foldl(fun(J, Us) ->
{ok, User} = ?DICT:find(J, Us),
?DICT:store(J,
User#user{role = Role},
Us)
end, StateData#state.users, LJIDs),
StateData#state.nicks}
end,
StateData#state{users = Users, nicks = Nicks}.
get_role(JID, StateData) ->
LJID = jlib:short_prepd_jid(JID),
@@ -1325,6 +1354,9 @@ get_default_role(Affiliation, StateData) ->
is_visitor(Jid, StateData) ->
get_role(Jid, StateData) =:= visitor.
is_moderator(Jid, StateData) ->
get_role(Jid, StateData) =:= moderator.
get_max_users(StateData) ->
MaxUsers = (StateData#state.config)#config.max_users,
ServiceMaxUsers = get_service_max_users(StateData),
@@ -1471,8 +1503,19 @@ add_online_user(JID, Nick, Role, StateData) ->
role = Role},
StateData#state.users),
add_to_log(join, Nick, StateData),
Nicks = ?DICT:update(Nick,
fun(Entry) ->
case lists:member(LJID, Entry) of
true ->
Entry;
false ->
[LJID|Entry]
end
end,
[LJID],
StateData#state.nicks),
tab_add_online_user(JID, StateData),
StateData#state{users = Users}.
StateData#state{users = Users, nicks = Nicks}.
remove_online_user(JID, StateData) ->
remove_online_user(JID, StateData, <<>>).
@@ -1484,7 +1527,15 @@ remove_online_user(JID, StateData, Reason) ->
add_to_log(leave, {Nick, Reason}, StateData),
tab_remove_online_user(JID, StateData),
Users = ?DICT:erase(LJID, StateData#state.users),
StateData#state{users = Users}.
Nicks = case ?DICT:find(Nick, StateData#state.nicks) of
{ok, [LJID]} ->
?DICT:erase(Nick, StateData#state.nicks);
{ok, U} ->
?DICT:store(Nick, U -- [LJID], StateData#state.nicks);
false ->
StateData#state.nicks
end,
StateData#state{users = Users, nicks = Nicks}.
filter_presence(#xmlel{name = 'presence'} = Packet) ->
@@ -1534,19 +1585,48 @@ add_user_presence_un(JID, Presence, StateData) ->
StateData#state{users = Users}.
is_nick_exists(Nick, StateData) ->
?DICT:fold(fun(_, #user{nick = N}, B) ->
B orelse (N == Nick)
end, false, StateData#state.users).
%% Find and return a list of the full JIDs of the users of Nick.
%% Return jid record.
find_jids_by_nick(Nick, StateData) ->
case ?DICT:find(Nick, StateData#state.nicks) of
{ok, [User]} ->
[exmpp_jid:make(User)];
{ok, Users} ->
[exmpp_jid:make(LJID) || LJID <- Users];
error ->
false
end.
%% @spec(Nick::binary(), StateData) -> JID::jid() | false
%% Find and return the full JID of the user of Nick with
%% highest-priority presence. Return jid record.
find_jid_by_nick(Nick, StateData) ->
?DICT:fold(fun(_, #user{jid = JID, nick = N}, R) ->
case Nick of
N -> JID;
_ -> R
end
end, false, StateData#state.users).
case ?DICT:find(Nick, StateData#state.nicks) of
{ok, [User]} ->
exmpp_jid:make(User);
{ok, [FirstUser|Users]} ->
#user{last_presence = FirstPresence} =
?DICT:fetch(FirstUser, StateData#state.users),
{LJID, _} =
lists:foldl(fun(Compare, {HighestUser, HighestPresence}) ->
#user{last_presence = P1} =
?DICT:fetch(Compare, StateData#state.users),
case higher_presence(P1, HighestPresence) of
true ->
{Compare, P1};
false ->
{HighestUser, HighestPresence}
end
end, {FirstUser, FirstPresence}, Users),
exmpp_jid:make(LJID);
error ->
false
end.
higher_presence(Pres1, Pres2) ->
Pri1 = exmpp_presence:get_priority(Pres1),
Pri2 = exmpp_presence:get_priority(Pres2),
Pri1 > Pri2.
is_nick_change(JID, Nick, StateData) ->
LJID = jlib:short_prepd_jid(JID),
@@ -1559,6 +1639,13 @@ is_nick_change(JID, Nick, StateData) ->
Nick /= OldNick
end.
nick_collision(User, Nick, StateData) ->
UserOfNick = find_jid_by_nick(Nick, StateData),
%% if nick is not used, or is used by another resource of the same
%% user, it's ok.
UserOfNick /= false andalso
not exmpp_jid:bare_compare(UserOfNick, User).
add_new_user(From, Nick, Packet, StateData) ->
Lang = exmpp_stanza:get_lang(Packet),
MaxUsers = get_max_users(StateData),
@@ -1571,13 +1658,14 @@ add_new_user(From, Nick, Packet, StateData) ->
MaxConferences = gen_mod:get_module_opt(
StateData#state.server_host,
mod_muc, max_user_conferences, 10),
Collision = nick_collision(From, Nick, StateData),
case {(ServiceAffiliation == owner orelse
MaxUsers < 0 orelse
((Affiliation == admin orelse Affiliation == owner) andalso
NUsers < MaxAdminUsers) orelse
NUsers < MaxUsers) andalso
NConferences < MaxConferences,
is_nick_exists(Nick, StateData),
Collision,
mod_muc:can_use_nick(StateData#state.host, From, Nick),
get_default_role(Affiliation, StateData)} of
{false, _, _, _} ->
@@ -1915,13 +2003,19 @@ send_new_presence(NJID, StateData) ->
%% @spec(NJID::jid(), Reason::binary(), StateData) ->
send_new_presence({U, S, R}, Reason, StateData) ->
send_new_presence(exmpp_jid:make(U, S, R), Reason, StateData);
send_new_presence(NJID, Reason, StateData) ->
send_new_presence(NJID1, Reason, StateData) ->
NJID = {exmpp_jid:node(NJID1), exmpp_jid:domain(NJID1), exmpp_jid:resource(NJID1)},
%% First, find the nick associated with this JID.
#user{nick = Nick} = ?DICT:fetch(NJID, StateData#state.users),
%% Then find the JID using this nick with highest priority.
LJID1 = find_jid_by_nick(Nick, StateData),
LJID = {exmpp_jid:node(LJID1), exmpp_jid:domain(LJID1), exmpp_jid:resource(LJID1)},
%% Then we get the presence data we're supposed to send.
{ok, #user{jid = RealJID,
nick = Nick,
role = Role,
last_presence = Presence}} =
?DICT:find(jlib:short_prepd_jid(NJID), StateData#state.users),
Affiliation = get_affiliation(NJID, StateData),
?DICT:find(LJID, StateData#state.users),
Affiliation = get_affiliation(LJID1, StateData),
SAffiliation = affiliation_to_binary(Affiliation),
SRole = role_to_binary(Role),
lists:foreach(
@@ -1960,11 +2054,19 @@ send_new_presence(NJID, Reason, StateData) ->
false ->
Status
end,
Status3 = case NJID == Info#user.jid of
true ->
[#xmlel{name = 'status',
attrs = [?XMLATTR(<<"code">>, <<"110">>)]}
| Status2];
false ->
Status2
end,
Packet = exmpp_xml:append_child(Presence,
#xmlel{ns = ?NS_MUC_USER, name = 'x',
children = [#xmlel{ns = ?NS_MUC_USER, name ='item',
attrs = ItemAttrs,
children = ItemEls} | Status2]}),
children = ItemEls} | Status3]}),
ejabberd_router:route(
jid_replace_resource(StateData#state.jid, Nick),
Info#user.jid,
@@ -1973,21 +2075,23 @@ send_new_presence(NJID, Reason, StateData) ->
send_existing_presences(ToJID, StateData) ->
LToJID = jlib:short_prepd_jid(ToJID),
LToJID = {exmpp_jid:node(ToJID), exmpp_jid:domain(ToJID), exmpp_jid:resource(ToJID)},
{ok, #user{jid = RealToJID,
role = Role}} =
?DICT:find(LToJID, StateData#state.users),
lists:foreach(
fun({LJID, #user{jid = FromJID,
nick = FromNick,
role = FromRole,
last_presence = Presence
}}) ->
fun({FromNick, _Users}) ->
LJID1 = find_jid_by_nick(FromNick, StateData),
LJID = {exmpp_jid:node(LJID1), exmpp_jid:domain(LJID1), exmpp_jid:resource(LJID1)},
#user{jid = FromJID,
role = FromRole,
last_presence = Presence
} = ?DICT:fetch(LJID, StateData#state.users),
case RealToJID of
FromJID ->
ok;
_ ->
{N,D,R} = LJID,
{N,D,R} = LJID,
FromAffiliation = get_affiliation(exmpp_jid:make(N,D,R),
StateData),
ItemAttrs =
@@ -2014,7 +2118,7 @@ send_existing_presences(ToJID, StateData) ->
RealToJID,
Packet)
end
end, ?DICT:to_list(StateData#state.users)).
end, ?DICT:to_list(StateData#state.nicks)).
@@ -2032,12 +2136,37 @@ change_nick(JID, Nick, StateData) ->
fun(#user{} = User) ->
User#user{nick = Nick}
end, StateData#state.users),
NewStateData = StateData#state{users = Users},
send_nick_changing(JID, OldNick, NewStateData),
OldNickUsers = ?DICT:fetch(OldNick, StateData#state.nicks),
NewNickUsers = case ?DICT:find(Nick, StateData#state.nicks) of
{ok, U} -> U;
error -> []
end,
%% Send unavailable presence from the old nick if it's no longer
%% used.
SendOldUnavailable = length(OldNickUsers) == 1,
%% If we send unavailable presence from the old nick, we should
%% probably send presence from the new nick, in order not to
%% confuse clients. Otherwise, do it only if the new nick was
%% unused.
SendNewAvailable = SendOldUnavailable orelse
NewNickUsers == [],
Nicks =
case OldNickUsers of
[LJID] ->
?DICT:store(Nick, [LJID|NewNickUsers],
?DICT:erase(OldNick, StateData#state.nicks));
[_|_] ->
?DICT:store(Nick, [LJID|NewNickUsers],
?DICT:store(OldNick, OldNickUsers -- [LJID],
StateData#state.nicks))
end,
NewStateData = StateData#state{users = Users, nicks = Nicks},
send_nick_changing(JID, OldNick, NewStateData, SendOldUnavailable, SendNewAvailable),
add_to_log(nickchange, {OldNick, Nick}, StateData),
NewStateData.
send_nick_changing(JID, OldNick, StateData) ->
send_nick_changing(JID, OldNick, StateData,
SendOldUnavailable, SendNewAvailable) ->
{ok, #user{jid = RealJID,
nick = Nick,
role = Role,
@@ -2090,15 +2219,22 @@ send_nick_changing(JID, OldNick, StateData) ->
children =[#xmlel{ns = ?NS_MUC_USER,
name = 'item',
attrs = ItemAttrs2}]}),
ejabberd_router:route(
jid_replace_resource(StateData#state.jid, OldNick),
Info#user.jid,
Packet1),
ejabberd_router:route(
jid_replace_resource(StateData#state.jid, Nick),
Info#user.jid,
Packet2)
if SendOldUnavailable ->
ejabberd_router:route(
jid_replace_resource(StateData#state.jid, OldNick),
Info#user.jid,
Packet1);
true ->
ok
end,
if SendNewAvailable ->
ejabberd_router:route(
jid_replace_resource(StateData#state.jid, Nick),
Info#user.jid,
Packet2);
true ->
ok
end
end, ?DICT:to_list(StateData#state.users)).
@@ -2264,16 +2400,19 @@ items_with_affiliation(SAffiliation, StateData) ->
fun({JID, {Affiliation, Reason}}) ->
{N, D, R} = JID,
#xmlel{name = 'item',
ns = ?NS_MUC_ADMIN,
attrs = [?XMLATTR(<<"affiliation">>,
affiliation_to_binary(Affiliation)),
?XMLATTR(<<"jid">>,
exmpp_jid:to_binary(N, D, R))],
children = [ #xmlel{name = 'reason',
ns = ?NS_MUC_ADMIN,
children = [#xmlcdata{cdata = Reason}]}]};
({JID, Affiliation}) ->
{N, D, R} = JID,
#xmlel{name = 'item',
ns = ?NS_MUC_ADMIN,
attrs = [?XMLATTR(<<"affiliation">>,
affiliation_to_binary(Affiliation)),
?XMLATTR(<<"jid">>,
@@ -2286,6 +2425,7 @@ user_to_item(#user{role = Role,
}, StateData) ->
Affiliation = get_affiliation(JID, StateData),
#xmlel{name = 'item',
ns =?NS_MUC_ADMIN,
attrs = [
?XMLATTR(<<"role">>, role_to_binary(Role)),
?XMLATTR(<<"affiliation">>, affiliation_to_binary(Affiliation)),
@@ -2322,7 +2462,7 @@ process_admin_items_set(UJID, Items, Lang, StateData) ->
lists:foldl(
fun(E, SD) ->
process_admin_items_set(E, SD)
end, StateData, Res),
end, StateData, lists:flatten(Res)),
case (NSD#state.config)#config.persistent of
true ->
mod_muc:store_room(NSD#state.host, NSD#state.room,
@@ -2419,7 +2559,7 @@ find_changed_items(UJID, UAffiliation, URole,
S when S =/= false ->
try exmpp_jid:parse(S) of
J ->
{value, J}
{value, [J]}
catch
_:_ ->
ErrText = io_lib:format(
@@ -2431,7 +2571,7 @@ find_changed_items(UJID, UAffiliation, URole,
_ ->
case exmpp_xml:get_attribute(Item, <<"nick">>, false) of
N when N =/= false ->
case find_jid_by_nick(N, StateData) of
case find_jids_by_nick(N, StateData) of
false ->
ErrText =
io_lib:format(
@@ -2448,7 +2588,7 @@ find_changed_items(UJID, UAffiliation, URole,
end
end,
case TJID of
{value, JID} ->
{value, [JID|_]=JIDs} ->
TAffiliation = get_affiliation(JID, StateData),
TRole = get_role(JID, StateData),
case exmpp_xml:get_attribute_as_binary(Item, <<"role">>,false) of
@@ -2498,16 +2638,13 @@ find_changed_items(UJID, UAffiliation, URole,
Items, Lang, StateData,
Res);
true ->
Reason = exmpp_xml:get_path(Item, [{element, 'reason'}, cdata]),
MoreRes = [{exmpp_jid:bare(Jidx), affiliation, SAffiliation, Reason} || Jidx <- JIDs],
find_changed_items(
UJID,
UAffiliation, URole,
Items, Lang, StateData,
[{exmpp_jid:bare(JID),
affiliation,
SAffiliation,
exmpp_xml:get_path(
Item, [{element, 'reason'},
cdata])} | Res]);
[MoreRes | Res]);
false ->
{error, 'not-allowed'}
end
@@ -2555,14 +2692,13 @@ find_changed_items(UJID, UAffiliation, URole,
Items, Lang, StateData,
Res);
true ->
Reason = exmpp_xml:get_path(Item, [{element, 'reason'}, cdata]),
MoreRes = [{Jidx, role, SRole, Reason} || Jidx <- JIDs],
find_changed_items(
UJID,
UAffiliation, URole,
Items, Lang, StateData,
[{JID, role, SRole,
exmpp_xml:get_path(
Item, [{element, 'reason'},
cdata])} | Res]);
[MoreRes | Res]);
_ ->
{error, 'not-allowed'}
end
@@ -3093,6 +3229,39 @@ get_config(Lang, StateData, From) ->
?BOOLXFIELD("Allow users to send private messages",
"allow_private_messages",
Config#config.allow_private_messages),
#xmlel{name = 'field', attrs = [
?XMLATTR(<<"type">>, <<"list-single">>),
?XMLATTR(<<"label">>,
translate:translate(Lang, "Allow visitors to send private messages to")),
?XMLATTR(<<"var">>, <<"allow_private_messages_from_visitors">>)],
children = [#xmlel{name = 'value',
children = [#xmlcdata{cdata =
case Config#config.allow_private_messages_from_visitors of
anyone ->
<<"anyone">>;
moderators ->
<<"moderators">>;
nobody ->
<<"nobody">>
end}]},
#xmlel{name = 'option', attrs = [
?XMLATTR(<<"label">>,
translate:translate(Lang, "nobody"))],
children = [#xmlel{name = 'value',
children = [#xmlcdata{cdata =
<<"nobody">>}]}]},
#xmlel{name = 'option', attrs = [
?XMLATTR(<<"label">>,
translate:translate(Lang, "moderators only"))],
children = [#xmlel{name = 'value',
children = [#xmlcdata{cdata =
<<"moderators">>}]}]},
#xmlel{name = 'option', attrs = [
?XMLATTR(<<"label">>,
translate:translate(Lang, "anyone"))],
children = [#xmlel{name = 'value',
children = [#xmlcdata{cdata =
<<"anyone">>}]}]}]},
?BOOLXFIELD("Allow users to query other users",
"allow_query_users",
Config#config.allow_query_users),
@@ -3197,6 +3366,17 @@ set_xoption([{"allow_query_users", [Val]} | Opts], Config) ->
?SET_BOOL_XOPT(allow_query_users, Val);
set_xoption([{"allow_private_messages", [Val]} | Opts], Config) ->
?SET_BOOL_XOPT(allow_private_messages, Val);
set_xoption([{"allow_private_messages_from_visitors", [Val]} | Opts], Config) ->
case Val of
"anyone" ->
?SET_STRING_XOPT(allow_private_messages_from_visitors, anyone);
"moderators" ->
?SET_STRING_XOPT(allow_private_messages_from_visitors, moderators);
"nobody" ->
?SET_STRING_XOPT(allow_private_messages_from_visitors, nobody);
_ ->
{error, 'bad-request'}
end;
set_xoption([{"muc#roomconfig_allowvisitorstatus", [Val]} | Opts], Config) ->
?SET_BOOL_XOPT(allow_visitor_status, Val);
set_xoption([{"muc#roomconfig_allowvisitornickchange", [Val]} | Opts], Config) ->
@@ -3299,6 +3479,7 @@ set_opts([{Opt, Val} | Opts], StateData) ->
allow_change_subj -> StateData#state{config = (StateData#state.config)#config{allow_change_subj = Val}};
allow_query_users -> StateData#state{config = (StateData#state.config)#config{allow_query_users = Val}};
allow_private_messages -> StateData#state{config = (StateData#state.config)#config{allow_private_messages = Val}};
allow_private_messages_from_visitors -> StateData#state{config = (StateData#state.config)#config{allow_private_messages_from_visitors = Val}};
allow_visitor_nickchange -> StateData#state{config = (StateData#state.config)#config{allow_visitor_nickchange = Val}};
allow_visitor_status -> StateData#state{config = (StateData#state.config)#config{allow_visitor_status = Val}};
public -> StateData#state{config = (StateData#state.config)#config{public = Val}};
@@ -3342,6 +3523,7 @@ make_opts(StateData) ->
?MAKE_CONFIG_OPT(allow_change_subj),
?MAKE_CONFIG_OPT(allow_query_users),
?MAKE_CONFIG_OPT(allow_private_messages),
?MAKE_CONFIG_OPT(allow_private_messages_from_visitors),
?MAKE_CONFIG_OPT(allow_visitor_status),
?MAKE_CONFIG_OPT(allow_visitor_nickchange),
?MAKE_CONFIG_OPT(public),
@@ -3450,7 +3632,7 @@ process_iq_disco_info(_From, get, Lang, StateData) ->
#xmlcdata{cdata = list_to_binary(Val)}]}]}).
iq_disco_info_extras(Lang, StateData) ->
Len = length(?DICT:to_list(StateData#state.users)),
Len = ?DICT:size(StateData#state.users),
RoomDescription = (StateData#state.config)#config.description,
[#xmlel{ns = ?NS_DATA_FORMS, name = 'x',
attrs = [?XMLATTR(<<"type">>, <<"result">>)],
@@ -3667,8 +3849,7 @@ check_decline_invitation(Packet) ->
%% The original stanza must be slightly modified.
send_decline_invitation({Packet, XEl, DEl = #xmlel{name='decline'}, ToJID},
RoomJID, FromJID) ->
FromString = exmpp_jid:to_binary(FromJID),
FromString = exmpp_jid:bare_to_binary(FromJID),
DEl1 = exmpp_xml:remove_attribute(DEl, <<"to">>),
DEl2 = exmpp_xml:set_attribute(DEl1, <<"from">>,FromString),
XEl2 = replace_subelement(XEl,DEl2),
@@ -3715,27 +3896,29 @@ add_to_log(Type, Data, StateData) ->
tab_add_online_user(JID, StateData) ->
LUser = exmpp_jid:prep_node(JID),
LServer = exmpp_jid:prep_domain(JID),
LResource = exmpp_jid:prep_resource(JID),
US = {LUser, LServer},
Room = StateData#state.room,
Host = StateData#state.host,
catch ets:insert(
muc_online_users,
#muc_online_users{us = US, room = Room, host = Host}).
#muc_online_users{us = US, resource = LResource, room = Room, host = Host}).
tab_remove_online_user(JID, StateData) when ?IS_JID(JID) ->
LUser = exmpp_jid:prep_node(JID),
LServer = exmpp_jid:prep_domain(JID),
tab_remove_online_user({LUser, LServer, none},StateData);
LResource = exmpp_jid:prep_resource(JID),
tab_remove_online_user({LUser, LServer, LResource},StateData);
tab_remove_online_user({LUser, LServer,_}, StateData) ->
tab_remove_online_user({LUser, LServer, LResource}, StateData) ->
US = {LUser, LServer},
Room = StateData#state.room,
Host = StateData#state.host,
catch ets:delete_object(
muc_online_users,
#muc_online_users{us = US, room = Room, host = Host}).
#muc_online_users{us = US, resource = LResource, room = Room, host = Host}).
tab_count_user(JID) ->
LUser = exmpp_jid:prep_node(JID),
+3
View File
@@ -31,6 +31,7 @@
allow_change_subj = true,
allow_query_users = true,
allow_private_messages = true,
allow_private_messages_from_visitors = anyone,
allow_visitor_status = true,
allow_visitor_nickchange = true,
public = true,
@@ -68,6 +69,7 @@
config = #config{},
users = ?DICT:new(),
robots = ?DICT:new(),
nicks = ?DICT:new(),
affiliations = ?DICT:new(),
history,
subject = "",
@@ -78,6 +80,7 @@
room_queue = queue:new()}).
-record(muc_online_users, {us,
resource,
room,
host}).
+13 -8
View File
@@ -112,12 +112,14 @@
%% default value for the maximum number of user messages
-define(MAX_USER_MESSAGES, infinity).
start(Host, Opts) ->
HostB = list_to_binary(Host),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
% Host = binary_to_list(HostB),
Backend = gen_mod:get_opt(backend, Opts, mnesia),
gen_storage:create_table(Backend, HostB, offline_msg,
[{disc_only_copies, [node()]},
{odbc_host, Host},
{odbc_host, HostB},
{type, bag},
{attributes, record_info(fields, offline_msg)},
{types, [{user_host, {text, text}},
@@ -125,7 +127,7 @@ start(Host, Opts) ->
{expire, bigint},
{from, jid},
{to, jid}]}]),
update_table(Host, Backend),
update_table(HostB, Backend),
ejabberd_hooks:add(offline_message_hook, HostB,
?MODULE, store_packet, 50),
ejabberd_hooks:add(resend_offline_messages_hook, HostB,
@@ -145,7 +147,7 @@ start(Host, Opts) ->
ejabberd_hooks:add(webadmin_user_parse_query, HostB,
?MODULE, webadmin_user_parse_query, 50),
AccessMaxOfflineMsgs = gen_mod:get_opt(access_max_user_messages, Opts, max_user_offline_messages),
register(gen_mod:get_module_proc(Host, ?PROCNAME),
register(gen_mod:get_module_proc(HostB, ?PROCNAME),
spawn(?MODULE, loop, [AccessMaxOfflineMsgs])).
stanza_to_store(Stanza) ->
@@ -258,7 +260,7 @@ store_packet(From, To, Packet) ->
LServer = exmpp_jid:prep_domain(To),
TimeStamp = make_timestamp(),
Expire = find_x_expire(TimeStamp, Packet#xmlel.children),
gen_mod:get_module_proc(LServer, ?PROCNAME) !
gen_mod:get_module_proc_existing(LServer, ?PROCNAME) !
#offline_msg{user_host = {LUser, LServer},
timestamp = TimeStamp,
expire = Expire,
@@ -464,6 +466,9 @@ remove_user(User, Server) when is_binary(User), is_binary(Server) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
update_table(global, Storage) ->
[update_table(HostB, Storage) || HostB <- ejabberd_hosts:get_hosts(ejabberd)];
update_table(Host, mnesia) ->
gen_storage_migration:migrate_mnesia(
Host, offline_msg,
@@ -720,8 +725,8 @@ get_messages_subset2(Max, Length, MsgsAll) ->
MsgsFirstN ++ [IntermediateMsg] ++ MsgsLastN.
webadmin_user(Acc, User, Server, Lang) ->
LUser = exmpp_stringprep:nodeprep(User),
LServer = exmpp_stringprep:nameprep(Server),
LUser = list_to_binary(exmpp_stringprep:nodeprep(User)),
LServer = list_to_binary(exmpp_stringprep:nameprep(Server)),
QueueLen = get_queue_length(LUser, LServer),
FQueueLen = [?AC("queue/", integer_to_list(QueueLen))],
Acc ++ [?XCT("h3", "Offline Messages:")] ++ FQueueLen ++ [?C(" "), ?INPUTT("submit", "removealloffline", "Remove All Offline Messages")].
+13 -10
View File
@@ -52,7 +52,7 @@
%% Hook callbacks
-export([iq_ping/3, user_online/3, user_offline/3, user_send/3]).
-record(state, {host = "",
-record(state, {host, % binary() | global
send_pings = ?DEFAULT_SEND_PINGS,
ping_interval = ?DEFAULT_PING_INTERVAL,
timeout_action = none,
@@ -66,16 +66,18 @@ start_link(Host, Opts) ->
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
start_ping(Host, JID) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
Proc = gen_mod:get_module_proc_existing(Host, ?MODULE),
gen_server:cast(Proc, {start_ping, JID}).
stop_ping(Host, JID) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
Proc = gen_mod:get_module_proc_existing(Host, ?MODULE),
gen_server:cast(Proc, {stop_ping, JID}).
%%====================================================================
%% gen_mod callbacks
%%====================================================================
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
PingSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
@@ -90,8 +92,7 @@ stop(Host) ->
%%====================================================================
%% gen_server callbacks
%%====================================================================
init([Host, Opts]) ->
HostB = list_to_binary(Host),
init([HostB, 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),
@@ -103,6 +104,8 @@ init([Host, Opts]) ->
?MODULE, iq_ping, IQDisc),
case SendPings of
true ->
%% Ping requests are sent to all entities, whether they
%% announce 'urn:xmpp:ping' in their caps or not
ejabberd_hooks:add(sm_register_connection_hook, HostB,
?MODULE, user_online, 100),
ejabberd_hooks:add(sm_remove_connection_hook, HostB,
@@ -112,14 +115,13 @@ init([Host, Opts]) ->
_ ->
ok
end,
{ok, #state{host = Host,
{ok, #state{host = HostB,
send_pings = SendPings,
ping_interval = PingInterval,
timeout_action = TimeoutAction,
timers = ?DICT:new()}}.
terminate(_Reason, #state{host = Host}) ->
HostB = list_to_binary(Host),
terminate(_Reason, #state{host = HostB}) ->
ejabberd_hooks:delete(sm_remove_connection_hook, HostB,
?MODULE, user_offline, 100),
ejabberd_hooks:delete(sm_register_connection_hook, HostB,
@@ -143,7 +145,8 @@ handle_cast({stop_ping, JID}, State) ->
{noreply, State#state{timers = Timers}};
handle_cast({iq_pong, JID, timeout}, State) ->
Timers = del_timer(JID, State#state.timers),
ejabberd_hooks:run(user_ping_timeout, list_to_binary(State#state.host), [JID]),
Host = exmpp_jid:domain(JID),
ejabberd_hooks:run(user_ping_timeout, Host, [JID]),
case State#state.timeout_action of
kill ->
case ejabberd_sm:get_session_pid(JID) of
@@ -171,7 +174,7 @@ handle_info({timeout, _TRef, {ping, JID}}, State) ->
F = fun(Response) ->
gen_server:cast(Pid, {iq_pong, JID, Response})
end,
From = exmpp_jid:make(State#state.host),
From = exmpp_jid:make(exmpp_jid:domain(JID)),
ejabberd_local:route_iq(From, JID, IQ, F),
Timers = add_timer(JID, State#state.ping_interval, State#state.timers),
{noreply, State#state{timers = Timers}};
+3 -2
View File
@@ -37,8 +37,9 @@
-record(pres_counter, {dir, start, count, logged = false}).
start(Host, _Opts) ->
HostB = list_to_binary(Host),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, _Opts) ->
ejabberd_hooks:add(privacy_check_packet, HostB,
?MODULE, check_packet, 25),
ok.
+34 -9
View File
@@ -114,28 +114,30 @@
updated_list/3]).
-include_lib("exmpp/include/exmpp.hrl").
-include_lib("exmpp/include/exmpp_jid.hrl").
-include("ejabberd.hrl").
-include("mod_privacy.hrl").
start(Host, Opts) ->
HostB = list_to_binary(Host),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
Backend = gen_mod:get_opt(backend, Opts, mnesia),
gen_storage:create_table(Backend, HostB, privacy_list,
[{disc_copies, [node()]},
{odbc_host, Host},
{odbc_host, HostB},
{type, bag},
{attributes, record_info(fields, privacy_list)},
{types, [{user_host, {text, text}}]}]),
gen_storage:create_table(Backend, HostB, privacy_default_list,
[{disc_copies, [node()]},
{odbc_host, Host},
{odbc_host, HostB},
{attributes, record_info(fields, privacy_default_list)},
{types, [{user_host, {text, text}}]}]),
gen_storage:create_table(Backend, HostB, privacy_list_data,
[{disc_copies, [node()]},
{odbc_host, Host},
{odbc_host, HostB},
{type, bag},
{attributes, record_info(fields, privacy_list_data)},
{types, [{user_host, {text, text}},
@@ -149,7 +151,7 @@ start(Host, Opts) ->
{match_presence_in, atom},
{match_presence_out, atom}
]}]),
update_tables(Host, Backend),
update_tables(HostB, Backend),
gen_storage:add_table_index(HostB, privacy_list, name),
gen_storage:add_table_index(HostB, privacy_list_data, name),
ejabberd_hooks:add(privacy_iq_get, HostB,
@@ -673,6 +675,27 @@ get_user_list(_, LUser, LServer)
%% From is the sender, To is the destination.
%% If Dir = out, User@Server is the sender account (From).
%% If Dir = in, User@Server is the destination account (To).
check_packet(_, _User, _Server,
_UserList,
{#jid{node = "", domain = Server} = _From,
#jid{domain = Server} = _To,
_},
in) ->
allow;
check_packet(_, _User, _Server,
_UserList,
{#jid{domain = Server} = _From,
#jid{node = "", domain = Server} = _To,
_},
out) ->
allow;
check_packet(_, _User, _Server,
_UserList,
{#jid{node = User, domain = Server} = _From,
#jid{node = User, domain = Server} = _To,
_},
_Dir) ->
allow;
check_packet(_, User, Server,
#userlist{list = List, needdb = NeedDb},
{From, To, #xmlel{name = PName} = El},
@@ -807,10 +830,12 @@ updated_list(_,
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
update_tables(Host, mnesia) ->
HostB = list_to_binary(Host),
update_tables(global, Storage) ->
[update_tables(HostB, Storage) || HostB <- ejabberd_hosts:get_hosts(ejabberd)];
update_tables(HostB, mnesia) ->
gen_storage_migration:migrate_mnesia(
Host, privacy_default_list,
HostB, privacy_default_list,
[{privacy, [us, default, lists],
fun({privacy, {U, S}, Default, Lists}) ->
US = {list_to_binary(U), list_to_binary(S)},
+8 -4
View File
@@ -69,16 +69,17 @@
%% TODO: usns instead of user_host_ns requires no migration
-record(private_storage, {user_host_ns, xml}).
start(Host, Opts) ->
HostB = list_to_binary(Host),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
Backend = gen_mod:get_opt(backend, Opts, mnesia),
gen_storage:create_table(Backend, HostB, private_storage,
[{disc_only_copies, [node()]},
{odbc_host, Host},
{odbc_host, HostB},
{attributes, record_info(fields, private_storage)},
{types, [{user_host_ns, {binary, binary, atom}}, {xml, xmlel}]}]),
update_table(Host, Backend),
update_table(HostB, Backend),
ejabberd_hooks:add(remove_user, HostB,
?MODULE, remove_user, 50),
gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_PRIVATE,
@@ -218,6 +219,9 @@ remove_user(User, Server)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
update_table(global, Storage) ->
[update_table(HostB, Storage) || HostB <- ejabberd_hosts:get_hosts(ejabberd)];
update_table(Host, mnesia) ->
gen_storage_migration:migrate_mnesia(
Host, private_storage,
+1 -1
View File
@@ -222,7 +222,7 @@ iq_vcard(Lang) ->
"\nCopyright (c) 2003-2011 ProcessOne")}]}].
parse_options(ServerHost, Opts) ->
MyHost = gen_mod:expand_host_name(ServerHost, Opts, "proxy"),
MyHost = gen_mod:get_opt_host(ServerHost, Opts, "proxy.@HOST@"),
Port = gen_mod:get_opt(port, Opts, 7777),
ACL = gen_mod:get_opt(access, Opts, all),
Name = gen_mod:get_opt(name, Opts, "SOCKS5 Bytestreams"),
+40 -15
View File
@@ -203,7 +203,7 @@ stop(Host) ->
init([ServerHost, Opts]) ->
?DEBUG("pubsub init ~p ~p",[ServerHost,Opts]),
Host = gen_mod:expand_host_name(ServerHost, Opts, "pubsub"),
Host = gen_mod:get_opt_host(ServerHost, Opts, "pubsub.@HOST@"),
Access = gen_mod:get_opt('access_createnode', Opts, 'all'),
PepOffline = gen_mod:get_opt('ignore_pep_from_offline', Opts, true),
IQDisc = gen_mod:get_opt('iqdisc', Opts, 'one_queue'),
@@ -250,6 +250,7 @@ init([ServerHost, Opts]) ->
ejabberd_router:register_route(Host),
update_node_database(Host, ServerHost),
update_state_database(Host, ServerHost),
put(server_host, ServerHost),
init_nodes(Host, ServerHost, NodeTree, Plugins),
State = #state{host = Host,
server_host = ServerHost,
@@ -2170,13 +2171,16 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
case transaction(CreateNode, transaction) of
{result, {NodeId, {Result, broadcast}}} ->
broadcast_created_node(Host, Node, NodeId, Type, NodeOptions),
ejabberd_hooks:run(pubsub_create_node, ServerHost, [ServerHost, Host, Node, NodeId, NodeOptions]),
case Result of
default -> {result, Reply};
_ -> {result, Result}
end;
{result, {_NodeId, default}} ->
{result, {NodeId, default}} ->
ejabberd_hooks:run(pubsub_create_node, ServerHost, [ServerHost, Host, Node, NodeId, NodeOptions]),
{result, Reply};
{result, {_NodeId, Result}} ->
{result, {NodeId, Result}} ->
ejabberd_hooks:run(pubsub_create_node, ServerHost, [ServerHost, Host, Node, NodeId, NodeOptions]),
{result, Result};
Error ->
%% in case we change transaction to sync_dirty...
@@ -2219,6 +2223,7 @@ delete_node(Host, Node, Owner) ->
end
end,
Reply = [],
ServerHost = get(server_host),
case transaction(Host, Node, Action, transaction) of
{result, {_, {Result, broadcast, Removed}}} ->
lists:foreach(fun({RNode, _RSubscriptions}) ->
@@ -2227,20 +2232,31 @@ delete_node(Host, Node, Owner) ->
Type = RNode#pubsub_node.type,
Options = RNode#pubsub_node.options,
broadcast_removed_node(RH, RN, Nidx, Type, Options),
ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, RH, RN, Nidx]),
unset_cached_item(RH, Nidx)
end, Removed),
case Result of
default -> {result, Reply};
_ -> {result, Result}
end;
{result, {_, {Result, _Removed}}} ->
{result, {_, {Result, Removed}}} ->
lists:foreach(fun({RNode, _RSubscriptions}) ->
{RH, RN} = RNode#pubsub_node.id,
Nidx = RNode#pubsub_node.idx,
ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, RH, RN, Nidx]),
unset_cached_item(RH, Nidx)
end, Removed),
case Result of
default -> {result, Reply};
_ -> {result, Result}
end;
{result, {_, default}} ->
{result, {TNode, default}} ->
Nidx = TNode#pubsub_node.idx,
ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, Host, Node, Nidx]),
{result, Reply};
{result, {_, Result}} ->
{result, {TNode, Result}} ->
Nidx = TNode#pubsub_node.idx,
ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, Host, Node, Nidx]),
{result, Result};
Error ->
Error
@@ -2420,8 +2436,8 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
DeliverPayloads = get_option(Options, deliver_payloads),
PersistItems = get_option(Options, persist_items),
MaxItems = case PersistItems of
0 -> 0;
1 -> max_items(Host, Options)
false -> 0;
true -> max_items(Host, Options)
end,
{PayloadCount, PayloadNS} = payload_els_ns(Payload),
PayloadSize = size(term_to_binary(Payload))-2, % size(term_to_binary([])) == 2
@@ -2467,11 +2483,17 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
Nidx = TNode#pubsub_node.idx,
Type = TNode#pubsub_node.type,
Options = TNode#pubsub_node.options,
BrPayload = case Broadcast of
broadcast -> Payload;
PluginPayload -> PluginPayload
end,
broadcast_publish_item(Host, Node, Nidx, Type, Options, ItemId, jlib:short_prepd_jid(Publisher), BrPayload, Removed),
case get_option(Options, deliver_notifications) of
true ->
BrPayload = case Broadcast of
broadcast -> Payload;
PluginPayload -> PluginPayload
end,
broadcast_publish_item(Host, Node, Nidx, Type, Options, ItemId,
jlib:short_prepd_jid(Publisher), BrPayload, Removed);
false ->
ok
end,
set_cached_item(Host, Nidx, ItemId, Publisher, Payload),
case Result of
default -> {result, Reply};
@@ -2503,8 +2525,11 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
case lists:member("auto-create", features(Type)) of
true ->
case create_node(Host, ServerHost, Node, Publisher, Type) of
{result, _} ->
publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload);
{result, Reply2} ->
NewNode = exmpp_xml:get_path(Reply2, [{element, 'create'},
{attribute, <<"node">>}]),
publish_item(Host, ServerHost, NewNode, Publisher, ItemId,
Payload);
_ ->
{error, 'item-not-found'}
end;
+38 -13
View File
@@ -203,7 +203,7 @@ stop(Host) ->
init([ServerHost, Opts]) ->
?DEBUG("pubsub init ~p ~p",[ServerHost,Opts]),
Host = gen_mod:expand_host_name(ServerHost, Opts, "pubsub"),
Host = gen_mod:get_opt_host(ServerHost, Opts, "pubsub.@HOST@"),
Access = gen_mod:get_opt('access_createnode', Opts, 'all'),
PepOffline = gen_mod:get_opt('ignore_pep_from_offline', Opts, true),
IQDisc = gen_mod:get_opt('iqdisc', Opts, 'one_queue'),
@@ -248,6 +248,7 @@ init([ServerHost, Opts]) ->
ok
end,
ejabberd_router:register_route(Host),
put(server_host, ServerHost),
init_nodes(Host, ServerHost, NodeTree, Plugins),
State = #state{host = Host,
server_host = ServerHost,
@@ -1964,13 +1965,16 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
case transaction(Host, CreateNode, transaction) of
{result, {NodeId, {Result, broadcast}}} ->
broadcast_created_node(Host, Node, NodeId, Type, NodeOptions),
ejabberd_hooks:run(pubsub_create_node, ServerHost, [ServerHost, Host, Node, NodeId, NodeOptions]),
case Result of
default -> {result, Reply};
_ -> {result, Result}
end;
{result, {_NodeId, default}} ->
{result, {NodeId, default}} ->
ejabberd_hooks:run(pubsub_create_node, ServerHost, [ServerHost, Host, Node, NodeId, NodeOptions]),
{result, Reply};
{result, {_NodeId, Result}} ->
{result, {NodeId, Result}} ->
ejabberd_hooks:run(pubsub_create_node, ServerHost, [ServerHost, Host, Node, NodeId, NodeOptions]),
{result, Result};
Error ->
%% in case we change transaction to sync_dirty...
@@ -2013,6 +2017,7 @@ delete_node(Host, Node, Owner) ->
end
end,
Reply = [],
ServerHost = get(server_host),
case transaction(Host, Node, Action, transaction) of
{result, {_, {Result, broadcast, Removed}}} ->
lists:foreach(fun({RNode, _RSubscriptions}) ->
@@ -2021,20 +2026,31 @@ delete_node(Host, Node, Owner) ->
Type = RNode#pubsub_node.type,
Options = RNode#pubsub_node.options,
broadcast_removed_node(RH, RN, Nidx, Type, Options),
ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, RH, RN, Nidx]),
unset_cached_item(RH, Nidx)
end, Removed),
case Result of
default -> {result, Reply};
_ -> {result, Result}
end;
{result, {_, {Result, _Removed}}} ->
{result, {_, {Result, Removed}}} ->
lists:foreach(fun({RNode, _RSubscriptions}) ->
{RH, RN} = RNode#pubsub_node.id,
Nidx = RNode#pubsub_node.idx,
ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, RH, RN, Nidx]),
unset_cached_item(RH, Nidx)
end, Removed),
case Result of
default -> {result, Reply};
_ -> {result, Result}
end;
{result, {_, default}} ->
{result, {TNode, default}} ->
Nidx = TNode#pubsub_node.idx,
ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, Host, Node, Nidx]),
{result, Reply};
{result, {_, Result}} ->
{result, {TNode, Result}} ->
Nidx = TNode#pubsub_node.idx,
ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, Host, Node, Nidx]),
{result, Result};
Error ->
Error
@@ -2259,11 +2275,17 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
Nidx = TNode#pubsub_node.idx,
Type = TNode#pubsub_node.type,
Options = TNode#pubsub_node.options,
BrPayload = case Broadcast of
broadcast -> Payload;
PluginPayload -> PluginPayload
end,
broadcast_publish_item(Host, Node, Nidx, Type, Options, ItemId, jlib:short_prepd_jid(Publisher), BrPayload, Removed),
case get_option(Options, deliver_notifications) of
true ->
BrPayload = case Broadcast of
broadcast -> Payload;
PluginPayload -> PluginPayload
end,
broadcast_publish_item(Host, Node, Nidx, Type, Options, ItemId,
jlib:short_prepd_jid(Publisher), BrPayload, Removed);
false ->
ok
end,
set_cached_item(Host, Nidx, ItemId, Publisher, Payload),
case Result of
default -> {result, Reply};
@@ -2295,8 +2317,11 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
case lists:member("auto-create", features(Type)) of
true ->
case create_node(Host, ServerHost, Node, Publisher, Type) of
{result, _} ->
publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload);
{result, Reply2} ->
NewNode = exmpp_xml:get_path(Reply2, [{element, 'create'},
{attribute, <<"node">>}]),
publish_item(Host, ServerHost, NewNode, Publisher, ItemId,
Payload);
_ ->
{error, 'item-not-found'}
end;
+1 -2
View File
@@ -178,8 +178,7 @@ create_node_permission(Host, ServerHost, _NodeId, _ParentNodeId, #jid{node = U,
).
create_node(NodeIdx, JID) ->
{result, _} = node_flat:create_node(NodeIdx, JID),
{result, []}.
node_flat:create_node(NodeIdx, JID).
% case node_flat:create_node(NodeIdx, JID) of
% {result, _} -> {result, []};
% Error -> Error
+263 -76
View File
@@ -1,5 +1,5 @@
--- mod_pubsub.erl 2011-03-23 09:31:16.000000000 +0100
+++ mod_pubsub_odbc.erl 2011-03-23 09:51:43.000000000 +0100
--- mod_pubsub.erl 2011-11-29 15:46:28.000000000 +0100
+++ mod_pubsub_odbc.erl 2011-11-29 15:47:30.000000000 +0100
@@ -42,7 +42,7 @@
%%% 6.2.3.1, 6.2.3.5, and 6.3. For information on subscription leases see
%%% XEP-0060 section 12.18.
@@ -46,10 +46,10 @@
ejabberd_router:register_route(Host),
- update_node_database(Host, ServerHost),
- update_state_database(Host, ServerHost),
put(server_host, ServerHost),
init_nodes(Host, ServerHost, NodeTree, Plugins),
State = #state{host = Host,
server_host = ServerHost,
@@ -351,229 +349,15 @@
@@ -352,229 +350,15 @@
init_nodes(Host, ServerHost, _NodeTree, Plugins) ->
%% TODO, this call should be done plugin side
@@ -282,7 +282,7 @@
-spec(send_loop/1 ::
(
State::#state{})
@@ -590,7 +374,10 @@
@@ -591,14 +375,17 @@
%% for each node From is subscribed to
%% and if the node is so configured, send the last published item to From
lists:foreach(fun(PType) ->
@@ -294,37 +294,70 @@
lists:foreach(
fun({Node, subscribed, _, SubJID}) ->
if (SubJID == LJID) or (SubJID == BJID) ->
@@ -763,7 +550,8 @@
- #pubsub_node{id = {H, NodeId}, type = Type, idx = Nidx, options = Options} = Node,
+ #pubsub_node{id = {H, NodeId}, type = Type, idx = NodeIdx, options = Options} = Node,
case get_option(Options, 'send_last_published_item') of
'on_sub_and_presence' ->
- send_items(H, NodeId, Nidx, Type, LJID, 'last');
+ send_items(H, NodeId, NodeIdx, Type, LJID, 'last');
_ ->
ok
end;
@@ -647,7 +434,7 @@
spawn(fun() ->
Host = State#state.host,
Owner = {U,S,undefined},
- lists:foreach(fun(#pubsub_node{id = {_, NodeId}, type = Type, idx = Nidx, options = Options}) ->
+ lists:foreach(fun(#pubsub_node{id = {_, NodeId}, type = Type, idx = NodeIdx, options = Options}) ->
case get_option(Options, 'send_last_published_item') of
'on_sub_and_presence' ->
lists:foreach(fun(Resource) ->
@@ -661,7 +448,7 @@
RosterGroups = get_option(Options, 'roster_groups_allowed', []),
element(2, get_roster_info(U, S, LJID, RosterGroups))
end,
- if Subscribed -> send_items(Owner, NodeId, Nidx, Type, LJID, 'last');
+ if Subscribed -> send_items(Owner, NodeId, NodeIdx, Type, LJID, 'last');
true -> ok
end
end, Resources);
@@ -764,8 +551,9 @@
[#xmlel{name = 'identity', ns = ?NS_DISCO_INFO,
attrs = [?XMLATTR(<<"category">>, <<"pubsub">>), ?XMLATTR(<<"type">>, <<"pep">>)]}];
disco_identity(#jid{node = U, domain = S, resource = R} = Host, NodeId, From) ->
- Action = fun(#pubsub_node{idx = NodeIdx, type = Type, options = Options, owners = Owners}) ->
- Action = fun(#pubsub_node{idx = Nidx, type = Type, options = Options, owners = Owners}) ->
- case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of
+ Action = fun(#pubsub_node{idx = NodeIdx, type = Type, options = Options}) ->
+ Owners = node_owners_call(Type, NodeIdx),
case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) of
+ case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) of
{result, _} ->
{result,
@@ -813,7 +601,8 @@
[#xmlel{name = 'identity', ns = ?NS_DISCO_INFO,
@@ -814,8 +602,9 @@
[?NS_PUBSUB_s
| [?NS_PUBSUB_s++"#"++Feature || Feature <- features("pep")]];
disco_features(#jid{node = U, domain = S, resource = R} = Host, NodeId, From) ->
- Action = fun(#pubsub_node{idx = NodeIdx, type = Type, options = Options, owners = Owners}) ->
- Action = fun(#pubsub_node{idx = Nidx, type = Type, options = Options, owners = Owners}) ->
- case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of
+ Action = fun(#pubsub_node{idx = NodeIdx, type = Type, options = Options}) ->
+ Owners = node_owners_call(Type, NodeIdx),
case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) of
+ case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) of
{result, _} ->
{result, [?NS_PUBSUB_s
@@ -853,7 +642,8 @@
| [?NS_PUBSUB_s ++ "#" ++ Feature || Feature <- features("pep")]]};
@@ -854,8 +643,9 @@
).
disco_items(#jid{raw = JID, node = U, domain = S, resource = R} = Host, <<>>, From) ->
- Action = fun(#pubsub_node{id ={_, NodeId}, options = Options, type = Type, idx = NodeIdx, owners = Owners}, Acc) ->
- Action = fun(#pubsub_node{id ={_, NodeId}, options = Options, type = Type, idx = Nidx, owners = Owners}, Acc) ->
- case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of
+ Action = fun(#pubsub_node{id ={_, NodeId}, options = Options, type = Type, idx = NodeIdx}, Acc) ->
+ Owners = node_owners_call(Type, NodeIdx),
case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) of
+ case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) of
{result, _} ->
[#xmlel{name = 'item', ns = ?NS_DISCO_INFO,
@@ -867,13 +657,14 @@
attrs = [?XMLATTR(<<"jid">>, JID),
@@ -868,14 +658,15 @@
_ -> Acc
end
end,
@@ -335,13 +368,15 @@
end;
disco_items(#jid{raw = JID, node = U, domain = S, resource = R} = Host, NodeId, From) ->
- Action = fun(#pubsub_node{idx = NodeIdx, type = Type, options = Options, owners = Owners}) ->
- Action = fun(#pubsub_node{idx = Nidx, type = Type, options = Options, owners = Owners}) ->
- case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of
+ Action = fun(#pubsub_node{idx = NodeIdx, type = Type, options = Options}) ->
+ Owners = node_owners_call(Type, NodeIdx),
case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) of
+ case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) of
{result, Items} ->
{result,
@@ -982,10 +773,10 @@
[#xmlel{name = 'item', ns = ?NS_DISCO_INFO,
@@ -983,10 +774,10 @@
lists:foreach(fun(PType) ->
{result, Subscriptions} = node_action(Host, PType, get_entity_subscriptions, [Host, Entity]),
lists:foreach(fun
@@ -354,7 +389,7 @@
true ->
node_action(Host, PType, unsubscribe_node, [Nidx, Entity, JID, all]);
false ->
@@ -1183,9 +974,10 @@
@@ -1184,9 +975,10 @@
end,
ejabberd_router:route(To, From, Res);
%% Service discovery : disco#items
@@ -367,16 +402,21 @@
{result, IQRes} ->
Result = #xmlel{ns = ?NS_DISCO_ITEMS,
name = 'query',
@@ -1335,7 +1127,7 @@
@@ -1332,11 +1124,11 @@
).
node_disco_info(Host, NodeId, From) ->
- Action = fun(#pubsub_node{type = Plugin, idx = Nidx}) ->
+ Action = fun(#pubsub_node{type = Plugin, idx = NodeIdx}) ->
Types = case tree_call(Host, get_subnodes, [Host, NodeId, From]) of
[] -> ["leaf"];
_ ->
- case node_call(Plugin, get_items, [NodeIdx, From]) of
- case node_call(Plugin, get_items, [Nidx, From]) of
+ case node_call(Plugin, get_items, [NodeIdx, From, none]) of
{result, []} -> ["collection"];
{result, _} -> ["leaf", "collection"];
_ -> []
@@ -1353,10 +1145,15 @@
@@ -1354,10 +1146,15 @@
[#xmlel{ns = ?NS_DISCO_INFO,
name = 'feature',
attrs = [?XMLATTR(<<"var">>, ?NS_PUBSUB_b)]} |
@@ -396,7 +436,7 @@
end,
case transaction(Host, NodeId, Action, sync_dirty) of
{result, {_, Result}} -> {result, Result};
@@ -1400,10 +1197,10 @@
@@ -1401,10 +1198,10 @@
name = 'feature',
attrs = [?XMLATTR(<<"var">>, ?NS_VCARD_b)]}]
++
@@ -411,7 +451,7 @@
iq_disco_info(Host, NodeId, From, _Lang)
when NodeId == ?NS_ADHOC_b orelse NodeId == ?NS_PUBSUB_GET_PENDING_b ->
command_disco_info(Host, NodeId, From);
@@ -1411,16 +1208,17 @@
@@ -1412,16 +1209,17 @@
node_disco_info(Host, NodeId, From).
@@ -432,7 +472,7 @@
case tree_action(Host, get_subnodes, [Host, <<>>, From]) of
Nodes when is_list(Nodes) ->
{result, lists:map(
@@ -1437,7 +1235,7 @@
@@ -1438,7 +1236,7 @@
Other ->
Other
end;
@@ -441,7 +481,7 @@
%% TODO: support localization of this string
{result,
[#xmlel{ns = ?NS_DISCO_ITEMS,
@@ -1445,14 +1243,15 @@
@@ -1446,14 +1244,15 @@
attrs = [?XMLATTR(<<"jid">>, Host),
?XMLATTR(<<"node">>, ?NS_PUBSUB_GET_PENDING_b),
?XMLATTR(<<"name">>, "Get Pending")]}]};
@@ -450,8 +490,8 @@
%% TODO
{result, []};
-iq_disco_items(Host, NodeId, From) ->
- Action = fun(#pubsub_node{idx = NodeIdx, type = Type, options = Options, owners = Owners}) ->
- NodeItems = case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) of
- Action = fun(#pubsub_node{idx = Nidx, type = Type, options = Options, owners = Owners}) ->
- NodeItems = case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of
+iq_disco_items(Host, NodeId, From, Rsm) ->
+ Action = fun(#pubsub_node{idx = NodeIdx, type = Type, options = Options}) ->
+ Owners = node_owners_call(Type, NodeIdx),
@@ -462,7 +502,7 @@
end,
Nodes = lists:map(
fun(#pubsub_node{id = {_, SubNodeId}, options = SubOptions}) ->
@@ -1475,7 +1274,7 @@
@@ -1476,7 +1275,7 @@
attrs = [?XMLATTR(<<"jid">>, Host),
?XMLATTR(<<"name">>, Name)]}
end, NodeItems),
@@ -471,7 +511,7 @@
end,
case transaction(Host, NodeId, Action, sync_dirty) of
{result, {_, Result}} -> {result, Result};
@@ -1484,12 +1283,6 @@
@@ -1485,12 +1284,6 @@
@@ -484,7 +524,7 @@
get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups) ->
if (AccessModel == presence) or (AccessModel == roster) ->
case Host of
@@ -1645,7 +1438,7 @@
@@ -1646,7 +1439,7 @@
end;
(_, Acc) -> Acc
end, [], exmpp_xml:remove_cdata_from_list(SubEls)),
@@ -493,7 +533,7 @@
{'get', 'subscriptions'} ->
get_subscriptions(Host, NodeId, From, Plugins);
{'get', 'affiliations'} ->
@@ -1836,7 +1629,8 @@
@@ -1837,7 +1630,8 @@
_ -> []
end
end,
@@ -503,7 +543,7 @@
sync_dirty) of
{result, Res} -> Res;
Err -> Err
@@ -1880,7 +1674,7 @@
@@ -1881,7 +1675,7 @@
%%% authorization handling
@@ -512,7 +552,7 @@
Lang = <<"en">>, %% TODO fix
{U, S, R} = Subscriber,
Stanza = #xmlel{ns = ?NS_JABBER_CLIENT, name = 'message', children =
@@ -1910,7 +1704,7 @@
@@ -1911,7 +1705,7 @@
lists:foreach(fun(Owner) ->
{U, S, R} = Owner,
ejabberd_router:route(service_jid(Host), exmpp_jid:make(U, S, R), Stanza)
@@ -521,7 +561,7 @@
find_authorization_response(Packet) ->
Els = Packet#xmlel.children,
@@ -1969,8 +1763,8 @@
@@ -1970,8 +1764,8 @@
"true" -> true;
_ -> false
end,
@@ -532,7 +572,7 @@
{result, Subscriptions} = node_call(Type, get_subscriptions, [Nidx, Subscriber]),
if
not IsApprover ->
@@ -2167,7 +1961,7 @@
@@ -2168,7 +1962,7 @@
end,
Reply = #xmlel{ns = ?NS_PUBSUB, name = 'pubsub', children =
[#xmlel{ns = ?NS_PUBSUB, name = 'create', attrs = nodeAttr(Node)}]},
@@ -540,8 +580,8 @@
+ case transaction(Host, CreateNode, transaction) of
{result, {NodeId, {Result, broadcast}}} ->
broadcast_created_node(Host, Node, NodeId, Type, NodeOptions),
case Result of
@@ -2269,7 +2063,7 @@
ejabberd_hooks:run(pubsub_create_node, ServerHost, [ServerHost, Host, Node, NodeId, NodeOptions]),
@@ -2285,7 +2079,7 @@
%%<li>The node does not exist.</li>
%%</ul>
subscribe_node(Host, Node, From, JID, Configuration) ->
@@ -550,7 +590,7 @@
{result, GoodSubOpts} -> GoodSubOpts;
_ -> invalid
end,
@@ -2279,7 +2073,7 @@
@@ -2295,7 +2089,7 @@
_:_ ->
{undefined, undefined, undefined}
end,
@@ -559,7 +599,7 @@
Features = features(Type),
SubscribeFeature = lists:member("subscribe", Features),
OptionsFeature = lists:member("subscription-options", Features),
@@ -2288,6 +2082,7 @@
@@ -2304,6 +2098,7 @@
AccessModel = get_option(Options, access_model),
SendLast = get_option(Options, send_last_published_item),
AllowedGroups = get_option(Options, roster_groups_allowed, []),
@@ -567,7 +607,21 @@
{PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, Subscriber, Owners, AccessModel, AllowedGroups),
if
not SubscribeFeature ->
@@ -2633,7 +2428,7 @@
@@ -2433,12 +2228,9 @@
Features = features(Type),
PublishFeature = lists:member("publish", Features),
PublishModel = get_option(Options, publish_model),
+ MaxItems = max_items(Host, Options),
DeliverPayloads = get_option(Options, deliver_payloads),
PersistItems = get_option(Options, persist_items),
- MaxItems = case PersistItems of
- false -> 0;
- true -> max_items(Host, Options)
- end,
{PayloadCount, PayloadNS} = payload_els_ns(Payload),
PayloadSize = size(term_to_binary(Payload))-2, % size(term_to_binary([])) == 2
PayloadMaxSize = get_option(Options, max_payload_size),
@@ -2661,7 +2453,7 @@
%% <p>The permission are not checked in this function.</p>
%% @todo We probably need to check that the user doing the query has the right
%% to read the items.
@@ -576,7 +630,7 @@
MaxItems =
if
SMaxItems == "" -> get_max_items_node(Host);
@@ -2647,12 +2442,13 @@
@@ -2675,12 +2467,13 @@
{error, Error} ->
{error, Error};
_ ->
@@ -591,7 +645,7 @@
{PresenceSubscription, RosterGroup} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups),
if
not RetreiveFeature ->
@@ -2665,11 +2461,11 @@
@@ -2693,11 +2486,11 @@
node_call(Type, get_items,
[Nidx, From,
AccessModel, PresenceSubscription, RosterGroup,
@@ -605,7 +659,7 @@
SendItems = case ItemIds of
[] ->
Items;
@@ -2682,7 +2478,7 @@
@@ -2710,7 +2503,7 @@
%% number of items sent to MaxItems:
{result, #xmlel{ns = ?NS_PUBSUB, name = 'pubsub', children =
[#xmlel{ns = ?NS_PUBSUB, name = 'items', attrs = nodeAttr(Node), children =
@@ -614,7 +668,27 @@
Error ->
Error
end
@@ -2723,6 +2519,17 @@
@@ -2726,8 +2519,8 @@
).
get_items(Host, NodeId) ->
- Action = fun(#pubsub_node{type = Type, idx = Nidx}) ->
- node_call(Type, get_items, [Nidx, service_jid(Host)])
+ Action = fun(#pubsub_node{type = Type, idx = NodeIdx}) ->
+ node_call(Type, get_items, [NodeIdx, service_jid(Host)])
end,
case transaction(Host, NodeId, Action, sync_dirty) of
{result, {_, Items}} -> Items
@@ -2744,13 +2537,24 @@
).
get_item(Host, NodeId, ItemId) ->
- Action = fun(#pubsub_node{type = Type, idx = Nidx}) ->
- node_call(Type, get_item, [Nidx, ItemId])
+ Action = fun(#pubsub_node{type = Type, idx = NodeIdx}) ->
+ node_call(Type, get_item, [NodeIdx, ItemId])
end,
case transaction(Host, NodeId, Action, sync_dirty) of
{result, {_, Items}} -> Items;
Error -> Error
end.
@@ -632,14 +706,16 @@
%% @spec (Host, Node, NodeId, Type, LJID, Number) -> any()
%% Host = pubsubHost()
@@ -2734,16 +2541,29 @@
@@ -2761,32 +2565,32 @@
%% Number = last | integer()
%% @doc <p>Resend the items of a node to the user.</p>
%% @todo use cache-last-item feature
send_items(Host, Node, NodeId, Type, LJID, 'last') ->
- case get_cached_item(Host, NodeId) of
-send_items(Host, Node, Nidx, Type, {U,S,R} = LJID, 'last') ->
- case get_cached_item(Host, Nidx) of
+send_items(Host, Node, NodeId, Type, LJID, 'last') ->
+ Stanza = case get_cached_item(Host, NodeId) of
undefined ->
- send_items(Host, Node, NodeId, Type, LJID, 1);
- send_items(Host, Node, Nidx, Type, LJID, 1);
+ % special ODBC optimization, works only with node_hometree_odbc, node_flat_odbc and node_pep_odbc
+ case node_action(Host, Type, get_last_items, [NodeId, LJID, 1]) of
+ {result, [LastItem]} ->
@@ -659,16 +735,55 @@
+ event_stanza_with_delay(
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = nodeAttr(Node),
- children = itemsEls([LastItem])}], ModifNow, ModifUSR),
- ejabberd_router:route(service_jid(Host), exmpp_jid:make(LJID), Stanza)
- case is_tuple(Host) of
- false ->
- ejabberd_router:route(service_jid(Host), exmpp_jid:make(U, S, R), Stanza);
- true ->
- case ejabberd_sm:get_session_pid(U,S,R) of
- C2SPid when is_pid(C2SPid) ->
- ejabberd_c2s:broadcast(C2SPid,
- {pep_message, << Node/binary, <<"+notify">>/binary >>},
- _Sender = service_jid(Host),
- Stanza);
- _ ->
- ok
- end
- end
- end;
-send_items(Host, Node, Nidx, Type, {U,S,R} = LJID, Number) ->
- ToSend = case node_action(Host, Type, get_items, [Nidx, LJID]) of
+ children = itemsEls([LastItem])}], ModifNow, ModifUSR)
+ end,
+ {U, S, R} = LJID,
+ ejabberd_router:route(service_jid(Host), exmpp_jid:make(U, S, R), Stanza);
send_items(Host, Node, NodeId, Type, {LU, LS, LR} = LJID, Number) ->
ToSend = case node_action(Host, Type, get_items, [NodeId, LJID]) of
+send_items(Host, Node, NodeId, Type, {LU, LS, LR} = LJID, Number) ->
+ ToSend = case node_action(Host, Type, get_items, [NodeId, LJID]) of
{result, []} ->
@@ -2870,7 +2690,8 @@
[];
{result, Items} ->
@@ -2808,20 +2612,7 @@
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = nodeAttr(Node), children =
itemsEls(ToSend)}])
end,
- case is_tuple(Host) of
- false ->
- ejabberd_router:route(service_jid(Host), exmpp_jid:make(U, S, R), Stanza);
- true ->
- case ejabberd_sm:get_session_pid(U,S,R) of
- C2SPid when is_pid(C2SPid) ->
- ejabberd_c2s:broadcast(C2SPid,
- {pep_message, << Node/binary, <<"+notify">>/binary >>},
- _Sender = service_jid(Host),
- Stanza);
- _ ->
- ok
- end
- end.
+ ejabberd_router:route(service_jid(Host), exmpp_jid:make(LU, LS, LR), Stanza).
%% @spec (Host, JID, Plugins) -> {error, Reason} | {result, Response}
%% Host = host()
@@ -2924,7 +2715,8 @@
error ->
{error, 'bad-request'};
_ ->
@@ -678,7 +793,7 @@
case lists:member(Owner, Owners) of
true ->
OwnerJID = exmpp_jid:make(Owner),
@@ -2880,24 +2701,8 @@
@@ -2934,24 +2726,8 @@
end,
lists:foreach(
fun({JID, Affiliation}) ->
@@ -705,11 +820,40 @@
end, FilteredEntities),
{result, []};
_ ->
@@ -2952,9 +2757,9 @@
@@ -2980,7 +2756,7 @@
Error -> Error
end.
read_sub(Subscriber, Node, NodeId, SubId, Lang) ->
- case pubsub_subscription:get_subscription(Subscriber, NodeId, SubId) of
-get_options_helper(JID, Lang, Node, Nidx, SubId, Type) ->
+get_options_helper(JID, Lang, Node, NodeId, SubId, Type) ->
Subscriber = try exmpp_jid:parse(JID) of
J -> jlib:short_jid(J)
catch
@@ -2988,7 +2764,7 @@
exmpp_jid:make("", "", "") %% TODO, check if use <<>> instead of ""
end,
{result, Subs} = node_call(Type, get_subscriptions,
- [Nidx, Subscriber]),
+ [NodeId, Subscriber]),
SubIds = lists:foldl(fun({subscribed, SID}, Acc) ->
[SID | Acc];
(_, Acc) ->
@@ -2998,17 +2774,17 @@
{_, []} ->
{error, extended_error('not-acceptable', "not-subscribed")};
{[], [SID]} ->
- read_sub(Subscriber, Node, Nidx, SID, Lang);
+ read_sub(Subscriber, Node, NodeId, SID, Lang);
{[], _} ->
{error, extended_error('not-acceptable', "subid-required")};
{_, _} ->
- read_sub(Subscriber, Node, Nidx, SubId, Lang)
+ read_sub(Subscriber, Node, NodeId, SubId, Lang)
end.
-read_sub(Subscriber, Node, Nidx, SubId, Lang) ->
- case pubsub_subscription:get_subscription(Subscriber, Nidx, SubId) of
+read_sub(Subscriber, Node, NodeId, SubId, Lang) ->
+ case pubsub_subscription_odbc:get_subscription(Subscriber, NodeId, SubId) of
{result, #pubsub_subscription{options = Options}} ->
- {result, XdataEl} = pubsub_subscription:get_options_xform(Lang, Options),
@@ -717,25 +861,53 @@
OptionsEl = #xmlel{ns = ?NS_PUBSUB, name = 'options',
attrs = [ ?XMLATTR(<<"jid">>, exmpp_jid:to_binary(Subscriber)),
?XMLATTR(<<"subid">>, SubId) | nodeAttr(Node)],
@@ -2987,7 +2792,7 @@
@@ -3040,8 +2816,8 @@
Error -> Error
end.
set_options_helper(Configuration, JID, NodeId, SubId, Type) ->
-set_options_helper(Configuration, JID, Nidx, SubId, Type) ->
- SubOpts = case pubsub_subscription:parse_options_xform(Configuration) of
+set_options_helper(Configuration, JID, NodeId, SubId, Type) ->
+ SubOpts = case pubsub_subscription_odbc:parse_options_xform(Configuration) of
{result, GoodSubOpts} -> GoodSubOpts;
_ -> invalid
end,
@@ -3019,7 +2824,7 @@
write_sub(_Subscriber, _NodeID, _SubID, []) ->
@@ -3051,7 +2827,7 @@
_ -> exmpp_jid:make("", "", "") %% TODO, check if use <<>> instead of ""
end,
{result, Subs} = node_call(Type, get_subscriptions,
- [Nidx, Subscriber]),
+ [NodeId, Subscriber]),
SubIds = lists:foldl(fun({subscribed, SID}, Acc) ->
[SID | Acc];
(_, Acc) ->
@@ -3061,19 +2837,19 @@
{_, []} ->
{error, extended_error('not-acceptable', "not-subscribed")};
{[], [SID]} ->
- write_sub(Subscriber, Nidx, SID, SubOpts);
+ write_sub(Subscriber, NodeId, SID, SubOpts);
{[], _} ->
{error, extended_error('not-acceptable', "subid-required")};
{_, _} ->
- write_sub(Subscriber, Nidx, SubId, SubOpts)
+ write_sub(Subscriber, NodeId, SubId, SubOpts)
end.
-write_sub(_Subscriber, _Nidx, _SubId, invalid) ->
+write_sub(_Subscriber, _NodeId, _SubId, invalid) ->
{error, extended_error('bad-request', "invalid-options")};
-write_sub(_Subscriber, _Nidx, _SubId, []) ->
+write_sub(_Subscriber, _NodeID, _SubID, []) ->
{result, []};
write_sub(Subscriber, NodeId, SubId, Options) ->
- case pubsub_subscription:set_subscription(Subscriber, NodeId, SubId, Options) of
-write_sub(Subscriber, Nidx, SubId, Options) ->
- case pubsub_subscription:set_subscription(Subscriber, Nidx, SubId, Options) of
+write_sub(Subscriber, NodeId, SubId, Options) ->
+ case pubsub_subscription_odbc:set_subscription(Subscriber, NodeId, SubId, Options) of
{result, _} ->
{result, []};
{error, _} ->
@@ -3193,8 +2998,8 @@
@@ -3247,8 +3023,8 @@
?XMLATTR(<<"subsription">>, subscription_to_string(Sub)) | nodeAttr(Node)]}]}]},
ejabberd_router:route(service_jid(Host), JID, Stanza)
end,
@@ -746,7 +918,7 @@
true ->
Result = lists:foldl(fun({JID, Subscription, SubId}, Acc) ->
@@ -3548,7 +3353,7 @@
@@ -3602,7 +3378,7 @@
Collection = tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]),
{result, [{Depth, [{N, sub_with_options(N)} || N <- Nodes]} || {Depth, Nodes} <- Collection]}
end,
@@ -755,18 +927,33 @@
{result, CollSubs} -> subscribed_nodes_by_jid(NotifyType, CollSubs);
_ -> []
end.
@@ -3617,8 +3422,8 @@
@@ -3660,19 +3436,19 @@
{_, JIDSubs} = lists:foldl(DepthsToDeliver, {[], []}, SubsByDepth),
JIDSubs.
-sub_with_options(#pubsub_node{type = Type, idx = Nidx}) ->
- case node_call(Type, get_node_subscriptions, [Nidx]) of
+sub_with_options(#pubsub_node{type = Type, id = NodeId}) ->
+ case node_call(Type, get_node_subscriptions, [NodeId]) of
{result, Subs} ->
lists:foldl(
- fun({JID, subscribed, SubId}, Acc) -> [sub_with_options(JID, Nidx, SubId) | Acc];
+ fun({JID, subscribed, SubId}, Acc) -> [sub_with_options(JID, NodeId, SubId) | Acc];
(_, Acc) -> Acc
end, [], Subs);
_ ->
[]
end.
sub_with_options(JID, NodeId, SubId) ->
- case pubsub_subscription:read_subscription(JID, NodeId, SubId) of
-sub_with_options(JID, Nidx, SubId) ->
- case pubsub_subscription:read_subscription(JID, Nidx, SubId) of
- #pubsub_subscription{options = Options} -> {JID, SubId, Options};
+sub_with_options(JID, NodeId, SubId) ->
+ case pubsub_subscription_odbc:read_subscription(JID, NodeId, SubId) of
+ {result, #pubsub_subscription{options = Options}} -> {JID, SubId, Options};
_ -> {JID, SubId, []}
end.
@@ -3730,6 +3535,30 @@
@@ -3784,6 +3560,30 @@
Result
end.
@@ -797,7 +984,7 @@
%% @spec (Host, Options) -> MaxItems
%% Host = host()
%% Options = [Option]
@@ -4253,9 +4082,14 @@
@@ -4307,9 +4107,14 @@
tree_action(Host, Function, Args) ->
?DEBUG("tree_action ~p ~p ~p",[Host,Function,Args]),
@@ -815,7 +1002,7 @@
%% @doc <p>node plugin call.</p>
-spec(node_call/3 ::
@@ -4293,7 +4127,7 @@
@@ -4347,7 +4152,7 @@
node_action(Host, Type, Function, Args) ->
?DEBUG("node_action ~p ~p ~p ~p",[Host,Type,Function,Args]),
@@ -824,7 +1011,7 @@
node_call(Type, Function, Args)
end, sync_dirty).
@@ -4308,7 +4142,7 @@
@@ -4362,7 +4167,7 @@
).
transaction(Host, NodeId, Action, Trans) ->
@@ -833,7 +1020,7 @@
case tree_call(Host, get_node, [Host, NodeId]) of
#pubsub_node{} = Node ->
case Action(Node) of
@@ -4322,7 +4156,7 @@
@@ -4376,7 +4181,7 @@
end, Trans).
@@ -842,7 +1029,7 @@
(
Host :: string() | host(),
Action :: fun(),
@@ -4330,21 +4164,28 @@
@@ -4384,21 +4189,28 @@
-> {'result', Nodes :: [] | [Node::pubsubNode()]}
).
@@ -876,7 +1063,7 @@
{result, Result} -> {result, Result};
{error, Error} -> {error, Error};
{atomic, {result, Result}} -> {result, Result};
@@ -4352,6 +4193,15 @@
@@ -4406,6 +4218,15 @@
{aborted, Reason} ->
?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]),
{error, 'internal-server-error'};
@@ -892,7 +1079,7 @@
{'EXIT', Reason} ->
?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]),
{error, 'internal-server-error'};
@@ -4360,6 +4210,16 @@
@@ -4414,6 +4235,16 @@
{error, 'internal-server-error'}
end.
+3 -2
View File
@@ -40,8 +40,9 @@
-include("ejabberd.hrl").
start(Host, Opts) ->
HostB = list_to_binary(Host),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
gen_iq_handler:add_iq_handler(ejabberd_local, HostB, ?NS_INBAND_REGISTER,
?MODULE, process_iq, IQDisc),
+14 -11
View File
@@ -134,26 +134,27 @@
%% Opts = list()
start(Host, Opts) when is_list(Host) ->
HostB = list_to_binary(Host),
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
Backend = gen_mod:get_opt(backend, Opts, mnesia),
gen_storage:create_table(Backend, HostB,
rosteritem, [{disc_copies, [node()]},
{odbc_host, Host},
{odbc_host, HostB},
{attributes, record_info(fields, rosteritem)},
{types, [{user_host_jid, {text, text, ljid}},
{subscription, atom},
{ask, atom}]}]),
gen_storage:create_table(Backend, HostB,
rostergroup, [{disc_copies, [node()]},
{odbc_host, Host},
{odbc_host, HostB},
{type, bag},
{attributes, record_info(fields, rostergroup)},
{types, [{user_host_jid, {text, text, ljid}}]}]),
mnesia:create_table(roster_version, [{disc_copies, [node()]},
{attributes, record_info(fields, roster_version)}]),
update_table(Host, Backend),
update_table(HostB, Backend),
mnesia:add_table_index(roster, us),
mnesia:add_table_index(roster_version, us),
ejabberd_hooks:add(roster_get, HostB,
@@ -383,7 +384,7 @@ storageroster_to_roster(#rosteritem{user_host_jid = {U, S, JID} = USJ,
fun(#rostergroup{user_host_jid = USJ1, grp = G}, R)
when USJ =:= USJ1 ->
%% G is a string when using odbc beckend, and a binary when using mnesia
GString = convert_to_string(G),
GString = convert_to_binary(G),
[GString | R];
(_, R) ->
R
@@ -391,14 +392,14 @@ storageroster_to_roster(#rosteritem{user_host_jid = {U, S, JID} = USJ,
#roster{usj = {U, S, JID},
us = US,
jid = JID,
name = convert_to_string(Name),
name = convert_to_binary(Name),
subscription = Subscription,
ask = Ask,
askmessage = AskMessage,
groups = Groups}.
convert_to_string(A) when is_binary(A) -> binary_to_list(A);
convert_to_string(A) when is_list(A) -> A.
convert_to_binary(A) when is_list(A) -> list_to_binary(A);
convert_to_binary(A) when is_binary(A) -> A.
%% @spec (Item) -> XML
%% Item = rosteritem()
@@ -1228,10 +1229,12 @@ get_jid_info(_, User, Server, JID)
%% Only supports migration from ejabberd 1.1.2 or higher.
update_table(Host, mnesia) ->
HostB = list_to_binary(Host),
update_table(global, Storage) ->
[update_table(HostB, Storage) || HostB <- ejabberd_hosts:get_hosts(ejabberd)];
update_table(HostB, mnesia) ->
gen_storage_migration:migrate_mnesia(
Host, rosteritem,
HostB, rosteritem,
[{roster, [usj, us, jid, name, subscription, ask, groups, askmessage, xs],
fun({roster, USJ, _, _, Name, Subscription, Ask, Groups, AskMessage, _Xs}) ->
%% Convert "" to undefined in JIDs and string() to binary().
+35 -18
View File
@@ -63,8 +63,9 @@
-record(sr_group, {group_host, opts}).
-record(sr_user, {us, group_host}).
start(Host, _Opts) ->
HostB = list_to_binary(Host),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, _Opts) ->
mnesia:create_table(sr_group,
[{disc_copies, [node()]},
{attributes, record_info(fields, sr_group)}]),
@@ -145,9 +146,11 @@ get_user_roster(Items, US) ->
fun(Group, Acc1) ->
GroupName = get_group_name(S, Group),
lists:foldl(
fun(UserServer, Acc2) ->
fun({User, Server} = UserServer, Acc2) ->
if UserServer == US -> Acc2;
true -> dict:append(UserServer,
true ->
UserServerB = {list_to_binary(User), list_to_binary(Server)},
dict:append(UserServerB,
GroupName,
Acc2)
end
@@ -172,8 +175,8 @@ get_user_roster(Items, US) ->
%% Export items in roster format:
ModVcard = get_vcard_module(S),
SRItems = [#roster{usj = {U, S, {U1, S1, undefined}},
us = US,
SRItems = [ #roster{usj = {U, S, {U1, S1, undefined}},
us = {U, S},
jid = {U1, S1, undefined},
name = get_rosteritem_name(ModVcard, U1, S1),
subscription = both,
@@ -332,7 +335,7 @@ get_subscription_lists({F, T}, User, Server)
fun(Group) ->
get_group_users(LServer, Group)
end, DisplayedGroups)),
SRJIDs = [{U1, S1, undefined} || {U1, S1} <- SRUsers],
SRJIDs = [{list_to_binary(U1), list_to_binary(S1), undefined} || {U1, S1} <- SRUsers],
{lists:usort(SRJIDs ++ F), lists:usort(SRJIDs ++ T)}
catch
_ ->
@@ -493,7 +496,8 @@ get_user_groups(US) ->
[]
end ++ get_special_users_groups(Host).
is_group_enabled(Host, Group) ->
is_group_enabled(Host1, Group1) ->
{Host, Group} = split_grouphost(Host1, Group1),
case catch mnesia:dirty_read(sr_group, {Group, Host}) of
[#sr_group{opts = Opts}] ->
not lists:member(disabled, Opts);
@@ -524,7 +528,8 @@ get_online_users(Host) ->
get_group_users(HostB, Group) when is_binary(HostB) ->
get_group_users(binary_to_list(HostB), Group);
get_group_users(Host, Group) ->
get_group_users(Host1, Group1) ->
{Host, Group} = split_grouphost(Host1, Group1),
case get_group_opt(Host, Group, all_users, false) of
true ->
ejabberd_auth:get_vh_registered_users(Host);
@@ -570,7 +575,8 @@ get_group_explicit_users(Host, Group) ->
[]
end.
get_group_name(Host, Group) ->
get_group_name(Host1, Group1) ->
{Host, Group} = split_grouphost(Host1, Group1),
get_group_opt(Host, Group, name, Group).
%% @spec(Host::string)
@@ -646,10 +652,10 @@ get_user_displayed_groups(US) ->
end, get_user_groups(US))),
[Group || Group <- DisplayedGroups1, is_group_enabled(Host, Group)].
is_user_in_group({_U, S} = US, Group, Host) ->
is_user_in_group(US, Group, Host) ->
case catch mnesia:dirty_match_object(
#sr_user{us=US, group_host={Group, Host}}) of
[] -> lists:member(US, get_group_users(S, Group));
[] -> lists:member(US, get_group_users(Host, Group));
_ -> true
end.
@@ -661,14 +667,15 @@ add_user_to_group(Host, {LUser, LServer} = US, Group) ->
case re:run(LUser, "^@.+@$", [{capture, none}]) of
match ->
GroupOpts = mod_shared_roster:get_group_opts(Host, Group),
AllUsersOpt =
case LUser == "@all@" of
true -> [{all_users, true}];
false -> []
MoreGroupOpts =
case LUser of
"@all@" -> [{all_users, true}];
"@online@" -> [{online_users, true}];
_ -> []
end,
mod_shared_roster:set_group_opts(
Host, Group,
GroupOpts ++ AllUsersOpt);
GroupOpts ++ MoreGroupOpts);
nomatch ->
%% Push this new user to members of groups where this group is displayed
push_user_to_displayed(LUser, LServer, Group, both),
@@ -697,7 +704,9 @@ remove_user_from_group(Host, {LUser, LServer} = US, Group) ->
NewGroupOpts =
case LUser of
"@all@" ->
lists:filter(fun(X) -> X/={all_users,true} end, GroupOpts)
lists:filter(fun(X) -> X/={all_users,true} end, GroupOpts);
"@online@" ->
lists:filter(fun(X) -> X/={online_users,true} end, GroupOpts)
end,
mod_shared_roster:set_group_opts(Host, Group, NewGroupOpts);
nomatch ->
@@ -1129,3 +1138,11 @@ get_opt(Opts, Opt, Default) ->
us_to_list({User, Server}) ->
exmpp_jid:bare_to_list(User, Server).
split_grouphost(Host, Group) ->
case string:tokens(Group, "@") of
[GroupName, HostName] ->
{HostName, GroupName};
[_] ->
{Host, Group}
end.
+29 -2
View File
@@ -63,6 +63,7 @@
base,
password,
uid,
deref_aliases,
group_attr,
group_desc,
user_desc,
@@ -330,6 +331,7 @@ eldap_search(State, FilterParseArgs, AttributesList) ->
[{base, State#state.base},
{filter, EldapFilter},
{timeout, ?LDAP_SEARCH_TIMEOUT},
{deref_aliases, State#state.deref_aliases},
{attributes, AttributesList}]) of
#eldap_search_result{entries = Es} ->
%% A result with entries. Return their list.
@@ -495,6 +497,17 @@ parse_options(Host, Opts) ->
ejabberd_config:get_local_option({ldap_tls_verify, Host});
Verify -> Verify
end,
LDAPTLSCAFile = case gen_mod:get_opt(ldap_tls_cacertfile, Opts, undefined) of
undefined ->
ejabberd_config:get_local_option({ldap_tls_cacertfile, Host});
CAFile -> CAFile
end,
LDAPTLSDepth = case gen_mod:get_opt(ldap_tls_depth, Opts, undefined) of
undefined ->
ejabberd_config:get_local_option({ldap_tls_depth, Host});
Depth ->
Depth
end,
LDAPPort = case gen_mod:get_opt(ldap_port, Opts, undefined) of
undefined ->
case ejabberd_config:get_local_option({ldap_port, Host}) of
@@ -631,7 +644,9 @@ parse_options(Host, Opts) ->
RF ->
RF
end,
lists:foreach(fun eldap_utils:check_filter/1,
[ConfigFilter, ConfigUserFilter,
ConfigGroupFilter, RosterFilter]),
SubFilter = "(&("++UIDAttr++"="++UIDAttrFormat++")("++GroupAttr++"=%g))",
UserSubFilter = case ConfigUserFilter of
undefined -> eldap_filter:do_sub(SubFilter, [{"%g", "*"}]);
@@ -658,17 +673,29 @@ parse_options(Host, Opts) ->
"" -> GroupSubFilter;
_ -> "(&" ++ GroupSubFilter ++ ConfigFilter ++ ")"
end,
DerefAliases = case gen_mod:get_opt(deref_aliases, Opts, undefined) of
undefined ->
case ejabberd_config:get_local_option(
{deref_aliases, Host}) of
undefined -> never;
D -> D
end;
D -> D
end,
#state{host = Host,
eldap_id = Eldap_ID,
servers = LDAPServers,
backups = LDAPBackups,
port = LDAPPort,
tls_options = [{encrypt, LDAPEncrypt},
{tls_verify, LDAPTLSVerify}],
{tls_verify, LDAPTLSVerify},
{tls_cacertfile, LDAPTLSCAFile},
{tls_depth, LDAPTLSDepth}],
dn = RootDN,
base = LDAPBase,
password = Password,
uid = UIDAttr,
deref_aliases = DerefAliases,
group_attr = GroupAttr,
group_desc = GroupDesc,
user_desc = UserDesc,
+3 -2
View File
@@ -38,8 +38,9 @@
-include_lib("exmpp/include/exmpp_jid.hrl").
-include("ejabberd.hrl").
start(Host, Opts) ->
HostB = list_to_binary(Host),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
mod_disco:register_feature(HostB, ?NS_SIC_0_s),
gen_iq_handler:add_iq_handler(ejabberd_local, HostB, ?NS_SIC_0_s,
+10 -8
View File
@@ -35,9 +35,11 @@
-include_lib("exmpp/include/exmpp.hrl").
start(Host, Opts) ->
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
gen_iq_handler:add_iq_handler(ejabberd_local, list_to_binary(Host), ?NS_STATS_s,
gen_iq_handler:add_iq_handler(ejabberd_local, HostB, ?NS_STATS_s,
?MODULE, process_local_iq, IQDisc).
stop(Host) ->
@@ -155,12 +157,12 @@ get_local_stat(_Server, [], Name) when Name == <<"users/all-hosts/online">> ->
end;
get_local_stat(_Server, [], Name) when Name == <<"users/all-hosts/total">> ->
case catch mnesia:table_info(passwd, size) of
{'EXIT', _Reason} ->
?STATERR(<<"500">>, <<"Internal Server Error">>);
Users ->
?STATVAL(list_to_binary(integer_to_list(Users)), <<"users">>)
end;
NumUsers = lists:foldl(
fun(Host, Total) ->
ejabberd_auth:get_vh_registered_users_number(Host)
+ Total
end, 0, ejabberd_config:get_global_option(hosts)),
?STATVAL(list_to_binary(integer_to_list(NumUsers)), <<"users">>);
get_local_stat(_Server, _, Name) ->
?STATERR(<<"404">>, <<"Not Found">>).
+5 -3
View File
@@ -39,12 +39,14 @@
-include("ejabberd.hrl").
start(Host, Opts) ->
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
%% TODO: Remove the next two lines once XEP-0090 is Obsolete
gen_iq_handler:add_iq_handler(ejabberd_local, list_to_binary(Host), ?NS_TIME_OLD,
gen_iq_handler:add_iq_handler(ejabberd_local, HostB, ?NS_TIME_OLD,
?MODULE, process_local_iq90, IQDisc),
gen_iq_handler:add_iq_handler(ejabberd_local, list_to_binary(Host), ?NS_TIME,
gen_iq_handler:add_iq_handler(ejabberd_local, HostB, ?NS_TIME,
?MODULE, process_local_iq, IQDisc).
stop(Host) ->
+14 -11
View File
@@ -104,20 +104,21 @@
-define(PROCNAME, ejabberd_mod_vcard).
start(Host, Opts) ->
HostB = list_to_binary(Host),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
Backend = gen_mod:get_opt(backend, Opts, mnesia),
gen_storage:create_table(Backend, HostB, vcard,
[{disc_only_copies, [node()]},
{odbc_host, Host},
{odbc_host, HostB},
{attributes, record_info(fields, vcard)},
{types, [{user_host, {text, text}}]}]),
gen_storage:create_table(Backend, HostB, vcard_search,
[{disc_copies, [node()]},
{odbc_host, Host},
{odbc_host, HostB},
{attributes, record_info(fields, vcard_search)},
{types, [{user_host, {text, text}}]}]),
update_tables(Host, Backend),
update_tables(HostB, Backend),
gen_storage:add_table_index(HostB, vcard_search, lusername),
gen_storage:add_table_index(HostB, vcard_search, lfn),
gen_storage:add_table_index(HostB, vcard_search, lfamily),
@@ -147,10 +148,10 @@ start(Host, Opts) ->
gen_iq_handler:add_iq_handler(ejabberd_sm, HostB, ?NS_VCARD,
?MODULE, process_sm_iq, IQDisc),
ejabberd_hooks:add(disco_sm_features, HostB, ?MODULE, get_sm_features, 50),
MyHost = gen_mod:expand_host_name(Host, Opts, "vjud"),
MyHost = gen_mod:get_opt_host(HostB, Opts, "vjud.@HOST@"),
Search = gen_mod:get_opt(search, Opts, true),
register(gen_mod:get_module_proc(Host, ?PROCNAME),
spawn(?MODULE, init, [MyHost, Host, Search])).
register(gen_mod:get_module_proc(HostB, ?PROCNAME),
spawn(?MODULE, init, [MyHost, HostB, Search])).
init(Host, ServerHost, Search) ->
@@ -343,7 +344,7 @@ set_vcard(User, Server, VCARD) ->
})
end,
gen_storage:transaction(Server, vcard, F),
ejabberd_hooks:run(vcard_set, Server, [LUser, Server, VCARD])
ejabberd_hooks:run(vcard_set, Server, [User, Server, VCARD])
catch
_ ->
{error, badarg}
@@ -443,9 +444,8 @@ do_route(ServerHost, From, To, Packet) ->
Err = exmpp_iq:error(Packet, 'not-allowed'),
ejabberd_router:route(To, From, Err);
{get, ?NS_DISCO_INFO} ->
ServerHostB = list_to_binary(ServerHost),
Info = ejabberd_hooks:run_fold(
disco_info, ServerHostB, [],
disco_info, ServerHost, [],
[ServerHost, ?MODULE, <<>>, ""]),
Result = #xmlel{ns = ?NS_DISCO_INFO, name = 'query',
children = Info ++ [
@@ -755,6 +755,9 @@ remove_user(User, Server) when is_binary(User), is_binary(Server) ->
%%% Update tables
%%%
update_tables(global, Storage) ->
[update_tables(HostB, Storage) || HostB <- ejabberd_hosts:get_hosts(ejabberd)];
update_tables(Host, mnesia) ->
gen_storage_migration:migrate_mnesia(
Host, vcard,
+43 -11
View File
@@ -75,6 +75,7 @@
search_fields,
search_reported,
search_reported_attrs,
deref_aliases,
matches
}).
@@ -285,9 +286,11 @@ find_ldap_user(User, State) ->
VCardAttrs = State#state.vcard_map_attrs,
case eldap_filter:parse(RFC2254_Filter, [{"%u", User}]) of
{ok, EldapFilter} ->
case eldap_pool:search(Eldap_ID, [{base, Base},
{filter, EldapFilter},
{attributes, VCardAttrs}]) of
case eldap_pool:search(Eldap_ID,
[{base, Base},
{filter, EldapFilter},
{deref_aliases, State#state.deref_aliases},
{attributes, VCardAttrs}]) of
#eldap_search_result{entries = [E | _]} ->
E;
_ ->
@@ -557,10 +560,12 @@ search(State, Data) ->
Limit = State#state.matches,
ReportedAttrs = State#state.search_reported_attrs,
Filter = eldap:'and'([SearchFilter, eldap_utils:make_filter(Data, UIDs)]),
case eldap_pool:search(Eldap_ID, [{base, Base},
{filter, Filter},
{limit, Limit},
{attributes, ReportedAttrs}]) of
case eldap_pool:search(Eldap_ID,
[{base, Base},
{filter, Filter},
{limit, Limit},
{deref_aliases, State#state.deref_aliases},
{attributes, ReportedAttrs}]) of
#eldap_search_result{entries = E} ->
search_items(E, State);
_ ->
@@ -645,7 +650,7 @@ find_xdata_el1([_ | Els]) ->
find_xdata_el1(Els).
parse_options(Host, Opts) ->
MyHost = gen_mod:expand_host_name(Host, Opts, "vjud"),
MyHost = gen_mod:get_opt_host(Host, Opts, "vjud.@HOST@"),
Search = gen_mod:get_opt(search, Opts, true),
Matches = case gen_mod:get_opt(matches, Opts, 30) of
infinity -> 0;
@@ -672,6 +677,17 @@ parse_options(Host, Opts) ->
ejabberd_config:get_local_option({ldap_tls_verify, Host});
Verify -> Verify
end,
LDAPTLSCAFile = case gen_mod:get_opt(ldap_tls_cacertfile, Opts, undefined) of
undefined ->
ejabberd_config:get_local_option({ldap_tls_cacertfile, Host});
CAFile -> CAFile
end,
LDAPTLSDepth = case gen_mod:get_opt(ldap_tls_depth, Opts, undefined) of
undefined ->
ejabberd_config:get_local_option({ldap_tls_depth, Host});
Depth ->
Depth
end,
LDAPPortTemp = case gen_mod:get_opt(ldap_port, Opts, undefined) of
undefined ->
ejabberd_config:get_local_option({ldap_port, Host});
@@ -721,10 +737,14 @@ parse_options(Host, Opts) ->
case ejabberd_config:get_local_option({ldap_filter, Host}) of
undefined -> SubFilter;
"" -> SubFilter;
F -> "(&" ++ SubFilter ++ F ++ ")"
F ->
eldap_utils:check_filter(F),
"(&" ++ SubFilter ++ F ++ ")"
end;
"" -> SubFilter;
F -> "(&" ++ SubFilter ++ F ++ ")"
F ->
eldap_utils:check_filter(F),
"(&" ++ SubFilter ++ F ++ ")"
end,
{ok, SearchFilter} = eldap_filter:parse(
eldap_filter:do_sub(UserFilter, [{"%u","*"}])),
@@ -745,6 +765,15 @@ parse_options(Host, Opts) ->
_ -> []
end
end, SearchReported) ++ UIDAttrs),
DerefAliases = case gen_mod:get_opt(deref_aliases, Opts, undefined) of
undefined ->
case ejabberd_config:get_local_option(
{deref_aliases, Host}) of
undefined -> never;
D -> D
end;
D -> D
end,
#state{serverhost = Host,
myhost = MyHost,
eldap_id = Eldap_ID,
@@ -753,7 +782,9 @@ parse_options(Host, Opts) ->
backups = LDAPBackups,
port = LDAPPort,
tls_options = [{encrypt, LDAPEncrypt},
{tls_verify, LDAPTLSVerify}],
{tls_verify, LDAPTLSVerify},
{tls_cacertfile, LDAPTLSCAFile},
{tls_depth, LDAPTLSDepth}],
dn = RootDN,
base = LDAPBase,
password = Password,
@@ -765,5 +796,6 @@ parse_options(Host, Opts) ->
search_fields = SearchFields,
search_reported = SearchReported,
search_reported_attrs = SearchReportedAttrs,
deref_aliases = DerefAliases,
matches = Matches
}.
+5 -4
View File
@@ -26,8 +26,9 @@
%% gen_mod callbacks
%%====================================================================
start(Host, _Opts) ->
HostB = list_to_binary(Host),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, _Opts) ->
mnesia:create_table(vcard_xupdate,
[{disc_copies, [node()]},
{attributes, record_info(fields, vcard_xupdate)}]),
@@ -65,7 +66,7 @@ vcard_set(User, Server, VCARD) ->
[] ->
remove_xupdate(User, Server);
BinVal ->
add_xupdate(User, Server, sha:sha(jlib:decode_base64(BinVal)))
add_xupdate(User, Server, list_to_binary(sha:sha(jlib:decode_base64(BinVal))))
end,
ejabberd_sm:force_update_presence(US).
@@ -105,7 +106,7 @@ presence_with_xupdate(Stanza, User, Host) ->
build_xphotoel(User, Host) ->
Hash = get_xupdate(User, Host),
PhotoSubEls = case Hash of
Hash when is_list(Hash) ->
Hash when is_binary(Hash) ->
[exmpp_xml:cdata(Hash)];
_ ->
[]
+4 -3
View File
@@ -38,10 +38,11 @@
-include("ejabberd.hrl").
start(Host, Opts) ->
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
gen_iq_handler:add_iq_handler(ejabberd_local, list_to_binary(Host), ?NS_SOFT_VERSION,
gen_iq_handler:add_iq_handler(ejabberd_local, HostB, ?NS_SOFT_VERSION,
?MODULE, process_local_iq, IQDisc).
stop(Host) ->
+9
View File
@@ -100,6 +100,7 @@ init([Host]) ->
[?MODULE]}
end, lists:seq(1, PoolSize))}}.
%% @spec (Host::string()) -> [pid()]
get_pids(Host) ->
case ejabberd_config:get_local_option({odbc_server, Host}) of
{host, Host1} ->
@@ -109,10 +110,18 @@ get_pids(Host) ->
[R#sql_pool.pid || R <- Rs]
end.
get_random_pid(HostB) when is_binary(HostB) ->
get_random_pid(binary_to_list(HostB));
get_random_pid(global) ->
get_random_pid("localhost");
get_random_pid(Host) ->
Pids = get_pids(ejabberd:normalize_host(Host)),
lists:nth(erlang:phash(now(), length(Pids)), Pids).
get_dbtype(HostB) when is_binary(HostB) ->
get_dbtype(binary_to_list(HostB));
get_dbtype(global) ->
get_dbtype("localhost");
get_dbtype(Host) ->
case ejabberd_config:get_local_option({odbc_server, Host}) of
{host, Host1} ->
+178 -146
View File
@@ -25,6 +25,10 @@
-- Needs MySQL (at least 4.0.x) with innodb back-end
SET table_type=InnoDB;
--
-- Tables schemas keep from previous ejabberd versions
--
CREATE TABLE hosts (
clusterid integer NOT NULL,
host varchar(250) NOT NULL PRIMARY KEY,
@@ -34,158 +38,12 @@ CREATE TABLE hosts (
INSERT INTO hosts (clusterid, host, config)
VALUES (1, 'localhost', '');
CREATE TABLE users (
host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
password text NOT NULL,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (host, username)
) CHARACTER SET utf8;
CREATE TABLE last (
host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
seconds text NOT NULL,
state text NOT NULL,
PRIMARY KEY (host, username)
) CHARACTER SET utf8;
CREATE TABLE rosteritem (
host varchar(250) NOT NULL,
user varchar(250) NOT NULL,
jid varchar(250) NOT NULL,
name text,
subscription text NOT NULL,
ask text NOT NULL,
askmessage text NOT NULL,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (host(75), username(75), jid(75))
) CHARACTER SET utf8;
CREATE INDEX i_rosteru_username ON rosteritem(username);
CREATE INDEX i_rosteru_jid ON rosteritem(jid);
CREATE TABLE rostergroup (
host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
jid varchar(250) NOT NULL,
grp text NOT NULL,
PRIMARY KEY (host(75), username(75), jid(75))
) CHARACTER SET utf8;
CREATE TABLE spool (
host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
xml text NOT NULL,
seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (host, username, seq)
) CHARACTER SET utf8;
CREATE TABLE vcard (
host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
vcard mediumtext NOT NULL,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (host, username)
) CHARACTER SET utf8;
CREATE TABLE vcard_search (
host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
lusername varchar(250) NOT NULL,
fn text NOT NULL,
lfn varchar(250) NOT NULL,
family text NOT NULL,
lfamily varchar(250) NOT NULL,
given text NOT NULL,
lgiven varchar(250) NOT NULL,
middle text NOT NULL,
lmiddle varchar(250) NOT NULL,
nickname text NOT NULL,
lnickname varchar(250) NOT NULL,
bday text NOT NULL,
lbday varchar(250) NOT NULL,
ctry text NOT NULL,
lctry varchar(250) NOT NULL,
locality text NOT NULL,
llocality varchar(250) NOT NULL,
email text NOT NULL,
lemail varchar(250) NOT NULL,
orgname text NOT NULL,
lorgname varchar(250) NOT NULL,
orgunit text NOT NULL,
lorgunit varchar(250) NOT NULL,
PRIMARY KEY (host, lusername)
) CHARACTER SET utf8;
CREATE INDEX i_vcard_search_lfn ON vcard_search(lfn);
CREATE INDEX i_vcard_search_lfamily ON vcard_search(lfamily);
CREATE INDEX i_vcard_search_lgiven ON vcard_search(lgiven);
CREATE INDEX i_vcard_search_lmiddle ON vcard_search(lmiddle);
CREATE INDEX i_vcard_search_lnickname ON vcard_search(lnickname);
CREATE INDEX i_vcard_search_lbday ON vcard_search(lbday);
CREATE INDEX i_vcard_search_lctry ON vcard_search(lctry);
CREATE INDEX i_vcard_search_llocality ON vcard_search(llocality);
CREATE INDEX i_vcard_search_lemail ON vcard_search(lemail);
CREATE INDEX i_vcard_search_lorgname ON vcard_search(lorgname);
CREATE INDEX i_vcard_search_lorgunit ON vcard_search(lorgunit);
CREATE TABLE privacy_default_list (
host varchar(250) NOT NULL,
username varchar(250),
name varchar(250) NOT NULL,
PRIMARY KEY (host, username)
) CHARACTER SET utf8;
CREATE TABLE privacy_list (
host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
name varchar(250) NOT NULL,
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (host, username, name)
) CHARACTER SET utf8;
CREATE TABLE privacy_list_data (
id bigint,
t character(1) NOT NULL,
value text NOT NULL,
action character(1) NOT NULL,
ord NUMERIC NOT NULL,
match_all boolean NOT NULL,
match_iq boolean NOT NULL,
match_message boolean NOT NULL,
match_presence_in boolean NOT NULL,
match_presence_out boolean NOT NULL
) CHARACTER SET utf8;
CREATE TABLE private_storage (
host varchar(250) NOT NULL,
username varchar(250) NOT NULL,
namespace varchar(250) NOT NULL,
data text NOT NULL,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (host(75), username(75), namespace(75))
) CHARACTER SET utf8;
CREATE INDEX i_private_storage_username USING BTREE ON private_storage(username);
-- Not tested in mysql
CREATE TABLE roster_version (
username varchar(250) PRIMARY KEY,
version text NOT NULL
) CHARACTER SET utf8;
-- To update from 1.x:
-- ALTER TABLE rosterusers ADD COLUMN askmessage text AFTER ask;
-- UPDATE rosterusers SET askmessage = '';
-- ALTER TABLE rosterusers ALTER COLUMN askmessage SET NOT NULL;
CREATE TABLE pubsub_node (
host text,
node text,
@@ -240,3 +98,177 @@ CREATE TABLE pubsub_subscription_opt (
opt_value text
);
CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt(subid(32), opt_name(32));
--
-- Tables schemas dumped from gen_storage
--
CREATE TABLE `last_activity` (
`user` varchar(255) NOT NULL DEFAULT '',
`host` varchar(255) NOT NULL DEFAULT '',
`timestamp` bigint(20) DEFAULT NULL,
`status` text,
PRIMARY KEY (`user`(105),`host`(105))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `muc_online_room` (
`name` varchar(255) NOT NULL DEFAULT '',
`host` varchar(255) NOT NULL DEFAULT '',
`pid` text,
PRIMARY KEY (`name`(105),`host`(105))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `muc_registered` (
`user` varchar(255) NOT NULL DEFAULT '',
`host` varchar(255) NOT NULL DEFAULT '',
`nick` text,
PRIMARY KEY (`user`(105),`host`(105)),
KEY `muc_registered_nick` (`nick`(75))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `muc_room_affiliation` (
`name` varchar(255) DEFAULT NULL,
`host` varchar(255) DEFAULT NULL,
`jid` varchar(255) DEFAULT NULL,
`affiliation` varchar(255) DEFAULT NULL,
`reason` varchar(255) DEFAULT NULL,
KEY `muc_room_affiliation_bag` (`name`(75),`host`(75))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `muc_room_opt` (
`name` varchar(255) DEFAULT NULL,
`host` varchar(255) DEFAULT NULL,
`opt` varchar(255) DEFAULT NULL,
`val` varchar(255) DEFAULT NULL,
KEY `muc_room_opt_bag` (`name`(75),`host`(75))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `offline_msg` (
`user` varchar(255) DEFAULT NULL,
`host` varchar(255) DEFAULT NULL,
`timestamp` bigint(20) DEFAULT NULL,
`expire` bigint(20) DEFAULT NULL,
`from` varchar(255) DEFAULT NULL,
`to` varchar(255) DEFAULT NULL,
`packet` text,
KEY `offline_msg_bag` (`user`(75),`host`(75))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `passwd` (
`user` varchar(255) NOT NULL DEFAULT '',
`host` varchar(255) NOT NULL DEFAULT '',
`password` text,
`storedkey` text,
`serverkey` text,
`salt` text,
`iterationcount` int(11) DEFAULT NULL,
PRIMARY KEY (`user`(105),`host`(105))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `privacy_default_list` (
`user` varchar(255) NOT NULL DEFAULT '',
`host` varchar(255) NOT NULL DEFAULT '',
`name` text,
PRIMARY KEY (`user`(105),`host`(105))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `privacy_list` (
`user` varchar(255) DEFAULT NULL,
`host` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
KEY `privacy_list_bag` (`user`(75),`host`(75)),
KEY `privacy_list_name` (`name`(75))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `privacy_list_data` (
`user` varchar(255) DEFAULT NULL,
`host` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`type` varchar(255) DEFAULT NULL,
`value` varchar(255) DEFAULT NULL,
`action` varchar(255) DEFAULT NULL,
`order` int(11) DEFAULT NULL,
`match_all` varchar(255) DEFAULT NULL,
`match_iq` varchar(255) DEFAULT NULL,
`match_message` varchar(255) DEFAULT NULL,
`match_presence_in` varchar(255) DEFAULT NULL,
`match_presence_out` varchar(255) DEFAULT NULL,
KEY `privacy_list_data_bag` (`user`(75),`host`(75)),
KEY `privacy_list_data_name` (`name`(75))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `private_storage` (
`user` varchar(255) NOT NULL DEFAULT '',
`host` varchar(255) NOT NULL DEFAULT '',
`ns` varchar(255) NOT NULL DEFAULT '',
`xml` text,
PRIMARY KEY (`user`(105),`host`(105),`ns`(105))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `rostergroup` (
`user` varchar(255) DEFAULT NULL,
`host` varchar(255) DEFAULT NULL,
`jid` varchar(255) DEFAULT NULL,
`grp` varchar(255) DEFAULT NULL,
KEY `rostergroup_bag` (`user`(75),`host`(75),`jid`(75))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `rosteritem` (
`user` varchar(255) NOT NULL DEFAULT '',
`host` varchar(255) NOT NULL DEFAULT '',
`jid` varchar(255) NOT NULL DEFAULT '',
`name` text,
`subscription` text,
`ask` text,
`askmessage` text,
PRIMARY KEY (`user`(105),`host`(105),`jid`(105))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `vcard` (
`user` varchar(255) NOT NULL DEFAULT '',
`host` varchar(255) NOT NULL DEFAULT '',
`vcard` text,
PRIMARY KEY (`user`(105),`host`(105))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `vcard_search` (
`user` varchar(255) NOT NULL DEFAULT '',
`host` varchar(255) NOT NULL DEFAULT '',
`username` text,
`lusername` text,
`fn` text,
`lfn` text,
`family` text,
`lfamily` text,
`given` text,
`lgiven` text,
`middle` text,
`lmiddle` text,
`nickname` text,
`lnickname` text,
`bday` text,
`lbday` text,
`ctry` text,
`lctry` text,
`locality` text,
`llocality` text,
`email` text,
`lemail` text,
`orgname` text,
`lorgname` text,
`orgunit` text,
`lorgunit` text,
PRIMARY KEY (`user`(105),`host`(105)),
KEY `vcard_search_lusername` (`lusername`(75)),
KEY `vcard_search_lfn` (`lfn`(75)),
KEY `vcard_search_lfamily` (`lfamily`(75)),
KEY `vcard_search_lgiven` (`lgiven`(75)),
KEY `vcard_search_lmiddle` (`lmiddle`(75)),
KEY `vcard_search_lnickname` (`lnickname`(75)),
KEY `vcard_search_lbday` (`lbday`(75)),
KEY `vcard_search_lctry` (`lctry`(75)),
KEY `vcard_search_llocality` (`llocality`(75)),
KEY `vcard_search_lemail` (`lemail`(75)),
KEY `vcard_search_lorgname` (`lorgname`(75)),
KEY `vcard_search_lorgunit` (`lorgunit`(75))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-4
View File
@@ -225,10 +225,6 @@ list_users(LServer, [{prefix, Prefix},
users_number(LServer) ->
Host = escape(LServer),
case element(1, ejabberd_config:get_local_option({odbc_server, LServer})) of
mysql ->
ejabberd_odbc:sql_query(
LServer,
"select table_rows from information_schema.tables where table_name='users'");
pgsql ->
case ejabberd_config:get_local_option({pgsql_users_number_estimate, LServer}) of
true ->
+82
View File
@@ -0,0 +1,82 @@
%%%----------------------------------------------------------------------
%%% File : scram.erl
%%% Author : Stephen Röttger <stephen.roettger@googlemail.com>
%%% Purpose : SCRAM (RFC 5802)
%%% Created : 7 Aug 2011 by Stephen Röttger <stephen.roettger@googlemail.com>
%%%
%%%
%%% 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(scram).
-author('stephen.roettger@googlemail.com').
%% External exports
-export([salted_password/3,
stored_key/1,
server_key/1,
server_signature/2,
client_signature/2,
client_key/1,
client_key/2
]).
%% ejabberd doesn't implement SASLPREP, so we use the similar RESOURCEPREP instead
salted_password(Password, Salt, IterationCount) ->
hi(exmpp_stringprep:resourceprep(Password), Salt, IterationCount).
client_key(SaltedPassword) ->
crypto:sha_mac(SaltedPassword, "Client Key").
stored_key(ClientKey) ->
crypto:sha(ClientKey).
server_key(SaltedPassword) ->
crypto:sha_mac(SaltedPassword, "Server Key").
client_signature(StoredKey, AuthMessage) ->
crypto:sha_mac(StoredKey, AuthMessage).
client_key(ClientProof, ClientSignature) ->
list_to_binary(lists:zipwith(fun(X, Y) ->
X bxor Y
end,
binary_to_list(ClientProof),
binary_to_list(ClientSignature))).
server_signature(ServerKey, AuthMessage) ->
crypto:sha_mac(ServerKey, AuthMessage).
hi(Password, Salt, IterationCount) ->
U1 = crypto:sha_mac(Password, string:concat(binary_to_list(Salt), [0,0,0,1])),
list_to_binary(lists:zipwith(fun(X, Y) ->
X bxor Y
end,
binary_to_list(U1),
binary_to_list(hi_round(Password, U1, IterationCount-1)))).
hi_round(Password, UPrev, 1) ->
crypto:sha_mac(Password, UPrev);
hi_round(Password, UPrev, IterationCount) ->
U = crypto:sha_mac(Password, UPrev),
list_to_binary(lists:zipwith(fun(X, Y) ->
X bxor Y
end,
binary_to_list(U),
binary_to_list(hi_round(Password, U, IterationCount-1)))).
+26 -4
View File
@@ -24,16 +24,26 @@
#include <openssl/md2.h>
#endif
/*
* R15B changed several driver callbacks to use ErlDrvSizeT and
* ErlDrvSSizeT typedefs instead of int.
* This provides missing typedefs on older OTP versions.
*/
#if ERL_DRV_EXTENDED_MAJOR_VERSION < 2
typedef int ErlDrvSizeT;
typedef int ErlDrvSSizeT;
#endif
static ErlDrvData sha_drv_start(ErlDrvPort port, char *buf)
{
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
return NULL;
}
static int sha_drv_control(ErlDrvData handle,
static ErlDrvSSizeT sha_drv_control(ErlDrvData handle,
unsigned int command,
char *buf, int len,
char **rbuf, int rlen)
char *buf, ErlDrvSizeT len,
char **rbuf, ErlDrvSizeT rlen)
{
ErlDrvBinary *b = NULL;
@@ -89,7 +99,19 @@ ErlDrvEntry sha_driver_entry = {
NULL, /* handle */
sha_drv_control, /* F_PTR control, port_command callback */
NULL, /* F_PTR timeout, reserved */
NULL /* F_PTR outputv, reserved */
NULL, /* F_PTR outputv, reserved */
/* Added in Erlang/OTP R15B: */
NULL, /* ready_async */
NULL, /* flush */
NULL, /* call */
NULL, /* event */
ERL_DRV_EXTENDED_MARKER, /* extended_marker */
ERL_DRV_EXTENDED_MAJOR_VERSION, /* major_version */
ERL_DRV_EXTENDED_MINOR_VERSION, /* minor_version */
0, /* driver_flags */
NULL, /* handle2 */
NULL, /* process_exit */
NULL /* stop_select */
};
DRIVER_INIT(sha_drv) /* must match name in driver_entry */
+29 -17
View File
@@ -44,6 +44,16 @@ typedef unsigned __int32 uint32_t;
#define SSL_OP_NO_TICKET 0
#endif
/*
* R15B changed several driver callbacks to use ErlDrvSizeT and
* ErlDrvSSizeT typedefs instead of int.
* This provides missing typedefs on older OTP versions.
*/
#if ERL_DRV_EXTENDED_MAJOR_VERSION < 2
typedef int ErlDrvSizeT;
typedef int ErlDrvSSizeT;
#endif
/*
* str_hash is based on the public domain code from
* http://www.burtleburtle.net/bob/hash/doobs.html
@@ -305,10 +315,10 @@ static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
}
static int tls_drv_control(ErlDrvData handle,
static ErlDrvSSizeT tls_drv_control(ErlDrvData handle,
unsigned int command,
char *buf, int len,
char **rbuf, int rlen)
char *buf, ErlDrvSizeT len,
char **rbuf, ErlDrvSizeT rlen)
{
tls_data *d = (tls_data *)handle;
int res;
@@ -407,22 +417,12 @@ static int tls_drv_control(ErlDrvData handle,
break;
case GET_ENCRYPTED_OUTPUT:
die_unless(d->ssl, "SSL not initialized");
size = BUF_SIZE + 1;
rlen = 1;
size = BIO_ctrl_pending(d->bio_write) + 1;
b = driver_alloc_binary(size);
b->orig_bytes[0] = 0;
while ((res = BIO_read(d->bio_write,
b->orig_bytes + rlen, BUF_SIZE)) > 0)
{
//printf("%d bytes of encrypted data read from state machine\r\n", res);
rlen += res;
size += BUF_SIZE;
b = driver_realloc_binary(b, size);
}
b = driver_realloc_binary(b, rlen);
BIO_read(d->bio_write, b->orig_bytes + 1, size - 1);
*rbuf = (char *)b;
return rlen;
return size;
case GET_DECRYPTED_INPUT:
if (!SSL_is_init_finished(d->ssl))
{
@@ -513,7 +513,19 @@ ErlDrvEntry tls_driver_entry = {
NULL, /* handle */
tls_drv_control, /* F_PTR control, port_command callback */
NULL, /* F_PTR timeout, reserved */
NULL /* F_PTR outputv, reserved */
NULL, /* F_PTR outputv, reserved */
/* Added in Erlang/OTP R15B: */
NULL, /* ready_async */
NULL, /* flush */
NULL, /* call */
NULL, /* event */
ERL_DRV_EXTENDED_MARKER, /* extended_marker */
ERL_DRV_EXTENDED_MAJOR_VERSION, /* major_version */
ERL_DRV_EXTENDED_MINOR_VERSION, /* minor_version */
0, /* driver_flags */
NULL, /* handle2 */
NULL, /* process_exit */
NULL /* stop_select */
};
DRIVER_INIT(tls_drv) /* must match name in driver_entry */
+6 -5
View File
@@ -95,7 +95,7 @@
-define(FSMOPTS, []).
-endif.
-define(BOSH_VERSION_b, <<"1.8">>).
-define(BOSH_VERSION_b, <<"1.10">>).
-define(MAX_REQUESTS, 2). % number of simultaneous requests
-define(MIN_POLLING, 2000000). % don't poll faster than that or we will
@@ -121,7 +121,7 @@
%% supervisor
start(XMPPDomain, Sid, Key, IP) ->
?DEBUG("Starting session", []),
SupervisorProc = gen_mod:get_module_proc(XMPPDomain, ?PROCNAME_MHB),
SupervisorProc = gen_mod:get_module_proc_existing(XMPPDomain, ?PROCNAME_MHB),
case catch supervisor:start_child(SupervisorProc, [Sid, Key, IP]) of
{ok, Pid} ->
{ok, Pid};
@@ -232,7 +232,7 @@ process_request(Data, IP) ->
%% Existing session
{ok, {Sid, Rid, Attrs, Payload1}} ->
StreamStart =
case exmpp_xml:get_attribute_from_list_as_list(Attrs, ?NS_BOSH, <<"restart">>, "") of
case exmpp_xml:get_attribute_from_list_as_list(Attrs, ?NS_XBOSH, <<"restart">>, "") of
"true" ->
true;
_ ->
@@ -303,7 +303,7 @@ handle_session_start(Pid, XmppDomain, Sid, Rid, Attrs,
{'EXIT', _} -> 0.0;
V -> V
end,
XmppVersion = exmpp_xml:get_attribute_from_list_as_list(Attrs, ?NS_BOSH, <<"version">>, ""),
XmppVersion = exmpp_xml:get_attribute_from_list_as_list(Attrs, ?NS_XBOSH, <<"version">>, ""),
?DEBUG("Create session: ~p", [Sid]),
mnesia:async_dirty(
fun() ->
@@ -1049,7 +1049,8 @@ prepare_outpacket_response(#http_bind{id=Sid, wait=Wait,
BOSH_attribs =
[#xmlattr{name = <<"authid">>, value = list_to_binary(AuthID)},
#xmlattr{name = <<"xmlns:stream">>, value = ?NS_XMPP_b}] ++
[#xmlattr{name = <<"version">>, ns = ?NS_BOSH_s, value = list_to_binary(Version)}],
[#xmlattr{name = <<"version">>, ns = ?NS_XBOSH_s, value = list_to_binary(Version)},
#xmlattr{name = <<"restartlogic">>, ns = ?NS_XBOSH_s, value = <<"true">>}],
MaxInactivity = get_max_inactivity(To, ?MAX_INACTIVITY),
MaxPause = get_max_pause(To),
{200, ?HEADER,
+61 -34
View File
@@ -1177,11 +1177,12 @@ process_admin(Host,
process_admin(Host, #request{lang = Lang,
auth = {_, _Auth, AJID}
} = Request) ->
{Hook, Opts} = case Host of
global -> {webadmin_page_main, [Request]};
Host -> {webadmin_page_host, [Host, Request]}
end,
case ejabberd_hooks:run_fold(Hook, list_to_binary(Host), [], Opts) of
{Hook, Opts, HostB} =
case Host of
global -> {webadmin_page_main, [Request], global};
Host -> {webadmin_page_host, [Host, Request], list_to_binary(Host)}
end,
case ejabberd_hooks:run_fold(Hook, HostB, [], Opts) of
[] ->
setelement(1, make_xhtml([?XC('h1', "Not Found")], Host, Lang, AJID), 404);
[{xmlel, _, _, _, _, _} | _] = Res ->
@@ -1658,8 +1659,7 @@ list_users_in_diapason(Host, Diap, Lang, URLFunc) ->
Sub = lists:sublist(SUsers, N1, N2 - N1 + 1),
[list_given_users(Host, Sub, "../../", Lang, URLFunc)].
list_given_users(Host, Users, Prefix, Lang, URLFunc) ->
ModOffline = get_offlinemsg_module(Host),
list_given_users(_Host, Users, Prefix, Lang, URLFunc) ->
?XE('table',
[?XE('thead',
[?XE('tr',
@@ -1672,7 +1672,7 @@ list_given_users(Host, Users, Prefix, Lang, URLFunc) ->
ServerB = list_to_binary(Server),
UserB = list_to_binary(User),
US = {UserB, ServerB},
QueueLenStr = get_offlinemsg_length(ModOffline, User, Server),
QueueLenStr = get_offlinemsg_length(User, Server),
FQueueLen = [?AC(URLFunc({users_queue, Prefix,
User, Server}),
QueueLenStr)],
@@ -1707,16 +1707,10 @@ list_given_users(Host, Users, Prefix, Lang, URLFunc) ->
end, Users)
)]).
get_offlinemsg_length(ModOffline, User, Server) ->
case ModOffline of
none -> "disabled";
_ -> pretty_string_int(ModOffline:get_queue_length(list_to_binary(User), list_to_binary(Server)))
end.
get_offlinemsg_module(Server) ->
case [mod_offline] -- gen_mod:loaded_modules(Server) of
[mod_offline] -> none;
[] -> mod_offline
get_offlinemsg_length(User, Server) ->
case gen_mod:is_loaded(Server, mod_offline) of
false -> "disabled";
true -> pretty_string_int(mod_offline:get_queue_length(list_to_binary(User), list_to_binary(Server)))
end.
us_to_list({User, Server}) ->
@@ -1730,7 +1724,10 @@ su_to_list({Server, User}) ->
get_stats(global, Lang) ->
OnlineUsers = mnesia:table_info(session, size),
RegisteredUsers = mnesia:table_info(passwd, size),
RegisteredUsers = lists:foldl(
fun(Host, Total) ->
ejabberd_auth:get_vh_registered_users_number(Host) + Total
end, 0, ejabberd_config:get_global_option(hosts)),
S2SConns = ejabberd_s2s:dirty_get_connections(),
S2SConnections = length(S2SConns),
S2SServers = length(lists:usort([element(2, C) || C <- S2SConns])),
@@ -1768,7 +1765,7 @@ host_info(Server, Query, Lang) ->
error -> [?XREST("Bad format")];
nothing -> []
end ++
[?XAE('p', [], [?CT("Is registered: "), ?CT(IsRegistered)])
[?XAE('p', [], [?CT("Is registered:"), ?C(" "), ?CT(IsRegistered)])
] ++ host_info_permanent(Server, Lang)
++ host_info_running(Server, Lang).
@@ -1779,14 +1776,14 @@ host_info_permanent(Server, Lang) ->
[?XAE('form', [?XMLATTR(<<"action">>, <<>>), ?XMLATTR(<<"method">>, <<"post">>)],
[?P, ?INPUTT("submit", "removehost", "Remove Host")])]}
end,
[?XAE('p', [], [?CT("Is permanent: "), PermanentX])] ++ FormList.
[?XAE('p', [], [?CT("Is permanent:"), ?C(" "), PermanentX])] ++ FormList.
host_info_running(Server, Lang) ->
{RunningX, InputName,InputString} = case ejabberd_hosts:running(Server) of
true -> {?CT("true"), "stophost", "Stop Host"};
false -> {?CT("false"), "starthost", "Start Host"}
end,
[?XAE('p', [], [?CT("Is running: "), RunningX]),
[?XAE('p', [], [?CT("Is running:"), ?C(" "), RunningX]),
?XAE('form', [?XMLATTR(<<"action">>, <<>>), ?XMLATTR(<<"method">>, <<"post">>)],
[?P, ?INPUTT("submit", InputName, InputString)])].
@@ -2443,12 +2440,21 @@ get_node(global, Node, ["backup"], Query, Lang) ->
get_node(global, Node, ["pid"], _Query, Lang) ->
NodeS = atom_to_list(Node),
Registered = lists:reverse(lists:sort(rpc:call(Node, erlang, registered, []))),
Processes = rpc:call(Node, erlang, processes, []),
ProcessesNumber = length(Processes),
RegisteredList = lists:foldl(
fun(P, RRL) ->
PS = atom_to_list(P),
NodePidS = NodeS ++ "/pid/" ++ PS,
[?AC("/admin/node/" ++ NodePidS ++ "/", PS), ?BR | RRL]
end,
[],
Registered),
ProcessesList = lists:map(
fun(P) ->
PS = pid_to_list(P),
NodePidS = NodeS ++ "/pid/" ++ PS,
NodePidS = NodeS ++ "/pid/process/" ++ PS,
?AC("/admin/node/" ++ NodePidS ++ "/", PS)
end,
Processes),
@@ -2461,10 +2467,23 @@ get_node(global, Node, ["pid"], _Query, Lang) ->
])
]),
?XAE('p', [],
[?CT("Processes: ")] ++ ProcessesList
[?CT("Registered processes and ports:"), ?C(" ")] ++ RegisteredList
),
?XAE('p', [],
[?CT("Total processes:"), ?C(" ")] ++ ProcessesList
)];
get_node(global, Node, ["pid", PidS], _Query, Lang) ->
get_node(global, Node, ["pid", [Char1|_] = RegS], _Query, Lang) when Char1 /= $< ->
Ref = whereis(list_to_atom(RegS)),
{Type, PidS} = try
{"process", pid_to_list(Ref)}
catch
error:badarg ->
{"port", RegS}
end,
get_node(global, Node, ["pid", Type, PidS], _Query, Lang);
get_node(global, Node, ["pid", "process" = Type, PidS], _Query, Lang) ->
NodeS = atom_to_list(Node),
ProcessInfo = rpc:call(Node, erlang, process_info, [list_to_pid(PidS)]),
ProcessInfoS = io_lib:format("~p", [ProcessInfo]),
@@ -2473,7 +2492,7 @@ get_node(global, Node, ["pid", PidS], _Query, Lang) ->
{match, PidsRareList} ->
lists:map(
fun([PS]) ->
NodePidS = NodeS ++ "/pid/" ++ PS,
NodePidS = NodeS ++ "/pid/" ++ Type ++ "/" ++ PS,
?AC("/admin/node/" ++ NodePidS ++ "/", PS)
end,
PidsRareList);
@@ -2495,7 +2514,7 @@ get_node(global, Node, ["pid", PidS], _Query, Lang) ->
_ ->
[]
end,
[?XC('h1', io_lib:format(?T("Erlang Process ~s at node ~p"), [PidS, Node])),
[?XC('h1', io_lib:format(?T("Erlang process ~s at node ~p"), [PidS, Node])),
?XC('h3', ?T("Process Information:")),
?XAE('pre', [], [?C(ProcessInfoS)]),
?XC('h3', ?T("Related Processes:")),
@@ -2506,6 +2525,14 @@ get_node(global, Node, ["pid", PidS], _Query, Lang) ->
|| {PortName, PortDescr} <- PortLinkList])
];
get_node(global, Node, ["pid", "port", PidS], _Query, Lang) ->
ProcessInfo = rpc:call(Node, erlang, port_info, [list_to_atom(PidS)]),
ProcessInfoS = io_lib:format("~p", [ProcessInfo]),
[?XC('h1', io_lib:format(?T("Erlang port ~s at node ~p"), [PidS, Node])),
?XC('h3', ?T("Port Information:")),
?XAE('pre', [], [?C(ProcessInfoS)])
];
get_node(global, Node, ["ports"], Query, Lang) ->
Ports = rpc:call(Node, ejabberd_config, get_local_option, [listen]),
Res = case catch node_ports_parse_query(Node, Ports, Query) of
@@ -2660,11 +2687,12 @@ get_node(global, Node, ["update"], Query, Lang) ->
];
get_node(Host, Node, NPath, Query, Lang) ->
{Hook, Opts} = case Host of
global -> {webadmin_page_node, [Node, NPath, Query, Lang]};
Host -> {webadmin_page_hostnode, [Host, Node, NPath, Query, Lang]}
end,
case ejabberd_hooks:run_fold(Hook, list_to_binary(Host), [], Opts) of
{Hook, Opts, HostB} =
case Host of
global -> {webadmin_page_node, [Node, NPath, Query, Lang], global};
Host -> {webadmin_page_hostnode, [Host, Node, NPath, Query, Lang], list_to_binary(Host)}
end,
case ejabberd_hooks:run_fold(Hook, HostB, [], Opts) of
[] -> [?XC('h1', "Not Found")];
Res -> Res
end.
@@ -3134,7 +3162,6 @@ make_host_menu(Host, HostNodeMenu, Lang, JID) ->
{"online-users", "Online Users"},
{"last-activity", "Last Activity"},
{"nodes", "Nodes", HostNodeMenu},
{"misc", "Miscelanea Options"},
{"stats", "Statistics"}]
++ get_menu_items_hook({host, Host}, Lang),
HostBasePath = url_to_path(HostBase),
@@ -3162,7 +3189,7 @@ make_server_menu(HostMenu, NodeMenu, Lang, JID) ->
{"access", "Access Rules"},
{"vhosts", "Virtual Hosts", HostMenu},
{"nodes", "Nodes", NodeMenu},
{"misc", "Miscelanea Options"},
{"misc", "Miscellanea Options"},
{"stats", "Statistics"}]
++ get_menu_items_hook(server, Lang),
BasePath = url_to_path(Base),
+4 -2
View File
@@ -103,9 +103,11 @@ get_human_html_xmlel() ->
%%%----------------------------------------------------------------------
%%% BEHAVIOUR CALLBACKS
%%%----------------------------------------------------------------------
start(Host, _Opts) ->
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, _Opts) ->
setup_database(),
Proc = gen_mod:get_module_proc(Host, ?PROCNAME_MHB),
Proc = gen_mod:get_module_proc(HostB, ?PROCNAME_MHB),
ChildSpec =
{Proc,
{ejabberd_tmp_sup, start_link,
+16 -10
View File
@@ -44,7 +44,7 @@
-export([process/2]).
%% ejabberd_hooks callbacks
-export([reopen_log/1]).
-export([reopen_log/0, reopen_log/1]).
-include("ejabberd.hrl").
-include("jlib.hrl").
@@ -81,9 +81,10 @@
{".html", "text/html"},
{".jar", "application/java-archive"},
{".jpeg", "image/jpeg"},
{".jpg", "image/jpeg"},
{".jpg", "image/jpeg"},
{".js", "text/javascript"},
{".png", "image/png"},
{".svg", "image/svg+xml"},
{".txt", "text/plain"},
{".xml", "application/xml"},
{".xpi", "application/x-xpinstall"},
@@ -95,11 +96,13 @@
%% gen_mod callbacks
%%====================================================================
start(Host, Opts) ->
Proc = get_proc_name(Host),
start(Host, Opts) when is_list(Host) ->
start(list_to_binary(Host), Opts);
start(HostB, Opts) ->
Proc = get_proc_name(HostB),
ChildSpec =
{Proc,
{?MODULE, start_link, [Host, Opts]},
{?MODULE, start_link, [HostB, Opts]},
transient, % if process crashes abruptly, it gets restarted
1000,
worker,
@@ -205,7 +208,7 @@ check_docroot_is_readable(DRInfo, DocRoot) ->
try_open_log(undefined, _Host) ->
undefined;
try_open_log(FN, Host) ->
try_open_log(FN, HostB) ->
FD = try open_log(FN) of
FD1 -> FD1
catch
@@ -213,7 +216,6 @@ try_open_log(FN, Host) ->
?ERROR_MSG("Cannot open access log file: ~p~nReason: ~p", [FN, Reason]),
undefined
end,
HostB = list_to_binary(Host),
ejabberd_hooks:add(reopen_log_hook, HostB, ?MODULE, reopen_log, 50),
FD.
@@ -287,7 +289,7 @@ code_change(_OldVsn, State, _Extra) ->
%% Returns the page to be sent back to the client and/or HTTP status code.
process(LocalPath, Request) ->
?DEBUG("Requested ~p", [LocalPath]),
try gen_server:call(get_proc_name(Request#request.host), {serve, LocalPath}) of
try gen_server:call(get_proc_name_existing(Request#request.host), {serve, LocalPath}) of
{FileSize, Code, Headers, Contents} ->
add_to_log(FileSize, Code, Request),
{Code, Headers, Contents}
@@ -359,11 +361,14 @@ reopen_log(FN, FD) ->
close_log(FD),
open_log(FN).
reopen_log() ->
%% This function is called when the hook was registered for host 'global'
gen_server:cast(get_proc_name_existing(global), reopen_log).
reopen_log(Host) ->
gen_server:cast(get_proc_name(Host), reopen_log).
gen_server:cast(get_proc_name_existing(Host), reopen_log).
add_to_log(FileSize, Code, Request) ->
gen_server:cast(get_proc_name(Request#request.host),
gen_server:cast(get_proc_name_existing(Request#request.host),
{add_to_log, FileSize, Code, Request}).
add_to_log(undefined, _FileSize, _Code, _Request) ->
@@ -404,6 +409,7 @@ find_header(Header, Headers, Default) ->
%%----------------------------------------------------------------------
get_proc_name(Host) -> gen_mod:get_module_proc(Host, ?PROCNAME).
get_proc_name_existing(Host) -> gen_mod:get_module_proc_existing(Host, ?PROCNAME).
join([], _) ->
"";
+8
View File
@@ -516,6 +516,12 @@ form_del_get(Host, Lang) ->
%% {error, not_allowed} |
%% {error, invalid_jid}
register_account(Username, Host, Password) ->
try exmpp_jid:make(Username, Host) of
_ -> register_account2(Username, Host, Password)
catch
_ -> {error, invalid_jid}
end.
register_account2(Username, Host, Password) ->
case ejabberd_auth:try_register(Username, Host, Password) of
{atomic, Res} ->
{success, Res, {Username, Host, Password}};
@@ -578,6 +584,8 @@ unregister_account(Username, Host, Password) ->
get_error_text({error, captcha_non_valid}) ->
"The captcha you entered is wrong";
get_error_text({success, exists, _}) ->
get_error_text({atomic, exists});
get_error_text({atomic, exists}) ->
"The account already exists";
get_error_text({error, password_incorrect}) ->
@@ -91,7 +91,7 @@ extract_lang_all ()
cd $MSGS_DIR
for i in $( ls *.msg ) ; do
MISSING=`cat $i.translate | grep "\", \"\"}." | wc -l`
LANGUAGE=`grep "Language:" $i.translate | sed 's/% Language: //g'`
LANGUAGE=`grep "X-Language:" $i.translate | sed 's/% Language: //g'`
LASTAUTH=`grep "Author:" $i.translate | head -n 1 | sed 's/% Author: //g'`
echo -e "$i\t$MISSING\t$LANGUAGE\t$LASTAUTH"
done
@@ -258,7 +258,7 @@ extract_lang_updateall ()
MISSING=`msgfmt --statistics $PO 2>&1 | awk '{printf "%5s", $4 }'`
echo -n " $MISSING"
LANGUAGE=`grep "Language:" $PO | sed 's/\"X-Language: //g' | sed 's/\\\\n\"//g' | awk '{printf "%-12s", $1}'`
LANGUAGE=`grep "X-Language:" $PO | sed 's/\"X-Language: //g' | sed 's/\\\\n\"//g' | awk '{printf "%-12s", $1}'`
echo -n " $LANGUAGE"
LASTAUTH=`grep "Last-Translator" $PO | sed 's/\"Last-Translator: //g' | sed 's/\\\\n\"//g'`