Compare commits

...

75 Commits

Author SHA1 Message Date
Christophe Romain f2f2f64161 update default version to 13.10 2013-09-27 11:19:22 +02:00
Badlop 2c09d7c8a7 Fix handling of format_status arguments (thanks to Nbaronov) 2013-09-26 17:19:56 +02:00
Badlop 389a99b2db Fix bypass for tls-required (thanks to Zeha) 2013-09-26 16:41:57 +02:00
Evgeniy Khramtsov 07c8bf5064 Fix roster version support 2013-09-24 18:59:20 +10:00
Evgeniy Khramtsov 7fd91a4b12 Better web-handlers detection 2013-09-22 21:47:59 +10:00
Evgeniy Khramtsov ae4356265f Merge branch 'master' of github.com:processone/ejabberd 2013-09-19 19:00:08 +10:00
Evgeniy Khramtsov a0396620f2 Reflect modules name changes in p1_mysql 2013-09-19 18:59:32 +10:00
Paweł Chmielowski 7e73ed88f7 Fix problem with decoding http headers over tls connections
This fixed GitHub issue 96.
2013-09-16 16:32:02 +02:00
Evgeniy Khramtsov 150a5392e5 Fix the template: do not use 'global' keyword 2013-08-30 20:44:12 +10:00
Jerome Sautret 8cbbe4a3eb Fix release version number. 2013-08-30 09:12:33 +02:00
Christophe Romain 4b82cb9953 update default version number 2013-08-27 17:14:58 +02:00
Christophe Romain 63b2d21b13 clean subscriptions handling and avoid function_clause 2013-08-27 14:46:54 +02:00
Christophe Romain 1b8876bf55 fix build of Parents list in #pubsub_node 2013-08-27 14:46:35 +02:00
Christophe Romain b82eeeeec9 fix call to get_session_pid with binary arguments 2013-08-27 14:46:22 +02:00
Evgeniy Khramtsov dd26398a02 Fix the ACL rule 2013-08-27 20:12:26 +10:00
Evgeniy Khramtsov 11292c809f Remove non-existent module from the testing config 2013-08-27 20:08:22 +10:00
Evgeniy Khramtsov a62fb69e20 Remove multiple host_config sections 2013-08-27 20:05:12 +10:00
Evgeniy Khramtsov e3483ef9e1 Do not rely on p1_logger_h module when lager is enabled 2013-08-27 17:45:07 +10:00
Evgeniy Khramtsov 0fe3de6b30 Fix ejabberdctl config name 2013-08-24 22:10:10 +10:00
Evgeniy Khramtsov 91a74e3e27 Change configuration file format to YAML 2013-08-21 22:17:59 +10:00
Evgeniy Khramtsov f68dfacbbf Do not try to read/write the deprecated "config" table 2013-08-21 19:37:22 +10:00
Evgeniy Khramtsov 0b9754884e Do not distribute the ACL table 2013-08-21 19:37:08 +10:00
Evgeniy Khramtsov cc6dcd161c Get rid of global configuration options 2013-08-21 19:36:35 +10:00
Badlop 7a77186fbe Enumerate in guide.tex what processes are started for ejabberd (EJAB-1082) 2013-08-05 19:22:22 +02:00
Evgeny Khramtsov 06bb10a032 Merge pull request #85 from imtal/patch-1
Fix version check
2013-07-24 06:01:45 -07:00
tjeerd 3e2f9dc6b0 Fix version check
Not all Erlang versions are of equal length. Added clauses to handle this.
2013-07-24 14:47:32 +02:00
Evgeniy Khramtsov 807a1fe164 Fix timestamp processing 2013-07-23 15:27:18 +10:00
Evgeniy Khramtsov 8ce22b790d Fix some type errors 2013-07-22 19:24:09 +10:00
Evgeniy Khramtsov f75d78d3f5 Make it possible to import without cursor usage 2013-07-22 10:46:47 +10:00
Evgeniy Khramtsov ca6463ed78 Typo fix 2013-07-22 00:22:05 +10:00
Evgeniy Khramtsov d58148fa8d Add SQL to Mnesia converter 2013-07-21 23:10:38 +10:00
Evgeniy Khramtsov a2ead99c83 Make it possible to enable/disable TLS compression 2013-07-17 22:46:18 +10:00
Evgeny Khramtsov 33f09c7a78 Merge pull request #83 from tagged/makefile
make deps/.built depend on deps/.got
2013-07-15 20:06:35 -07:00
Christopher A. Stelma 2d43b035d7 make deps/.built depend on deps/.got
fixes race condition when running make with simultaneous jobs
2013-07-15 10:52:34 -07:00
Evgeniy Khramtsov 986f4d1a7f Do not forget to re-define the crash.log path 2013-07-15 11:51:09 +10:00
Evgeniy Khramtsov 8482641b4e Increase the timetrap timeout 2013-07-15 11:51:02 +10:00
Evgeny Khramtsov 8fc5d86704 Merge pull request #82 from tuncer/rebar-debug_info
rebar.config.script: fix debug_info handling
2013-07-14 04:07:39 -07:00
Tuncer Ayaz 47994806f0 rebar.config.script: fix debug_info handling
debug_info is enabled by default and can be disabled by passing
'no_debug_info' in erl_opts.
2013-07-13 21:33:50 +02:00
Badlop 0d7a5476c0 Allow room member to get members list using XEP 2013-07-11 12:11:48 +02:00
Christophe Romain 4e72dd6751 fix badmatch in send_loop (EJAB-1650) 2013-07-10 09:47:49 +02:00
Christophe Romain 48819d163a fix badmatch on remove_user on PEP nodes (EJAB-1649) 2013-07-09 21:50:56 +02:00
Christophe Romain 8621a8f006 fix invalid response on get_options when no options set (EJAB-1648) 2013-07-09 18:11:27 +02:00
Badlop b66e4fbdc4 Apparently configure.erl is not needed anymore 2013-07-08 15:58:38 +02:00
Alexey Shchepin b5623d6bee gen_iq_handler:check_type was missing 2013-07-08 10:40:39 +03:00
Evgeniy Khramtsov e89f0f6461 Cleanup the test suite (just a little) 2013-07-07 04:47:47 +10:00
Evgeniy Khramtsov 2b24e97936 Use error_logger directly in the LDAP test server 2013-07-07 03:48:16 +10:00
Evgeniy Khramtsov a302af7770 Do not build p1_logger if lager is enabled 2013-07-07 03:10:51 +10:00
Evgeniy Khramtsov 0aca3a4585 Improve the applications start-up
* Check if all modules present for every application loaded.
* Get rid of now obsoleted 'ejabberd_check' module.
2013-07-07 02:19:51 +10:00
Evgeniy Khramtsov c262c08513 Improve the XMPP codec, fix the test suite accordingly 2013-07-07 02:19:10 +10:00
Christophe Romain 0266207e9d fix pubsub unsubscription without SubId on odbc 2013-07-04 15:13:21 +02:00
Christophe Romain 60600c341e improve pubsub odbc use of i2l 2013-07-04 10:07:53 +02:00
Evgeniy Khramtsov 02c59422cf Re-generate the XMPP codec 2013-07-03 01:55:00 +10:00
Evgeniy Khramtsov 97810b4cc3 Update the rebar script 2013-07-01 02:22:18 +10:00
Evgeniy Khramtsov 1a9f0ab084 Update the rebar script 2013-06-30 23:41:24 +10:00
Evgeniy Khramtsov fa7fe73a0e Improve the rebar script. Keep the build graph by default. 2013-06-30 23:41:18 +10:00
Evgeniy Khramtsov 49e17922a3 Remove stupid slow code from rebar, regenerate the script 2013-06-30 23:41:09 +10:00
Evgeniy Khramtsov 09c450062a Update XMPP codec spec to reflect
the changes in xml_gen
2013-06-29 18:41:00 +10:00
Christophe Romain ac3cd2ebaa ip_adresse() does not need binary representation in config 2013-06-28 18:32:22 +02:00
Evgeniy Khramtsov 2636da0d98 Fix IDNA conversion 2013-06-28 02:45:42 +10:00
Badlop 91744733c1 Store only messages with body or subject (EJABS-2034) 2013-06-27 11:58:31 +02:00
Evgeniy Khramtsov 295ea0633e Re-create the configure script 2013-06-27 19:46:38 +10:00
Evgeniy Khramtsov bc2ea224eb Fix a typo 2013-06-27 19:45:45 +10:00
Evgeniy Khramtsov 9f4d12bd0a Add --enable-tools to --enable-all 2013-06-27 19:45:28 +10:00
Evgeniy Khramtsov 653e23c0e7 Re-create the configure script 2013-06-27 19:31:33 +10:00
Evgeniy Khramtsov c7e2128dab Enable lager support by default 2013-06-27 19:31:05 +10:00
Evgeniy Khramtsov 8a9743ab3b Avoid custom loglevels processing for lager 2013-06-27 19:27:56 +10:00
Evgeniy Khramtsov 597934637c Logger improvements.
lager:
	* It is now possible to change the loglevel.
	* Log rotation is now supported as well.
p1_logger:
	* When rotating a log file, add suffix ".0" to the renamed file.
	  This is needed in order to be consistent with lager
	  rotation mechanism.
