mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-24 16:23:40 +01:00
Change configuration file format to YAML
This commit is contained in:
parent
f68dfacbbf
commit
91a74e3e27
12
Makefile.in
12
Makefile.in
@ -109,9 +109,9 @@ install: all
|
|||||||
#
|
#
|
||||||
# Configuration files
|
# Configuration files
|
||||||
$(INSTALL) -d -m 750 $(G_USER) $(ETCDIR)
|
$(INSTALL) -d -m 750 $(G_USER) $(ETCDIR)
|
||||||
[ -f $(ETCDIR)/ejabberd.cfg ] \
|
[ -f $(ETCDIR)/ejabberd.yml ] \
|
||||||
&& $(INSTALL) -b -m 640 $(G_USER) ejabberd.cfg.example $(ETCDIR)/ejabberd.cfg-new \
|
&& $(INSTALL) -b -m 640 $(G_USER) ejabberd.yml.example $(ETCDIR)/ejabberd.yml-new \
|
||||||
|| $(INSTALL) -b -m 640 $(G_USER) ejabberd.cfg.example $(ETCDIR)/ejabberd.cfg
|
|| $(INSTALL) -b -m 640 $(G_USER) ejabberd.yml.example $(ETCDIR)/ejabberd.yml
|
||||||
$(SED) -e "s*{{rootdir}}*@prefix@*" \
|
$(SED) -e "s*{{rootdir}}*@prefix@*" \
|
||||||
-e "s*{{installuser}}*@INSTALLUSER@*" \
|
-e "s*{{installuser}}*@INSTALLUSER@*" \
|
||||||
-e "s*{{libdir}}*@libdir@*" \
|
-e "s*{{libdir}}*@libdir@*" \
|
||||||
@ -120,9 +120,9 @@ install: all
|
|||||||
-e "s*{{docdir}}*@docdir@*" \
|
-e "s*{{docdir}}*@docdir@*" \
|
||||||
-e "s*{{erl}}*@ERL@*" ejabberdctl.template \
|
-e "s*{{erl}}*@ERL@*" ejabberdctl.template \
|
||||||
> ejabberdctl.example
|
> ejabberdctl.example
|
||||||
[ -f $(ETCDIR)/ejabberdctl.cfg ] \
|
[ -f $(ETCDIR)/ejabberdctl.yml ] \
|
||||||
&& $(INSTALL) -b -m 640 $(G_USER) ejabberdctl.cfg.example $(ETCDIR)/ejabberdctl.cfg-new \
|
&& $(INSTALL) -b -m 640 $(G_USER) ejabberdctl.yml.example $(ETCDIR)/ejabberdctl.yml-new \
|
||||||
|| $(INSTALL) -b -m 640 $(G_USER) ejabberdctl.cfg.example $(ETCDIR)/ejabberdctl.cfg
|
|| $(INSTALL) -b -m 640 $(G_USER) ejabberdctl.yml.example $(ETCDIR)/ejabberdctl.yml
|
||||||
$(INSTALL) -b -m 644 $(G_USER) inetrc $(ETCDIR)/inetrc
|
$(INSTALL) -b -m 644 $(G_USER) inetrc $(ETCDIR)/inetrc
|
||||||
#
|
#
|
||||||
# Administration script
|
# Administration script
|
||||||
|
1
README
1
README
@ -9,6 +9,7 @@ To compile ejabberd you need:
|
|||||||
- GNU Make
|
- GNU Make
|
||||||
- GCC
|
- GCC
|
||||||
- Libexpat 1.95 or higher
|
- Libexpat 1.95 or higher
|
||||||
|
- Libyaml 1.4 or higher
|
||||||
- Erlang/OTP R15B or higher.
|
- Erlang/OTP R15B or higher.
|
||||||
- OpenSSL 0.9.8 or higher, for STARTTLS, SASL and SSL encryption.
|
- OpenSSL 0.9.8 or higher, for STARTTLS, SASL and SSL encryption.
|
||||||
- Zlib 1.2.3 or higher, for Stream Compression support
|
- Zlib 1.2.3 or higher, for Stream Compression support
|
||||||
|
2967
doc/guide.html
2967
doc/guide.html
File diff suppressed because it is too large
Load Diff
2687
doc/guide.tex
2687
doc/guide.tex
File diff suppressed because it is too large
Load Diff
@ -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:
|
|
615
ejabberd.yml.example
Normal file
615
ejabberd.yml.example
Normal file
@ -0,0 +1,615 @@
|
|||||||
|
###
|
||||||
|
### 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": global
|
||||||
|
|
||||||
|
## Local users: don't modify this.
|
||||||
|
##
|
||||||
|
local:
|
||||||
|
user_regexp:
|
||||||
|
- "": global
|
||||||
|
|
||||||
|
##
|
||||||
|
## More examples of ACLs
|
||||||
|
##
|
||||||
|
## jabberorg:
|
||||||
|
## server:
|
||||||
|
## - "jabber.org"
|
||||||
|
## aleksey:
|
||||||
|
## user:
|
||||||
|
## - "aleksey": "jabber.ru"
|
||||||
|
## test:
|
||||||
|
## user_regexp:
|
||||||
|
## - "^test": global
|
||||||
|
## user_glob:
|
||||||
|
## - "test*": global
|
||||||
|
|
||||||
|
##
|
||||||
|
## 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
|
@ -65,7 +65,7 @@ if [ -f "$EJABBERDCTL_CONFIG_PATH" ] ; then
|
|||||||
. "$EJABBERDCTL_CONFIG_PATH"
|
. "$EJABBERDCTL_CONFIG_PATH"
|
||||||
fi
|
fi
|
||||||
if [ "$EJABBERD_CONFIG_PATH" = "" ] ; then
|
if [ "$EJABBERD_CONFIG_PATH" = "" ] ; then
|
||||||
EJABBERD_CONFIG_PATH=$ETCDIR/ejabberd.cfg
|
EJABBERD_CONFIG_PATH=$ETCDIR/ejabberd.yml
|
||||||
fi
|
fi
|
||||||
if [ "$LOGS_DIR" = "" ] ; then
|
if [ "$LOGS_DIR" = "" ] ; then
|
||||||
LOGS_DIR={{localstatedir}}/log/ejabberd
|
LOGS_DIR={{localstatedir}}/log/ejabberd
|
||||||
|
@ -56,6 +56,7 @@ Deps = [{p1_cache_tab, ".*", {git, "git://github.com/processone/cache_tab"}},
|
|||||||
{p1_tls, ".*", {git, "git://github.com/processone/tls"}},
|
{p1_tls, ".*", {git, "git://github.com/processone/tls"}},
|
||||||
{p1_stringprep, ".*", {git, "git://github.com/processone/stringprep"}},
|
{p1_stringprep, ".*", {git, "git://github.com/processone/stringprep"}},
|
||||||
{p1_xml, ".*", {git, "git://github.com/processone/xml"}},
|
{p1_xml, ".*", {git, "git://github.com/processone/xml"}},
|
||||||
|
{p1_yaml, ".*", {git, "git://github.com/processone/p1_yaml"}},
|
||||||
{xmlrpc, ".*", {git, "git://github.com/rds13/xmlrpc"}}],
|
{xmlrpc, ".*", {git, "git://github.com/rds13/xmlrpc"}}],
|
||||||
|
|
||||||
ConfigureCmd = fun(Pkg, Flags) ->
|
ConfigureCmd = fun(Pkg, Flags) ->
|
||||||
|
@ -28,7 +28,7 @@ ConfiguredOTPApps = lists:flatmap(
|
|||||||
|
|
||||||
OTPApps = RequiredOTPApps ++ ConfiguredOTPApps,
|
OTPApps = RequiredOTPApps ++ ConfiguredOTPApps,
|
||||||
|
|
||||||
DepRequiredApps = [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(
|
DepConfiguredApps = lists:flatmap(
|
||||||
fun({mysql, true}) -> [p1_mysql];
|
fun({mysql, true}) -> [p1_mysql];
|
||||||
@ -39,7 +39,7 @@ DepConfiguredApps = lists:flatmap(
|
|||||||
({json, true}) -> [jiffy];
|
({json, true}) -> [jiffy];
|
||||||
({iconv, true}) -> [p1_iconv];
|
({iconv, true}) -> [p1_iconv];
|
||||||
({http, true}) -> [ibrowse, lhttpc];
|
({http, true}) -> [ibrowse, lhttpc];
|
||||||
({lager, true}) -> [lager];
|
({lager, true}) -> [lager, goldrush];
|
||||||
({lager, false}) -> [p1_logger];
|
({lager, false}) -> [p1_logger];
|
||||||
(_) -> []
|
(_) -> []
|
||||||
end, Vars),
|
end, Vars),
|
||||||
@ -89,7 +89,7 @@ Overlay = [
|
|||||||
{template, "files/erl", "\{\{erts_vsn\}\}/bin/erl"},
|
{template, "files/erl", "\{\{erts_vsn\}\}/bin/erl"},
|
||||||
{template, "../ejabberdctl.template", "bin/ejabberdctl"},
|
{template, "../ejabberdctl.template", "bin/ejabberdctl"},
|
||||||
{copy, "../ejabberdctl.cfg.example", "etc/ejabberd/ejabberdctl.cfg"},
|
{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, "../inetrc", "etc/ejabberd/inetrc"},
|
||||||
{copy, "files/install_upgrade.escript", "bin/install_upgrade.escript"}
|
{copy, "files/install_upgrade.escript", "bin/install_upgrade.escript"}
|
||||||
],
|
],
|
||||||
|
399
src/acl.erl
399
src/acl.erl
@ -29,35 +29,38 @@
|
|||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-export([start/0, to_record/3, add/3, add_list/3,
|
-export([start/0, to_record/3, add/3, add_list/3,
|
||||||
add_local/3, add_list_local/3,
|
add_local/3, add_list_local/3, load_from_config/0,
|
||||||
match_rule/3, match_acl/3]).
|
match_rule/3, match_acl/3, transform_options/1]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
|
|
||||||
-record(acl, {aclname, aclspec}).
|
-record(acl, {aclname, aclspec}).
|
||||||
|
-record(access, {name :: access_name(),
|
||||||
|
rules = [] :: [access_rule()]}).
|
||||||
|
|
||||||
-type regexp() :: binary().
|
-type regexp() :: binary().
|
||||||
-type glob() :: binary().
|
-type glob() :: binary().
|
||||||
|
-type access_name() :: atom().
|
||||||
|
-type access_rule() :: {atom(), any()}.
|
||||||
|
-type host() :: binary().
|
||||||
-type aclname() :: {atom(), binary() | global}.
|
-type aclname() :: {atom(), binary() | global}.
|
||||||
-type aclspec() :: all | none |
|
-type aclspec() :: all | none |
|
||||||
{user, binary()} |
|
{user, {binary(), host()} | binary()} |
|
||||||
{user, binary(), binary()} |
|
|
||||||
{server, binary()} |
|
{server, binary()} |
|
||||||
{resource, binary()} |
|
{resource, binary()} |
|
||||||
{user_regexp, regexp()} |
|
{user_regexp, {regexp(), host()} | regexp()} |
|
||||||
{shared_group, binary()} |
|
{shared_group, {binary(), host()} | binary()} |
|
||||||
{shared_group, binary(), binary()} |
|
{user_regexp, {regexp(), host()} | regexp()} |
|
||||||
{user_regexp, regexp(), binary()} |
|
|
||||||
{server_regexp, regexp()} |
|
{server_regexp, regexp()} |
|
||||||
{resource_regexp, regexp()} |
|
{resource_regexp, regexp()} |
|
||||||
{node_regexp, regexp(), regexp()} |
|
{node_regexp, {regexp(), regexp()}} |
|
||||||
{user_glob, glob()} |
|
{user_glob, {glob(), host()} | glob()} |
|
||||||
{user_glob, glob(), binary()} |
|
|
||||||
{server_glob, glob()} |
|
{server_glob, glob()} |
|
||||||
{resource_glob, glob()} |
|
{resource_glob, glob()} |
|
||||||
{node_glob, glob(), glob()}.
|
{ip, {inet:ip_address(), integer()}} |
|
||||||
|
{node_glob, {glob(), glob()}}.
|
||||||
|
|
||||||
-type acl() :: #acl{aclname :: aclname(),
|
-type acl() :: #acl{aclname :: aclname(),
|
||||||
aclspec :: aclspec()}.
|
aclspec :: aclspec()}.
|
||||||
@ -65,12 +68,23 @@
|
|||||||
-export_type([acl/0]).
|
-export_type([acl/0]).
|
||||||
|
|
||||||
start() ->
|
start() ->
|
||||||
|
case catch mnesia:table_info(acl, storage_type) of
|
||||||
|
disc_copies ->
|
||||||
|
mnesia:delete_table(acl);
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
mnesia:create_table(acl,
|
mnesia:create_table(acl,
|
||||||
[{disc_copies, [node()]}, {type, bag},
|
[{ram_copies, [node()]}, {type, bag},
|
||||||
{local_content, true},
|
{local_content, true},
|
||||||
{attributes, record_info(fields, acl)}]),
|
{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),
|
mnesia:add_table_copy(acl, node(), ram_copies),
|
||||||
update_table(),
|
mnesia:add_table_copy(access, node(), ram_copies),
|
||||||
|
load_from_config(),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
-spec to_record(binary(), atom(), aclspec()) -> acl().
|
-spec to_record(binary(), atom(), aclspec()) -> acl().
|
||||||
@ -82,7 +96,7 @@ to_record(Host, ACLName, ACLSpec) ->
|
|||||||
-spec add(binary(), aclname(), aclspec()) -> ok | {error, any()}.
|
-spec add(binary(), aclname(), aclspec()) -> ok | {error, any()}.
|
||||||
|
|
||||||
add(Host, ACLName, ACLSpec) ->
|
add(Host, ACLName, ACLSpec) ->
|
||||||
{ResL, BadNodes} = rpc:multicall(ejabberd_cluster:get_nodes(),
|
{ResL, BadNodes} = rpc:multicall(mnesia:system_info(running_db_nodes),
|
||||||
?MODULE, add_local,
|
?MODULE, add_local,
|
||||||
[Host, ACLName, ACLSpec]),
|
[Host, ACLName, ACLSpec]),
|
||||||
case lists:keyfind(aborted, 1, ResL) of
|
case lists:keyfind(aborted, 1, ResL) of
|
||||||
@ -109,7 +123,7 @@ add_local(Host, ACLName, ACLSpec) ->
|
|||||||
-spec add_list(binary(), [acl()], boolean()) -> ok | {error, any()}.
|
-spec add_list(binary(), [acl()], boolean()) -> ok | {error, any()}.
|
||||||
|
|
||||||
add_list(Host, ACLs, Clear) ->
|
add_list(Host, ACLs, Clear) ->
|
||||||
{ResL, BadNodes} = rpc:multicall(ejabberd_cluster:get_nodes(),
|
{ResL, BadNodes} = rpc:multicall(mnesia:system_info(running_db_nodes),
|
||||||
?MODULE, add_list_local,
|
?MODULE, add_list_local,
|
||||||
[Host, ACLs, Clear]),
|
[Host, ACLs, Clear]),
|
||||||
case lists:keyfind(aborted, 1, ResL) of
|
case lists:keyfind(aborted, 1, ResL) of
|
||||||
@ -147,117 +161,184 @@ add_list_local(Host, ACLs, Clear) ->
|
|||||||
end,
|
end,
|
||||||
mnesia:transaction(F).
|
mnesia:transaction(F).
|
||||||
|
|
||||||
normalize(A) -> jlib:nodeprep(iolist_to_binary(A)).
|
-spec add_access(binary() | global,
|
||||||
|
access_name(), [access_rule()]) -> ok | {error, any()}.
|
||||||
|
|
||||||
normalize_spec({A, B}) -> {A, normalize(B)};
|
add_access(Host, Access, Rules) ->
|
||||||
normalize_spec({A, B, C}) ->
|
case mnesia:transaction(
|
||||||
{A, normalize(B), normalize(C)};
|
fun() ->
|
||||||
normalize_spec(all) -> all;
|
mnesia:write(
|
||||||
normalize_spec(none) -> none.
|
#access{name = {Access, Host},
|
||||||
|
rules = Rules})
|
||||||
|
end) of
|
||||||
|
{atomic, ok} ->
|
||||||
|
ok;
|
||||||
|
Err ->
|
||||||
|
{error, Err}
|
||||||
|
end.
|
||||||
|
|
||||||
-spec match_rule(global | binary(), atom(), jid() | ljid()) -> any().
|
-spec load_from_config() -> ok.
|
||||||
|
|
||||||
match_rule(global, Rule, JID) ->
|
load_from_config() ->
|
||||||
case Rule of
|
Hosts = [global|?MYHOSTS],
|
||||||
all -> allow;
|
lists:foreach(
|
||||||
none -> deny;
|
fun(Host) ->
|
||||||
_ ->
|
ACLs = ejabberd_config:get_option(
|
||||||
case ejabberd_config:get_global_option(
|
{acl, Host}, fun(V) -> V end, []),
|
||||||
{access, Rule, global}, fun(V) -> V end)
|
AccessRules = ejabberd_config:get_option(
|
||||||
of
|
{access, Host}, fun(V) -> V end, []),
|
||||||
undefined -> deny;
|
lists:foreach(
|
||||||
GACLs -> match_acls(GACLs, JID, global)
|
fun({ACLName, SpecList}) ->
|
||||||
end
|
lists:foreach(
|
||||||
end;
|
fun({ACLType, ACLSpecs}) when is_list(ACLSpecs) ->
|
||||||
match_rule(Host, Rule, JID) ->
|
lists:foreach(
|
||||||
case Rule of
|
fun(ACLSpec) ->
|
||||||
all -> allow;
|
add(Host, ACLName,
|
||||||
none -> deny;
|
{ACLType, ACLSpec})
|
||||||
_ ->
|
end, lists:flatten(ACLSpecs));
|
||||||
case ejabberd_config:get_global_option(
|
({ACLType, ACLSpecs}) ->
|
||||||
{access, Rule, global}, fun(V) -> V end)
|
add(Host, ACLName, {ACLType, ACLSpecs})
|
||||||
of
|
end, lists:flatten(SpecList))
|
||||||
undefined ->
|
end, ACLs),
|
||||||
case ejabberd_config:get_global_option(
|
lists:foreach(
|
||||||
{access, Rule, Host}, fun(V) -> V end)
|
fun({Access, Rules}) ->
|
||||||
of
|
add_access(Host, Access, Rules)
|
||||||
undefined -> deny;
|
end, AccessRules)
|
||||||
ACLs -> match_acls(ACLs, JID, Host)
|
end, Hosts).
|
||||||
end;
|
|
||||||
GACLs ->
|
b(S) ->
|
||||||
case ejabberd_config:get_global_option(
|
iolist_to_binary(S).
|
||||||
{access, Rule, Host}, fun(V) -> V end)
|
|
||||||
of
|
nodeprep(S) ->
|
||||||
undefined -> match_acls(GACLs, JID, Host);
|
jlib:nodeprep(b(S)).
|
||||||
ACLs ->
|
|
||||||
case lists:reverse(GACLs) of
|
nameprep(S) ->
|
||||||
[{allow, all} | Rest] ->
|
jlib:nameprep(b(S)).
|
||||||
match_acls(lists:reverse(Rest) ++
|
|
||||||
ACLs ++ [{allow, all}],
|
resourceprep(S) ->
|
||||||
JID, Host);
|
jlib:resourceprep(b(S)).
|
||||||
_ -> match_acls(GACLs ++ ACLs, JID, Host)
|
|
||||||
end
|
normalize_spec(Spec) ->
|
||||||
end
|
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
|
||||||
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([], _, _Host) -> deny;
|
||||||
match_acls([{Access, ACL} | ACLs], JID, Host) ->
|
match_acls([{ACL, Access} | ACLs], JID, Host) ->
|
||||||
case match_acl(ACL, JID, Host) of
|
case match_acl(ACL, JID, Host) of
|
||||||
true -> Access;
|
true -> Access;
|
||||||
_ -> match_acls(ACLs, JID, Host)
|
_ -> match_acls(ACLs, JID, Host)
|
||||||
end.
|
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) ->
|
match_acl(ACL, JID, Host) ->
|
||||||
case ACL of
|
|
||||||
all -> true;
|
|
||||||
none -> false;
|
|
||||||
_ ->
|
|
||||||
{User, Server, Resource} = jlib:jid_tolower(JID),
|
{User, Server, Resource} = jlib:jid_tolower(JID),
|
||||||
lists:any(fun (#acl{aclspec = Spec}) ->
|
lists:any(
|
||||||
|
fun(#acl{aclspec = Spec}) ->
|
||||||
case Spec of
|
case Spec of
|
||||||
all -> true;
|
all -> true;
|
||||||
|
{user, {U, S}} -> U == User andalso S == Server;
|
||||||
{user, U} ->
|
{user, U} ->
|
||||||
U == User andalso
|
U == User andalso
|
||||||
(Host == Server orelse
|
lists:member(Server, ?MYHOSTS);
|
||||||
Host == global andalso
|
|
||||||
lists:member(Server, ?MYHOSTS));
|
|
||||||
{user, U, S} -> U == User andalso S == Server;
|
|
||||||
{server, S} -> S == Server;
|
{server, S} -> S == Server;
|
||||||
{resource, R} -> R == Resource;
|
{resource, R} -> R == Resource;
|
||||||
{user_regexp, UR} ->
|
{shared_group, {G, H}} ->
|
||||||
(Host == Server orelse
|
Mod = loaded_shared_roster_module(H),
|
||||||
Host == global andalso
|
Mod:is_user_in_group({User, Server}, G, H);
|
||||||
lists:member(Server, ?MYHOSTS))
|
|
||||||
andalso is_regexp_match(User, UR);
|
|
||||||
{shared_group, G} ->
|
{shared_group, G} ->
|
||||||
Mod = loaded_shared_roster_module(Host),
|
Mod = loaded_shared_roster_module(Host),
|
||||||
Mod:is_user_in_group({User, Server}, G, Host);
|
Mod:is_user_in_group({User, Server}, G, Host);
|
||||||
{shared_group, G, H} ->
|
{user_regexp, {UR, S}} ->
|
||||||
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);
|
S == Server andalso is_regexp_match(User, UR);
|
||||||
|
{user_regexp, UR} ->
|
||||||
|
lists:member(Server, ?MYHOSTS)
|
||||||
|
andalso is_regexp_match(User, UR);
|
||||||
{server_regexp, SR} ->
|
{server_regexp, SR} ->
|
||||||
is_regexp_match(Server, SR);
|
is_regexp_match(Server, SR);
|
||||||
{resource_regexp, RR} ->
|
{resource_regexp, RR} ->
|
||||||
is_regexp_match(Resource, RR);
|
is_regexp_match(Resource, RR);
|
||||||
{node_regexp, UR, SR} ->
|
{node_regexp, {UR, SR}} ->
|
||||||
is_regexp_match(Server, SR) andalso
|
is_regexp_match(Server, SR) andalso
|
||||||
is_regexp_match(User, UR);
|
is_regexp_match(User, UR);
|
||||||
{user_glob, UR} ->
|
{user_glob, {UR, S}} ->
|
||||||
(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);
|
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);
|
{server_glob, SR} -> is_glob_match(Server, SR);
|
||||||
{resource_glob, RR} ->
|
{resource_glob, RR} ->
|
||||||
is_glob_match(Resource, RR);
|
is_glob_match(Resource, RR);
|
||||||
{node_glob, UR, SR} ->
|
{node_glob, {UR, SR}} ->
|
||||||
is_glob_match(Server, SR) andalso
|
is_glob_match(Server, SR) andalso
|
||||||
is_glob_match(User, UR);
|
is_glob_match(User, UR);
|
||||||
WrongSpec ->
|
WrongSpec ->
|
||||||
@ -268,9 +349,8 @@ match_acl(ACL, JID, Host) ->
|
|||||||
false
|
false
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
ets:lookup(acl, {ACL, global}) ++
|
ets:lookup(acl, {ACL, Host}) ++
|
||||||
ets:lookup(acl, {ACL, Host}))
|
ets:lookup(acl, {ACL, global})).
|
||||||
end.
|
|
||||||
|
|
||||||
is_regexp_match(String, RegExp) ->
|
is_regexp_match(String, RegExp) ->
|
||||||
case ejabberd_regexp:run(String, RegExp) of
|
case ejabberd_regexp:run(String, RegExp) of
|
||||||
@ -286,34 +366,115 @@ is_glob_match(String, Glob) ->
|
|||||||
is_regexp_match(String,
|
is_regexp_match(String,
|
||||||
ejabberd_regexp:sh_to_awk(Glob)).
|
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) ->
|
loaded_shared_roster_module(Host) ->
|
||||||
case gen_mod:is_loaded(Host, mod_shared_roster_ldap) of
|
case gen_mod:is_loaded(Host, mod_shared_roster_ldap) of
|
||||||
true -> mod_shared_roster_ldap;
|
true -> mod_shared_roster_ldap;
|
||||||
false -> mod_shared_roster
|
false -> mod_shared_roster
|
||||||
end.
|
end.
|
||||||
|
|
||||||
update_table() ->
|
parse_ip_netmask(S) ->
|
||||||
Fields = record_info(fields, acl),
|
case str:tokens(S, <<"/">>) of
|
||||||
case mnesia:table_info(acl, attributes) of
|
[IPStr] ->
|
||||||
Fields ->
|
case inet_parse:address(binary_to_list(IPStr)) of
|
||||||
ejabberd_config:convert_table_to_binary(
|
{ok, {_, _, _, _} = IP} -> {ok, IP, 32};
|
||||||
acl, Fields, bag,
|
{ok, {_, _, _, _, _, _, _, _} = IP} -> {ok, IP, 128};
|
||||||
fun(#acl{aclspec = Spec}) when is_tuple(Spec) ->
|
_ -> error
|
||||||
element(2, Spec);
|
end;
|
||||||
(_) ->
|
[IPStr, MaskStr] ->
|
||||||
'$next'
|
case catch jlib:binary_to_integer(MaskStr) of
|
||||||
end,
|
Mask when is_integer(Mask), Mask >= 0 ->
|
||||||
fun(#acl{aclname = {ACLName, Host},
|
case inet_parse:address(binary_to_list(IPStr)) of
|
||||||
aclspec = Spec} = R) ->
|
{ok, {_, _, _, _} = IP} when Mask =< 32 ->
|
||||||
NewHost = if Host == global ->
|
{ok, IP, Mask};
|
||||||
Host;
|
{ok, {_, _, _, _, _, _, _, _} = IP} when Mask =< 128 ->
|
||||||
true ->
|
{ok, IP, Mask};
|
||||||
iolist_to_binary(Host)
|
_ -> error
|
||||||
end,
|
end;
|
||||||
R#acl{aclname = {ACLName, NewHost},
|
_ -> error
|
||||||
aclspec = normalize_spec(Spec)}
|
end;
|
||||||
end);
|
_ -> error
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating acl table", []),
|
|
||||||
mnesia:transform_table(acl, ignore, Fields)
|
|
||||||
end.
|
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].
|
||||||
|
@ -204,11 +204,11 @@ get_local_fqdn() ->
|
|||||||
Str when is_binary(Str) -> Str;
|
Str when is_binary(Str) -> Str;
|
||||||
_ ->
|
_ ->
|
||||||
<<"unknown-fqdn, please configure fqdn "
|
<<"unknown-fqdn, please configure fqdn "
|
||||||
"option in ejabberd.cfg!">>
|
"option in ejabberd.yml!">>
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_local_fqdn2() ->
|
get_local_fqdn2() ->
|
||||||
case ejabberd_config:get_local_option(
|
case ejabberd_config:get_option(
|
||||||
fqdn, fun iolist_to_binary/1) of
|
fqdn, fun iolist_to_binary/1) of
|
||||||
ConfiguredFqdn when is_binary(ConfiguredFqdn) ->
|
ConfiguredFqdn when is_binary(ConfiguredFqdn) ->
|
||||||
ConfiguredFqdn;
|
ConfiguredFqdn;
|
||||||
|
@ -164,6 +164,12 @@ commands() ->
|
|||||||
module = ejd2odbc, function = export,
|
module = ejd2odbc, function = export,
|
||||||
args = [{host, string}, {file, string}], result = {res, rescode}},
|
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],
|
#ejabberd_commands{name = delete_expired_messages, tags = [purge],
|
||||||
desc = "Delete expired offline messages from database",
|
desc = "Delete expired offline messages from database",
|
||||||
module = ?MODULE, function = delete_expired_messages,
|
module = ?MODULE, function = delete_expired_messages,
|
||||||
|
@ -47,12 +47,14 @@ start(normal, _Args) ->
|
|||||||
db_init(),
|
db_init(),
|
||||||
start(),
|
start(),
|
||||||
translate:start(),
|
translate:start(),
|
||||||
acl:start(),
|
|
||||||
ejabberd_ctl:init(),
|
ejabberd_ctl:init(),
|
||||||
ejabberd_commands:init(),
|
ejabberd_commands:init(),
|
||||||
ejabberd_admin:start(),
|
ejabberd_admin:start(),
|
||||||
gen_mod:start(),
|
gen_mod:start(),
|
||||||
ejabberd_config:start(),
|
ejabberd_config:start(),
|
||||||
|
set_loglevel_from_config(),
|
||||||
|
acl:start(),
|
||||||
|
shaper:start(),
|
||||||
connect_nodes(),
|
connect_nodes(),
|
||||||
Sup = ejabberd_sup:start_link(),
|
Sup = ejabberd_sup:start_link(),
|
||||||
ejabberd_rdbms:start(),
|
ejabberd_rdbms:start(),
|
||||||
@ -119,7 +121,7 @@ db_init() ->
|
|||||||
start_modules() ->
|
start_modules() ->
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(Host) ->
|
fun(Host) ->
|
||||||
Modules = ejabberd_config:get_local_option(
|
Modules = ejabberd_config:get_option(
|
||||||
{modules, Host},
|
{modules, Host},
|
||||||
fun(Mods) ->
|
fun(Mods) ->
|
||||||
lists:map(
|
lists:map(
|
||||||
@ -137,7 +139,7 @@ start_modules() ->
|
|||||||
stop_modules() ->
|
stop_modules() ->
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(Host) ->
|
fun(Host) ->
|
||||||
Modules = ejabberd_config:get_local_option(
|
Modules = ejabberd_config:get_option(
|
||||||
{modules, Host},
|
{modules, Host},
|
||||||
fun(Mods) ->
|
fun(Mods) ->
|
||||||
lists:map(
|
lists:map(
|
||||||
@ -152,7 +154,7 @@ stop_modules() ->
|
|||||||
end, ?MYHOSTS).
|
end, ?MYHOSTS).
|
||||||
|
|
||||||
connect_nodes() ->
|
connect_nodes() ->
|
||||||
Nodes = ejabberd_config:get_local_option(
|
Nodes = ejabberd_config:get_option(
|
||||||
cluster_nodes,
|
cluster_nodes,
|
||||||
fun(Ns) ->
|
fun(Ns) ->
|
||||||
true = lists:all(fun is_atom/1, Ns),
|
true = lists:all(fun is_atom/1, Ns),
|
||||||
@ -212,9 +214,17 @@ delete_pid_file() ->
|
|||||||
file:delete(PidFilename)
|
file:delete(PidFilename)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
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() ->
|
start_apps() ->
|
||||||
ejabberd:start_app(sasl),
|
ejabberd:start_app(sasl),
|
||||||
ejabberd:start_app(ssl),
|
ejabberd:start_app(ssl),
|
||||||
|
ejabberd:start_app(p1_yaml),
|
||||||
ejabberd:start_app(p1_tls),
|
ejabberd:start_app(p1_tls),
|
||||||
ejabberd:start_app(p1_xml),
|
ejabberd:start_app(p1_xml),
|
||||||
ejabberd:start_app(p1_stringprep),
|
ejabberd:start_app(p1_stringprep),
|
||||||
|
@ -423,7 +423,7 @@ auth_modules() ->
|
|||||||
%% Return the list of authenticated modules for a given host
|
%% Return the list of authenticated modules for a given host
|
||||||
auth_modules(Server) ->
|
auth_modules(Server) ->
|
||||||
LServer = jlib:nameprep(Server),
|
LServer = jlib:nameprep(Server),
|
||||||
Methods = ejabberd_config:get_local_option(
|
Methods = ejabberd_config:get_option(
|
||||||
{auth_method, LServer},
|
{auth_method, LServer},
|
||||||
fun(V) when is_list(V) ->
|
fun(V) when is_list(V) ->
|
||||||
true = lists:all(fun is_atom/1, V),
|
true = lists:all(fun is_atom/1, V),
|
||||||
|
@ -104,7 +104,7 @@ is_login_anonymous_enabled(Host) ->
|
|||||||
%% Return the anonymous protocol to use: sasl_anon|login_anon|both
|
%% Return the anonymous protocol to use: sasl_anon|login_anon|both
|
||||||
%% defaults to login_anon
|
%% defaults to login_anon
|
||||||
anonymous_protocol(Host) ->
|
anonymous_protocol(Host) ->
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
{anonymous_protocol, Host},
|
{anonymous_protocol, Host},
|
||||||
fun(sasl_anon) -> sasl_anon;
|
fun(sasl_anon) -> sasl_anon;
|
||||||
(login_anon) -> login_anon;
|
(login_anon) -> login_anon;
|
||||||
@ -115,7 +115,7 @@ anonymous_protocol(Host) ->
|
|||||||
%% Return true if multiple connections have been allowed in the config file
|
%% Return true if multiple connections have been allowed in the config file
|
||||||
%% defaults to false
|
%% defaults to false
|
||||||
allow_multiple_connections(Host) ->
|
allow_multiple_connections(Host) ->
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
{allow_multiple_connections, Host},
|
{allow_multiple_connections, Host},
|
||||||
fun(V) when is_boolean(V) -> V end,
|
fun(V) when is_boolean(V) -> V end,
|
||||||
false).
|
false).
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
%%% API
|
%%% API
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
start(Host) ->
|
start(Host) ->
|
||||||
Cmd = ejabberd_config:get_local_option(
|
Cmd = ejabberd_config:get_option(
|
||||||
{extauth_program, Host},
|
{extauth_program, Host},
|
||||||
fun(V) ->
|
fun(V) ->
|
||||||
binary_to_list(iolist_to_binary(V))
|
binary_to_list(iolist_to_binary(V))
|
||||||
@ -171,7 +171,7 @@ remove_user(User, Server, Password) ->
|
|||||||
|
|
||||||
%% @spec (Host::string()) -> false | {true, CacheTime::integer()}
|
%% @spec (Host::string()) -> false | {true, CacheTime::integer()}
|
||||||
get_cache_option(Host) ->
|
get_cache_option(Host) ->
|
||||||
case ejabberd_config:get_local_option(
|
case ejabberd_config:get_option(
|
||||||
{extauth_cache, Host},
|
{extauth_cache, Host},
|
||||||
fun(I) when is_integer(I), I > 0 -> I end) of
|
fun(I) when is_integer(I), I > 0 -> I end) of
|
||||||
undefined -> false;
|
undefined -> false;
|
||||||
|
@ -406,7 +406,7 @@ is_scrammed() ->
|
|||||||
|
|
||||||
is_option_scram() ->
|
is_option_scram() ->
|
||||||
scram ==
|
scram ==
|
||||||
ejabberd_config:get_local_option({auth_password_format, ?MYNAME},
|
ejabberd_config:get_option({auth_password_format, ?MYNAME},
|
||||||
fun(V) -> V end).
|
fun(V) -> V end).
|
||||||
|
|
||||||
maybe_alert_password_scrammed_without_option() ->
|
maybe_alert_password_scrammed_without_option() ->
|
||||||
|
@ -369,8 +369,10 @@ parse_options(Host) ->
|
|||||||
{iolist_to_binary(U),
|
{iolist_to_binary(U),
|
||||||
iolist_to_binary(P)};
|
iolist_to_binary(P)};
|
||||||
({U}) ->
|
({U}) ->
|
||||||
|
{iolist_to_binary(U)};
|
||||||
|
(U) ->
|
||||||
{iolist_to_binary(U)}
|
{iolist_to_binary(U)}
|
||||||
end, Us)
|
end, lists:flatten(Us))
|
||||||
end, [{<<"uid">>, <<"%u">>}]),
|
end, [{<<"uid">>, <<"%u">>}]),
|
||||||
UIDs = eldap_utils:uids_domain_subst(Host, UIDsTemp),
|
UIDs = eldap_utils:uids_domain_subst(Host, UIDsTemp),
|
||||||
SubFilter = eldap_utils:generate_subfilter(UIDs),
|
SubFilter = eldap_utils:generate_subfilter(UIDs),
|
||||||
|
@ -107,13 +107,13 @@ store_type() -> external.
|
|||||||
%% Internal functions
|
%% Internal functions
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
get_pam_service(Host) ->
|
get_pam_service(Host) ->
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
{pam_service, Host},
|
{pam_service, Host},
|
||||||
fun iolist_to_binary/1,
|
fun iolist_to_binary/1,
|
||||||
<<"ejabberd">>).
|
<<"ejabberd">>).
|
||||||
|
|
||||||
get_pam_userinfotype(Host) ->
|
get_pam_userinfotype(Host) ->
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
{pam_userinfotype, Host},
|
{pam_userinfotype, Host},
|
||||||
fun(username) -> username;
|
fun(username) -> username;
|
||||||
(jid) -> jid
|
(jid) -> jid
|
||||||
|
@ -47,7 +47,8 @@
|
|||||||
del_aux_field/2,
|
del_aux_field/2,
|
||||||
get_subscription/2,
|
get_subscription/2,
|
||||||
broadcast/4,
|
broadcast/4,
|
||||||
get_subscribed/1]).
|
get_subscribed/1,
|
||||||
|
transform_listen_option/2]).
|
||||||
|
|
||||||
%% gen_fsm callbacks
|
%% gen_fsm callbacks
|
||||||
-export([init/1,
|
-export([init/1,
|
||||||
@ -233,11 +234,10 @@ init([{SockMod, Socket}, Opts]) ->
|
|||||||
{value, {_, XS}} -> XS;
|
{value, {_, XS}} -> XS;
|
||||||
_ -> false
|
_ -> false
|
||||||
end,
|
end,
|
||||||
Zlib = lists:member(zlib, Opts),
|
Zlib = proplists:get_bool(zlib, Opts),
|
||||||
StartTLS = lists:member(starttls, Opts),
|
StartTLS = proplists:get_bool(starttls, Opts),
|
||||||
StartTLSRequired = lists:member(starttls_required,
|
StartTLSRequired = proplists:get_bool(starttls_required, Opts),
|
||||||
Opts),
|
TLSEnabled = proplists:get_bool(tls, Opts),
|
||||||
TLSEnabled = lists:member(tls, Opts),
|
|
||||||
TLS = StartTLS orelse
|
TLS = StartTLS orelse
|
||||||
StartTLSRequired orelse TLSEnabled,
|
StartTLSRequired orelse TLSEnabled,
|
||||||
TLSOpts1 = lists:filter(fun ({certfile, _}) -> true;
|
TLSOpts1 = lists:filter(fun ({certfile, _}) -> true;
|
||||||
@ -682,7 +682,7 @@ wait_for_feature_request({xmlstreamelement, El},
|
|||||||
when TLS == true, TLSEnabled == false,
|
when TLS == true, TLSEnabled == false,
|
||||||
SockMod == gen_tcp ->
|
SockMod == gen_tcp ->
|
||||||
TLSOpts = case
|
TLSOpts = case
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
{domain_certfile, StateData#state.server},
|
{domain_certfile, StateData#state.server},
|
||||||
fun iolist_to_binary/1)
|
fun iolist_to_binary/1)
|
||||||
of
|
of
|
||||||
@ -876,7 +876,7 @@ resource_conflict_action(U, S, R) ->
|
|||||||
R)
|
R)
|
||||||
of
|
of
|
||||||
true ->
|
true ->
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
{resource_conflict, S},
|
{resource_conflict, S},
|
||||||
fun(setresource) -> setresource;
|
fun(setresource) -> setresource;
|
||||||
(closeold) -> closeold;
|
(closeold) -> closeold;
|
||||||
@ -2279,7 +2279,7 @@ fsm_limit_opts(Opts) ->
|
|||||||
case lists:keysearch(max_fsm_queue, 1, Opts) of
|
case lists:keysearch(max_fsm_queue, 1, Opts) of
|
||||||
{value, {_, N}} when is_integer(N) -> [{max_queue, N}];
|
{value, {_, N}} when is_integer(N) -> [{max_queue, N}];
|
||||||
_ ->
|
_ ->
|
||||||
case ejabberd_config:get_local_option(
|
case ejabberd_config:get_option(
|
||||||
max_fsm_queue,
|
max_fsm_queue,
|
||||||
fun(I) when is_integer(I), I > 0 -> I end) of
|
fun(I) when is_integer(I), I > 0 -> I end) of
|
||||||
undefined -> [];
|
undefined -> [];
|
||||||
@ -2377,3 +2377,6 @@ pack_string(String, Pack) ->
|
|||||||
{value, PackedString} -> {PackedString, Pack};
|
{value, PackedString} -> {PackedString, Pack};
|
||||||
none -> {String, gb_trees:insert(String, String, Pack)}
|
none -> {String, gb_trees:insert(String, String, Pack)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
transform_listen_option(Opt, Opts) ->
|
||||||
|
[Opt|Opts].
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
%% Get first c2s configuration limitations to apply it to other c2s
|
%% Get first c2s configuration limitations to apply it to other c2s
|
||||||
%% connectors.
|
%% connectors.
|
||||||
get_c2s_limits() ->
|
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 -> [];
|
undefined -> [];
|
||||||
C2SFirstListen ->
|
C2SFirstListen ->
|
||||||
case lists:keysearch(ejabberd_c2s, 2, C2SFirstListen) of
|
case lists:keysearch(ejabberd_c2s, 2, C2SFirstListen) of
|
||||||
|
@ -504,7 +504,7 @@ do_create_image(Key) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
get_prog_name() ->
|
get_prog_name() ->
|
||||||
case ejabberd_config:get_local_option(
|
case ejabberd_config:get_option(
|
||||||
captcha_cmd,
|
captcha_cmd,
|
||||||
fun(FileName) ->
|
fun(FileName) ->
|
||||||
F = iolist_to_binary(FileName),
|
F = iolist_to_binary(FileName),
|
||||||
@ -521,7 +521,7 @@ get_prog_name() ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
get_url(Str) ->
|
get_url(Str) ->
|
||||||
CaptchaHost = ejabberd_config:get_local_option(
|
CaptchaHost = ejabberd_config:get_option(
|
||||||
captcha_host,
|
captcha_host,
|
||||||
fun iolist_to_binary/1,
|
fun iolist_to_binary/1,
|
||||||
<<"">>),
|
<<"">>),
|
||||||
@ -549,7 +549,7 @@ get_transfer_protocol(PortString) ->
|
|||||||
get_captcha_transfer_protocol(PortListeners).
|
get_captcha_transfer_protocol(PortListeners).
|
||||||
|
|
||||||
get_port_listeners(PortNumber) ->
|
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,
|
lists:filter(fun ({{Port, _Ip, _Netp}, _Module1,
|
||||||
_Opts1})
|
_Opts1})
|
||||||
when Port == PortNumber ->
|
when Port == PortNumber ->
|
||||||
@ -579,7 +579,7 @@ get_captcha_transfer_protocol([_ | Listeners]) ->
|
|||||||
|
|
||||||
is_limited(undefined) -> false;
|
is_limited(undefined) -> false;
|
||||||
is_limited(Limiter) ->
|
is_limited(Limiter) ->
|
||||||
case ejabberd_config:get_local_option(
|
case ejabberd_config:get_option(
|
||||||
captcha_limit,
|
captcha_limit,
|
||||||
fun(I) when is_integer(I), I > 0 -> I end) of
|
fun(I) when is_integer(I), I > 0 -> I end) of
|
||||||
undefined -> false;
|
undefined -> false;
|
||||||
|
@ -27,16 +27,16 @@
|
|||||||
-module(ejabberd_config).
|
-module(ejabberd_config).
|
||||||
-author('alexey@process-one.net').
|
-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,
|
add_global_option/2, add_local_option/2,
|
||||||
get_global_option/2, get_local_option/2,
|
get_global_option/2, get_local_option/2,
|
||||||
get_global_option/3, get_local_option/3,
|
get_global_option/3, get_local_option/3,
|
||||||
get_option/2, get_option/3, add_option/2]).
|
get_option/2, get_option/3, add_option/2,
|
||||||
-export([get_vh_by_auth_method/1]).
|
get_vh_by_auth_method/1, is_file_readable/1,
|
||||||
-export([is_file_readable/1]).
|
get_version/0, get_myhosts/0, get_mylang/0,
|
||||||
-export([get_version/0, get_myhosts/0, get_mylang/0]).
|
prepare_opt_val/4, convert_table_to_binary/5,
|
||||||
-export([prepare_opt_val/4]).
|
transform_options/1, collect_options/1,
|
||||||
-export([convert_table_to_binary/5]).
|
convert_to_yaml/1, convert_to_yaml/2]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
@ -53,21 +53,29 @@
|
|||||||
|
|
||||||
|
|
||||||
start() ->
|
start() ->
|
||||||
|
case catch mnesia:table_info(local_config, storage_type) of
|
||||||
|
disc_copies ->
|
||||||
|
mnesia:delete_table(local_config);
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
mnesia:create_table(local_config,
|
mnesia:create_table(local_config,
|
||||||
[{disc_copies, [node()]},
|
[{ram_copies, [node()]},
|
||||||
{local_content, true},
|
{local_content, true},
|
||||||
{attributes, record_info(fields, local_config)}]),
|
{attributes, record_info(fields, local_config)}]),
|
||||||
mnesia:add_table_copy(local_config, node(), ram_copies),
|
mnesia:add_table_copy(local_config, node(), ram_copies),
|
||||||
Config = get_ejabberd_config_path(),
|
Config = get_ejabberd_config_path(),
|
||||||
load_file(Config),
|
State = read_file(Config),
|
||||||
%% This start time is used by mod_last:
|
%% This start time is used by mod_last:
|
||||||
add_option(node_start, now()),
|
{MegaSecs, Secs, _} = now(),
|
||||||
ok.
|
UnixTime = MegaSecs*1000000 + Secs,
|
||||||
|
State1 = set_option({node_start, global}, UnixTime, State),
|
||||||
|
set_opts(State1).
|
||||||
|
|
||||||
%% @doc Get the filename of the ejabberd configuration file.
|
%% @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.
|
%% 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()
|
%% @spec () -> string()
|
||||||
get_ejabberd_config_path() ->
|
get_ejabberd_config_path() ->
|
||||||
case application:get_env(config) of
|
case application:get_env(config) of
|
||||||
@ -81,16 +89,59 @@ get_ejabberd_config_path() ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc Load the ejabberd configuration file.
|
%% @doc Read the ejabberd configuration file.
|
||||||
%% It also includes additional configuration files and replaces macros.
|
%% It also includes additional configuration files and replaces macros.
|
||||||
%% This function will crash if finds some error in the configuration file.
|
%% This function will crash if finds some error in the configuration file.
|
||||||
%% @spec (File::string()) -> ok
|
%% @spec (File::string()) -> #state{}.
|
||||||
load_file(File) ->
|
read_file(File) ->
|
||||||
Terms = get_plain_terms_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),
|
State = lists:foldl(fun search_hosts/2, #state{}, Terms),
|
||||||
Terms_macros = replace_macros(Terms),
|
{Head, Tail} = lists:partition(
|
||||||
Res = lists:foldl(fun process_term/2, State, Terms_macros),
|
fun({host_config, _}) -> false;
|
||||||
set_opts(Res).
|
({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.
|
%% @doc Read an ejabberd configuration file and return the terms.
|
||||||
%% Input is an absolute or relative path to an ejabberd config file.
|
%% Input is an absolute or relative path to an ejabberd config file.
|
||||||
@ -99,22 +150,47 @@ load_file(File) ->
|
|||||||
%% and the terms in those files were included.
|
%% and the terms in those files were included.
|
||||||
%% @spec(string()) -> [term()]
|
%% @spec(string()) -> [term()]
|
||||||
%% @spec(iolist()) -> [term()]
|
%% @spec(iolist()) -> [term()]
|
||||||
get_plain_terms_file(File) when is_binary(File) ->
|
get_plain_terms_file(File) ->
|
||||||
get_plain_terms_file(binary_to_list(File));
|
get_plain_terms_file(File, [{include_files, true}]).
|
||||||
get_plain_terms_file(File1) ->
|
|
||||||
|
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),
|
File = get_absolute_path(File1),
|
||||||
case file:consult(File) of
|
case consult(File) of
|
||||||
{ok, Terms} ->
|
{ok, Terms} ->
|
||||||
BinTerms = strings_to_binary(Terms),
|
BinTerms = strings_to_binary(Terms),
|
||||||
|
case proplists:get_bool(include_files, Opts) of
|
||||||
|
true ->
|
||||||
include_config_files(BinTerms);
|
include_config_files(BinTerms);
|
||||||
{error, {LineNumber, erl_parse, _ParseMessage} = Reason} ->
|
false ->
|
||||||
ExitText = describe_config_problem(File, Reason, LineNumber),
|
BinTerms
|
||||||
?ERROR_MSG(ExitText, []),
|
end;
|
||||||
exit_or_halt(ExitText);
|
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
ExitText = describe_config_problem(File, Reason),
|
?ERROR_MSG(Reason, []),
|
||||||
?ERROR_MSG(ExitText, []),
|
exit_or_halt(Reason)
|
||||||
exit_or_halt(ExitText)
|
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.
|
end.
|
||||||
|
|
||||||
%% @doc Convert configuration filename to absolute path.
|
%% @doc Convert configuration filename to absolute path.
|
||||||
@ -158,7 +234,7 @@ search_hosts(Term, State) ->
|
|||||||
|
|
||||||
add_hosts_to_option(Hosts, State) ->
|
add_hosts_to_option(Hosts, State) ->
|
||||||
PrepHosts = normalize_hosts(Hosts),
|
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) ->
|
||||||
normalize_hosts(Hosts,[]).
|
normalize_hosts(Hosts,[]).
|
||||||
@ -232,21 +308,37 @@ exit_or_halt(ExitText) ->
|
|||||||
%% @doc Include additional configuration files in the list of terms.
|
%% @doc Include additional configuration files in the list of terms.
|
||||||
%% @spec ([term()]) -> [term()]
|
%% @spec ([term()]) -> [term()]
|
||||||
include_config_files(Terms) ->
|
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) ->
|
transform_include_option({include_config_file, File}) when is_list(File) ->
|
||||||
Res;
|
case is_string(File) of
|
||||||
include_config_files([{include_config_file, Filename} | Terms], Res) ->
|
true -> {File, []};
|
||||||
include_config_files([{include_config_file, Filename, []} | Terms], Res);
|
false -> File
|
||||||
include_config_files([{include_config_file, Filename, Options} | Terms], Res) ->
|
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),
|
Included_terms = get_plain_terms_file(Filename),
|
||||||
Disallow = proplists:get_value(disallow, Options, []),
|
Disallow = proplists:get_value(disallow, Options, []),
|
||||||
Included_terms2 = delete_disallowed(Disallow, Included_terms),
|
Included_terms2 = delete_disallowed(Disallow, Included_terms),
|
||||||
Allow_only = proplists:get_value(allow_only, Options, all),
|
Allow_only = proplists:get_value(allow_only, Options, all),
|
||||||
Included_terms3 = keep_only_allowed(Allow_only, Included_terms2),
|
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]).
|
|
||||||
|
|
||||||
%% @doc Filter from the list of terms the disallowed.
|
%% @doc Filter from the list of terms the disallowed.
|
||||||
%% Returns a sublist of Terms without the ones which first element is
|
%% Returns a sublist of Terms without the ones which first element is
|
||||||
@ -308,12 +400,19 @@ split_terms_macros(Terms) ->
|
|||||||
fun(Term, {TOs, Ms}) ->
|
fun(Term, {TOs, Ms}) ->
|
||||||
case Term of
|
case Term of
|
||||||
{define_macro, Key, Value} ->
|
{define_macro, Key, Value} ->
|
||||||
case is_atom(Key) and is_all_uppercase(Key) of
|
case is_correct_macro({Key, Value}) of
|
||||||
true ->
|
true ->
|
||||||
{TOs, Ms++[{Key, Value}]};
|
{TOs, Ms++[{Key, Value}]};
|
||||||
false ->
|
false ->
|
||||||
exit({macro_not_properly_defined, Term})
|
exit({macro_not_properly_defined, Term})
|
||||||
end;
|
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 ->
|
Term ->
|
||||||
{TOs ++ [Term], Ms}
|
{TOs ++ [Term], Ms}
|
||||||
end
|
end
|
||||||
@ -321,6 +420,11 @@ split_terms_macros(Terms) ->
|
|||||||
{[], []},
|
{[], []},
|
||||||
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.
|
%% @doc Recursively replace in Terms macro usages with the defined value.
|
||||||
%% @spec (Terms, Macros) -> Terms
|
%% @spec (Terms, Macros) -> Terms
|
||||||
%% Terms = [term()]
|
%% Terms = [term()]
|
||||||
@ -328,7 +432,9 @@ split_terms_macros(Terms) ->
|
|||||||
replace([], _) ->
|
replace([], _) ->
|
||||||
[];
|
[];
|
||||||
replace([Term|Terms], Macros) ->
|
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) ->
|
replace_term(Key, Macros) when is_atom(Key) ->
|
||||||
case is_all_uppercase(Key) of
|
case is_all_uppercase(Key) of
|
||||||
@ -362,121 +468,65 @@ is_all_uppercase(Atom) ->
|
|||||||
|
|
||||||
process_term(Term, State) ->
|
process_term(Term, State) ->
|
||||||
case Term of
|
case Term of
|
||||||
override_global ->
|
{host_config, HostTerms} ->
|
||||||
State#state{override_global = true};
|
lists:foldl(
|
||||||
override_local ->
|
fun({Host, Terms}, AccState) ->
|
||||||
State#state{override_local = true};
|
lists:foldl(fun(T, S) ->
|
||||||
override_acls ->
|
process_host_term(T, Host, S, set)
|
||||||
State#state{override_acls = true};
|
end, AccState, Terms)
|
||||||
{host_config, Host, Terms} ->
|
end, State, HostTerms);
|
||||||
lists:foldl(fun(T, S) -> process_host_term(T, Host, S) end,
|
{append_host_config, HostTerms} ->
|
||||||
State, Terms);
|
lists:foldl(
|
||||||
{listen, Listeners} ->
|
fun({Host, Terms}, AccState) ->
|
||||||
Listeners2 =
|
lists:foldl(fun(T, S) ->
|
||||||
lists:map(
|
process_host_term(T, Host, S, append)
|
||||||
fun({PortIP, Module, Opts}) ->
|
end, AccState, Terms)
|
||||||
{Port, IPT, _, _, Proto, OptsClean} =
|
end, State, HostTerms);
|
||||||
ejabberd_listener:parse_listener_portip(PortIP, Opts),
|
|
||||||
{{Port, IPT, Proto}, Module, OptsClean}
|
|
||||||
end,
|
|
||||||
Listeners),
|
|
||||||
add_option(listen, Listeners2, 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;
|
|
||||||
{loglevel, Loglevel} ->
|
|
||||||
ejabberd_logger:set(Loglevel),
|
|
||||||
State;
|
|
||||||
_ ->
|
_ ->
|
||||||
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
|
process_host_term(Term, global, State, set)
|
||||||
State, [global|State#state.hosts])
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
process_host_term(Term, Host, State) ->
|
process_host_term(Term, Host, State, Action) ->
|
||||||
case Term of
|
case Term of
|
||||||
{acl, ACLName, ACLData} ->
|
{modules, Modules} when Action == set ->
|
||||||
State#state{opts =
|
set_option({modules, Host}, replace_modules(Modules), State);
|
||||||
[acl:to_record(Host, ACLName, ACLData) | State#state.opts]};
|
{modules, Modules} when Action == append ->
|
||||||
{access, RuleName, Rules} ->
|
append_option({modules, Host}, replace_modules(Modules), State);
|
||||||
add_option({access, RuleName, Host}, Rules, State);
|
|
||||||
{shaper, Name, Data} ->
|
|
||||||
add_option({shaper, Name, Host}, Data, State);
|
|
||||||
{modules, Modules} ->
|
|
||||||
add_option({modules, Host}, replace_modules(Modules), State);
|
|
||||||
{host, _} ->
|
{host, _} ->
|
||||||
State;
|
State;
|
||||||
{hosts, _} ->
|
{hosts, _} ->
|
||||||
State;
|
State;
|
||||||
{Opt, Val} ->
|
{Opt, Val} when Action == set ->
|
||||||
add_option({Opt, Host}, Val, State)
|
set_option({Opt, Host}, Val, State);
|
||||||
end.
|
{Opt, Val} when Action == append ->
|
||||||
|
append_option({Opt, Host}, Val, State);
|
||||||
add_option(Opt, Val, State) when is_atom(Opt) ->
|
|
||||||
add_option({Opt, global}, Val, State);
|
|
||||||
add_option(Opt, Val, State) ->
|
|
||||||
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.
|
|
||||||
|
|
||||||
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 ->
|
Opt ->
|
||||||
%% Then prepend the new value to the list of old values
|
?WARNING_MSG("Ignore invalid (outdated?) option ~p", [Opt]),
|
||||||
Os2 ++ [#local_config{key = Opt,
|
State
|
||||||
value = Val++O#local_config.value}
|
|
||||||
] ++ Os1;
|
|
||||||
_ ->
|
|
||||||
compact(Opt, Val, Os1, Os2++[O])
|
|
||||||
end.
|
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) ->
|
set_opts(State) ->
|
||||||
Opts = lists:reverse(State#state.opts),
|
Opts = State#state.opts,
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
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) ->
|
lists:foreach(fun(R) ->
|
||||||
mnesia:write(R)
|
mnesia:write(R)
|
||||||
end, Opts)
|
end, Opts)
|
||||||
@ -565,11 +615,22 @@ get_option(Opt, F) ->
|
|||||||
get_option(Opt, F, Default) when is_atom(Opt) ->
|
get_option(Opt, F, Default) when is_atom(Opt) ->
|
||||||
get_option({Opt, global}, F, Default);
|
get_option({Opt, global}, F, Default);
|
||||||
get_option(Opt, 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
|
case ets:lookup(local_config, Opt) of
|
||||||
[#local_config{value = Val}] ->
|
[#local_config{value = Val}] ->
|
||||||
prepare_opt_val(Opt, Val, F, Default);
|
prepare_opt_val(Opt, Val, F, Default);
|
||||||
|
_ ->
|
||||||
|
case Opt of
|
||||||
|
{Key, Host} when Host /= global ->
|
||||||
|
get_option({Key, global}, F, Default);
|
||||||
_ ->
|
_ ->
|
||||||
Default
|
Default
|
||||||
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get_vh_by_auth_method(atom()) -> [binary()].
|
-spec get_vh_by_auth_method(atom()) -> [binary()].
|
||||||
@ -632,14 +693,14 @@ replace_modules(Modules) ->
|
|||||||
emit_deprecation_warning(Module, NewModule, DBType),
|
emit_deprecation_warning(Module, NewModule, DBType),
|
||||||
NewOpts = [{db_type, DBType} |
|
NewOpts = [{db_type, DBType} |
|
||||||
lists:keydelete(db_type, 1, Opts)],
|
lists:keydelete(db_type, 1, Opts)],
|
||||||
{NewModule, NewOpts};
|
{NewModule, transform_module_options(Module, NewOpts)};
|
||||||
NewModule ->
|
NewModule ->
|
||||||
if Module /= NewModule ->
|
if Module /= NewModule ->
|
||||||
emit_deprecation_warning(Module, NewModule);
|
emit_deprecation_warning(Module, NewModule);
|
||||||
true ->
|
true ->
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
{NewModule, Opts}
|
{NewModule, transform_module_options(Module, Opts)}
|
||||||
end
|
end
|
||||||
end, Modules).
|
end, Modules).
|
||||||
|
|
||||||
@ -695,6 +756,142 @@ format_term(S) when is_list(S), S /= [] ->
|
|||||||
format_term(T) ->
|
format_term(T) ->
|
||||||
io_lib:format("~p", [binary_to_strings(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(),
|
-spec convert_table_to_binary(atom(), [atom()], atom(),
|
||||||
fun(), fun()) -> ok.
|
fun(), fun()) -> ok.
|
||||||
|
|
||||||
|
@ -237,7 +237,7 @@ process2(Args, Auth, AccessCommands) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
get_accesscommands() ->
|
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, []).
|
fun(V) when is_list(V) -> V end, []).
|
||||||
|
|
||||||
%%-----------------------------
|
%%-----------------------------
|
||||||
|
@ -280,7 +280,7 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
|||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
check_starttls(SockMod, Socket, Receiver, Opts) ->
|
check_starttls(SockMod, Socket, Receiver, Opts) ->
|
||||||
TLSEnabled = lists:member(tls, Opts),
|
TLSEnabled = proplists:get_bool(tls, Opts),
|
||||||
TLSOpts = lists:filter(fun({certfile, _}) -> true;
|
TLSOpts = lists:filter(fun({certfile, _}) -> true;
|
||||||
(_) -> false
|
(_) -> false
|
||||||
end, Opts),
|
end, Opts),
|
||||||
@ -292,4 +292,3 @@ check_starttls(SockMod, Socket, Receiver, Opts) ->
|
|||||||
true ->
|
true ->
|
||||||
{SockMod, Socket}
|
{SockMod, Socket}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -30,7 +30,8 @@
|
|||||||
|
|
||||||
%% External exports
|
%% External exports
|
||||||
-export([start/2, start_link/2, become_controller/1,
|
-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
|
%% Callbacks
|
||||||
-export([init/2]).
|
-export([init/2]).
|
||||||
@ -91,7 +92,7 @@ start_link(SockData, Opts) ->
|
|||||||
[SockData, Opts])}.
|
[SockData, Opts])}.
|
||||||
|
|
||||||
init({SockMod, Socket}, Opts) ->
|
init({SockMod, Socket}, Opts) ->
|
||||||
TLSEnabled = lists:member(tls, Opts),
|
TLSEnabled = proplists:get_bool(tls, Opts),
|
||||||
TLSOpts1 = lists:filter(fun ({certfile, _}) -> true;
|
TLSOpts1 = lists:filter(fun ({certfile, _}) -> true;
|
||||||
(_) -> false
|
(_) -> false
|
||||||
end,
|
end,
|
||||||
@ -133,12 +134,13 @@ init({SockMod, Socket}, Opts) ->
|
|||||||
true -> [{[<<"http-poll">>], ejabberd_http_poll}];
|
true -> [{[<<"http-poll">>], ejabberd_http_poll}];
|
||||||
false -> []
|
false -> []
|
||||||
end,
|
end,
|
||||||
DefinedHandlers = case lists:keysearch(request_handlers,
|
DefinedHandlers = gen_mod:get_opt(
|
||||||
1, Opts)
|
request_handlers, Opts,
|
||||||
of
|
fun(Hs) ->
|
||||||
{value, {request_handlers, H}} -> H;
|
[{str:tokens(
|
||||||
false -> []
|
iolist_to_binary(Path), <<"/">>),
|
||||||
end,
|
Mod} || {Path, Mod} <- Hs]
|
||||||
|
end, []),
|
||||||
RequestHandlers = DefinedHandlers ++ Captcha ++ Register ++
|
RequestHandlers = DefinedHandlers ++ Captcha ++ Register ++
|
||||||
Admin ++ Bind ++ Poll,
|
Admin ++ Bind ++ Poll,
|
||||||
?DEBUG("S: ~p~n", [RequestHandlers]),
|
?DEBUG("S: ~p~n", [RequestHandlers]),
|
||||||
@ -484,7 +486,7 @@ analyze_ip_xff(IP, [], _Host) -> IP;
|
|||||||
analyze_ip_xff({IPLast, Port}, XFF, Host) ->
|
analyze_ip_xff({IPLast, Port}, XFF, Host) ->
|
||||||
[ClientIP | ProxiesIPs] = str:tokens(XFF, <<", ">>) ++
|
[ClientIP | ProxiesIPs] = str:tokens(XFF, <<", ">>) ++
|
||||||
[jlib:ip_to_list(IPLast)],
|
[jlib:ip_to_list(IPLast)],
|
||||||
TrustedProxies = ejabberd_config:get_local_option(
|
TrustedProxies = ejabberd_config:get_option(
|
||||||
{trusted_proxies, Host},
|
{trusted_proxies, Host},
|
||||||
fun(TPs) ->
|
fun(TPs) ->
|
||||||
[iolist_to_binary(TP) || TP <- TPs]
|
[iolist_to_binary(TP) || TP <- TPs]
|
||||||
@ -834,3 +836,25 @@ normalize_path([_Parent, <<"..">>|Path], Norm) ->
|
|||||||
normalize_path(Path, Norm);
|
normalize_path(Path, Norm);
|
||||||
normalize_path([Part | Path], Norm) ->
|
normalize_path([Part | Path], Norm) ->
|
||||||
normalize_path(Path, [Part|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].
|
||||||
|
@ -205,7 +205,7 @@ get_human_html_xmlel() ->
|
|||||||
init([ID, Key, IP]) ->
|
init([ID, Key, IP]) ->
|
||||||
?INFO_MSG("started: ~p", [{ID, Key, IP}]),
|
?INFO_MSG("started: ~p", [{ID, Key, IP}]),
|
||||||
Opts = ejabberd_c2s_config:get_c2s_limits(),
|
Opts = ejabberd_c2s_config:get_c2s_limits(),
|
||||||
HTTPPollTimeout = ejabberd_config:get_local_option(
|
HTTPPollTimeout = ejabberd_config:get_option(
|
||||||
{http_poll_timeout, ?MYNAME},
|
{http_poll_timeout, ?MYNAME},
|
||||||
fun(I) when is_integer(I), I>0 -> I end,
|
fun(I) when is_integer(I), I>0 -> I end,
|
||||||
?HTTP_POLL_TIMEOUT) * 1000,
|
?HTTP_POLL_TIMEOUT) * 1000,
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
parse_listener_portip/2,
|
parse_listener_portip/2,
|
||||||
add_listener/3,
|
add_listener/3,
|
||||||
delete_listener/2,
|
delete_listener/2,
|
||||||
|
transform_options/1,
|
||||||
validate_cfg/1
|
validate_cfg/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
@ -55,7 +56,7 @@ init(_) ->
|
|||||||
{ok, {{one_for_one, 10, 1}, []}}.
|
{ok, {{one_for_one, 10, 1}, []}}.
|
||||||
|
|
||||||
bind_tcp_ports() ->
|
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 ->
|
undefined ->
|
||||||
ignore;
|
ignore;
|
||||||
Ls ->
|
Ls ->
|
||||||
@ -88,7 +89,7 @@ bind_tcp_port(PortIP, Module, RawOpts) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
start_listeners() ->
|
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 ->
|
undefined ->
|
||||||
ignore;
|
ignore;
|
||||||
Ls ->
|
Ls ->
|
||||||
@ -267,7 +268,7 @@ strip_ip_option(Opts) ->
|
|||||||
Opts),
|
Opts),
|
||||||
case IPL of
|
case IPL of
|
||||||
%% Only the first ip option is considered
|
%% Only the first ip option is considered
|
||||||
[{ip, T1} | _] when is_tuple(T1) ->
|
[{ip, T1} | _] ->
|
||||||
{T1, OptsNoIP};
|
{T1, OptsNoIP};
|
||||||
[] ->
|
[] ->
|
||||||
{no_ip_option, OptsNoIP}
|
{no_ip_option, OptsNoIP}
|
||||||
@ -364,7 +365,7 @@ start_listener_sup(Port, Module, Opts) ->
|
|||||||
supervisor:start_child(ejabberd_listeners, ChildSpec).
|
supervisor:start_child(ejabberd_listeners, ChildSpec).
|
||||||
|
|
||||||
stop_listeners() ->
|
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(
|
lists:foreach(
|
||||||
fun({PortIpNetp, Module, _Opts}) ->
|
fun({PortIpNetp, Module, _Opts}) ->
|
||||||
delete_listener(PortIpNetp, Module)
|
delete_listener(PortIpNetp, Module)
|
||||||
@ -397,7 +398,7 @@ add_listener(PortIP, Module, Opts) ->
|
|||||||
PortIP1 = {Port, IPT, Proto},
|
PortIP1 = {Port, IPT, Proto},
|
||||||
case start_listener(PortIP1, Module, Opts) of
|
case start_listener(PortIP1, Module, Opts) of
|
||||||
{ok, _Pid} ->
|
{ok, _Pid} ->
|
||||||
Ports = case ejabberd_config:get_local_option(
|
Ports = case ejabberd_config:get_option(
|
||||||
listen, fun validate_cfg/1) of
|
listen, fun validate_cfg/1) of
|
||||||
undefined ->
|
undefined ->
|
||||||
[];
|
[];
|
||||||
@ -406,7 +407,8 @@ add_listener(PortIP, Module, Opts) ->
|
|||||||
end,
|
end,
|
||||||
Ports1 = lists:keydelete(PortIP1, 1, Ports),
|
Ports1 = lists:keydelete(PortIP1, 1, Ports),
|
||||||
Ports2 = [{PortIP1, Module, Opts} | Ports1],
|
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;
|
ok;
|
||||||
{error, {already_started, _Pid}} ->
|
{error, {already_started, _Pid}} ->
|
||||||
{error, {already_started, PortIP}};
|
{error, {already_started, PortIP}};
|
||||||
@ -428,7 +430,7 @@ delete_listener(PortIP, Module) ->
|
|||||||
delete_listener(PortIP, Module, Opts) ->
|
delete_listener(PortIP, Module, Opts) ->
|
||||||
{Port, IPT, _, _, Proto, _} = parse_listener_portip(PortIP, Opts),
|
{Port, IPT, _, _, Proto, _} = parse_listener_portip(PortIP, Opts),
|
||||||
PortIP1 = {Port, IPT, Proto},
|
PortIP1 = {Port, IPT, Proto},
|
||||||
Ports = case ejabberd_config:get_local_option(
|
Ports = case ejabberd_config:get_option(
|
||||||
listen, fun validate_cfg/1) of
|
listen, fun validate_cfg/1) of
|
||||||
undefined ->
|
undefined ->
|
||||||
[];
|
[];
|
||||||
@ -436,7 +438,8 @@ delete_listener(PortIP, Module, Opts) ->
|
|||||||
Ls
|
Ls
|
||||||
end,
|
end,
|
||||||
Ports1 = lists:keydelete(PortIP1, 1, Ports),
|
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).
|
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_PORT(P), (is_integer(P) and (P > 0) and (P =< 65535))).
|
||||||
-define(IS_TRANSPORT(T), ((T == tcp) or (T == udp))).
|
-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 transport() :: udp | tcp.
|
||||||
-type port_ip_transport() :: inet:port_number() |
|
-type port_ip_transport() :: inet:port_number() |
|
||||||
{inet:port_number(), transport()} |
|
{inet:port_number(), transport()} |
|
||||||
@ -551,18 +603,21 @@ format_error(Reason) ->
|
|||||||
|
|
||||||
validate_cfg(L) ->
|
validate_cfg(L) ->
|
||||||
lists:map(
|
lists:map(
|
||||||
fun({PortIPTransport, Mod1, Opts}) when is_atom(Mod1), is_list(Opts) ->
|
fun(LOpts) ->
|
||||||
Mod = prepare_mod(Mod1),
|
lists:foldl(
|
||||||
case PortIPTransport of
|
fun({port, Port}, {{_, IP, T}, Mod, Opts}) ->
|
||||||
Port when ?IS_PORT(Port) ->
|
true = ?IS_PORT(Port),
|
||||||
{Port, Mod, Opts};
|
{{Port, IP, T}, Mod, Opts};
|
||||||
{Port, Trans} when ?IS_PORT(Port) and ?IS_TRANSPORT(Trans) ->
|
({ip, IP}, {{Port, _, T}, Mod, Opts}) ->
|
||||||
{{Port, Trans}, Mod, Opts};
|
{{Port, prepare_ip(IP), T}, Mod, Opts};
|
||||||
{Port, IP} when ?IS_PORT(Port) ->
|
({transport, T}, {{Port, IP, _}, Mod, Opts}) ->
|
||||||
{{Port, prepare_ip(IP)}, Mod, Opts};
|
true = ?IS_TRANSPORT(T),
|
||||||
{Port, IP, Trans} when ?IS_PORT(Port) and ?IS_TRANSPORT(Trans) ->
|
{{Port, IP, T}, Mod, Opts};
|
||||||
{{Port, prepare_ip(IP), Trans}, Mod, Opts}
|
({module, Mod}, {Port, _, Opts}) ->
|
||||||
end
|
{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).
|
end, L).
|
||||||
|
|
||||||
prepare_ip({A, B, C, D} = IP)
|
prepare_ip({A, B, C, D} = IP)
|
||||||
@ -583,5 +638,5 @@ prepare_mod(ejabberd_stun) ->
|
|||||||
prepare_mod(stun) ->
|
prepare_mod(stun) ->
|
||||||
ejabberd:start_app(p1_stun),
|
ejabberd:start_app(p1_stun),
|
||||||
stun;
|
stun;
|
||||||
prepare_mod(Mod) ->
|
prepare_mod(Mod) when is_atom(Mod) ->
|
||||||
Mod.
|
Mod.
|
||||||
|
@ -84,7 +84,12 @@ get_closest_node(Name) ->
|
|||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
init([]) ->
|
init([]) ->
|
||||||
{FE, BE} =
|
{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 ->
|
frontend ->
|
||||||
{true, false};
|
{true, false};
|
||||||
backend ->
|
backend ->
|
||||||
|
@ -71,12 +71,12 @@
|
|||||||
|
|
||||||
-define(TOP_LEVEL_TXN, 0).
|
-define(TOP_LEVEL_TXN, 0).
|
||||||
|
|
||||||
-define(MAX_TRANSACTION_RESTARTS, 10).
|
|
||||||
|
|
||||||
-define(PGSQL_PORT, 5432).
|
-define(PGSQL_PORT, 5432).
|
||||||
|
|
||||||
-define(MYSQL_PORT, 3306).
|
-define(MYSQL_PORT, 3306).
|
||||||
|
|
||||||
|
-define(MAX_TRANSACTION_RESTARTS, 10).
|
||||||
|
|
||||||
-define(TRANSACTION_TIMEOUT, 60000).
|
-define(TRANSACTION_TIMEOUT, 60000).
|
||||||
|
|
||||||
-define(KEEPALIVE_TIMEOUT, 60000).
|
-define(KEEPALIVE_TIMEOUT, 60000).
|
||||||
@ -201,8 +201,8 @@ decode_term(Bin) ->
|
|||||||
%%% Callback functions from gen_fsm
|
%%% Callback functions from gen_fsm
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
init([Host, StartInterval]) ->
|
init([Host, StartInterval]) ->
|
||||||
case ejabberd_config:get_local_option(
|
case ejabberd_config:get_option(
|
||||||
{odbc_keepalive_interval, Host},
|
{keepalive_interval, Host},
|
||||||
fun(I) when is_integer(I), I>0 -> I end) of
|
fun(I) when is_integer(I), I>0 -> I end) of
|
||||||
undefined ->
|
undefined ->
|
||||||
ok;
|
ok;
|
||||||
@ -573,39 +573,39 @@ log(Level, Format, Args) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
db_opts(Host) ->
|
db_opts(Host) ->
|
||||||
case ejabberd_config:get_local_option(
|
Type = ejabberd_config:get_option({odbc_type, Host},
|
||||||
{odbc_server, Host},
|
fun(mysql) -> mysql;
|
||||||
fun({Type, Server, DB, User, Pass}) ->
|
(pgsql) -> pgsql;
|
||||||
{Type,
|
(odbc) -> odbc
|
||||||
iolist_to_binary(Server),
|
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
|
case Type of
|
||||||
mysql -> ?MYSQL_PORT;
|
mysql -> ?MYSQL_PORT;
|
||||||
pgsql -> ?PGSQL_PORT
|
pgsql -> ?PGSQL_PORT
|
||||||
end,
|
end),
|
||||||
iolist_to_binary(DB),
|
DB = ejabberd_config:get_option({odbc_database, Host},
|
||||||
iolist_to_binary(User),
|
fun iolist_to_binary/1,
|
||||||
iolist_to_binary(Pass)};
|
<<"ejabberd">>),
|
||||||
({Type, Server, Port, DB, User, Pass})
|
User = ejabberd_config:get_option({odbc_username, Host},
|
||||||
when ((Type == mysql) or (Type == pgsql))
|
fun iolist_to_binary/1,
|
||||||
and (is_integer(Port) and ((Port > 0)
|
<<"ejabberd">>),
|
||||||
and (Port < 65536))) ->
|
Pass = ejabberd_config:get_option({odbc_password, Host},
|
||||||
{Type,
|
fun iolist_to_binary/1,
|
||||||
iolist_to_binary(Server),
|
<<"">>),
|
||||||
Port,
|
[Type, Server, Port, DB, User, Pass]
|
||||||
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]
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
max_fsm_queue() ->
|
max_fsm_queue() ->
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
max_fsm_queue,
|
max_fsm_queue,
|
||||||
fun(N) when is_integer(N), N > 0 -> N end).
|
fun(N) when is_integer(N), N > 0 -> N end).
|
||||||
|
|
||||||
|
@ -30,11 +30,15 @@
|
|||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([start_link/1, init/1, add_pid/2, remove_pid/2,
|
-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("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
-define(PGSQL_PORT, 5432).
|
||||||
|
|
||||||
|
-define(MYSQL_PORT, 3306).
|
||||||
|
|
||||||
-define(DEFAULT_POOL_SIZE, 10).
|
-define(DEFAULT_POOL_SIZE, 10).
|
||||||
|
|
||||||
-define(DEFAULT_ODBC_START_INTERVAL, 30).
|
-define(DEFAULT_ODBC_START_INTERVAL, 30).
|
||||||
@ -56,11 +60,11 @@ start_link(Host) ->
|
|||||||
?MODULE, [Host]).
|
?MODULE, [Host]).
|
||||||
|
|
||||||
init([Host]) ->
|
init([Host]) ->
|
||||||
PoolSize = ejabberd_config:get_local_option(
|
PoolSize = ejabberd_config:get_option(
|
||||||
{odbc_pool_size, Host},
|
{odbc_pool_size, Host},
|
||||||
fun(I) when is_integer(I), I>0 -> I end,
|
fun(I) when is_integer(I), I>0 -> I end,
|
||||||
?DEFAULT_POOL_SIZE),
|
?DEFAULT_POOL_SIZE),
|
||||||
StartInterval = ejabberd_config:get_local_option(
|
StartInterval = ejabberd_config:get_option(
|
||||||
{odbc_start_interval, Host},
|
{odbc_start_interval, Host},
|
||||||
fun(I) when is_integer(I), I>0 -> I end,
|
fun(I) when is_integer(I), I>0 -> I end,
|
||||||
?DEFAULT_ODBC_START_INTERVAL),
|
?DEFAULT_ODBC_START_INTERVAL),
|
||||||
@ -93,3 +97,20 @@ remove_pid(Host, Pid) ->
|
|||||||
mnesia:delete_object(#sql_pool{host = Host, pid = Pid})
|
mnesia:delete_object(#sql_pool{host = Host, pid = Pid})
|
||||||
end,
|
end,
|
||||||
mnesia:ets(F).
|
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].
|
||||||
|
@ -69,18 +69,16 @@ start_odbc(Host, App) ->
|
|||||||
start_odbc(Host, App)
|
start_odbc(Host, App)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Returns {true, App} 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) ->
|
needs_odbc(Host) ->
|
||||||
LHost = jlib:nameprep(Host),
|
LHost = jlib:nameprep(Host),
|
||||||
case ejabberd_config:get_local_option(
|
case ejabberd_config:get_option({odbc_type, LHost},
|
||||||
{odbc_server, LHost}, fun(Res) -> Res end) of
|
fun(mysql) -> mysql;
|
||||||
{mysql, _, _, _, _} -> {true, p1_mysql};
|
(pgsql) -> pgsql;
|
||||||
{pgsql, _, _, _, _} -> {true, p1_pgsql};
|
(odbc) -> odbc
|
||||||
{mysql, _, _, _, _, _} -> {true, p1_mysql};
|
end, undefined) of
|
||||||
{pgsql, _, _, _, _, _} -> {true, p1_pgsql};
|
mysql -> {true, p1_mysql};
|
||||||
S ->
|
pgsql -> {true, p1_pgsql};
|
||||||
case catch iolist_to_binary(S) of
|
odbc -> {true, odbc};
|
||||||
{'EXIT', _} -> false;
|
undefined -> false
|
||||||
_ -> true
|
|
||||||
end
|
|
||||||
end.
|
end.
|
||||||
|
@ -387,14 +387,10 @@ do_route(OrigFrom, OrigTo, OrigPacket) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
get_component_number(LDomain) ->
|
get_component_number(LDomain) ->
|
||||||
case
|
ejabberd_config:get_option(
|
||||||
ejabberd_config:get_local_option({domain_balancing_component_number,
|
{domain_balancing_component_number, LDomain},
|
||||||
LDomain}, fun(D) -> D end)
|
fun(N) when is_integer(N), N > 1 -> N end,
|
||||||
of
|
undefined).
|
||||||
N when is_integer(N), N > 1 -> N;
|
|
||||||
_ -> undefined
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
update_tables() ->
|
update_tables() ->
|
||||||
case catch mnesia:table_info(route, attributes) of
|
case catch mnesia:table_info(route, attributes) of
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
handle_info/2, terminate/2, code_change/3]).
|
handle_info/2, terminate/2, code_change/3]).
|
||||||
|
|
||||||
%% ejabberd API
|
%% ejabberd API
|
||||||
-export([get_info_s2s_connections/1]).
|
-export([get_info_s2s_connections/1, transform_options/1]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
@ -461,12 +461,12 @@ needed_connections_number(Ls, MaxS2SConnectionsNumber,
|
|||||||
%% --------------------------------------------------------------------
|
%% --------------------------------------------------------------------
|
||||||
is_service(From, To) ->
|
is_service(From, To) ->
|
||||||
LFromDomain = From#jid.lserver,
|
LFromDomain = From#jid.lserver,
|
||||||
case ejabberd_config:get_local_option(
|
case ejabberd_config:get_option(
|
||||||
{route_subdomains, LFromDomain},
|
{route_subdomains, LFromDomain},
|
||||||
fun(s2s) -> s2s end) of
|
fun(s2s) -> s2s; (local) -> local end, local) of
|
||||||
s2s -> % bypass RFC 3920 10.3
|
s2s -> % bypass RFC 3920 10.3
|
||||||
false;
|
false;
|
||||||
undefined ->
|
local ->
|
||||||
Hosts = (?MYHOSTS),
|
Hosts = (?MYHOSTS),
|
||||||
P = fun (ParentDomain) ->
|
P = fun (ParentDomain) ->
|
||||||
lists:member(ParentDomain, Hosts)
|
lists:member(ParentDomain, Hosts)
|
||||||
@ -548,34 +548,50 @@ allow_host2(MyServer, S2SHost) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
allow_host1(MyHost, S2SHost) ->
|
allow_host1(MyHost, S2SHost) ->
|
||||||
case ejabberd_config:get_local_option(
|
Rule = ejabberd_config:get_option(
|
||||||
{{s2s_host, S2SHost}, MyHost},
|
s2s_access,
|
||||||
fun(deny) -> deny; (allow) -> allow end)
|
fun(A) when is_atom(A) -> A end,
|
||||||
of
|
all),
|
||||||
|
JID = jlib:make_jid(<<"">>, S2SHost, <<"">>),
|
||||||
|
case acl:match_rule(MyHost, Rule, JID) of
|
||||||
deny -> false;
|
deny -> false;
|
||||||
allow -> true;
|
allow ->
|
||||||
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,
|
case ejabberd_hooks:run_fold(s2s_allow_host, MyHost,
|
||||||
allow, [MyHost, S2SHost])
|
allow, [MyHost, S2SHost]) of
|
||||||
of
|
|
||||||
deny -> false;
|
deny -> false;
|
||||||
allow -> true;
|
allow -> true;
|
||||||
_ -> true
|
_ -> true
|
||||||
end
|
end
|
||||||
end
|
|
||||||
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.
|
%% Get information about S2S connections of the specified type.
|
||||||
%% @spec (Type) -> [Info]
|
%% @spec (Type) -> [Info]
|
||||||
%% where Type = in | out
|
%% where Type = in | out
|
||||||
%% Info = [{InfoName::atom(), InfoValue::any()}]
|
%% Info = [{InfoName::atom(), InfoValue::any()}]
|
||||||
|
|
||||||
get_info_s2s_connections(Type) ->
|
get_info_s2s_connections(Type) ->
|
||||||
ChildType = case Type of
|
ChildType = case Type of
|
||||||
in -> ejabberd_s2s_in_sup;
|
in -> ejabberd_s2s_in_sup;
|
||||||
|
@ -149,7 +149,7 @@ init([{SockMod, Socket}, Opts]) ->
|
|||||||
_ -> none
|
_ -> none
|
||||||
end,
|
end,
|
||||||
{StartTLS, TLSRequired, TLSCertverify} =
|
{StartTLS, TLSRequired, TLSCertverify} =
|
||||||
case ejabberd_config:get_local_option(
|
case ejabberd_config:get_option(
|
||||||
s2s_use_starttls,
|
s2s_use_starttls,
|
||||||
fun(false) -> false;
|
fun(false) -> false;
|
||||||
(true) -> true;
|
(true) -> true;
|
||||||
@ -171,7 +171,7 @@ init([{SockMod, Socket}, Opts]) ->
|
|||||||
required_trusted ->
|
required_trusted ->
|
||||||
{true, true, true}
|
{true, true, true}
|
||||||
end,
|
end,
|
||||||
TLSOpts1 = case ejabberd_config:get_local_option(
|
TLSOpts1 = case ejabberd_config:get_option(
|
||||||
s2s_certfile,
|
s2s_certfile,
|
||||||
fun iolist_to_binary/1) of
|
fun iolist_to_binary/1) of
|
||||||
undefined -> [];
|
undefined -> [];
|
||||||
@ -324,7 +324,7 @@ wait_for_feature_request({xmlstreamelement, El},
|
|||||||
?DEBUG("starttls", []),
|
?DEBUG("starttls", []),
|
||||||
Socket = StateData#state.socket,
|
Socket = StateData#state.socket,
|
||||||
TLSOpts1 = case
|
TLSOpts1 = case
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
{domain_certfile, StateData#state.server},
|
{domain_certfile, StateData#state.server},
|
||||||
fun iolist_to_binary/1) of
|
fun iolist_to_binary/1) of
|
||||||
undefined -> StateData#state.tls_options;
|
undefined -> StateData#state.tls_options;
|
||||||
@ -332,7 +332,7 @@ wait_for_feature_request({xmlstreamelement, El},
|
|||||||
[{certfile, CertFile} | lists:keydelete(certfile, 1,
|
[{certfile, CertFile} | lists:keydelete(certfile, 1,
|
||||||
StateData#state.tls_options)]
|
StateData#state.tls_options)]
|
||||||
end,
|
end,
|
||||||
TLSOpts = case ejabberd_config:get_local_option(
|
TLSOpts = case ejabberd_config:get_option(
|
||||||
{s2s_tls_compression, StateData#state.server},
|
{s2s_tls_compression, StateData#state.server},
|
||||||
fun(true) -> true;
|
fun(true) -> true;
|
||||||
(false) -> false
|
(false) -> false
|
||||||
@ -843,7 +843,7 @@ fsm_limit_opts(Opts) ->
|
|||||||
case lists:keysearch(max_fsm_queue, 1, Opts) of
|
case lists:keysearch(max_fsm_queue, 1, Opts) of
|
||||||
{value, {_, N}} when is_integer(N) -> [{max_queue, N}];
|
{value, {_, N}} when is_integer(N) -> [{max_queue, N}];
|
||||||
_ ->
|
_ ->
|
||||||
case ejabberd_config:get_local_option(
|
case ejabberd_config:get_option(
|
||||||
max_fsm_queue,
|
max_fsm_queue,
|
||||||
fun(I) when is_integer(I), I > 0 -> I end) of
|
fun(I) when is_integer(I), I > 0 -> I end) of
|
||||||
undefined -> [];
|
undefined -> [];
|
||||||
|
@ -35,7 +35,8 @@
|
|||||||
start_link/3,
|
start_link/3,
|
||||||
start_connection/1,
|
start_connection/1,
|
||||||
terminate_if_waiting_delay/2,
|
terminate_if_waiting_delay/2,
|
||||||
stop_connection/1]).
|
stop_connection/1,
|
||||||
|
transform_options/1]).
|
||||||
|
|
||||||
%% p1_fsm callbacks (same as gen_fsm)
|
%% p1_fsm callbacks (same as gen_fsm)
|
||||||
-export([init/1,
|
-export([init/1,
|
||||||
@ -161,7 +162,7 @@ init([From, Server, Type]) ->
|
|||||||
process_flag(trap_exit, true),
|
process_flag(trap_exit, true),
|
||||||
?DEBUG("started: ~p", [{From, Server, Type}]),
|
?DEBUG("started: ~p", [{From, Server, Type}]),
|
||||||
{TLS, TLSRequired} = case
|
{TLS, TLSRequired} = case
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
s2s_use_starttls,
|
s2s_use_starttls,
|
||||||
fun(true) -> true;
|
fun(true) -> true;
|
||||||
(false) -> false;
|
(false) -> false;
|
||||||
@ -184,13 +185,13 @@ init([From, Server, Type]) ->
|
|||||||
end,
|
end,
|
||||||
UseV10 = TLS,
|
UseV10 = TLS,
|
||||||
TLSOpts1 = case
|
TLSOpts1 = case
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
s2s_certfile, fun iolist_to_binary/1)
|
s2s_certfile, fun iolist_to_binary/1)
|
||||||
of
|
of
|
||||||
undefined -> [connect];
|
undefined -> [connect];
|
||||||
CertFile -> [{certfile, CertFile}, connect]
|
CertFile -> [{certfile, CertFile}, connect]
|
||||||
end,
|
end,
|
||||||
TLSOpts = case ejabberd_config:get_local_option(
|
TLSOpts = case ejabberd_config:get_option(
|
||||||
{s2s_tls_compression, From},
|
{s2s_tls_compression, From},
|
||||||
fun(true) -> true;
|
fun(true) -> true;
|
||||||
(false) -> false
|
(false) -> false
|
||||||
@ -702,7 +703,7 @@ wait_for_starttls_proceed({xmlstreamelement, El},
|
|||||||
[{StateData#state.myname, StateData#state.server}]),
|
[{StateData#state.myname, StateData#state.server}]),
|
||||||
Socket = StateData#state.socket,
|
Socket = StateData#state.socket,
|
||||||
TLSOpts = case
|
TLSOpts = case
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
{domain_certfile, StateData#state.myname},
|
{domain_certfile, StateData#state.myname},
|
||||||
fun iolist_to_binary/1)
|
fun iolist_to_binary/1)
|
||||||
of
|
of
|
||||||
@ -1142,16 +1143,15 @@ get_addr_port(Server) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
srv_lookup(Server) ->
|
srv_lookup(Server) ->
|
||||||
Options = case
|
TimeoutMs = timer:seconds(
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
s2s_dns_options, fun(L) when is_list(L) -> L end)
|
s2s_dns_timeout,
|
||||||
of
|
fun(I) when is_integer(I), I>=0 -> I end,
|
||||||
undefined -> [];
|
10)),
|
||||||
L -> L
|
Retries = ejabberd_config:get_option(
|
||||||
end,
|
s2s_dns_retries,
|
||||||
TimeoutMs = timer:seconds(proplists:get_value(timeout,
|
fun(I) when is_integer(I), I>=0 -> I end,
|
||||||
Options, 10)),
|
2),
|
||||||
Retries = proplists:get_value(retries, Options, 2),
|
|
||||||
srv_lookup(binary_to_list(Server), TimeoutMs, Retries).
|
srv_lookup(binary_to_list(Server), TimeoutMs, Retries).
|
||||||
|
|
||||||
%% XXX - this behaviour is suboptimal in the case that the domain
|
%% XXX - this behaviour is suboptimal in the case that the domain
|
||||||
@ -1211,15 +1211,15 @@ get_addrs(Host, Family) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
outgoing_s2s_port() ->
|
outgoing_s2s_port() ->
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
outgoing_s2s_port,
|
outgoing_s2s_port,
|
||||||
fun(I) when is_integer(I), I > 0, I =< 65536 -> I end,
|
fun(I) when is_integer(I), I > 0, I =< 65536 -> I end,
|
||||||
5269).
|
5269).
|
||||||
|
|
||||||
outgoing_s2s_families() ->
|
outgoing_s2s_families() ->
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
outgoing_s2s_options,
|
outgoing_s2s_families,
|
||||||
fun({Families, _}) ->
|
fun(Families) ->
|
||||||
true = lists:all(
|
true = lists:all(
|
||||||
fun(ipv4) -> true;
|
fun(ipv4) -> true;
|
||||||
(ipv6) -> true
|
(ipv6) -> true
|
||||||
@ -1228,14 +1228,43 @@ outgoing_s2s_families() ->
|
|||||||
end, [ipv4, ipv6]).
|
end, [ipv4, ipv6]).
|
||||||
|
|
||||||
outgoing_s2s_timeout() ->
|
outgoing_s2s_timeout() ->
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
outgoing_s2s_options,
|
outgoing_s2s_timeout,
|
||||||
fun({_, TimeOut}) when is_integer(TimeOut), TimeOut > 0 ->
|
fun(TimeOut) when is_integer(TimeOut), TimeOut > 0 ->
|
||||||
TimeOut;
|
TimeOut;
|
||||||
({_, infinity}) ->
|
(infinity) ->
|
||||||
infinity
|
infinity
|
||||||
end, 10000).
|
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
|
%% Human readable S2S logging: Log only new outgoing connections as INFO
|
||||||
%% Do not log dialback
|
%% Do not log dialback
|
||||||
log_s2s_out(false, _, _, _) -> ok;
|
log_s2s_out(false, _, _, _) -> ok;
|
||||||
@ -1278,7 +1307,7 @@ wait_before_reconnect(StateData) ->
|
|||||||
queue = queue:new()}}.
|
queue = queue:new()}}.
|
||||||
|
|
||||||
get_max_retry_delay() ->
|
get_max_retry_delay() ->
|
||||||
case ejabberd_config:get_local_option(
|
case ejabberd_config:get_option(
|
||||||
s2s_max_retry_delay,
|
s2s_max_retry_delay,
|
||||||
fun(I) when is_integer(I), I > 0 -> I end) of
|
fun(I) when is_integer(I), I > 0 -> I end) of
|
||||||
undefined -> ?MAX_RETRY_DELAY;
|
undefined -> ?MAX_RETRY_DELAY;
|
||||||
@ -1295,7 +1324,7 @@ terminate_if_waiting_delay(From, To) ->
|
|||||||
Pids).
|
Pids).
|
||||||
|
|
||||||
fsm_limit_opts() ->
|
fsm_limit_opts() ->
|
||||||
case ejabberd_config:get_local_option(
|
case ejabberd_config:get_option(
|
||||||
max_fsm_queue,
|
max_fsm_queue,
|
||||||
fun(I) when is_integer(I), I > 0 -> I end) of
|
fun(I) when is_integer(I), I > 0 -> I end) of
|
||||||
undefined -> [];
|
undefined -> [];
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
%% External exports
|
%% External exports
|
||||||
-export([start/2, start_link/2, send_text/2,
|
-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
|
%% gen_fsm callbacks
|
||||||
-export([init/1, wait_for_stream/2,
|
-export([init/1, wait_for_stream/2,
|
||||||
@ -124,29 +124,18 @@ init([{SockMod, Socket}, Opts]) ->
|
|||||||
{value, {_, A}} -> A;
|
{value, {_, A}} -> A;
|
||||||
_ -> all
|
_ -> all
|
||||||
end,
|
end,
|
||||||
{Hosts, Password} = case lists:keysearch(hosts, 1, Opts)
|
%% This should be improved probably
|
||||||
of
|
{Hosts, HostOpts} = case lists:keyfind(hosts, 1, Opts) of
|
||||||
{value, {_, Hs, HOpts}} ->
|
{_, HOpts} ->
|
||||||
case lists:keysearch(password, 1, HOpts) of
|
{[H || {H, _} <- HOpts],
|
||||||
{value, {_, P}} -> {Hs, P};
|
lists:flatten(
|
||||||
|
[O || {_, O} <- HOpts])};
|
||||||
_ ->
|
_ ->
|
||||||
% 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,
|
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
|
Shaper = case lists:keysearch(shaper_rule, 1, Opts) of
|
||||||
{value, {_, S}} -> S;
|
{value, {_, S}} -> S;
|
||||||
_ -> none
|
_ -> none
|
||||||
@ -384,12 +373,30 @@ send_element(StateData, El) ->
|
|||||||
|
|
||||||
new_id() -> randoms:get_string().
|
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) ->
|
fsm_limit_opts(Opts) ->
|
||||||
case lists:keysearch(max_fsm_queue, 1, Opts) of
|
case lists:keysearch(max_fsm_queue, 1, Opts) of
|
||||||
{value, {_, N}} when is_integer(N) ->
|
{value, {_, N}} when is_integer(N) ->
|
||||||
[{max_queue, N}];
|
[{max_queue, N}];
|
||||||
_ ->
|
_ ->
|
||||||
case ejabberd_config:get_local_option(
|
case ejabberd_config:get_option(
|
||||||
max_fsm_queue,
|
max_fsm_queue,
|
||||||
fun(I) when is_integer(I), I > 0 -> I end) of
|
fun(I) when is_integer(I), I > 0 -> I end) of
|
||||||
undefined -> [];
|
undefined -> [];
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
%% Description: Starts the server
|
%% Description: Starts the server
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
start_link() ->
|
start_link() ->
|
||||||
LH = ejabberd_config:get_local_option(
|
LH = ejabberd_config:get_option(
|
||||||
watchdog_large_heap,
|
watchdog_large_heap,
|
||||||
fun(I) when is_integer(I), I > 0 -> I end,
|
fun(I) when is_integer(I), I > 0 -> I end,
|
||||||
1000000),
|
1000000),
|
||||||
@ -200,7 +200,7 @@ send_message(From, To, Body) ->
|
|||||||
[{xmlcdata, Body}]}]}).
|
[{xmlcdata, Body}]}]}).
|
||||||
|
|
||||||
get_admin_jids() ->
|
get_admin_jids() ->
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
watchdog_admins,
|
watchdog_admins,
|
||||||
fun(JIDs) ->
|
fun(JIDs) ->
|
||||||
[jlib:jid_tolower(
|
[jlib:jid_tolower(
|
||||||
|
@ -827,14 +827,14 @@ process_admin(Host,
|
|||||||
{value, {_, String}} ->
|
{value, {_, String}} ->
|
||||||
case parse_access_rule(String) of
|
case parse_access_rule(String) of
|
||||||
{ok, Rs} ->
|
{ok, Rs} ->
|
||||||
ejabberd_config:add_global_option({access, Name, Host},
|
ejabberd_config:add_option({access, Name, Host},
|
||||||
Rs),
|
Rs),
|
||||||
ok;
|
ok;
|
||||||
_ -> error
|
_ -> error
|
||||||
end;
|
end;
|
||||||
_ -> nothing
|
_ -> nothing
|
||||||
end,
|
end,
|
||||||
Rules = case ejabberd_config:get_global_option(
|
Rules = case ejabberd_config:get_option(
|
||||||
{access, Name, Host}, fun(V) -> V end)
|
{access, Name, Host}, fun(V) -> V end)
|
||||||
of
|
of
|
||||||
undefined -> [];
|
undefined -> [];
|
||||||
@ -1198,7 +1198,7 @@ access_parse_addnew(_AccessRules, Host, Query) ->
|
|||||||
case lists:keysearch(<<"namenew">>, 1, Query) of
|
case lists:keysearch(<<"namenew">>, 1, Query) of
|
||||||
{value, {_, String}} when String /= <<"">> ->
|
{value, {_, String}} when String /= <<"">> ->
|
||||||
Name = jlib:binary_to_atom(String),
|
Name = jlib:binary_to_atom(String),
|
||||||
ejabberd_config:add_global_option({access, Name, Host},
|
ejabberd_config:add_option({access, Name, Host},
|
||||||
[]),
|
[]),
|
||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
@ -182,7 +182,7 @@ get_opt({Key, Host}, Opts, F) ->
|
|||||||
get_opt({Key, Host}, Opts, F, Default) ->
|
get_opt({Key, Host}, Opts, F, Default) ->
|
||||||
case gen_mod:get_opt(Key, Opts, F, undefined) of
|
case gen_mod:get_opt(Key, Opts, F, undefined) of
|
||||||
undefined ->
|
undefined ->
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
{Key, Host}, F, Default);
|
{Key, Host}, F, Default);
|
||||||
Val ->
|
Val ->
|
||||||
Val
|
Val
|
||||||
|
@ -106,7 +106,7 @@ random_instance(MaxNum) ->
|
|||||||
random:uniform(MaxNum) - 1.
|
random:uniform(MaxNum) - 1.
|
||||||
|
|
||||||
get_instances(Server) ->
|
get_instances(Server) ->
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
{extauth_instances, Server},
|
{extauth_instances, Server},
|
||||||
fun(V) when is_integer(V), V > 0 ->
|
fun(V) when is_integer(V), V > 0 ->
|
||||||
V
|
V
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
%% API
|
%% API
|
||||||
-export([start_link/3, add_iq_handler/6,
|
-export([start_link/3, add_iq_handler/6,
|
||||||
remove_iq_handler/3, stop_iq_handler/3, handle/7,
|
remove_iq_handler/3, stop_iq_handler/3, handle/7,
|
||||||
process_iq/6, check_type/1]).
|
process_iq/6, check_type/1, transform_module_options/1]).
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([init/1, handle_call/3, handle_cast/2,
|
-export([init/1, handle_call/3, handle_cast/2,
|
||||||
@ -46,7 +46,7 @@
|
|||||||
-record(state, {host, module, function}).
|
-record(state, {host, module, function}).
|
||||||
|
|
||||||
-type component() :: ejabberd_sm | ejabberd_local.
|
-type component() :: ejabberd_sm | ejabberd_local.
|
||||||
-type type() :: no_queue | one_queue | {queues, pos_integer()} | parallel.
|
-type type() :: no_queue | one_queue | pos_integer() | parallel.
|
||||||
-type opts() :: no_queue | {one_queue, pid()} | {queues, [pid()]} | parallel.
|
-type opts() :: no_queue | {one_queue, pid()} | {queues, [pid()]} | parallel.
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
@ -71,7 +71,7 @@ add_iq_handler(Component, Host, NS, Module, Function,
|
|||||||
[Host, Module, Function]),
|
[Host, Module, Function]),
|
||||||
Component:register_iq_handler(Host, NS, Module,
|
Component:register_iq_handler(Host, NS, Module,
|
||||||
Function, {one_queue, Pid});
|
Function, {one_queue, Pid});
|
||||||
{queues, N} ->
|
N when is_integer(N) ->
|
||||||
Pids = lists:map(fun (_) ->
|
Pids = lists:map(fun (_) ->
|
||||||
{ok, Pid} =
|
{ok, Pid} =
|
||||||
supervisor:start_child(ejabberd_iq_sup,
|
supervisor:start_child(ejabberd_iq_sup,
|
||||||
@ -130,9 +130,19 @@ process_iq(_Host, Module, Function, From, To, IQ) ->
|
|||||||
|
|
||||||
check_type(no_queue) -> no_queue;
|
check_type(no_queue) -> no_queue;
|
||||||
check_type(one_queue) -> one_queue;
|
check_type(one_queue) -> one_queue;
|
||||||
check_type({queues, N}) when is_integer(N), N>0 -> {queues, N};
|
check_type(N) when is_integer(N), N>0 -> N;
|
||||||
check_type(parallel) -> parallel.
|
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
|
%% gen_server callbacks
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
|
@ -64,13 +64,11 @@ start() ->
|
|||||||
-spec start_module(binary(), atom(), opts()) -> any().
|
-spec start_module(binary(), atom(), opts()) -> any().
|
||||||
|
|
||||||
start_module(Host, Module, Opts) ->
|
start_module(Host, Module, Opts) ->
|
||||||
set_module_opts_mnesia(Host, Module, Opts),
|
|
||||||
ets:insert(ejabberd_modules,
|
ets:insert(ejabberd_modules,
|
||||||
#ejabberd_module{module_host = {Module, Host},
|
#ejabberd_module{module_host = {Module, Host},
|
||||||
opts = Opts}),
|
opts = Opts}),
|
||||||
try Module:start(Host, Opts) catch
|
try Module:start(Host, Opts) catch
|
||||||
Class:Reason ->
|
Class:Reason ->
|
||||||
del_module_mnesia(Host, Module),
|
|
||||||
ets:delete(ejabberd_modules, {Module, Host}),
|
ets:delete(ejabberd_modules, {Module, Host}),
|
||||||
ErrorText =
|
ErrorText =
|
||||||
io_lib:format("Problem starting the module ~p for host "
|
io_lib:format("Problem starting the module ~p for host "
|
||||||
@ -101,7 +99,7 @@ is_app_running(AppName) ->
|
|||||||
stop_module(Host, Module) ->
|
stop_module(Host, Module) ->
|
||||||
case stop_module_keep_config(Host, Module) of
|
case stop_module_keep_config(Host, Module) of
|
||||||
error -> error;
|
error -> error;
|
||||||
ok -> del_module_mnesia(Host, Module)
|
ok -> ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc Stop the module in a host, but keep its configuration.
|
%% @doc Stop the module in a host, but keep its configuration.
|
||||||
@ -232,25 +230,6 @@ loaded_modules_with_opts(Host) ->
|
|||||||
opts = '$2'},
|
opts = '$2'},
|
||||||
[], [{{'$1', '$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()].
|
-spec get_hosts(opts(), binary()) -> [binary()].
|
||||||
|
|
||||||
get_hosts(Opts, Prefix) ->
|
get_hosts(Opts, Prefix) ->
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
%%% Author : Eric Cestari <ecestari@process-one.net>
|
%%% Author : Eric Cestari <ecestari@process-one.net>
|
||||||
%%% Purpose : Message Carbons XEP-0280 0.8
|
%%% Purpose : Message Carbons XEP-0280 0.8
|
||||||
%%% Created : 5 May 2008 by Mickael Remond <mremond@process-one.net>
|
%%% 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, []}
|
%%% {mod_carboncopy, []}
|
||||||
%%%
|
%%%
|
||||||
%%%
|
%%%
|
||||||
|
@ -129,7 +129,7 @@ process_get(#xmlel{name = <<"info">>}) ->
|
|||||||
children = []}};
|
children = []}};
|
||||||
process_get(#xmlel{name = <<"welcome-message">>,
|
process_get(#xmlel{name = <<"welcome-message">>,
|
||||||
attrs = Attrs}) ->
|
attrs = Attrs}) ->
|
||||||
{Subj, Body} = ejabberd_config:get_local_option(
|
{Subj, Body} = ejabberd_config:get_option(
|
||||||
welcome_message,
|
welcome_message,
|
||||||
fun({Subj, Body}) ->
|
fun({Subj, Body}) ->
|
||||||
{iolist_to_binary(Subj),
|
{iolist_to_binary(Subj),
|
||||||
@ -145,7 +145,7 @@ process_get(#xmlel{name = <<"welcome-message">>,
|
|||||||
children = [{xmlcdata, Body}]}]}};
|
children = [{xmlcdata, Body}]}]}};
|
||||||
process_get(#xmlel{name = <<"registration-watchers">>,
|
process_get(#xmlel{name = <<"registration-watchers">>,
|
||||||
attrs = Attrs}) ->
|
attrs = Attrs}) ->
|
||||||
SubEls = ejabberd_config:get_local_option(
|
SubEls = ejabberd_config:get_option(
|
||||||
registration_watchers,
|
registration_watchers,
|
||||||
fun(JIDs) when is_list(JIDs) ->
|
fun(JIDs) when is_list(JIDs) ->
|
||||||
lists:map(
|
lists:map(
|
||||||
|
@ -36,7 +36,8 @@
|
|||||||
process_sm_iq_items/3, process_sm_iq_info/3,
|
process_sm_iq_items/3, process_sm_iq_info/3,
|
||||||
get_sm_identity/5, get_sm_features/5, get_sm_items/5,
|
get_sm_identity/5, get_sm_features/5, get_sm_items/5,
|
||||||
get_info/5, register_feature/2, unregister_feature/2,
|
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("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
@ -440,6 +441,22 @@ get_user_resources(User, Server) ->
|
|||||||
end,
|
end,
|
||||||
lists:sort(Rs)).
|
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
|
%%% 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_info(Acc, _, _, _Node, _) -> Acc.
|
||||||
|
|
||||||
get_fields_xml(Host, Module) ->
|
get_fields_xml(Host, Module) ->
|
||||||
Fields = gen_mod:get_module_opt(Host, ?MODULE, server_info,
|
Fields = gen_mod:get_module_opt(
|
||||||
fun(L) when is_list(L) -> L end,
|
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, _, _}) ->
|
Fields_good = lists:filter(fun ({Modules, _, _}) ->
|
||||||
case Modules of
|
case Modules of
|
||||||
all -> true;
|
all -> true;
|
||||||
|
@ -32,7 +32,8 @@
|
|||||||
|
|
||||||
-export([start/2, stop/1, process_local_iq/3, export/1,
|
-export([start/2, stop/1, process_local_iq/3, export/1,
|
||||||
process_sm_iq/3, on_presence_update/4, import/1, import/3,
|
process_sm_iq/3, on_presence_update/4, import/1, import/3,
|
||||||
store_last_info/4, get_last_info/2, remove_user/2]).
|
store_last_info/4, get_last_info/2, remove_user/2,
|
||||||
|
transform_options/1]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
@ -101,18 +102,13 @@ process_local_iq(_From, _To,
|
|||||||
%% @doc Get the uptime of the ejabberd node, expressed in seconds.
|
%% @doc Get the uptime of the ejabberd node, expressed in seconds.
|
||||||
%% When ejabberd is starting, ejabberd_config:start/0 stores the datetime.
|
%% When ejabberd is starting, ejabberd_config:start/0 stores the datetime.
|
||||||
get_node_uptime() ->
|
get_node_uptime() ->
|
||||||
case ejabberd_config:get_local_option(
|
case ejabberd_config:get_option(
|
||||||
node_start,
|
node_start,
|
||||||
fun({MegaSecs, Secs, MicroSecs} = Now)
|
fun(S) when is_integer(S), S >= 0 -> S end) of
|
||||||
when is_integer(MegaSecs), MegaSecs >= 0,
|
|
||||||
is_integer(Secs), Secs >= 0,
|
|
||||||
is_integer(MicroSecs), MicroSecs >= 0 ->
|
|
||||||
Now
|
|
||||||
end) of
|
|
||||||
undefined ->
|
undefined ->
|
||||||
trunc(element(1, erlang:statistics(wall_clock)) / 1000);
|
trunc(element(1, erlang:statistics(wall_clock)) / 1000);
|
||||||
StartNow ->
|
Now ->
|
||||||
now_to_seconds(now()) - now_to_seconds(StartNow)
|
now_to_seconds(now()) - Now
|
||||||
end.
|
end.
|
||||||
|
|
||||||
now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
|
now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
|
||||||
@ -319,3 +315,13 @@ import(_LServer, mnesia, #last_activity{} = LA) ->
|
|||||||
mnesia:dirty_write(LA);
|
mnesia:dirty_write(LA);
|
||||||
import(_, _, _) ->
|
import(_, _, _) ->
|
||||||
pass.
|
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].
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
-behaviour(gen_mod).
|
-behaviour(gen_mod).
|
||||||
|
|
||||||
%% API
|
%% 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]).
|
check_access_log/2, add_to_log/5]).
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
@ -111,6 +111,14 @@ check_access_log(Host, From) ->
|
|||||||
Res -> Res
|
Res -> Res
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
transform_module_options(Opts) ->
|
||||||
|
lists:map(
|
||||||
|
fun({top_link, {S1, S2}}) ->
|
||||||
|
{top_link, [{S1, S2}]};
|
||||||
|
(Opt) ->
|
||||||
|
Opt
|
||||||
|
end, Opts).
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
@ -152,14 +160,14 @@ init([Host, Opts]) ->
|
|||||||
(universal) -> universal
|
(universal) -> universal
|
||||||
end, local),
|
end, local),
|
||||||
Top_link = gen_mod:get_opt(top_link, Opts,
|
Top_link = gen_mod:get_opt(top_link, Opts,
|
||||||
fun({S1, S2}) ->
|
fun([{S1, S2}]) ->
|
||||||
{iolist_to_binary(S1),
|
{iolist_to_binary(S1),
|
||||||
iolist_to_binary(S2)}
|
iolist_to_binary(S2)}
|
||||||
end, {<<"/">>, <<"Home">>}),
|
end, {<<"/">>, <<"Home">>}),
|
||||||
NoFollow = gen_mod:get_opt(spam_prevention, Opts,
|
NoFollow = gen_mod:get_opt(spam_prevention, Opts,
|
||||||
fun(B) when is_boolean(B) -> B end,
|
fun(B) when is_boolean(B) -> B end,
|
||||||
true),
|
true),
|
||||||
Lang = ejabberd_config:get_local_option(
|
Lang = ejabberd_config:get_option(
|
||||||
{language, Host},
|
{language, Host},
|
||||||
fun iolist_to_binary/1,
|
fun iolist_to_binary/1,
|
||||||
?MYLANG),
|
?MYLANG),
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
-behaviour(supervisor).
|
-behaviour(supervisor).
|
||||||
|
|
||||||
%% gen_mod callbacks.
|
%% gen_mod callbacks.
|
||||||
-export([start/2, stop/1]).
|
-export([start/2, stop/1, transform_module_options/1]).
|
||||||
|
|
||||||
%% supervisor callbacks.
|
%% supervisor callbacks.
|
||||||
-export([init/1]).
|
-export([init/1]).
|
||||||
@ -64,6 +64,9 @@ start_link(Host, Opts) ->
|
|||||||
supervisor:start_link({local, Proc}, ?MODULE,
|
supervisor:start_link({local, Proc}, ?MODULE,
|
||||||
[Host, Opts]).
|
[Host, Opts]).
|
||||||
|
|
||||||
|
transform_module_options(Opts) ->
|
||||||
|
mod_proxy65_service:transform_module_options(Opts).
|
||||||
|
|
||||||
init([Host, Opts]) ->
|
init([Host, Opts]) ->
|
||||||
Service = {mod_proxy65_service,
|
Service = {mod_proxy65_service,
|
||||||
{mod_proxy65_service, start_link, [Host, Opts]},
|
{mod_proxy65_service, start_link, [Host, Opts]},
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
handle_cast/2, terminate/2, code_change/3]).
|
handle_cast/2, terminate/2, code_change/3]).
|
||||||
|
|
||||||
%% API.
|
%% API.
|
||||||
-export([start_link/2, add_listener/2,
|
-export([start_link/2, add_listener/2, transform_module_options/1,
|
||||||
delete_listener/1]).
|
delete_listener/1]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
@ -261,16 +261,15 @@ parse_options(ServerHost, Opts) ->
|
|||||||
Name = gen_mod:get_opt(name, Opts, fun iolist_to_binary/1,
|
Name = gen_mod:get_opt(name, Opts, fun iolist_to_binary/1,
|
||||||
<<"SOCKS5 Bytestreams">>),
|
<<"SOCKS5 Bytestreams">>),
|
||||||
IP = gen_mod:get_opt(ip, Opts,
|
IP = gen_mod:get_opt(ip, Opts,
|
||||||
fun(Addr) ->
|
fun(S) ->
|
||||||
jlib:ip_to_list(Addr),
|
{ok, Addr} = inet_parse:address(
|
||||||
|
binary_to_list(
|
||||||
|
iolist_to_binary(S))),
|
||||||
Addr
|
Addr
|
||||||
end, get_my_ip()),
|
end, get_my_ip()),
|
||||||
HostName = gen_mod:get_opt(hostname, Opts,
|
HostName = gen_mod:get_opt(hostname, Opts,
|
||||||
fun(Addr) when is_tuple(Addr) ->
|
fun iolist_to_binary/1,
|
||||||
jlib:ip_to_list(Addr);
|
jlib:ip_to_list(IP)),
|
||||||
(S) ->
|
|
||||||
iolist_to_binary(S)
|
|
||||||
end, jlib:ip_to_list(IP)),
|
|
||||||
StreamAddr = [{<<"jid">>, MyHost},
|
StreamAddr = [{<<"jid">>, MyHost},
|
||||||
{<<"host">>, HostName},
|
{<<"host">>, HostName},
|
||||||
{<<"port">>, jlib:integer_to_binary(Port)}],
|
{<<"port">>, jlib:integer_to_binary(Port)}],
|
||||||
@ -278,6 +277,16 @@ parse_options(ServerHost, Opts) ->
|
|||||||
name = Name, port = Port, ip = IP,
|
name = Name, port = Port, ip = IP,
|
||||||
stream_addr = StreamAddr, acl = ACL}.
|
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() ->
|
get_my_ip() ->
|
||||||
{ok, MyHostName} = inet:gethostname(),
|
{ok, MyHostName} = inet:gethostname(),
|
||||||
case inet:getaddr(MyHostName, inet) of
|
case inet:getaddr(MyHostName, inet) of
|
||||||
|
@ -279,10 +279,14 @@ select_auth_method(anonymous, AuthMethods) ->
|
|||||||
|
|
||||||
%% Obviously, we must use shaper with maximum rate.
|
%% Obviously, we must use shaper with maximum rate.
|
||||||
find_maxrate(Shaper, JID1, JID2, Host) ->
|
find_maxrate(Shaper, JID1, JID2, Host) ->
|
||||||
MaxRate1 = shaper:new(acl:match_rule(Host, Shaper,
|
MaxRate1 = case acl:match_rule(Host, Shaper, JID1) of
|
||||||
JID1)),
|
deny -> none;
|
||||||
MaxRate2 = shaper:new(acl:match_rule(Host, Shaper,
|
R1 -> shaper:new(R1)
|
||||||
JID2)),
|
end,
|
||||||
|
MaxRate2 = case acl:match_rule(Host, Shaper, JID2) of
|
||||||
|
deny -> none;
|
||||||
|
R2 -> shaper:new(R2)
|
||||||
|
end,
|
||||||
if MaxRate1 == none; MaxRate2 == none -> none;
|
if MaxRate1 == none; MaxRate2 == none -> none;
|
||||||
true -> lists:max([MaxRate1, MaxRate2])
|
true -> lists:max([MaxRate1, MaxRate2])
|
||||||
end.
|
end.
|
||||||
|
@ -32,7 +32,8 @@
|
|||||||
|
|
||||||
-export([start/2, stop/1, stream_feature_register/2,
|
-export([start/2, stop/1, stream_feature_register/2,
|
||||||
unauthenticated_iq_register/4, try_register/5,
|
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("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
@ -418,7 +419,11 @@ try_register(User, Server, Password, SourceRaw, Lang) ->
|
|||||||
send_welcome_message(JID) ->
|
send_welcome_message(JID) ->
|
||||||
Host = JID#jid.lserver,
|
Host = JID#jid.lserver,
|
||||||
case gen_mod:get_module_opt(Host, ?MODULE, welcome_message,
|
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(S),
|
||||||
iolist_to_binary(B)}
|
iolist_to_binary(B)}
|
||||||
end, {<<"">>, <<"">>})
|
end, {<<"">>, <<"">>})
|
||||||
@ -483,7 +488,7 @@ check_from(JID, Server) ->
|
|||||||
|
|
||||||
check_timeout(undefined) -> true;
|
check_timeout(undefined) -> true;
|
||||||
check_timeout(Source) ->
|
check_timeout(Source) ->
|
||||||
Timeout = ejabberd_config:get_local_option(
|
Timeout = ejabberd_config:get_option(
|
||||||
registration_timeout,
|
registration_timeout,
|
||||||
fun(TO) when is_integer(TO), TO > 0 ->
|
fun(TO) when is_integer(TO), TO > 0 ->
|
||||||
TO;
|
TO;
|
||||||
@ -537,7 +542,7 @@ clean_treap(Treap, CleanPriority) ->
|
|||||||
|
|
||||||
remove_timeout(undefined) -> true;
|
remove_timeout(undefined) -> true;
|
||||||
remove_timeout(Source) ->
|
remove_timeout(Source) ->
|
||||||
Timeout = ejabberd_config:get_local_option(
|
Timeout = ejabberd_config:get_option(
|
||||||
registration_timeout,
|
registration_timeout,
|
||||||
fun(TO) when is_integer(TO), TO > 0 ->
|
fun(TO) when is_integer(TO), TO > 0 ->
|
||||||
TO;
|
TO;
|
||||||
@ -604,6 +609,54 @@ is_strong_password(Server, Password) ->
|
|||||||
ejabberd_auth:entropy(Password) >= Entropy
|
ejabberd_auth:entropy(Password) >= Entropy
|
||||||
end.
|
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
|
%%% ip_access management
|
||||||
%%%
|
%%%
|
||||||
@ -614,75 +667,15 @@ may_remove_resource(From) -> From.
|
|||||||
|
|
||||||
get_ip_access(Host) ->
|
get_ip_access(Host) ->
|
||||||
gen_mod:get_module_opt(Host, ?MODULE, ip_access,
|
gen_mod:get_module_opt(Host, ?MODULE, ip_access,
|
||||||
fun(IPAccess) ->
|
fun(A) when is_atom(A) -> A end,
|
||||||
lists:flatmap(
|
all).
|
||||||
fun({Access, S}) ->
|
|
||||||
{ok, IP, Mask} =
|
|
||||||
parse_ip_netmask(
|
|
||||||
iolist_to_binary(S)),
|
|
||||||
[{Access, IP, Mask}]
|
|
||||||
end, IPAccess)
|
|
||||||
end, []).
|
|
||||||
|
|
||||||
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) ->
|
check_ip_access({User, Server, Resource}, IPAccess) ->
|
||||||
case ejabberd_sm:get_user_ip(User, Server, Resource) of
|
case ejabberd_sm:get_user_ip(User, Server, Resource) of
|
||||||
{IPAddress, _PortNumber} ->
|
{IPAddress, _PortNumber} ->
|
||||||
check_ip_access(IPAddress, IPAccess);
|
check_ip_access(IPAddress, IPAccess);
|
||||||
_ -> true
|
_ ->
|
||||||
|
deny
|
||||||
end;
|
end;
|
||||||
check_ip_access({_, _, _, _} = IP,
|
check_ip_access(IPAddress, IPAccess) ->
|
||||||
[{Access, {_, _, _, _} = Net, Mask} | IPAccess]) ->
|
acl:match_rule(global, IPAccess, IPAddress).
|
||||||
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.
|
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
-export([start/2, start_link/2, stop/1,
|
-export([start/2, start_link/2, stop/1,
|
||||||
get_sm_features/5, process_local_iq/3, process_sm_iq/3,
|
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("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
@ -767,7 +767,7 @@ parse_options(Host, Opts) ->
|
|||||||
VCardMap = gen_mod:get_opt(ldap_vcard_map, Opts,
|
VCardMap = gen_mod:get_opt(ldap_vcard_map, Opts,
|
||||||
fun(Ls) ->
|
fun(Ls) ->
|
||||||
lists:map(
|
lists:map(
|
||||||
fun({S, P, L}) ->
|
fun({S, [{P, L}]}) ->
|
||||||
{iolist_to_binary(S),
|
{iolist_to_binary(S),
|
||||||
iolist_to_binary(P),
|
iolist_to_binary(P),
|
||||||
[iolist_to_binary(E)
|
[iolist_to_binary(E)
|
||||||
@ -823,6 +823,20 @@ parse_options(Host, Opts) ->
|
|||||||
search_reported_attrs = SearchReportedAttrs,
|
search_reported_attrs = SearchReportedAttrs,
|
||||||
matches = Matches}.
|
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) ->
|
check_filter(F) ->
|
||||||
NewF = iolist_to_binary(F),
|
NewF = iolist_to_binary(F),
|
||||||
{ok, _} = eldap_filter:parse(NewF),
|
{ok, _} = eldap_filter:parse(NewF),
|
||||||
|
@ -490,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
|
%% Check that the mod_caps module is enabled in that Jabber Host
|
||||||
%% If not, show a warning message in the ejabberd log file.
|
%% If not, show a warning message in the ejabberd log file.
|
||||||
complain_if_modcaps_disabled(ServerHost) ->
|
complain_if_modcaps_disabled(ServerHost) ->
|
||||||
Modules = ejabberd_config:get_local_option({modules, ServerHost}, fun(Ms) when is_list(Ms) -> Ms end),
|
case gen_mod:is_loaded(ServerHost, mod_caps) of
|
||||||
ModCaps = [mod_caps_enabled || {mod_caps, _Opts} <- Modules],
|
false ->
|
||||||
case ModCaps of
|
|
||||||
[] ->
|
|
||||||
?WARNING_MSG("The PEP plugin is enabled in mod_pubsub "
|
?WARNING_MSG("The PEP plugin is enabled in mod_pubsub "
|
||||||
"of host ~p. This plugin requires mod_caps "
|
"of host ~p. This plugin requires mod_caps "
|
||||||
"to be enabled, but it isn't.",
|
"to be enabled, but it isn't.",
|
||||||
[ServerHost]);
|
[ServerHost]);
|
||||||
_ -> ok
|
true -> ok
|
||||||
end.
|
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
|
%% Check that the mod_caps module is enabled in that Jabber Host
|
||||||
%% If not, show a warning message in the ejabberd log file.
|
%% If not, show a warning message in the ejabberd log file.
|
||||||
complain_if_modcaps_disabled(ServerHost) ->
|
complain_if_modcaps_disabled(ServerHost) ->
|
||||||
Modules = ejabberd_config:get_local_option({modules,
|
Modules = ejabberd_config:get_option({modules,
|
||||||
ServerHost},
|
ServerHost},
|
||||||
fun(Ms) when is_list(Ms) -> Ms end),
|
fun(Ms) when is_list(Ms) -> Ms end),
|
||||||
ModCaps = [mod_caps_enabled
|
ModCaps = [mod_caps_enabled
|
||||||
|
@ -219,13 +219,15 @@ list_users(LServer,
|
|||||||
[Prefix, Limit, Offset]))]).
|
[Prefix, Limit, Offset]))]).
|
||||||
|
|
||||||
users_number(LServer) ->
|
users_number(LServer) ->
|
||||||
case element(1,
|
Type = ejabberd_config:get_option({odbc_type, LServer},
|
||||||
ejabberd_config:get_local_option(
|
fun(pgsql) -> pgsql;
|
||||||
{odbc_server, LServer}, fun(V) -> V end))
|
(mysql) -> mysql;
|
||||||
of
|
(odbc) -> odbc
|
||||||
|
end, odbc),
|
||||||
|
case Type of
|
||||||
pgsql ->
|
pgsql ->
|
||||||
case
|
case
|
||||||
ejabberd_config:get_local_option(
|
ejabberd_config:get_option(
|
||||||
{pgsql_users_number_estimate, LServer},
|
{pgsql_users_number_estimate, LServer},
|
||||||
fun(V) when is_boolean(V) -> V end,
|
fun(V) when is_boolean(V) -> V end,
|
||||||
false)
|
false)
|
||||||
|
@ -28,7 +28,8 @@
|
|||||||
|
|
||||||
-author('alexey@process-one.net').
|
-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("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
@ -37,32 +38,66 @@
|
|||||||
lastrate = 0.0 :: float(),
|
lastrate = 0.0 :: float(),
|
||||||
lasttime = 0 :: integer()}).
|
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]).
|
-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) ->
|
new(Name) ->
|
||||||
Data = ejabberd_config:get_global_option(
|
MaxRate = case ets:lookup(shaper, {Name, global}) of
|
||||||
{shaper, Name, global},
|
[#shaper{maxrate = R}] ->
|
||||||
fun({maxrate, R}) when is_integer(R), R>0 ->
|
R;
|
||||||
{maxrate, R};
|
[] ->
|
||||||
(none) ->
|
?WARNING_MSG("Attempt to initialize an "
|
||||||
|
"unspecified shaper '~s'", [Name]),
|
||||||
none
|
none
|
||||||
end, none),
|
end,
|
||||||
new1(Data).
|
new1(MaxRate).
|
||||||
|
|
||||||
-spec new1(none | {maxrate, integer()}) -> maxrate().
|
-spec new1(none | integer()) -> shaper().
|
||||||
|
|
||||||
new1(none) -> none;
|
new1(none) -> none;
|
||||||
new1({maxrate, MaxRate}) ->
|
new1(MaxRate) ->
|
||||||
#maxrate{maxrate = MaxRate, lastrate = 0.0,
|
#maxrate{maxrate = MaxRate, lastrate = 0.0,
|
||||||
lasttime = now_to_usec(now())}.
|
lasttime = now_to_usec(now())}.
|
||||||
|
|
||||||
-spec update(maxrate(), integer()) -> {maxrate(), integer()}.
|
-spec update(shaper(), integer()) -> {shaper(), integer()}.
|
||||||
|
|
||||||
update(none, _Size) -> {none, 0};
|
update(none, _Size) -> {none, 0};
|
||||||
update(#maxrate{} = State, Size) ->
|
update(#maxrate{} = State, Size) ->
|
||||||
@ -84,5 +119,15 @@ update(#maxrate{} = State, Size) ->
|
|||||||
lasttime = NextNow},
|
lasttime = NextNow},
|
||||||
Pause}.
|
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}) ->
|
now_to_usec({MSec, Sec, USec}) ->
|
||||||
(MSec * 1000000 + Sec) * 1000000 + USec.
|
(MSec * 1000000 + Sec) * 1000000 + USec.
|
||||||
|
267
test/ejabberd_SUITE_data/ejabberd.yml
Normal file
267
test/ejabberd_SUITE_data/ejabberd.yml
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
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:
|
||||||
|
db_type: odbc
|
||||||
|
mod_mam:
|
||||||
|
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: []
|
||||||
|
host_config:
|
||||||
|
"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:
|
||||||
|
db_type: odbc
|
||||||
|
mod_mam:
|
||||||
|
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: []
|
||||||
|
host_config:
|
||||||
|
"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:
|
||||||
|
db_type: internal
|
||||||
|
mod_mam:
|
||||||
|
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: []
|
||||||
|
host_config:
|
||||||
|
"localhost":
|
||||||
|
auth_method: internal
|
||||||
|
host_config:
|
||||||
|
"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: []
|
||||||
|
host_config:
|
||||||
|
"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
|
@ -21,7 +21,7 @@ init_config(Config) ->
|
|||||||
PrivDir = proplists:get_value(priv_dir, Config),
|
PrivDir = proplists:get_value(priv_dir, Config),
|
||||||
[_, _|Tail] = lists:reverse(filename:split(DataDir)),
|
[_, _|Tail] = lists:reverse(filename:split(DataDir)),
|
||||||
BaseDir = filename:join(lists:reverse(Tail)),
|
BaseDir = filename:join(lists:reverse(Tail)),
|
||||||
ConfigPath = filename:join([DataDir, "ejabberd.cfg"]),
|
ConfigPath = filename:join([DataDir, "ejabberd.yml"]),
|
||||||
LogPath = filename:join([PrivDir, "ejabberd.log"]),
|
LogPath = filename:join([PrivDir, "ejabberd.log"]),
|
||||||
SASLPath = filename:join([PrivDir, "sasl.log"]),
|
SASLPath = filename:join([PrivDir, "sasl.log"]),
|
||||||
MnesiaDir = filename:join([PrivDir, "mnesia"]),
|
MnesiaDir = filename:join([PrivDir, "mnesia"]),
|
||||||
|
Loading…
Reference in New Issue
Block a user