2013-06-27 19:03:33 +10:00
Evgeniy Khramtsov 2ea397e476 Pre-load applications before setting the environment variables 2013-06-27 19:03:25 +10:00
Evgeniy Khramtsov 9094169440 Do not forget to remove testing beams on "clean" 2013-06-27 15:36:39 +10:00
Evgeniy Khramtsov 3631301304 Do not check the second SASL challenge 2013-06-27 15:36:31 +10:00
Evgeniy Khramtsov 42a9e4f4cf Split the test suite into modules 2013-06-27 15:36:23 +10:00
Evgeniy Khramtsov aab70fc066 Fix external authentication 2013-06-26 12:30:32 +10:00
Evgeniy Khramtsov 167f02ab72 Do not fetch disco#info multiple times 2013-06-26 03:32:38 +10:00
Evgeniy Khramtsov 1b0c02cb2e Start the LDAP server before ejabberd application 2013-06-26 03:06:19 +10:00
Evgeniy Khramtsov c05edabe58 Add more pubsub test cases 2013-06-26 03:06:11 +10:00
99 changed files with 10933 additions and 8090 deletions
+5 -4
View File
@@ -77,7 +77,7 @@ deps/.got:
rm -rf deps/.built
$(REBAR) get-deps && :> deps/.got
deps/.built:
deps/.built: deps/.got
$(REBAR) compile && :> deps/.built
src: deps/.built
@@ -109,9 +109,9 @@ install: all
#
# Configuration files
$(INSTALL) -d -m 750 $(G_USER) $(ETCDIR)
[ -f $(ETCDIR)/ejabberd.cfg ] \
&& $(INSTALL) -b -m 640 $(G_USER) ejabberd.cfg.example $(ETCDIR)/ejabberd.cfg-new \
|| $(INSTALL) -b -m 640 $(G_USER) ejabberd.cfg.example $(ETCDIR)/ejabberd.cfg
[ -f $(ETCDIR)/ejabberd.yml ] \
&& $(INSTALL) -b -m 640 $(G_USER) ejabberd.yml.example $(ETCDIR)/ejabberd.yml-new \
|| $(INSTALL) -b -m 640 $(G_USER) ejabberd.yml.example $(ETCDIR)/ejabberd.yml
$(SED) -e "s*{{rootdir}}*@prefix@*" \
-e "s*{{installuser}}*@INSTALLUSER@*" \
-e "s*{{libdir}}*@libdir@*" \
@@ -215,6 +215,7 @@ uninstall-all: uninstall-binary
clean:
rm -rf deps/.got
rm -rf deps/.built
rm -rf test/*.beam
$(REBAR) clean
clean-rel:
+1
View File
@@ -9,6 +9,7 @@ To compile ejabberd you need:
- GNU Make
- GCC
- Libexpat 1.95 or higher
- Libyaml 1.4 or higher
- Erlang/OTP R15B or higher.
- OpenSSL 0.9.8 or higher, for STARTTLS, SASL and SSL encryption.
- Zlib 1.2.3 or higher, for Stream Compression support
Vendored
+206 -235
View File
File diff suppressed because it is too large Load Diff
+14 -14
View File
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.53)
AC_INIT(ejabberd, community, [ejabberd@process-one.net], [ejabberd])
AC_INIT(ejabberd, community m4_esyscmd([git describe --tags | tr -d '\n']), [ejabberd@process-one.net], [ejabberd])
REQUIRE_ERLANG_MIN="5.9.1 (Erlang/OTP R15B01)"
REQUIRE_ERLANG_MAX="9.0.0 (No Max)"
@@ -35,7 +35,7 @@ AC_ERLANG_NEED_ERLC
AC_ARG_ENABLE(erlang-version-check,
[AC_HELP_STRING([--enable-erlang-version-check],
[Check Erlang/OTP version @<:@default=yes@:>@])])
case "$enable_erlang_version_check" in
case "$enable_erlang_version_check" in
yes|'')
ERLANG_VERSION_CHECK([$REQUIRE_ERLANG_MIN],[$REQUIRE_ERLANG_MAX])
;;
@@ -105,21 +105,21 @@ AC_ARG_ENABLE(mssql,
*) AC_MSG_ERROR(bad value ${enableval} for --enable-mssql) ;;
esac],[db_type=generic])
AC_ARG_ENABLE(all,
[AC_HELP_STRING([--enable-all], [same as --enable-nif --enable-odbc --enable-mysql --enable-pgsql --enable-pam --enable-zlib --enable-stun --enable-riak --enable-json --enable-iconv --enable-debug --enable-http --enable-lager --enable-tools (useful for Dialyzer checks, default: no)])],
[case "${enableval}" in
yes) nif=true odbc=true mysql=true pgsql=true pam=true zlib=true stun=true riak=true json=true iconv=true debug=true http=true lager=true tools=true ;;
no) nif=false odbc=false mysql=false pgsql=false pam=false zlib=false stun=false riak=false json=false iconv=false debug=false http=false lager=false tools=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-all) ;;
esac],[])
AC_ARG_ENABLE(tools,
[AC_HELP_STRING([--enable-tools], [build development tools (currently the ejabberd profiler only, default: no)])],
[AC_HELP_STRING([--enable-tools], [build development tools (default: no)])],
[case "${enableval}" in
yes) tools=true ;;
no) tools=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-tools) ;;
esac],[tools=false])
AC_ARG_ENABLE(all,
[AC_HELP_STRING([--enable-all], [same as --enable-nif --enable-odbc --enable-mysql --enable-pgsql --enable-pam --enable-zlib --enable-stun --enable-json --enable-iconv --enable-debug --enable-http (useful for Dialyzer checks, default: no)])],
[case "${enableval}" in
yes) nif=true odbc=true mysql=true pgsql=true pam=true zlib=true stun=true json=true iconv=true debug=true http=true ;;
no) nif=false odbc=false mysql=false pgsql=false pam=false zlib=false stun=false json=false iconv=false debug=false http=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-all) ;;
esac],[])
esac],[if test "x$tools" = "x"; then tools=false; fi])
AC_ARG_ENABLE(nif,
[AC_HELP_STRING([--enable-nif], [replace some functions with C equivalents. Requires Erlang R13B04 or higher (default: no)])],
@@ -210,12 +210,12 @@ AC_ARG_ENABLE(http,
esac],[if test "x$http" = "x"; then http=false; fi])
AC_ARG_ENABLE(lager,
[AC_HELP_STRING([--enable-lager], [enable lager support (default: no)])],
[AC_HELP_STRING([--enable-lager], [enable lager support (default: yes)])],
[case "${enableval}" in
yes) lager=true ;;
no) lager=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-lager) ;;
esac],[if test "x$lager" = "x"; then lager=false; fi])
esac],[if test "x$lager" = "x"; then lager=true; fi])
AC_CONFIG_FILES([Makefile
vars.config
+2 -2
View File
@@ -13,12 +13,12 @@ all: release pdf html
release:
@echo "Notes for the releaser:"
@echo "* Do not forget to add a link to the release notes in guide.tex"
@echo "* Do not forget to update the version number in src/ejabberd.app!"
@echo "* Do not forget to update the version number in ebin/ejabberd.app!"
@echo "* Do not forget to update the features in introduction.tex (including \new{} and \improved{} tags)."
@echo "Press any key to continue"
##@read foo
@echo "% ejabberd version (automatically generated)." > version.tex
@echo "\newcommand{\version}{"`sed '/vsn/!d;s/\(.*\)"\(.*\)"\(.*\)/\2/' ../src/ejabberd.app`"}" >> version.tex
@echo "\newcommand{\version}{"`sed '/vsn/!d;s/\(.*\)"\(.*\)"\(.*\)/\2/' ../ebin/ejabberd.app`"}" >> version.tex
@echo -n "% Contributed modules (automatically generated)." > contributed_modules.tex
@echo -e "$(CONTRIBUTED_MODULES)" >> contributed_modules.tex
+1555 -1418
View File
File diff suppressed because it is too large Load Diff
+1327 -1378
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,2 +1,2 @@
% ejabberd version (automatically generated).
\newcommand{\version}{13.03-beta2}
\newcommand{\version}{13.10}
-609
View File
@@ -1,609 +0,0 @@
%%%
%%% ejabberd configuration file
%%%
%%%'
%%% The parameters used in this configuration file are explained in more detail
%%% in the ejabberd Installation and Operation Guide.
%%% Please consult the Guide in case of doubts, it is included with
%%% your copy of ejabberd, and is also available online at
%%% http://www.process-one.net/en/ejabberd/docs/
%%% This configuration file contains Erlang terms.
%%% In case you want to understand the syntax, here are the concepts:
%%%
%%% - The character to comment a line is %
%%%
%%% - Each term ends in a dot, for example:
%%% override_global.
%%%
%%% - A tuple has a fixed definition, its elements are
%%% enclosed in {}, and separated with commas:
%%% {loglevel, 4}.
%%%
%%% - A list can have as many elements as you want,
%%% and is enclosed in [], for example:
%%% [http_poll, web_admin, tls]
%%%
%%% - A keyword of ejabberd is a word in lowercase.
%%% Strings are enclosed in "" and can contain spaces, dots, ...
%%% {language, "en"}.
%%% {ldap_rootdn, "dc=example,dc=com"}.
%%%
%%% - This term includes a tuple, a keyword, a list, and two strings:
%%% {hosts, ["jabber.example.net", "im.example.com"]}.
%%%
%%%. =======================
%%%' OVERRIDE STORED OPTIONS
%%
%% Override the old values stored in the database.
%%
%%
%% Override global options (shared by all ejabberd nodes in a cluster).
%%
%%override_global.
%%
%% Override local options (specific for this particular ejabberd node).
%%
%%override_local.
%%
%% Remove the Access Control Lists before new ones are added.
%%
%%override_acls.
%%%. =========
%%%' DEBUGGING
%%
%% loglevel: Verbosity of log files generated by ejabberd.
%% 0: No ejabberd log at all (not recommended)
%% 1: Critical
%% 2: Error
%% 3: Warning
%% 4: Info
%% 5: Debug
%%
{loglevel, 4}.
%%
%% watchdog_admins: Only useful for developers: if an ejabberd process
%% consumes a lot of memory, send live notifications to these XMPP
%% accounts.
%%
%%{watchdog_admins, ["bob@example.com"]}.
%%%. ================
%%%' SERVED HOSTNAMES
%%
%% hosts: Domains served by ejabberd.
%% You can define one or several, for example:
%% {hosts, ["example.net", "example.com", "example.org"]}.
%%
{hosts, ["localhost"]}.
%%
%% route_subdomains: Delegate subdomains to other XMPP servers.
%% For example, if this ejabberd serves example.org and you want
%% to allow communication with an XMPP server called im.example.org.
%%
%%{route_subdomains, s2s}.
%%%. ===============
%%%' LISTENING PORTS
%%
%% listen: The ports ejabberd will listen on, which service each is handled
%% by and what options to start it with.
%%
{listen,
[
{5222, ejabberd_c2s, [
%%
%% If TLS is compiled in and you installed a SSL
%% certificate, specify the full path to the
%% file and uncomment this line:
%%
%%{certfile, "/path/to/ssl.pem"}, starttls,
{access, c2s},
{shaper, c2s_shaper},
{max_stanza_size, 65536}
]},
%%
%% To enable the old SSL connection method on port 5223:
%%
%%{5223, ejabberd_c2s, [
%% {access, c2s},
%% {shaper, c2s_shaper},
%% {certfile, "/path/to/ssl.pem"}, tls,
%% {max_stanza_size, 65536}
%% ]},
{5269, ejabberd_s2s_in, [
{shaper, s2s_shaper},
{max_stanza_size, 131072}
]},
%%
%% ejabberd_service: Interact with external components (transports, ...)
%%
%%{8888, ejabberd_service, [
%% {access, all},
%% {shaper_rule, fast},
%% {hosts, ["icq.example.org", "sms.example.org"],
%% [{password, "secret"}]
%% }
%% ]},
%%
%% ejabberd_stun: Handles STUN Binding requests
%%
%%{{3478, udp}, ejabberd_stun, []},
{5280, ejabberd_http, [
%%{request_handlers,
%% [
%% {["pub", "archive"], mod_http_fileserver}
%% ]},
captcha,
http_bind,
http_poll,
%%register,
web_admin
]}
]}.
%%
%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections.
%% Allowed values are: false optional required required_trusted
%% You must specify a certificate file.
%%
%%{s2s_use_starttls, optional}.
%%
%% s2s_certfile: Specify a certificate file.
%%
%%{s2s_certfile, "/path/to/ssl.pem"}.
%%
%% domain_certfile: Specify a different certificate for each served hostname.
%%
%%{domain_certfile, "example.org", "/path/to/example_org.pem"}.
%%{domain_certfile, "example.com", "/path/to/example_com.pem"}.
%%
%% S2S whitelist or blacklist
%%
%% Default s2s policy for undefined hosts.
%%
%%{s2s_default_policy, allow}.
%%
%% Allow or deny communication with specific servers.
%%
%%{{s2s_host, "goodhost.org"}, allow}.
%%{{s2s_host, "badhost.org"}, deny}.
%%
%% Outgoing S2S options
%%
%% Preferred address families (which to try first) and connect timeout
%% in milliseconds.
%%
%%{outgoing_s2s_options, [ipv4, ipv6], 10000}.
%%%. ==============
%%%' AUTHENTICATION
%%
%% auth_method: Method used to authenticate the users.
%% The default method is the internal.
%% If you want to use a different method,
%% comment this line and enable the correct ones.
%%
{auth_method, internal}.
%%
%% Store the plain passwords or hashed for SCRAM:
%%{auth_password_format, plain}.
%%{auth_password_format, scram}.
%%
%% Define the FQDN if ejabberd doesn't detect it:
%%{fqdn, "server3.example.com"}.
%%
%% Authentication using external script
%% Make sure the script is executable by ejabberd.
%%
%%{auth_method, external}.
%%{extauth_program, "/path/to/authentication/script"}.
%%
%% Authentication using ODBC
%% Remember to setup a database in the next section.
%%
%%{auth_method, odbc}.
%%
%% Authentication using PAM
%%
%%{auth_method, pam}.
%%{pam_service, "pamservicename"}.
%%
%% Authentication using LDAP
%%
%%{auth_method, ldap}.
%%
%% List of LDAP servers:
%%{ldap_servers, ["localhost"]}.
%%
%% Encryption of connection to LDAP servers:
%%{ldap_encrypt, none}.
%%{ldap_encrypt, tls}.
%%
%% Port to connect to on LDAP servers:
%%{ldap_port, 389}.
%%{ldap_port, 636}.
%%
%% LDAP manager:
%%{ldap_rootdn, "dc=example,dc=com"}.
%%
%% Password of LDAP manager:
%%{ldap_password, "******"}.
%%
%% Search base of LDAP directory:
%%{ldap_base, "dc=example,dc=com"}.
%%
%% LDAP attribute that holds user ID:
%%{ldap_uids, [{"mail", "%u@mail.example.org"}]}.
%%
%% LDAP filter:
%%{ldap_filter, "(objectClass=shadowAccount)"}.
%%
%% Anonymous login support:
%% auth_method: anonymous
%% anonymous_protocol: sasl_anon | login_anon | both
%% allow_multiple_connections: true | false
%%
%%{host_config, "public.example.org", [{auth_method, anonymous},
%% {allow_multiple_connections, false},
%% {anonymous_protocol, sasl_anon}]}.
%%
%% To use both anonymous and internal authentication:
%%
%%{host_config, "public.example.org", [{auth_method, [internal, anonymous]}]}.
%%%. ==============
%%%' DATABASE SETUP
%% ejabberd by default uses the internal Mnesia database,
%% so you do not necessarily need this section.
%% This section provides configuration examples in case
%% you want to use other database backends.
%% Please consult the ejabberd Guide for details on database creation.
%%
%% MySQL server:
%%
%%{odbc_server, {mysql, "server", "database", "username", "password"}}.
%%
%% If you want to specify the port:
%%{odbc_server, {mysql, "server", 1234, "database", "username", "password"}}.
%%
%% PostgreSQL server:
%%
%%{odbc_server, {pgsql, "server", "database", "username", "password"}}.
%%
%% If you want to specify the port:
%%{odbc_server, {pgsql, "server", 1234, "database", "username", "password"}}.
%%
%% If you use PostgreSQL, have a large database, and need a
%% faster but inexact replacement for "select count(*) from users"
%%
%%{pgsql_users_number_estimate, true}.
%%
%% ODBC compatible or MSSQL server:
%%
%%{odbc_server, "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"}.
%%
%% Number of connections to open to the database for each virtual host
%%
%%{odbc_pool_size, 10}.
%%
%% Interval to make a dummy SQL request to keep the connections to the
%% database alive. Specify in seconds: for example 28800 means 8 hours
%%
%%{odbc_keepalive_interval, undefined}.
%%%. ===============
%%%' TRAFFIC SHAPERS
%%
%% The "normal" shaper limits traffic speed to 1000 B/s
%%
{shaper, normal, {maxrate, 1000}}.
%%
%% The "fast" shaper limits traffic speed to 50000 B/s
%%
{shaper, fast, {maxrate, 50000}}.
%%
%% This option specifies the maximum number of elements in the queue
%% of the FSM. Refer to the documentation for details.
%%
{max_fsm_queue, 1000}.
%%%. ====================
%%%' ACCESS CONTROL LISTS
%%
%% The 'admin' ACL grants administrative privileges to XMPP accounts.
%% You can put here as many accounts as you want.
%%
%%{acl, admin, {user, "aleksey", "localhost"}}.
%%{acl, admin, {user, "ermine", "example.org"}}.
%%
%% Blocked users
%%
%%{acl, blocked, {user, "baduser", "example.org"}}.
%%{acl, blocked, {user, "test"}}.
%%
%% Local users: don't modify this line.
%%
{acl, local, {user_regexp, ""}}.
%%
%% More examples of ACLs
%%
%%{acl, jabberorg, {server, "jabber.org"}}.
%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}.
%%{acl, test, {user_regexp, "^test"}}.
%%{acl, test, {user_glob, "test*"}}.
%%
%% Define specific ACLs in a virtual host.
%%
%%{host_config, "localhost",
%% [
%% {acl, admin, {user, "bob-local", "localhost"}}
%% ]
%%}.
%%%. ============
%%%' ACCESS RULES
%% Maximum number of simultaneous sessions allowed for a single user:
{access, max_user_sessions, [{10, all}]}.
%% Maximum number of offline messages that users can have:
{access, max_user_offline_messages, [{5000, admin}, {100, all}]}.
%% This rule allows access only for local users:
{access, local, [{allow, local}]}.
%% Only non-blocked users can use c2s connections:
{access, c2s, [{deny, blocked},
{allow, all}]}.
%% For C2S connections, all users except admins use the "normal" shaper
{access, c2s_shaper, [{none, admin},
{normal, all}]}.
%% All S2S connections use the "fast" shaper
{access, s2s_shaper, [{fast, all}]}.
%% Only admins can send announcement messages:
{access, announce, [{allow, admin}]}.
%% Only admins can use the configuration interface:
{access, configure, [{allow, admin}]}.
%% Admins of this server are also admins of the MUC service:
{access, muc_admin, [{allow, admin}]}.
%% Only accounts of the local ejabberd server can create rooms:
{access, muc_create, [{allow, local}]}.
%% All users are allowed to use the MUC service:
{access, muc, [{allow, all}]}.
%% Only accounts on the local ejabberd server can create Pubsub nodes:
{access, pubsub_createnode, [{allow, local}]}.
%% In-band registration allows registration of any possible username.
%% To disable in-band registration, replace 'allow' with 'deny'.
{access, register, [{allow, all}]}.
%% By default the frequency of account registrations from the same IP
%% is limited to 1 account every 10 minutes. To disable, specify: infinity
%%{registration_timeout, 600}.
%%
%% Define specific Access Rules in a virtual host.
%%
%%{host_config, "localhost",
%% [
%% {access, c2s, [{allow, admin}, {deny, all}]},
%% {access, register, [{deny, all}]}
%% ]
%%}.
%%%. ================
%%%' DEFAULT LANGUAGE
%%
%% language: Default language used for server messages.
%%
{language, "en"}.
%%
%% Set a different default language in a virtual host.
%%
%%{host_config, "localhost",
%% [{language, "ru"}]
%%}.
%%%. =======
%%%' CAPTCHA
%%
%% Full path to a script that generates the image.
%%
%%{captcha_cmd, "/lib/ejabberd/priv/bin/captcha.sh"}.
%%
%% Host for the URL and port where ejabberd listens for CAPTCHA requests.
%%
%%{captcha_host, "example.org:5280"}.
%%
%% Limit CAPTCHA calls per minute for JID/IP to avoid DoS.
%%
%%{captcha_limit, 5}.
%%%. =======
%%%' MODULES
%%
%% Modules enabled in all ejabberd virtual hosts.
%%
{modules,
[
{mod_adhoc, []},
{mod_announce, [{access, announce}]}, % recommends mod_adhoc
{mod_blocking,[]}, % requires mod_privacy
{mod_caps, []},
{mod_configure,[]}, % requires mod_adhoc
{mod_disco, []},
%%{mod_echo, [{host, "echo.localhost"}]},
{mod_irc, []},
{mod_http_bind, []},
%%{mod_http_fileserver, [
%% {docroot, "/var/www"},
%% {accesslog, "/var/log/ejabberd/access.log"}
%% ]},
{mod_last, []},
{mod_muc, [
%%{host, "conference.@HOST@"},
{access, muc},
{access_create, muc_create},
{access_persistent, muc_create},
{access_admin, muc_admin}
]},
%%{mod_muc_log,[]},
{mod_offline, [{access_max_user_messages, max_user_offline_messages}]},
{mod_ping, []},
%%{mod_pres_counter,[{count, 5}, {interval, 60}]},
{mod_privacy, []},
{mod_private, []},
%%{mod_proxy65,[]},
{mod_pubsub, [
{access_createnode, pubsub_createnode},
{ignore_pep_from_offline, true}, % reduces resource comsumption, but XEP incompliant
%%{ignore_pep_from_offline, false}, % XEP compliant, but increases resource comsumption
{last_item_cache, false},
{plugins, ["flat", "hometree", "pep"]} % pep requires mod_caps
]},
{mod_register, [
%%
%% Protect In-Band account registrations with CAPTCHA.
%%
%%{captcha_protected, true},
%%
%% Set the minimum informational entropy for passwords.
%%
%%{password_strength, 32},
%%
%% After successful registration, the user receives
%% a message with this subject and body.
%%
{welcome_message, {"Welcome!",
"Hi.\nWelcome to this XMPP server."}},
%%
%% When a user registers, send a notification to
%% these XMPP accounts.
%%
%%{registration_watchers, ["admin1@example.org"]},
%%
%% Only clients in the server machine can register accounts
%%
{ip_access, [{allow, "127.0.0.0/8"},
{deny, "0.0.0.0/0"}]},
%%
%% Local c2s or remote s2s users cannot register accounts
%%
%%{access_from, deny},
{access, register}
]},
%%{mod_register_web, [
%%
%% When a user registers, send a notification to
%% these XMPP accounts.
%%
%%{registration_watchers, ["admin1@example.org"]}
%% ]},
{mod_roster, []},
%%{mod_service_log,[]},
{mod_shared_roster,[]},
{mod_stats, []},
{mod_time, []},
{mod_vcard, []},
{mod_version, []}
]}.
%%
%% Enable modules with custom options in a specific virtual host
%%
%%{host_config, "localhost",
%% [{{add, modules},
%% [
%% {mod_echo, [{host, "mirror.localhost"}]}
%% ]
%% }
%% ]}.
%%%.
%%%'
%%% $Id$
%%% Local Variables:
%%% mode: erlang
%%% End:
%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker:
+612
View File
@@ -0,0 +1,612 @@
###
### ejabberd configuration file
###
###
### The parameters used in this configuration file are explained in more detail
### in the ejabberd Installation and Operation Guide.
### Please consult the Guide in case of doubts, it is included with
### your copy of ejabberd, and is also available online at
### http://www.process-one.net/en/ejabberd/docs/
### The configuration file is written in YAML.
### Refer to http://en.wikipedia.org/wiki/YAML for the brief description.
### However, ejabberd treats different literals as different types:
###
### - unquoted or single-quoted strings. They are called "atoms".
### Example: dog, 'Jupiter', '3.14159', YELLOW
###
### - numeric literals. Example: 3, -45.0, .0
###
### - quoted or folded strings.
### Examples of quoted string: "Lizzard", "orange".
### Example of folded string:
### > Art thou not Romeo,
### and a Montague?
### =========
### DEBUGGING
##
## loglevel: Verbosity of log files generated by ejabberd.
## 0: No ejabberd log at all (not recommended)
## 1: Critical
## 2: Error
## 3: Warning
## 4: Info
## 5: Debug
##
loglevel: 4
##
## watchdog_admins: Only useful for developers: if an ejabberd process
## consumes a lot of memory, send live notifications to these XMPP
## accounts.
##
## watchdog_admins:
## - "bob@example.com"
### ================
### SERVED HOSTNAMES
##
## hosts: Domains served by ejabberd.
## You can define one or several, for example:
## hosts:
## - "example.net"
## - "example.com"
## - "example.org"
##
hosts:
- "localhost"
##
## route_subdomains: Delegate subdomains to other XMPP servers.
## For example, if this ejabberd serves example.org and you want
## to allow communication with an XMPP server called im.example.org.
##
## route_subdomains: s2s
### ===============
### LISTENING PORTS
##
## listen: The ports ejabberd will listen on, which service each is handled
## by and what options to start it with.
##
listen:
-
port: 5222
module: ejabberd_c2s
##
## If TLS is compiled in and you installed a SSL
## certificate, specify the full path to the
## file and uncomment this line:
##
## certfile: "/path/to/ssl.pem"
## starttls: true
max_stanza_size: 65536
shaper: c2s_shaper
access: c2s
-
port: 5269
module: ejabberd_s2s_in
##
## ejabberd_service: Interact with external components (transports, ...)
##
## -
## port: 8888
## module: ejabberd_service
## access: all
## shaper_rule: fast
## ip: "127.0.0.1"
## hosts:
## "icq.example.org":
## password: "secret"
## "sms.example.org":
## password: "secret"
##
## ejabberd_stun: Handles STUN Binding requests
##
## -
## port: 3478
## transport: udp
## module: ejabberd_stun
##
## To handle XML-RPC requests that provide admin credentials:
##
## -
## port: 4560
## module: ejabberd_xmlrpc
-
port: 5280
module: ejabberd_http
## request_handlers:
## "/pub/archive": mod_http_fileserver
web_admin: true
http_poll: true
http_bind: true
## register: true
captcha: true
##
## s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections.
## Allowed values are: false optional required required_trusted
## You must specify a certificate file.
##
## s2s_use_starttls: optional
##
## s2s_certfile: Specify a certificate file.
##
## s2s_certfile: "/path/to/ssl.pem"
##
## domain_certfile: Specify a different certificate for each served hostname.
##
## host_config:
## "example.org":
## domain_certfile: "/path/to/example_org.pem"
## "example.com":
## domain_certfile: "/path/to/example_com.pem"
##
## S2S whitelist or blacklist
##
## Default s2s policy for undefined hosts.
##
## s2s_policy: s2s_access
##
## Outgoing S2S options
##
## Preferred address families (which to try first) and connect timeout
## in milliseconds.
##
## outgoing_s2s_families:
## - ipv4
## - ipv6
## outgoing_s2s_timeout: 10000
### ==============
### AUTHENTICATION
##
## auth_method: Method used to authenticate the users.
## The default method is the internal.
## If you want to use a different method,
## comment this line and enable the correct ones.
##
auth_method: internal
##
## Store the plain passwords or hashed for SCRAM:
## auth_password_format: plain
## auth_password_format: scram
##
## Define the FQDN if ejabberd doesn't detect it:
## fqdn: "server3.example.com"
##
## Authentication using external script
## Make sure the script is executable by ejabberd.
##
## auth_method: external
## extauth_program: "/path/to/authentication/script"
##
## Authentication using ODBC
## Remember to setup a database in the next section.
##
## auth_method: odbc
##
## Authentication using PAM
##
## auth_method: pam
## pam_service: "pamservicename"
##
## Authentication using LDAP
##
## auth_method: ldap
##
## List of LDAP servers:
## ldap_servers:
## - "localhost"
##
## Encryption of connection to LDAP servers:
## ldap_encrypt: none
## ldap_encrypt: tls
##
## Port to connect to on LDAP servers:
## ldap_port: 389
## ldap_port: 636
##
## LDAP manager:
## ldap_rootdn: "dc=example,dc=com"
##
## Password of LDAP manager:
## ldap_password: "******"
##
## Search base of LDAP directory:
## ldap_base: "dc=example,dc=com"
##
## LDAP attribute that holds user ID:
## ldap_uids:
## - "mail": "%u@mail.example.org"
##
## LDAP filter:
## ldap_filter: "(objectClass=shadowAccount)"
##
## Anonymous login support:
## auth_method: anonymous
## anonymous_protocol: sasl_anon | login_anon | both
## allow_multiple_connections: true | false
##
## host_config:
## "public.example.org":
## auth_method: anonymous
## allow_multiple_connections: false
## anonymous_protocol: sasl_anon
##
## To use both anonymous and internal authentication:
##
## host_config:
## "public.example.org":
## auth_method:
## - internal
## - anonymous
### ==============
### DATABASE SETUP
## ejabberd by default uses the internal Mnesia database,
## so you do not necessarily need this section.
## This section provides configuration examples in case
## you want to use other database backends.
## Please consult the ejabberd Guide for details on database creation.
##
## MySQL server:
##
## odbc_type: mysql
## odbc_server: "server"
## odbc_database: "database"
## odbc_username: "username"
## odbc_password: "password"
##
## If you want to specify the port:
## odbc_port: 1234
##
## PostgreSQL server:
##
## odbc_type: pgsql
## odbc_server: "server"
## odbc_database: "database"
## odbc_username: "username"
## odbc_password: "password"
##
## If you want to specify the port:
## odbc_port: 1234
##
## If you use PostgreSQL, have a large database, and need a
## faster but inexact replacement for "select count(*) from users"
##
## pgsql_users_number_estimate: true
##
## ODBC compatible or MSSQL server:
##
## odbc_type: odbc
## odbc_server: "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"
##
## Number of connections to open to the database for each virtual host
##
## odbc_pool_size: 10
##
## Interval to make a dummy SQL request to keep the connections to the
## database alive. Specify in seconds: for example 28800 means 8 hours
##
## odbc_keepalive_interval: undefined
### ===============
### TRAFFIC SHAPERS
shaper:
##
## The "normal" shaper limits traffic speed to 1000 B/s
##
normal: 1000
##
## The "fast" shaper limits traffic speed to 50000 B/s
##
fast: 50000
##
## This option specifies the maximum number of elements in the queue
## of the FSM. Refer to the documentation for details.
##
max_fsm_queue: 1000
###. ====================
###' ACCESS CONTROL LISTS
acl:
##
## The 'admin' ACL grants administrative privileges to XMPP accounts.
## You can put here as many accounts as you want.
##
## admin:
## user:
## - "aleksey": "localhost"
## - "ermine": "example.org"
##
## Blocked users
##
## blocked:
## user:
## - "baduser": "example.org"
## - "test"
## Local users: don't modify this.
##
local:
user_regexp: ""
##
## More examples of ACLs
##
## jabberorg:
## server:
## - "jabber.org"
## aleksey:
## user:
## - "aleksey": "jabber.ru"
## test:
## user_regexp: "^test"
## user_glob: "test*"
##
## Loopback network
##
loopback:
ip:
- "127.0.0.0/8"
##
## Bad XMPP servers
##
## bad_servers:
## server:
## - "xmpp.zombie.org"
## - "xmpp.spam.com"
##
## Define specific ACLs in a virtual host.
##
## host_config:
## "localhost":
## acl:
## admin:
## user:
## - "bob-local": "localhost"
### ============
### ACCESS RULES
access:
## Maximum number of simultaneous sessions allowed for a single user:
max_user_sessions:
all: 10
## Maximum number of offline messages that users can have:
max_user_offline_messages:
admin: 5000
all: 100
## This rule allows access only for local users:
local:
local: allow
## Only non-blocked users can use c2s connections:
c2s:
blocked: deny
all: allow
## For C2S connections, all users except admins use the "normal" shaper
c2s_shaper:
admin: none
all: normal
## All S2S connections use the "fast" shaper
s2s_shaper:
all: fast
## Only admins can send announcement messages:
announce:
admin: allow
## Only admins can use the configuration interface:
configure:
admin: allow
## Admins of this server are also admins of the MUC service:
muc_admin:
admin: allow
## Only accounts of the local ejabberd server can create rooms:
muc_create:
local: allow
## All users are allowed to use the MUC service:
muc:
all: allow
## Only accounts on the local ejabberd server can create Pubsub nodes:
pubsub_createnode:
local: allow
## In-band registration allows registration of any possible username.
## To disable in-band registration, replace 'allow' with 'deny'.
register:
all: allow
## Only allow to register from localhost
trusted_network:
loopback: allow
## Do not establish S2S connections with bad servers
## s2s_access:
## bad_servers: deny
## all: allow
## By default the frequency of account registrations from the same IP
## is limited to 1 account every 10 minutes. To disable, specify: infinity
## registration_timeout: 600
##
## Define specific Access Rules in a virtual host.
##
## host_config:
## "localhost":
## access:
## c2s:
## admin: allow
## all: deny
## register:
## all: deny
### ================
### DEFAULT LANGUAGE
##
## language: Default language used for server messages.
##
language: "en"
##
## Set a different default language in a virtual host.
##
## host_config:
## "localhost":
## language: "ru"
### =======
### CAPTCHA
##
## Full path to a script that generates the image.
##
## captcha_cmd: "/lib/ejabberd/priv/bin/captcha.sh"
##
## Host for the URL and port where ejabberd listens for CAPTCHA requests.
##
## captcha_host: "example.org:5280"
##
## Limit CAPTCHA calls per minute for JID/IP to avoid DoS.
##
## captcha_limit: 5
### =======
### MODULES
##
## Modules enabled in all ejabberd virtual hosts.
##
modules:
mod_adhoc: {}
mod_announce: # recommends mod_adhoc
access: announce
mod_blocking: {} # requires mod_privacy
mod_caps: {}
mod_configure: {} # requires mod_adhoc
mod_disco: {}
## mod_echo: {}
mod_irc: {}
mod_http_bind: {}
## mod_http_fileserver:
## docroot: "/var/www"
## accesslog: "/var/log/ejabberd/access.log"
mod_last: {}
mod_muc:
## host: "conference.@HOST@"
access: muc
access_create: muc_create
access_persistent: muc_create
access_admin: muc_admin
## mod_muc_log: {}
mod_offline:
access_max_user_messages: max_user_offline_messages
mod_ping: {}
## mod_pres_counter:
## count: 5
## interval: 60
mod_privacy: {}
mod_private: {}
## mod_proxy65: {}
mod_pubsub:
access_createnode: pubsub_createnode
## reduces resource comsumption, but XEP incompliant
ignore_pep_from_offline: true
## XEP compliant, but increases resource comsumption
## ignore_pep_from_offline: false
last_item_cache: false
plugins:
- "flat"
- "hometree"
- "pep" # pep requires mod_caps
mod_register:
##
## Protect In-Band account registrations with CAPTCHA.
##
## captcha_protected: true
##
## Set the minimum informational entropy for passwords.
##
## password_strength: 32
##
## After successful registration, the user receives
## a message with this subject and body.
##
welcome_message:
subject: "Welcome!"
body: |-
Hi.
Welcome to this XMPP server.
##
## When a user registers, send a notification to
## these XMPP accounts.
##
## registration_watchers:
## - "admin1@example.org"
##
## Only clients in the server machine can register accounts
##
ip_access: trusted_network
##
## Local c2s or remote s2s users cannot register accounts
##
## access_from: deny
access: register
mod_roster: {}
mod_shared_roster: {}
mod_stats: {}
mod_time: {}
mod_vcard: {}
mod_version: {}
##
## Enable modules with custom options in a specific virtual host
##
## host_config:
## "localhost":
## add:
## modules:
## mod_echo:
## host: "mirror.localhost"
### Local Variables:
### mode: yaml
### End:
### vim: set filetype=yaml tabstop=8
+1 -1
View File
@@ -65,7 +65,7 @@ if [ -f "$EJABBERDCTL_CONFIG_PATH" ] ; then
. "$EJABBERDCTL_CONFIG_PATH"
fi
if [ "$EJABBERD_CONFIG_PATH" = "" ] ; then
EJABBERD_CONFIG_PATH=$ETCDIR/ejabberd.cfg
EJABBERD_CONFIG_PATH=$ETCDIR/ejabberd.yml
fi
if [ "$LOGS_DIR" = "" ] ; then
LOGS_DIR={{localstatedir}}/log/ejabberd
+1 -4
View File
@@ -19,15 +19,12 @@
%%%
%%%----------------------------------------------------------------------
-record(config, {key :: any(), value :: any()}).
-record(local_config, {key :: any(), value :: any()}).
-type config() :: #config{}.
-type local_config() :: #local_config{}.
-record(state,
{opts = [] :: [acl:acl() | config() | local_config()],
{opts = [] :: [acl:acl() | local_config()],
hosts = [] :: [binary()],
override_local = false :: boolean(),
override_global = false :: boolean(),
+5 -5
View File
@@ -41,17 +41,17 @@
-else.
-define(DEBUG(Format, Args),
ejabberd_logger:debug_msg(?MODULE, ?LINE, Format, Args)).
p1_logger:debug_msg(?MODULE, ?LINE, Format, Args)).
-define(INFO_MSG(Format, Args),
ejabberd_logger:info_msg(?MODULE, ?LINE, Format, Args)).
p1_logger:info_msg(?MODULE, ?LINE, Format, Args)).
-define(WARNING_MSG(Format, Args),
ejabberd_logger:warning_msg(?MODULE, ?LINE, Format, Args)).
p1_logger:warning_msg(?MODULE, ?LINE, Format, Args)).
-define(ERROR_MSG(Format, Args),
ejabberd_logger:error_msg(?MODULE, ?LINE, Format, Args)).
p1_logger:error_msg(?MODULE, ?LINE, Format, Args)).
-define(CRITICAL_MSG(Format, Args),
ejabberd_logger:critical_msg(?MODULE, ?LINE, Format, Args)).
p1_logger:critical_msg(?MODULE, ?LINE, Format, Args)).
-endif.
+4
View File
@@ -50,6 +50,10 @@ parse(Version) ->
less_or_equal([[]], [[]]) ->
true;
less_or_equal([[]], _Any) ->
true;
less_or_equal(_Any, [[]]) ->
false;
less_or_equal([[Left| Rl]], [[Right| Rr]]) ->
case {Left < Right, Left == Right} of
{true, _} ->
Vendored
BIN
View File
Binary file not shown.
+7 -4
View File
@@ -30,9 +30,9 @@ Macros = lists:flatmap(
DebugInfo = case lists:keysearch(debug, 1, Cfg) of
{value, {debug, true}} ->
[debug_info];
[];
_ ->
[]
[no_debug_info]
end,
HiPE = case lists:keysearch(hipe, 1, Cfg) of
@@ -52,11 +52,11 @@ SrcDirs = lists:foldl(
Acc
end, [], Cfg),
Deps = [{p1_logger, ".*", {git, "git://github.com/processone/p1_logger"}},
{p1_cache_tab, ".*", {git, "git://github.com/processone/cache_tab"}},
Deps = [{p1_cache_tab, ".*", {git, "git://github.com/processone/cache_tab"}},
{p1_tls, ".*", {git, "git://github.com/processone/tls"}},
{p1_stringprep, ".*", {git, "git://github.com/processone/stringprep"}},
{p1_xml, ".*", {git, "git://github.com/processone/xml"}},
{p1_yaml, ".*", {git, "git://github.com/processone/p1_yaml"}},
{xmlrpc, ".*", {git, "git://github.com/rds13/xmlrpc"}}],
ConfigureCmd = fun(Pkg, Flags) ->
@@ -98,6 +98,8 @@ CfgDeps = lists:flatmap(
{lhttpc, ".*", {git, "git://github.com/esl/lhttpc"}}];
({lager, true}) ->
[{lager, ".*", {git, "git://github.com/basho/lager"}}];
({lager, false}) ->
[{p1_logger, ".*", {git, "git://github.com/processone/p1_logger"}}];
(_) ->
[]
end, Cfg),
@@ -118,6 +120,7 @@ CfgPostHooks = lists:flatmap(
Config = [{erl_opts, Includes ++ Macros ++ HiPE ++ DebugInfo ++
[{src_dirs, [asn1, src | SrcDirs]}]},
{sub_dirs, ["rel"]},
{keep_build_info, true},
{ct_extra_params, "-include "
++ filename:join([Cwd, "tools"]) ++ " "
++ filename:join([Cwd, "deps", "p1_xml", "include"])},
+4 -3
View File
@@ -28,7 +28,7 @@ ConfiguredOTPApps = lists:flatmap(
OTPApps = RequiredOTPApps ++ ConfiguredOTPApps,
DepRequiredApps = [p1_logger, p1_cache_tab, p1_tls, p1_stringprep, p1_xml, xmlrpc],
DepRequiredApps = [p1_cache_tab, p1_tls, p1_stringprep, p1_xml, p1_yaml, xmlrpc],
DepConfiguredApps = lists:flatmap(
fun({mysql, true}) -> [p1_mysql];
@@ -39,7 +39,8 @@ DepConfiguredApps = lists:flatmap(
({json, true}) -> [jiffy];
({iconv, true}) -> [p1_iconv];
({http, true}) -> [ibrowse, lhttpc];
({lager, true}) -> [lager];
({lager, true}) -> [lager, goldrush];
({lager, false}) -> [p1_logger];
(_) -> []
end, Vars),
@@ -88,7 +89,7 @@ Overlay = [
{template, "files/erl", "\{\{erts_vsn\}\}/bin/erl"},
{template, "../ejabberdctl.template", "bin/ejabberdctl"},
{copy, "../ejabberdctl.cfg.example", "etc/ejabberd/ejabberdctl.cfg"},
{copy, "../ejabberd.cfg.example", "etc/ejabberd/ejabberd.cfg"},
{copy, "../ejabberd.yml.example", "etc/ejabberd/ejabberd.yml"},
{copy, "../inetrc", "etc/ejabberd/inetrc"},
{copy, "files/install_upgrade.escript", "bin/install_upgrade.escript"}
],
+345 -154
View File
@@ -29,34 +29,38 @@
-author('alexey@process-one.net').
-export([start/0, to_record/3, add/3, add_list/3,
match_rule/3, match_acl/3]).
add_local/3, add_list_local/3, load_from_config/0,
match_rule/3, match_acl/3, transform_options/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
-include("jlib.hrl").
-record(acl, {aclname, aclspec}).
-record(access, {name :: access_name(),
rules = [] :: [access_rule()]}).
-type regexp() :: binary().
-type glob() :: binary().
-type access_name() :: atom().
-type access_rule() :: {atom(), any()}.
-type host() :: binary().
-type aclname() :: {atom(), binary() | global}.
-type aclspec() :: all | none |
{user, binary()} |
{user, binary(), binary()} |
{user, {binary(), host()} | binary()} |
{server, binary()} |
{resource, binary()} |
{user_regexp, regexp()} |
{shared_group, binary()} |
{shared_group, binary(), binary()} |
{user_regexp, regexp(), binary()} |
{user_regexp, {regexp(), host()} | regexp()} |
{shared_group, {binary(), host()} | binary()} |
{user_regexp, {regexp(), host()} | regexp()} |
{server_regexp, regexp()} |
{resource_regexp, regexp()} |
{node_regexp, regexp(), regexp()} |
{user_glob, glob()} |
{user_glob, glob(), binary()} |
{node_regexp, {regexp(), regexp()}} |
{user_glob, {glob(), host()} | glob()} |
{server_glob, glob()} |
{resource_glob, glob()} |
{node_glob, glob(), glob()}.
{ip, {inet:ip_address(), integer()}} |
{node_glob, {glob(), glob()}}.
-type acl() :: #acl{aclname :: aclname(),
aclspec :: aclspec()}.
@@ -64,11 +68,23 @@
-export_type([acl/0]).
start() ->
case catch mnesia:table_info(acl, storage_type) of
disc_copies ->
mnesia:delete_table(acl);
_ ->
ok
end,
mnesia:create_table(acl,
[{disc_copies, [node()]}, {type, bag},
[{ram_copies, [node()]}, {type, bag},
{local_content, true},
{attributes, record_info(fields, acl)}]),
mnesia:create_table(access,
[{ram_copies, [node()]},
{local_content, true},
{attributes, record_info(fields, access)}]),
mnesia:add_table_copy(acl, node(), ram_copies),
update_table(),
mnesia:add_table_copy(access, node(), ram_copies),
load_from_config(),
ok.
-spec to_record(binary(), atom(), aclspec()) -> acl().
@@ -77,18 +93,49 @@ to_record(Host, ACLName, ACLSpec) ->
#acl{aclname = {ACLName, Host},
aclspec = normalize_spec(ACLSpec)}.
-spec add(binary(), aclname(), aclspec()) -> {atomic, ok} | {aborted, any()}.
-spec add(binary(), aclname(), aclspec()) -> ok | {error, any()}.
add(Host, ACLName, ACLSpec) ->
{ResL, BadNodes} = rpc:multicall(mnesia:system_info(running_db_nodes),
?MODULE, add_local,
[Host, ACLName, ACLSpec]),
case lists:keyfind(aborted, 1, ResL) of
false when BadNodes == [] ->
ok;
false ->
{error, {failed_nodes, BadNodes}};
Err ->
{error, Err}
end.
add_local(Host, ACLName, ACLSpec) ->
F = fun () ->
mnesia:write(#acl{aclname = {ACLName, Host},
aclspec = normalize_spec(ACLSpec)})
end,
mnesia:transaction(F).
case mnesia:transaction(F) of
{atomic, ok} ->
ok;
Err ->
Err
end.
-spec add_list(binary(), [acl()], boolean()) -> false | ok.
-spec add_list(binary(), [acl()], boolean()) -> ok | {error, any()}.
add_list(Host, ACLs, Clear) ->
{ResL, BadNodes} = rpc:multicall(mnesia:system_info(running_db_nodes),
?MODULE, add_list_local,
[Host, ACLs, Clear]),
case lists:keyfind(aborted, 1, ResL) of
false when BadNodes == [] ->
ok;
false ->
{error, {failed_nodes, BadNodes}};
Err ->
{error, Err}
end.
add_list_local(Host, ACLs, Clear) ->
F = fun () ->
if Clear ->
Ks = mnesia:select(acl,
@@ -112,135 +159,198 @@ add_list(Host, ACLs, Clear) ->
end,
ACLs)
end,
case mnesia:transaction(F) of
{atomic, _} -> ok;
_ -> false
mnesia:transaction(F).
-spec add_access(binary() | global,
access_name(), [access_rule()]) -> ok | {error, any()}.
add_access(Host, Access, Rules) ->
case mnesia:transaction(
fun() ->
mnesia:write(
#access{name = {Access, Host},
rules = Rules})
end) of
{atomic, ok} ->
ok;
Err ->
{error, Err}
end.
normalize(A) -> jlib:nodeprep(iolist_to_binary(A)).
-spec load_from_config() -> ok.
normalize_spec({A, B}) -> {A, normalize(B)};
normalize_spec({A, B, C}) ->
{A, normalize(B), normalize(C)};
normalize_spec(all) -> all;
normalize_spec(none) -> none.
load_from_config() ->
Hosts = [global|?MYHOSTS],
lists:foreach(
fun(Host) ->
ACLs = ejabberd_config:get_option(
{acl, Host}, fun(V) -> V end, []),
AccessRules = ejabberd_config:get_option(
{access, Host}, fun(V) -> V end, []),
lists:foreach(
fun({ACLName, SpecList}) ->
lists:foreach(
fun({ACLType, ACLSpecs}) when is_list(ACLSpecs) ->
lists:foreach(
fun(ACLSpec) ->
add(Host, ACLName,
{ACLType, ACLSpec})
end, lists:flatten(ACLSpecs));
({ACLType, ACLSpecs}) ->
add(Host, ACLName, {ACLType, ACLSpecs})
end, lists:flatten(SpecList))
end, ACLs),
lists:foreach(
fun({Access, Rules}) ->
add_access(Host, Access, Rules)
end, AccessRules)
end, Hosts).
-spec match_rule(global | binary(), atom(), jid() | ljid()) -> any().
b(S) ->
iolist_to_binary(S).
match_rule(global, Rule, JID) ->
case Rule of
all -> allow;
none -> deny;
_ ->
case ejabberd_config:get_global_option(
{access, Rule, global}, fun(V) -> V end)
of
undefined -> deny;
GACLs -> match_acls(GACLs, JID, global)
end
end;
match_rule(Host, Rule, JID) ->
case Rule of
all -> allow;
none -> deny;
_ ->
case ejabberd_config:get_global_option(
{access, Rule, global}, fun(V) -> V end)
of
undefined ->
case ejabberd_config:get_global_option(
{access, Rule, Host}, fun(V) -> V end)
of
undefined -> deny;
ACLs -> match_acls(ACLs, JID, Host)
end;
GACLs ->
case ejabberd_config:get_global_option(
{access, Rule, Host}, fun(V) -> V end)
of
undefined -> match_acls(GACLs, JID, Host);
ACLs ->
case lists:reverse(GACLs) of
[{allow, all} | Rest] ->
match_acls(lists:reverse(Rest) ++
ACLs ++ [{allow, all}],
JID, Host);
_ -> match_acls(GACLs ++ ACLs, JID, Host)
end
end
end
nodeprep(S) ->
jlib:nodeprep(b(S)).
nameprep(S) ->
jlib:nameprep(b(S)).
resourceprep(S) ->
jlib:resourceprep(b(S)).
normalize_spec(Spec) ->
case Spec of
all -> all;
none -> none;
{user, {U, S}} -> {user, {nodeprep(U), nameprep(S)}};
{user, U} -> {user, nodeprep(U)};
{shared_group, {G, H}} -> {shared_group, {b(G), nameprep(H)}};
{shared_group, G} -> {shared_group, b(G)};
{user_regexp, {UR, S}} -> {user_regexp, {b(UR), nameprep(S)}};
{user_regexp, UR} -> {user_regexp, b(UR)};
{node_regexp, {UR, SR}} -> {node_regexp, {b(UR), b(SR)}};
{user_glob, {UR, S}} -> {user_glob, {b(UR), nameprep(S)}};
{user_glob, UR} -> {user_glob, b(UR)};
{node_glob, {UR, SR}} -> {node_glob, {b(UR), b(SR)}};
{server, S} -> {server, nameprep(S)};
{resource, R} -> {resource, resourceprep(R)};
{server_regexp, SR} -> {server_regexp, b(SR)};
{server_glob, S} -> {server_glob, b(S)};
{resource_glob, R} -> {resource_glob, b(R)};
{ip, S} ->
case parse_ip_netmask(b(S)) of
{ok, Net, Mask} ->
{ip, {Net, Mask}};
error ->
?INFO_MSG("Invalid network address: ~p", [S]),
none
end
end.
-spec match_rule(global | binary(), access_name(),
jid() | ljid() | inet:ip_address()) -> any().
match_rule(_Host, all, _JID) ->
allow;
match_rule(_Host, none, _JID) ->
deny;
match_rule(Host, Access, JID) ->
GAccess = ets:lookup(access, {Access, global}),
LAccess = if Host /= global ->
ets:lookup(access, {Access, Host});
true ->
[]
end,
case GAccess ++ LAccess of
[] ->
?WARNING_MSG("Attempt to match against unspecified "
"access rule '~s' (scope: ~s)",
[Access, Host]),
deny;
AccessList ->
Rules = lists:flatmap(
fun(#access{rules = Rs}) ->
Rs
end, AccessList),
match_acls(Rules, JID, Host)
end.
match_acls([], _, _Host) -> deny;
match_acls([{Access, ACL} | ACLs], JID, Host) ->
match_acls([{ACL, Access} | ACLs], JID, Host) ->
case match_acl(ACL, JID, Host) of
true -> Access;
_ -> match_acls(ACLs, JID, Host)
end.
-spec match_acl(atom(), jid() | ljid(), binary()) -> boolean().
-spec match_acl(atom(),
jid() | ljid() | inet:ip_address(),
binary()) -> boolean().
match_acl(all, _JID, _Host) ->
true;
match_acl(none, _JID, _Host) ->
false;
match_acl(ACL, IP, Host) when tuple_size(IP) == 4;
tuple_size(IP) == 8 ->
lists:any(
fun(#acl{aclspec = {ip, {Net, Mask}}}) ->
is_ip_match(IP, Net, Mask);
(_) ->
false
end,
ets:lookup(acl, {ACL, Host}) ++
ets:lookup(acl, {ACL, global}));
match_acl(ACL, JID, Host) ->
case ACL of
all -> true;
none -> false;
_ ->
{User, Server, Resource} = jlib:jid_tolower(JID),
lists:any(fun (#acl{aclspec = Spec}) ->
case Spec of
all -> true;
{user, U} ->
U == User andalso
(Host == Server orelse
Host == global andalso
lists:member(Server, ?MYHOSTS));
{user, U, S} -> U == User andalso S == Server;
{server, S} -> S == Server;
{resource, R} -> R == Resource;
{user_regexp, UR} ->
(Host == Server orelse
Host == global andalso
lists:member(Server, ?MYHOSTS))
andalso is_regexp_match(User, UR);
{shared_group, G} ->
Mod = loaded_shared_roster_module(Host),
Mod:is_user_in_group({User, Server}, G, Host);
{shared_group, G, H} ->
Mod = loaded_shared_roster_module(H),
Mod:is_user_in_group({User, Server}, G, H);
{user_regexp, UR, S} ->
S == Server andalso is_regexp_match(User, UR);
{server_regexp, SR} ->
is_regexp_match(Server, SR);
{resource_regexp, RR} ->
is_regexp_match(Resource, RR);
{node_regexp, UR, SR} ->
is_regexp_match(Server, SR) andalso
is_regexp_match(User, UR);
{user_glob, UR} ->
(Host == Server orelse
Host == global andalso
lists:member(Server, ?MYHOSTS))
andalso is_glob_match(User, UR);
{user_glob, UR, S} ->
S == Server andalso is_glob_match(User, UR);
{server_glob, SR} -> is_glob_match(Server, SR);
{resource_glob, RR} ->
is_glob_match(Resource, RR);
{node_glob, UR, SR} ->
is_glob_match(Server, SR) andalso
is_glob_match(User, UR);
WrongSpec ->
?ERROR_MSG("Wrong ACL expression: ~p~nCheck your "
"config file and reload it with the override_a"
"cls option enabled",
[WrongSpec]),
false
end
end,
ets:lookup(acl, {ACL, global}) ++
ets:lookup(acl, {ACL, Host}))
end.
{User, Server, Resource} = jlib:jid_tolower(JID),
lists:any(
fun(#acl{aclspec = Spec}) ->
case Spec of
all -> true;
{user, {U, S}} -> U == User andalso S == Server;
{user, U} ->
U == User andalso
lists:member(Server, ?MYHOSTS);
{server, S} -> S == Server;
{resource, R} -> R == Resource;
{shared_group, {G, H}} ->
Mod = loaded_shared_roster_module(H),
Mod:is_user_in_group({User, Server}, G, H);
{shared_group, G} ->
Mod = loaded_shared_roster_module(Host),
Mod:is_user_in_group({User, Server}, G, Host);
{user_regexp, {UR, S}} ->
S == Server andalso is_regexp_match(User, UR);
{user_regexp, UR} ->
lists:member(Server, ?MYHOSTS)
andalso is_regexp_match(User, UR);
{server_regexp, SR} ->
is_regexp_match(Server, SR);
{resource_regexp, RR} ->
is_regexp_match(Resource, RR);
{node_regexp, {UR, SR}} ->
is_regexp_match(Server, SR) andalso
is_regexp_match(User, UR);
{user_glob, {UR, S}} ->
S == Server andalso is_glob_match(User, UR);
{user_glob, UR} ->
lists:member(Server, ?MYHOSTS)
andalso is_glob_match(User, UR);
{server_glob, SR} -> is_glob_match(Server, SR);
{resource_glob, RR} ->
is_glob_match(Resource, RR);
{node_glob, {UR, SR}} ->
is_glob_match(Server, SR) andalso
is_glob_match(User, UR);
WrongSpec ->
?ERROR_MSG("Wrong ACL expression: ~p~nCheck your "
"config file and reload it with the override_a"
"cls option enabled",
[WrongSpec]),
false
end
end,
ets:lookup(acl, {ACL, Host}) ++
ets:lookup(acl, {ACL, global})).
is_regexp_match(String, RegExp) ->
case ejabberd_regexp:run(String, RegExp) of
@@ -256,34 +366,115 @@ is_glob_match(String, Glob) ->
is_regexp_match(String,
ejabberd_regexp:sh_to_awk(Glob)).
is_ip_match({_, _, _, _} = IP, {_, _, _, _} = Net, Mask) ->
IPInt = ip_to_integer(IP),
NetInt = ip_to_integer(Net),
M = bnot (1 bsl (32 - Mask) - 1),
IPInt band M =:= NetInt band M;
is_ip_match({_, _, _, _, _, _, _, _} = IP,
{_, _, _, _, _, _, _, _} = Net, Mask) ->
IPInt = ip_to_integer(IP),
NetInt = ip_to_integer(Net),
M = bnot (1 bsl (128 - Mask) - 1),
IPInt band M =:= NetInt band M;
is_ip_match(_, _, _) ->
false.
ip_to_integer({IP1, IP2, IP3, IP4}) ->
IP1 bsl 8 bor IP2 bsl 8 bor IP3 bsl 8 bor IP4;
ip_to_integer({IP1, IP2, IP3, IP4, IP5, IP6, IP7,
IP8}) ->
IP1 bsl 16 bor IP2 bsl 16 bor IP3 bsl 16 bor IP4 bsl 16
bor IP5
bsl 16
bor IP6
bsl 16
bor IP7
bsl 16
bor IP8.
loaded_shared_roster_module(Host) ->
case gen_mod:is_loaded(Host, mod_shared_roster_ldap) of
true -> mod_shared_roster_ldap;
false -> mod_shared_roster
end.
update_table() ->
Fields = record_info(fields, acl),
case mnesia:table_info(acl, attributes) of
Fields ->
ejabberd_config:convert_table_to_binary(
acl, Fields, bag,
fun(#acl{aclspec = Spec}) when is_tuple(Spec) ->
element(2, Spec);
(_) ->
'$next'
end,
fun(#acl{aclname = {ACLName, Host},
aclspec = Spec} = R) ->
NewHost = if Host == global ->
Host;
true ->
iolist_to_binary(Host)
end,
R#acl{aclname = {ACLName, NewHost},
aclspec = normalize_spec(Spec)}
end);
_ ->
?INFO_MSG("Recreating acl table", []),
mnesia:transform_table(acl, ignore, Fields)
parse_ip_netmask(S) ->
case str:tokens(S, <<"/">>) of
[IPStr] ->
case inet_parse:address(binary_to_list(IPStr)) of
{ok, {_, _, _, _} = IP} -> {ok, IP, 32};
{ok, {_, _, _, _, _, _, _, _} = IP} -> {ok, IP, 128};
_ -> error
end;
[IPStr, MaskStr] ->
case catch jlib:binary_to_integer(MaskStr) of
Mask when is_integer(Mask), Mask >= 0 ->
case inet_parse:address(binary_to_list(IPStr)) of
{ok, {_, _, _, _} = IP} when Mask =< 32 ->
{ok, IP, Mask};
{ok, {_, _, _, _, _, _, _, _} = IP} when Mask =< 128 ->
{ok, IP, Mask};
_ -> error
end;
_ -> error
end;
_ -> error
end.
transform_options(Opts) ->
Opts1 = lists:foldl(fun transform_options/2, [], Opts),
{ACLOpts, Opts2} = lists:mapfoldl(
fun({acl, Os}, Acc) ->
{Os, Acc};
(O, Acc) ->
{[], [O|Acc]}
end, [], Opts1),
{AccessOpts, Opts3} = lists:mapfoldl(
fun({access, Os}, Acc) ->
{Os, Acc};
(O, Acc) ->
{[], [O|Acc]}
end, [], Opts2),
ACLOpts1 = ejabberd_config:collect_options(lists:flatten(ACLOpts)),
AccessOpts1 = case ejabberd_config:collect_options(
lists:flatten(AccessOpts)) of
[] -> [];
L1 -> [{access, L1}]
end,
ACLOpts2 = case lists:map(
fun({ACLName, Os}) ->
{ACLName, ejabberd_config:collect_options(Os)}
end, ACLOpts1) of
[] -> [];
L2 -> [{acl, L2}]
end,
ACLOpts2 ++ AccessOpts1 ++ Opts3.
transform_options({acl, Name, Type}, Opts) ->
T = case Type of
all -> all;
none -> none;
{user, U} -> {user, [U]};
{user, U, S} -> {user, [[{U, S}]]};
{shared_group, G} -> {shared_group, [G]};
{shared_group, G, H} -> {shared_group, [[{G, H}]]};
{user_regexp, UR} -> {user_regexp, [UR]};
{user_regexp, UR, S} -> {user_regexp, [[{UR, S}]]};
{node_regexp, UR, SR} -> {node_regexp, [[{UR, SR}]]};
{user_glob, UR} -> {user_glob, [UR]};
{user_glob, UR, S} -> {user_glob, [[{UR, S}]]};
{node_glob, UR, SR} -> {node_glob, [[{UR, SR}]]};
{server, S} -> {server, [S]};
{resource, R} -> {resource, [R]};
{server_regexp, SR} -> {server_regexp, [SR]};
{server_glob, S} -> {server_glob, [S]};
{ip, S} -> {ip, [S]};
{resource_glob, R} -> {resource_glob, [R]}
end,
[{acl, [{Name, [T]}]}|Opts];
transform_options({access, Name, Rules}, Opts) ->
NewRules = [{ACL, Action} || {Action, ACL} <- Rules],
[{access, [{Name, NewRules}]}|Opts];
transform_options(Opt, Opts) ->
[Opt|Opts].
+2 -2
View File
@@ -204,11 +204,11 @@ get_local_fqdn() ->
Str when is_binary(Str) -> Str;
_ ->
<<"unknown-fqdn, please configure fqdn "
"option in ejabberd.cfg!">>
"option in ejabberd.yml!">>
end.
get_local_fqdn2() ->
case ejabberd_config:get_local_option(
case ejabberd_config:get_option(
fqdn, fun iolist_to_binary/1) of
ConfiguredFqdn when is_binary(ConfiguredFqdn) ->
ConfiguredFqdn;
+81 -15
View File
@@ -27,8 +27,8 @@
-module(ejabberd).
-author('alexey@process-one.net').
-export([start/0, stop/0, start_app/1,
get_pid_file/0]).
-export([start/0, stop/0, start_app/1, start_app/2,
get_pid_file/0, check_app/1]).
-include("logger.hrl").
@@ -51,27 +51,93 @@ get_pid_file() ->
Path
end.
start_app(App) when not is_list(App) ->
start_app([App]);
start_app([App|Apps]) ->
start_app(App) ->
start_app(App, temporary).
start_app(App, Type) ->
StartFlag = not is_loaded(),
start_app(App, Type, StartFlag).
check_app(App) ->
StartFlag = not is_loaded(),
spawn(fun() -> check_app_modules(App, StartFlag) end),
ok.
is_loaded() ->
Apps = application:which_applications(),
lists:keymember(ejabberd, 1, Apps).
start_app(App, Type, StartFlag) when not is_list(App) ->
start_app([App], Type, StartFlag);
start_app([App|Apps], Type, StartFlag) ->
case application:start(App) of
ok ->
start_app(Apps);
spawn(fun() -> check_app_modules(App, StartFlag) end),
start_app(Apps, Type, StartFlag);
{error, {already_started, _}} ->
start_app(Apps);
start_app(Apps, Type, StartFlag);
{error, {not_started, DepApp}} ->
case lists:member(DepApp, [App|Apps]) of
true ->
?CRITICAL_MSG("failed to start application '~p': "
"circular dependency on '~p' detected",
[App, DepApp]),
erlang:error(application_start_failed);
Reason = io_lib:format(
"failed to start application '~p': "
"circular dependency on '~p' detected",
[App, DepApp]),
exit_or_halt(Reason, StartFlag);
false ->
start_app([DepApp,App|Apps])
start_app([DepApp,App|Apps], Type, StartFlag)
end;
Err ->
?CRITICAL_MSG("failed to start application '~p': ~p", [App, Err]),
erlang:error(application_start_failed)
Reason = io_lib:format("failed to start application '~p': ~p",
[App, Err]),
exit_or_halt(Reason, StartFlag)
end;
start_app([]) ->
start_app([], _Type, _StartFlag) ->
ok.
check_app_modules(App, StartFlag) ->
{A, B, C} = now(),
random:seed(A, B, C),
sleep(5000),
case application:get_key(App, modules) of
{ok, Mods} ->
lists:foreach(
fun(Mod) ->
case code:which(Mod) of
non_existing ->
File = get_module_file(App, Mod),
Reason = io_lib:format(
"couldn't find module ~s "
"needed for application '~p'",
[File, App]),
exit_or_halt(Reason, StartFlag);
_ ->
sleep(10)
end
end, Mods);
_ ->
%% No modules? This is strange
ok
end.
exit_or_halt(Reason, StartFlag) ->
?CRITICAL_MSG(Reason, []),
if StartFlag ->
%% Wait for the critical message is written in the console/log
timer:sleep(1000),
halt(string:substr(lists:flatten(Reason), 1, 199));
true ->
erlang:error(application_start_failed)
end.
sleep(N) ->
timer:sleep(random:uniform(N)).
get_module_file(App, Mod) ->
BaseName = atom_to_list(Mod),
case code:lib_dir(App, ebin) of
{error, _} ->
BaseName;
Dir ->
filename:join([Dir, BaseName ++ ".beam"])
end.
+6
View File
@@ -164,6 +164,12 @@ commands() ->
module = ejd2odbc, function = export,
args = [{host, string}, {file, string}], result = {res, rescode}},
#ejabberd_commands{name = convert_to_yaml, tags = [config],
desc = "Convert the input file from Erlang to YAML format",
module = ejabberd_config, function = convert_to_yaml,
args = [{in, string}, {out, string}],
result = {res, rescode}},
#ejabberd_commands{name = delete_expired_messages, tags = [purge],
desc = "Delete expired offline messages from database",
module = ?MODULE, function = delete_expired_messages,
+17 -46
View File
@@ -29,7 +29,7 @@
-behaviour(application).
-export([start_modules/0,start/2, get_log_path/0, prep_stop/1, stop/1, init/0]).
-export([start_modules/0,start/2, prep_stop/1, stop/1, init/0]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -39,21 +39,22 @@
%%%
start(normal, _Args) ->
maybe_start_lager(),
ejabberd_logger:set(4),
ejabberd_logger:start(),
write_pid_file(),
start_apps(),
ejabberd:check_app(ejabberd),
randoms:start(),
db_init(),
start(),
translate:start(),
acl:start(),
ejabberd_ctl:init(),
ejabberd_commands:init(),
ejabberd_admin:start(),
gen_mod:start(),
ejabberd_config:start(),
ejabberd_check:config(),
set_loglevel_from_config(),
acl:start(),
shaper:start(),
connect_nodes(),
Sup = ejabberd_sup:start_link(),
ejabberd_rdbms:start(),
@@ -98,10 +99,6 @@ start() ->
init() ->
register(ejabberd, self()),
%erlang:system_flag(fullsweep_after, 0),
%error_logger:logfile({open, ?LOG_PATH}),
LogPath = get_log_path(),
ejabberd_logger:set_logfile(LogPath),
loop().
loop() ->
@@ -117,14 +114,14 @@ db_init() ->
_ ->
ok
end,
application:start(mnesia, permanent),
ejabberd:start_app(mnesia, permanent),
mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity).
%% Start all the modules in all the hosts
start_modules() ->
lists:foreach(
fun(Host) ->
Modules = ejabberd_config:get_local_option(
Modules = ejabberd_config:get_option(
{modules, Host},
fun(Mods) ->
lists:map(
@@ -142,7 +139,7 @@ start_modules() ->
stop_modules() ->
lists:foreach(
fun(Host) ->
Modules = ejabberd_config:get_local_option(
Modules = ejabberd_config:get_option(
{modules, Host},
fun(Mods) ->
lists:map(
@@ -157,7 +154,7 @@ stop_modules() ->
end, ?MYHOSTS).
connect_nodes() ->
Nodes = ejabberd_config:get_local_option(
Nodes = ejabberd_config:get_option(
cluster_nodes,
fun(Ns) ->
true = lists:all(fun is_atom/1, Ns),
@@ -167,26 +164,6 @@ connect_nodes() ->
net_kernel:connect_node(Node)
end, Nodes).
%% @spec () -> string()
%% @doc Returns the full path to the ejabberd log file.
%% It first checks for application configuration parameter 'log_path'.
%% If not defined it checks the environment variable EJABBERD_LOG_PATH.
%% And if that one is neither defined, returns the default value:
%% "ejabberd.log" in current directory.
get_log_path() ->
case application:get_env(log_path) of
{ok, Path} ->
Path;
undefined ->
case os:getenv("EJABBERD_LOG_PATH") of
false ->
?LOG_PATH;
Path ->
Path
end
end.
%% If ejabberd is running on some Windows machine, get nameservers and add to Erlang
maybe_add_nameservers() ->
case os:type() of
@@ -237,23 +214,17 @@ delete_pid_file() ->
file:delete(PidFilename)
end.
-ifdef(LAGER).
maybe_start_lager() ->
lager:start().
-else.
maybe_start_lager() ->
ok.
-endif.
set_loglevel_from_config() ->
Level = ejabberd_config:get_option(
loglevel,
fun(P) when P>=0, P=<5 -> P end,
4),
ejabberd_logger:set(Level).
start_apps() ->
ejabberd:start_app(sasl),
ejabberd:start_app(ssl),
ejabberd:start_app(p1_yaml),
ejabberd:start_app(p1_tls),
ejabberd:start_app(p1_xml),
ejabberd:start_app(p1_stringprep),
+11 -3
View File
@@ -35,8 +35,8 @@
check_password/5, check_password_with_authmodule/3,
check_password_with_authmodule/5, try_register/3,
dirty_get_registered_users/0, get_vh_registered_users/1,
get_vh_registered_users/2, export/1,
get_vh_registered_users_number/1,
get_vh_registered_users/2, export/1, import/1,
get_vh_registered_users_number/1, import/3,
get_vh_registered_users_number/2, get_password/2,
get_password_s/2, get_password_with_authmodule/2,
is_user_exists/2, is_user_exists_in_other_modules/3,
@@ -423,7 +423,7 @@ auth_modules() ->
%% Return the list of authenticated modules for a given host
auth_modules(Server) ->
LServer = jlib:nameprep(Server),
Methods = ejabberd_config:get_local_option(
Methods = ejabberd_config:get_option(
{auth_method, LServer},
fun(V) when is_list(V) ->
true = lists:all(fun is_atom/1, V),
@@ -437,3 +437,11 @@ auth_modules(Server) ->
export(Server) ->
ejabberd_auth_internal:export(Server).
import(Server) ->
ejabberd_auth_internal:import(Server).
import(Server, mnesia, Passwd) ->
ejabberd_auth_internal:import(Server, mnesia, Passwd);
import(_, _, _) ->
pass.
+2 -2
View File
@@ -104,7 +104,7 @@ is_login_anonymous_enabled(Host) ->
%% Return the anonymous protocol to use: sasl_anon|login_anon|both
%% defaults to login_anon
anonymous_protocol(Host) ->
ejabberd_config:get_local_option(
ejabberd_config:get_option(
{anonymous_protocol, Host},
fun(sasl_anon) -> sasl_anon;
(login_anon) -> login_anon;
@@ -115,7 +115,7 @@ anonymous_protocol(Host) ->
%% Return true if multiple connections have been allowed in the config file
%% defaults to false
allow_multiple_connections(Host) ->
ejabberd_config:get_local_option(
ejabberd_config:get_option(
{allow_multiple_connections, Host},
fun(V) when is_boolean(V) -> V end,
false).
+2 -2
View File
@@ -48,7 +48,7 @@
%%% API
%%%----------------------------------------------------------------------
start(Host) ->
Cmd = ejabberd_config:get_local_option(
Cmd = ejabberd_config:get_option(
{extauth_program, Host},
fun(V) ->
binary_to_list(iolist_to_binary(V))
@@ -171,7 +171,7 @@ remove_user(User, Server, Password) ->
%% @spec (Host::string()) -> false | {true, CacheTime::integer()}
get_cache_option(Host) ->
case ejabberd_config:get_local_option(
case ejabberd_config:get_option(
{extauth_cache, Host},
fun(I) when is_integer(I), I > 0 -> I end) of
undefined -> false;
+14 -3
View File
@@ -38,8 +38,8 @@
get_vh_registered_users_number/1,
get_vh_registered_users_number/2, get_password/2,
get_password_s/2, is_user_exists/2, remove_user/2,
remove_user/3, store_type/0, export/1,
plain_password_required/0]).
remove_user/3, store_type/0, export/1, import/1,
import/3, plain_password_required/0]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -406,7 +406,7 @@ is_scrammed() ->
is_option_scram() ->
scram ==
ejabberd_config:get_local_option({auth_password_format, ?MYNAME},
ejabberd_config:get_option({auth_password_format, ?MYNAME},
fun(V) -> V end).
maybe_alert_password_scrammed_without_option() ->
@@ -474,3 +474,14 @@ export(_Server) ->
(_Host, _R) ->
[]
end}].
import(LServer) ->
[{<<"select username, password from users;">>,
fun([LUser, Password]) ->
#passwd{us = {LUser, LServer}, password = Password}
end}].
import(_LServer, mnesia, #passwd{} = P) ->
mnesia:dirty_write(P);
import(_, _, _) ->
pass.
+3 -1
View File
@@ -369,8 +369,10 @@ parse_options(Host) ->
{iolist_to_binary(U),
iolist_to_binary(P)};
({U}) ->
{iolist_to_binary(U)};
(U) ->
{iolist_to_binary(U)}
end, Us)
end, lists:flatten(Us))
end, [{<<"uid">>, <<"%u">>}]),
UIDs = eldap_utils:uids_domain_subst(Host, UIDsTemp),
SubFilter = eldap_utils:generate_subfilter(UIDs),
+2 -2
View File
@@ -107,13 +107,13 @@ store_type() -> external.
%% Internal functions
%%====================================================================
get_pam_service(Host) ->
ejabberd_config:get_local_option(
ejabberd_config:get_option(
{pam_service, Host},
fun iolist_to_binary/1,
<<"ejabberd">>).
get_pam_userinfotype(Host) ->
ejabberd_config:get_local_option(
ejabberd_config:get_option(
{pam_userinfotype, Host},
fun(username) -> username;
(jid) -> jid
+17 -10
View File
@@ -47,7 +47,8 @@
del_aux_field/2,
get_subscription/2,
broadcast/4,
get_subscribed/1]).
get_subscribed/1,
transform_listen_option/2]).
%% gen_fsm callbacks
-export([init/1,
@@ -233,18 +234,21 @@ init([{SockMod, Socket}, Opts]) ->
{value, {_, XS}} -> XS;
_ -> false
end,
Zlib = lists:member(zlib, Opts),
StartTLS = lists:member(starttls, Opts),
StartTLSRequired = lists:member(starttls_required,
Opts),
TLSEnabled = lists:member(tls, Opts),
Zlib = proplists:get_bool(zlib, Opts),
StartTLS = proplists:get_bool(starttls, Opts),
StartTLSRequired = proplists:get_bool(starttls_required, Opts),
TLSEnabled = proplists:get_bool(tls, Opts),
TLS = StartTLS orelse
StartTLSRequired orelse TLSEnabled,
TLSOpts1 = lists:filter(fun ({certfile, _}) -> true;
(_) -> false
end,
Opts),
TLSOpts = [verify_none | TLSOpts1],
TLSOpts2 = case proplists:get_bool(tls_compression, Opts) of
false -> [compression_none | TLSOpts1];
true -> TLSOpts1
end,
TLSOpts = [verify_none | TLSOpts2],
IP = peerip(SockMod, Socket),
%% Check if IP is blacklisted:
case is_ip_blacklisted(IP) of
@@ -678,7 +682,7 @@ wait_for_feature_request({xmlstreamelement, El},
when TLS == true, TLSEnabled == false,
SockMod == gen_tcp ->
TLSOpts = case
ejabberd_config:get_local_option(
ejabberd_config:get_option(
{domain_certfile, StateData#state.server},
fun iolist_to_binary/1)
of
@@ -872,7 +876,7 @@ resource_conflict_action(U, S, R) ->
R)
of
true ->
ejabberd_config:get_local_option(
ejabberd_config:get_option(
{resource_conflict, S},
fun(setresource) -> setresource;
(closeold) -> closeold;
@@ -2275,7 +2279,7 @@ fsm_limit_opts(Opts) ->
case lists:keysearch(max_fsm_queue, 1, Opts) of
{value, {_, N}} when is_integer(N) -> [{max_queue, N}];
_ ->
case ejabberd_config:get_local_option(
case ejabberd_config:get_option(
max_fsm_queue,
fun(I) when is_integer(I), I > 0 -> I end) of
undefined -> [];
@@ -2373,3 +2377,6 @@ pack_string(String, Pack) ->
{value, PackedString} -> {PackedString, Pack};
none -> {String, gb_trees:insert(String, String, Pack)}
end.
transform_listen_option(Opt, Opts) ->
[Opt|Opts].
+1 -1
View File
@@ -34,7 +34,7 @@
%% Get first c2s configuration limitations to apply it to other c2s
%% connectors.
get_c2s_limits() ->
case ejabberd_config:get_local_option(listen, fun(V) -> V end) of
case ejabberd_config:get_option(listen, fun(V) -> V end) of
undefined -> [];
C2SFirstListen ->
case lists:keysearch(ejabberd_c2s, 2, C2SFirstListen) of
+4 -4
View File
@@ -504,7 +504,7 @@ do_create_image(Key) ->
end.
get_prog_name() ->
case ejabberd_config:get_local_option(
case ejabberd_config:get_option(
captcha_cmd,
fun(FileName) ->
F = iolist_to_binary(FileName),
@@ -521,7 +521,7 @@ get_prog_name() ->
end.
get_url(Str) ->
CaptchaHost = ejabberd_config:get_local_option(
CaptchaHost = ejabberd_config:get_option(
captcha_host,
fun iolist_to_binary/1,
<<"">>),
@@ -549,7 +549,7 @@ get_transfer_protocol(PortString) ->
get_captcha_transfer_protocol(PortListeners).
get_port_listeners(PortNumber) ->
AllListeners = ejabberd_config:get_local_option(listen, fun(V) -> V end),
AllListeners = ejabberd_config:get_option(listen, fun(V) -> V end),
lists:filter(fun ({{Port, _Ip, _Netp}, _Module1,
_Opts1})
when Port == PortNumber ->
@@ -579,7 +579,7 @@ get_captcha_transfer_protocol([_ | Listeners]) ->
is_limited(undefined) -> false;
is_limited(Limiter) ->
case ejabberd_config:get_local_option(
case ejabberd_config:get_option(
captcha_limit,
fun(I) when is_integer(I), I > 0 -> I end) of
undefined -> false;
-111
View File
@@ -1,111 +0,0 @@
%%%----------------------------------------------------------------------
%%% File : ejabberd_check.erl
%%% Author : Mickael Remond <mremond@process-one.net>
%%% Purpose : Check ejabberd configuration and
%%% Created : 27 Feb 2008 by Mickael Remond <mremond@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2013 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_check).
-export([libs/0, config/0]).
-include("ejabberd.hrl").
-include("logger.hrl").
-include("ejabberd_config.hrl").
%% TODO:
%% We want to implement library checking at launch time to issue
%% human readable user messages.
libs() ->
ok.
%% @doc Consistency check on ejabberd configuration
config() ->
check_database_modules().
check_database_modules() ->
[check_database_module(M)||M<-get_db_used()].
check_database_module(odbc) ->
check_modules(odbc, [odbc, odbc_app, odbc_sup, ejabberd_odbc, ejabberd_odbc_sup, odbc_queries]);
check_database_module(mysql) ->
check_modules(mysql, [mysql, mysql_auth, mysql_conn, mysql_recv]);
check_database_module(pgsql) ->
check_modules(pgsql, [pgsql, pgsql_proto, pgsql_tcp, pgsql_util]).
%% @doc Issue a critical error and throw an exit if needing module is
%% missing.
check_modules(DB, Modules) ->
case get_missing_modules(Modules) of
[] ->
ok;
MissingModules when is_list(MissingModules) ->
?CRITICAL_MSG("ejabberd is configured to use '~p', but the following Erlang modules are not installed: '~p'", [DB, MissingModules]),
exit(database_module_missing)
end.
%% @doc Return the list of undefined modules
get_missing_modules(Modules) ->
lists:filter(fun(Module) ->
case catch Module:module_info() of
{'EXIT', {undef, _}} ->
true;
_ -> false
end
end, Modules).
%% @doc Return the list of databases used
get_db_used() ->
%% Retrieve domains with a database configured:
Domains =
ets:match(local_config, #local_config{key={odbc_server, '$1'},
value='$2'}),
%% Check that odbc is the auth method used for those domains:
%% and return the database name
DBs = lists:foldr(
fun([Domain, DB], Acc) ->
case check_odbc_option(
ejabberd_config:get_local_option(
{auth_method, Domain}, fun(V) -> V end)) of
true -> [get_db_type(DB)|Acc];
_ -> Acc
end
end,
[], Domains),
lists:usort(DBs).
%% @doc Depending in the DB definition, return which type of DB this is.
%% Note that MSSQL is detected as ODBC.
%% @spec (DB) -> mysql | pgsql | odbc
get_db_type(DB) when is_tuple(DB) ->
element(1, DB);
get_db_type(DB) when is_list(DB) ->
odbc.
%% @doc Return true if odbc option is used
check_odbc_option(odbc) ->
true;
check_odbc_option(AuthMethods) when is_list(AuthMethods) ->
lists:member(odbc, AuthMethods);
check_odbc_option(_) ->
false.
+382 -252
View File
@@ -27,15 +27,16 @@
-module(ejabberd_config).
-author('alexey@process-one.net').
-export([start/0, load_file/1,
-export([start/0, load_file/1, read_file/1,
add_global_option/2, add_local_option/2,
get_global_option/2, get_local_option/2,
get_global_option/3, get_local_option/3]).
-export([get_vh_by_auth_method/1]).
-export([is_file_readable/1]).
-export([get_version/0, get_myhosts/0, get_mylang/0]).
-export([prepare_opt_val/4]).
-export([convert_table_to_binary/5]).
get_global_option/3, get_local_option/3,
get_option/2, get_option/3, add_option/2,
get_vh_by_auth_method/1, is_file_readable/1,
get_version/0, get_myhosts/0, get_mylang/0,
prepare_opt_val/4, convert_table_to_binary/5,
transform_options/1, collect_options/1,
convert_to_yaml/1, convert_to_yaml/2]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -52,25 +53,29 @@
start() ->
mnesia:create_table(config,
[{disc_copies, [node()]},
{attributes, record_info(fields, config)}]),
mnesia:add_table_copy(config, node(), ram_copies),
case catch mnesia:table_info(local_config, storage_type) of
disc_copies ->
mnesia:delete_table(local_config);
_ ->
ok
end,
mnesia:create_table(local_config,
[{disc_copies, [node()]},
[{ram_copies, [node()]},
{local_content, true},
{attributes, record_info(fields, local_config)}]),
mnesia:add_table_copy(local_config, node(), ram_copies),
Config = get_ejabberd_config_path(),
load_file(Config),
State = read_file(Config),
%% This start time is used by mod_last:
add_local_option(node_start, now()),
ok.
{MegaSecs, Secs, _} = now(),
UnixTime = MegaSecs*1000000 + Secs,
State1 = set_option({node_start, global}, UnixTime, State),
set_opts(State1).
%% @doc Get the filename of the ejabberd configuration file.
%% The filename can be specified with: erl -config "/path/to/ejabberd.cfg".
%% The filename can be specified with: erl -config "/path/to/ejabberd.yml".
%% It can also be specified with the environtment variable EJABBERD_CONFIG_PATH.
%% If not specified, the default value 'ejabberd.cfg' is assumed.
%% If not specified, the default value 'ejabberd.yml' is assumed.
%% @spec () -> string()
get_ejabberd_config_path() ->
case application:get_env(config) of
@@ -84,16 +89,59 @@ get_ejabberd_config_path() ->
end
end.
%% @doc Load the ejabberd configuration file.
%% @doc Read the ejabberd configuration file.
%% It also includes additional configuration files and replaces macros.
%% This function will crash if finds some error in the configuration file.
%% @spec (File::string()) -> ok
load_file(File) ->
Terms = get_plain_terms_file(File),
%% @spec (File::string()) -> #state{}.
read_file(File) ->
read_file(File, [{replace_macros, true},
{include_files, true}]).
read_file(File, Opts) ->
Terms1 = get_plain_terms_file(File, Opts),
Terms_macros = case proplists:get_bool(replace_macros, Opts) of
true -> replace_macros(Terms1);
false -> Terms1
end,
Terms = transform_terms(Terms_macros),
State = lists:foldl(fun search_hosts/2, #state{}, Terms),
Terms_macros = replace_macros(Terms),
Res = lists:foldl(fun process_term/2, State, Terms_macros),
set_opts(Res).
{Head, Tail} = lists:partition(
fun({host_config, _}) -> false;
({append_host_config, _}) -> false;
(_) -> true
end, Terms),
State1 = lists:foldl(fun process_term/2, State, Head ++ Tail),
State1#state{opts = compact(State1#state.opts)}.
-spec load_file(string()) -> ok.
load_file(File) ->
State = read_file(File),
set_opts(State).
-spec convert_to_yaml(file:filename()) -> ok | {error, any()}.
convert_to_yaml(File) ->
convert_to_yaml(File, stdout).
-spec convert_to_yaml(file:filename(),
stdout | file:filename()) -> ok | {error, any()}.
convert_to_yaml(File, Output) ->
State = read_file(File, [{include_files, false}]),
Opts = [{K, V} || #local_config{key = K, value = V} <- State#state.opts],
{GOpts, HOpts} = split_by_hosts(Opts),
NewOpts = GOpts ++ lists:map(
fun({Host, Opts1}) ->
{host_config, [{Host, Opts1}]}
end, HOpts),
Data = p1_yaml:encode(lists:reverse(NewOpts)),
case Output of
stdout ->
io:format("~s~n", [Data]);
FileName ->
file:write_file(FileName, Data)
end.
%% @doc Read an ejabberd configuration file and return the terms.
%% Input is an absolute or relative path to an ejabberd config file.
@@ -102,22 +150,47 @@ load_file(File) ->
%% and the terms in those files were included.
%% @spec(string()) -> [term()]
%% @spec(iolist()) -> [term()]
get_plain_terms_file(File) when is_binary(File) ->
get_plain_terms_file(binary_to_list(File));
get_plain_terms_file(File1) ->
get_plain_terms_file(File) ->
get_plain_terms_file(File, [{include_files, true}]).
get_plain_terms_file(File, Opts) when is_binary(File) ->
get_plain_terms_file(binary_to_list(File), Opts);
get_plain_terms_file(File1, Opts) ->
File = get_absolute_path(File1),
case file:consult(File) of
case consult(File) of
{ok, Terms} ->
BinTerms = strings_to_binary(Terms),
include_config_files(BinTerms);
{error, {LineNumber, erl_parse, _ParseMessage} = Reason} ->
ExitText = describe_config_problem(File, Reason, LineNumber),
?ERROR_MSG(ExitText, []),
exit_or_halt(ExitText);
case proplists:get_bool(include_files, Opts) of
true ->
include_config_files(BinTerms);
false ->
BinTerms
end;
{error, Reason} ->
ExitText = describe_config_problem(File, Reason),
?ERROR_MSG(ExitText, []),
exit_or_halt(ExitText)
?ERROR_MSG(Reason, []),
exit_or_halt(Reason)
end.
consult(File) ->
case filename:extension(File) of
".yml" ->
case p1_yaml:decode_from_file(File, [plain_as_atom]) of
{ok, []} ->
{ok, []};
{ok, [Document|_]} ->
{ok, Document};
{error, Err} ->
{error, p1_yaml:format_error(Err)}
end;
_ ->
case file:consult(File) of
{ok, Terms} ->
{ok, Terms};
{error, {LineNumber, erl_parse, _ParseMessage} = Reason} ->
{error, describe_config_problem(File, Reason, LineNumber)};
{error, Reason} ->
{error, describe_config_problem(File, Reason)}
end
end.
%% @doc Convert configuration filename to absolute path.
@@ -161,7 +234,7 @@ search_hosts(Term, State) ->
add_hosts_to_option(Hosts, State) ->
PrepHosts = normalize_hosts(Hosts),
add_option(hosts, PrepHosts, State#state{hosts = PrepHosts}).
set_option({hosts, global}, PrepHosts, State#state{hosts = PrepHosts}).
normalize_hosts(Hosts) ->
normalize_hosts(Hosts,[]).
@@ -235,21 +308,37 @@ exit_or_halt(ExitText) ->
%% @doc Include additional configuration files in the list of terms.
%% @spec ([term()]) -> [term()]
include_config_files(Terms) ->
include_config_files(Terms, []).
{FileOpts, Terms1} =
lists:mapfoldl(
fun({include_config_file, _} = T, Ts) ->
{[transform_include_option(T)], Ts};
({include_config_file, _, _} = T, Ts) ->
{[transform_include_option(T)], Ts};
(T, Ts) ->
{[], [T|Ts]}
end, [], Terms),
Terms2 = lists:flatmap(
fun({File, Opts}) ->
include_config_file(File, Opts)
end, lists:flatten(FileOpts)),
Terms1 ++ Terms2.
include_config_files([], Res) ->
Res;
include_config_files([{include_config_file, Filename} | Terms], Res) ->
include_config_files([{include_config_file, Filename, []} | Terms], Res);
include_config_files([{include_config_file, Filename, Options} | Terms], Res) ->
transform_include_option({include_config_file, File}) when is_list(File) ->
case is_string(File) of
true -> {File, []};
false -> File
end;
transform_include_option({include_config_file, Filename}) ->
{Filename, []};
transform_include_option({include_config_file, Filename, Options}) ->
{Filename, Options}.
include_config_file(Filename, Options) ->
Included_terms = get_plain_terms_file(Filename),
Disallow = proplists:get_value(disallow, Options, []),
Included_terms2 = delete_disallowed(Disallow, Included_terms),
Allow_only = proplists:get_value(allow_only, Options, all),
Included_terms3 = keep_only_allowed(Allow_only, Included_terms2),
include_config_files(Terms, Res ++ Included_terms3);
include_config_files([Term | Terms], Res) ->
include_config_files(Terms, Res ++ [Term]).
keep_only_allowed(Allow_only, Included_terms2).
%% @doc Filter from the list of terms the disallowed.
%% Returns a sublist of Terms without the ones which first element is
@@ -311,12 +400,19 @@ split_terms_macros(Terms) ->
fun(Term, {TOs, Ms}) ->
case Term of
{define_macro, Key, Value} ->
case is_atom(Key) and is_all_uppercase(Key) of
case is_correct_macro({Key, Value}) of
true ->
{TOs, Ms++[{Key, Value}]};
false ->
exit({macro_not_properly_defined, Term})
end;
{define_macro, KeyVals} ->
case lists:all(fun is_correct_macro/1, KeyVals) of
true ->
{TOs, Ms ++ KeyVals};
false ->
exit({macros_not_properly_defined, Term})
end;
Term ->
{TOs ++ [Term], Ms}
end
@@ -324,6 +420,11 @@ split_terms_macros(Terms) ->
{[], []},
Terms).
is_correct_macro({Key, _Val}) ->
is_atom(Key) and is_all_uppercase(Key);
is_correct_macro(_) ->
false.
%% @doc Recursively replace in Terms macro usages with the defined value.
%% @spec (Terms, Macros) -> Terms
%% Terms = [term()]
@@ -331,7 +432,9 @@ split_terms_macros(Terms) ->
replace([], _) ->
[];
replace([Term|Terms], Macros) ->
[replace_term(Term, Macros) | replace(Terms, Macros)].
[replace_term(Term, Macros) | replace(Terms, Macros)];
replace(Term, Macros) ->
replace_term(Term, Macros).
replace_term(Key, Macros) when is_atom(Key) ->
case is_all_uppercase(Key) of
@@ -365,197 +468,65 @@ is_all_uppercase(Atom) ->
process_term(Term, State) ->
case Term of
override_global ->
State#state{override_global = true};
override_local ->
State#state{override_local = true};
override_acls ->
State#state{override_acls = true};
{acl, _ACLName, _ACLData} ->
process_host_term(Term, global, State);
{access, _RuleName, _Rules} ->
process_host_term(Term, global, State);
{shaper, _Name, _Data} ->
%%lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
%% State, State#state.hosts);
process_host_term(Term, global, State);
{host, _Host} ->
State;
{hosts, _Hosts} ->
State;
{fqdn, HostFQDN} ->
?DEBUG("FQDN set to: ~p", [HostFQDN]),
add_option(fqdn, HostFQDN, State);
{host_config, Host, Terms} ->
lists:foldl(fun(T, S) -> process_host_term(T, Host, S) end,
State, Terms);
{listen, Listeners} ->
Listeners2 =
lists:map(
fun({PortIP, Module, Opts}) ->
{Port, IPT, _, _, Proto, OptsClean} =
ejabberd_listener:parse_listener_portip(PortIP, Opts),
{{Port, IPT, Proto}, Module, OptsClean}
end,
Listeners),
add_option(listen, Listeners2, State);
{language, Val} ->
add_option(language, Val, State);
{outgoing_s2s_port, Port} ->
add_option(outgoing_s2s_port, Port, State);
{outgoing_s2s_options, Methods, Timeout} ->
add_option(outgoing_s2s_options, {Methods, Timeout}, State);
{s2s_dns_options, PropList} ->
add_option(s2s_dns_options, PropList, State);
{s2s_use_starttls, Port} ->
add_option(s2s_use_starttls, Port, State);
{s2s_certfile, CertFile} ->
CertFileS = binary_to_list(CertFile),
case ejabberd_config:is_file_readable(CertFileS) of
true -> add_option(s2s_certfile, CertFile, State);
false ->
ErrorText = "There is a problem in the configuration: "
"the specified file is not readable: ",
throw({error, ErrorText ++ CertFileS})
end;
{domain_certfile, Domain, CertFile} ->
CertFileS = binary_to_list(CertFile),
case ejabberd_config:is_file_readable(CertFileS) of
true -> add_option({domain_certfile, Domain}, CertFile, State);
false ->
ErrorText = "There is a problem in the configuration: "
"the specified file is not readable: ",
throw({error, ErrorText ++ CertFileS})
end;
{node_type, NodeType} ->
add_option(node_type, NodeType, State);
{cluster_nodes, Nodes} ->
add_option(cluster_nodes, Nodes, State);
{domain_balancing, Domain, Balancing} ->
add_option({domain_balancing, Domain}, Balancing, State);
{domain_balancing_component_number, Domain, N} ->
add_option({domain_balancing_component_number, Domain}, N, State);
{watchdog_admins, Admins} ->
add_option(watchdog_admins, Admins, State);
{watchdog_large_heap, LH} ->
add_option(watchdog_large_heap, LH, State);
{registration_timeout, Timeout} ->
add_option(registration_timeout, Timeout, State);
{captcha_cmd, Cmd} ->
add_option(captcha_cmd, Cmd, State);
{captcha_host, Host} ->
add_option(captcha_host, Host, State);
{captcha_limit, Limit} ->
add_option(captcha_limit, Limit, State);
{ejabberdctl_access_commands, ACs} ->
add_option(ejabberdctl_access_commands, ACs, State);
{loglevel, Loglevel} ->
ejabberd_logger:set(Loglevel),
State;
{max_fsm_queue, N} ->
add_option(max_fsm_queue, N, State);
{_Opt, _Val} ->
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
State, State#state.hosts)
end.
process_host_term(Term, Host, State) ->
case Term of
{acl, ACLName, ACLData} ->
State#state{opts =
[acl:to_record(Host, ACLName, ACLData) | State#state.opts]};
{access, RuleName, Rules} ->
State#state{opts = [#config{key = {access, RuleName, Host},
value = Rules} |
State#state.opts]};
{shaper, Name, Data} ->
State#state{opts = [#config{key = {shaper, Name, Host},
value = Data} |
State#state.opts]};
{host, Host} ->
State;
{hosts, _Hosts} ->
State;
{odbc_server, ODBC_server} ->
add_option({odbc_server, Host}, ODBC_server, State);
{modules, Modules} ->
add_option({modules, Host}, replace_modules(Modules), State);
{Opt, Val} ->
add_option({Opt, Host}, Val, State)
end.
add_option(Opt, Val, State) ->
Table = case Opt of
hosts ->
config;
language ->
config;
_ ->
local_config
end,
case Table of
config ->
State#state{opts = [#config{key = Opt, value = Val} |
State#state.opts]};
local_config ->
case Opt of
{{add, OptName}, Host} ->
State#state{opts = compact({OptName, Host}, Val,
State#state.opts, [])};
_ ->
State#state{opts = [#local_config{key = Opt, value = Val} |
State#state.opts]}
end
end.
compact({OptName, Host} = Opt, Val, [], Os) ->
?WARNING_MSG("The option '~p' is defined for the host ~p using host_config "
"before the global '~p' option. This host_config option may get overwritten.", [OptName, Host, OptName]),
[#local_config{key = Opt, value = Val}] ++ Os;
%% Traverse the list of the options already parsed
compact(Opt, Val, [O | Os1], Os2) ->
case catch O#local_config.key of
%% If the key of a local_config matches the Opt that wants to be added
Opt ->
%% Then prepend the new value to the list of old values
Os2 ++ [#local_config{key = Opt,
value = Val++O#local_config.value}
] ++ Os1;
{host_config, HostTerms} ->
lists:foldl(
fun({Host, Terms}, AccState) ->
lists:foldl(fun(T, S) ->
process_host_term(T, Host, S, set)
end, AccState, Terms)
end, State, HostTerms);
{append_host_config, HostTerms} ->
lists:foldl(
fun({Host, Terms}, AccState) ->
lists:foldl(fun(T, S) ->
process_host_term(T, Host, S, append)
end, AccState, Terms)
end, State, HostTerms);
_ ->
compact(Opt, Val, Os1, Os2++[O])
process_host_term(Term, global, State, set)
end.
process_host_term(Term, Host, State, Action) ->
case Term of
{modules, Modules} when Action == set ->
set_option({modules, Host}, replace_modules(Modules), State);
{modules, Modules} when Action == append ->
append_option({modules, Host}, replace_modules(Modules), State);
{host, _} ->
State;
{hosts, _} ->
State;
{Opt, Val} when Action == set ->
set_option({Opt, Host}, Val, State);
{Opt, Val} when Action == append ->
append_option({Opt, Host}, Val, State);
Opt ->
?WARNING_MSG("Ignore invalid (outdated?) option ~p", [Opt]),
State
end.
set_option(Opt, Val, State) ->
State#state{opts = [#local_config{key = Opt, value = Val} |
State#state.opts]}.
append_option({Opt, Host}, Val, State) ->
GlobalVals = lists:flatmap(
fun(#local_config{key = {O, global}, value = V})
when O == Opt ->
if is_list(V) -> V;
true -> [V]
end;
(_) ->
[]
end, State#state.opts),
NewVal = if is_list(Val) -> Val ++ GlobalVals;
true -> [Val|GlobalVals]
end,
set_option({Opt, Host}, NewVal, State).
set_opts(State) ->
Opts = lists:reverse(State#state.opts),
Opts = State#state.opts,
F = fun() ->
if
State#state.override_global ->
Ksg = mnesia:all_keys(config),
lists:foreach(fun(K) ->
mnesia:delete({config, K})
end, Ksg);
true ->
ok
end,
if
State#state.override_local ->
Ksl = mnesia:all_keys(local_config),
lists:foreach(fun(K) ->
mnesia:delete({local_config, K})
end, Ksl);
true ->
ok
end,
if
State#state.override_acls ->
Ksa = mnesia:all_keys(acl),
lists:foreach(fun(K) ->
mnesia:delete({acl, K})
end, Ksa);
true ->
ok
end,
lists:foreach(fun(R) ->
mnesia:write(R)
end, Opts)
@@ -576,12 +547,14 @@ set_opts(State) ->
end.
add_global_option(Opt, Val) ->
mnesia:transaction(fun() ->
mnesia:write(#config{key = Opt,
value = Val})
end).
add_option(Opt, Val).
add_local_option(Opt, Val) ->
add_option(Opt, Val).
add_option(Opt, Val) when is_atom(Opt) ->
add_option({Opt, global}, Val);
add_option(Opt, Val) ->
mnesia:transaction(fun() ->
mnesia:write(#local_config{key = Opt,
value = Val})
@@ -615,31 +588,49 @@ prepare_opt_val(Opt, Val, F, Default) ->
-spec get_global_option(any(), check_fun()) -> any().
get_global_option(Opt, F) ->
get_global_option(Opt, F, undefined).
get_option(Opt, F, undefined).
-spec get_global_option(any(), check_fun(), any()) -> any().
get_global_option(Opt, F, Default) ->
case ets:lookup(config, Opt) of
[#config{value = Val}] ->
prepare_opt_val(Opt, Val, F, Default);
_ ->
Default
end.
get_option(Opt, F, Default).
-spec get_local_option(any(), check_fun()) -> any().
get_local_option(Opt, F) ->
get_local_option(Opt, F, undefined).
get_option(Opt, F, undefined).
-spec get_local_option(any(), check_fun(), any()) -> any().
get_local_option(Opt, F, Default) ->
get_option(Opt, F, Default).
-spec get_option(any(), check_fun()) -> any().
get_option(Opt, F) ->
get_option(Opt, F, undefined).
-spec get_option(any(), check_fun(), any()) -> any().
get_option(Opt, F, Default) when is_atom(Opt) ->
get_option({Opt, global}, F, Default);
get_option(Opt, F, Default) ->
case Opt of
{O, global} when is_atom(O) -> ok;
{O, H} when is_atom(O), is_binary(H) -> ok;
_ -> ?WARNING_MSG("Option ~p has invalid (outdated?) format. "
"This is likely a bug", [Opt])
end,
case ets:lookup(local_config, Opt) of
[#local_config{value = Val}] ->
prepare_opt_val(Opt, Val, F, Default);
_ ->
Default
_ ->
case Opt of
{Key, Host} when Host /= global ->
get_option({Key, global}, F, Default);
_ ->
Default
end
end.
-spec get_vh_by_auth_method(atom()) -> [binary()].
@@ -669,12 +660,12 @@ get_version() ->
-spec get_myhosts() -> [binary()].
get_myhosts() ->
ejabberd_config:get_global_option(hosts, fun(V) -> V end).
get_option(hosts, fun(V) -> V end).
-spec get_mylang() -> binary().
get_mylang() ->
ejabberd_config:get_global_option(
get_option(
language,
fun iolist_to_binary/1,
<<"en">>).
@@ -702,14 +693,14 @@ replace_modules(Modules) ->
emit_deprecation_warning(Module, NewModule, DBType),
NewOpts = [{db_type, DBType} |
lists:keydelete(db_type, 1, Opts)],
{NewModule, NewOpts};
{NewModule, transform_module_options(Module, NewOpts)};
NewModule ->
if Module /= NewModule ->
emit_deprecation_warning(Module, NewModule);
true ->
ok
end,
{NewModule, Opts}
{NewModule, transform_module_options(Module, Opts)}
end
end, Modules).
@@ -722,6 +713,9 @@ strings_to_binary(L) when is_list(L) ->
false ->
strings_to_binary1(L)
end;
strings_to_binary({A, B, C, D}) when
is_integer(A), is_integer(B), is_integer(C), is_integer(D) ->
{A, B, C ,D};
strings_to_binary(T) when is_tuple(T) ->
list_to_tuple(strings_to_binary1(tuple_to_list(T)));
strings_to_binary(X) ->
@@ -762,6 +756,142 @@ format_term(S) when is_list(S), S /= [] ->
format_term(T) ->
io_lib:format("~p", [binary_to_strings(T)]).
transform_terms(Terms) ->
%% We could check all ejabberd beams, but this
%% slows down start-up procedure :(
Mods = [mod_register,
mod_last,
ejabberd_s2s,
ejabberd_listener,
ejabberd_odbc_sup,
shaper,
ejabberd_s2s_out,
acl,
ejabberd_config],
collect_options(transform_terms(Mods, Terms)).
transform_terms([Mod|Mods], Terms) ->
case catch Mod:transform_options(Terms) of
{'EXIT', _} = Err ->
?ERROR_MSG("Failed to transform terms by ~p: ~p", [Mod, Err]),
transform_terms(Mods, Terms);
NewTerms ->
transform_terms(Mods, NewTerms)
end;
transform_terms([], NewTerms) ->
NewTerms.
transform_module_options(Module, Opts) ->
Opts1 = gen_iq_handler:transform_module_options(Opts),
try
Module:transform_module_options(Opts1)
catch error:undef ->
Opts1
end.
compact(Cfg) ->
Opts = [{K, V} || #local_config{key = K, value = V} <- Cfg],
{GOpts, HOpts} = split_by_hosts(Opts),
[#local_config{key = {O, global}, value = V} || {O, V} <- GOpts] ++
lists:flatmap(
fun({Host, OptVal}) ->
case lists:member(OptVal, GOpts) of
true ->
[];
false ->
[#local_config{key = {Opt, Host}, value = Val}
|| {Opt, Val} <- OptVal]
end
end, lists:flatten(HOpts)).
split_by_hosts(Opts) ->
Opts1 = orddict:to_list(
lists:foldl(
fun({{Opt, Host}, Val}, D) ->
orddict:append(Host, {Opt, Val}, D)
end, orddict:new(), Opts)),
case lists:keytake(global, 1, Opts1) of
{value, {global, GlobalOpts}, HostOpts} ->
{GlobalOpts, HostOpts};
_ ->
{[], Opts1}
end.
collect_options(Opts) ->
{D, InvalidOpts} =
lists:foldl(
fun({K, V}, {D, Os}) when is_list(V) ->
{orddict:append_list(K, V, D), Os};
({K, V}, {D, Os}) ->
{orddict:store(K, V, D), Os};
(Opt, {D, Os}) ->
{D, [Opt|Os]}
end, {orddict:new(), []}, Opts),
InvalidOpts ++ orddict:to_list(D).
transform_options(Opts) ->
Opts1 = lists:foldl(fun transform_options/2, [], Opts),
{HOpts, Opts2} = lists:mapfoldl(
fun({host_config, O}, Os) ->
{[O], Os};
(O, Os) ->
{[], [O|Os]}
end, [], Opts1),
{AHOpts, Opts3} = lists:mapfoldl(
fun({append_host_config, O}, Os) ->
{[O], Os};
(O, Os) ->
{[], [O|Os]}
end, [], Opts2),
HOpts1 = case collect_options(lists:flatten(HOpts)) of
[] ->
[];
HOs ->
[{host_config,
[{H, transform_terms(O)} || {H, O} <- HOs]}]
end,
AHOpts1 = case collect_options(lists:flatten(AHOpts)) of
[] ->
[];
AHOs ->
[{append_host_config,
[{H, transform_terms(O)} || {H, O} <- AHOs]}]
end,
HOpts1 ++ AHOpts1 ++ Opts3.
transform_options({domain_certfile, Domain, CertFile}, Opts) ->
?WARNING_MSG("Option 'domain_certfile' now should be defined "
"per virtual host or globally. The old format is "
"still supported but it is better to fix your config", []),
[{host_config, [{Domain, [{domain_certfile, CertFile}]}]}|Opts];
transform_options(Opt, Opts) when Opt == override_global;
Opt == override_local;
Opt == override_acls ->
?WARNING_MSG("Ignoring '~s' option which has no effect anymore", [Opt]),
Opts;
transform_options({host_config, Host, HOpts}, Opts) ->
{AddOpts, HOpts1} =
lists:mapfoldl(
fun({{add, Opt}, Val}, Os) ->
?WARNING_MSG("Option 'add' is deprecated. "
"The option is still supported "
"but it is better to fix your config: "
"use 'append_host_config' instead.", []),
{[{Opt, Val}], Os};
(O, Os) ->
{[], [O|Os]}
end, [], HOpts),
[{append_host_config, [{Host, lists:flatten(AddOpts)}]},
{host_config, [{Host, HOpts1}]}|Opts];
transform_options({define_macro, Macro, Val}, Opts) ->
[{define_macro, [{Macro, Val}]}|Opts];
transform_options({include_config_file, _} = Opt, Opts) ->
[{include_config_file, [transform_include_option(Opt)]} | Opts];
transform_options({include_config_file, _, _} = Opt, Opts) ->
[{include_config_file, [transform_include_option(Opt)]} | Opts];
transform_options(Opt, Opts) ->
[Opt|Opts].
-spec convert_table_to_binary(atom(), [atom()], atom(),
fun(), fun()) -> ok.
+2 -2
View File
@@ -135,7 +135,7 @@ process(["status"]) ->
[node(), InternalStatus, ProvidedStatus]),
case lists:keysearch(ejabberd, 1, application:which_applications()) of
false ->
EjabberdLogPath = ejabberd_app:get_log_path(),
EjabberdLogPath = ejabberd_logger:get_log_path(),
?PRINT("ejabberd is not running in that node~n"
"Check for error messages: ~s~n"
"or other files in that directory.~n", [EjabberdLogPath]),
@@ -237,7 +237,7 @@ process2(Args, Auth, AccessCommands) ->
end.
get_accesscommands() ->
ejabberd_config:get_local_option(ejabberdctl_access_commands,
ejabberd_config:get_option(ejabberdctl_access_commands,
fun(V) when is_list(V) -> V end, []).
%%-----------------------------
+1 -2
View File
@@ -280,7 +280,7 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
%%% Internal functions
%%--------------------------------------------------------------------
check_starttls(SockMod, Socket, Receiver, Opts) ->
TLSEnabled = lists:member(tls, Opts),
TLSEnabled = proplists:get_bool(tls, Opts),
TLSOpts = lists:filter(fun({certfile, _}) -> true;
(_) -> false
end, Opts),
@@ -292,4 +292,3 @@ check_starttls(SockMod, Socket, Receiver, Opts) ->
true ->
{SockMod, Socket}
end.
+45 -17
View File
@@ -30,7 +30,8 @@
%% External exports
-export([start/2, start_link/2, become_controller/1,
socket_type/0, receive_headers/1, url_encode/1]).
socket_type/0, receive_headers/1, url_encode/1,
transform_listen_option/2]).
%% Callbacks
-export([init/2]).
@@ -91,12 +92,16 @@ start_link(SockData, Opts) ->
[SockData, Opts])}.
init({SockMod, Socket}, Opts) ->
TLSEnabled = lists:member(tls, Opts),
TLSEnabled = proplists:get_bool(tls, Opts),
TLSOpts1 = lists:filter(fun ({certfile, _}) -> true;
(_) -> false
end,
Opts),
TLSOpts = [verify_none | TLSOpts1],
TLSOpts2 = case proplists:get_bool(tls_compression, Opts) of
false -> [compression_none | TLSOpts1];
true -> TLSOpts1
end,
TLSOpts = [verify_none | TLSOpts2],
{SockMod1, Socket1} = if TLSEnabled ->
inet:setopts(Socket, [{recbuf, 8192}]),
{ok, TLSSocket} = p1_tls:tcp_to_tls(Socket,
@@ -109,32 +114,33 @@ init({SockMod, Socket}, Opts) ->
inet:setopts(Socket1, [{packet, http_bin}, {recbuf, 8192}]);
_ -> ok
end,
Captcha = case lists:member(captcha, Opts) of
Captcha = case proplists:get_bool(captcha, Opts) of
true -> [{[<<"captcha">>], ejabberd_captcha}];
false -> []
end,
Register = case lists:member(register, Opts) of
Register = case proplists:get_bool(register, Opts) of
true -> [{[<<"register">>], mod_register_web}];
false -> []
end,
Admin = case lists:member(web_admin, Opts) of
Admin = case proplists:get_bool(web_admin, Opts) of
true -> [{[<<"admin">>], ejabberd_web_admin}];
false -> []
end,
Bind = case lists:member(http_bind, Opts) of
Bind = case proplists:get_bool(http_bind, Opts) of
true -> [{[<<"http-bind">>], mod_http_bind}];
false -> []
end,
Poll = case lists:member(http_poll, Opts) of
Poll = case proplists:get_bool(http_poll, Opts) of
true -> [{[<<"http-poll">>], ejabberd_http_poll}];
false -> []
end,
DefinedHandlers = case lists:keysearch(request_handlers,
1, Opts)
of
{value, {request_handlers, H}} -> H;
false -> []
end,
DefinedHandlers = gen_mod:get_opt(
request_handlers, Opts,
fun(Hs) ->
[{str:tokens(
iolist_to_binary(Path), <<"/">>),
Mod} || {Path, Mod} <- Hs]
end, []),
RequestHandlers = DefinedHandlers ++ Captcha ++ Register ++
Admin ++ Bind ++ Poll,
?DEBUG("S: ~p~n", [RequestHandlers]),
@@ -195,8 +201,8 @@ parse_headers(#state{request_method = Method,
trail = Data} =
State) ->
PktType = case Method of
undefined -> http;
_ -> httph
undefined -> http_bin;
_ -> httph_bin
end,
case erlang:decode_packet(PktType, Data, []) of
{ok, Pkt, Rest} ->
@@ -480,7 +486,7 @@ analyze_ip_xff(IP, [], _Host) -> IP;
analyze_ip_xff({IPLast, Port}, XFF, Host) ->
[ClientIP | ProxiesIPs] = str:tokens(XFF, <<", ">>) ++
[jlib:ip_to_list(IPLast)],
TrustedProxies = ejabberd_config:get_local_option(
TrustedProxies = ejabberd_config:get_option(
{trusted_proxies, Host},
fun(TPs) ->
[iolist_to_binary(TP) || TP <- TPs]
@@ -830,3 +836,25 @@ normalize_path([_Parent, <<"..">>|Path], Norm) ->
normalize_path(Path, Norm);
normalize_path([Part | Path], Norm) ->
normalize_path(Path, [Part|Norm]).
transform_listen_option(captcha, Opts) ->
[{captcha, true}|Opts];
transform_listen_option(register, Opts) ->
[{register, true}|Opts];
transform_listen_option(web_admin, Opts) ->
[{web_admin, true}|Opts];
transform_listen_option(http_bind, Opts) ->
[{http_bind, true}|Opts];
transform_listen_option(http_poll, Opts) ->
[{http_poll, true}|Opts];
transform_listen_option({request_handlers, Hs}, Opts) ->
Hs1 = lists:map(
fun({PList, Mod}) when is_list(PList) ->
Path = iolist_to_binary([[$/, P] || P <- PList]),
{Path, Mod};
(Opt) ->
Opt
end, Hs),
[{request_handlers, Hs1} | Opts];
transform_listen_option(Opt, Opts) ->
[Opt|Opts].
+1 -1
View File
@@ -205,7 +205,7 @@ get_human_html_xmlel() ->
init([ID, Key, IP]) ->
?INFO_MSG("started: ~p", [{ID, Key, IP}]),
Opts = ejabberd_c2s_config:get_c2s_limits(),
HTTPPollTimeout = ejabberd_config:get_local_option(
HTTPPollTimeout = ejabberd_config:get_option(
{http_poll_timeout, ?MYNAME},
fun(I) when is_integer(I), I>0 -> I end,
?HTTP_POLL_TIMEOUT) * 1000,
+77 -22
View File
@@ -36,7 +36,8 @@
parse_listener_portip/2,
add_listener/3,
delete_listener/2,
validate_cfg/1
transform_options/1,
validate_cfg/1
]).
-include("ejabberd.hrl").
@@ -55,7 +56,7 @@ init(_) ->
{ok, {{one_for_one, 10, 1}, []}}.
bind_tcp_ports() ->
case ejabberd_config:get_local_option(listen, fun validate_cfg/1) of
case ejabberd_config:get_option(listen, fun validate_cfg/1) of
undefined ->
ignore;
Ls ->
@@ -88,7 +89,7 @@ bind_tcp_port(PortIP, Module, RawOpts) ->
end.
start_listeners() ->
case ejabberd_config:get_local_option(listen, fun validate_cfg/1) of
case ejabberd_config:get_option(listen, fun validate_cfg/1) of
undefined ->
ignore;
Ls ->
@@ -267,7 +268,7 @@ strip_ip_option(Opts) ->
Opts),
case IPL of
%% Only the first ip option is considered
[{ip, T1} | _] when is_tuple(T1) ->
[{ip, T1} | _] ->
{T1, OptsNoIP};
[] ->
{no_ip_option, OptsNoIP}
@@ -364,7 +365,7 @@ start_listener_sup(Port, Module, Opts) ->
supervisor:start_child(ejabberd_listeners, ChildSpec).
stop_listeners() ->
Ports = ejabberd_config:get_local_option(listen, fun validate_cfg/1),
Ports = ejabberd_config:get_option(listen, fun validate_cfg/1),
lists:foreach(
fun({PortIpNetp, Module, _Opts}) ->
delete_listener(PortIpNetp, Module)
@@ -397,7 +398,7 @@ add_listener(PortIP, Module, Opts) ->
PortIP1 = {Port, IPT, Proto},
case start_listener(PortIP1, Module, Opts) of
{ok, _Pid} ->
Ports = case ejabberd_config:get_local_option(
Ports = case ejabberd_config:get_option(
listen, fun validate_cfg/1) of
undefined ->
[];
@@ -406,7 +407,8 @@ add_listener(PortIP, Module, Opts) ->
end,
Ports1 = lists:keydelete(PortIP1, 1, Ports),
Ports2 = [{PortIP1, Module, Opts} | Ports1],
ejabberd_config:add_local_option(listen, Ports2),
Ports3 = lists:map(fun transform_option/1, Ports2),
ejabberd_config:add_option(listen, Ports3),
ok;
{error, {already_started, _Pid}} ->
{error, {already_started, PortIP}};
@@ -428,7 +430,7 @@ delete_listener(PortIP, Module) ->
delete_listener(PortIP, Module, Opts) ->
{Port, IPT, _, _, Proto, _} = parse_listener_portip(PortIP, Opts),
PortIP1 = {Port, IPT, Proto},
Ports = case ejabberd_config:get_local_option(
Ports = case ejabberd_config:get_option(
listen, fun validate_cfg/1) of
undefined ->
[];
@@ -436,7 +438,8 @@ delete_listener(PortIP, Module, Opts) ->
Ls
end,
Ports1 = lists:keydelete(PortIP1, 1, Ports),
ejabberd_config:add_local_option(listen, Ports1),
Ports2 = lists:map(fun transform_option/1, Ports1),
ejabberd_config:add_option(listen, Ports2),
stop_listener(PortIP1, Module).
@@ -541,6 +544,55 @@ format_error(Reason) ->
-define(IS_PORT(P), (is_integer(P) and (P > 0) and (P =< 65535))).
-define(IS_TRANSPORT(T), ((T == tcp) or (T == udp))).
transform_option({{Port, IP, Transport}, Mod, Opts}) ->
IPStr = if is_tuple(IP) ->
list_to_binary(inet_parse:ntoa(IP));
true ->
IP
end,
Opts1 = lists:map(
fun({ip, IPT}) when is_tuple(IPT) ->
{ip, list_to_binary(inet_parse:ntoa(IP))};
(tls) -> {tls, true};
(ssl) -> {tls, true};
(zlib) -> {zlib, true};
(starttls) -> {starttls, true};
(starttls_required) -> {starttls_required, true};
(Opt) -> Opt
end, Opts),
Opts2 = lists:foldl(
fun(Opt, Acc) ->
try
Mod:transform_listen_option(Opt, Acc)
catch error:undef ->
Acc
end
end, [], Opts1),
TransportOpt = if Transport == tcp -> [];
true -> [{transport, Transport}]
end,
IPOpt = if IPStr == <<"0.0.0.0">> -> [];
true -> [{ip, IPStr}]
end,
IPOpt ++ TransportOpt ++ [{port, Port}, {module, Mod} | Opts2];
transform_option({{Port, Transport}, Mod, Opts})
when ?IS_TRANSPORT(Transport) ->
transform_option({{Port, {0,0,0,0}, Transport}, Mod, Opts});
transform_option({{Port, IP}, Mod, Opts}) ->
transform_option({{Port, IP, tcp}, Mod, Opts});
transform_option({Port, Mod, Opts}) ->
transform_option({{Port, {0,0,0,0}, tcp}, Mod, Opts});
transform_option(Opt) ->
Opt.
transform_options(Opts) ->
lists:foldl(fun transform_options/2, [], Opts).
transform_options({listen, LOpts}, Opts) ->
[{listen, lists:map(fun transform_option/1, LOpts)} | Opts];
transform_options(Opt, Opts) ->
[Opt|Opts].
-type transport() :: udp | tcp.
-type port_ip_transport() :: inet:port_number() |
{inet:port_number(), transport()} |
@@ -551,18 +603,21 @@ format_error(Reason) ->
validate_cfg(L) ->
lists:map(
fun({PortIPTransport, Mod1, Opts}) when is_atom(Mod1), is_list(Opts) ->
Mod = prepare_mod(Mod1),
case PortIPTransport of
Port when ?IS_PORT(Port) ->
{Port, Mod, Opts};
{Port, Trans} when ?IS_PORT(Port) and ?IS_TRANSPORT(Trans) ->
{{Port, Trans}, Mod, Opts};
{Port, IP} when ?IS_PORT(Port) ->
{{Port, prepare_ip(IP)}, Mod, Opts};
{Port, IP, Trans} when ?IS_PORT(Port) and ?IS_TRANSPORT(Trans) ->
{{Port, prepare_ip(IP), Trans}, Mod, Opts}
end
fun(LOpts) ->
lists:foldl(
fun({port, Port}, {{_, IP, T}, Mod, Opts}) ->
true = ?IS_PORT(Port),
{{Port, IP, T}, Mod, Opts};
({ip, IP}, {{Port, _, T}, Mod, Opts}) ->
{{Port, prepare_ip(IP), T}, Mod, Opts};
({transport, T}, {{Port, IP, _}, Mod, Opts}) ->
true = ?IS_TRANSPORT(T),
{{Port, IP, T}, Mod, Opts};
({module, Mod}, {Port, _, Opts}) ->
{Port, prepare_mod(Mod), Opts};
(Opt, {Port, Mod, Opts}) ->
{Port, Mod, [Opt|Opts]}
end, {{5222, {0,0,0,0}, tcp}, ejabberd_c2s, []}, LOpts)
end, L).
prepare_ip({A, B, C, D} = IP)
@@ -583,5 +638,5 @@ prepare_mod(ejabberd_stun) ->
prepare_mod(stun) ->
ejabberd:start_app(p1_stun),
stun;
prepare_mod(Mod) ->
prepare_mod(Mod) when is_atom(Mod) ->
Mod.
+122 -28
View File
@@ -27,31 +27,118 @@
-module(ejabberd_logger).
%% API
-export([start/0, set_logfile/1, reopen_log/0, get/0, set/1,
debug_msg/4, info_msg/4, warning_msg/4, error_msg/4,
critical_msg/4]).
-export([start/0, reopen_log/0, get/0, set/1, get_log_path/0]).
-include("ejabberd.hrl").
-type loglevel() :: 0 | 1 | 2 | 3 | 4 | 5.
-spec start() -> ok.
-spec get_log_path() -> string().
-spec reopen_log() -> ok.
-spec get() -> {loglevel(), atom(), string()}.
-spec set(loglevel() | {loglevel(), list()}) -> {module, module()}.
%%%===================================================================
%%% API
%%%===================================================================
%% @doc Returns the full path to the ejabberd log file.
%% It first checks for application configuration parameter 'log_path'.
%% If not defined it checks the environment variable EJABBERD_LOG_PATH.
%% And if that one is neither defined, returns the default value:
%% "ejabberd.log" in current directory.
get_log_path() ->
case application:get_env(ejabberd, log_path) of
{ok, Path} ->
Path;
undefined ->
case os:getenv("EJABBERD_LOG_PATH") of
false ->
?LOG_PATH;
Path ->
Path
end
end.
-ifdef(LAGER).
start() ->
application:load(lager),
ConsoleLog = get_log_path(),
Dir = filename:dirname(ConsoleLog),
ErrorLog = filename:join([Dir, "error.log"]),
CrashLog = filename:join([Dir, "crash.log"]),
application:set_env(
lager, handlers,
[{lager_console_backend, info},
{lager_file_backend, [{file, ConsoleLog}, {level, info}, {count, 1}]},
{lager_file_backend, [{file, ErrorLog}, {level, error}, {count, 1}]}]),
application:set_env(lager, crash_log, CrashLog),
ejabberd:start_app(lager),
ok.
set_logfile(FileName) ->
error_logger:add_report_handler(p1_logger_h, FileName).
reopen_log() ->
lists:foreach(
fun({lager_file_backend, File}) ->
whereis(lager_event) ! {rotate, File};
(_) ->
ok
end, gen_event:which_handlers(lager_event)),
reopen_sasl_log().
get() ->
case lager:get_loglevel(lager_console_backend) of
none -> {0, no_log, "No log"};
emergency -> {1, critical, "Critical"};
alert -> {1, critical, "Critical"};
critical -> {1, critical, "Critical"};
error -> {2, error, "Error"};
warning -> {3, warning, "Warning"};
notice -> {3, warning, "Warning"};
info -> {4, info, "Info"};
debug -> {5, debug, "Debug"}
end.
set(LogLevel) when is_integer(LogLevel) ->
LagerLogLevel = case LogLevel of
0 -> none;
1 -> critical;
2 -> error;
3 -> warning;
4 -> info;
5 -> debug
end,
case lager:get_loglevel(lager_console_backend) of
LagerLogLevel ->
ok;
_ ->
ConsoleLog = get_log_path(),
lists:foreach(
fun({lager_file_backend, File} = H) when File == ConsoleLog ->
lager:set_loglevel(H, LagerLogLevel);
(lager_console_backend = H) ->
lager:set_loglevel(H, LagerLogLevel);
(_) ->
ok
end, gen_event:which_handlers(lager_event))
end,
{module, lager};
set({_LogLevel, _}) ->
error_logger:error_msg("custom loglevels are not supported for 'lager'"),
{module, lager}.
-else.
start() ->
set(4),
LogPath = get_log_path(),
error_logger:add_report_handler(p1_logger_h, LogPath),
ok.
reopen_log() ->
%% TODO: Use the Reopen log API for logger_h ?
p1_logger_h:reopen_log(),
case application:get_env(sasl,sasl_error_logger) of
{ok, {file, SASLfile}} ->
error_logger:delete_report_handler(sasl_report_file_h),
p1_logger_h:rotate_log(SASLfile),
error_logger:add_report_handler(sasl_report_file_h,
{SASLfile, get_sasl_error_logger_type()});
_ -> false
end,
ok.
reopen_sasl_log().
get() ->
p1_loglevel:get().
@@ -59,24 +146,31 @@ get() ->
set(LogLevel) ->
p1_loglevel:set(LogLevel).
debug_msg(Mod, Line, Format, Args) ->
p1_logger:debug_msg(Mod, Line, Format, Args).
info_msg(Mod, Line, Format, Args) ->
p1_logger:info_msg(Mod, Line, Format, Args).
warning_msg(Mod, Line, Format, Args) ->
p1_logger:warning_msg(Mod, Line, Format, Args).
error_msg(Mod, Line, Format, Args) ->
p1_logger:error_msg(Mod, Line, Format, Args).
critical_msg(Mod, Line, Format, Args) ->
p1_logger:critical_msg(Mod, Line, Format, Args).
-endif.
%%%===================================================================
%%% Internal functions
%%%===================================================================
reopen_sasl_log() ->
case application:get_env(sasl,sasl_error_logger) of
{ok, {file, SASLfile}} ->
error_logger:delete_report_handler(sasl_report_file_h),
rotate_sasl_log(SASLfile),
error_logger:add_report_handler(sasl_report_file_h,
{SASLfile, get_sasl_error_logger_type()});
_ -> false
end,
ok.
rotate_sasl_log(Filename) ->
case file:read_file_info(Filename) of
{ok, _FileInfo} ->
file:rename(Filename, [Filename, ".0"]),
ok;
{error, _Reason} ->
ok
end.
%% Function copied from Erlang/OTP lib/sasl/src/sasl.erl which doesn't export it
get_sasl_error_logger_type () ->
case application:get_env (sasl, errlog_type) of
+6 -1
View File
@@ -84,7 +84,12 @@ get_closest_node(Name) ->
%%--------------------------------------------------------------------
init([]) ->
{FE, BE} =
case ejabberd_config:get_local_option(node_type, fun(N) -> N end) of
case ejabberd_config:get_option(
node_type,
fun(frontend) -> frontend;
(backend) -> backend;
(generic) -> generic
end, generic) of
frontend ->
{true, false};
backend ->
+46 -43
View File
@@ -71,12 +71,12 @@
-define(TOP_LEVEL_TXN, 0).
-define(MAX_TRANSACTION_RESTARTS, 10).
-define(PGSQL_PORT, 5432).
-define(MYSQL_PORT, 3306).
-define(MAX_TRANSACTION_RESTARTS, 10).
-define(TRANSACTION_TIMEOUT, 60000).
-define(KEEPALIVE_TIMEOUT, 60000).
@@ -201,8 +201,8 @@ decode_term(Bin) ->
%%% Callback functions from gen_fsm
%%%----------------------------------------------------------------------
init([Host, StartInterval]) ->
case ejabberd_config:get_local_option(
{odbc_keepalive_interval, Host},
case ejabberd_config:get_option(
{keepalive_interval, Host},
fun(I) when is_integer(I), I>0 -> I end) of
undefined ->
ok;
@@ -325,7 +325,7 @@ handle_info(Info, StateName, State) ->
terminate(_Reason, _StateName, State) ->
ejabberd_odbc_sup:remove_pid(State#state.host, self()),
case State#state.db_type of
mysql -> catch mysql_conn:stop(State#state.db_ref);
mysql -> catch p1_mysql_conn:stop(State#state.db_ref);
_ -> ok
end,
ok.
@@ -447,9 +447,9 @@ sql_query_internal(Query) ->
mysql ->
?DEBUG("MySQL, Send query~n~p~n", [Query]),
%%squery to be able to specify result_type = binary
%%[Query] because mysql_conn expect query to be a list (elements can be binaries, or iolist)
%%[Query] because p1_mysql_conn expect query to be a list (elements can be binaries, or iolist)
%% but doesn't accept just a binary
R = mysql_to_odbc(mysql_conn:squery(State#state.db_ref,
R = mysql_to_odbc(p1_mysql_conn:squery(State#state.db_ref,
[Query], self(),
[{timeout, (?TRANSACTION_TIMEOUT) - 1000},
{result_type, binary}])),
@@ -515,6 +515,9 @@ pgsql_to_odbc({ok, PGSQLResult}) ->
pgsql_item_to_odbc({<<"SELECT", _/binary>>, Rows,
Recs}) ->
{selected, [element(1, Row) || Row <- Rows], Recs};
pgsql_item_to_odbc({<<"FETCH", _/binary>>, Rows,
Recs}) ->
{selected, [element(1, Row) || Row <- Rows], Recs};
pgsql_item_to_odbc(<<"INSERT ", OIDN/binary>>) ->
[_OID, N] = str:tokens(OIDN, <<" ">>),
{updated, jlib:binary_to_integer(N)};
@@ -530,12 +533,12 @@ pgsql_item_to_odbc(_) -> {updated, undefined}.
%% part of init/1
%% Open a database connection to MySQL
mysql_connect(Server, Port, DB, Username, Password) ->
case mysql_conn:start(binary_to_list(Server), Port,
case p1_mysql_conn:start(binary_to_list(Server), Port,
binary_to_list(Username), binary_to_list(Password),
binary_to_list(DB), fun log/3)
of
{ok, Ref} ->
mysql_conn:fetch(Ref, [<<"set names 'utf8';">>],
p1_mysql_conn:fetch(Ref, [<<"set names 'utf8';">>],
self()),
{ok, Ref};
Err -> Err
@@ -543,15 +546,15 @@ mysql_connect(Server, Port, DB, Username, Password) ->
%% Convert MySQL query result to Erlang ODBC result formalism
mysql_to_odbc({updated, MySQLRes}) ->
{updated, mysql:get_result_affected_rows(MySQLRes)};
{updated, p1_mysql:get_result_affected_rows(MySQLRes)};
mysql_to_odbc({data, MySQLRes}) ->
mysql_item_to_odbc(mysql:get_result_field_info(MySQLRes),
mysql:get_result_rows(MySQLRes));
mysql_item_to_odbc(p1_mysql:get_result_field_info(MySQLRes),
p1_mysql:get_result_rows(MySQLRes));
mysql_to_odbc({error, MySQLRes})
when is_binary(MySQLRes) ->
{error, MySQLRes};
mysql_to_odbc({error, MySQLRes}) ->
{error, mysql:get_result_reason(MySQLRes)}.
{error, p1_mysql:get_result_reason(MySQLRes)}.
%% When tabular data is returned, convert it to the ODBC formalism
mysql_item_to_odbc(Columns, Recs) ->
@@ -570,39 +573,39 @@ log(Level, Format, Args) ->
end.
db_opts(Host) ->
case ejabberd_config:get_local_option(
{odbc_server, Host},
fun({Type, Server, DB, User, Pass}) ->
{Type,
iolist_to_binary(Server),
case Type of
mysql -> ?MYSQL_PORT;
pgsql -> ?PGSQL_PORT
end,
iolist_to_binary(DB),
iolist_to_binary(User),
iolist_to_binary(Pass)};
({Type, Server, Port, DB, User, Pass})
when ((Type == mysql) or (Type == pgsql))
and (is_integer(Port) and ((Port > 0)
and (Port < 65536))) ->
{Type,
iolist_to_binary(Server),
Port,
iolist_to_binary(DB),
iolist_to_binary(User),
iolist_to_binary(Pass)};
(S) ->
iolist_to_binary(S)
end, <<"localhost">>) of
{Type, Server, Port, DB, User, Pass} ->
[Type, Server, Port, DB, User, Pass];
SQLServer ->
[odbc, SQLServer]
Type = ejabberd_config:get_option({odbc_type, Host},
fun(mysql) -> mysql;
(pgsql) -> pgsql;
(odbc) -> odbc
end, odbc),
Server = ejabberd_config:get_option({odbc_server, Host},
fun iolist_to_binary/1,
<<"localhost">>),
case Type of
odbc ->
[odbc, Server];
_ ->
Port = ejabberd_config:get_option(
{port, Host},
fun(P) when is_integer(P), P > 0, P < 65536 -> P end,
case Type of
mysql -> ?MYSQL_PORT;
pgsql -> ?PGSQL_PORT
end),
DB = ejabberd_config:get_option({odbc_database, Host},
fun iolist_to_binary/1,
<<"ejabberd">>),
User = ejabberd_config:get_option({odbc_username, Host},
fun iolist_to_binary/1,
<<"ejabberd">>),
Pass = ejabberd_config:get_option({odbc_password, Host},
fun iolist_to_binary/1,
<<"">>),
[Type, Server, Port, DB, User, Pass]
end.
max_fsm_queue() ->
ejabberd_config:get_local_option(
ejabberd_config:get_option(
max_fsm_queue,
fun(N) when is_integer(N), N > 0 -> N end).
+24 -3
View File
@@ -30,11 +30,15 @@
%% API
-export([start_link/1, init/1, add_pid/2, remove_pid/2,
get_pids/1, get_random_pid/1]).
get_pids/1, get_random_pid/1, transform_options/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
-define(PGSQL_PORT, 5432).
-define(MYSQL_PORT, 3306).
-define(DEFAULT_POOL_SIZE, 10).
-define(DEFAULT_ODBC_START_INTERVAL, 30).
@@ -56,11 +60,11 @@ start_link(Host) ->
?MODULE, [Host]).
init([Host]) ->
PoolSize = ejabberd_config:get_local_option(
PoolSize = ejabberd_config:get_option(
{odbc_pool_size, Host},
fun(I) when is_integer(I), I>0 -> I end,
?DEFAULT_POOL_SIZE),
StartInterval = ejabberd_config:get_local_option(
StartInterval = ejabberd_config:get_option(
{odbc_start_interval, Host},
fun(I) when is_integer(I), I>0 -> I end,
?DEFAULT_ODBC_START_INTERVAL),
@@ -93,3 +97,20 @@ remove_pid(Host, Pid) ->
mnesia:delete_object(#sql_pool{host = Host, pid = Pid})
end,
mnesia:ets(F).
transform_options(Opts) ->
lists:foldl(fun transform_options/2, [], Opts).
transform_options({odbc_server, {Type, Server, Port, DB, User, Pass}}, Opts) ->
[{odbc_type, Type},
{odbc_server, Server},
{odbc_port, Port},
{odbc_database, DB},
{odbc_username, User},
{odbc_password, Pass}|Opts];
transform_options({odbc_server, {mysql, Server, DB, User, Pass}}, Opts) ->
transform_options({odbc_server, {mysql, Server, ?MYSQL_PORT, DB, User, Pass}}, Opts);
transform_options({odbc_server, {pgsql, Server, DB, User, Pass}}, Opts) ->
transform_options({odbc_server, {pgsql, Server, ?PGSQL_PORT, DB, User, Pass}}, Opts);
transform_options(Opt, Opts) ->
[Opt|Opts].
+17 -7
View File
@@ -34,7 +34,8 @@
-include("logger.hrl").
start() ->
case lists:any(fun needs_odbc/1, ?MYHOSTS) of
case lists:any(fun(H) -> needs_odbc(H) /= false end,
?MYHOSTS) of
true ->
start_hosts();
false ->
@@ -45,14 +46,15 @@ start() ->
start_hosts() ->
lists:foreach(fun (Host) ->
case needs_odbc(Host) of
true -> start_odbc(Host);
{true, App} -> start_odbc(Host, App);
false -> ok
end
end,
?MYHOSTS).
%% Start the ODBC module on the given host
start_odbc(Host) ->
start_odbc(Host, App) ->
ejabberd:start_app(App),
Supervisor_name = gen_mod:get_module_proc(Host,
ejabberd_odbc_sup),
ChildSpec = {Supervisor_name,
@@ -64,11 +66,19 @@ start_odbc(Host) ->
?ERROR_MSG("Start of supervisor ~p failed:~n~p~nRetrying."
"..~n",
[Supervisor_name, _Error]),
start_odbc(Host)
start_odbc(Host, App)
end.
%% Returns true if we have configured odbc_server for the given host
%% Returns {true, App} if we have configured odbc for the given host
needs_odbc(Host) ->
LHost = jlib:nameprep(Host),
ejabberd_config:get_local_option(
{odbc_server, LHost}, fun(_) -> true end, false).
case ejabberd_config:get_option({odbc_type, LHost},
fun(mysql) -> mysql;
(pgsql) -> pgsql;
(odbc) -> odbc
end, undefined) of
mysql -> {true, p1_mysql};
pgsql -> {true, p1_pgsql};
odbc -> {true, odbc};
undefined -> false
end.
+4 -8
View File
@@ -387,14 +387,10 @@ do_route(OrigFrom, OrigTo, OrigPacket) ->
end.
get_component_number(LDomain) ->
case
ejabberd_config:get_local_option({domain_balancing_component_number,
LDomain}, fun(D) -> D end)
of
N when is_integer(N), N > 1 -> N;
_ -> undefined
end.
ejabberd_config:get_option(
{domain_balancing_component_number, LDomain},
fun(N) when is_integer(N), N > 1 -> N end,
undefined).
update_tables() ->
case catch mnesia:table_info(route, attributes) of
+42 -26
View File
@@ -45,7 +45,7 @@
handle_info/2, terminate/2, code_change/3]).
%% ejabberd API
-export([get_info_s2s_connections/1]).
-export([get_info_s2s_connections/1, transform_options/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -461,12 +461,12 @@ needed_connections_number(Ls, MaxS2SConnectionsNumber,
%% --------------------------------------------------------------------
is_service(From, To) ->
LFromDomain = From#jid.lserver,
case ejabberd_config:get_local_option(
case ejabberd_config:get_option(
{route_subdomains, LFromDomain},
fun(s2s) -> s2s end) of
fun(s2s) -> s2s; (local) -> local end, local) of
s2s -> % bypass RFC 3920 10.3
false;
undefined ->
local ->
Hosts = (?MYHOSTS),
P = fun (ParentDomain) ->
lists:member(ParentDomain, Hosts)
@@ -548,34 +548,50 @@ allow_host2(MyServer, S2SHost) ->
end.
allow_host1(MyHost, S2SHost) ->
case ejabberd_config:get_local_option(
{{s2s_host, S2SHost}, MyHost},
fun(deny) -> deny; (allow) -> allow end)
of
deny -> false;
allow -> true;
undefined ->
case ejabberd_config:get_local_option(
{s2s_default_policy, MyHost},
fun(deny) -> deny; (allow) -> allow end)
of
deny -> false;
_ ->
case ejabberd_hooks:run_fold(s2s_allow_host, MyHost,
allow, [MyHost, S2SHost])
of
deny -> false;
allow -> true;
_ -> true
end
end
Rule = ejabberd_config:get_option(
s2s_access,
fun(A) when is_atom(A) -> A end,
all),
JID = jlib:make_jid(<<"">>, S2SHost, <<"">>),
case acl:match_rule(MyHost, Rule, JID) of
deny -> false;
allow ->
case ejabberd_hooks:run_fold(s2s_allow_host, MyHost,
allow, [MyHost, S2SHost]) of
deny -> false;
allow -> true;
_ -> true
end
end.
transform_options(Opts) ->
lists:foldl(fun transform_options/2, [], Opts).
transform_options({{s2s_host, Host}, Action}, Opts) ->
?WARNING_MSG("Option 's2s_host' is deprecated. "
"The option is still supported but it is better to "
"fix your config: use access rules instead.", []),
ACLName = jlib:binary_to_atom(
iolist_to_binary(["s2s_access_", Host])),
[{acl, ACLName, {server, Host}},
{access, s2s, [{Action, ACLName}]},
{s2s_access, s2s} |
Opts];
transform_options({s2s_default_policy, Action}, Opts) ->
?WARNING_MSG("Option 's2s_default_policy' is deprecated. "
"The option is still supported but it is better to "
"fix your config: "
"use 's2s_access' with an access rule.", []),
[{access, s2s, [{Action, all}]},
{s2s_access, s2s} |
Opts];
transform_options(Opt, Opts) ->
[Opt|Opts].
%% Get information about S2S connections of the specified type.
%% @spec (Type) -> [Info]
%% where Type = in | out
%% Info = [{InfoName::atom(), InfoValue::any()}]
get_info_s2s_connections(Type) ->
ChildType = case Type of
in -> ejabberd_s2s_in_sup;
+20 -6
View File
@@ -149,7 +149,7 @@ init([{SockMod, Socket}, Opts]) ->
_ -> none
end,
{StartTLS, TLSRequired, TLSCertverify} =
case ejabberd_config:get_local_option(
case ejabberd_config:get_option(
s2s_use_starttls,
fun(false) -> false;
(true) -> true;
@@ -171,12 +171,16 @@ init([{SockMod, Socket}, Opts]) ->
required_trusted ->
{true, true, true}
end,
TLSOpts = case ejabberd_config:get_local_option(
TLSOpts1 = case ejabberd_config:get_option(
s2s_certfile,
fun iolist_to_binary/1) of
undefined -> [];
CertFile -> [{certfile, CertFile}]
end,
TLSOpts = case proplists:get_bool(tls_compression, Opts) of
false -> [compression_none | TLSOpts1];
true -> TLSOpts1
end,
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
{ok, wait_for_stream,
#state{socket = Socket, sockmod = SockMod,
@@ -289,7 +293,9 @@ wait_for_stream({xmlstreamstart, _Name, Attrs},
[Server])}),
{next_state, stream_established, StateData};
{<<"jabber:server">>, <<"jabber:server:dialback">>,
_Server, _} ->
_Server, _} when
(StateData#state.tls_required and StateData#state.tls_enabled)
or (not StateData#state.tls_required) ->
send_text(StateData, ?STREAM_HEADER(<<"">>)),
{next_state, stream_established, StateData};
_ ->
@@ -319,8 +325,8 @@ wait_for_feature_request({xmlstreamelement, El},
SockMod == gen_tcp ->
?DEBUG("starttls", []),
Socket = StateData#state.socket,
TLSOpts = case
ejabberd_config:get_local_option(
TLSOpts1 = case
ejabberd_config:get_option(
{domain_certfile, StateData#state.server},
fun iolist_to_binary/1) of
undefined -> StateData#state.tls_options;
@@ -328,6 +334,14 @@ wait_for_feature_request({xmlstreamelement, El},
[{certfile, CertFile} | lists:keydelete(certfile, 1,
StateData#state.tls_options)]
end,
TLSOpts = case ejabberd_config:get_option(
{s2s_tls_compression, StateData#state.server},
fun(true) -> true;
(false) -> false
end, true) of
true -> lists:delete(compression_none, TLSOpts1);
false -> [compression_none | TLSOpts1]
end,
TLSSocket = (StateData#state.sockmod):starttls(Socket,
TLSOpts,
xml:element_to_binary(#xmlel{name
@@ -831,7 +845,7 @@ fsm_limit_opts(Opts) ->
case lists:keysearch(max_fsm_queue, 1, Opts) of
{value, {_, N}} when is_integer(N) -> [{max_queue, N}];
_ ->
case ejabberd_config:get_local_option(
case ejabberd_config:get_option(
max_fsm_queue,
fun(I) when is_integer(I), I > 0 -> I end) of
undefined -> [];
+62 -25
View File
@@ -35,7 +35,8 @@
start_link/3,
start_connection/1,
terminate_if_waiting_delay/2,
stop_connection/1]).
stop_connection/1,
transform_options/1]).
%% p1_fsm callbacks (same as gen_fsm)
-export([init/1,
@@ -161,7 +162,7 @@ init([From, Server, Type]) ->
process_flag(trap_exit, true),
?DEBUG("started: ~p", [{From, Server, Type}]),
{TLS, TLSRequired} = case
ejabberd_config:get_local_option(
ejabberd_config:get_option(
s2s_use_starttls,
fun(true) -> true;
(false) -> false;
@@ -183,13 +184,21 @@ init([From, Server, Type]) ->
{true, true}
end,
UseV10 = TLS,
TLSOpts = case
ejabberd_config:get_local_option(
TLSOpts1 = case
ejabberd_config:get_option(
s2s_certfile, fun iolist_to_binary/1)
of
undefined -> [connect];
CertFile -> [{certfile, CertFile}, connect]
end,
TLSOpts = case ejabberd_config:get_option(
{s2s_tls_compression, From},
fun(true) -> true;
(false) -> false
end, true) of
false -> [compression_none | TLSOpts1];
true -> TLSOpts1
end,
{New, Verify} = case Type of
{new, Key} -> {Key, false};
{verify, Pid, Key, SID} ->
@@ -694,7 +703,7 @@ wait_for_starttls_proceed({xmlstreamelement, El},
[{StateData#state.myname, StateData#state.server}]),
Socket = StateData#state.socket,
TLSOpts = case
ejabberd_config:get_local_option(
ejabberd_config:get_option(
{domain_certfile, StateData#state.myname},
fun iolist_to_binary/1)
of
@@ -1134,16 +1143,15 @@ get_addr_port(Server) ->
end.
srv_lookup(Server) ->
Options = case
ejabberd_config:get_local_option(
s2s_dns_options, fun(L) when is_list(L) -> L end)
of
undefined -> [];
L -> L
end,
TimeoutMs = timer:seconds(proplists:get_value(timeout,
Options, 10)),
Retries = proplists:get_value(retries, Options, 2),
TimeoutMs = timer:seconds(
ejabberd_config:get_option(
s2s_dns_timeout,
fun(I) when is_integer(I), I>=0 -> I end,
10)),
Retries = ejabberd_config:get_option(
s2s_dns_retries,
fun(I) when is_integer(I), I>=0 -> I end,
2),
srv_lookup(binary_to_list(Server), TimeoutMs, Retries).
%% XXX - this behaviour is suboptimal in the case that the domain
@@ -1203,15 +1211,15 @@ get_addrs(Host, Family) ->
end.
outgoing_s2s_port() ->
ejabberd_config:get_local_option(
ejabberd_config:get_option(
outgoing_s2s_port,
fun(I) when is_integer(I), I > 0, I =< 65536 -> I end,
5269).
outgoing_s2s_families() ->
ejabberd_config:get_local_option(
outgoing_s2s_options,
fun({Families, _}) ->
ejabberd_config:get_option(
outgoing_s2s_families,
fun(Families) ->
true = lists:all(
fun(ipv4) -> true;
(ipv6) -> true
@@ -1220,14 +1228,43 @@ outgoing_s2s_families() ->
end, [ipv4, ipv6]).
outgoing_s2s_timeout() ->
ejabberd_config:get_local_option(
outgoing_s2s_options,
fun({_, TimeOut}) when is_integer(TimeOut), TimeOut > 0 ->
ejabberd_config:get_option(
outgoing_s2s_timeout,
fun(TimeOut) when is_integer(TimeOut), TimeOut > 0 ->
TimeOut;
({_, infinity}) ->
(infinity) ->
infinity
end, 10000).
transform_options(Opts) ->
lists:foldl(fun transform_options/2, [], Opts).
transform_options({outgoing_s2s_options, Families, Timeout}, Opts) ->
?WARNING_MSG("Option 'outgoing_s2s_options' is deprecated. "
"The option is still supported "
"but it is better to fix your config: "
"use 'outgoing_s2s_timeout' and "
"'outgoing_s2s_families' instead.", []),
[{outgoing_s2s_families, Families},
{outgoing_s2s_timeout, Timeout}
| Opts];
transform_options({s2s_dns_options, S2SDNSOpts}, AllOpts) ->
?WARNING_MSG("Option 's2s_dns_options' is deprecated. "
"The option is still supported "
"but it is better to fix your config: "
"use 's2s_dns_timeout' and "
"'s2s_dns_retries' instead", []),
lists:foldr(
fun({timeout, T}, AccOpts) ->
[{s2s_dns_timeout, T}|AccOpts];
({retries, R}, AccOpts) ->
[{s2s_dns_retries, R}|AccOpts];
(_, AccOpts) ->
AccOpts
end, AllOpts, S2SDNSOpts);
transform_options(Opt, Opts) ->
[Opt|Opts].
%% Human readable S2S logging: Log only new outgoing connections as INFO
%% Do not log dialback
log_s2s_out(false, _, _, _) -> ok;
@@ -1270,7 +1307,7 @@ wait_before_reconnect(StateData) ->
queue = queue:new()}}.
get_max_retry_delay() ->
case ejabberd_config:get_local_option(
case ejabberd_config:get_option(
s2s_max_retry_delay,
fun(I) when is_integer(I), I > 0 -> I end) of
undefined -> ?MAX_RETRY_DELAY;
@@ -1287,7 +1324,7 @@ terminate_if_waiting_delay(From, To) ->
Pids).
fsm_limit_opts() ->
case ejabberd_config:get_local_option(
case ejabberd_config:get_option(
max_fsm_queue,
fun(I) when is_integer(I), I > 0 -> I end) of
undefined -> [];
+32 -25
View File
@@ -34,7 +34,7 @@
%% External exports
-export([start/2, start_link/2, send_text/2,
send_element/2, socket_type/0]).
send_element/2, socket_type/0, transform_listen_option/2]).
%% gen_fsm callbacks
-export([init/1, wait_for_stream/2,
@@ -124,29 +124,18 @@ init([{SockMod, Socket}, Opts]) ->
{value, {_, A}} -> A;
_ -> all
end,
{Hosts, Password} = case lists:keysearch(hosts, 1, Opts)
of
{value, {_, Hs, HOpts}} ->
case lists:keysearch(password, 1, HOpts) of
{value, {_, P}} -> {Hs, P};
_ ->
% TODO: generate error
false
end;
_ ->
case lists:keysearch(host, 1, Opts) of
{value, {_, H, HOpts}} ->
case lists:keysearch(password, 1, HOpts) of
{value, {_, P}} -> {[H], P};
_ ->
% TODO: generate error
false
end;
_ ->
% TODO: generate error
false
end
end,
%% This should be improved probably
{Hosts, HostOpts} = case lists:keyfind(hosts, 1, Opts) of
{_, HOpts} ->
{[H || {H, _} <- HOpts],
lists:flatten(
[O || {_, O} <- HOpts])};
_ ->
{[], []}
end,
Password = gen_mod:get_opt(password, HostOpts,
fun iolist_to_binary/1,
p1_sha:sha(crypto:rand_bytes(20))),
Shaper = case lists:keysearch(shaper_rule, 1, Opts) of
{value, {_, S}} -> S;
_ -> none
@@ -384,12 +373,30 @@ send_element(StateData, El) ->
new_id() -> randoms:get_string().
transform_listen_option({hosts, Hosts, O}, Opts) ->
case lists:keyfind(hosts, 1, Opts) of
{_, PrevHostOpts} ->
NewHostOpts =
lists:foldl(
fun(H, Acc) ->
dict:append_list(H, O, Acc)
end, dict:from_list(PrevHostOpts), Hosts),
[{hosts, dict:to_list(NewHostOpts)}|
lists:keydelete(hosts, 1, Opts)];
_ ->
[{hosts, [{H, O} || H <- Hosts]}|Opts]
end;
transform_listen_option({host, Host, Os}, Opts) ->
transform_listen_option({hosts, [Host], Os}, Opts);
transform_listen_option(Opt, Opts) ->
[Opt|Opts].
fsm_limit_opts(Opts) ->
case lists:keysearch(max_fsm_queue, 1, Opts) of
{value, {_, N}} when is_integer(N) ->
[{max_queue, N}];
_ ->
case ejabberd_config:get_local_option(
case ejabberd_config:get_option(
max_fsm_queue,
fun(I) when is_integer(I), I > 0 -> I end) of
undefined -> [];
+2 -2
View File
@@ -53,7 +53,7 @@
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
LH = ejabberd_config:get_local_option(
LH = ejabberd_config:get_option(
watchdog_large_heap,
fun(I) when is_integer(I), I > 0 -> I end,
1000000),
@@ -200,7 +200,7 @@ send_message(From, To, Body) ->
[{xmlcdata, Body}]}]}).
get_admin_jids() ->
ejabberd_config:get_local_option(
ejabberd_config:get_option(
watchdog_admins,
fun(JIDs) ->
[jlib:jid_tolower(
+13 -13
View File
@@ -724,8 +724,8 @@ process_admin(Host,
auth = {_, _Auth, AJID}, q = Query, lang = Lang}) ->
SetAccess = fun (Rs) ->
mnesia:transaction(fun () ->
Os = mnesia:select(config,
[{{config,
Os = mnesia:select(local_config,
[{{local_config,
{access,
'$1',
Host},
@@ -739,7 +739,7 @@ process_admin(Host,
lists:foreach(fun ({access,
Name,
Rules}) ->
mnesia:write({config,
mnesia:write({local_config,
{access,
Name,
Host},
@@ -764,8 +764,8 @@ process_admin(Host,
end;
_ -> nothing
end,
Access = ets:select(config,
[{{config, {access, '$1', Host}, '$2'}, [],
Access = ets:select(local_config,
[{{local_config, {access, '$1', Host}, '$2'}, [],
[{{access, '$1', '$2'}}]}]),
{NumLines, AccessP} = term_to_paragraph(lists:keysort(2,Access), 80),
make_xhtml((?H1GL((?T(<<"Access Rules">>)),
@@ -798,8 +798,8 @@ process_admin(Host,
end;
_ -> nothing
end,
AccessRules = ets:select(config,
[{{config, {access, '$1', Host}, '$2'}, [],
AccessRules = ets:select(local_config,
[{{local_config, {access, '$1', Host}, '$2'}, [],
[{{access, '$1', '$2'}}]}]),
make_xhtml((?H1GL((?T(<<"Access Rules">>)),
<<"AccessRights">>, <<"Access Rights">>))
@@ -827,14 +827,14 @@ process_admin(Host,
{value, {_, String}} ->
case parse_access_rule(String) of
{ok, Rs} ->
ejabberd_config:add_global_option({access, Name, Host},
ejabberd_config:add_option({access, Name, Host},
Rs),
ok;
_ -> error
end;
_ -> nothing
end,
Rules = case ejabberd_config:get_global_option(
Rules = case ejabberd_config:get_option(
{access, Name, Host}, fun(V) -> V end)
of
undefined -> [];
@@ -1181,8 +1181,8 @@ access_rules_to_xhtml(AccessRules, Lang) ->
<<"Add New">>)])])]))]).
access_parse_query(Host, Query) ->
AccessRules = ets:select(config,
[{{config, {access, '$1', Host}, '$2'}, [],
AccessRules = ets:select(local_config,
[{{local_config, {access, '$1', Host}, '$2'}, [],
[{{access, '$1', '$2'}}]}]),
case lists:keysearch(<<"addnew">>, 1, Query) of
{value, _} ->
@@ -1198,7 +1198,7 @@ access_parse_addnew(_AccessRules, Host, Query) ->
case lists:keysearch(<<"namenew">>, 1, Query) of
{value, {_, String}} when String /= <<"">> ->
Name = jlib:binary_to_atom(String),
ejabberd_config:add_global_option({access, Name, Host},
ejabberd_config:add_option({access, Name, Host},
[]),
ok
end.
@@ -1210,7 +1210,7 @@ access_parse_delete(AccessRules, Host, Query) ->
case lists:member({<<"selected">>, ID}, Query) of
true ->
mnesia:transaction(fun () ->
mnesia:delete({config,
mnesia:delete({local_config,
{access,
Name,
Host}})
+160 -17
View File
@@ -28,10 +28,14 @@
-author('alexey@process-one.net').
-export([export/2, export/3]).
-include("logger.hrl").
-export([export/2, export/3, import_file/2, import/2, import/3]).
-define(MAX_RECORDS_PER_TRANSACTION, 100).
-record(dump, {fd, cont = start}).
%%%----------------------------------------------------------------------
%%% API
%%%----------------------------------------------------------------------
@@ -42,21 +46,24 @@
%%% - Output can be either odbc to export to the configured relational
%%% database or "Filename" to export to text file.
modules() ->
[ejabberd_auth,
mod_announce,
mod_caps,
mod_irc,
mod_last,
mod_muc,
mod_offline,
mod_privacy,
mod_private,
mod_roster,
mod_shared_roster,
mod_vcard,
mod_vcard_xupdate].
export(Server, Output) ->
LServer = jlib:nameprep(iolist_to_binary(Server)),
Modules = [ejabberd_auth,
mod_announce,
mod_caps,
mod_irc,
mod_last,
mod_muc,
mod_offline,
mod_privacy,
mod_private,
mod_roster,
mod_shared_roster,
mod_vcard,
mod_vcard_xupdate],
Modules = modules(),
IO = prepare_output(Output),
lists:foreach(
fun(Module) ->
@@ -73,6 +80,50 @@ export(Server, Output, Module) ->
end, Module:export(Server)),
close_output(Output, IO).
import_file(Server, FileName) when is_binary(FileName) ->
import(Server, binary_to_list(FileName));
import_file(Server, FileName) ->
case disk_log:open([{name, make_ref()},
{file, FileName},
{mode, read_only}]) of
{ok, Fd} ->
LServer = jlib:nameprep(Server),
Mods = [{Mod, gen_mod:db_type(LServer, Mod)}
|| Mod <- modules(), gen_mod:is_loaded(LServer, Mod)],
AuthMods = case lists:member(ejabberd_auth_internal,
ejabberd_auth:auth_modules(LServer)) of
true ->
[{ejabberd_auth, mnesia}];
false ->
[]
end,
import_dump(LServer, AuthMods ++ Mods, #dump{fd = Fd});
Err ->
exit(Err)
end.
import(Server, Output) ->
import(Server, Output, [{fast, true}]).
import(Server, Output, Opts) ->
LServer = jlib:nameprep(iolist_to_binary(Server)),
Modules = modules(),
IO = prepare_output(Output, disk_log),
lists:foreach(
fun(Module) ->
import(LServer, IO, Opts, Module)
end, Modules),
close_output(Output, IO).
import(Server, Output, Opts, Module) ->
LServer = jlib:nameprep(iolist_to_binary(Server)),
IO = prepare_output(Output, disk_log),
lists:foreach(
fun({SelectQuery, ConvertFun}) ->
import(LServer, SelectQuery, IO, ConvertFun, Opts)
end, Module:import(Server)),
close_output(Output, IO).
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
@@ -109,18 +160,110 @@ output(_LServer, Table, Fd, SQLs) ->
file:write(Fd, ["-- \n-- Mnesia table: ", atom_to_list(Table),
"\n--\n", SQLs]).
prepare_output(FileName) when is_list(FileName); is_binary(FileName) ->
import(LServer, SelectQuery, IO, ConvertFun, Opts) ->
F = case proplists:get_bool(fast, Opts) of
true ->
fun() ->
case ejabberd_odbc:sql_query_t(SelectQuery) of
{selected, _, Rows} ->
lists:foldl(fun process_sql_row/2,
{IO, ConvertFun, undefined}, Rows);
Err ->
erlang:error(Err)
end
end;
false ->
fun() ->
ejabberd_odbc:sql_query_t(
[iolist_to_binary(
[<<"declare c cursor for ">>, SelectQuery])]),
fetch(IO, ConvertFun, undefined)
end
end,
ejabberd_odbc:sql_transaction(LServer, F).
fetch(IO, ConvertFun, PrevRow) ->
case ejabberd_odbc:sql_query_t([<<"fetch c;">>]) of
{selected, _, [Row]} ->
process_sql_row(Row, {IO, ConvertFun, PrevRow}),
fetch(IO, ConvertFun, Row);
{selected, _, []} ->
ok;
Err ->
erlang:error(Err)
end.
process_sql_row(Row, {IO, ConvertFun, PrevRow}) when Row == PrevRow ->
%% Avoid calling ConvertFun with the same input
{IO, ConvertFun, Row};
process_sql_row(Row, {IO, ConvertFun, _PrevRow}) ->
case catch ConvertFun(Row) of
{'EXIT', _} = Err ->
?ERROR_MSG("failed to convert ~p: ~p", [Row, Err]);
Term ->
ok = disk_log:log(IO#dump.fd, Term)
end,
{IO, ConvertFun, Row}.
import_dump(LServer, Mods, #dump{fd = Fd, cont = Cont}) ->
case disk_log:chunk(Fd, Cont) of
{NewCont, Terms} ->
import_terms(LServer, Mods, Terms),
import_dump(LServer, Mods, #dump{fd = Fd, cont = NewCont});
eof ->
ok;
Err ->
exit(Err)
end.
import_terms(LServer, Mods, [Term|Terms]) ->
import_term(LServer, Mods, Term),
import_terms(LServer, Mods, Terms);
import_terms(_LServer, _Mods, []) ->
ok.
import_term(LServer, [{Mod, DBType}|Mods], Term) ->
case catch Mod:import(LServer, DBType, Term) of
pass -> import_term(LServer, Mods, Term);
ok -> ok;
Err ->
?ERROR_MSG("failed to import ~p for module ~p: ~p",
[Term, Mod, Err])
end;
import_term(_LServer, [], _Term) ->
ok.
prepare_output(FileName) ->
prepare_output(FileName, normal).
prepare_output(FileName, Type) when is_binary(FileName) ->
prepare_output(binary_to_list(FileName), Type);
prepare_output(FileName, normal) when is_list(FileName) ->
case file:open(FileName, [write, raw]) of
{ok, Fd} ->
Fd;
Err ->
exit(Err)
end;
prepare_output(Output) ->
prepare_output(FileName, disk_log) when is_list(FileName) ->
case disk_log:open([{name, make_ref()},
{repair, truncate},
{file, FileName}]) of
{ok, Fd} ->
#dump{fd = Fd};
Err ->
exit(Err)
end;
prepare_output(Output, _Type) ->
Output.
close_output(FileName, Fd) when FileName /= Fd ->
file:close(Fd),
case Fd of
#dump{} ->
disk_log:close(Fd#dump.fd);
_ ->
file:close(Fd)
end,
ok;
close_output(_, _) ->
ok.
+1 -1
View File
@@ -182,7 +182,7 @@ get_opt({Key, Host}, Opts, F) ->
get_opt({Key, Host}, Opts, F, Default) ->
case gen_mod:get_opt(Key, Opts, F, undefined) of
undefined ->
ejabberd_config:get_local_option(
ejabberd_config:get_option(
{Key, Host}, F, Default);
Val ->
Val
+8 -14
View File
@@ -70,28 +70,28 @@ get_process_name(Host, Integer) ->
eauth).
check_password(User, Server, Password) ->
call_port(Server, ["auth", User, Server, Password]).
call_port(Server, [<<"auth">>, User, Server, Password]).
is_user_exists(User, Server) ->
call_port(Server, ["isuser", User, Server]).
call_port(Server, [<<"isuser">>, User, Server]).
set_password(User, Server, Password) ->
call_port(Server, ["setpass", User, Server, Password]).
call_port(Server, [<<"setpass">>, User, Server, Password]).
try_register(User, Server, Password) ->
case call_port(Server,
["tryregister", User, Server, Password])
[<<"tryregister">>, User, Server, Password])
of
true -> {atomic, ok};
false -> {error, not_allowed}
end.
remove_user(User, Server) ->
call_port(Server, ["removeuser", User, Server]).
call_port(Server, [<<"removeuser">>, User, Server]).
remove_user(User, Server, Password) ->
call_port(Server,
["removeuser3", User, Server, Password]).
[<<"removeuser3">>, User, Server, Password]).
call_port(Server, Msg) ->
LServer = jlib:nameprep(Server),
@@ -106,7 +106,7 @@ random_instance(MaxNum) ->
random:uniform(MaxNum) - 1.
get_instances(Server) ->
ejabberd_config:get_local_option(
ejabberd_config:get_option(
{extauth_instances, Server},
fun(V) when is_integer(V), V > 0 ->
V
@@ -154,13 +154,7 @@ flush_buffer_and_forward_messages(Pid) ->
after 0 -> true
end.
join(List, Sep) ->
lists:foldl(fun (A, "") -> A;
(A, Acc) -> Acc ++ Sep ++ A
end,
"", List).
encode(L) -> join(L, ":").
encode(L) -> str:join(L, <<":">>).
decode([0, 0]) -> false;
decode([0, 1]) -> true.
+23 -2
View File
@@ -33,7 +33,7 @@
%% API
-export([start_link/3, add_iq_handler/6,
remove_iq_handler/3, stop_iq_handler/3, handle/7,
process_iq/6]).
process_iq/6, check_type/1, transform_module_options/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2,
@@ -45,6 +45,10 @@
-record(state, {host, module, function}).
-type component() :: ejabberd_sm | ejabberd_local.
-type type() :: no_queue | one_queue | pos_integer() | parallel.
-type opts() :: no_queue | {one_queue, pid()} | {queues, [pid()]} | parallel.
%%====================================================================
%% API
%%====================================================================
@@ -67,7 +71,7 @@ add_iq_handler(Component, Host, NS, Module, Function,
[Host, Module, Function]),
Component:register_iq_handler(Host, NS, Module,
Function, {one_queue, Pid});
{queues, N} ->
N when is_integer(N) ->
Pids = lists:map(fun (_) ->
{ok, Pid} =
supervisor:start_child(ejabberd_iq_sup,
@@ -122,6 +126,23 @@ process_iq(_Host, Module, Function, From, To, IQ) ->
end
end.
-spec check_type(type()) -> type().
check_type(no_queue) -> no_queue;
check_type(one_queue) -> one_queue;
check_type(N) when is_integer(N), N>0 -> N;
check_type(parallel) -> parallel.
-spec transform_module_options([{atom(), any()}]) -> [{atom(), any()}].
transform_module_options(Opts) ->
lists:map(
fun({iqdisc, {queues, N}}) ->
{iqdisc, N};
(Opt) ->
Opt
end, Opts).
%%====================================================================
%% gen_server callbacks
%%====================================================================
+1 -22
View File
@@ -64,13 +64,11 @@ start() ->
-spec start_module(binary(), atom(), opts()) -> any().
start_module(Host, Module, Opts) ->
set_module_opts_mnesia(Host, Module, Opts),
ets:insert(ejabberd_modules,
#ejabberd_module{module_host = {Module, Host},
opts = Opts}),
try Module:start(Host, 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 "
@@ -101,7 +99,7 @@ is_app_running(AppName) ->
stop_module(Host, Module) ->
case stop_module_keep_config(Host, Module) of
error -> error;
ok -> del_module_mnesia(Host, Module)
ok -> ok
end.
%% @doc Stop the module in a host, but keep its configuration.
@@ -232,25 +230,6 @@ loaded_modules_with_opts(Host) ->
opts = '$2'},
[], [{{'$1', '$2'}}]}]).
set_module_opts_mnesia(Host, Module, Opts) ->
Modules = ejabberd_config:get_local_option(
{modules, Host},
fun(Ls) when is_list(Ls) -> Ls end,
[]),
Modules1 = lists:keydelete(Module, 1, Modules),
Modules2 = [{Module, Opts} | Modules1],
ejabberd_config:add_local_option({modules, Host},
Modules2).
del_module_mnesia(Host, Module) ->
Modules = ejabberd_config:get_local_option(
{modules, Host},
fun(Ls) when is_list(Ls) -> Ls end,
[]),
Modules1 = lists:keydelete(Module, 1, Modules),
ejabberd_config:add_local_option({modules, Host},
Modules1).
-spec get_hosts(opts(), binary()) -> [binary()].
get_hosts(Opts, Prefix) ->
+23 -4
View File
@@ -28,18 +28,21 @@
-author('alexey@process-one.net').
%%-compile(export_all).
-export([domain_utf8_to_ascii/1,
domain_ucs2_to_ascii/1,
utf8_to_ucs2/1]).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.
-spec domain_utf8_to_ascii(binary()) -> false | binary().
domain_utf8_to_ascii(Domain) ->
domain_ucs2_to_ascii(utf8_to_ucs2(Domain)).
utf8_to_ucs2(S) ->
list_to_binary(utf8_to_ucs2(binary_to_list(S), "")).
utf8_to_ucs2(binary_to_list(S), "").
utf8_to_ucs2([], R) -> lists:reverse(R);
utf8_to_ucs2([C | S], R) when C < 128 ->
@@ -51,10 +54,10 @@ utf8_to_ucs2([C1, C2, C3 | S], R) when C1 < 240 ->
[C1 band 15 bsl 12 bor (C2 band 63 bsl 6) bor C3 band 63
| R]).
-spec domain_ucs2_to_ascii(binary()) -> false | binary().
-spec domain_ucs2_to_ascii(list()) -> false | binary().
domain_ucs2_to_ascii(Domain) ->
case catch domain_ucs2_to_ascii1(binary_to_list(Domain)) of
case catch domain_ucs2_to_ascii1(Domain) of
{'EXIT', _Reason} -> false;
Res -> iolist_to_binary(Res)
end.
@@ -204,3 +207,19 @@ codepoint(C) ->
if (0 =< C) and (C =< 25) -> C + 97;
(26 =< C) and (C =< 35) -> C + 22
end.
%%%===================================================================
%%% Unit tests
%%%===================================================================
-ifdef(TEST).
acsii_test() ->
?assertEqual(<<"test.org">>, domain_utf8_to_ascii(<<"test.org">>)).
utf8_test() ->
?assertEqual(
<<"xn--d1acufc.xn--p1ai">>,
domain_utf8_to_ascii(
<<208,180,208,190,208,188,208,181,208,189,46,209,128,209,132>>)).
-endif.
+19 -5
View File
@@ -48,9 +48,10 @@
now_to_local_string/1, datetime_string_to_timestamp/1,
decode_base64/1, encode_base64/1, ip_to_list/1,
rsm_encode/1, rsm_encode/2, rsm_decode/1,
binary_to_integer/1, binary_to_integer/2,
integer_to_binary/1, integer_to_binary/2,
atom_to_binary/1, binary_to_atom/1, tuple_to_binary/1]).
binary_to_integer/1, binary_to_integer/2,
integer_to_binary/1, integer_to_binary/2,
atom_to_binary/1, binary_to_atom/1, tuple_to_binary/1,
l2i/1, i2l/1, i2l/2]).
%% TODO: Remove once XEP-0091 is Obsolete
%% TODO: Remove once XEP-0091 is Obsolete
@@ -599,8 +600,6 @@ rsm_encode_count(Count, Arr) ->
children = [{xmlcdata, i2l(Count)}]}
| Arr].
i2l(I) when is_integer(I) -> integer_to_binary(I).
-type tz() :: {binary(), {integer(), integer()}} | {integer(), integer()} | utc.
%% Timezone = utc | {Sign::string(), {Hours, Minutes}} | {Hours, Minutes}
@@ -880,3 +879,18 @@ tuple_to_binary(T) ->
atom_to_binary(A) ->
erlang:atom_to_binary(A, utf8).
l2i(I) when is_integer(I) -> I;
l2i(L) when is_binary(L) -> binary_to_integer(L).
i2l(I) when is_integer(I) -> integer_to_binary(I);
i2l(L) when is_binary(L) -> L.
i2l(I, N) when is_integer(I) -> i2l(i2l(I), N);
i2l(L, N) when is_binary(L) ->
case str:len(L) of
N -> L;
C when C > N -> L;
_ -> i2l(<<$0, L/binary>>, N)
end.
+21 -1
View File
@@ -35,7 +35,9 @@
-export([start/2,
init/0,
stop/1,
export/1,
export/1,
import/1,
import/3,
announce/3,
send_motd/1,
disco_identity/5,
@@ -1072,3 +1074,21 @@ export(_Server) ->
(_Host, _R) ->
[]
end}].
import(LServer) ->
[{<<"select xml from motd where username='';">>,
fun([XML]) ->
El = xml_stream:parse_element(XML),
#motd{server = LServer, packet = El}
end},
{<<"select username from motd where xml='';">>,
fun([LUser]) ->
#motd_users{us = {LUser, LServer}}
end}].
import(_LServer, mnesia, #motd{} = Motd) ->
mnesia:dirty_write(Motd);
import(_LServer, mnesia, #motd_users{} = Users) ->
mnesia:dirty_write(Users);
import(_, _, _) ->
pass.
+1 -1
View File
@@ -3,7 +3,7 @@
%%% Author : Eric Cestari <ecestari@process-one.net>
%%% Purpose : Message Carbons XEP-0280 0.8
%%% Created : 5 May 2008 by Mickael Remond <mremond@process-one.net>
%%% Usage : Add the following line in modules section of ejabberd.cfg:
%%% Usage : Add the following line in modules section of ejabberd.yml:
%%% {mod_carboncopy, []}
%%%
%%%
+5 -5
View File
@@ -1365,8 +1365,8 @@ get_form(Host, [<<"config">>, <<"access">>], Lang) ->
[{xmlcdata, S}]}
end,
str:tokens(iolist_to_binary(io_lib:format("~p.",
[ets:select(config,
[{{config,
[ets:select(local_config,
[{{local_config,
{access,
'$1',
'$2'},
@@ -1770,8 +1770,8 @@ set_form(_From, Host, [<<"config">>, <<"access">>],
_Lang, XData) ->
SetAccess = fun (Rs) ->
mnesia:transaction(fun () ->
Os = mnesia:select(config,
[{{config,
Os = mnesia:select(local_config,
[{{local_config,
{access,
'$1',
'$2'},
@@ -1787,7 +1787,7 @@ set_form(_From, Host, [<<"config">>, <<"access">>],
lists:foreach(fun ({access,
Name,
Rules}) ->
mnesia:write({config,
mnesia:write({local_config,
{access,
Name,
Host},
+4 -4
View File
@@ -129,7 +129,7 @@ process_get(#xmlel{name = <<"info">>}) ->
children = []}};
process_get(#xmlel{name = <<"welcome-message">>,
attrs = Attrs}) ->
{Subj, Body} = ejabberd_config:get_local_option(
{Subj, Body} = ejabberd_config:get_option(
welcome_message,
fun({Subj, Body}) ->
{iolist_to_binary(Subj),
@@ -145,7 +145,7 @@ process_get(#xmlel{name = <<"welcome-message">>,
children = [{xmlcdata, Body}]}]}};
process_get(#xmlel{name = <<"registration-watchers">>,
attrs = Attrs}) ->
SubEls = ejabberd_config:get_local_option(
SubEls = ejabberd_config:get_option(
registration_watchers,
fun(JIDs) when is_list(JIDs) ->
lists:map(
@@ -167,8 +167,8 @@ process_get(#xmlel{name = <<"acls">>, attrs = Attrs}) ->
process_get(#xmlel{name = <<"access">>,
attrs = Attrs}) ->
Str = iolist_to_binary(io_lib:format("~p.",
[ets:select(config,
[{{config, {access, '$1'},
[ets:select(local_config,
[{{local_config, {access, '$1'},
'$2'},
[],
[{{access, '$1',
+29 -4
View File
@@ -36,7 +36,8 @@
process_sm_iq_items/3, process_sm_iq_info/3,
get_sm_identity/5, get_sm_features/5, get_sm_items/5,
get_info/5, register_feature/2, unregister_feature/2,
register_extra_domain/2, unregister_extra_domain/2]).
register_extra_domain/2, unregister_extra_domain/2,
transform_module_options/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -440,6 +441,22 @@ get_user_resources(User, Server) ->
end,
lists:sort(Rs)).
transform_module_options(Opts) ->
lists:map(
fun({server_info, Infos}) ->
NewInfos = lists:map(
fun({Modules, Name, URLs}) ->
[[{modules, Modules},
{name, Name},
{urls, URLs}]];
(Opt) ->
Opt
end, Infos),
{server_info, NewInfos};
(Opt) ->
Opt
end, Opts).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Support for: XEP-0157 Contact Addresses for XMPP Services
@@ -465,9 +482,17 @@ get_info(_A, Host, Mod, Node, _Lang) when Node == <<>> ->
get_info(Acc, _, _, _Node, _) -> Acc.
get_fields_xml(Host, Module) ->
Fields = gen_mod:get_module_opt(Host, ?MODULE, server_info,
fun(L) when is_list(L) -> L end,
[]),
Fields = gen_mod:get_module_opt(
Host, ?MODULE, server_info,
fun(L) ->
lists:map(
fun(Opts) ->
Mods = proplists:get_value(modules, Opts, all),
Name = proplists:get_value(names, Opts, <<>>),
URLs = proplists:get_value(urls, Opts, []),
{Mods, Name, URLs}
end, lists:flatmap(L))
end, []),
Fields_good = lists:filter(fun ({Modules, _, _}) ->
case Modules of
all -> true;
+16 -2
View File
@@ -33,8 +33,8 @@
-behaviour(gen_mod).
%% API
-export([start_link/2, start/2, stop/1, export/1,
closed_connection/3, get_connection_params/3]).
-export([start_link/2, start/2, stop/1, export/1, import/1,
import/3, closed_connection/3, get_connection_params/3]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2,
@@ -1288,3 +1288,17 @@ export(_Server) ->
[]
end
end}].
import(_LServer) ->
[{<<"select jid, host, data from irc_custom;">>,
fun([SJID, IRCHost, SData]) ->
#jid{luser = U, lserver = S} = jlib:string_to_jid(SJID),
Data = ejabberd_odbc:decode_term(SData),
#irc_custom{us_host = {{U, S}, IRCHost},
data = Data}
end}].
import(_LServer, mnesia, #irc_custom{} = R) ->
mnesia:dirty_write(R);
import(_, _, _) ->
pass.
+31 -11
View File
@@ -31,8 +31,9 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process_local_iq/3, export/1,
process_sm_iq/3, on_presence_update/4,
store_last_info/4, get_last_info/2, remove_user/2]).
process_sm_iq/3, on_presence_update/4, import/1, import/3,
store_last_info/4, get_last_info/2, remove_user/2,
transform_options/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -101,18 +102,13 @@ process_local_iq(_From, _To,
%% @doc Get the uptime of the ejabberd node, expressed in seconds.
%% When ejabberd is starting, ejabberd_config:start/0 stores the datetime.
get_node_uptime() ->
case ejabberd_config:get_local_option(
case ejabberd_config:get_option(
node_start,
fun({MegaSecs, Secs, MicroSecs} = Now)
when is_integer(MegaSecs), MegaSecs >= 0,
is_integer(Secs), Secs >= 0,
is_integer(MicroSecs), MicroSecs >= 0 ->
Now
end) of
fun(S) when is_integer(S), S >= 0 -> S end) of
undefined ->
trunc(element(1, erlang:statistics(wall_clock)) / 1000);
StartNow ->
now_to_seconds(now()) - now_to_seconds(StartNow)
Now ->
now_to_seconds(now()) - Now
end.
now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
@@ -305,3 +301,27 @@ export(_Server) ->
(_Host, _R) ->
[]
end}].
import(LServer) ->
[{<<"select username, seconds, state from last">>,
fun([LUser, TimeStamp, State]) ->
#last_activity{us = {LUser, LServer},
timestamp = jlib:binary_to_integer(
TimeStamp),
status = State}
end}].
import(_LServer, mnesia, #last_activity{} = LA) ->
mnesia:dirty_write(LA);
import(_, _, _) ->
pass.
transform_options(Opts) ->
lists:foldl(fun transform_options/2, [], Opts).
transform_options({node_start, {_, _, _} = Now}, Opts) ->
?WARNING_MSG("Old 'node_start' format detected. This is still supported "
"but it is better to fix your config.", []),
[{node_start, now_to_seconds(Now)}|Opts];
transform_options(Opt, Opts) ->
[Opt|Opts].
+64
View File
@@ -44,6 +44,9 @@
shutdown_rooms/1,
process_iq_disco_items/4,
broadcast_service_message/2,
export/1,
import/1,
import/3,
can_use_nick/4]).
%% gen_server callbacks
@@ -1141,3 +1144,64 @@ update_muc_registered_table(_Host) ->
?INFO_MSG("Recreating muc_registered table", []),
mnesia:transform_table(muc_registered, ignore, Fields)
end.
export(_Server) ->
[{muc_room,
fun(Host, #muc_room{name_host = {Name, RoomHost}, opts = Opts}) ->
case str:suffix(Host, RoomHost) of
true ->
SName = ejabberd_odbc:escape(Name),
SRoomHost = ejabberd_odbc:escape(RoomHost),
SOpts = ejabberd_odbc:encode_term(Opts),
[[<<"delete from muc_room where name='">>, SName,
<<"' and host='">>, SRoomHost, <<"';">>],
[<<"insert into muc_room(name, host, opts) ",
"values (">>,
<<"'">>, SName, <<"', '">>, SRoomHost,
<<"', '">>, SOpts, <<"');">>]];
false ->
[]
end
end},
{muc_registered,
fun(Host, #muc_registered{us_host = {{U, S}, RoomHost},
nick = Nick}) ->
case str:suffix(Host, RoomHost) of
true ->
SJID = ejabberd_odbc:escape(
jlib:jid_to_string(
jlib:make_jid(U, S, <<"">>))),
SNick = ejabberd_odbc:escape(Nick),
SRoomHost = ejabberd_odbc:escape(RoomHost),
[[<<"delete from muc_registered where jid='">>,
SJID, <<"' and host='">>, SRoomHost, <<"';">>],
[<<"insert into muc_registered(jid, host, "
"nick) values ('">>,
SJID, <<"', '">>, SRoomHost, <<"', '">>, SNick,
<<"');">>]];
false ->
[]
end
end}].
import(_LServer) ->
[{<<"select name, host, opts from muc_room;">>,
fun([Name, RoomHost, SOpts]) ->
Opts = opts_to_binary(ejabberd_odbc:decode_term(SOpts)),
#muc_room{name_host = {Name, RoomHost},
opts = Opts}
end},
{<<"select jid, host, nick from muc_registered;">>,
fun([J, RoomHost, Nick]) ->
#jid{user = U, server = S} =
jlib:string_to_jid(J),
#muc_registered{us_host = {{U, S}, RoomHost},
nick = Nick}
end}].
import(_LServer, mnesia, #muc_room{} = R) ->
mnesia:dirty_write(R);
import(_LServer, mnesia, #muc_registered{} = R) ->
mnesia:dirty_write(R);
import(_, _, _) ->
pass.
+11 -3
View File
@@ -33,7 +33,7 @@
-behaviour(gen_mod).
%% API
-export([start_link/2, start/2, stop/1,
-export([start_link/2, start/2, stop/1, transform_module_options/1,
check_access_log/2, add_to_log/5]).
%% gen_server callbacks
@@ -111,6 +111,14 @@ check_access_log(Host, From) ->
Res -> Res
end.
transform_module_options(Opts) ->
lists:map(
fun({top_link, {S1, S2}}) ->
{top_link, [{S1, S2}]};
(Opt) ->
Opt
end, Opts).
%%====================================================================
%% gen_server callbacks
%%====================================================================
@@ -152,14 +160,14 @@ init([Host, Opts]) ->
(universal) -> universal
end, local),
Top_link = gen_mod:get_opt(top_link, Opts,
fun({S1, S2}) ->
fun([{S1, S2}]) ->
{iolist_to_binary(S1),
iolist_to_binary(S2)}
end, {<<"/">>, <<"Home">>}),
NoFollow = gen_mod:get_opt(spam_prevention, Opts,
fun(B) when is_boolean(B) -> B end,
true),
Lang = ejabberd_config:get_local_option(
Lang = ejabberd_config:get_option(
{language, Host},
fun iolist_to_binary/1,
?MYLANG),
+19 -3
View File
@@ -943,9 +943,14 @@ process_groupchat_message(From,
Packet)
end,
?DICT:to_list(StateData#state.users)),
NewStateData2 = add_message_to_history(FromNick, From,
NewStateData2 = case has_body_or_subject(Packet) of
true ->
add_message_to_history(FromNick, From,
Packet,
NewStateData1),
NewStateData1);
false ->
NewStateData1
end,
{next_state, normal_state, NewStateData2};
_ ->
Err = case
@@ -2500,7 +2505,8 @@ process_iq_admin(From, get, Lang, SubEl, StateData) ->
{'EXIT', _} -> {error, ?ERR_BAD_REQUEST};
SAffiliation ->
if (FAffiliation == owner) or
(FAffiliation == admin) ->
(FAffiliation == admin) or
((FAffiliation == member) and (SAffiliation == member)) ->
Items = items_with_affiliation(SAffiliation,
StateData),
{result, Items, StateData};
@@ -4462,3 +4468,13 @@ tab_count_user(JID) ->
element_size(El) ->
byte_size(xml:element_to_binary(El)).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Detect messange stanzas that don't have meaninful content
has_body_or_subject(Packet) ->
[] /= lists:dropwhile(fun
(#xmlel{name = <<"body">>}) -> false;
(#xmlel{name = <<"subject">>}) -> false;
(_) -> true
end, Packet#xmlel.children).
+64
View File
@@ -42,6 +42,9 @@
remove_expired_messages/1,
remove_old_messages/2,
remove_user/2,
import/1,
import/3,
export/1,
get_queue_length/2,
get_offline_els/2,
webadmin_page/3,
@@ -873,3 +876,64 @@ count_offline_messages(LUser, LServer) ->
_ ->
0
end.
export(_Server) ->
[{offline_msg,
fun(Host, #offline_msg{us = {LUser, LServer},
timestamp = TimeStamp, from = From, to = To,
packet = Packet})
when LServer == Host ->
Username = ejabberd_odbc:escape(LUser),
#xmlel{name = Name, attrs = Attrs, children = Els} =
Packet,
Attrs2 =
jlib:replace_from_to_attrs(jlib:jid_to_string(From),
jlib:jid_to_string(To),
Attrs),
NewPacket = #xmlel{name = Name, attrs = Attrs2,
children =
Els ++
[jlib:timestamp_to_xml(
calendar:now_to_universal_time(TimeStamp),
utc,
jlib:make_jid(<<"">>,
LServer,
<<"">>),
<<"Offline Storage">>),
jlib:timestamp_to_xml(
calendar:now_to_universal_time(TimeStamp))]},
XML =
ejabberd_odbc:escape(xml:element_to_binary(NewPacket)),
[[<<"delete from spool where username='">>, Username, <<"';">>],
[<<"insert into spool(username, xml) values ('">>,
Username, <<"', '">>, XML, <<"');">>]];
(_Host, _R) ->
[]
end}].
import(LServer) ->
[{<<"select username, xml from spool;">>,
fun([LUser, XML]) ->
El = #xmlel{} = xml_stream:parse_element(XML),
From = #jid{} = jlib:string_to_jid(
xml:get_attr_s(<<"from">>, El#xmlel.attrs)),
To = #jid{} = jlib:string_to_jid(
xml:get_attr_s(<<"to">>, El#xmlel.attrs)),
Stamp = xml:get_path_s(El, [{elem, <<"delay">>},
{attr, <<"stamp">>}]),
TS = case jlib:datetime_string_to_timestamp(Stamp) of
{_, _, _} = Now ->
Now;
undefined ->
now()
end,
Expire = find_x_expire(TS, El#xmlel.children),
#offline_msg{us = {LUser, LServer},
from = From, to = To,
timestamp = TS, expire = Expire}
end}].
import(_LServer, mnesia, #offline_msg{} = Msg) ->
mnesia:dirty_write(Msg);
import(_, _, _) ->
pass.
+41 -2
View File
@@ -30,11 +30,11 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process_iq/3, export/1,
-export([start/2, stop/1, process_iq/3, export/1, import/1,
process_iq_set/4, process_iq_get/5, get_user_list/3,
check_packet/6, remove_user/2, item_to_raw/1,
raw_to_item/1, is_list_needdb/1, updated_list/3,
item_to_xml/1, get_user_lists/2]).
item_to_xml/1, get_user_lists/2, import/3]).
%% For mod_blocking
-export([sql_add_privacy_list/2,
@@ -965,6 +965,11 @@ sql_get_privacy_list_data(LUser, LServer, Name) ->
odbc_queries:get_privacy_list_data(LServer, Username,
SName).
sql_get_privacy_list_data_t(LUser, Name) ->
Username = ejabberd_odbc:escape(LUser),
SName = ejabberd_odbc:escape(Name),
odbc_queries:get_privacy_list_data_t(Username, SName).
sql_get_privacy_list_data_by_id(ID, LServer) ->
odbc_queries:get_privacy_list_data_by_id(LServer, ID).
@@ -1098,3 +1103,37 @@ get_id() ->
ID = get(id),
put(id, ID + 1),
ID + 1.
import(LServer) ->
[{<<"select username from privacy_list;">>,
fun([LUser]) ->
Default = case sql_get_default_privacy_list_t(LUser) of
{selected, [<<"name">>], []} ->
none;
{selected, [<<"name">>], [[DefName]]} ->
DefName;
_ ->
none
end,
{selected, [<<"name">>], Names} =
sql_get_privacy_list_names_t(LUser),
Lists = lists:flatmap(
fun([Name]) ->
case sql_get_privacy_list_data_t(LUser, Name) of
{selected, _, RItems} ->
[{Name,
lists:map(fun raw_to_item/1,
RItems)}];
_ ->
[]
end
end, Names),
#privacy{default = Default,
us = {LUser, LServer},
lists = Lists}
end}].
import(_LServer, mnesia, #privacy{} = P) ->
mnesia:dirty_write(P);
import(_, _, _) ->
pass.
+15 -2
View File
@@ -30,8 +30,8 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process_sm_iq/3,
remove_user/2, get_data/2, export/1]).
-export([start/2, stop/1, process_sm_iq/3, import/3,
remove_user/2, get_data/2, export/1, import/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -277,3 +277,16 @@ export(_Server) ->
(_Host, _R) ->
[]
end}].
import(LServer) ->
[{<<"select username, namespace, data from private_storage;">>,
fun([LUser, XMLNS, XML]) ->
El = #xmlel{} = xml_stream:parse_element(XML),
#private_storage{usns = {LUser, LServer, XMLNS},
xml = El}
end}].
import(_LServer, mnesia, #private_storage{} = PS) ->
mnesia:dirty_write(PS);
import(_, _, _) ->
pass.
+4 -1
View File
@@ -33,7 +33,7 @@
-behaviour(supervisor).
%% gen_mod callbacks.
-export([start/2, stop/1]).
-export([start/2, stop/1, transform_module_options/1]).
%% supervisor callbacks.
-export([init/1]).
@@ -64,6 +64,9 @@ start_link(Host, Opts) ->
supervisor:start_link({local, Proc}, ?MODULE,
[Host, Opts]).
transform_module_options(Opts) ->
mod_proxy65_service:transform_module_options(Opts).
init([Host, Opts]) ->
Service = {mod_proxy65_service,
{mod_proxy65_service, start_link, [Host, Opts]},
+17 -8
View File
@@ -35,7 +35,7 @@
handle_cast/2, terminate/2, code_change/3]).
%% API.
-export([start_link/2, add_listener/2,
-export([start_link/2, add_listener/2, transform_module_options/1,
delete_listener/1]).
-include("ejabberd.hrl").
@@ -261,16 +261,15 @@ parse_options(ServerHost, Opts) ->
Name = gen_mod:get_opt(name, Opts, fun iolist_to_binary/1,
<<"SOCKS5 Bytestreams">>),
IP = gen_mod:get_opt(ip, Opts,
fun(Addr) ->
jlib:ip_to_list(Addr),
fun(S) ->
{ok, Addr} = inet_parse:address(
binary_to_list(
iolist_to_binary(S))),
Addr
end, get_my_ip()),
HostName = gen_mod:get_opt(hostname, Opts,
fun(Addr) when is_tuple(Addr) ->
jlib:ip_to_list(Addr);
(S) ->
iolist_to_binary(S)
end, jlib:ip_to_list(IP)),
fun iolist_to_binary/1,
jlib:ip_to_list(IP)),
StreamAddr = [{<<"jid">>, MyHost},
{<<"host">>, HostName},
{<<"port">>, jlib:integer_to_binary(Port)}],
@@ -278,6 +277,16 @@ parse_options(ServerHost, Opts) ->
name = Name, port = Port, ip = IP,
stream_addr = StreamAddr, acl = ACL}.
transform_module_options(Opts) ->
lists:map(
fun({ip, IP}) when is_tuple(IP) ->
{ip, jlib:ip_to_list(IP)};
({hostname, IP}) when is_tuple(IP) ->
{hostname, jlib:ip_to_list(IP)};
(Opt) ->
Opt
end, Opts).
get_my_ip() ->
{ok, MyHostName} = inet:gethostname(),
case inet:getaddr(MyHostName, inet) of
+8 -4
View File
@@ -279,10 +279,14 @@ select_auth_method(anonymous, AuthMethods) ->
%% Obviously, we must use shaper with maximum rate.
find_maxrate(Shaper, JID1, JID2, Host) ->
MaxRate1 = shaper:new(acl:match_rule(Host, Shaper,
JID1)),
MaxRate2 = shaper:new(acl:match_rule(Host, Shaper,
JID2)),
MaxRate1 = case acl:match_rule(Host, Shaper, JID1) of
deny -> none;
R1 -> shaper:new(R1)
end,
MaxRate2 = case acl:match_rule(Host, Shaper, JID2) of
deny -> none;
R2 -> shaper:new(R2)
end,
if MaxRate1 == none; MaxRate2 == none -> none;
true -> lists:max([MaxRate1, MaxRate2])
end.
+24 -16
View File
@@ -1302,7 +1302,8 @@ remove_user(User, Server) ->
[NodeId,
Entity,
JID,
all])
all]);
(_) -> ok
end,
Subscriptions),
{result, Affiliations} =
@@ -3682,26 +3683,33 @@ get_options_helper(JID, Lang, Node, NodeID, SubID, Type) ->
{error,
extended_error(?ERR_NOT_ACCEPTABLE, <<"subid-required">>)};
{_, _} ->
read_sub(Subscriber, Node, NodeID, SubID, Lang)
ValidSubId = lists:member(SubID, SubIDs),
if ValidSubId ->
read_sub(Subscriber, Node, NodeID, SubID, Lang);
true ->
{error,
extended_error(?ERR_NOT_ACCEPTABLE, <<"invalid-subid">>)}
end
end.
read_sub(Subscriber, Node, NodeID, SubID, Lang) ->
case pubsub_subscription:get_subscription(Subscriber, NodeID, SubID) of
Children = case pubsub_subscription:get_subscription(Subscriber, NodeID, SubID) of
{error, notfound} ->
{error, extended_error(?ERR_NOT_ACCEPTABLE, <<"invalid-subid">>)};
[];
{result, #pubsub_subscription{options = Options}} ->
{result, XdataEl} = pubsub_subscription:get_options_xform(Lang, Options),
OptionsEl = #xmlel{name = <<"options">>,
attrs =
[{<<"jid">>, jlib:jid_to_string(Subscriber)},
{<<"subid">>, SubID}
| nodeAttr(Node)],
children = [XdataEl]},
PubsubEl = #xmlel{name = <<"pubsub">>,
attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
children = [OptionsEl]},
{result, PubsubEl}
end.
[XdataEl]
end,
OptionsEl = #xmlel{name = <<"options">>,
attrs =
[{<<"jid">>, jlib:jid_to_string(Subscriber)},
{<<"subid">>, SubID}
| nodeAttr(Node)],
children = Children},
PubsubEl = #xmlel{name = <<"pubsub">>,
attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
children = [OptionsEl]},
{result, PubsubEl}.
set_options(Host, Node, JID, SubID, Configuration) ->
Action = fun (#pubsub_node{type = Type, id = NodeID}) ->
@@ -4436,7 +4444,7 @@ broadcast_stanza({LUser, LServer, LResource}, Publisher, Node, NodeId, Type, Nod
[] ->
case user_resources(LUser, LServer) of
[Resource|_] -> Resource;
_ -> ""
_ -> <<>>
end;
_ ->
LResource
+25 -17
View File
@@ -443,7 +443,7 @@ send_loop(State) ->
LJID = jlib:jid_tolower(JID),
BJID = jlib:jid_remove_resource(LJID),
lists:foreach(fun (PType) ->
{result, Subscriptions} = case catch node_action(Host,
Subscriptions = case catch node_action(Host,
PType,
get_entity_subscriptions_for_send_last,
[Host, JID]) of
@@ -952,7 +952,8 @@ remove_user(User, Server) ->
[NodeId,
Entity,
JID,
all])
all]);
(_) -> ok
end,
Subscriptions),
{result, Affiliations} =
@@ -3301,26 +3302,33 @@ get_options_helper(JID, Lang, Node, NodeID, SubID, Type) ->
{error,
extended_error(?ERR_NOT_ACCEPTABLE, <<"subid-required">>)};
{_, _} ->
read_sub(Subscriber, Node, NodeID, SubID, Lang)
ValidSubId = lists:member(SubID, SubIDs),
if ValidSubId ->
read_sub(Subscriber, Node, NodeID, SubID, Lang);
true ->
{error,
extended_error(?ERR_NOT_ACCEPTABLE, <<"invalid-subid">>)}
end
end.
read_sub(Subscriber, Node, NodeID, SubID, Lang) ->
case pubsub_subscription_odbc:get_subscription(Subscriber, NodeID, SubID) of
Children = case pubsub_subscription_odbc:get_subscription(Subscriber, NodeID, SubID) of
{error, notfound} ->
{error, extended_error(?ERR_NOT_ACCEPTABLE, <<"invalid-subid">>)};
[];
{result, #pubsub_subscription{options = Options}} ->
{result, XdataEl} = pubsub_subscription_odbc:get_options_xform(Lang, Options),
OptionsEl = #xmlel{name = <<"options">>,
attrs =
[{<<"jid">>, jlib:jid_to_string(Subscriber)},
{<<"subid">>, SubID}
| nodeAttr(Node)],
children = [XdataEl]},
PubsubEl = #xmlel{name = <<"pubsub">>,
attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
children = [OptionsEl]},
{result, PubsubEl}
end.
[XdataEl]
end,
OptionsEl = #xmlel{name = <<"options">>,
attrs =
[{<<"jid">>, jlib:jid_to_string(Subscriber)},
{<<"subid">>, SubID}
| nodeAttr(Node)],
children = Children},
PubsubEl = #xmlel{name = <<"pubsub">>,
attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
children = [OptionsEl]},
{result, PubsubEl}.
set_options(Host, Node, JID, SubID, Configuration) ->
Action = fun (#pubsub_node{type = Type, id = NodeID}) ->
@@ -4055,7 +4063,7 @@ broadcast_stanza({LUser, LServer, LResource}, Publisher, Node, NodeId, Type, Nod
[] ->
case user_resources(LUser, LServer) of
[Resource|_] -> Resource;
_ -> ""
_ -> <<>>
end;
_ ->
LResource
+65 -72
View File
@@ -32,7 +32,8 @@
-export([start/2, stop/1, stream_feature_register/2,
unauthenticated_iq_register/4, try_register/5,
process_iq/3, send_registration_notifications/3]).
process_iq/3, send_registration_notifications/3,
transform_options/1, transform_module_options/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -418,7 +419,11 @@ try_register(User, Server, Password, SourceRaw, Lang) ->
send_welcome_message(JID) ->
Host = JID#jid.lserver,
case gen_mod:get_module_opt(Host, ?MODULE, welcome_message,
fun({S, B}) ->
fun(Opts) ->
S = proplists:get_value(
subject, Opts, <<>>),
B = proplists:get_value(
body, Opts, <<>>),
{iolist_to_binary(S),
iolist_to_binary(B)}
end, {<<"">>, <<"">>})
@@ -483,7 +488,7 @@ check_from(JID, Server) ->
check_timeout(undefined) -> true;
check_timeout(Source) ->
Timeout = ejabberd_config:get_local_option(
Timeout = ejabberd_config:get_option(
registration_timeout,
fun(TO) when is_integer(TO), TO > 0 ->
TO;
@@ -537,7 +542,7 @@ clean_treap(Treap, CleanPriority) ->
remove_timeout(undefined) -> true;
remove_timeout(Source) ->
Timeout = ejabberd_config:get_local_option(
Timeout = ejabberd_config:get_option(
registration_timeout,
fun(TO) when is_integer(TO), TO > 0 ->
TO;
@@ -604,6 +609,54 @@ is_strong_password(Server, Password) ->
ejabberd_auth:entropy(Password) >= Entropy
end.
transform_options(Opts) ->
Opts1 = transform_ip_access(Opts),
transform_module_options(Opts1).
transform_ip_access(Opts) ->
try
{value, {modules, ModOpts}, Opts1} = lists:keytake(modules, 1, Opts),
{value, {?MODULE, RegOpts}, ModOpts1} = lists:keytake(?MODULE, 1, ModOpts),
{value, {ip_access, L}, RegOpts1} = lists:keytake(ip_access, 1, RegOpts),
true = is_list(L),
?WARNING_MSG("Old 'ip_access' format detected. "
"The old format is still supported "
"but it is better to fix your config: "
"use access rules instead.", []),
ACLs = lists:flatmap(
fun({Action, S}) ->
ACLName = jlib:binary_to_atom(
iolist_to_binary(
["ip_", S])),
[{Action, ACLName},
{acl, ACLName, {ip, S}}]
end, L),
Access = {access, mod_register_networks,
[{Action, ACLName} || {Action, ACLName} <- ACLs]},
[ACL || {acl, _, _} = ACL <- ACLs] ++
[Access,
{modules,
[{mod_register,
[{ip_access, mod_register_networks}|RegOpts1]}
| ModOpts1]}|Opts1]
catch error:{badmatch, false} ->
Opts
end.
transform_module_options(Opts) ->
lists:flatmap(
fun({welcome_message, {Subj, Body}}) ->
?WARNING_MSG("Old 'welcome_message' format detected. "
"The old format is still supported "
"but it is better to fix your config: "
"change it to {welcome_message, "
"[{subject, Subject}, {body, Body}]}",
[]),
[{welcome_message, [{subject, Subj}, {body, Body}]}];
(Opt) ->
[Opt]
end, Opts).
%%%
%%% ip_access management
%%%
@@ -614,75 +667,15 @@ may_remove_resource(From) -> From.
get_ip_access(Host) ->
gen_mod:get_module_opt(Host, ?MODULE, ip_access,
fun(IPAccess) ->
lists:flatmap(
fun({Access, S}) ->
{ok, IP, Mask} =
parse_ip_netmask(
iolist_to_binary(S)),
[{Access, IP, Mask}]
end, IPAccess)
end, []).
fun(A) when is_atom(A) -> A end,
all).
parse_ip_netmask(S) ->
case str:tokens(S, <<"/">>) of
[IPStr] ->
case inet_parse:address(binary_to_list(IPStr)) of
{ok, {_, _, _, _} = IP} -> {ok, IP, 32};
{ok, {_, _, _, _, _, _, _, _} = IP} -> {ok, IP, 128};
_ -> error
end;
[IPStr, MaskStr] ->
case catch jlib:binary_to_integer(MaskStr) of
Mask when is_integer(Mask), Mask >= 0 ->
case inet_parse:address(binary_to_list(IPStr)) of
{ok, {_, _, _, _} = IP} when Mask =< 32 ->
{ok, IP, Mask};
{ok, {_, _, _, _, _, _, _, _} = IP} when Mask =< 128 ->
{ok, IP, Mask};
_ -> error
end;
_ -> error
end;
_ -> error
end.
check_ip_access(_Source, []) -> allow;
check_ip_access({User, Server, Resource}, IPAccess) ->
case ejabberd_sm:get_user_ip(User, Server, Resource) of
{IPAddress, _PortNumber} ->
check_ip_access(IPAddress, IPAccess);
_ -> true
{IPAddress, _PortNumber} ->
check_ip_access(IPAddress, IPAccess);
_ ->
deny
end;
check_ip_access({_, _, _, _} = IP,
[{Access, {_, _, _, _} = Net, Mask} | IPAccess]) ->
IPInt = ip_to_integer(IP),
NetInt = ip_to_integer(Net),
M = bnot (1 bsl (32 - Mask) - 1),
if IPInt band M =:= NetInt band M -> Access;
true -> check_ip_access(IP, IPAccess)
end;
check_ip_access({_, _, _, _, _, _, _, _} = IP,
[{Access, {_, _, _, _, _, _, _, _} = Net, Mask}
| IPAccess]) ->
IPInt = ip_to_integer(IP),
NetInt = ip_to_integer(Net),
M = bnot (1 bsl (128 - Mask) - 1),
if IPInt band M =:= NetInt band M -> Access;
true -> check_ip_access(IP, IPAccess)
end;
check_ip_access(IP, [_ | IPAccess]) ->
check_ip_access(IP, IPAccess).
ip_to_integer({IP1, IP2, IP3, IP4}) ->
IP1 bsl 8 bor IP2 bsl 8 bor IP3 bsl 8 bor IP4;
ip_to_integer({IP1, IP2, IP3, IP4, IP5, IP6, IP7,
IP8}) ->
IP1 bsl 16 bor IP2 bsl 16 bor IP3 bsl 16 bor IP4 bsl 16
bor IP5
bsl 16
bor IP6
bsl 16
bor IP7
bsl 16
bor IP8.
check_ip_access(IPAddress, IPAccess) ->
acl:match_rule(global, IPAccess, IPAddress).
+29 -3
View File
@@ -39,8 +39,8 @@
-behaviour(gen_mod).
-export([start/2, stop/1, process_iq/3, export/1,
process_local_iq/3, get_user_roster/2,
-export([start/2, stop/1, process_iq/3, export/1, import/1,
process_local_iq/3, get_user_roster/2, import/3,
get_subscription_lists/3, get_roster/2,
get_in_pending_subscriptions/3, in_subscription/6,
out_subscription/4, set_items/3, remove_user/2,
@@ -196,7 +196,7 @@ read_roster_version(LUser, LServer, mnesia) ->
[#roster_version{version = V}] -> V;
[] -> error
end;
read_roster_version(LServer, LUser, odbc) ->
read_roster_version(LUser, LServer, odbc) ->
Username = ejabberd_odbc:escape(LUser),
case odbc_queries:get_roster_version(LServer, Username)
of
@@ -1569,3 +1569,29 @@ export(_Server) ->
(_Host, _R) ->
[]
end}].
import(LServer) ->
[{<<"select username, jid, nick, subscription, "
"ask, askmessage, server, subscribe, type from rosterusers;">>,
fun([LUser, JID|_] = Row) ->
Item = raw_to_record(LServer, Row),
Username = ejabberd_odbc:escape(LUser),
SJID = ejabberd_odbc:escape(JID),
{selected, _, Rows} =
ejabberd_odbc:sql_query_t(
[<<"select grp from rostergroups where username='">>,
Username, <<"' and jid='">>, SJID, <<"'">>]),
Groups = [Grp || [Grp] <- Rows],
Item#roster{groups = Groups}
end},
{<<"select username, version from roster_version;">>,
fun([LUser, Ver]) ->
#roster_version{us = {LUser, LServer}, version = Ver}
end}].
import(_LServer, mnesia, #roster{} = R) ->
mnesia:dirty_write(R);
import(_LServer, mnesia, #roster_version{} = RV) ->
mnesia:dirty_write(RV);
import(_, _, _) ->
pass.
+21 -2
View File
@@ -30,9 +30,9 @@
-behaviour(gen_mod).
-export([start/2, stop/1, item_to_xml/1, export/1,
-export([start/2, stop/1, item_to_xml/1, export/1, import/1,
webadmin_menu/3, webadmin_page/3, get_user_roster/2,
get_subscription_lists/3, get_jid_info/4,
get_subscription_lists/3, get_jid_info/4, import/3,
process_item/2, in_subscription/6, out_subscription/4,
user_available/1, unset_presence/4, register_user/2,
remove_user/2, list_groups/1, create_group/2,
@@ -1334,3 +1334,22 @@ export(_Server) ->
(_Host, _R) ->
[]
end}].
import(LServer) ->
[{<<"select name, opts from sr_group;">>,
fun([Group, SOpts]) ->
#sr_group{group_host = {Group, LServer},
opts = ejabberd_odbc:decode_term(SOpts)}
end},
{<<"select jid, grp from sr_user;">>,
fun([SJID, Group]) ->
#jid{luser = U, lserver = S} = jlib:string_to_jid(SJID),
#sr_user{us = {U, S}, group_host = {Group, LServer}}
end}].
import(_LServer, mnesia, #sr_group{} = G) ->
mnesia:dirty_write(G);
import(_LServer, mnesia, #sr_user{} = U) ->
mnesia:dirty_write(U);
import(_, _, _) ->
pass.
+37 -1
View File
@@ -32,7 +32,7 @@
-export([start/2, init/3, stop/1, get_sm_features/5,
process_local_iq/3, process_sm_iq/3, reindex_vcards/0,
remove_user/2, export/1]).
remove_user/2, export/1, import/1, import/3]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -1006,3 +1006,39 @@ export(_Server) ->
(_Host, _R) ->
[]
end}].
import(LServer) ->
[{<<"select username, vcard from vcard;">>,
fun([LUser, SVCard]) ->
#xmlel{} = VCARD = xml_stream:parse_element(SVCard),
#vcard{us = {LUser, LServer}, vcard = VCARD}
end},
{<<"select username, lusername, fn, lfn, family, lfamily, "
"given, lgiven, middle, lmiddle, nickname, lnickname, "
"bday, lbday, ctry, lctry, locality, llocality, email, "
"lemail, orgname, lorgname, orgunit, lorgunit from vcard_search;">>,
fun([User, LUser, FN, LFN,
Family, LFamily, Given, LGiven,
Middle, LMiddle, Nickname, LNickname,
BDay, LBDay, CTRY, LCTRY, Locality, LLocality,
EMail, LEMail, OrgName, LOrgName, OrgUnit, LOrgUnit]) ->
#vcard_search{us = {LUser, LServer},
user = {User, LServer}, luser = LUser,
fn = FN, lfn = LFN, family = Family,
lfamily = LFamily, given = Given,
lgiven = LGiven, middle = Middle,
lmiddle = LMiddle, nickname = Nickname,
lnickname = LNickname, bday = BDay,
lbday = LBDay, ctry = CTRY, lctry = LCTRY,
locality = Locality, llocality = LLocality,
email = EMail, lemail = LEMail,
orgname = OrgName, lorgname = LOrgName,
orgunit = OrgUnit, lorgunit = LOrgUnit}
end}].
import(_LServer, mnesia, #vcard{} = VCard) ->
mnesia:dirty_write(VCard);
import(_LServer, mnesia, #vcard_search{} = S) ->
mnesia:dirty_write(S);
import(_, _, _) ->
pass.
+16 -2
View File
@@ -38,7 +38,7 @@
-export([start/2, start_link/2, stop/1,
get_sm_features/5, process_local_iq/3, process_sm_iq/3,
remove_user/1, route/4]).
remove_user/1, route/4, transform_module_options/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -767,7 +767,7 @@ parse_options(Host, Opts) ->
VCardMap = gen_mod:get_opt(ldap_vcard_map, Opts,
fun(Ls) ->
lists:map(
fun({S, P, L}) ->
fun({S, [{P, L}]}) ->
{iolist_to_binary(S),
iolist_to_binary(P),
[iolist_to_binary(E)
@@ -823,6 +823,20 @@ parse_options(Host, Opts) ->
search_reported_attrs = SearchReportedAttrs,
matches = Matches}.
transform_module_options(Opts) ->
lists:map(
fun({ldap_vcard_map, Map}) ->
NewMap = lists:map(
fun({Field, Pattern, Attrs}) ->
{Field, [{Pattern, Attrs}]};
(Opt) ->
Opt
end, Map),
{ldap_vcard_map, NewMap};
(Opt) ->
Opt
end, Opts).
check_filter(F) ->
NewF = iolist_to_binary(F),
{ok, _} = eldap_filter:parse(NewF),
+12 -1
View File
@@ -13,7 +13,7 @@
-export([start/2, stop/1]).
%% hooks
-export([update_presence/3, vcard_set/3, export/1]).
-export([update_presence/3, vcard_set/3, export/1, import/1, import/3]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -203,3 +203,14 @@ export(_Server) ->
(_Host, _R) ->
[]
end}].
import(LServer) ->
[{<<"select username, hash from vcard_xupdate;">>,
fun([LUser, Hash]) ->
#vcard_xupdate{us = {LUser, LServer}, hash = Hash}
end}].
import(_LServer, mnesia, #vcard_xupdate{} = R) ->
mnesia:dirty_write(R);
import(_, _, _) ->
pass.
+11 -33
View File
@@ -383,7 +383,7 @@ unsubscribe_node(NodeId, Sender, Subscriber, SubId) ->
{Affiliation, Subscriptions} =
select_affiliation_subscriptions(NodeId, SubKey),
SubIdExists = case SubId of
[] -> false;
<<>> -> false;
List when is_binary(List) -> true;
_ -> false
end,
@@ -788,15 +788,8 @@ get_entity_subscriptions(Host, Owner) ->
case decode_subscriptions(S) of
[] -> [{Node, none, Jid} | Acc];
Subs ->
lists:foldl(fun ({Sub, SubId},
Acc2) ->
[{Node, Sub,
SubId, Jid}
| Acc2];
(Sub, Acc2) ->
[{Node, Sub,
Jid}
| Acc2]
lists:foldl(fun ({Sub, SubId}, Acc2) ->
[{Node, Sub, SubId, Jid} | Acc2]
end,
Acc, Subs)
end
@@ -1253,7 +1246,7 @@ get_items(NodeId, From, none) ->
get_items(NodeId, _From,
#rsm_in{max = M, direction = Direction, id = I,
index = IncIndex}) ->
Max = (?PUBSUB):escape(i2l(M)),
Max = (?PUBSUB):escape(jlib:i2l(M)),
{Way, Order} = case Direction of
aft -> {<<"<">>, <<"desc">>};
before when I == <<>> -> {<<"is not">>, <<"asc">>};
@@ -1271,7 +1264,7 @@ get_items(NodeId, _From,
NodeId,
<<"' and modification > pi.modification "
"having count1 = ">>,
(?PUBSUB):escape(i2l(IncIndex)),
(?PUBSUB):escape(jlib:i2l(IncIndex)),
<<" );">>])
of
{selected, [_], [[O]]} ->
@@ -1281,7 +1274,7 @@ get_items(NodeId, _From,
undefined -> [<<"modification">>, <<"null">>];
<<>> -> [<<"modification">>, <<"null">>];
I ->
[A, B] = str:tokens((?PUBSUB):escape(i2l(I)),
[A, B] = str:tokens((?PUBSUB):escape(jlib:i2l(I)),
<<"@">>),
[A, <<"'", B/binary, "'">>]
end,
@@ -1300,7 +1293,7 @@ get_items(NodeId, _From,
NodeId, <<"' and ">>, AttrName, <<" ">>,
Way, <<" ">>, Id, <<" order by ">>,
AttrName, <<" ">>, Order, <<" limit ">>,
i2l(Max), <<" ;">>])
jlib:i2l(Max), <<" ;">>])
of
{selected,
[<<"itemid">>, <<"publisher">>, <<"creation">>,
@@ -1322,7 +1315,7 @@ get_items(NodeId, _From,
[_, _, _, L, _] = lists:last(RItems),
RsmOut = #rsm_out{count = Count, index = Index,
first = <<"modification@", F/binary>>,
last = <<"modification@", (i2l(L))/binary>>},
last = <<"modification@", (jlib:i2l(L))/binary>>},
{result, {[raw_to_item(NodeId, RItem) || RItem <- RItems], RsmOut}};
0 -> {result, {[], #rsm_out{count = Count}}}
end;
@@ -1377,7 +1370,7 @@ get_last_items(NodeId, _From, Count) ->
"where nodeid='">>,
NodeId,
<<"' order by modification desc limit ">>,
i2l(Count), <<";">>])
jlib:i2l(Count), <<";">>])
of
{selected,
[<<"itemid">>, <<"publisher">>, <<"creation">>,
@@ -1459,7 +1452,7 @@ set_item(Item) ->
Payload = Item#pubsub_item.payload,
XML = (?PUBSUB):escape(str:join([xml:element_to_binary(X) || X<-Payload], <<>>)),
S = fun ({T1, T2, T3}) ->
str:join([i2l(T1, 6), i2l(T2, 6), i2l(T3, 6)], <<":">>)
str:join([jlib:i2l(T1, 6), jlib:i2l(T2, 6), jlib:i2l(T3, 6)], <<":">>)
end,
case catch
ejabberd_odbc:sql_query_t([<<"update pubsub_item set publisher='">>,
@@ -1666,7 +1659,7 @@ raw_to_item(NodeId,
JID = decode_jid(SJID),
ToTime = fun (Str) ->
[T1, T2, T3] = str:tokens(Str, <<":">>),
{l2i(T1), l2i(T2), l2i(T3)}
{jlib:l2i(T1), jlib:l2i(T2), jlib:l2i(T3)}
end,
Payload = case xml_stream:parse_element(XML) of
{error, _Reason} -> [];
@@ -1676,18 +1669,3 @@ raw_to_item(NodeId,
creation = {ToTime(Creation), JID},
modification = {ToTime(Modification), JID},
payload = Payload}.
l2i(I) when is_integer(I) -> I;
l2i(L) when is_binary(L) -> jlib:binary_to_integer(L).
i2l(I) when is_integer(I) ->
iolist_to_binary(integer_to_list(I));
i2l(L) when is_binary(L) -> L.
i2l(I, N) when is_integer(I) -> i2l(i2l(I), N);
i2l(L, N) when is_binary(L) ->
case str:len(L) of
N -> L;
C when C > N -> L;
_ -> i2l(<<$0, L/binary>>, N)
end.
+7 -11
View File
@@ -144,12 +144,10 @@ create_node(NodeIdx, Owner) ->
).
delete_node(Removed) ->
{result, {_, _, Removed}} = node_hometree:delete_node(Removed),
{result, {[], Removed}}.
% case node_hometree:delete_node(Removed) of
% {result, {_, _, Removed}} -> {result, {[], Removed}};
% Error -> Error
% end.
case node_hometree:delete_node(Removed) of
{result, {_, _, Result}} -> {result, {[], Result}};
Error -> Error
end.
-spec(subscribe_node/8 ::
(
@@ -492,13 +490,11 @@ path_to_node(Path) -> node_flat:path_to_node(Path).
%% Check that the mod_caps module is enabled in that Jabber Host
%% If not, show a warning message in the ejabberd log file.
complain_if_modcaps_disabled(ServerHost) ->
Modules = ejabberd_config:get_local_option({modules, ServerHost}, fun(Ms) when is_list(Ms) -> Ms end),
ModCaps = [mod_caps_enabled || {mod_caps, _Opts} <- Modules],
case ModCaps of
[] ->
case gen_mod:is_loaded(ServerHost, mod_caps) of
false ->
?WARNING_MSG("The PEP plugin is enabled in mod_pubsub "
"of host ~p. This plugin requires mod_caps "
"to be enabled, but it isn't.",
[ServerHost]);
_ -> ok
true -> ok
end.
+2 -2
View File
@@ -156,7 +156,7 @@ create_node(NodeIdx, Owner) ->
).
delete_node(Removed) ->
case node_hometree_odbc:delete_node(Removed) of
{result, {_, _, Removed}} -> {result, {[], Removed}};
{result, {_, _, Result}} -> {result, {[], Result}};
Error -> Error
end.
@@ -433,7 +433,7 @@ path_to_node(Path) -> node_flat_odbc:path_to_node(Path).
%% Check that the mod_caps module is enabled in that Jabber Host
%% If not, show a warning message in the ejabberd log file.
complain_if_modcaps_disabled(ServerHost) ->
Modules = ejabberd_config:get_local_option({modules,
Modules = ejabberd_config:get_option({modules,
ServerHost},
fun(Ms) when is_list(Ms) -> Ms end),
ModCaps = [mod_caps_enabled
+5 -1
View File
@@ -362,9 +362,13 @@ raw_to_node(Host, [Node, Parent, Type, NodeIdx]) ->
StdOpts, DbOpts);
_ -> []
end,
Parents = case Parent of
<<>> -> [];
_ -> [Parent]
end,
#pubsub_node{nodeid =
{Host, Node},
parents = [Parent],
parents = Parents,
id = NodeIdx, type = Type, options = Options}.
% @spec (NodeRecord) -> ok | {error, Reason}
+17 -6
View File
@@ -45,7 +45,7 @@
get_default_privacy_list_t/1, get_privacy_list_names/2,
get_privacy_list_names_t/1, get_privacy_list_id/3,
get_privacy_list_id_t/2, get_privacy_list_data/3,
get_privacy_list_data_by_id/2,
get_privacy_list_data_by_id/2, get_privacy_list_data_t/2,
get_privacy_list_data_by_id_t/1,
set_default_privacy_list/2,
unset_default_privacy_list/2,
@@ -219,13 +219,15 @@ list_users(LServer,
[Prefix, Limit, Offset]))]).
users_number(LServer) ->
case element(1,
ejabberd_config:get_local_option(
{odbc_server, LServer}, fun(V) -> V end))
of
Type = ejabberd_config:get_option({odbc_type, LServer},
fun(pgsql) -> pgsql;
(mysql) -> mysql;
(odbc) -> odbc
end, odbc),
case Type of
pgsql ->
case
ejabberd_config:get_local_option(
ejabberd_config:get_option(
{pgsql_users_number_estimate, LServer},
fun(V) when is_boolean(V) -> V end,
false)
@@ -519,6 +521,15 @@ get_privacy_list_data(LServer, Username, SName) ->
Username, <<"' and name='">>, SName,
<<"') order by ord;">>]).
get_privacy_list_data_t(Username, SName) ->
ejabberd_odbc:sql_query_t([<<"select t, value, action, ord, match_all, "
"match_iq, match_message, match_presence_in, "
"match_presence_out from privacy_list_data "
"where id = (select id from privacy_list "
"where username='">>,
Username, <<"' and name='">>, SName,
<<"') order by ord;">>]).
get_privacy_list_data_by_id(LServer, ID) ->
ejabberd_odbc:sql_query(LServer,
[<<"select t, value, action, ord, match_all, "
+1 -1
View File
@@ -792,7 +792,7 @@ get_msg(Msg) -> Msg.
%% Status information
%%-----------------------------------------------------------------
format_status(Opt, StatusData) ->
[PDict, SysState, Parent, Debug, [Name, StateName, StateData, Mod, _Time]] =
[PDict, SysState, Parent, Debug, [Name, StateName, StateData, Mod, _Time, _Limits, _Queue, _QueueLen]] =
StatusData,
NameTag = if is_pid(Name) ->
pid_to_list(Name);
+60 -15
View File
@@ -28,7 +28,8 @@
-author('alexey@process-one.net').
-export([new/1, new1/1, update/2]).
-export([start/0, new/1, new1/1, update/2,
transform_options/1, load_from_config/0]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -37,32 +38,66 @@
lastrate = 0.0 :: float(),
lasttime = 0 :: integer()}).
-type maxrate() :: none | #maxrate{}.
-record(shaper, {name :: {atom(), global},
maxrate :: integer()}).
-type shaper() :: maxrate() | {maxrate(), integer()}.
-type shaper() :: none | #maxrate{}.
-export_type([shaper/0]).
-spec new(atom()) -> maxrate().
-spec start() -> ok.
start() ->
mnesia:create_table(shaper,
[{ram_copies, [node()]},
{local_content, true},
{attributes, record_info(fields, shaper)}]),
mnesia:add_table_copy(shaper, node(), ram_copies),
load_from_config(),
ok.
-spec load_from_config() -> ok | {error, any()}.
load_from_config() ->
Shapers = ejabberd_config:get_option(
shaper, fun(V) -> V end, []),
case mnesia:transaction(
fun() ->
lists:foreach(
fun({Name, MaxRate}) ->
mnesia:write(#shaper{name = {Name, global},
maxrate = MaxRate})
end, Shapers)
end) of
{atomic, ok} ->
ok;
Err ->
{error, Err}
end.
-spec new(atom()) -> shaper().
new(none) ->
none;
new(Name) ->
Data = ejabberd_config:get_global_option(
{shaper, Name, global},
fun({maxrate, R}) when is_integer(R), R>0 ->
{maxrate, R};
(none) ->
none
end, none),
new1(Data).
MaxRate = case ets:lookup(shaper, {Name, global}) of
[#shaper{maxrate = R}] ->
R;
[] ->
?WARNING_MSG("Attempt to initialize an "
"unspecified shaper '~s'", [Name]),
none
end,
new1(MaxRate).
-spec new1(none | {maxrate, integer()}) -> maxrate().
-spec new1(none | integer()) -> shaper().
new1(none) -> none;
new1({maxrate, MaxRate}) ->
new1(MaxRate) ->
#maxrate{maxrate = MaxRate, lastrate = 0.0,
lasttime = now_to_usec(now())}.
-spec update(maxrate(), integer()) -> {maxrate(), integer()}.
-spec update(shaper(), integer()) -> {shaper(), integer()}.
update(none, _Size) -> {none, 0};
update(#maxrate{} = State, Size) ->
@@ -84,5 +119,15 @@ update(#maxrate{} = State, Size) ->
lasttime = NextNow},
Pause}.
transform_options(Opts) ->
lists:foldl(fun transform_options/2, [], Opts).
transform_options({shaper, Name, {maxrate, N}}, Opts) ->
[{shaper, [{Name, N}]}|Opts];
transform_options({shaper, Name, none}, Opts) ->
[{shaper, [{Name, none}]}|Opts];
transform_options(Opt, Opts) ->
[Opt|Opts].
now_to_usec({MSec, Sec, USec}) ->
(MSec * 1000000 + Sec) * 1000000 + USec.
+138 -481
View File
@@ -10,107 +10,38 @@
-compile(export_all).
-include_lib("common_test/include/ct.hrl").
-include("xml.hrl").
-include("ns.hrl").
-include("ejabberd.hrl").
-include("mod_proxy65.hrl").
-include("xmpp_codec.hrl").
-import(suite, [init_config/1, connect/1, disconnect/1,
recv/0, send/2, send_recv/2, my_jid/1, server_jid/1,
pubsub_jid/1, proxy_jid/1, muc_jid/1,
muc_room_jid/1, get_features/2, re_register/1,
is_feature_advertised/2, subscribe_to_events/1,
is_feature_advertised/3, set_opt/3, auth_SASL/2,
wait_for_master/1, wait_for_slave/1,
make_iq_result/1, start_event_relay/0,
stop_event_relay/1, put_event/2, get_event/1,
bind/1, auth/1, open_session/1, zlib/1, starttls/1]).
-define(STREAM_HEADER,
<<"<?xml version='1.0'?><stream:stream "
"xmlns:stream='http://etherx.jabber.org/stream"
"s' xmlns='jabber:client' to='~s' version='1.0"
"'>">>).
-define(STREAM_TRAILER, <<"</stream:stream>">>).
-define(PUBSUB(Node), <<(?NS_PUBSUB)/binary, "#", Node>>).
-define(recv2(P1, P2),
(fun() ->
case {R1 = recv(), R2 = recv()} of
{P1, P2} -> {R1, R2};
{P2, P1} -> {R2, R1}
end
end)()).
-define(recv3(P1, P2, P3),
(fun() ->
case R3 = recv() of
P1 -> insert(R3, 1, ?recv2(P2, P3));
P2 -> insert(R3, 2, ?recv2(P1, P3));
P3 -> insert(R3, 3, ?recv2(P1, P2))
end
end)()).
-define(recv4(P1, P2, P3, P4),
(fun() ->
case R4 = recv() of
P1 -> insert(R4, 1, ?recv3(P2, P3, P4));
P2 -> insert(R4, 2, ?recv3(P1, P3, P4));
P3 -> insert(R4, 3, ?recv3(P1, P2, P4));
P4 -> insert(R4, 4, ?recv3(P1, P2, P3))
end
end)()).
-define(recv5(P1, P2, P3, P4, P5),
(fun() ->
case R5 = recv() of
P1 -> insert(R5, 1, ?recv4(P2, P3, P4, P5));
P2 -> insert(R5, 2, ?recv4(P1, P3, P4, P5));
P3 -> insert(R5, 3, ?recv4(P1, P2, P4, P5));
P4 -> insert(R5, 4, ?recv4(P1, P2, P3, P5));
P5 -> insert(R5, 5, ?recv4(P1, P2, P3, P4))
end
end)()).
-define(COMMON_VHOST, <<"localhost">>).
-define(MNESIA_VHOST, <<"mnesia.localhost">>).
-define(MYSQL_VHOST, <<"mysql.localhost">>).
-define(PGSQL_VHOST, <<"pgsql.localhost">>).
-define(LDAP_VHOST, <<"ldap.localhost">>).
-include("suite.hrl").
suite() ->
[{timetrap, {seconds,10}}].
[{timetrap, {seconds,20}}].
init_per_suite(Config) ->
DataDir = proplists:get_value(data_dir, Config),
PrivDir = proplists:get_value(priv_dir, Config),
[_, _|Tail] = lists:reverse(filename:split(DataDir)),
BaseDir = filename:join(lists:reverse(Tail)),
ConfigPath = filename:join([DataDir, "ejabberd.cfg"]),
LogPath = filename:join([PrivDir, "ejabberd.log"]),
SASLPath = filename:join([PrivDir, "sasl.log"]),
MnesiaDir = filename:join([PrivDir, "mnesia"]),
CertFile = filename:join([DataDir, "cert.pem"]),
LDIFFile = filename:join([DataDir, "ejabberd.ldif"]),
NewConfig = init_config(Config),
DataDir = proplists:get_value(data_dir, NewConfig),
{ok, CWD} = file:get_cwd(),
{ok, _} = file:copy(CertFile, filename:join([CWD, "cert.pem"])),
application:set_env(ejabberd, config, ConfigPath),
application:set_env(ejabberd, log_path, LogPath),
application:set_env(sasl, sasl_error_logger, {file, SASLPath}),
application:set_env(mnesia, dir, MnesiaDir),
ExtAuthScript = filename:join([DataDir, "extauth.py"]),
LDIFFile = filename:join([DataDir, "ejabberd.ldif"]),
{ok, _} = file:copy(ExtAuthScript, filename:join([CWD, "extauth.py"])),
{ok, _} = ldap_srv:start(LDIFFile),
ok = application:start(ejabberd),
[{server_port, 5222},
{server_host, "localhost"},
{server, ?COMMON_VHOST},
{user, <<"test_single">>},
{certfile, CertFile},
{base_dir, BaseDir},
{ldif_file, LDIFFile},
{resource, <<"resource">>},
{password, <<"password">>}
|Config].
NewConfig.
end_per_suite(_Config) ->
ok.
init_per_group(no_db, Config) ->
User = ?config(user, Config),
Server = ?config(server, Config),
Password = ?config(password, Config),
{atomic, ok} = ejabberd_auth:try_register(User, Server, Password),
re_register(Config),
Config;
init_per_group(mnesia, Config) ->
mod_muc:shutdown_rooms(?MNESIA_VHOST),
@@ -134,8 +65,9 @@ init_per_group(pgsql, Config) ->
{skip, {pgsql_not_available, Err}}
end;
init_per_group(ldap, Config) ->
{ok, _} = ldap_srv:start(?config(ldif_file, Config)),
set_opt(server, ?LDAP_VHOST, Config);
init_per_group(extauth, Config) ->
set_opt(server, ?EXTAUTH_VHOST, Config);
init_per_group(_GroupName, Config) ->
Pid = start_event_relay(),
set_opt(event_relay, Pid, Config).
@@ -150,6 +82,8 @@ end_per_group(no_db, _Config) ->
ok;
end_per_group(ldap, _Config) ->
ok;
end_per_group(extauth, _Config) ->
ok;
end_per_group(_GroupName, Config) ->
stop_event_relay(Config),
ok.
@@ -227,6 +161,7 @@ db_tests() ->
presence_broadcast,
last,
roster_get,
roster_ver,
private,
privacy,
blocking,
@@ -248,8 +183,14 @@ ldap_tests() ->
[test_auth,
vcard_get]}].
extauth_tests() ->
[{extauth_tests, [sequence],
[test_auth,
test_unregister]}].
groups() ->
[{ldap, [sequence], ldap_tests()},
{extauth, [sequence], extauth_tests()},
{no_db, [sequence], no_db_tests()},
{mnesia, [sequence], db_tests()},
{mysql, [sequence], db_tests()},
@@ -261,6 +202,7 @@ all() ->
{group, mnesia},
{group, mysql},
{group, pgsql},
{group, extauth},
stop_ejabberd].
stop_ejabberd(Config) ->
@@ -272,44 +214,6 @@ stop_ejabberd(Config) ->
test_connect(Config) ->
disconnect(connect(Config)).
connect(Config) ->
{ok, Sock} = ejabberd_socket:connect(
?config(server_host, Config),
?config(server_port, Config),
[binary, {packet, 0}, {active, false}]),
init_stream(set_opt(socket, Sock, Config)).
init_stream(Config) ->
ok = send_text(Config, io_lib:format(?STREAM_HEADER,
[?config(server, Config)])),
{xmlstreamstart, <<"stream:stream">>, Attrs} = recv(),
<<"jabber:client">> = xml:get_attr_s(<<"xmlns">>, Attrs),
<<"1.0">> = xml:get_attr_s(<<"version">>, Attrs),
#stream_features{sub_els = Fs} = recv(),
Mechs = lists:flatmap(
fun(#sasl_mechanisms{list = Ms}) ->
Ms;
(_) ->
[]
end, Fs),
lists:foldl(
fun(#feature_register{}, Acc) ->
set_opt(register, true, Acc);
(#starttls{}, Acc) ->
set_opt(starttls, true, Acc);
(#compression{methods = Ms}, Acc) ->
set_opt(compression, Ms, Acc);
(_, Acc) ->
Acc
end, set_opt(mechs, Mechs, Config), Fs).
disconnect(Config) ->
Socket = ?config(socket, Config),
ok = ejabberd_socket:send(Socket, ?STREAM_TRAILER),
{xmlstreamend, <<"stream:stream">>} = recv(),
ejabberd_socket:close(Socket),
Config.
test_starttls(Config) ->
case ?config(starttls, Config) of
true ->
@@ -318,15 +222,6 @@ test_starttls(Config) ->
{skipped, 'starttls_not_available'}
end.
starttls(Config) ->
send(Config, #starttls{}),
#starttls_proceed{} = recv(),
TLSSocket = ejabberd_socket:starttls(
?config(socket, Config),
[{certfile, ?config(certfile, Config)},
connect]),
init_stream(set_opt(socket, TLSSocket, Config)).
test_zlib(Config) ->
case ?config(compression, Config) of
[_|_] = Ms ->
@@ -340,12 +235,6 @@ test_zlib(Config) ->
{skipped, 'compression_not_available'}
end.
zlib(Config) ->
send(Config, #compress{methods = [<<"zlib">>]}),
#compressed{} = recv(),
ZlibSocket = ejabberd_socket:compress(?config(socket, Config)),
init_stream(set_opt(socket, ZlibSocket, Config)).
test_register(Config) ->
case ?config(register, Config) of
true ->
@@ -386,45 +275,63 @@ try_unregister(Config) ->
#stream_error{reason = conflict} = recv(),
Config.
auth_md5(Config) ->
Mechs = ?config(mechs, Config),
case lists:member(<<"DIGEST-MD5">>, Mechs) of
true ->
disconnect(auth_SASL(<<"DIGEST-MD5">>, Config));
false ->
disconnect(Config),
{skipped, 'DIGEST-MD5_not_available'}
end.
auth_plain(Config) ->
Mechs = ?config(mechs, Config),
case lists:member(<<"PLAIN">>, Mechs) of
true ->
disconnect(auth_SASL(<<"PLAIN">>, Config));
false ->
disconnect(Config),
{skipped, 'PLAIN_not_available'}
end.
test_auth(Config) ->
disconnect(auth(Config)).
auth(Config) ->
Mechs = ?config(mechs, Config),
HaveMD5 = lists:member(<<"DIGEST-MD5">>, Mechs),
HavePLAIN = lists:member(<<"PLAIN">>, Mechs),
if HavePLAIN ->
auth_SASL(<<"PLAIN">>, Config);
HaveMD5 ->
auth_SASL(<<"DIGEST-MD5">>, Config);
true ->
ct:fail(no_sasl_mechanisms_available)
end.
test_bind(Config) ->
disconnect(bind(Config)).
bind(Config) ->
#iq{type = result, sub_els = [#bind{}]} =
send_recv(
Config,
#iq{type = set,
sub_els = [#bind{resource = ?config(resource, Config)}]}),
Config.
test_open_session(Config) ->
disconnect(open_session(Config)).
open_session(Config) ->
#iq{type = result, sub_els = []} =
send_recv(Config, #iq{type = set, sub_els = [#session{}]}),
Config.
roster_get(Config) ->
#iq{type = result, sub_els = [#roster{items = []}]} =
send_recv(Config, #iq{type = get, sub_els = [#roster{}]}),
disconnect(Config).
roster_ver(Config) ->
%% Get initial "ver"
#iq{type = result, sub_els = [#roster{ver = Ver1, items = []}]} =
send_recv(Config, #iq{type = get,
sub_els = [#roster{ver = <<"">>}]}),
%% Should receive empty IQ-result
#iq{type = result, sub_els = []} =
send_recv(Config, #iq{type = get,
sub_els = [#roster{ver = Ver1}]}),
%% Attempting to subscribe to server's JID
send(Config, #presence{type = subscribe, to = server_jid(Config)}),
%% Receive a single roster push with the new "ver"
#iq{type = set, sub_els = [#roster{ver = Ver2}]} = recv(),
%% Requesting roster with the previous "ver". Should receive Ver2 again
#iq{type = result, sub_els = [#roster{ver = Ver2}]} =
send_recv(Config, #iq{type = get,
sub_els = [#roster{ver = Ver1}]}),
%% Now requesting roster with the newest "ver". Should receive empty IQ.
#iq{type = result, sub_els = []} =
send_recv(Config, #iq{type = get,
sub_els = [#roster{ver = Ver2}]}),
disconnect(Config).
presence(Config) ->
send(Config, #presence{}),
JID = my_jid(Config),
@@ -486,19 +393,22 @@ private(Config) ->
<<"some.conference.org">>,
<<>>)},
Storage = #bookmark_storage{conference = [Conference]},
StorageXMLOut = xmpp_codec:encode(Storage),
#iq{type = error} =
send_recv(Config, #iq{type = get, sub_els = [#private{}],
to = server_jid(Config)}),
#iq{type = result, sub_els = []} =
send_recv(
Config, #iq{type = set,
sub_els = [#private{sub_els = [Storage]}]}),
sub_els = [#private{xml_els = [StorageXMLOut]}]}),
#iq{type = result,
sub_els = [#private{sub_els = [Storage]}]} =
sub_els = [#private{xml_els = [StorageXMLIn]}]} =
send_recv(
Config,
#iq{type = get,
sub_els = [#private{sub_els = [#bookmark_storage{}]}]}),
sub_els = [#private{xml_els = [xmpp_codec:encode(
#bookmark_storage{})]}]}),
Storage = xmpp_codec:decode(StorageXMLIn),
disconnect(Config).
last(Config) ->
@@ -653,26 +563,33 @@ stats(Config) ->
disconnect(Config).
pubsub(Config) ->
true = is_feature_advertised(Config, ?NS_PUBSUB),
Features = get_features(Config, pubsub_jid(Config)),
true = lists:member(?NS_PUBSUB, Features),
%% Publish <presence/> element within node "presence"
ItemID = randoms:get_string(),
Node = <<"presence">>,
Item = #pubsub_item{id = ItemID, sub_els = [#presence{}]},
Item = #pubsub_item{id = ItemID,
xml_els = [xmpp_codec:encode(#presence{})]},
#iq{type = result,
sub_els = [#pubsub{publish = {<<"presence">>,
[#pubsub_item{id = ItemID}]}}]} =
sub_els = [#pubsub{publish = #pubsub_publish{
node = Node,
items = [#pubsub_item{id = ItemID}]}}]} =
send_recv(Config,
#iq{type = set, to = pubsub_jid(Config),
sub_els = [#pubsub{publish = {Node, [Item]}}]}),
sub_els = [#pubsub{publish = #pubsub_publish{
node = Node,
items = [Item]}}]}),
%% Subscribe to node "presence"
I = send(Config,
I1 = send(Config,
#iq{type = set, to = pubsub_jid(Config),
sub_els = [#pubsub{subscribe = {Node, my_jid(Config)}}]}),
sub_els = [#pubsub{subscribe = #pubsub_subscribe{
node = Node,
jid = my_jid(Config)}}]}),
?recv2(
#message{sub_els = [#pubsub_event{}, #delay{}]},
#iq{type = result, id = I}),
#iq{type = result, id = I1}),
%% Get subscriptions
true = is_feature_advertised(Config, ?PUBSUB("retrieve-subscriptions")),
true = lists:member(?PUBSUB("retrieve-subscriptions"), Features),
#iq{type = result,
sub_els =
[#pubsub{subscriptions =
@@ -680,35 +597,53 @@ pubsub(Config) ->
send_recv(Config, #iq{type = get, to = pubsub_jid(Config),
sub_els = [#pubsub{subscriptions = {none, []}}]}),
%% Get affiliations
true = is_feature_advertised(Config, ?PUBSUB("retrieve-affiliations")),
true = lists:member(?PUBSUB("retrieve-affiliations"), Features),
#iq{type = result,
sub_els = [#pubsub{
affiliations =
[#pubsub_affiliation{node = Node, type = owner}]}]} =
send_recv(Config, #iq{type = get, to = pubsub_jid(Config),
sub_els = [#pubsub{affiliations = []}]}),
%% Get subscription options
true = lists:member(?PUBSUB("subscription-options"), Features),
#iq{type = result, sub_els = [#pubsub{options = #pubsub_options{
node = Node}}]} =
send_recv(Config,
#iq{type = get, to = pubsub_jid(Config),
sub_els = [#pubsub{options = #pubsub_options{
node = Node,
jid = my_jid(Config)}}]}),
%% Fetching published items from node "presence"
#iq{type = result,
sub_els = [#pubsub{items = #pubsub_items{
node = Node,
items = [Item]}}]} =
send_recv(Config,
#iq{type = get, to = pubsub_jid(Config),
sub_els = [#pubsub{items = #pubsub_items{node = Node}}]}),
%% Deleting the item from the node
true = lists:member(?PUBSUB("delete-items"), Features),
I2 = send(Config,
#iq{type = set, to = pubsub_jid(Config),
sub_els = [#pubsub{retract = #pubsub_retract{
node = Node,
items = [#pubsub_item{id = ItemID}]}}]}),
?recv2(
#iq{type = result, id = I2, sub_els = []},
#message{sub_els = [#pubsub_event{
items = [#pubsub_event_items{
node = Node,
retract = [ItemID]}]},
#shim{headers = [{<<"Collection">>, Node}]}]}),
%% Unsubscribe from node "presence"
#iq{type = result, sub_els = []} =
send_recv(Config,
#iq{type = set, to = pubsub_jid(Config),
sub_els = [#pubsub{unsubscribe = #pubsub_unsubscribe{
node = Node,
jid = my_jid(Config)}}]}),
disconnect(Config).
auth_md5(Config) ->
Mechs = ?config(mechs, Config),
case lists:member(<<"DIGEST-MD5">>, Mechs) of
true ->
disconnect(auth_SASL(<<"DIGEST-MD5">>, Config));
false ->
disconnect(Config),
{skipped, 'DIGEST-MD5_not_available'}
end.
auth_plain(Config) ->
Mechs = ?config(mechs, Config),
case lists:member(<<"PLAIN">>, Mechs) of
true ->
disconnect(auth_SASL(<<"PLAIN">>, Config));
false ->
disconnect(Config),
{skipped, 'PLAIN_not_available'}
end.
roster_subscribe_master(Config) ->
send(Config, #presence{}),
#presence{} = recv(),
@@ -975,217 +910,13 @@ offline_slave(Config) ->
#message{from = Peer,
body = [#text{data = <<"body">>}],
subject = [#text{data = <<"subject">>}]}),
lists:foreach(
fun(#legacy_delay{}) -> ok;
(#delay{}) -> ok
end, SubEls),
true = lists:keymember(delay, 1, SubEls),
true = lists:keymember(legacy_delay, 1, SubEls),
disconnect(Config).
auth_SASL(Mech, Config) ->
{Response, SASL} = sasl_new(Mech,
?config(user, Config),
?config(server, Config),
?config(password, Config)),
send(Config, #sasl_auth{mechanism = Mech, text = Response}),
wait_auth_SASL_result(set_opt(sasl, SASL, Config)).
wait_auth_SASL_result(Config) ->
case recv() of
#sasl_success{} ->
ejabberd_socket:reset_stream(?config(socket, Config)),
send_text(Config,
io_lib:format(?STREAM_HEADER,
[?config(server, Config)])),
{xmlstreamstart, <<"stream:stream">>, Attrs} = recv(),
<<"jabber:client">> = xml:get_attr_s(<<"xmlns">>, Attrs),
<<"1.0">> = xml:get_attr_s(<<"version">>, Attrs),
#stream_features{} = recv(),
Config;
#sasl_challenge{text = ClientIn} ->
{Response, SASL} = (?config(sasl, Config))(ClientIn),
send(Config, #sasl_response{text = Response}),
wait_auth_SASL_result(set_opt(sasl, SASL, Config));
#sasl_failure{} ->
ct:fail(sasl_auth_failed)
end.
%%%===================================================================
%%% Aux functions
%%%===================================================================
re_register(Config) ->
User = ?config(user, Config),
Server = ?config(server, Config),
Pass = ?config(password, Config),
{atomic, ok} = ejabberd_auth:try_register(User, Server, Pass),
ok.
recv() ->
receive
{'$gen_event', {xmlstreamelement, El}} ->
Pkt = xmpp_codec:decode(fix_ns(El)),
ct:pal("recv: ~p ->~n~s", [El, xmpp_codec:pp(Pkt)]),
Pkt;
{'$gen_event', Event} ->
Event
end.
fix_ns(#xmlel{name = Tag, attrs = Attrs} = El)
when Tag == <<"stream:features">>; Tag == <<"stream:error">> ->
NewAttrs = [{<<"xmlns">>, <<"http://etherx.jabber.org/streams">>}
|lists:keydelete(<<"xmlns">>, 1, Attrs)],
El#xmlel{attrs = NewAttrs};
fix_ns(#xmlel{name = Tag, attrs = Attrs} = El)
when Tag == <<"message">>; Tag == <<"iq">>; Tag == <<"presence">> ->
NewAttrs = [{<<"xmlns">>, <<"jabber:client">>}
|lists:keydelete(<<"xmlns">>, 1, Attrs)],
El#xmlel{attrs = NewAttrs};
fix_ns(El) ->
El.
send_text(Config, Text) ->
ejabberd_socket:send(?config(socket, Config), Text).
send(State, Pkt) ->
{NewID, NewPkt} = case Pkt of
#message{id = I} ->
ID = id(I),
{ID, Pkt#message{id = ID}};
#presence{id = I} ->
ID = id(I),
{ID, Pkt#presence{id = ID}};
#iq{id = I} ->
ID = id(I),
{ID, Pkt#iq{id = ID}};
_ ->
{undefined, Pkt}
end,
El = xmpp_codec:encode(NewPkt),
ct:pal("sent: ~p <-~n~s", [El, xmpp_codec:pp(NewPkt)]),
ok = send_text(State, xml:element_to_binary(El)),
NewID.
send_recv(State, IQ) ->
ID = send(State, IQ),
#iq{id = ID} = recv().
sasl_new(<<"PLAIN">>, User, Server, Password) ->
{<<User/binary, $@, Server/binary, 0, User/binary, 0, Password/binary>>,
fun (_) -> {error, <<"Invalid SASL challenge">>} end};
sasl_new(<<"DIGEST-MD5">>, User, Server, Password) ->
{<<"">>,
fun (ServerIn) ->
case cyrsasl_digest:parse(ServerIn) of
bad -> {error, <<"Invalid SASL challenge">>};
KeyVals ->
Nonce = xml:get_attr_s(<<"nonce">>, KeyVals),
CNonce = id(),
DigestURI = <<"xmpp/", Server/binary>>,
Realm = Server,
NC = <<"00000001">>,
QOP = <<"auth">>,
AuthzId = <<"">>,
MyResponse = response(User, Password, Nonce, AuthzId,
Realm, CNonce, DigestURI, NC, QOP,
<<"AUTHENTICATE">>),
ServerResponse = response(User, Password, Nonce,
AuthzId, Realm, CNonce, DigestURI,
NC, QOP, <<"">>),
Resp = <<"username=\"", User/binary, "\",realm=\"",
Realm/binary, "\",nonce=\"", Nonce/binary,
"\",cnonce=\"", CNonce/binary, "\",nc=", NC/binary,
",qop=", QOP/binary, ",digest-uri=\"",
DigestURI/binary, "\",response=\"",
MyResponse/binary, "\"">>,
{Resp,
fun (ServerIn2) ->
case cyrsasl_digest:parse(ServerIn2) of
bad -> {error, <<"Invalid SASL challenge">>};
KeyVals2 ->
RspAuth = xml:get_attr_s(<<"rspauth">>,
KeyVals2),
if RspAuth == ServerResponse ->
{<<"">>,
fun (_) ->
{error,
<<"Invalid SASL challenge">>}
end};
true ->
{error, <<"Invalid SASL challenge">>}
end
end
end}
end
end}.
hex(S) ->
p1_sha:to_hexlist(S).
response(User, Passwd, Nonce, AuthzId, Realm, CNonce,
DigestURI, NC, QOP, A2Prefix) ->
A1 = case AuthzId of
<<"">> ->
<<((crypto:md5(<<User/binary, ":", Realm/binary, ":",
Passwd/binary>>)))/binary,
":", Nonce/binary, ":", CNonce/binary>>;
_ ->
<<((crypto:md5(<<User/binary, ":", Realm/binary, ":",
Passwd/binary>>)))/binary,
":", Nonce/binary, ":", CNonce/binary, ":",
AuthzId/binary>>
end,
A2 = case QOP of
<<"auth">> ->
<<A2Prefix/binary, ":", DigestURI/binary>>;
_ ->
<<A2Prefix/binary, ":", DigestURI/binary,
":00000000000000000000000000000000">>
end,
T = <<(hex((crypto:md5(A1))))/binary, ":", Nonce/binary,
":", NC/binary, ":", CNonce/binary, ":", QOP/binary,
":", (hex((crypto:md5(A2))))/binary>>,
hex((crypto:md5(T))).
my_jid(Config) ->
jlib:make_jid(?config(user, Config),
?config(server, Config),
?config(resource, Config)).
server_jid(Config) ->
jlib:make_jid(<<>>, ?config(server, Config), <<>>).
pubsub_jid(Config) ->
Server = ?config(server, Config),
jlib:make_jid(<<>>, <<"pubsub.", Server/binary>>, <<>>).
proxy_jid(Config) ->
Server = ?config(server, Config),
jlib:make_jid(<<>>, <<"proxy.", Server/binary>>, <<>>).
muc_jid(Config) ->
Server = ?config(server, Config),
jlib:make_jid(<<>>, <<"conference.", Server/binary>>, <<>>).
muc_room_jid(Config) ->
Server = ?config(server, Config),
jlib:make_jid(<<"test">>, <<"conference.", Server/binary>>, <<>>).
id() ->
id(undefined).
id(undefined) ->
randoms:get_string();
id(ID) ->
ID.
is_feature_advertised(Config, Feature) ->
is_feature_advertised(Config, Feature, server_jid(Config)).
is_feature_advertised(Config, Feature, To) ->
ID = send(Config, #iq{type = get, sub_els = [#disco_info{}], to = To}),
#iq{type = result, id = ID,
sub_els = [#disco_info{feature = Features}]} = recv(),
lists:member(Feature, Features).
bookmark_conference() ->
#bookmark_conference{name = <<"Some name">>,
autojoin = true,
@@ -1194,20 +925,6 @@ bookmark_conference() ->
<<"some.conference.org">>,
<<>>)}.
set_opt(Opt, Val, Config) ->
[{Opt, Val}|lists:keydelete(Opt, 1, Config)].
wait_for_master(Config) ->
put_event(Config, slave_ready),
master_ready = get_event(Config).
wait_for_slave(Config) ->
put_event(Config, master_ready),
slave_ready = get_event(Config).
make_iq_result(#iq{from = From} = IQ) ->
IQ#iq{type = result, to = From, from = undefined, sub_els = []}.
socks5_connect(#streamhost{host = Host, port = Port},
{SID, JID1, JID2}) ->
Hash = p1_sha:sha([SID, jlib:jid_to_string(JID1), jlib:jid_to_string(JID2)]),
@@ -1231,66 +948,6 @@ socks5_send(Sock, Data) ->
socks5_recv(Sock, Data) ->
{ok, Data} = gen_tcp:recv(Sock, size(Data)).
%%%===================================================================
%%% Clients puts and gets events via this relay.
%%%===================================================================
start_event_relay() ->
spawn(fun event_relay/0).
stop_event_relay(Config) ->
Pid = ?config(event_relay, Config),
exit(Pid, normal).
event_relay() ->
event_relay([], []).
event_relay(Events, Subscribers) ->
receive
{subscribe, From} ->
From ! {ok, self()},
lists:foreach(
fun(Event) -> From ! {event, Event, self()}
end, Events),
event_relay(Events, [From|Subscribers]);
{put, Event, From} ->
From ! {ok, self()},
lists:foreach(
fun(Pid) when Pid /= From ->
Pid ! {event, Event, self()};
(_) ->
ok
end, Subscribers),
event_relay([Event|Events], Subscribers)
end.
subscribe_to_events(Config) ->
Relay = ?config(event_relay, Config),
Relay ! {subscribe, self()},
receive
{ok, Relay} ->
ok
end.
put_event(Config, Event) ->
Relay = ?config(event_relay, Config),
Relay ! {put, Event, self()},
receive
{ok, Relay} ->
ok
end.
get_event(Config) ->
Relay = ?config(event_relay, Config),
receive
{event, Event, Relay} ->
Event
end.
insert(Val, N, Tuple) ->
L = tuple_to_list(Tuple),
{H, T} = lists:split(N-1, L),
list_to_tuple(H ++ [Val|T]).
%%%===================================================================
%%% SQL stuff
%%%===================================================================
+4
View File
@@ -3,6 +3,7 @@
"mnesia.localhost",
"mysql.localhost",
"pgsql.localhost",
"extauth.localhost",
"ldap.localhost"]}.
{define_macro, 'CERTFILE', "cert.pem"}.
{listen,
@@ -59,6 +60,9 @@
{mod_version, []}
]}.
{host_config, "localhost", [{auth_method, internal}]}.
{host_config, "extauth.localhost",
[{auth_method, external},
{extauth_program, "python extauth.py"}]}.
{host_config, "mnesia.localhost",
[{auth_method, internal},
{{add, modules}, [{mod_announce, [{db_type, internal}]},
+261
View File
@@ -0,0 +1,261 @@
host_config:
"pgsql.localhost":
odbc_username: "ejabberd_test"
odbc_type: pgsql
odbc_server: "localhost"
odbc_port: 5432
odbc_pool_size: 1
odbc_password: "ejabberd_test"
odbc_database: "ejabberd_test"
auth_method: odbc
modules:
mod_announce:
db_type: odbc
mod_blocking:
db_type: odbc
mod_caps:
db_type: odbc
mod_last:
db_type: odbc
mod_muc:
db_type: odbc
mod_offline:
db_type: odbc
mod_privacy:
db_type: odbc
mod_private:
db_type: odbc
mod_pubsub_odbc:
access_createnode: pubsub_createnode
ignore_pep_from_offline: true
last_item_cache: false
plugins:
- "flat"
- "hometree"
- "pep"
mod_roster:
versioning: true
store_current_id: true
db_type: odbc
mod_vcard:
db_type: odbc
mod_adhoc: []
mod_configure: []
mod_disco: []
mod_ping: []
mod_proxy65: []
mod_register:
welcome_message:
subject: "Welcome!"
body: "Hi.
Welcome to this XMPP server."
mod_stats: []
mod_time: []
mod_version: []
"mysql.localhost":
odbc_username: "ejabberd_test"
odbc_type: mysql
odbc_server: "localhost"
odbc_port: 3306
odbc_pool_size: 1
odbc_password: "ejabberd_test"
odbc_database: "ejabberd_test"
auth_method: odbc
modules:
mod_announce:
db_type: odbc
mod_blocking:
db_type: odbc
mod_caps:
db_type: odbc
mod_last:
db_type: odbc
mod_muc:
db_type: odbc
mod_offline:
db_type: odbc
mod_privacy:
db_type: odbc
mod_private:
db_type: odbc
mod_pubsub_odbc:
access_createnode: pubsub_createnode
ignore_pep_from_offline: true
last_item_cache: false
plugins:
- "flat"
- "hometree"
- "pep"
mod_roster:
versioning: true
store_current_id: true
db_type: odbc
mod_vcard:
db_type: odbc
mod_adhoc: []
mod_configure: []
mod_disco: []
mod_ping: []
mod_proxy65: []
mod_register:
welcome_message:
subject: "Welcome!"
body: "Hi.
Welcome to this XMPP server."
mod_stats: []
mod_time: []
mod_version: []
"mnesia.localhost":
auth_method: internal
modules:
mod_announce:
db_type: internal
mod_blocking:
db_type: internal
mod_caps:
db_type: internal
mod_last:
db_type: internal
mod_muc:
db_type: internal
mod_offline:
db_type: internal
mod_privacy:
db_type: internal
mod_private:
db_type: internal
mod_pubsub:
access_createnode: pubsub_createnode
ignore_pep_from_offline: true
last_item_cache: false
plugins:
- "flat"
- "hometree"
- "pep"
mod_roster:
versioning: true
store_current_id: true
db_type: internal
mod_vcard:
db_type: internal
mod_adhoc: []
mod_configure: []
mod_disco: []
mod_ping: []
mod_proxy65: []
mod_register:
welcome_message:
subject: "Welcome!"
body: "Hi.
Welcome to this XMPP server."
mod_stats: []
mod_time: []
mod_version: []
"localhost":
auth_method: internal
"ldap.localhost":
ldap_servers:
- "localhost"
ldap_rootdn: "cn=admin,dc=localhost"
ldap_port: 1389
ldap_password: "password"
ldap_base: "ou=users,dc=localhost"
auth_method: ldap
modules:
mod_vcard_ldap: []
mod_adhoc: []
mod_configure: []
mod_disco: []
mod_ping: []
mod_proxy65: []
mod_register:
welcome_message:
subject: "Welcome!"
body: "Hi.
Welcome to this XMPP server."
mod_stats: []
mod_time: []
mod_version: []
"extauth.localhost":
extauth_program: "python extauth.py"
auth_method: external
hosts:
- "localhost"
- "mnesia.localhost"
- "mysql.localhost"
- "pgsql.localhost"
- "extauth.localhost"
- "ldap.localhost"
access:
announce:
admin: allow
c2s:
blocked: deny
all: allow
c2s_shaper:
admin: none
all: normal
configure:
admin: allow
local:
local: allow
max_user_offline_messages:
admin: 5000
all: 100
max_user_sessions:
all: 10
muc:
all: allow
muc_admin:
admin: allow
muc_create:
local: allow
pubsub_createnode:
local: allow
register:
all: allow
s2s_shaper:
all: fast
acl:
local:
user_regexp: ""
define_macro:
CERTFILE: "cert.pem"
language: "en"
listen:
-
port: 5222
module: ejabberd_c2s
max_stanza_size: 65536
certfile: CERTFILE
zlib: true
starttls: true
shaper: c2s_shaper
access: c2s
-
port: 5269
module: ejabberd_s2s_in
-
port: 5280
module: ejabberd_http
captcha: true
loglevel: 4
max_fsm_queue: 1000
modules:
mod_adhoc: []
mod_configure: []
mod_disco: []
mod_ping: []
mod_proxy65: []
mod_register:
welcome_message:
subject: "Welcome!"
body: "Hi.
Welcome to this XMPP server."
mod_stats: []
mod_time: []
mod_version: []
registration_timeout: infinity
shaper:
fast: 50000
normal: 1000
+36
View File
@@ -0,0 +1,36 @@
import sys
import struct
def read():
(pkt_size,) = struct.unpack('>H', sys.stdin.read(2))
pkt = sys.stdin.read(pkt_size).split(':')
cmd = pkt[0]
args_num = len(pkt) - 1
if cmd == 'auth' and args_num == 3:
write(True)
elif cmd == 'isuser' and args_num == 2:
write(True)
elif cmd == 'setpass' and args_num == 3:
write(True)
elif cmd == 'tryregister' and args_num == 3:
write(True)
elif cmd == 'removeuser' and args_num == 2:
write(True)
elif cmd == 'removeuser3' and args_num == 3:
write(True)
else:
write(False)
read()
def write(result):
if result:
sys.stdout.write('\x00\x02\x00\x01')
else:
sys.stdout.write('\x00\x02\x00\x00')
sys.stdout.flush()
if __name__ == "__main__":
try:
read()
except struct.error:
pass
+8 -6
View File
@@ -22,9 +22,11 @@
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("logger.hrl").
-include("ELDAPv3.hrl").
-define(INFO_MSG(Fmt, Args), error_logger:info_msg(Fmt, Args)).
-define(ERROR_MSG(Fmt, Args), error_logger:error_msg(Fmt, Args)).
-define(TCP_SEND_TIMEOUT, 32000).
-define(SERVER, ?MODULE).
@@ -75,8 +77,8 @@ handle_cast(_Msg, State) ->
handle_info({'DOWN', MRef, _Type, _Object, Info},
#state{listener = MRef} = State) ->
?CRITICAL_MSG("listener died with reason ~p, terminating",
[Info]),
?ERROR_MSG("listener died with reason ~p, terminating",
[Info]),
{stop, normal, State};
handle_info(_Info, State) ->
{noreply, State}.
@@ -111,7 +113,7 @@ process(Socket, Tree) ->
fun(ReplyOp) ->
Reply = #'LDAPMessage'{messageID = Id,
protocolOp = ReplyOp},
?DEBUG("sent:~n~p", [Reply]),
%%?DEBUG("sent:~n~p", [Reply]),
{ok, Bytes} = asn1rt:encode(
'ELDAPv3', 'LDAPMessage', Reply),
gen_tcp:send(Socket, Bytes)
@@ -125,8 +127,8 @@ process(Socket, Tree) ->
Err
end.
process_msg(#'LDAPMessage'{protocolOp = Op} = Msg, TopTree) ->
?DEBUG("got:~n~p", [Msg]),
process_msg(#'LDAPMessage'{protocolOp = Op} = _Msg, TopTree) ->
%%?DEBUG("got:~n~p", [Msg]),
case Op of
{bindRequest,
#'BindRequest'{name = DN}} ->
+392
View File
@@ -0,0 +1,392 @@
%%%-------------------------------------------------------------------
%%% @author Evgeniy Khramtsov <>
%%% @copyright (C) 2013, Evgeniy Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 27 Jun 2013 by Evgeniy Khramtsov <>
%%%-------------------------------------------------------------------
-module(suite).
%% API
-compile(export_all).
-include("suite.hrl").
%%%===================================================================
%%% API
%%%===================================================================
init_config(Config) ->
DataDir = proplists:get_value(data_dir, Config),
PrivDir = proplists:get_value(priv_dir, Config),
[_, _|Tail] = lists:reverse(filename:split(DataDir)),
BaseDir = filename:join(lists:reverse(Tail)),
ConfigPath = filename:join([DataDir, "ejabberd.yml"]),
LogPath = filename:join([PrivDir, "ejabberd.log"]),
SASLPath = filename:join([PrivDir, "sasl.log"]),
MnesiaDir = filename:join([PrivDir, "mnesia"]),
CertFile = filename:join([DataDir, "cert.pem"]),
{ok, CWD} = file:get_cwd(),
{ok, _} = file:copy(CertFile, filename:join([CWD, "cert.pem"])),
ok = application:load(sasl),
ok = application:load(mnesia),
ok = application:load(ejabberd),
application:set_env(ejabberd, config, ConfigPath),
application:set_env(ejabberd, log_path, LogPath),
application:set_env(sasl, sasl_error_logger, {file, SASLPath}),
application:set_env(mnesia, dir, MnesiaDir),
[{server_port, 5222},
{server_host, "localhost"},
{server, ?COMMON_VHOST},
{user, <<"test_single">>},
{certfile, CertFile},
{base_dir, BaseDir},
{resource, <<"resource">>},
{password, <<"password">>}
|Config].
connect(Config) ->
{ok, Sock} = ejabberd_socket:connect(
?config(server_host, Config),
?config(server_port, Config),
[binary, {packet, 0}, {active, false}]),
init_stream(set_opt(socket, Sock, Config)).
init_stream(Config) ->
ok = send_text(Config, io_lib:format(?STREAM_HEADER,
[?config(server, Config)])),
{xmlstreamstart, <<"stream:stream">>, Attrs} = recv(),
<<"jabber:client">> = xml:get_attr_s(<<"xmlns">>, Attrs),
<<"1.0">> = xml:get_attr_s(<<"version">>, Attrs),
#stream_features{sub_els = Fs} = recv(),
Mechs = lists:flatmap(
fun(#sasl_mechanisms{list = Ms}) ->
Ms;
(_) ->
[]
end, Fs),
lists:foldl(
fun(#feature_register{}, Acc) ->
set_opt(register, true, Acc);
(#starttls{}, Acc) ->
set_opt(starttls, true, Acc);
(#compression{methods = Ms}, Acc) ->
set_opt(compression, Ms, Acc);
(_, Acc) ->
Acc
end, set_opt(mechs, Mechs, Config), Fs).
disconnect(Config) ->
Socket = ?config(socket, Config),
ok = ejabberd_socket:send(Socket, ?STREAM_TRAILER),
{xmlstreamend, <<"stream:stream">>} = recv(),
ejabberd_socket:close(Socket),
Config.
starttls(Config) ->
send(Config, #starttls{}),
#starttls_proceed{} = recv(),
TLSSocket = ejabberd_socket:starttls(
?config(socket, Config),
[{certfile, ?config(certfile, Config)},
connect]),
init_stream(set_opt(socket, TLSSocket, Config)).
zlib(Config) ->
send(Config, #compress{methods = [<<"zlib">>]}),
#compressed{} = recv(),
ZlibSocket = ejabberd_socket:compress(?config(socket, Config)),
init_stream(set_opt(socket, ZlibSocket, Config)).
auth(Config) ->
Mechs = ?config(mechs, Config),
HaveMD5 = lists:member(<<"DIGEST-MD5">>, Mechs),
HavePLAIN = lists:member(<<"PLAIN">>, Mechs),
if HavePLAIN ->
auth_SASL(<<"PLAIN">>, Config);
HaveMD5 ->
auth_SASL(<<"DIGEST-MD5">>, Config);
true ->
ct:fail(no_sasl_mechanisms_available)
end.
bind(Config) ->
#iq{type = result, sub_els = [#bind{}]} =
send_recv(
Config,
#iq{type = set,
sub_els = [#bind{resource = ?config(resource, Config)}]}),
Config.
open_session(Config) ->
#iq{type = result, sub_els = []} =
send_recv(Config, #iq{type = set, sub_els = [#session{}]}),
Config.
auth_SASL(Mech, Config) ->
{Response, SASL} = sasl_new(Mech,
?config(user, Config),
?config(server, Config),
?config(password, Config)),
send(Config, #sasl_auth{mechanism = Mech, text = Response}),
wait_auth_SASL_result(set_opt(sasl, SASL, Config)).
wait_auth_SASL_result(Config) ->
case recv() of
#sasl_success{} ->
ejabberd_socket:reset_stream(?config(socket, Config)),
send_text(Config,
io_lib:format(?STREAM_HEADER,
[?config(server, Config)])),
{xmlstreamstart, <<"stream:stream">>, Attrs} = recv(),
<<"jabber:client">> = xml:get_attr_s(<<"xmlns">>, Attrs),
<<"1.0">> = xml:get_attr_s(<<"version">>, Attrs),
#stream_features{} = recv(),
Config;
#sasl_challenge{text = ClientIn} ->
{Response, SASL} = (?config(sasl, Config))(ClientIn),
send(Config, #sasl_response{text = Response}),
wait_auth_SASL_result(set_opt(sasl, SASL, Config));
#sasl_failure{} ->
ct:fail(sasl_auth_failed)
end.
re_register(Config) ->
User = ?config(user, Config),
Server = ?config(server, Config),
Pass = ?config(password, Config),
{atomic, ok} = ejabberd_auth:try_register(User, Server, Pass),
ok.
recv() ->
receive
{'$gen_event', {xmlstreamelement, El}} ->
Pkt = xmpp_codec:decode(fix_ns(El)),
ct:pal("recv: ~p ->~n~s", [El, xmpp_codec:pp(Pkt)]),
Pkt;
{'$gen_event', Event} ->
Event
end.
fix_ns(#xmlel{name = Tag, attrs = Attrs} = El)
when Tag == <<"stream:features">>; Tag == <<"stream:error">> ->
NewAttrs = [{<<"xmlns">>, <<"http://etherx.jabber.org/streams">>}
|lists:keydelete(<<"xmlns">>, 1, Attrs)],
El#xmlel{attrs = NewAttrs};
fix_ns(#xmlel{name = Tag, attrs = Attrs} = El)
when Tag == <<"message">>; Tag == <<"iq">>; Tag == <<"presence">> ->
NewAttrs = [{<<"xmlns">>, <<"jabber:client">>}
|lists:keydelete(<<"xmlns">>, 1, Attrs)],
El#xmlel{attrs = NewAttrs};
fix_ns(El) ->
El.
send_text(Config, Text) ->
ejabberd_socket:send(?config(socket, Config), Text).
send(State, Pkt) ->
{NewID, NewPkt} = case Pkt of
#message{id = I} ->
ID = id(I),
{ID, Pkt#message{id = ID}};
#presence{id = I} ->
ID = id(I),
{ID, Pkt#presence{id = ID}};
#iq{id = I} ->
ID = id(I),
{ID, Pkt#iq{id = ID}};
_ ->
{undefined, Pkt}
end,
El = xmpp_codec:encode(NewPkt),
ct:pal("sent: ~p <-~n~s", [El, xmpp_codec:pp(NewPkt)]),
ok = send_text(State, xml:element_to_binary(El)),
NewID.
send_recv(State, IQ) ->
ID = send(State, IQ),
#iq{id = ID} = recv().
sasl_new(<<"PLAIN">>, User, Server, Password) ->
{<<User/binary, $@, Server/binary, 0, User/binary, 0, Password/binary>>,
fun (_) -> {error, <<"Invalid SASL challenge">>} end};
sasl_new(<<"DIGEST-MD5">>, User, Server, Password) ->
{<<"">>,
fun (ServerIn) ->
case cyrsasl_digest:parse(ServerIn) of
bad -> {error, <<"Invalid SASL challenge">>};
KeyVals ->
Nonce = xml:get_attr_s(<<"nonce">>, KeyVals),
CNonce = id(),
Realm = proplists:get_value(<<"realm">>, KeyVals, Server),
DigestURI = <<"xmpp/", Realm/binary>>,
NC = <<"00000001">>,
QOP = <<"auth">>,
AuthzId = <<"">>,
MyResponse = response(User, Password, Nonce, AuthzId,
Realm, CNonce, DigestURI, NC, QOP,
<<"AUTHENTICATE">>),
Resp = <<"username=\"", User/binary, "\",realm=\"",
Realm/binary, "\",nonce=\"", Nonce/binary,
"\",cnonce=\"", CNonce/binary, "\",nc=", NC/binary,
",qop=", QOP/binary, ",digest-uri=\"",
DigestURI/binary, "\",response=\"",
MyResponse/binary, "\"">>,
{Resp,
fun (ServerIn2) ->
case cyrsasl_digest:parse(ServerIn2) of
bad -> {error, <<"Invalid SASL challenge">>};
_KeyVals2 ->
{<<"">>,
fun (_) ->
{error,
<<"Invalid SASL challenge">>}
end}
end
end}
end
end}.
hex(S) ->
p1_sha:to_hexlist(S).
response(User, Passwd, Nonce, AuthzId, Realm, CNonce,
DigestURI, NC, QOP, A2Prefix) ->
A1 = case AuthzId of
<<"">> ->
<<((crypto:md5(<<User/binary, ":", Realm/binary, ":",
Passwd/binary>>)))/binary,
":", Nonce/binary, ":", CNonce/binary>>;
_ ->
<<((crypto:md5(<<User/binary, ":", Realm/binary, ":",
Passwd/binary>>)))/binary,
":", Nonce/binary, ":", CNonce/binary, ":",
AuthzId/binary>>
end,
A2 = case QOP of
<<"auth">> ->
<<A2Prefix/binary, ":", DigestURI/binary>>;
_ ->
<<A2Prefix/binary, ":", DigestURI/binary,
":00000000000000000000000000000000">>
end,
T = <<(hex((crypto:md5(A1))))/binary, ":", Nonce/binary,
":", NC/binary, ":", CNonce/binary, ":", QOP/binary,
":", (hex((crypto:md5(A2))))/binary>>,
hex((crypto:md5(T))).
my_jid(Config) ->
jlib:make_jid(?config(user, Config),
?config(server, Config),
?config(resource, Config)).
server_jid(Config) ->
jlib:make_jid(<<>>, ?config(server, Config), <<>>).
pubsub_jid(Config) ->
Server = ?config(server, Config),
jlib:make_jid(<<>>, <<"pubsub.", Server/binary>>, <<>>).
proxy_jid(Config) ->
Server = ?config(server, Config),
jlib:make_jid(<<>>, <<"proxy.", Server/binary>>, <<>>).
muc_jid(Config) ->
Server = ?config(server, Config),
jlib:make_jid(<<>>, <<"conference.", Server/binary>>, <<>>).
muc_room_jid(Config) ->
Server = ?config(server, Config),
jlib:make_jid(<<"test">>, <<"conference.", Server/binary>>, <<>>).
id() ->
id(undefined).
id(undefined) ->
randoms:get_string();
id(ID) ->
ID.
get_features(Config) ->
get_features(Config, server_jid(Config)).
get_features(Config, To) ->
#iq{type = result, sub_els = [#disco_info{features = Features}]} =
send_recv(Config, #iq{type = get, sub_els = [#disco_info{}], to = To}),
Features.
is_feature_advertised(Config, Feature) ->
is_feature_advertised(Config, Feature, server_jid(Config)).
is_feature_advertised(Config, Feature, To) ->
Features = get_features(Config, To),
lists:member(Feature, Features).
set_opt(Opt, Val, Config) ->
[{Opt, Val}|lists:keydelete(Opt, 1, Config)].
wait_for_master(Config) ->
put_event(Config, slave_ready),
master_ready = get_event(Config).
wait_for_slave(Config) ->
put_event(Config, master_ready),
slave_ready = get_event(Config).
make_iq_result(#iq{from = From} = IQ) ->
IQ#iq{type = result, to = From, from = undefined, sub_els = []}.
%%%===================================================================
%%% Clients puts and gets events via this relay.
%%%===================================================================
start_event_relay() ->
spawn(fun event_relay/0).
stop_event_relay(Config) ->
Pid = ?config(event_relay, Config),
exit(Pid, normal).
event_relay() ->
event_relay([], []).
event_relay(Events, Subscribers) ->
receive
{subscribe, From} ->
From ! {ok, self()},
lists:foreach(
fun(Event) -> From ! {event, Event, self()}
end, Events),
event_relay(Events, [From|Subscribers]);
{put, Event, From} ->
From ! {ok, self()},
lists:foreach(
fun(Pid) when Pid /= From ->
Pid ! {event, Event, self()};
(_) ->
ok
end, Subscribers),
event_relay([Event|Events], Subscribers)
end.
subscribe_to_events(Config) ->
Relay = ?config(event_relay, Config),
Relay ! {subscribe, self()},
receive
{ok, Relay} ->
ok
end.
put_event(Config, Event) ->
Relay = ?config(event_relay, Config),
Relay ! {put, Event, self()},
receive
{ok, Relay} ->
ok
end.
get_event(Config) ->
Relay = ?config(event_relay, Config),
receive
{event, Event, Relay} ->
Event
end.
+66
View File
@@ -0,0 +1,66 @@
-include_lib("common_test/include/ct.hrl").
-include("xml.hrl").
-include("ns.hrl").
-include("ejabberd.hrl").
-include("mod_proxy65.hrl").
-include("xmpp_codec.hrl").
-define(STREAM_HEADER,
<<"<?xml version='1.0'?><stream:stream "
"xmlns:stream='http://etherx.jabber.org/stream"
"s' xmlns='jabber:client' to='~s' version='1.0"
"'>">>).
-define(STREAM_TRAILER, <<"</stream:stream>">>).
-define(PUBSUB(Node), <<(?NS_PUBSUB)/binary, "#", Node>>).
-define(recv2(P1, P2),
(fun() ->
case {R1 = recv(), R2 = recv()} of
{P1, P2} -> {R1, R2};
{P2, P1} -> {R2, R1}
end
end)()).
-define(recv3(P1, P2, P3),
(fun() ->
case R3 = recv() of
P1 -> insert(R3, 1, ?recv2(P2, P3));
P2 -> insert(R3, 2, ?recv2(P1, P3));
P3 -> insert(R3, 3, ?recv2(P1, P2))
end
end)()).
-define(recv4(P1, P2, P3, P4),
(fun() ->
case R4 = recv() of
P1 -> insert(R4, 1, ?recv3(P2, P3, P4));
P2 -> insert(R4, 2, ?recv3(P1, P3, P4));
P3 -> insert(R4, 3, ?recv3(P1, P2, P4));
P4 -> insert(R4, 4, ?recv3(P1, P2, P3))
end
end)()).
-define(recv5(P1, P2, P3, P4, P5),
(fun() ->
case R5 = recv() of
P1 -> insert(R5, 1, ?recv4(P2, P3, P4, P5));
P2 -> insert(R5, 2, ?recv4(P1, P3, P4, P5));
P3 -> insert(R5, 3, ?recv4(P1, P2, P4, P5));
P4 -> insert(R5, 4, ?recv4(P1, P2, P3, P5));
P5 -> insert(R5, 5, ?recv4(P1, P2, P3, P4))
end
end)()).
-define(COMMON_VHOST, <<"localhost">>).
-define(MNESIA_VHOST, <<"mnesia.localhost">>).
-define(MYSQL_VHOST, <<"mysql.localhost">>).
-define(PGSQL_VHOST, <<"pgsql.localhost">>).
-define(LDAP_VHOST, <<"ldap.localhost">>).
-define(EXTAUTH_VHOST, <<"extauth.localhost">>).
insert(Val, N, Tuple) ->
L = tuple_to_list(Tuple),
{H, T} = lists:split(N-1, L),
list_to_tuple(H ++ [Val|T]).
+1966 -881
View File
File diff suppressed because it is too large Load Diff
+58 -32
View File
@@ -9,6 +9,10 @@
host :: binary(),
port = 1080 :: non_neg_integer()}).
-record(pubsub_unsubscribe, {node :: binary(),
jid :: any(),
subid :: binary()}).
-record(ping, {}).
-record(delay, {stamp :: any(),
@@ -34,12 +38,15 @@
-record(gone, {uri :: binary()}).
-record(private, {sub_els = [] :: [any()]}).
-record(private, {xml_els = [] :: [any()]}).
-record(p1_ack, {}).
-record(pubsub_item, {id :: binary(),
sub_els = [] :: [any()]}).
xml_els = [] :: [any()]}).
-record(pubsub_publish, {node :: binary(),
items = [] :: [#pubsub_item{}]}).
-record(roster_item, {jid :: any(),
name :: binary(),
@@ -60,7 +67,7 @@
-record(stat, {name :: binary(),
units :: binary(),
value :: binary(),
error = [] :: [{integer(),binary()}]}).
error = [] :: [{integer(),'undefined' | binary()}]}).
-record('see-other-host', {host :: binary()}).
@@ -81,6 +88,9 @@
-record(sasl_response, {text :: any()}).
-record(pubsub_subscribe, {node :: binary(),
jid :: any()}).
-record(sasl_auth, {mechanism :: binary(),
text :: any()}).
@@ -116,6 +126,8 @@
subid :: binary(),
type :: 'none' | 'pending' | 'subscribed' | 'unconfigured'}).
-record(shim, {headers = [] :: [{binary(),'undefined' | binary()}]}).
-record(caps, {hash :: binary(),
node :: binary(),
ver :: any()}).
@@ -127,11 +139,6 @@
-record(stats, {stat = [] :: [#stat{}]}).
-record(pubsub, {subscriptions :: {binary(),[#pubsub_subscription{}]},
affiliations :: [#pubsub_affiliation{}],
publish :: {binary(),[#pubsub_item{}]},
subscribe :: {binary(),_}}).
-record(pubsub_items, {node :: binary(),
max_items :: non_neg_integer(),
subid :: binary(),
@@ -150,6 +157,10 @@
x400 = false :: boolean(),
userid :: binary()}).
-record(pubsub_retract, {node :: binary(),
notify = false :: any(),
items = [] :: [#pubsub_item{}]}).
-record(text, {lang :: binary(),
data :: binary()}).
@@ -171,9 +182,9 @@
values = [] :: [binary()],
options = [] :: [binary()]}).
-record(version, {version_name :: binary(),
version_ver :: binary(),
version_os :: binary()}).
-record(version, {name :: binary(),
ver :: binary(),
os :: binary()}).
-record(muc_invite, {reason :: binary(),
from :: any(),
@@ -233,6 +244,7 @@
-record(identity, {category :: binary(),
type :: binary(),
lang :: binary(),
name :: binary()}).
-record(bookmark_conference, {name :: binary(),
@@ -244,23 +256,23 @@
-record(register, {registered = false :: boolean(),
remove = false :: boolean(),
instructions :: binary(),
username :: binary(),
nick :: binary(),
password :: binary(),
name :: binary(),
first :: binary(),
last :: binary(),
email :: binary(),
address :: binary(),
city :: binary(),
state :: binary(),
zip :: binary(),
phone :: binary(),
url :: binary(),
date :: binary(),
misc :: binary(),
text :: binary(),
key :: binary()}).
username :: 'none' | binary(),
nick :: 'none' | binary(),
password :: 'none' | binary(),
name :: 'none' | binary(),
first :: 'none' | binary(),
last :: 'none' | binary(),
email :: 'none' | binary(),
address :: 'none' | binary(),
city :: 'none' | binary(),
state :: 'none' | binary(),
zip :: 'none' | binary(),
phone :: 'none' | binary(),
url :: 'none' | binary(),
date :: 'none' | binary(),
misc :: 'none' | binary(),
text :: 'none' | binary(),
key :: 'none' | binary()}).
-record(bookmark_url, {name :: binary(),
url :: binary()}).
@@ -310,9 +322,23 @@
-record(muc_owner, {destroy :: #muc_owner_destroy{},
config :: #xdata{}}).
-record(pubsub_options, {node :: binary(),
jid :: any(),
subid :: binary(),
xdata :: #xdata{}}).
-record(pubsub, {subscriptions :: {'none' | binary(),[#pubsub_subscription{}]},
affiliations :: [#pubsub_affiliation{}],
publish :: #pubsub_publish{},
subscribe :: #pubsub_subscribe{},
unsubscribe :: #pubsub_unsubscribe{},
options :: #pubsub_options{},
items :: #pubsub_items{},
retract :: #pubsub_retract{}}).
-record(disco_info, {node :: binary(),
identity = [] :: [#identity{}],
feature = [] :: [binary()],
identities = [] :: [#identity{}],
features = [] :: [binary()],
xdata = [] :: [#xdata{}]}).
-record(sasl_mechanisms, {list = [] :: [binary()]}).
@@ -362,8 +388,8 @@
items = [] :: [#privacy_item{}]}).
-record(privacy, {lists = [] :: [#privacy_list{}],
default :: binary(),
active :: binary()}).
default :: 'none' | binary(),
active :: 'none' | binary()}).
-record(stream_error, {reason :: atom() | #'see-other-host'{},
text :: #text{}}).
+1937 -1874
View File
File diff suppressed because it is too large Load Diff