Compare commits
281 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b72ed7afa4 | |||
| c109d3eff0 | |||
| 0d743da595 | |||
| ef57067edc | |||
| e054c2800b | |||
| 49f1b4a691 | |||
| 8b61c7fe4b | |||
| 9bac2fa185 | |||
| c3f62c037d | |||
| 4ddee2d89b | |||
| 054426072e | |||
| f28200b6de | |||
| 8c16400332 | |||
| 0394baaa7a | |||
| 1b3a6dd54a | |||
| 6d1ea222c0 | |||
| 08f3d066b1 | |||
| ddca2e8b4a | |||
| a2b2a27bb6 | |||
| d60d72d7bf | |||
| 7ff5f2d3fa | |||
| 1866b56e3b | |||
| e96bfbdbfa | |||
| 3cc964fbcc | |||
| 29f6c43ae3 | |||
| 03de853e4f | |||
| fb367469d4 | |||
| d2cdfa66f9 | |||
| d5c1174385 | |||
| dd888f90ec | |||
| bb9593dd12 | |||
| de385591d0 | |||
| 78dae4036e | |||
| dafea66c0f | |||
| c851f9608a | |||
| 3367c5b120 | |||
| d4579d2a20 | |||
| 410ac9b966 | |||
| adf0d7de91 | |||
| 8c03427c25 | |||
| c156eabb24 | |||
| 1d6cbd2561 | |||
| a7a1e7be94 | |||
| 8673d2926d | |||
| 2d246f61dd | |||
| 9de2ca4568 | |||
| b545301f63 | |||
| 145c0116bf | |||
| 79c511a441 | |||
| 90b22da880 | |||
| 57936bfa4e | |||
| 5c931d7004 | |||
| 88d0b71d58 | |||
| b416527e4f | |||
| 8c8c480477 | |||
| 66132353df | |||
| b30a9f2f75 | |||
| 0cbd41fbdc | |||
| 5b055d7eec | |||
| 01a1f929b4 | |||
| f0f3ec211e | |||
| fd76bc9242 | |||
| 6cd70947be | |||
| 8cefe58a89 | |||
| c3361bab95 | |||
| f3f3b1586e | |||
| 0bf93eefcb | |||
| 10e01b7bfc | |||
| 8f0e066135 | |||
| dfd96b6037 | |||
| 5b373470ac | |||
| 0146189b65 | |||
| 4c4c82897c | |||
| 5509e648ad | |||
| c9ba0e83d2 | |||
| b56c012407 | |||
| 9b48dc9cc3 | |||
| efb4fd0d10 | |||
| 83e2462853 | |||
| cdfd0cce7b | |||
| 2d45832a39 | |||
| 1af2cf37ea | |||
| ca022b6d1f | |||
| e54f1a8485 | |||
| 1be2112634 | |||
| 6dc452e7f5 | |||
| 57a3512dcc | |||
| 1de69174ef | |||
| 26b9d25f32 | |||
| 8ad6afd652 | |||
| 7fed5a3eb6 | |||
| b199b68380 | |||
| e433a63105 | |||
| 68c9328a9c | |||
| 6601f182c4 | |||
| 326db5535c | |||
| 2539be1a04 | |||
| 4e9930597d | |||
| 2dfb5a6a5c | |||
| 8faa6afa67 | |||
| 12e537c43f | |||
| 4394ec38b6 | |||
| 420e05fa0d | |||
| e2fb154fe9 | |||
| e9f219a0ac | |||
| 711c5c0d54 | |||
| f9ed34db4d | |||
| 9a895058e7 | |||
| e76a57e144 | |||
| 6fc6bdefc2 | |||
| 96e35a3248 | |||
| 48be8e7b1e | |||
| f40f3a9da7 | |||
| f81b49fe44 | |||
| 395d2e86bc | |||
| 5b3af9d4cd | |||
| 7e5d766a02 | |||
| c5dd1bdd9d | |||
| d03432a956 | |||
| 4b747c2c78 | |||
| 3a566e3cdf | |||
| b915469f5e | |||
| 8b9166d067 | |||
| dc6861eb73 | |||
| 90a4aafec0 | |||
| 4c06f13d18 | |||
| 8c796ed027 | |||
| 68d12017cc | |||
| 491993d401 | |||
| a981bf9a59 | |||
| 920e4512b6 | |||
| fbdcc44fd9 | |||
| b2b29269ec | |||
| 11811e5f48 | |||
| b7f62a4fa7 | |||
| 0bb14d16c7 | |||
| 59f5a098b5 | |||
| ed1ee6061e | |||
| 50b645aa92 | |||
| 52f2a7de4b | |||
| bce8922e5d | |||
| 86236431b9 | |||
| c0d4d31b5b | |||
| 295bec8551 | |||
| b341a3cef3 | |||
| fface33d54 | |||
| fbf6ba2738 | |||
| 38ec3f66c7 | |||
| 56dc625f9a | |||
| 7c5ee93c88 | |||
| 77163c43d2 | |||
| d1d02e2f26 | |||
| 6b8bc811ac | |||
| b662ec2a78 | |||
| 8ca035496e | |||
| a463f5a25a | |||
| dce4e4de6d | |||
| 9b70177fd5 | |||
| 1fbb36c34a | |||
| 46abf7cfab | |||
| 62cb398734 | |||
| dff940b89e | |||
| 66591b1c0d | |||
| b094ce8ea5 | |||
| 9c82c2f6d0 | |||
| 0a40ab93c8 | |||
| c526b0e8ff | |||
| 35136f47ed | |||
| 8f2233eff7 | |||
| 8879d1d533 | |||
| 71ae7e9fd9 | |||
| 644873dae9 | |||
| 9a11db91f9 | |||
| af8c6d2428 | |||
| 87357c700f | |||
| 7881c5670c | |||
| 881e02632b | |||
| 47d117c1bf | |||
| cf6f540d53 | |||
| 557e6ecdd0 | |||
| 5dd3f4c22b | |||
| e7c3b57b8b | |||
| c907915695 | |||
| 911ed4a7ca | |||
| 499ae96254 | |||
| ac31c85866 | |||
| c4c91cc956 | |||
| d809aafba0 | |||
| 6ffb120fce | |||
| 55f8aa1b22 | |||
| 274e9fe7b5 | |||
| f465742f2c | |||
| 9e83c45b3c | |||
| 446e6e6f3b | |||
| 3a5d2dbed8 | |||
| aea6166efe | |||
| 23cc0f8c3c | |||
| 81f4dd0e6a | |||
| dee3081df1 | |||
| 6acac7c93f | |||
| c2f664f941 | |||
| 75127a0deb | |||
| bb76da03ea | |||
| 3099702039 | |||
| 3ec623f329 | |||
| 6c323b729b | |||
| 17b05ff4b7 | |||
| 7bd5c7fe59 | |||
| 7b04a625be | |||
| 49b08949b1 | |||
| 141be53c21 | |||
| fd8e07af47 | |||
| c3c75affa9 | |||
| 85f09b365f | |||
| 645f11d79d | |||
| 0a20e45690 | |||
| 0a9f522222 | |||
| 4f293751f0 | |||
| 6f481e3ceb | |||
| 00534d4566 | |||
| 40185b6bd3 | |||
| 1de407c420 | |||
| 815b95c623 | |||
| 8e1a13b259 | |||
| a4049d9418 | |||
| ba30ac8ce8 | |||
| 43c3134f55 | |||
| 67fc2015de | |||
| 548ef7b835 | |||
| 4337300fce | |||
| cab8005bf3 | |||
| 4fc8d1c4a4 | |||
| 1261502f6a | |||
| dacba3ec00 | |||
| 4f8af723c6 | |||
| 5e446d50a8 | |||
| 22d76659c0 | |||
| 0f9db50c8d | |||
| 52b8226671 | |||
| 3b9e6eaa95 | |||
| 2c18f89d5b | |||
| 0381ce1e75 | |||
| fc77051b68 | |||
| bfe2545c01 | |||
| c6a9c30f1c | |||
| 508f3ef88d | |||
| 97f7d99007 | |||
| 2bbfc0b79e | |||
| 10a5a5eb01 | |||
| 2e529f5826 | |||
| 4a4cc32650 | |||
| d2114be6f3 | |||
| db51d522e8 | |||
| 82c42051c3 | |||
| e4c106e0dd | |||
| b64e1d95d2 | |||
| c41bab9ca0 | |||
| 063737e4f5 | |||
| cb3bb710bd | |||
| c30715e67b | |||
| 27594db029 | |||
| 6ac8f6eaee | |||
| 338d27b45b | |||
| 35a076c251 | |||
| 680384c342 | |||
| 11ff2a1ccf | |||
| 3ac1675919 | |||
| de85c1718e | |||
| 46f47db512 | |||
| 6811b92a80 | |||
| 8766854870 | |||
| 61ae0ff02c | |||
| 5522403e8e | |||
| b23d5754e8 | |||
| b1a03cc346 | |||
| ca94cbfd31 | |||
| 410db89167 | |||
| 638f2d2e67 | |||
| 56ee6f0518 | |||
| 4e83fc41d4 | |||
| 4ea481d1dd |
+18
-12
@@ -1,15 +1,21 @@
|
||||
> What version of ejabberd are you using?
|
||||
Environment
|
||||
-----------
|
||||
- ejabberd version: 18.06
|
||||
- Erlang version: `erl +V`
|
||||
- OS: Linux (Debian)
|
||||
- Installed from: source | distro package | official deb/rpm | official binary installer | other
|
||||
|
||||
Configuration (only if needed): grep -Ev '^$|^\s*#' ejabberd.yml
|
||||
---------------------------------------------------------------------------
|
||||
```yaml
|
||||
loglevel: 4
|
||||
...
|
||||
```
|
||||
|
||||
Errors from error.log/crash.log
|
||||
-------------------------------
|
||||
No errors
|
||||
|
||||
> What operating system (version) are you using?
|
||||
|
||||
|
||||
|
||||
> How did you install ejabberd (source, package, distribution)?
|
||||
|
||||
|
||||
|
||||
> What did not work as expected? Are there error messages in the log? What
|
||||
> was the unexpected behavior? What was the expected result?
|
||||
|
||||
Bug description
|
||||
---------------
|
||||
Nothing works, plz halp :(
|
||||
|
||||
@@ -58,6 +58,9 @@ JSDIR = $(PRIVDIR)/js
|
||||
# /usr/lib/ejabberd/priv/sql
|
||||
SQLDIR = $(PRIVDIR)/sql
|
||||
|
||||
# /usr/lib/ejabberd/priv/lua
|
||||
LUADIR = $(PRIVDIR)/lua
|
||||
|
||||
# /var/lib/ejabberd/
|
||||
SPOOLDIR = $(DESTDIR)@localstatedir@/lib/ejabberd
|
||||
|
||||
@@ -283,6 +286,8 @@ uninstall-binary:
|
||||
rm -fr $(JSDIR)
|
||||
rm -f $(SQLDIR)/*.sql
|
||||
rm -fr $(SQLDIR)
|
||||
rm -fr $(LUADIR)/*.lua
|
||||
rm -fr $(LUADIR)
|
||||
rm -fr $(PRIVDIR)
|
||||
rm -fr $(EJABBERDDIR)
|
||||
|
||||
|
||||
@@ -93,7 +93,6 @@ Moreover, ejabberd comes with a wide range of other state-of-the-art features:
|
||||
- Users Directory based on users vCards.
|
||||
- Publish-Subscribe component with support for Personal Eventing.
|
||||
- Support for web clients: HTTP Polling and HTTP Binding (BOSH).
|
||||
- IRC transport.
|
||||
- Component support: interface with networks such as AIM, ICQ and MSN.
|
||||
|
||||
|
||||
@@ -112,8 +111,6 @@ To compile ejabberd you need:
|
||||
- OpenSSL 1.0.0 or higher, for STARTTLS, SASL and SSL encryption.
|
||||
- Zlib 1.2.3 or higher, for Stream Compression support (XEP-0138). Optional.
|
||||
- PAM library. Optional. For Pluggable Authentication Modules (PAM).
|
||||
- GNU Iconv 1.8 or higher, for the IRC Transport (mod_irc). Optional. Not
|
||||
needed on systems with GNU Libc.
|
||||
- ImageMagick's Convert program. Optional. For CAPTCHA challenges.
|
||||
|
||||
If your system splits packages in libraries and development headers, you must
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
XmppAddr { iso(1) identified-organization(3)
|
||||
dod(6) internet(1) security(5) mechanisms(5) pkix(7)
|
||||
id-on(8) id-on-xmppAddr(5) }
|
||||
|
||||
DEFINITIONS EXPLICIT TAGS ::=
|
||||
BEGIN
|
||||
|
||||
id-on-xmppAddr OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
|
||||
dod(6) internet(1) security(5) mechanisms(5) pkix(7)
|
||||
id-on(8) 5 }
|
||||
|
||||
XmppAddr ::= UTF8String
|
||||
|
||||
END
|
||||
@@ -94,9 +94,6 @@ defmodule Ejabberd.ConfigFile do
|
||||
module :mod_disco do
|
||||
end
|
||||
|
||||
module :mod_irc do
|
||||
end
|
||||
|
||||
module :mod_http_bind do
|
||||
end
|
||||
|
||||
|
||||
@@ -565,7 +565,6 @@ modules:
|
||||
mod_configure: {} # requires mod_adhoc
|
||||
mod_disco: {}
|
||||
## mod_echo: {}
|
||||
mod_irc: {}
|
||||
mod_http_bind: {}
|
||||
## mod_http_fileserver:
|
||||
## docroot: "/var/www"
|
||||
|
||||
+2
-4
@@ -197,12 +197,12 @@ AC_ARG_ENABLE(elixir,
|
||||
esac],[if test "x$elixir" = "x"; then elixir=false; fi])
|
||||
|
||||
AC_ARG_ENABLE(iconv,
|
||||
[AC_HELP_STRING([--enable-iconv], [enable iconv support (default: yes)])],
|
||||
[AC_HELP_STRING([--enable-iconv], [enable iconv support (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) iconv=true ;;
|
||||
no) iconv=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-iconv) ;;
|
||||
esac],[if test "x$iconv" = "x"; then iconv=true; fi])
|
||||
esac],[if test "x$iconv" = "x"; then iconv=false; fi])
|
||||
|
||||
AC_ARG_ENABLE(debug,
|
||||
[AC_HELP_STRING([--enable-debug], [enable debug information (default: yes)])],
|
||||
@@ -275,8 +275,6 @@ if test "$ENABLEGROUP" != ""; then
|
||||
AC_SUBST([INSTALLGROUP], [$ENABLEGROUP])
|
||||
fi
|
||||
|
||||
ERLANG_DEPRECATED_TYPES_CHECK
|
||||
|
||||
if test "$sqlite" = "true"; then
|
||||
AX_LIB_SQLITE3([3.6.19])
|
||||
if test "x$SQLITE3_VERSION" = "x"; then
|
||||
|
||||
@@ -13,6 +13,7 @@ ExecStart=/bin/sh -c '@ctlscriptpath@/ejabberdctl start && @ctlscriptpath@/ejabb
|
||||
ExecStop=/bin/sh -c '@ctlscriptpath@/ejabberdctl stop && @ctlscriptpath@/ejabberdctl stopped'
|
||||
ExecReload=@ctlscriptpath@/ejabberdctl reload_config
|
||||
PrivateDevices=true
|
||||
TimeoutSec=300
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
+76
-731
@@ -1,15 +1,16 @@
|
||||
###
|
||||
###' ejabberd configuration file
|
||||
### ejabberd configuration file
|
||||
###
|
||||
### The parameters used in this configuration file are explained at
|
||||
###
|
||||
### https://docs.ejabberd.im/admin/configuration
|
||||
###
|
||||
|
||||
### 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.
|
||||
### *******************************************************
|
||||
### ******* !!! WARNING !!! *******
|
||||
### ******* YAML IS INDENTATION SENSITIVE *******
|
||||
### ******* MAKE SURE YOU INDENT SECTIONS CORRECTLY *******
|
||||
### *******************************************************
|
||||
### Refer to http://en.wikipedia.org/wiki/YAML for the brief description.
|
||||
### However, ejabberd treats different literals as different types:
|
||||
###
|
||||
@@ -23,590 +24,77 @@
|
||||
### Example of folded string:
|
||||
### > Art thou not Romeo,
|
||||
### and a Montague?
|
||||
###
|
||||
|
||||
###. =======
|
||||
###' LOGGING
|
||||
|
||||
##
|
||||
## 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
|
||||
|
||||
##
|
||||
## rotation: Describe how to rotate logs. Either size and/or date can trigger
|
||||
## log rotation. Setting count to N keeps N rotated logs. Setting count to 0
|
||||
## does not disable rotation, it instead rotates the file and keeps no previous
|
||||
## versions around. Setting size to X rotate log when it reaches X bytes.
|
||||
## To disable rotation set the size to 0 and the date to ""
|
||||
## Date syntax is taken from the syntax newsyslog uses in newsyslog.conf.
|
||||
## Some examples:
|
||||
## $D0 rotate every night at midnight
|
||||
## $D23 rotate every day at 23:00 hr
|
||||
## $W0D23 rotate every week on Sunday at 23:00 hr
|
||||
## $W5D16 rotate every week on Friday at 16:00 hr
|
||||
## $M1D0 rotate on the first day of every month at midnight
|
||||
## $M5D6 rotate on every 5th day of the month at 6:00 hr
|
||||
##
|
||||
log_rotate_size: 10485760
|
||||
log_rotate_date: ""
|
||||
log_rotate_count: 1
|
||||
|
||||
##
|
||||
## overload protection: If you want to limit the number of messages per second
|
||||
## allowed from error_logger, which is a good idea if you want to avoid a flood
|
||||
## of messages when system is overloaded, you can set a limit.
|
||||
## 100 is ejabberd's default.
|
||||
log_rate_limit: 100
|
||||
|
||||
##
|
||||
## 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"
|
||||
|
||||
###. ===============
|
||||
###' NODE PARAMETERS
|
||||
|
||||
##
|
||||
## net_ticktime: Specifies net_kernel tick time in seconds. This options must have
|
||||
## identical value on all nodes, and in most cases shouldn't be changed at all from
|
||||
## default value.
|
||||
##
|
||||
## net_ticktime: 60
|
||||
|
||||
###. ================
|
||||
###' 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
|
||||
loglevel: 4
|
||||
log_rotate_size: 10485760
|
||||
log_rotate_date: ""
|
||||
log_rotate_count: 1
|
||||
log_rate_limit: 100
|
||||
|
||||
###. ============
|
||||
###' Certificates
|
||||
certfiles:
|
||||
- "/etc/letsencrypt/live/*/*.pem"
|
||||
|
||||
## List all available PEM files containing certificates for your domains,
|
||||
## chains of certificates or certificate keys. Full chains will be built
|
||||
## automatically by ejabberd.
|
||||
##
|
||||
## certfiles:
|
||||
## - "/etc/letsencrypt/live/example.org/*.pem"
|
||||
## - "/etc/letsencrypt/live/example.com/*.pem"
|
||||
##
|
||||
## If your system provides only a single CA file (CentOS/FreeBSD):
|
||||
## ca_file: "/etc/ssl/certs/ca-bundle.pem"
|
||||
|
||||
###. =================
|
||||
###' TLS configuration
|
||||
|
||||
## Note that the following configuration is the default
|
||||
## configuration of the TLS driver, so you don't need to
|
||||
## uncomment it.
|
||||
##
|
||||
## define_macro:
|
||||
## 'TLS_CIPHERS': "HIGH:!aNULL:!eNULL:!3DES:@STRENGTH"
|
||||
## 'TLS_OPTIONS':
|
||||
## - "no_sslv3"
|
||||
## - "cipher_server_preference"
|
||||
## - "no_compression"
|
||||
## 'DH_FILE': "/path/to/dhparams.pem" # generated with: openssl dhparam -out dhparams.pem 2048
|
||||
##
|
||||
## c2s_dhfile: 'DH_FILE'
|
||||
## s2s_dhfile: 'DH_FILE'
|
||||
## c2s_ciphers: 'TLS_CIPHERS'
|
||||
## s2s_ciphers: 'TLS_CIPHERS'
|
||||
## c2s_protocol_options: 'TLS_OPTIONS'
|
||||
## s2s_protocol_options: 'TLS_OPTIONS'
|
||||
|
||||
###. ===============
|
||||
###' 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
|
||||
ip: "::"
|
||||
module: ejabberd_c2s
|
||||
##
|
||||
## If TLS is compiled in and you installed a SSL
|
||||
## certificate, uncomment this line:
|
||||
##
|
||||
## starttls: true
|
||||
##
|
||||
## To enforce TLS encryption for client connections,
|
||||
## use this instead of the "starttls" option:
|
||||
##
|
||||
## starttls_required: true
|
||||
##
|
||||
## Stream compression
|
||||
##
|
||||
## zlib: true
|
||||
##
|
||||
max_stanza_size: 65536
|
||||
max_stanza_size: 262144
|
||||
shaper: c2s_shaper
|
||||
access: c2s
|
||||
##
|
||||
## Direct-TLS for C2S (XEP-0368). A good practice is to forward
|
||||
## traffic from port 443 to this port, possibly multiplexing it
|
||||
## with HTTP using e.g. sslh [https://wiki.xmpp.org/web/Tech_pages/XEP-0368],
|
||||
## so modern clients can bypass restrictive firewalls (in airports, hotels, etc.).
|
||||
##
|
||||
## -
|
||||
## port: 5223
|
||||
## ip: "::"
|
||||
## module: ejabberd_c2s
|
||||
## tls: true
|
||||
## max_stanza_size: 65536
|
||||
## shaper: c2s_shaper
|
||||
## access: c2s
|
||||
starttls_required: true
|
||||
-
|
||||
port: 5269
|
||||
ip: "::"
|
||||
module: ejabberd_s2s_in
|
||||
max_stanza_size: 524288
|
||||
-
|
||||
port: 5280
|
||||
port: 5443
|
||||
ip: "::"
|
||||
module: ejabberd_http
|
||||
request_handlers:
|
||||
"/ws": ejabberd_http_ws
|
||||
"/bosh": mod_bosh
|
||||
"/api": mod_http_api
|
||||
## "/pub/archive": mod_http_fileserver
|
||||
"/bosh": mod_bosh
|
||||
"/upload": mod_http_upload
|
||||
"/ws": ejabberd_http_ws
|
||||
web_admin: true
|
||||
## register: true
|
||||
captcha: true
|
||||
tls: true
|
||||
|
||||
##
|
||||
## ejabberd_service: Interact with external components (transports, ...)
|
||||
##
|
||||
## -
|
||||
## port: 8888
|
||||
## ip: "::"
|
||||
## module: ejabberd_service
|
||||
## access: all
|
||||
## shaper_rule: fast
|
||||
## ip: "127.0.0.1"
|
||||
## privilege_access:
|
||||
## roster: "both"
|
||||
## message: "outgoing"
|
||||
## presence: "roster"
|
||||
## delegations:
|
||||
## "urn:xmpp:mam:1":
|
||||
## filtering: ["node"]
|
||||
## "http://jabber.org/protocol/pubsub":
|
||||
## filtering: []
|
||||
## hosts:
|
||||
## "icq.example.org":
|
||||
## password: "secret"
|
||||
## "sms.example.org":
|
||||
## password: "secret"
|
||||
s2s_use_starttls: optional
|
||||
|
||||
##
|
||||
## ejabberd_stun: Handles STUN Binding requests
|
||||
##
|
||||
## -
|
||||
## port: 3478
|
||||
## transport: udp
|
||||
## module: ejabberd_stun
|
||||
|
||||
##
|
||||
## To handle XML-RPC requests that provide admin credentials:
|
||||
##
|
||||
## -
|
||||
## port: 4560
|
||||
## ip: "::"
|
||||
## module: ejabberd_xmlrpc
|
||||
## maxsessions: 10
|
||||
## timeout: 5000
|
||||
## access_commands:
|
||||
## admin:
|
||||
## commands: all
|
||||
## options: []
|
||||
|
||||
##
|
||||
## To enable secure http upload
|
||||
##
|
||||
## -
|
||||
## port: 5444
|
||||
## ip: "::"
|
||||
## module: ejabberd_http
|
||||
## request_handlers:
|
||||
## "": mod_http_upload
|
||||
## tls: true
|
||||
## protocol_options: 'TLS_OPTIONS'
|
||||
## dhfile: 'DH_FILE'
|
||||
## ciphers: 'TLS_CIPHERS'
|
||||
|
||||
## Disabling digest-md5 SASL authentication. digest-md5 requires plain-text
|
||||
## password storage (see auth_password_format option).
|
||||
## disable_sasl_mechanisms: "digest-md5"
|
||||
|
||||
###. ==================
|
||||
###' S2S GLOBAL OPTIONS
|
||||
|
||||
##
|
||||
## s2s_use_starttls: Enable STARTTLS for S2S connections.
|
||||
## Allowed values are: false, optional or required
|
||||
## You must specify 'certfiles' option
|
||||
##
|
||||
## s2s_use_starttls: optional
|
||||
|
||||
##
|
||||
## S2S whitelist or blacklist
|
||||
##
|
||||
## Default s2s policy for undefined hosts.
|
||||
##
|
||||
## s2s_access: s2s
|
||||
|
||||
##
|
||||
## Outgoing S2S options
|
||||
##
|
||||
## Preferred address families (which to try first) and connect timeout
|
||||
## in seconds.
|
||||
##
|
||||
## outgoing_s2s_families:
|
||||
## - ipv4
|
||||
## - ipv6
|
||||
## outgoing_s2s_timeout: 190
|
||||
|
||||
###. ==============
|
||||
###' 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 SQL
|
||||
## Remember to setup a database in the next section.
|
||||
##
|
||||
## auth_method: sql
|
||||
|
||||
##
|
||||
## 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:
|
||||
##
|
||||
## sql_type: mysql
|
||||
## sql_server: "server"
|
||||
## sql_database: "database"
|
||||
## sql_username: "username"
|
||||
## sql_password: "password"
|
||||
##
|
||||
## If you want to specify the port:
|
||||
## sql_port: 1234
|
||||
|
||||
##
|
||||
## PostgreSQL server:
|
||||
##
|
||||
## sql_type: pgsql
|
||||
## sql_server: "server"
|
||||
## sql_database: "database"
|
||||
## sql_username: "username"
|
||||
## sql_password: "password"
|
||||
##
|
||||
## If you want to specify the port:
|
||||
## sql_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
|
||||
|
||||
##
|
||||
## SQLite:
|
||||
##
|
||||
## sql_type: sqlite
|
||||
## sql_database: "/path/to/database.db"
|
||||
|
||||
##
|
||||
## ODBC compatible or MSSQL server:
|
||||
##
|
||||
## sql_type: odbc
|
||||
## sql_server: "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"
|
||||
|
||||
##
|
||||
## Number of connections to open to the database for each virtual host
|
||||
##
|
||||
## sql_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
|
||||
##
|
||||
## sql_keepalive_interval: undefined
|
||||
|
||||
##
|
||||
## Use the new SQL schema
|
||||
##
|
||||
## new_sql_schema: true
|
||||
|
||||
###. ===============
|
||||
###' 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: 10000
|
||||
|
||||
###. ====================
|
||||
###' ACCESS CONTROL LISTS
|
||||
acl:
|
||||
##
|
||||
## The 'admin' ACL grants administrative privileges to XMPP accounts.
|
||||
## You can put here as many accounts as you want.
|
||||
##
|
||||
## admin:
|
||||
## user:
|
||||
## - "aleksey@localhost"
|
||||
## - "ermine@example.org"
|
||||
##
|
||||
## Blocked users
|
||||
##
|
||||
## blocked:
|
||||
## user:
|
||||
## - "baduser@example.org"
|
||||
## - "test"
|
||||
|
||||
## Local users: don't modify this.
|
||||
##
|
||||
local:
|
||||
user_regexp: ""
|
||||
|
||||
##
|
||||
## More examples of ACLs
|
||||
##
|
||||
## jabberorg:
|
||||
## server:
|
||||
## - "jabber.org"
|
||||
## aleksey:
|
||||
## user:
|
||||
## - "aleksey@jabber.ru"
|
||||
## test:
|
||||
## user_regexp: "^test"
|
||||
## user_glob: "test*"
|
||||
|
||||
##
|
||||
## Loopback network
|
||||
##
|
||||
loopback:
|
||||
ip:
|
||||
- "127.0.0.0/8"
|
||||
- "::1/128"
|
||||
- "::FFFF:127.0.0.1/128"
|
||||
|
||||
##
|
||||
## 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"
|
||||
|
||||
###. ============
|
||||
###' SHAPER RULES
|
||||
|
||||
shaper_rules:
|
||||
## Maximum number of simultaneous sessions allowed for a single user:
|
||||
max_user_sessions: 10
|
||||
## Maximum number of offline messages that users can have:
|
||||
max_user_offline_messages:
|
||||
- 5000: admin
|
||||
- 100
|
||||
## For C2S connections, all users except admins use the "normal" shaper
|
||||
c2s_shaper:
|
||||
- none: admin
|
||||
- normal
|
||||
## All S2S connections use the "fast" shaper
|
||||
s2s_shaper: fast
|
||||
|
||||
###. ============
|
||||
###' ACCESS RULES
|
||||
access_rules:
|
||||
## This rule allows access only for local users:
|
||||
local:
|
||||
- allow: local
|
||||
## Only non-blocked users can use c2s connections:
|
||||
c2s:
|
||||
- deny: blocked
|
||||
- allow
|
||||
## Only admins can send announcement messages:
|
||||
announce:
|
||||
- allow: admin
|
||||
## Only admins can use the configuration interface:
|
||||
configure:
|
||||
- allow: admin
|
||||
## Only accounts of the local ejabberd server can create rooms:
|
||||
muc_create:
|
||||
- allow: local
|
||||
## Only accounts on the local ejabberd server can create Pubsub nodes:
|
||||
pubsub_createnode:
|
||||
- allow: local
|
||||
## In-band registration allows registration of any possible username.
|
||||
## To disable in-band registration, replace 'allow' with 'deny'.
|
||||
register:
|
||||
- allow
|
||||
## Only allow to register from localhost
|
||||
trusted_network:
|
||||
- allow: loopback
|
||||
## Do not establish S2S connections with bad servers
|
||||
## If you enable this you also have to uncomment "s2s_access: s2s"
|
||||
## s2s:
|
||||
## - deny:
|
||||
## - ip: "XXX.XXX.XXX.XXX/32"
|
||||
## - deny:
|
||||
## - ip: "XXX.XXX.XXX.XXX/32"
|
||||
## - allow
|
||||
|
||||
## ===============
|
||||
## API PERMISSIONS
|
||||
## ===============
|
||||
##
|
||||
## This section allows you to define who and using what method
|
||||
## can execute commands offered by ejabberd.
|
||||
##
|
||||
## By default "console commands" section allow executing all commands
|
||||
## issued using ejabberdctl command, and "admin access" section allows
|
||||
## users in admin acl that connect from 127.0.0.1 to execute all
|
||||
## commands except start and stop with any available access method
|
||||
## (ejabberdctl, http-api, xmlrpc depending what is enabled on server).
|
||||
##
|
||||
## If you remove "console commands" there will be one added by
|
||||
## default allowing executing all commands, but if you just change
|
||||
## permissions in it, version from config file will be used instead
|
||||
## of default one.
|
||||
##
|
||||
api_permissions:
|
||||
"console commands":
|
||||
from:
|
||||
@@ -636,154 +124,68 @@ api_permissions:
|
||||
- "status"
|
||||
- "connected_users_number"
|
||||
|
||||
## 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
|
||||
## register:
|
||||
## - deny
|
||||
shaper:
|
||||
normal: 1000
|
||||
fast: 50000
|
||||
|
||||
###. ================
|
||||
###' DEFAULT LANGUAGE
|
||||
shaper_rules:
|
||||
max_user_sessions: 10
|
||||
max_user_offline_messages:
|
||||
- 5000: admin
|
||||
- 100
|
||||
c2s_shaper:
|
||||
- none: admin
|
||||
- normal
|
||||
s2s_shaper: fast
|
||||
|
||||
##
|
||||
## 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
|
||||
|
||||
###. ====
|
||||
###' ACME
|
||||
##
|
||||
## In order to use the acme certificate acquiring through "Let's Encrypt"
|
||||
## an http listener has to be configured to listen to port 80 so that
|
||||
## the authorization challenges posed by "Let's Encrypt" can be solved.
|
||||
##
|
||||
## A simple way of doing this would be to add the following in the listening
|
||||
## section and to configure port forwarding from 80 to 5280 either via NAT
|
||||
## (for ipv4 only) or using frontends such as haproxy/nginx/sslh/etc.
|
||||
## -
|
||||
## port: 5280
|
||||
## ip: "::"
|
||||
## module: ejabberd_http
|
||||
|
||||
acme:
|
||||
|
||||
## A contact mail that the ACME Certificate Authority can contact in case of
|
||||
## an authorization issue, such as a server-initiated certificate revocation.
|
||||
## It is not mandatory to provide an email address but it is highly suggested.
|
||||
contact: "mailto:example-admin@example.com"
|
||||
|
||||
|
||||
## The ACME Certificate Authority URL.
|
||||
## This could either be:
|
||||
## - https://acme-v01.api.letsencrypt.org - (Default) for the production CA
|
||||
## - https://acme-staging.api.letsencrypt.org - for the staging CA
|
||||
## - http://localhost:4000 - for a local version of the CA
|
||||
ca_url: "https://acme-v01.api.letsencrypt.org"
|
||||
|
||||
###. =======
|
||||
###' MODULES
|
||||
|
||||
##
|
||||
## Modules enabled in all ejabberd virtual hosts.
|
||||
##
|
||||
modules:
|
||||
mod_adhoc: {}
|
||||
mod_admin_extra: {}
|
||||
mod_announce: # recommends mod_adhoc
|
||||
mod_announce:
|
||||
access: announce
|
||||
mod_blocking: {} # requires mod_privacy
|
||||
mod_avatar: {}
|
||||
mod_blocking: {}
|
||||
mod_bosh: {}
|
||||
mod_caps: {}
|
||||
mod_carboncopy: {}
|
||||
mod_client_state: {}
|
||||
mod_configure: {} # requires mod_adhoc
|
||||
## mod_delegation: {} # for xep0356
|
||||
mod_configure: {}
|
||||
mod_disco: {}
|
||||
mod_echo: {}
|
||||
mod_irc: {}
|
||||
mod_bosh: {}
|
||||
## mod_http_fileserver:
|
||||
## docroot: "/var/www"
|
||||
## accesslog: "/var/log/ejabberd/access.log"
|
||||
## mod_http_upload:
|
||||
## # docroot: "@HOME@/upload"
|
||||
## put_url: "https://@HOST@:5444"
|
||||
## thumbnail: false # otherwise needs ejabberd to be compiled with libgd support
|
||||
## mod_http_upload_quota:
|
||||
## max_days: 30
|
||||
mod_fail2ban: {}
|
||||
mod_http_api: {}
|
||||
mod_http_upload:
|
||||
put_url: "https://@HOST@:5443/upload"
|
||||
mod_last: {}
|
||||
## XEP-0313: Message Archive Management
|
||||
## You might want to setup a SQL backend for MAM because the mnesia database is
|
||||
## limited to 2GB which might be exceeded on large servers
|
||||
## mod_mam: {} # for xep0313, mnesia is limited to 2GB, better use an SQL backend
|
||||
mod_mam:
|
||||
## Mnesia is limited to 2GB, better to use an SQL backend
|
||||
## For small servers SQLite is a good fit and is very easy
|
||||
## to configure. Uncomment this when you have SQL configured:
|
||||
## db_type: sql
|
||||
assume_mam_usage: true
|
||||
default: always
|
||||
mod_muc:
|
||||
## host: "conference.@HOST@"
|
||||
access:
|
||||
- allow
|
||||
access_admin:
|
||||
- allow: admin
|
||||
access_create: muc_create
|
||||
access_persistent: muc_create
|
||||
default_room_options:
|
||||
mam: true
|
||||
mod_muc_admin: {}
|
||||
## mod_muc_log: {}
|
||||
## mod_multicast: {}
|
||||
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
|
||||
- "pep"
|
||||
force_node_config:
|
||||
## Avoid using OMEMO by default because it
|
||||
## introduces a lot of hard-to-track problems
|
||||
## Comment out the following lines to enable OMEMO support
|
||||
## See https://github.com/processone/ejabberd/issues/2425
|
||||
"eu.siacs.conversations.axolotl.*":
|
||||
access_model: whitelist
|
||||
## Avoid buggy clients to make their bookmarks public
|
||||
@@ -791,82 +193,25 @@ modules:
|
||||
access_model: whitelist
|
||||
mod_push: {}
|
||||
mod_push_keepalive: {}
|
||||
## 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:
|
||||
search: false
|
||||
mod_vcard_xupdate: {}
|
||||
mod_avatar: {}
|
||||
mod_version: {}
|
||||
mod_stream_mgmt: {}
|
||||
## Non-SASL Authentication (XEP-0078) is now disabled by default
|
||||
## because it's obsoleted and is used mostly by abandoned
|
||||
## client software
|
||||
## mod_legacy_auth: {}
|
||||
## The module for S2S dialback (XEP-0220). Please note that you cannot
|
||||
## rely solely on dialback if you want to federate with other servers,
|
||||
## because a lot of servers have dialback disabled and instead rely on
|
||||
## PKIX authentication. Make sure you have proper certificates installed
|
||||
## and check your accessibility at https://check.messaging.one/
|
||||
mod_register:
|
||||
## Only accept registration requests from the "trusted"
|
||||
## network (see access_rules section above).
|
||||
## Think twice before enabling registration from any
|
||||
## address. See the Jabber SPAM Manifesto for details:
|
||||
## https://github.com/ge0rg/jabber-spam-fighting-manifesto
|
||||
ip_access: trusted_network
|
||||
mod_roster:
|
||||
versioning: true
|
||||
mod_s2s_dialback: {}
|
||||
mod_http_api: {}
|
||||
mod_fail2ban: {}
|
||||
mod_shared_roster: {}
|
||||
mod_stream_mgmt:
|
||||
resend_on_timeout: if_offline
|
||||
mod_vcard: {}
|
||||
mod_vcard_xupdate: {}
|
||||
mod_version:
|
||||
show_os: false
|
||||
|
||||
##
|
||||
## Enable modules with custom options in a specific virtual host
|
||||
##
|
||||
## host_config:
|
||||
## "localhost":
|
||||
## modules:
|
||||
## mod_echo:
|
||||
## host: "mirror.localhost"
|
||||
|
||||
##
|
||||
## Enable modules management via ejabberdctl for installation and
|
||||
## uninstallation of public/private contributed modules
|
||||
## (enabled by default)
|
||||
##
|
||||
|
||||
allow_contrib_modules: true
|
||||
|
||||
###.
|
||||
###'
|
||||
### Local Variables:
|
||||
### mode: yaml
|
||||
### End:
|
||||
### vim: set filetype=yaml tabstop=8 foldmarker=###',###. foldmethod=marker:
|
||||
### vim: set filetype=yaml tabstop=8
|
||||
|
||||
@@ -33,10 +33,10 @@
|
||||
# from a client or from another Jabber server. So take this into
|
||||
# account when setting this limit.
|
||||
#
|
||||
# Default: 32000
|
||||
# Default: 65536 (or 8196 on Windows)
|
||||
# Maximum: 268435456
|
||||
#
|
||||
#ERL_MAX_PORTS=32000
|
||||
#ERL_MAX_PORTS=65536
|
||||
|
||||
#.
|
||||
#' FIREWALL_WINDOW: Range of allowed ports to pass through a firewall
|
||||
@@ -85,10 +85,10 @@
|
||||
# Erlang, and therefore not related to the operating system processes, you do
|
||||
# not have to worry about allowing a huge number of them.
|
||||
#
|
||||
# Default: 250000
|
||||
# Default: 262144
|
||||
# Maximum: 268435456
|
||||
#
|
||||
#ERL_PROCESSES=250000
|
||||
#ERL_PROCESSES=262144
|
||||
|
||||
#.
|
||||
#' ERL_MAX_ETS_TABLES: Maximum number of ETS and Mnesia tables
|
||||
@@ -99,9 +99,9 @@
|
||||
# You can safely increase this limit when starting ejabberd. It impacts memory
|
||||
# consumption but the difference will be quite small.
|
||||
#
|
||||
# Default: 1400
|
||||
# Default: 2053
|
||||
#
|
||||
#ERL_MAX_ETS_TABLES=1400
|
||||
#ERL_MAX_ETS_TABLES=2053
|
||||
|
||||
#.
|
||||
#' ERL_OPTIONS: Additional Erlang options
|
||||
|
||||
@@ -70,7 +70,7 @@ done
|
||||
echo '7. compile ejabberd'
|
||||
|
||||
gmake
|
||||
for A in mod_irc mod_muc mod_pubsub; do
|
||||
for A in mod_muc mod_pubsub; do
|
||||
(cd $A; gmake)
|
||||
done
|
||||
|
||||
|
||||
@@ -51,7 +51,6 @@ override_acls.
|
||||
{mod_offline, []},
|
||||
{mod_echo, [{host, "echo.jabber.dbc.mtview.ca.us"}]},
|
||||
{mod_private, []},
|
||||
% {mod_irc, []},
|
||||
{mod_muc, []},
|
||||
{mod_pubsub, []},
|
||||
{mod_time, []},
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-ifndef(EJABBERD_HRL).
|
||||
-define(EJABBERD_HRL, true).
|
||||
|
||||
-define(VERSION, ejabberd_config:get_version()).
|
||||
|
||||
-define(MYHOSTS, ejabberd_config:get_myhosts()).
|
||||
|
||||
-define(MYNAME, hd(ejabberd_config:get_myhosts())).
|
||||
|
||||
-define(MYLANG, ejabberd_config:get_mylang()).
|
||||
|
||||
-define(MSGS_DIR, filename:join(["priv", "msgs"])).
|
||||
|
||||
-define(SQL_DIR, filename:join(["priv", "sql"])).
|
||||
|
||||
-define(CONFIG_PATH, <<"ejabberd.yml">>).
|
||||
|
||||
-define(LOG_PATH, "ejabberd.log").
|
||||
|
||||
-define(EJABBERD_URI, <<"http://www.process-one.net/en/ejabberd/">>).
|
||||
|
||||
-define(COPYRIGHT, "Copyright (c) ProcessOne").
|
||||
|
||||
%%-define(DBGFSM, true).
|
||||
|
||||
-record(scram,
|
||||
{storedkey = <<"">>,
|
||||
serverkey = <<"">>,
|
||||
salt = <<"">>,
|
||||
iterationcount = 0 :: integer()}).
|
||||
|
||||
-type scram() :: #scram{}.
|
||||
|
||||
-define(SCRAM_DEFAULT_ITERATION_COUNT, 4096).
|
||||
|
||||
-ifdef(ERL_DEPRECATED_TYPES).
|
||||
|
||||
-define(TDICT, dict()).
|
||||
-define(TGB_TREE, gb_tree()).
|
||||
-define(TGB_SET, gb_set()).
|
||||
-define(TQUEUE, queue()).
|
||||
|
||||
-else.
|
||||
|
||||
-define(TDICT, dict:dict()).
|
||||
-define(TGB_TREE, gb_trees:tree()).
|
||||
-define(TGB_SET, gb_sets:set()).
|
||||
-define(TQUEUE, queue:queue()).
|
||||
|
||||
-endif.
|
||||
|
||||
-endif.
|
||||
@@ -31,7 +31,10 @@
|
||||
port = 5280 :: inet:port_number(),
|
||||
opts = [] :: list(),
|
||||
tp = http :: protocol(),
|
||||
headers = [] :: [{atom() | binary(), binary()}]}).
|
||||
headers = [] :: [{atom() | binary(), binary()}],
|
||||
length = 0 :: non_neg_integer(),
|
||||
sockmod :: gen_tcp | fast_tls,
|
||||
socket :: inet:socket() | fast_tls:tls_socket()}).
|
||||
|
||||
-record(ws,
|
||||
{socket :: inet:socket() | fast_tls:tls_socket(),
|
||||
|
||||
@@ -1,501 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-include("ns.hrl").
|
||||
-include("fxml.hrl").
|
||||
|
||||
-define(STANZA_ERROR(Code, Type, Condition),
|
||||
#xmlel{name = <<"error">>,
|
||||
attrs = [{<<"code">>, Code}, {<<"type">>, Type}],
|
||||
children =
|
||||
[#xmlel{name = Condition,
|
||||
attrs = [{<<"xmlns">>, ?NS_STANZAS}],
|
||||
children = []}]}).
|
||||
|
||||
-define(ERR_BAD_FORMAT,
|
||||
?STANZA_ERROR(<<"406">>, <<"modify">>,
|
||||
<<"bad-format">>)).
|
||||
|
||||
-define(ERR_BAD_REQUEST,
|
||||
?STANZA_ERROR(<<"400">>, <<"modify">>,
|
||||
<<"bad-request">>)).
|
||||
|
||||
-define(ERR_CONFLICT,
|
||||
?STANZA_ERROR(<<"409">>, <<"cancel">>, <<"conflict">>)).
|
||||
|
||||
-define(ERR_FEATURE_NOT_IMPLEMENTED,
|
||||
?STANZA_ERROR(<<"501">>, <<"cancel">>,
|
||||
<<"feature-not-implemented">>)).
|
||||
|
||||
-define(ERR_FORBIDDEN,
|
||||
?STANZA_ERROR(<<"403">>, <<"auth">>, <<"forbidden">>)).
|
||||
|
||||
-define(ERR_GONE,
|
||||
?STANZA_ERROR(<<"302">>, <<"modify">>, <<"gone">>)).
|
||||
|
||||
-define(ERR_INTERNAL_SERVER_ERROR,
|
||||
?STANZA_ERROR(<<"500">>, <<"wait">>,
|
||||
<<"internal-server-error">>)).
|
||||
|
||||
-define(ERR_ITEM_NOT_FOUND,
|
||||
?STANZA_ERROR(<<"404">>, <<"cancel">>,
|
||||
<<"item-not-found">>)).
|
||||
|
||||
-define(ERR_JID_MALFORMED,
|
||||
?STANZA_ERROR(<<"400">>, <<"modify">>,
|
||||
<<"jid-malformed">>)).
|
||||
|
||||
-define(ERR_NOT_ACCEPTABLE,
|
||||
?STANZA_ERROR(<<"406">>, <<"modify">>,
|
||||
<<"not-acceptable">>)).
|
||||
|
||||
-define(ERR_NOT_ALLOWED,
|
||||
?STANZA_ERROR(<<"405">>, <<"cancel">>,
|
||||
<<"not-allowed">>)).
|
||||
|
||||
-define(ERR_NOT_AUTHORIZED,
|
||||
?STANZA_ERROR(<<"401">>, <<"auth">>,
|
||||
<<"not-authorized">>)).
|
||||
|
||||
-define(ERR_PAYMENT_REQUIRED,
|
||||
?STANZA_ERROR(<<"402">>, <<"auth">>,
|
||||
<<"payment-required">>)).
|
||||
|
||||
-define(ERR_RECIPIENT_UNAVAILABLE,
|
||||
?STANZA_ERROR(<<"404">>, <<"wait">>,
|
||||
<<"recipient-unavailable">>)).
|
||||
|
||||
-define(ERR_REDIRECT,
|
||||
?STANZA_ERROR(<<"302">>, <<"modify">>, <<"redirect">>)).
|
||||
|
||||
-define(ERR_REGISTRATION_REQUIRED,
|
||||
?STANZA_ERROR(<<"407">>, <<"auth">>,
|
||||
<<"registration-required">>)).
|
||||
|
||||
-define(ERR_REMOTE_SERVER_NOT_FOUND,
|
||||
?STANZA_ERROR(<<"404">>, <<"cancel">>,
|
||||
<<"remote-server-not-found">>)).
|
||||
|
||||
-define(ERR_REMOTE_SERVER_TIMEOUT,
|
||||
?STANZA_ERROR(<<"504">>, <<"wait">>,
|
||||
<<"remote-server-timeout">>)).
|
||||
|
||||
-define(ERR_RESOURCE_CONSTRAINT,
|
||||
?STANZA_ERROR(<<"500">>, <<"wait">>,
|
||||
<<"resource-constraint">>)).
|
||||
|
||||
-define(ERR_SERVICE_UNAVAILABLE,
|
||||
?STANZA_ERROR(<<"503">>, <<"cancel">>,
|
||||
<<"service-unavailable">>)).
|
||||
|
||||
-define(ERR_SUBSCRIPTION_REQUIRED,
|
||||
?STANZA_ERROR(<<"407">>, <<"auth">>,
|
||||
<<"subscription-required">>)).
|
||||
|
||||
-define(ERR_UNEXPECTED_REQUEST,
|
||||
?STANZA_ERROR(<<"400">>, <<"wait">>,
|
||||
<<"unexpected-request">>)).
|
||||
|
||||
-define(ERR_UNEXPECTED_REQUEST_CANCEL,
|
||||
?STANZA_ERROR(<<"401">>, <<"cancel">>,
|
||||
<<"unexpected-request">>)).
|
||||
|
||||
%-define(ERR_,
|
||||
% ?STANZA_ERROR("", "", "")).
|
||||
|
||||
-define(STANZA_ERRORT(Code, Type, Condition, Lang,
|
||||
Text),
|
||||
#xmlel{name = <<"error">>,
|
||||
attrs = [{<<"code">>, Code}, {<<"type">>, Type}],
|
||||
children =
|
||||
[#xmlel{name = Condition,
|
||||
attrs = [{<<"xmlns">>, ?NS_STANZAS}], children = []},
|
||||
#xmlel{name = <<"text">>,
|
||||
attrs = [{<<"xmlns">>, ?NS_STANZAS}],
|
||||
children =
|
||||
[{xmlcdata,
|
||||
translate:translate(Lang, Text)}]}]}).
|
||||
|
||||
-define(ERRT_BAD_FORMAT(Lang, Text),
|
||||
?STANZA_ERRORT(<<"406">>, <<"modify">>,
|
||||
<<"bad-format">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_BAD_REQUEST(Lang, Text),
|
||||
?STANZA_ERRORT(<<"400">>, <<"modify">>,
|
||||
<<"bad-request">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_CONFLICT(Lang, Text),
|
||||
?STANZA_ERRORT(<<"409">>, <<"cancel">>, <<"conflict">>,
|
||||
Lang, Text)).
|
||||
|
||||
-define(ERRT_FEATURE_NOT_IMPLEMENTED(Lang, Text),
|
||||
?STANZA_ERRORT(<<"501">>, <<"cancel">>,
|
||||
<<"feature-not-implemented">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_FORBIDDEN(Lang, Text),
|
||||
?STANZA_ERRORT(<<"403">>, <<"auth">>, <<"forbidden">>,
|
||||
Lang, Text)).
|
||||
|
||||
-define(ERRT_GONE(Lang, Text),
|
||||
?STANZA_ERRORT(<<"302">>, <<"modify">>, <<"gone">>,
|
||||
Lang, Text)).
|
||||
|
||||
-define(ERRT_INTERNAL_SERVER_ERROR(Lang, Text),
|
||||
?STANZA_ERRORT(<<"500">>, <<"wait">>,
|
||||
<<"internal-server-error">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_ITEM_NOT_FOUND(Lang, Text),
|
||||
?STANZA_ERRORT(<<"404">>, <<"cancel">>,
|
||||
<<"item-not-found">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_JID_MALFORMED(Lang, Text),
|
||||
?STANZA_ERRORT(<<"400">>, <<"modify">>,
|
||||
<<"jid-malformed">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_NOT_ACCEPTABLE(Lang, Text),
|
||||
?STANZA_ERRORT(<<"406">>, <<"modify">>,
|
||||
<<"not-acceptable">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_NOT_ALLOWED(Lang, Text),
|
||||
?STANZA_ERRORT(<<"405">>, <<"cancel">>,
|
||||
<<"not-allowed">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_NOT_AUTHORIZED(Lang, Text),
|
||||
?STANZA_ERRORT(<<"401">>, <<"auth">>,
|
||||
<<"not-authorized">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_PAYMENT_REQUIRED(Lang, Text),
|
||||
?STANZA_ERRORT(<<"402">>, <<"auth">>,
|
||||
<<"payment-required">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_RECIPIENT_UNAVAILABLE(Lang, Text),
|
||||
?STANZA_ERRORT(<<"404">>, <<"wait">>,
|
||||
<<"recipient-unavailable">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_REDIRECT(Lang, Text),
|
||||
?STANZA_ERRORT(<<"302">>, <<"modify">>, <<"redirect">>,
|
||||
Lang, Text)).
|
||||
|
||||
-define(ERRT_REGISTRATION_REQUIRED(Lang, Text),
|
||||
?STANZA_ERRORT(<<"407">>, <<"auth">>,
|
||||
<<"registration-required">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_REMOTE_SERVER_NOT_FOUND(Lang, Text),
|
||||
?STANZA_ERRORT(<<"404">>, <<"cancel">>,
|
||||
<<"remote-server-not-found">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_REMOTE_SERVER_TIMEOUT(Lang, Text),
|
||||
?STANZA_ERRORT(<<"504">>, <<"wait">>,
|
||||
<<"remote-server-timeout">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_RESOURCE_CONSTRAINT(Lang, Text),
|
||||
?STANZA_ERRORT(<<"500">>, <<"wait">>,
|
||||
<<"resource-constraint">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_SERVICE_UNAVAILABLE(Lang, Text),
|
||||
?STANZA_ERRORT(<<"503">>, <<"cancel">>,
|
||||
<<"service-unavailable">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_SUBSCRIPTION_REQUIRED(Lang, Text),
|
||||
?STANZA_ERRORT(<<"407">>, <<"auth">>,
|
||||
<<"subscription-required">>, Lang, Text)).
|
||||
|
||||
-define(ERRT_UNEXPECTED_REQUEST(Lang, Text),
|
||||
?STANZA_ERRORT(<<"400">>, <<"wait">>,
|
||||
<<"unexpected-request">>, Lang, Text)).
|
||||
|
||||
-define(ERR_AUTH_NO_RESOURCE_PROVIDED(Lang),
|
||||
?ERRT_NOT_ACCEPTABLE(Lang, <<"No resource provided">>)).
|
||||
|
||||
-define(ERR_AUTH_BAD_RESOURCE_FORMAT(Lang),
|
||||
?ERRT_NOT_ACCEPTABLE(Lang,
|
||||
<<"Illegal resource format">>)).
|
||||
|
||||
-define(ERR_AUTH_RESOURCE_CONFLICT(Lang),
|
||||
?ERRT_CONFLICT(Lang, <<"Resource conflict">>)).
|
||||
|
||||
-define(STREAM_ERROR(Condition, Cdata),
|
||||
#xmlel{name = <<"stream:error">>, attrs = [],
|
||||
children =
|
||||
[#xmlel{name = Condition,
|
||||
attrs = [{<<"xmlns">>, ?NS_STREAMS}],
|
||||
children = [{xmlcdata, Cdata}]}]}).
|
||||
|
||||
-define(SERR_BAD_FORMAT,
|
||||
?STREAM_ERROR(<<"bad-format">>, <<"">>)).
|
||||
|
||||
-define(SERR_BAD_NAMESPACE_PREFIX,
|
||||
?STREAM_ERROR(<<"bad-namespace-prefix">>, <<"">>)).
|
||||
|
||||
-define(SERR_CONFLICT,
|
||||
?STREAM_ERROR(<<"conflict">>, <<"">>)).
|
||||
|
||||
-define(SERR_CONNECTION_TIMEOUT,
|
||||
?STREAM_ERROR(<<"connection-timeout">>, <<"">>)).
|
||||
|
||||
-define(SERR_HOST_GONE,
|
||||
?STREAM_ERROR(<<"host-gone">>, <<"">>)).
|
||||
|
||||
-define(SERR_HOST_UNKNOWN,
|
||||
?STREAM_ERROR(<<"host-unknown">>, <<"">>)).
|
||||
|
||||
-define(SERR_IMPROPER_ADDRESSING,
|
||||
?STREAM_ERROR(<<"improper-addressing">>, <<"">>)).
|
||||
|
||||
-define(SERR_INTERNAL_SERVER_ERROR,
|
||||
?STREAM_ERROR(<<"internal-server-error">>, <<"">>)).
|
||||
|
||||
-define(SERR_INVALID_FROM,
|
||||
?STREAM_ERROR(<<"invalid-from">>, <<"">>)).
|
||||
|
||||
-define(SERR_INVALID_ID,
|
||||
?STREAM_ERROR(<<"invalid-id">>, <<"">>)).
|
||||
|
||||
-define(SERR_INVALID_NAMESPACE,
|
||||
?STREAM_ERROR(<<"invalid-namespace">>, <<"">>)).
|
||||
|
||||
-define(SERR_INVALID_XML,
|
||||
?STREAM_ERROR(<<"invalid-xml">>, <<"">>)).
|
||||
|
||||
-define(SERR_NOT_AUTHORIZED,
|
||||
?STREAM_ERROR(<<"not-authorized">>, <<"">>)).
|
||||
|
||||
-define(SERR_POLICY_VIOLATION,
|
||||
?STREAM_ERROR(<<"policy-violation">>, <<"">>)).
|
||||
|
||||
-define(SERR_REMOTE_CONNECTION_FAILED,
|
||||
?STREAM_ERROR(<<"remote-connection-failed">>, <<"">>)).
|
||||
|
||||
-define(SERR_RESOURSE_CONSTRAINT,
|
||||
?STREAM_ERROR(<<"resource-constraint">>, <<"">>)).
|
||||
|
||||
-define(SERR_RESTRICTED_XML,
|
||||
?STREAM_ERROR(<<"restricted-xml">>, <<"">>)).
|
||||
|
||||
-define(SERR_SEE_OTHER_HOST(Host),
|
||||
?STREAM_ERROR(<<"see-other-host">>, Host)).
|
||||
|
||||
-define(SERR_SYSTEM_SHUTDOWN,
|
||||
?STREAM_ERROR(<<"system-shutdown">>, <<"">>)).
|
||||
|
||||
-define(SERR_UNSUPPORTED_ENCODING,
|
||||
?STREAM_ERROR(<<"unsupported-encoding">>, <<"">>)).
|
||||
|
||||
-define(SERR_UNSUPPORTED_STANZA_TYPE,
|
||||
?STREAM_ERROR(<<"unsupported-stanza-type">>, <<"">>)).
|
||||
|
||||
-define(SERR_UNSUPPORTED_VERSION,
|
||||
?STREAM_ERROR(<<"unsupported-version">>, <<"">>)).
|
||||
|
||||
-define(SERR_XML_NOT_WELL_FORMED,
|
||||
?STREAM_ERROR(<<"xml-not-well-formed">>, <<"">>)).
|
||||
|
||||
%-define(SERR_,
|
||||
% ?STREAM_ERROR("", "")).
|
||||
|
||||
-define(STREAM_ERRORT(Condition, Cdata, Lang, Text),
|
||||
#xmlel{name = <<"stream:error">>, attrs = [],
|
||||
children =
|
||||
[#xmlel{name = Condition,
|
||||
attrs = [{<<"xmlns">>, ?NS_STREAMS}],
|
||||
children = [{xmlcdata, Cdata}]},
|
||||
#xmlel{name = <<"text">>,
|
||||
attrs =
|
||||
[{<<"xml:lang">>, Lang},
|
||||
{<<"xmlns">>, ?NS_STREAMS}],
|
||||
children =
|
||||
[{xmlcdata,
|
||||
translate:translate(Lang, Text)}]}]}).
|
||||
|
||||
-define(SERRT_BAD_FORMAT(Lang, Text),
|
||||
?STREAM_ERRORT(<<"bad-format">>, <<"">>, Lang, Text)).
|
||||
|
||||
-define(SERRT_BAD_NAMESPACE_PREFIX(Lang, Text),
|
||||
?STREAM_ERRORT(<<"bad-namespace-prefix">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_CONFLICT(Lang, Text),
|
||||
?STREAM_ERRORT(<<"conflict">>, <<"">>, Lang, Text)).
|
||||
|
||||
-define(SERRT_CONNECTION_TIMEOUT(Lang, Text),
|
||||
?STREAM_ERRORT(<<"connection-timeout">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_HOST_GONE(Lang, Text),
|
||||
?STREAM_ERRORT(<<"host-gone">>, <<"">>, Lang, Text)).
|
||||
|
||||
-define(SERRT_HOST_UNKNOWN(Lang, Text),
|
||||
?STREAM_ERRORT(<<"host-unknown">>, <<"">>, Lang, Text)).
|
||||
|
||||
-define(SERRT_IMPROPER_ADDRESSING(Lang, Text),
|
||||
?STREAM_ERRORT(<<"improper-addressing">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_INTERNAL_SERVER_ERROR(Lang, Text),
|
||||
?STREAM_ERRORT(<<"internal-server-error">>, <<"">>,
|
||||
Lang, Text)).
|
||||
|
||||
-define(SERRT_INVALID_FROM(Lang, Text),
|
||||
?STREAM_ERRORT(<<"invalid-from">>, <<"">>, Lang, Text)).
|
||||
|
||||
-define(SERRT_INVALID_ID(Lang, Text),
|
||||
?STREAM_ERRORT(<<"invalid-id">>, <<"">>, Lang, Text)).
|
||||
|
||||
-define(SERRT_INVALID_NAMESPACE(Lang, Text),
|
||||
?STREAM_ERRORT(<<"invalid-namespace">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_INVALID_XML(Lang, Text),
|
||||
?STREAM_ERRORT(<<"invalid-xml">>, <<"">>, Lang, Text)).
|
||||
|
||||
-define(SERRT_NOT_AUTHORIZED(Lang, Text),
|
||||
?STREAM_ERRORT(<<"not-authorized">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_POLICY_VIOLATION(Lang, Text),
|
||||
?STREAM_ERRORT(<<"policy-violation">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_REMOTE_CONNECTION_FAILED(Lang, Text),
|
||||
?STREAM_ERRORT(<<"remote-connection-failed">>, <<"">>,
|
||||
Lang, Text)).
|
||||
|
||||
-define(SERRT_RESOURSE_CONSTRAINT(Lang, Text),
|
||||
?STREAM_ERRORT(<<"resource-constraint">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_RESTRICTED_XML(Lang, Text),
|
||||
?STREAM_ERRORT(<<"restricted-xml">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_SEE_OTHER_HOST(Host, Lang, Text),
|
||||
?STREAM_ERRORT(<<"see-other-host">>, Host, Lang, Text)).
|
||||
|
||||
-define(SERRT_SYSTEM_SHUTDOWN(Lang, Text),
|
||||
?STREAM_ERRORT(<<"system-shutdown">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_UNSUPPORTED_ENCODING(Lang, Text),
|
||||
?STREAM_ERRORT(<<"unsupported-encoding">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_UNSUPPORTED_STANZA_TYPE(Lang, Text),
|
||||
?STREAM_ERRORT(<<"unsupported-stanza-type">>, <<"">>,
|
||||
Lang, Text)).
|
||||
|
||||
-define(SERRT_UNSUPPORTED_VERSION(Lang, Text),
|
||||
?STREAM_ERRORT(<<"unsupported-version">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-define(SERRT_XML_NOT_WELL_FORMED(Lang, Text),
|
||||
?STREAM_ERRORT(<<"xml-not-well-formed">>, <<"">>, Lang,
|
||||
Text)).
|
||||
|
||||
-record(jid, {user = <<"">> :: binary(),
|
||||
server = <<"">> :: binary(),
|
||||
resource = <<"">> :: binary(),
|
||||
luser = <<"">> :: binary(),
|
||||
lserver = <<"">> :: binary(),
|
||||
lresource = <<"">> :: binary()}).
|
||||
|
||||
-type(jid() :: #jid{}).
|
||||
|
||||
-type(ljid() :: {binary(), binary(), binary()}).
|
||||
|
||||
-record(iq, {id = <<"">> :: binary(),
|
||||
type = get :: get | set | result | error,
|
||||
xmlns = <<"">> :: binary(),
|
||||
lang = <<"">> :: binary(),
|
||||
sub_el = #xmlel{} :: xmlel() | [xmlel()]}).
|
||||
|
||||
-type(iq_get()
|
||||
:: #iq{
|
||||
id :: binary(),
|
||||
type :: get,
|
||||
xmlns :: binary(),
|
||||
lang :: binary(),
|
||||
sub_el :: xmlel()
|
||||
}
|
||||
).
|
||||
|
||||
-type(iq_set()
|
||||
:: #iq{
|
||||
id :: binary(),
|
||||
type :: set,
|
||||
xmlns :: binary(),
|
||||
lang :: binary(),
|
||||
sub_el :: xmlel()
|
||||
}
|
||||
).
|
||||
|
||||
-type iq_request() :: iq_get() | iq_set().
|
||||
|
||||
-type(iq_result()
|
||||
:: #iq{
|
||||
id :: binary(),
|
||||
type :: result,
|
||||
xmlns :: binary(),
|
||||
lang :: binary(),
|
||||
sub_el :: [xmlel()]
|
||||
}
|
||||
).
|
||||
|
||||
-type(iq_error()
|
||||
:: #iq{
|
||||
id :: binary(),
|
||||
type :: error,
|
||||
xmlns :: binary(),
|
||||
lang :: binary(),
|
||||
sub_el :: [xmlel()]
|
||||
}
|
||||
).
|
||||
|
||||
-type iq_reply() :: iq_result() | iq_error() .
|
||||
|
||||
-type(iq() :: iq_request() | iq_reply()).
|
||||
|
||||
-record(rsm_in, {max :: integer() | error | undefined,
|
||||
direction :: before | aft | undefined,
|
||||
id :: binary() | undefined,
|
||||
index :: integer() | error | undefined}).
|
||||
|
||||
-record(rsm_out, {count :: integer() | undefined,
|
||||
index :: integer() | undefined,
|
||||
first :: binary() | undefined,
|
||||
last :: binary() | undefined}).
|
||||
|
||||
-type(rsm_in() :: #rsm_in{}).
|
||||
|
||||
-type(rsm_out() :: #rsm_out{}).
|
||||
|
||||
-type broadcast() :: {broadcast, broadcast_data()}.
|
||||
|
||||
-type broadcast_data() ::
|
||||
{rebind, pid(), binary()} | %% ejabberd_c2s
|
||||
{item, ljid(), mod_roster:subscription()} | %% mod_roster/mod_shared_roster
|
||||
{exit, binary()} | %% mod_roster/mod_shared_roster
|
||||
{privacy_list, mod_privacy:userlist(), binary()} | %% mod_privacy
|
||||
{blocking, unblock_all | {block | unblock, [ljid()]}}. %% mod_blocking
|
||||
|
||||
-record(xmlelement, {name = "" :: string(),
|
||||
attrs = [] :: [{string(), string()}],
|
||||
children = [] :: [{xmlcdata, iodata()} | xmlelement()]}).
|
||||
|
||||
-type xmlelement() :: #xmlelement{}.
|
||||
@@ -42,3 +42,6 @@
|
||||
false -> ok;
|
||||
_ -> 'Elixir.Logger':bare_log(error, io_lib:format(Format, Args), [?MODULE])
|
||||
end).
|
||||
|
||||
%% Uncomment if you want to debug p1_fsm/gen_fsm
|
||||
%%-define(DBGFSM, true).
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-type conn_param() :: {binary(), binary(), inet:port_number(), binary()} |
|
||||
{binary(), binary(), inet:port_number()} |
|
||||
{binary(), binary()} |
|
||||
{binary()}.
|
||||
|
||||
-type irc_data() :: [{username, binary()} | {connections_params, [conn_param()]}].
|
||||
|
||||
-record(irc_connection,
|
||||
{jid_server_host = {#jid{}, <<"">>, <<"">>} :: {jid(), binary(), binary()},
|
||||
pid = self() :: pid()}).
|
||||
|
||||
-record(irc_custom,
|
||||
{us_host = {{<<"">>, <<"">>}, <<"">>} :: {{binary(), binary()},
|
||||
binary()},
|
||||
data = [] :: irc_data()}).
|
||||
@@ -18,8 +18,6 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-define(MAX_USERS_DEFAULT, 200).
|
||||
|
||||
-define(SETS, gb_sets).
|
||||
@@ -64,9 +62,10 @@
|
||||
logging = false :: boolean(),
|
||||
vcard = <<"">> :: binary(),
|
||||
vcard_xupdate = undefined :: undefined | external | binary(),
|
||||
captcha_whitelist = (?SETS):empty() :: ?TGB_SET,
|
||||
captcha_whitelist = (?SETS):empty() :: gb_sets:set(),
|
||||
mam = false :: boolean(),
|
||||
pubsub = <<"">> :: binary()
|
||||
pubsub = <<"">> :: binary(),
|
||||
lang = ejabberd_config:get_mylang() :: binary()
|
||||
}).
|
||||
|
||||
-type config() :: #config{}.
|
||||
@@ -106,13 +105,13 @@
|
||||
access = {none,none,none,none} :: {atom(), atom(), atom(), atom()},
|
||||
jid = #jid{} :: jid(),
|
||||
config = #config{} :: config(),
|
||||
users = (?DICT):new() :: ?TDICT,
|
||||
subscribers = (?DICT):new() :: ?TDICT,
|
||||
subscriber_nicks = (?DICT):new() :: ?TDICT,
|
||||
users = (?DICT):new() :: dict:dict(),
|
||||
subscribers = (?DICT):new() :: dict:dict(),
|
||||
subscriber_nicks = (?DICT):new() :: dict:dict(),
|
||||
last_voice_request_time = treap:empty() :: treap:treap(),
|
||||
robots = (?DICT):new() :: ?TDICT,
|
||||
nicks = (?DICT):new() :: ?TDICT,
|
||||
affiliations = (?DICT):new() :: ?TDICT,
|
||||
robots = (?DICT):new() :: dict:dict(),
|
||||
nicks = (?DICT):new() :: dict:dict(),
|
||||
affiliations = (?DICT):new() :: dict:dict(),
|
||||
history :: lqueue(),
|
||||
subject = [] :: [text()],
|
||||
subject_author = <<"">> :: binary(),
|
||||
|
||||
+5
-7
@@ -18,8 +18,6 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
%% -------------------------------
|
||||
%% Pubsub constants
|
||||
-define(ERR_EXTENDED(E, C), mod_pubsub:extended_error(E, C)).
|
||||
@@ -131,13 +129,13 @@
|
||||
id ,% :: mod_pubsub:nodeIdx(),
|
||||
parents = [] ,% :: [mod_pubsub:nodeId(),...],
|
||||
type = <<"flat">>,% :: binary(),
|
||||
owners = [] ,% :: [jlib:ljid(),...],
|
||||
owners = [] ,% :: [jid:ljid(),...],
|
||||
options = [] % :: mod_pubsub:nodeOptions()
|
||||
}).
|
||||
|
||||
-record(pubsub_state,
|
||||
{
|
||||
stateid ,% :: {jlib:ljid(), mod_pubsub:nodeIdx()},
|
||||
stateid ,% :: {jid:ljid(), mod_pubsub:nodeIdx()},
|
||||
nodeidx ,% :: mod_pubsub:nodeIdx(),
|
||||
items = [] ,% :: [mod_pubsub:itemId(),...],
|
||||
affiliation = 'none',% :: mod_pubsub:affiliation(),
|
||||
@@ -148,8 +146,8 @@
|
||||
{
|
||||
itemid ,% :: {mod_pubsub:itemId(), mod_pubsub:nodeIdx()},
|
||||
nodeidx ,% :: mod_pubsub:nodeIdx(),
|
||||
creation = {unknown, unknown},% :: {erlang:timestamp(), jlib:ljid()},
|
||||
modification = {unknown, unknown},% :: {erlang:timestamp(), jlib:ljid()},
|
||||
creation = {unknown, unknown},% :: {erlang:timestamp(), jid:ljid()},
|
||||
modification = {unknown, unknown},% :: {erlang:timestamp(), jid:ljid()},
|
||||
payload = [] % :: mod_pubsub:payload()
|
||||
}).
|
||||
|
||||
@@ -163,7 +161,7 @@
|
||||
{
|
||||
nodeid ,% :: {binary(), mod_pubsub:nodeIdx()},
|
||||
itemid ,% :: mod_pubsub:itemId(),
|
||||
creation ,% :: {erlang:timestamp(), jlib:ljid()},
|
||||
creation ,% :: {erlang:timestamp(), jid:ljid()},
|
||||
payload % :: mod_pubsub:payload()
|
||||
}).
|
||||
|
||||
|
||||
@@ -84,20 +84,3 @@ EOF
|
||||
AC_MSG_RESULT([ok])
|
||||
fi
|
||||
]) dnl ERLANG_VERSION_CHECK
|
||||
|
||||
AC_DEFUN([ERLANG_DEPRECATED_TYPES_CHECK],
|
||||
[ AC_MSG_CHECKING([whether Erlang is using deprecated types])
|
||||
cat > conftest.erl <<EOF
|
||||
-module(conftest).
|
||||
|
||||
-record(state, {host = dict:new() :: dict:dict()}).
|
||||
EOF
|
||||
|
||||
if $ERLC conftest.erl > /dev/null 2>&1; then
|
||||
AC_MSG_RESULT([no])
|
||||
AC_SUBST(erlang_deprecated_types, false)
|
||||
else
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_SUBST(erlang_deprecated_types, true)
|
||||
fi
|
||||
])
|
||||
|
||||
@@ -3,7 +3,7 @@ defmodule Ejabberd.Mixfile do
|
||||
|
||||
def project do
|
||||
[app: :ejabberd,
|
||||
version: "18.4.0",
|
||||
version: "18.6.0",
|
||||
description: description(),
|
||||
elixir: "~> 1.4",
|
||||
elixirc_paths: ["lib"],
|
||||
@@ -61,7 +61,7 @@ defmodule Ejabberd.Mixfile do
|
||||
[{:lager, "~> 3.4.0"},
|
||||
{:p1_utils, "~> 1.0"},
|
||||
{:fast_xml, "~> 1.1"},
|
||||
{:xmpp, "~> 1.1"},
|
||||
{:xmpp, "~> 1.2"},
|
||||
{:cache_tab, "~> 1.0"},
|
||||
{:stringprep, "~> 1.0"},
|
||||
{:fast_yaml, "~> 1.0"},
|
||||
@@ -125,7 +125,7 @@ defmodule Ejabberd.Mixfile do
|
||||
defp vars do
|
||||
case :file.consult("vars.config") do
|
||||
{:ok,config} -> config
|
||||
_ -> [zlib: true, iconv: true]
|
||||
_ -> [zlib: true, iconv: false]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
%{
|
||||
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
|
||||
"cache_tab": {:hex, :cache_tab, "1.0.13", "e09857af9b7ba89428227d3801256852cb0d51a4de47e4edcb160d96cc96f8eb", [:rebar3], [{:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"distillery": {:hex, :distillery, "1.5.2", "eec18b2d37b55b0bcb670cf2bcf64228ed38ce8b046bb30a9b636a6f5a4c0080", [:mix], [], "hexpm"},
|
||||
"earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm"},
|
||||
"eimp": {:hex, :eimp, "1.0.3", "e40108d622d672cf6003d279d98fc46a98df182dbe8756857896ffd28883090d", [:rebar3], [{:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"cache_tab": {:hex, :cache_tab, "1.0.16", "0223820e5071d3252b9921db9dcc74a09ec8a660120271fdb47c3c40b6b52bee", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"distillery": {:hex, :distillery, "1.5.3", "b2f4fc34ec71ab4f1202a796f9290e068883b042319aa8c9aa45377ecac8597a", [:mix], [], "hexpm"},
|
||||
"earmark": {:hex, :earmark, "1.2.5", "4d21980d5d2862a2e13ec3c49ad9ad783ffc7ca5769cf6ff891a4553fbaae761", [:mix], [], "hexpm"},
|
||||
"eimp": {:hex, :eimp, "1.0.9", "daf0d2904be3ef5e4128d946e158113cdb4d52555023746d29b83ce86b531f3c", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"epam": {:hex, :epam, "1.0.4", "2a5e40cbf9b2cf41df515782894c3b33c81b8ad32fff2fc847c3f725071dfaed", [:rebar3], [], "hexpm"},
|
||||
"eredis": {:hex, :eredis, "1.1.0", "8d8d74496f35216679b97726b75fb1c8715e99dd7f3ef9f9824a2264c3e0aac0", [:rebar3], [], "hexpm"},
|
||||
"esip": {:hex, :esip, "1.0.22", "3e387312614762fb84d3f77ba4f17650faf52510482521300b3d98ecdcbec21d", [:rebar3], [{:fast_tls, "1.0.21", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.21", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"esip": {:hex, :esip, "1.0.26", "b50c92f8ac3e8e8ba901f0a6cc7e0e47fdc832b0f3044eddb6032ca26845cf97", [:rebar3], [{:fast_tls, "1.0.25", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.25", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.18.3", "f4b0e4a2ec6f333dccf761838a4b253d75e11f714b85ae271c9ae361367897b7", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ezlib": {:hex, :ezlib, "1.0.4", "2434e4bb549cb060d5ac02261ba48fbe1a69b2ae4e1bf7485a3b27b3f3ec618d", [:rebar3], [], "hexpm"},
|
||||
"fast_tls": {:hex, :fast_tls, "1.0.21", "7005fe030c0472643314c9c31e7627bb296dcb96a9ce0b5dd8ccb34273f4c1ff", [:rebar3], [{:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"fast_xml": {:hex, :fast_xml, "1.1.29", "c6356d28f0f76ffefc68b5eb65916f0b8ca513bab71db8ad95bd8577c47e30e2", [:rebar3], [{:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"fast_yaml": {:hex, :fast_yaml, "1.0.13", "adcb8db20bb96d4e56b63b48c75d47ca15a6bd409da0200ffbd32db382131e22", [:rebar3], [{:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"fast_tls": {:hex, :fast_tls, "1.0.25", "cbf875fe709d6fd03d3266c920bfe15f4d22736535d73421300cebf9d86bd851", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"fast_xml": {:hex, :fast_xml, "1.1.34", "d76fc639d3607a44c4f0fb2dfdee1067b6c37b02b39706d8f067cb77eb2f6016", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"fast_yaml": {:hex, :fast_yaml, "1.0.17", "e945ef64e0cb7c311c7b42804dbe32a24e13a2afc0ffe249b7e0f9f9ac08e176", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm"},
|
||||
"hamcrest": {:hex, :basho_hamcrest, "0.4.1", "fb7b2c92d252a1e9db936750b86089addaebeb8f87967fb4bbdda61e8863338e", [:make, :mix, :rebar3], [], "hexpm"},
|
||||
"iconv": {:hex, :iconv, "1.0.7", "f81eb6b8c977b1fd078515937fdce64292d64c6102353fbbfe57db580f4689d1", [:rebar3], [{:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"iconv": {:hex, :iconv, "1.0.10", "fc7de75c0a1fbc1e4ed0c68637ae7464a5dd9eee81710fff26321922d144ecea", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"jiffy": {:hex, :jiffy, "0.14.13", "225a9a35e26417832c611526567194b4d3adc4f0dfa5f2f7008f4684076f2a01", [:rebar3], [], "hexpm"},
|
||||
"jose": {:hex, :jose, "1.8.4", "7946d1e5c03a76ac9ef42a6e6a20001d35987afd68c2107bcd8f01a84e75aa73", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"lager": {:hex, :lager, "3.4.2", "150b9a17b23ae6d3265cc10dc360747621cf217b7a22b8cddf03b2909dbf7aa5", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"luerl": {:hex, :luerl, "0.3.1", "5412807630aac1aaf59ffe5a1bc09259c447b4faeb1d3fe2d4ef41b87676cb04", [:rebar3], [], "hexpm"},
|
||||
"meck": {:hex, :meck, "0.8.9", "64c5c0bd8bcca3a180b44196265c8ed7594e16bcc845d0698ec6b4e577f48188", [:rebar3], [], "hexpm"},
|
||||
"meck": {:hex, :meck, "0.8.10", "455aaef8403be46752272206613e7a15467c014d40994b22fb54cde4d1ff7075", [:rebar3], [], "hexpm"},
|
||||
"moka": {:git, "https://github.com/processone/moka.git", "3eed3a6dd7dedb70a6cd18f86c7561a18626eb3b", [tag: "1.0.5c"]},
|
||||
"p1_mysql": {:hex, :p1_mysql, "1.0.5", "2a9644d27050a6aa9e7eb70a0620043f93655212b15f3620dc12f2fbd1a8c43a", [:rebar3], [], "hexpm"},
|
||||
"p1_oauth2": {:hex, :p1_oauth2, "0.6.2", "cc381038920e3d34ef32aa10ba7eb637bdff38a946748c4fd99329ff484a3889", [:rebar3], [], "hexpm"},
|
||||
"p1_pgsql": {:hex, :p1_pgsql, "1.1.5", "1e1bef6e6d906e10552a608b9fe5ef39b3099caf0f44c07d3d9e18ac4dee34d1", [:rebar3], [], "hexpm"},
|
||||
"p1_utils": {:hex, :p1_utils, "1.0.11", "a471f80644d4b464fa67572affddda7e95df5d4b099624b8907f5726e8a1769c", [:rebar3], [], "hexpm"},
|
||||
"p1_mysql": {:hex, :p1_mysql, "1.0.7", "9fbadf8fa283ae8657faa4f6bbe13f2e3b9da0cdcfbc699cfc120d0905282056", [:rebar3], [], "hexpm"},
|
||||
"p1_oauth2": {:hex, :p1_oauth2, "0.6.3", "fbd91ba86bd7f03d2a4f6e62affa86bab9930abfd6b473d61eefb148f246cd46", [:rebar3], [], "hexpm"},
|
||||
"p1_pgsql": {:hex, :p1_pgsql, "1.1.6", "631004602b06ca6f55d759001f180185685c7097e583f3b0f7dd9b8e05ba5db1", [:rebar3], [], "hexpm"},
|
||||
"p1_utils": {:hex, :p1_utils, "1.0.13", "176577cafb54a8c2fdc0a7fc62b9a21ab0f66588f4062792cd9e65f3e500bfdb", [:rebar3], [], "hexpm"},
|
||||
"riak_pb": {:hex, :riak_pb, "2.3.2", "48ffbf66dbb3f136ab9a7134bac4e496754baa5ef58c4f50a61326736d996390", [:make, :mix, :rebar3], [{:hamcrest, "~> 0.4.1", [hex: :basho_hamcrest, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"riakc": {:hex, :riakc, "2.5.3", "6132d9e687a0dfd314b2b24c4594302ca8b55568a5d733c491d8fb6cd4004763", [:make, :mix, :rebar3], [{:riak_pb, "~> 2.3", [hex: :riak_pb, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"samerlib": {:git, "https://github.com/processone/samerlib", "fbbba035b1548ac4e681df00d61bf609645333a0", [tag: "0.8.0c"]},
|
||||
"sqlite3": {:hex, :sqlite3, "1.1.6", "4ea71af0b45908b5f02c9b09e4c87177039ef404f20accb35049cd8924cc417c", [:rebar3], [], "hexpm"},
|
||||
"stringprep": {:hex, :stringprep, "1.0.11", "002e6972ab36c35f3dd88c11725014e62608c45a00399c083681879973fa8026", [:rebar3], [{:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"stun": {:hex, :stun, "1.0.21", "087fb20497081927690ef9d70b5bd6f9f4bea256ad758c500842722c0b6bb6ab", [:rebar3], [{:fast_tls, "1.0.21", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"xmpp": {:hex, :xmpp, "1.1.20", "33ddcc698518061f5051b98a6f731eef9342799f0c276a9debdfffe85c32fe6d", [:rebar3], [{:fast_xml, "1.1.29", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.11", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.11", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"stringprep": {:hex, :stringprep, "1.0.14", "230a2d1c576bba168749d653fd6681166d02431ef07161a918444f3edb478ad0", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"stun": {:hex, :stun, "1.0.25", "e324c94c28d636578db79eb26979cd07140f0dabcdc0d5b197650ba0bc44a31a", [:rebar3], [{:fast_tls, "1.0.25", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"xmpp": {:hex, :xmpp, "1.2.5", "98ae86706013e51349e962b67c30293d14672603b5c7d782b2c79b52ceaa659f", [:rebar3], [{:ezlib, "1.0.4", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.0.25", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.34", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.14", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
redis.replicate_commands()
|
||||
local cursor = redis.call('GET', KEYS[3]) or 0
|
||||
local scan_result = redis.call('HSCAN', KEYS[1], cursor, 'COUNT', ARGV[1])
|
||||
local newcursor = scan_result[1]
|
||||
local cursor = redis.call('SET', KEYS[3], newcursor)
|
||||
redis.call('EXPIRE', KEYS[3], 30)
|
||||
for key,value in ipairs(scan_result[2]) do
|
||||
local uskey, sidkey = string.match(value, '(.*)||(.*)')
|
||||
if uskey and sidkey then
|
||||
redis.call('HDEL', uskey, sidkey)
|
||||
redis.call('HDEL', KEYS[1], value)
|
||||
else
|
||||
redis.call('HDEL', KEYS[2], value)
|
||||
end
|
||||
end
|
||||
return newcursor
|
||||
+1707
-1251
File diff suppressed because it is too large
Load Diff
+123
-121
@@ -152,7 +152,7 @@ msgstr "Agosto"
|
||||
|
||||
#: mod_pubsub:1842
|
||||
msgid "Automatic node creation is not enabled"
|
||||
msgstr ""
|
||||
msgstr "Criação automatizada de nós está desabilitada"
|
||||
|
||||
#: ejabberd_web_admin:1867 mod_configure:148 mod_configure:617
|
||||
msgid "Backup"
|
||||
@@ -185,11 +185,11 @@ msgstr "Aniversário"
|
||||
|
||||
#: mod_legacy_auth:102
|
||||
msgid "Both the username and the resource are required"
|
||||
msgstr ""
|
||||
msgstr "Nome de usuário e recurso são necessários"
|
||||
|
||||
#: mod_proxy65_service:226
|
||||
msgid "Bytestream already activated"
|
||||
msgstr ""
|
||||
msgstr "Bytestream já foi ativado"
|
||||
|
||||
#: ejabberd_captcha:135
|
||||
msgid "CAPTCHA web page"
|
||||
@@ -201,11 +201,11 @@ msgstr "Tempo de CPU"
|
||||
|
||||
#: mod_privacy:334
|
||||
msgid "Cannot remove active list"
|
||||
msgstr ""
|
||||
msgstr "Não é possível remover uma lista ativa"
|
||||
|
||||
#: mod_privacy:341
|
||||
msgid "Cannot remove default list"
|
||||
msgstr ""
|
||||
msgstr "Não é possível remover uma lista padrão"
|
||||
|
||||
#: ejabberd_web_admin:1679 mod_register_web:194 mod_register_web:353
|
||||
#: mod_register_web:361 mod_register_web:386
|
||||
@@ -383,7 +383,7 @@ msgstr "Exportar para arquivo texto"
|
||||
|
||||
#: mod_roster:180
|
||||
msgid "Duplicated groups are not allowed by RFC6121"
|
||||
msgstr ""
|
||||
msgstr "Grupos duplicados não são permitidos pela RFC6121"
|
||||
|
||||
#: mod_configure:1776
|
||||
msgid "Edit Properties"
|
||||
@@ -413,7 +413,7 @@ msgstr "Permitir criação de logs"
|
||||
|
||||
#: mod_push:252
|
||||
msgid "Enabling push without 'node' attribute is not supported"
|
||||
msgstr ""
|
||||
msgstr "Abilitar push sem o atributo 'node' não é suportado"
|
||||
|
||||
#: mod_irc:834
|
||||
msgid "Encoding for server ~b"
|
||||
@@ -503,15 +503,15 @@ msgstr ""
|
||||
|
||||
#: mod_delegation:275
|
||||
msgid "External component failure"
|
||||
msgstr ""
|
||||
msgstr "Falha de componente externo"
|
||||
|
||||
#: mod_delegation:283
|
||||
msgid "External component timeout"
|
||||
msgstr ""
|
||||
msgstr "Tempo esgotado à espera de componente externo"
|
||||
|
||||
#: mod_proxy65_service:218
|
||||
msgid "Failed to activate bytestream"
|
||||
msgstr ""
|
||||
msgstr "Falha ao ativar bytestream"
|
||||
|
||||
#: mod_muc_room:910
|
||||
msgid "Failed to extract JID from your voice request approval"
|
||||
@@ -519,19 +519,19 @@ msgstr "Não foi possível extrair o JID (Jabber ID) da requisição de voz"
|
||||
|
||||
#: mod_delegation:257
|
||||
msgid "Failed to map delegated namespace to external component"
|
||||
msgstr ""
|
||||
msgstr "Falha ao mapear namespace delegado ao componente externo"
|
||||
|
||||
#: mod_http_upload:602
|
||||
msgid "Failed to parse HTTP response"
|
||||
msgstr ""
|
||||
msgstr "Falha ao analisar resposta HTTP"
|
||||
|
||||
#: mod_irc:426
|
||||
msgid "Failed to parse chanserv"
|
||||
msgstr ""
|
||||
msgstr "Falha ao analisar chanserv"
|
||||
|
||||
#: mod_muc_room:3297
|
||||
msgid "Failed to process option '~s'"
|
||||
msgstr ""
|
||||
msgstr "Falha ao processar opção '~s'"
|
||||
|
||||
#: mod_vcard_ldap:332 mod_vcard_ldap:345 mod_vcard_mnesia:103
|
||||
#: mod_vcard_mnesia:117 mod_vcard_sql:158 mod_vcard_sql:172
|
||||
@@ -544,7 +544,7 @@ msgstr "Fevereiro"
|
||||
|
||||
#: mod_http_upload:555
|
||||
msgid "File larger than ~w bytes"
|
||||
msgstr ""
|
||||
msgstr "Arquivo maior que ~w bytes"
|
||||
|
||||
#: mod_vcard:437
|
||||
msgid ""
|
||||
@@ -610,7 +610,7 @@ msgstr "Máquina"
|
||||
|
||||
#: mod_s2s_dialback:325
|
||||
msgid "Host unknown"
|
||||
msgstr ""
|
||||
msgstr "Máquina desconhecida"
|
||||
|
||||
#: ejabberd_web_admin:2463
|
||||
msgid "IP"
|
||||
@@ -699,15 +699,15 @@ msgstr "Importar dados dos usuários de um diretório-fila jabberd14:"
|
||||
|
||||
#: xmpp_stream_in:983
|
||||
msgid "Improper 'from' attribute"
|
||||
msgstr ""
|
||||
msgstr "Atributo 'from' incorreto"
|
||||
|
||||
#: xmpp_stream_in:477
|
||||
msgid "Improper 'to' attribute"
|
||||
msgstr ""
|
||||
msgstr "Atributo 'to' incorreto"
|
||||
|
||||
#: ejabberd_service:189
|
||||
msgid "Improper domain part of 'from' attribute"
|
||||
msgstr ""
|
||||
msgstr "Atributo 'from' contém domínio incorreto"
|
||||
|
||||
#: mod_muc_room:260
|
||||
msgid "Improper message type"
|
||||
@@ -715,11 +715,11 @@ msgstr "Tipo de mensagem incorreto"
|
||||
|
||||
#: ejabberd_web_admin:1586
|
||||
msgid "Incoming s2s Connections:"
|
||||
msgstr "Conexões que entram de s2s"
|
||||
msgstr "Conexões s2s de Entrada:"
|
||||
|
||||
#: mod_muc_room:3669 mod_register:187
|
||||
msgid "Incorrect CAPTCHA submit"
|
||||
msgstr ""
|
||||
msgstr "CAPTCHA submetido incorretamente"
|
||||
|
||||
#: mod_muc:772 mod_muc_room:3052 mod_pubsub:1194 mod_register:183 mod_vcard:256
|
||||
#, fuzzy
|
||||
@@ -732,41 +732,41 @@ msgstr "Senha incorreta"
|
||||
|
||||
#: mod_irc:585
|
||||
msgid "Incorrect value in data form"
|
||||
msgstr ""
|
||||
msgstr "Valor incorreto em formulário de dados"
|
||||
|
||||
#: mod_adhoc:276
|
||||
msgid "Incorrect value of 'action' attribute"
|
||||
msgstr ""
|
||||
msgstr "Valor incorreto do atributo 'action'"
|
||||
|
||||
#: mod_configure:1806
|
||||
msgid "Incorrect value of 'action' in data form"
|
||||
msgstr ""
|
||||
msgstr "Valor incorreto de 'action' no formulário de dados"
|
||||
|
||||
#: mod_configure:1335 mod_configure:1367 mod_configure:1399 mod_configure:1419
|
||||
#: mod_configure:1439
|
||||
msgid "Incorrect value of 'path' in data form"
|
||||
msgstr ""
|
||||
msgstr "Valor incorreto de 'path' no formulário de dados"
|
||||
|
||||
#: mod_irc:331
|
||||
msgid "Incorrect value of 'type' attribute"
|
||||
msgstr ""
|
||||
msgstr "Valor incorreto do atributo 'type'"
|
||||
|
||||
#: mod_privilege:100
|
||||
msgid "Insufficient privilege"
|
||||
msgstr ""
|
||||
msgstr "Privilégio insuficiente"
|
||||
|
||||
#: mod_privilege:286
|
||||
msgid "Invalid 'from' attribute in forwarded message"
|
||||
msgstr ""
|
||||
msgstr "Atributo 'from' inválido na mensagem reenviada"
|
||||
|
||||
#: mod_privilege:300
|
||||
msgid "Invalid <forwarded/> element"
|
||||
msgstr ""
|
||||
msgstr "Elemento <forwarded/> inválido"
|
||||
|
||||
#: mod_muc_room:3926
|
||||
#, fuzzy
|
||||
msgid "Invitations are not allowed in this conference"
|
||||
msgstr "Requisições de voz estão desabilitadas nesta conferência"
|
||||
msgstr "Convites estão desabilitados nesta sala de conferência"
|
||||
|
||||
#: mod_muc_room:233 mod_muc_room:375 mod_muc_room:1046
|
||||
msgid ""
|
||||
@@ -786,11 +786,11 @@ msgstr "Não é permitido enviar mensagens privadas do tipo \"groupchat\""
|
||||
|
||||
#: mod_muc_room:244
|
||||
msgid "It is not allowed to send private messages to the conference"
|
||||
msgstr "Impedir o envio de mensagens privadas para a sala"
|
||||
msgstr "Não é permitido enviar mensagens privadas para a sala de conferência"
|
||||
|
||||
#: mod_register_web:181 mod_register_web:189
|
||||
msgid "Jabber Account Registration"
|
||||
msgstr "Registros de Contas Jabber"
|
||||
msgstr "Registro de Contas Jabber"
|
||||
|
||||
#: mod_configure:1122 mod_configure:1139 mod_configure:1149 mod_configure:1159
|
||||
#: mod_configure:1169 mod_configure:1183 mod_configure:1192 mod_configure:1600
|
||||
@@ -805,11 +805,11 @@ msgstr "Janeiro"
|
||||
|
||||
#: mod_irc:665
|
||||
msgid "Join IRC channel"
|
||||
msgstr "Juntar-se ao canal IRC"
|
||||
msgstr "Entrar no canal IRC"
|
||||
|
||||
#: mod_irc:689
|
||||
msgid "Join the IRC channel here."
|
||||
msgstr "Aqui! Juntar-se ao canal IRC."
|
||||
msgstr "Entre no canal IRC aqui."
|
||||
|
||||
#: mod_irc:690
|
||||
msgid "Join the IRC channel in this Jabber ID: ~s"
|
||||
@@ -890,7 +890,7 @@ msgstr "Tornar sala pública possível de ser encontrada"
|
||||
#: mod_register:317
|
||||
#, fuzzy
|
||||
msgid "Malformed username"
|
||||
msgstr "Usuário IRC"
|
||||
msgstr "Nome de usuário inválido"
|
||||
|
||||
#: mod_muc_log:475
|
||||
msgid "March"
|
||||
@@ -910,7 +910,7 @@ msgstr "Membros:"
|
||||
|
||||
#: mod_muc_room:1833
|
||||
msgid "Membership is required to enter this room"
|
||||
msgstr "Necessitas ser membro desta sala para poder entrar"
|
||||
msgstr "É necessário ser membro desta sala para poder entrar"
|
||||
|
||||
#: mod_register_web:262
|
||||
msgid ""
|
||||
@@ -918,9 +918,9 @@ msgid ""
|
||||
"Jabber there isn't an automated way to recover your password if you forget "
|
||||
"it."
|
||||
msgstr ""
|
||||
"Memorize a sua senha, ou escreva-a em um papel e guarde-o em um lugar "
|
||||
"seguro. Jabber não é uma maneira automatizada para recuperar a sua senha, se "
|
||||
"você a esquecer eventualmente."
|
||||
"Memorize a sua senha, ou anote-a em um papel, guardado em um local seguro. "
|
||||
"No Jabber, não há uma maneira automatizada de recuperar a sua senha, "
|
||||
"caso a esqueça."
|
||||
|
||||
#: ejabberd_web_admin:1954
|
||||
msgid "Memory"
|
||||
@@ -932,7 +932,7 @@ msgstr "Corpo da mensagem"
|
||||
|
||||
#: mod_privilege:291
|
||||
msgid "Message not found in forwarded payload"
|
||||
msgstr ""
|
||||
msgstr "Mensagem não encontrada em conteúdo encaminhado"
|
||||
|
||||
#: mod_vcard_ldap:331 mod_vcard_ldap:344 mod_vcard_mnesia:102
|
||||
#: mod_vcard_mnesia:116 mod_vcard_sql:157 mod_vcard_sql:171
|
||||
@@ -941,15 +941,15 @@ msgstr "Nome do meio"
|
||||
|
||||
#: mod_irc:704
|
||||
msgid "Missing 'channel' or 'server' in the data form"
|
||||
msgstr ""
|
||||
msgstr "Faltando 'channel' ou 'server' no formulário de dados"
|
||||
|
||||
#: xmpp_stream_in:990
|
||||
msgid "Missing 'from' attribute"
|
||||
msgstr ""
|
||||
msgstr "Faltando atributo 'from'"
|
||||
|
||||
#: xmpp_stream_in:472 xmpp_stream_in:993
|
||||
msgid "Missing 'to' attribute"
|
||||
msgstr ""
|
||||
msgstr "Faltando atributo 'to'"
|
||||
|
||||
#: mod_muc_room:2536 mod_muc_room:3721 mod_muc_room:3765 mod_muc_room:3798
|
||||
msgid "Moderator privileges required"
|
||||
@@ -965,7 +965,7 @@ msgstr "Módulo"
|
||||
|
||||
#: gen_iq_handler:153
|
||||
msgid "Module failed to handle the query"
|
||||
msgstr ""
|
||||
msgstr "Módulo falhou ao processar a consulta"
|
||||
|
||||
#: ejabberd_web_admin:1885 mod_configure:582 mod_configure:595
|
||||
msgid "Modules"
|
||||
@@ -989,7 +989,7 @@ msgstr "Multicast"
|
||||
|
||||
#: mod_roster:195
|
||||
msgid "Multiple <item/> elements are not allowed by RFC6121"
|
||||
msgstr ""
|
||||
msgstr "Vários elementos <item/> não são permitidos pela RFC6121"
|
||||
|
||||
#: ejabberd_web_admin:1951 mod_vcard_mnesia:101 mod_vcard_mnesia:115
|
||||
#: mod_vcard_sql:156 mod_vcard_sql:170
|
||||
@@ -1002,11 +1002,11 @@ msgstr "Nome:"
|
||||
|
||||
#: mod_muc_room:2696
|
||||
msgid "Neither 'jid' nor 'nick' attribute found"
|
||||
msgstr ""
|
||||
msgstr "Nem o atributo 'jid' nem 'nick' foram encontrados"
|
||||
|
||||
#: mod_muc_room:2518 mod_muc_room:2701
|
||||
msgid "Neither 'role' nor 'affiliation' attribute found"
|
||||
msgstr ""
|
||||
msgstr "Nem o atributo 'role' nem 'affiliation' foram encontrados"
|
||||
|
||||
#: ejabberd_web_admin:1506 ejabberd_web_admin:1687 mod_configure:1629
|
||||
msgid "Never"
|
||||
@@ -1031,41 +1031,41 @@ msgstr "O apelido ~s não existe na sala"
|
||||
|
||||
#: mod_configure:1496
|
||||
msgid "No 'access' found in data form"
|
||||
msgstr ""
|
||||
msgstr "'access' não foi encontrado em formulário de dados"
|
||||
|
||||
#: mod_configure:1455
|
||||
msgid "No 'acls' found in data form"
|
||||
msgstr ""
|
||||
msgstr "'acls' não foi encontrado em formulário de dados"
|
||||
|
||||
#: mod_muc_room:3075
|
||||
msgid "No 'affiliation' attribute found"
|
||||
msgstr ""
|
||||
msgstr "Atributo 'affiliation' não foi encontrado"
|
||||
|
||||
#: mod_muc_room:2505
|
||||
#, fuzzy
|
||||
msgid "No 'item' element found"
|
||||
msgstr "Nó não encontrado"
|
||||
msgstr "Elemento 'item' não foi encontrado"
|
||||
|
||||
#: mod_configure:1280
|
||||
msgid "No 'modules' found in data form"
|
||||
msgstr ""
|
||||
msgstr "'modules' não foi encontrado em formulário de dados"
|
||||
|
||||
#: mod_configure:1799
|
||||
msgid "No 'password' found in data form"
|
||||
msgstr ""
|
||||
msgstr "'password' não foi encontrado em formulário de dados"
|
||||
|
||||
#: mod_register:148
|
||||
msgid "No 'password' found in this query"
|
||||
msgstr ""
|
||||
msgstr "'password' não foi encontrado nesta consulta"
|
||||
|
||||
#: mod_configure:1319 mod_configure:1350 mod_configure:1382 mod_configure:1413
|
||||
#: mod_configure:1433
|
||||
msgid "No 'path' found in data form"
|
||||
msgstr ""
|
||||
msgstr "'path' não foi encontrado em formulário de dados"
|
||||
|
||||
#: mod_muc_room:3922
|
||||
msgid "No 'to' attribute found in the invitation"
|
||||
msgstr ""
|
||||
msgstr "Atributo 'to' não foi encontrado no convite"
|
||||
|
||||
#: ejabberd_web_admin:1767
|
||||
msgid "No Data"
|
||||
@@ -1073,7 +1073,7 @@ msgstr "Nenhum dado"
|
||||
|
||||
#: ejabberd_local:181
|
||||
msgid "No available resource found"
|
||||
msgstr ""
|
||||
msgstr "Nenhum recurso disponível foi encontrado"
|
||||
|
||||
#: mod_announce:575
|
||||
msgid "No body provided for announce message"
|
||||
@@ -1082,45 +1082,45 @@ msgstr "Nenhum corpo de texto fornecido para anunciar mensagem"
|
||||
#: mod_irc:335 mod_pubsub:1183 mod_pubsub:3289
|
||||
#, fuzzy
|
||||
msgid "No data form found"
|
||||
msgstr "Nó não encontrado"
|
||||
msgstr "Formulário de dados não foi encontrado"
|
||||
|
||||
#: mod_disco:224 mod_vcard:282
|
||||
msgid "No features available"
|
||||
msgstr ""
|
||||
msgstr "Nenhuma funcionalidade disponível"
|
||||
|
||||
#: mod_adhoc:239
|
||||
msgid "No hook has processed this command"
|
||||
msgstr ""
|
||||
msgstr "Nenhum hook processou este comando"
|
||||
|
||||
#: mod_last:218
|
||||
msgid "No info about last activity found"
|
||||
msgstr ""
|
||||
msgstr "Não foi encontrada informação sobre última atividade"
|
||||
|
||||
#: mod_blocking:99
|
||||
msgid "No items found in this query"
|
||||
msgstr ""
|
||||
msgstr "Nenhum item encontrado nesta consulta"
|
||||
|
||||
#: ejabberd_local:90 ejabberd_sm:863 mod_blocking:92 mod_blocking:110
|
||||
#: mod_http_upload:513 mod_muc:482 mod_muc:534 mod_muc:556 mod_muc:580
|
||||
#: mod_offline:303 mod_privacy:168 mod_privacy:285 mod_roster:207
|
||||
msgid "No module is handling this query"
|
||||
msgstr ""
|
||||
msgstr "Nenhum módulo está processando esta consulta"
|
||||
|
||||
#: mod_pubsub:1541
|
||||
msgid "No node specified"
|
||||
msgstr ""
|
||||
msgstr "Nenhum nó especificado"
|
||||
|
||||
#: mod_pubsub:1426
|
||||
msgid "No pending subscriptions found"
|
||||
msgstr ""
|
||||
msgstr "Não foram encontradas subscrições"
|
||||
|
||||
#: mod_privacy:201 mod_privacy:295 mod_privacy:311 mod_privacy:344
|
||||
msgid "No privacy list with this name found"
|
||||
msgstr ""
|
||||
msgstr "Nenhuma lista de privacidade encontrada com este nome"
|
||||
|
||||
#: mod_private:96
|
||||
msgid "No private data found in this query"
|
||||
msgstr ""
|
||||
msgstr "Nenhum dado privado encontrado nesta consulta"
|
||||
|
||||
#: mod_configure:869 mod_configure:908 mod_configure:1222 mod_configure:1254
|
||||
#: mod_configure:1275 mod_configure:1314 mod_configure:1345 mod_configure:1377
|
||||
@@ -1131,15 +1131,15 @@ msgstr "Nó não encontrado"
|
||||
|
||||
#: mod_disco:252 mod_vcard:265
|
||||
msgid "No services available"
|
||||
msgstr ""
|
||||
msgstr "Não há serviços disponíveis"
|
||||
|
||||
#: mod_stats:101
|
||||
msgid "No statistics found for this item"
|
||||
msgstr ""
|
||||
msgstr "Não foram encontradas estatísticas para este item"
|
||||
|
||||
#: nodetree_dag:72 nodetree_tree:181 nodetree_tree_sql:255
|
||||
msgid "Node already exists"
|
||||
msgstr ""
|
||||
msgstr "Nó já existe"
|
||||
|
||||
#: nodetree_tree_sql:99
|
||||
#, fuzzy
|
||||
@@ -1159,7 +1159,7 @@ msgstr "Nó ~p"
|
||||
|
||||
#: mod_vcard:385
|
||||
msgid "Nodeprep has failed"
|
||||
msgstr ""
|
||||
msgstr "Processo de identificação de nó falhou (nodeprep)"
|
||||
|
||||
#: ejabberd_web_admin:1837
|
||||
msgid "Nodes"
|
||||
@@ -1176,7 +1176,7 @@ msgstr "Não encontrado"
|
||||
|
||||
#: mod_disco:296 mod_disco:370 mod_last:159
|
||||
msgid "Not subscribed"
|
||||
msgstr ""
|
||||
msgstr "Não subscrito"
|
||||
|
||||
#: mod_muc_log:483
|
||||
msgid "November"
|
||||
@@ -1227,11 +1227,11 @@ msgstr "Usuários online"
|
||||
|
||||
#: mod_carboncopy:141
|
||||
msgid "Only <enable/> or <disable/> tags are allowed"
|
||||
msgstr ""
|
||||
msgstr "Apenas tags <enable/> ou <disable/> são permitidas"
|
||||
|
||||
#: mod_privacy:154
|
||||
msgid "Only <list/> element is allowed in this query"
|
||||
msgstr ""
|
||||
msgstr "Apenas elemento <list/> é permitido nesta consulta"
|
||||
|
||||
#: mod_mam:379
|
||||
msgid "Only members may query archives of this room"
|
||||
@@ -1255,11 +1255,11 @@ msgstr "Somente moderadores podem aprovar requisições de voz"
|
||||
|
||||
#: mod_muc_room:424 mod_muc_room:792 mod_muc_room:3989
|
||||
msgid "Only occupants are allowed to send messages to the conference"
|
||||
msgstr "Somente os ocupantes podem enviar mensagens à sala"
|
||||
msgstr "Somente os ocupantes podem enviar mensagens à sala de conferência"
|
||||
|
||||
#: mod_muc_room:457
|
||||
msgid "Only occupants are allowed to send queries to the conference"
|
||||
msgstr "Somente os ocupantes podem enviar consultas à sala"
|
||||
msgstr "Somente os ocupantes podem enviar consultas à sala de conferência"
|
||||
|
||||
#: mod_muc:422
|
||||
msgid "Only service administrators are allowed to send service messages"
|
||||
@@ -1282,11 +1282,11 @@ msgstr "Departamento/Unidade"
|
||||
|
||||
#: mod_configure:508
|
||||
msgid "Outgoing s2s Connections"
|
||||
msgstr "Conexões que partam de s2s"
|
||||
msgstr "Conexões s2s de Saída"
|
||||
|
||||
#: ejabberd_web_admin:1583
|
||||
msgid "Outgoing s2s Connections:"
|
||||
msgstr "Conexões que partem de s2s"
|
||||
msgstr "Conexões s2s de Saída"
|
||||
|
||||
#: mod_muc_room:3023 mod_muc_room:3067 mod_muc_room:3697 mod_pubsub:1302
|
||||
#: mod_pubsub:1394 mod_pubsub:1553 mod_pubsub:2118 mod_pubsub:2184
|
||||
@@ -1300,11 +1300,11 @@ msgstr "Pacote"
|
||||
|
||||
#: mod_irc:578
|
||||
msgid "Parse error"
|
||||
msgstr ""
|
||||
msgstr "Erro de análise de dados"
|
||||
|
||||
#: mod_configure:1299 mod_configure:1468 mod_configure:1513
|
||||
msgid "Parse failed"
|
||||
msgstr ""
|
||||
msgstr "Análise de dados falhou"
|
||||
|
||||
#: ejabberd_oauth:431 ejabberd_web_admin:1435 mod_configure:1126
|
||||
#: mod_configure:1173 mod_configure:1602 mod_configure:1781 mod_muc_log:1036
|
||||
@@ -1384,7 +1384,7 @@ msgstr "Porta ~b"
|
||||
|
||||
#: mod_roster:173
|
||||
msgid "Possessing 'ask' attribute is not allowed by RFC6121"
|
||||
msgstr ""
|
||||
msgstr "Possuir atributo 'ask' não é permitido pela RFC6121"
|
||||
|
||||
#: ejabberd_web_admin:2464
|
||||
msgid "Protocol"
|
||||
@@ -1400,16 +1400,16 @@ msgstr "Publicação de Tópico"
|
||||
|
||||
#: node_dag:81
|
||||
msgid "Publishing items to collection node is not allowed"
|
||||
msgstr ""
|
||||
msgstr "Publicar items em um nó de coleção não é permitido"
|
||||
|
||||
#: mod_muc_room:462
|
||||
msgid "Queries to the conference members are not allowed in this room"
|
||||
msgstr "Nesta sala não se permite consultas aos membros da sala"
|
||||
msgstr "Nesta sala de conferência, consultas aos membros não são permitidas"
|
||||
|
||||
#: mod_blocking:85 mod_disco:325 mod_disco:393 mod_offline:270 mod_privacy:146
|
||||
#: mod_private:118 mod_roster:163 mod_sic:90
|
||||
msgid "Query to another users is forbidden"
|
||||
msgstr ""
|
||||
msgstr "Consultar a outro usuário é proibido"
|
||||
|
||||
#: mod_configure:889
|
||||
msgid "RAM and disc copy"
|
||||
@@ -1540,7 +1540,7 @@ msgstr "Lista de contatos"
|
||||
|
||||
#: mod_roster:334
|
||||
msgid "Roster module has failed"
|
||||
msgstr ""
|
||||
msgstr "O módulo Roster falhou"
|
||||
|
||||
#: mod_roster:968
|
||||
msgid "Roster of "
|
||||
@@ -1556,7 +1556,7 @@ msgstr "Nós em execução"
|
||||
|
||||
#: xmpp_stream_in:541 xmpp_stream_in:549
|
||||
msgid "SASL negotiation is not allowed in this state"
|
||||
msgstr ""
|
||||
msgstr "Negociação SASL não é permitida neste estado"
|
||||
|
||||
#: mod_muc_log:468
|
||||
msgid "Saturday"
|
||||
@@ -1564,12 +1564,12 @@ msgstr "Sábado"
|
||||
|
||||
#: mod_irc:582
|
||||
msgid "Scan error"
|
||||
msgstr ""
|
||||
msgstr "Erro de escaneamento"
|
||||
|
||||
#: mod_configure:1303 mod_configure:1472 mod_configure:1517
|
||||
#, fuzzy
|
||||
msgid "Scan failed"
|
||||
msgstr "O CAPTCHA é inválido."
|
||||
msgstr "O escaneamento falhou"
|
||||
|
||||
#: ejabberd_web_admin:2282
|
||||
msgid "Script check"
|
||||
@@ -1605,11 +1605,11 @@ msgstr "Setembro"
|
||||
|
||||
#: mod_irc_connection:648
|
||||
msgid "Server Connect Failed"
|
||||
msgstr ""
|
||||
msgstr "Conexão ao servidor falhou"
|
||||
|
||||
#: ejabberd_s2s:369
|
||||
msgid "Server connections to local subdomains are forbidden"
|
||||
msgstr ""
|
||||
msgstr "Conexões de servidor a subdomínios locais estão proibidas"
|
||||
|
||||
#: mod_irc:842
|
||||
msgid "Server ~b"
|
||||
@@ -1724,7 +1724,7 @@ msgstr "Subscrição"
|
||||
|
||||
#: mod_muc_room:3708
|
||||
msgid "Subscriptions are not allowed"
|
||||
msgstr ""
|
||||
msgstr "Subscrições não estão permitidas"
|
||||
|
||||
#: mod_muc_log:469
|
||||
msgid "Sunday"
|
||||
@@ -1748,15 +1748,15 @@ msgstr "A verificação do CAPTCHA falhou"
|
||||
|
||||
#: mod_muc_room:302
|
||||
msgid "The feature requested is not supported by the conference"
|
||||
msgstr ""
|
||||
msgstr "A funcionalidade solicitada não é suportada pela sala de conferência"
|
||||
|
||||
#: mod_register:308 mod_register:366
|
||||
msgid "The password contains unacceptable characters"
|
||||
msgstr ""
|
||||
msgstr "A senha contém caracteres proibidos"
|
||||
|
||||
#: mod_register:311 mod_register:370
|
||||
msgid "The password is too weak"
|
||||
msgstr "Senha considerada fraca'"
|
||||
msgstr "Senha considerada muito fraca"
|
||||
|
||||
#: mod_register_web:141
|
||||
msgid "The password of your Jabber account was successfully changed."
|
||||
@@ -1764,17 +1764,19 @@ msgstr "A senha da sua conta Jabber foi mudada com sucesso."
|
||||
|
||||
#: mod_register:160 mod_vcard:219
|
||||
msgid "The query is only allowed from local users"
|
||||
msgstr ""
|
||||
msgstr "Esta consulta só é permitida a partir de usuários locais"
|
||||
|
||||
#: mod_roster:203
|
||||
msgid "The query must not contain <item/> elements"
|
||||
msgstr ""
|
||||
msgstr "A consulta não pode conter elementos <item/>"
|
||||
|
||||
#: mod_privacy:280
|
||||
msgid ""
|
||||
"The stanza MUST contain only one <active/> element, one <default/> element, "
|
||||
"or one <list/> element"
|
||||
msgstr ""
|
||||
"A instância DEVE conter apenas um elemento <active/>, um elemento <default/>, "
|
||||
"ou um elemento <list/>"
|
||||
|
||||
#: mod_register_web:146
|
||||
msgid "There was an error changing the password: "
|
||||
@@ -1830,7 +1832,7 @@ msgstr "Para"
|
||||
|
||||
#: mod_register:215
|
||||
msgid "To register, visit ~s"
|
||||
msgstr ""
|
||||
msgstr "Para registrar, visite ~s"
|
||||
|
||||
#: mod_configure:709
|
||||
msgid "To ~s"
|
||||
@@ -1838,19 +1840,19 @@ msgstr "Para ~s"
|
||||
|
||||
#: ejabberd_oauth:439
|
||||
msgid "Token TTL"
|
||||
msgstr ""
|
||||
msgstr "Token TTL"
|
||||
|
||||
#: xmpp_stream_in:463
|
||||
msgid "Too long value of 'xml:lang' attribute"
|
||||
msgstr ""
|
||||
msgstr "Valor do atributo 'xml:lang' é demasiado longo"
|
||||
|
||||
#: mod_muc_room:2541 mod_muc_room:3081
|
||||
msgid "Too many <item/> elements"
|
||||
msgstr ""
|
||||
msgstr "Número excessivo de elementos <item/>"
|
||||
|
||||
#: mod_privacy:164
|
||||
msgid "Too many <list/> elements"
|
||||
msgstr ""
|
||||
msgstr "Número excessivo de elementos <list/>"
|
||||
|
||||
#: mod_muc_room:1924 mod_register:240
|
||||
msgid "Too many CAPTCHA requests"
|
||||
@@ -1859,20 +1861,20 @@ msgstr "Número excessivo de requisições para o CAPTCHA"
|
||||
#: mod_proxy65_service:223
|
||||
#, fuzzy
|
||||
msgid "Too many active bytestreams"
|
||||
msgstr "número excessivo de instâncias sem confirmação"
|
||||
msgstr "Número excessivo de bytestreams ativos"
|
||||
|
||||
#: mod_stream_mgmt:205
|
||||
msgid "Too many unacked stanzas"
|
||||
msgstr "número excessivo de instâncias sem confirmação"
|
||||
msgstr "Número excessivo de instâncias sem confirmação"
|
||||
|
||||
#: mod_muc_room:1802
|
||||
#, fuzzy
|
||||
msgid "Too many users in this conference"
|
||||
msgstr "Requisições de voz estão desabilitadas nesta conferência"
|
||||
msgstr "Número excessivo de usuários nesta sala de conferência"
|
||||
|
||||
#: mod_register:355
|
||||
msgid "Too many users registered"
|
||||
msgstr ""
|
||||
msgstr "Número excessivo de usuários registrados"
|
||||
|
||||
#: mod_muc_admin:368
|
||||
msgid "Total rooms"
|
||||
@@ -1908,7 +1910,7 @@ msgstr "Impossível gerar um CAPTCHA"
|
||||
|
||||
#: ejabberd_service:120
|
||||
msgid "Unable to register route on existing local domain"
|
||||
msgstr ""
|
||||
msgstr "Não foi possível registrar rota no domínio local existente"
|
||||
|
||||
#: ejabberd_web_admin:209 ejabberd_web_admin:221 ejabberd_web_admin:241
|
||||
#: ejabberd_web_admin:253
|
||||
@@ -1917,7 +1919,7 @@ msgstr "Não Autorizado"
|
||||
|
||||
#: mod_announce:485 mod_configure:830 mod_configure:1758
|
||||
msgid "Unexpected action"
|
||||
msgstr ""
|
||||
msgstr "Ação inesperada"
|
||||
|
||||
#: mod_register_web:488
|
||||
msgid "Unregister"
|
||||
@@ -1929,11 +1931,11 @@ msgstr "Deletar conta Jabber"
|
||||
|
||||
#: mod_mam:526
|
||||
msgid "Unsupported <index/> element"
|
||||
msgstr ""
|
||||
msgstr "Elemento <index/> não suportado"
|
||||
|
||||
#: mod_mix:119
|
||||
msgid "Unsupported MIX query"
|
||||
msgstr ""
|
||||
msgstr "Consula MIX não suportada"
|
||||
|
||||
#: ejabberd_web_admin:1872 ejabberd_web_admin:2285
|
||||
msgid "Update"
|
||||
@@ -1979,7 +1981,7 @@ msgstr "Usuário"
|
||||
|
||||
#: ejabberd_oauth:428
|
||||
msgid "User (jid)"
|
||||
msgstr ""
|
||||
msgstr "Usuário (jid)"
|
||||
|
||||
#: mod_configure:308 mod_configure:505
|
||||
msgid "User Management"
|
||||
@@ -1987,11 +1989,11 @@ msgstr "Gerenciamento de Usuários"
|
||||
|
||||
#: mod_register:345
|
||||
msgid "User already exists"
|
||||
msgstr ""
|
||||
msgstr "Usuário já existe"
|
||||
|
||||
#: mod_echo:138
|
||||
msgid "User part of JID in 'from' is empty"
|
||||
msgstr ""
|
||||
msgstr "Parte do usuário do JID em 'from' está vazia"
|
||||
|
||||
#: ejabberd_sm:193 ejabberd_sm:662 ejabberd_sm:687 mod_sic:106
|
||||
#, fuzzy
|
||||
@@ -2000,7 +2002,7 @@ msgstr "Nó não encontrado"
|
||||
|
||||
#: mod_stream_mgmt:561 mod_stream_mgmt:583
|
||||
msgid "User session terminated"
|
||||
msgstr ""
|
||||
msgstr "Sessão de usuário terminada"
|
||||
|
||||
#: ejabberd_web_admin:1700
|
||||
msgid "User ~s"
|
||||
@@ -2029,7 +2031,7 @@ msgstr "Validar"
|
||||
#: mod_carboncopy:144 mod_irc:345 mod_muc_room:3662 mod_muc_room:3802
|
||||
#: mod_pubsub:895 mod_push:249
|
||||
msgid "Value 'get' of 'type' attribute is not allowed"
|
||||
msgstr ""
|
||||
msgstr "Valor 'get' não permitido para atributo 'type'"
|
||||
|
||||
#: mod_disco:159 mod_disco:175 mod_disco:279 mod_disco:346 mod_irc:270
|
||||
#: mod_irc:285 mod_irc:339 mod_last:118 mod_last:140 mod_muc:479 mod_muc:504
|
||||
@@ -2038,20 +2040,20 @@ msgstr ""
|
||||
#: mod_pubsub:821 mod_pubsub:839 mod_pubsub:877 mod_sic:81 mod_sic:93
|
||||
#: mod_stats:55 mod_time:62 mod_vcard:198 mod_vcard:236 mod_version:62
|
||||
msgid "Value 'set' of 'type' attribute is not allowed"
|
||||
msgstr ""
|
||||
msgstr "Valor 'set' não permitido para atributo 'type'"
|
||||
|
||||
#: pubsub_subscription:237 pubsub_subscription_sql:202
|
||||
msgid "Value of '~s' should be boolean"
|
||||
msgstr ""
|
||||
msgstr "Value de '~s' deveria ser um booleano"
|
||||
|
||||
#: pubsub_subscription:215 pubsub_subscription_sql:180
|
||||
msgid "Value of '~s' should be datetime string"
|
||||
msgstr ""
|
||||
msgstr "Valor de '~s' deveria ser data e hora"
|
||||
|
||||
#: pubsub_subscription:209 pubsub_subscription:227 pubsub_subscription_sql:174
|
||||
#: pubsub_subscription_sql:192
|
||||
msgid "Value of '~s' should be integer"
|
||||
msgstr ""
|
||||
msgstr "Valor de '~s' deveria ser um inteiro"
|
||||
|
||||
#: ejabberd_web_admin:950
|
||||
msgid "Virtual Hosts"
|
||||
@@ -2071,7 +2073,7 @@ msgstr "Requisição de voz"
|
||||
|
||||
#: mod_muc_room:885
|
||||
msgid "Voice requests are disabled in this conference"
|
||||
msgstr "Requisições de voz estão desabilitadas nesta conferência"
|
||||
msgstr "Requisições de voz estão desabilitadas nesta sala de conferência"
|
||||
|
||||
#: mod_muc_log:465
|
||||
msgid "Wednesday"
|
||||
@@ -2087,7 +2089,7 @@ msgstr "Você foi banido desta sala"
|
||||
|
||||
#: mod_muc_room:1811
|
||||
msgid "You have joined too many conferences"
|
||||
msgstr ""
|
||||
msgstr "Você entrou em um número excessivo de salas de conferência"
|
||||
|
||||
#: mod_muc:777
|
||||
msgid "You must fill in field \"Nickname\" in the form"
|
||||
@@ -2379,7 +2381,7 @@ msgstr "~s's Fila de Mensagens Offline"
|
||||
#~ msgstr "Preencha campos para buscar usuários Jabber que concordem"
|
||||
|
||||
#~ msgid "Outgoing s2s Servers:"
|
||||
#~ msgstr "Servidores que partem de s2s"
|
||||
#~ msgstr "Servidores s2s de Saída"
|
||||
|
||||
#~ msgid "Delete"
|
||||
#~ msgstr "Eliminar"
|
||||
|
||||
+15
-21
@@ -19,24 +19,24 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
{deps, [{lager, ".*", {git, "https://github.com/erlang-lager/lager",
|
||||
{tag, {if_version_above, "17", "3.4.2", "3.2.1"}}}},
|
||||
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.11"}}},
|
||||
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.13"}}},
|
||||
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.22"}}},
|
||||
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.11"}}},
|
||||
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.30"}}},
|
||||
{xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.1.21"}}},
|
||||
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.14"}}},
|
||||
{tag, {if_version_above, "17", "3.6.5", "3.2.1"}}}},
|
||||
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.13"}}},
|
||||
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.16"}}},
|
||||
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.25"}}},
|
||||
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.14"}}},
|
||||
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.34"}}},
|
||||
{xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.2.5"}}},
|
||||
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.17"}}},
|
||||
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
|
||||
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.3"}}},
|
||||
{jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.8.4"}}},
|
||||
{eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.5"}}},
|
||||
{if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.22"}}}},
|
||||
{if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.23"}}}},
|
||||
{eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.8"}}},
|
||||
{if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.25"}}}},
|
||||
{if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.26"}}}},
|
||||
{if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql",
|
||||
{tag, "1.0.5"}}}},
|
||||
{tag, "1.0.7"}}}},
|
||||
{if_var_true, pgsql, {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql",
|
||||
{tag, "1.1.5"}}}},
|
||||
{tag, "1.1.6"}}}},
|
||||
{if_var_true, sqlite, {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3",
|
||||
{tag, "1.1.6"}}}},
|
||||
{if_var_true, pam, {epam, ".*", {git, "https://github.com/processone/epam",
|
||||
@@ -52,13 +52,11 @@
|
||||
{if_not_rebar3, {if_var_true, elixir, {rebar_elixir_plugin, ".*",
|
||||
{git, "https://github.com/processone/rebar_elixir_plugin", "0.1.0"}}}},
|
||||
{if_var_true, iconv, {iconv, ".*", {git, "https://github.com/processone/iconv",
|
||||
{tag, "1.0.7"}}}},
|
||||
{tag, "1.0.10"}}}},
|
||||
{if_var_true, tools, {luerl, ".*", {git, "https://github.com/rvirding/luerl",
|
||||
{tag, "v0.3"}}}},
|
||||
{if_var_true, tools, {meck, "0.8.*", {git, "https://github.com/eproxus/meck",
|
||||
{tag, "0.8.4"}}}},
|
||||
{if_var_true, tools, {moka, ".*", {git, "https://github.com/processone/moka",
|
||||
{tag, "1.0.5c"}}}},
|
||||
{if_var_true, redis, {eredis, ".*", {git, "https://github.com/wooga/eredis",
|
||||
{tag, "v1.0.8"}}}}]}.
|
||||
|
||||
@@ -96,14 +94,13 @@
|
||||
{if_var_true, roster_gateway_workaround, {d, 'ROSTER_GATWAY_WORKAROUND'}},
|
||||
{if_var_match, db_type, mssql, {d, 'mssql'}},
|
||||
{if_var_true, elixir, {d, 'ELIXIR_ENABLED'}},
|
||||
{if_var_true, erlang_deprecated_types, {d, 'ERL_DEPRECATED_TYPES'}},
|
||||
{if_have_fun, {crypto, strong_rand_bytes, 1}, {d, 'STRONG_RAND_BYTES'}},
|
||||
{if_have_fun, {rand, uniform, 1}, {d, 'RAND_UNIFORM'}},
|
||||
{if_have_fun, {gb_sets, iterator_from, 2}, {d, 'GB_SETS_ITERATOR_FROM'}},
|
||||
{if_have_fun, {public_key, short_name_hash, 1}, {d, 'SHORT_NAME_HASH'}},
|
||||
{if_var_true, new_sql_schema, {d, 'NEW_SQL_SCHEMA'}},
|
||||
{if_var_true, hipe, native},
|
||||
{src_dirs, [asn1, src,
|
||||
{src_dirs, [src,
|
||||
{if_var_true, tools, tools},
|
||||
{if_var_true, elixir, include}]}]}.
|
||||
|
||||
@@ -165,9 +162,6 @@
|
||||
{if_var_true, zlib, {"ezlib", []}},
|
||||
{if_var_true, iconv, {"iconv", []}}]}.
|
||||
|
||||
{port_env, [{"CFLAGS", "-g -O2 -Wall"}]}.
|
||||
|
||||
{port_specs, [{"priv/lib/jid.so", ["c_src/jid.c"]}]}.
|
||||
%% Local Variables:
|
||||
%% mode: erlang
|
||||
%% End:
|
||||
|
||||
@@ -329,16 +329,6 @@ CREATE TABLE muc_room_subscribers (
|
||||
CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers(host, jid);
|
||||
CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers(host, room, jid);
|
||||
|
||||
CREATE TABLE irc_custom (
|
||||
jid text NOT NULL,
|
||||
host text NOT NULL,
|
||||
server_host text NOT NULL,
|
||||
data text NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_irc_custom_jid_host ON irc_custom (jid, host);
|
||||
|
||||
CREATE TABLE motd (
|
||||
username text NOT NULL,
|
||||
server_host text NOT NULL,
|
||||
|
||||
@@ -302,15 +302,6 @@ CREATE TABLE muc_room_subscribers (
|
||||
CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers(host, jid);
|
||||
CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers(host, room, jid);
|
||||
|
||||
CREATE TABLE irc_custom (
|
||||
jid text NOT NULL,
|
||||
host text NOT NULL,
|
||||
data text NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_irc_custom_jid_host ON irc_custom (jid, host);
|
||||
|
||||
CREATE TABLE motd (
|
||||
username text PRIMARY KEY,
|
||||
xml text,
|
||||
|
||||
+4
-14
@@ -72,16 +72,6 @@ CREATE TABLE [dbo].[caps_features] (
|
||||
CREATE CLUSTERED INDEX [caps_features_node_subnode] ON [caps_features] (node, subnode)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE TABLE [dbo].[irc_custom] (
|
||||
[jid] [varchar] (255) NOT NULL,
|
||||
[host] [varchar] (255) NOT NULL,
|
||||
[data] [text] NOT NULL,
|
||||
[created_at] [datetime] NOT NULL DEFAULT GETDATE()
|
||||
) TEXTIMAGE_ON [PRIMARY];
|
||||
|
||||
CREATE UNIQUE CLUSTERED INDEX [irc_custom_jid_host] ON [irc_custom] (jid, host)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE TABLE [dbo].[last] (
|
||||
[username] [varchar] (250) NOT NULL,
|
||||
[seconds] [text] NOT NULL,
|
||||
@@ -144,9 +134,9 @@ CREATE TABLE [dbo].[muc_online_users] (
|
||||
node text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE CLUSTERED INDEX [muc_online_users_i] ON [muc_online_users] (username, server, resource, name, host)
|
||||
CREATE UNIQUE INDEX [muc_online_users_i] ON [muc_online_users] (username, server, resource, name, host)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
CREATE UNIQUE CLUSTERED INDEX [muc_online_users_us] ON [muc_online_users] (username, server);
|
||||
CREATE UNIQUE CLUSTERED INDEX [muc_online_users_us] ON [muc_online_users] (username, server)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE TABLE [dbo].[muc_room_subscribers] (
|
||||
@@ -539,7 +529,7 @@ CREATE TABLE [dbo].[bosh] (
|
||||
(
|
||||
[sid] ASC
|
||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
|
||||
) TEXTIMAGE_ON [PRIMARY];
|
||||
);
|
||||
|
||||
CREATE TABLE [dbo].[carboncopy] (
|
||||
[username] [varchar] (255) NOT NULL,
|
||||
@@ -565,5 +555,5 @@ CREATE TABLE [dbo].[push_session] (
|
||||
CREATE UNIQUE CLUSTERED INDEX [i_push_usn] ON [push_session] (username, service, node)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE UNIQUE CLUSTERED INDEX [i_push_ut] ON [push_session] (username, timestamp)
|
||||
CREATE UNIQUE INDEX [i_push_ut] ON [push_session] (username, timestamp)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
+3
-13
@@ -90,7 +90,7 @@ CREATE INDEX i_sr_user_sh_grp ON sr_user(server_host(191), grp);
|
||||
CREATE TABLE spool (
|
||||
username varchar(191) NOT NULL,
|
||||
server_host text NOT NULL,
|
||||
xml BLOB NOT NULL,
|
||||
xml mediumtext NOT NULL,
|
||||
seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
@@ -104,8 +104,8 @@ CREATE TABLE archive (
|
||||
timestamp BIGINT UNSIGNED NOT NULL,
|
||||
peer varchar(191) NOT NULL,
|
||||
bare_peer varchar(191) NOT NULL,
|
||||
xml text NOT NULL,
|
||||
txt text,
|
||||
xml mediumtext NOT NULL,
|
||||
txt mediumtext,
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
|
||||
kind varchar(10),
|
||||
nick varchar(191),
|
||||
@@ -345,16 +345,6 @@ CREATE TABLE muc_room_subscribers (
|
||||
|
||||
CREATE INDEX i_muc_room_subscribers_host_jid USING BTREE ON muc_room_subscribers(host, jid);
|
||||
|
||||
CREATE TABLE irc_custom (
|
||||
jid text NOT NULL,
|
||||
host text NOT NULL,
|
||||
server_host text NOT NULL,
|
||||
data text NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE UNIQUE INDEX i_irc_custom_jid_host USING BTREE ON irc_custom(jid(75), host(75));
|
||||
|
||||
CREATE TABLE motd (
|
||||
username varchar(191) NOT NULL,
|
||||
server_host text NOT NULL,
|
||||
|
||||
+3
-12
@@ -80,7 +80,7 @@ CREATE INDEX i_sr_user_grp ON sr_user(grp);
|
||||
|
||||
CREATE TABLE spool (
|
||||
username varchar(191) NOT NULL,
|
||||
xml BLOB NOT NULL,
|
||||
xml mediumtext NOT NULL,
|
||||
seq BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
@@ -93,8 +93,8 @@ CREATE TABLE archive (
|
||||
timestamp BIGINT UNSIGNED NOT NULL,
|
||||
peer varchar(191) NOT NULL,
|
||||
bare_peer varchar(191) NOT NULL,
|
||||
xml text NOT NULL,
|
||||
txt text,
|
||||
xml mediumtext NOT NULL,
|
||||
txt mediumtext,
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE,
|
||||
kind varchar(10),
|
||||
nick varchar(191),
|
||||
@@ -318,15 +318,6 @@ CREATE TABLE muc_room_subscribers (
|
||||
|
||||
CREATE INDEX i_muc_room_subscribers_host_jid USING BTREE ON muc_room_subscribers(host, jid);
|
||||
|
||||
CREATE TABLE irc_custom (
|
||||
jid text NOT NULL,
|
||||
host text NOT NULL,
|
||||
data text NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE UNIQUE INDEX i_irc_custom_jid_host USING BTREE ON irc_custom(jid(75), host(75));
|
||||
|
||||
CREATE TABLE motd (
|
||||
username varchar(191) PRIMARY KEY,
|
||||
xml text,
|
||||
|
||||
@@ -144,9 +144,6 @@
|
||||
-- ALTER TABLE muc_online_users ADD COLUMN server_host text NOT NULL DEFAULT '<HOST>';
|
||||
-- ALTER TABLE muc_online_users ALTER COLUMN server_host DROP DEFAULT;
|
||||
|
||||
-- ALTER TABLE irc_custom ADD COLUMN server_host text NOT NULL DEFAULT '<HOST>';
|
||||
-- ALTER TABLE irc_custom ALTER COLUMN server_host DROP DEFAULT;
|
||||
|
||||
-- ALTER TABLE motd ADD COLUMN server_host text NOT NULL DEFAULT '<HOST>';
|
||||
-- ALTER TABLE motd DROP CONSTRAINT motd_pkey;
|
||||
-- ALTER TABLE motd ADD PRIMARY KEY (server_host, username);
|
||||
@@ -498,16 +495,6 @@ CREATE TABLE muc_room_subscribers (
|
||||
CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers USING btree (host, jid);
|
||||
CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers USING btree (host, room, jid);
|
||||
|
||||
CREATE TABLE irc_custom (
|
||||
jid text NOT NULL,
|
||||
host text NOT NULL,
|
||||
server_host text NOT NULL,
|
||||
data text NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_irc_custom_jid_host ON irc_custom USING btree (jid, host);
|
||||
|
||||
CREATE TABLE motd (
|
||||
username text NOT NULL,
|
||||
server_host text NOT NULL,
|
||||
|
||||
@@ -320,15 +320,6 @@ CREATE TABLE muc_room_subscribers (
|
||||
CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers USING btree (host, jid);
|
||||
CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers USING btree (host, room, jid);
|
||||
|
||||
CREATE TABLE irc_custom (
|
||||
jid text NOT NULL,
|
||||
host text NOT NULL,
|
||||
data text NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_irc_custom_jid_host ON irc_custom USING btree (jid, host);
|
||||
|
||||
CREATE TABLE motd (
|
||||
username text PRIMARY KEY,
|
||||
xml text,
|
||||
|
||||
+24
-13
@@ -36,14 +36,13 @@
|
||||
any_rules_allowed/3, transform_options/1, opt_type/1,
|
||||
acl_rule_matches/3, acl_rule_verify/1, access_matches/3,
|
||||
transform_access_rules_config/1,
|
||||
parse_ip_netmask/1,
|
||||
parse_ip_netmask/1, ip_matches_mask/3,
|
||||
access_rules_validator/1, shaper_rules_validator/1,
|
||||
normalize_spec/1, resolve_access/2]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("jid.hrl").
|
||||
|
||||
@@ -195,7 +194,7 @@ add_access(Host, Access, Rules) ->
|
||||
-spec load_from_config() -> ok.
|
||||
|
||||
load_from_config() ->
|
||||
Hosts = [global|?MYHOSTS],
|
||||
Hosts = [global|ejabberd_config:get_myhosts()],
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ACLs = ejabberd_config:get_option(
|
||||
@@ -313,7 +312,7 @@ normalize_spec(Spec) ->
|
||||
{ok, Net, Mask} ->
|
||||
{ip, {Net, Mask}};
|
||||
error ->
|
||||
?INFO_MSG("Invalid network address: ~p", [S]),
|
||||
?WARNING_MSG("Invalid network address: ~p", [S]),
|
||||
none
|
||||
end;
|
||||
BadVal ->
|
||||
@@ -447,13 +446,13 @@ acl_rule_matches({acl, Name}, Data, Host) ->
|
||||
RawACLs = lists:map(fun(#acl{aclspec = R}) -> R end, ACLs),
|
||||
any_acl_rules_matches(RawACLs, Data, Host);
|
||||
acl_rule_matches({ip, {Net, Mask}}, #{ip := {IP, _Port}}, _Host) ->
|
||||
is_ip_match(IP, Net, Mask);
|
||||
ip_matches_mask(IP, Net, Mask);
|
||||
acl_rule_matches({ip, {Net, Mask}}, #{ip := IP}, _Host) ->
|
||||
is_ip_match(IP, Net, Mask);
|
||||
ip_matches_mask(IP, Net, Mask);
|
||||
acl_rule_matches({user, {U, S}}, #{usr := {U, S, _}}, _Host) ->
|
||||
true;
|
||||
acl_rule_matches({user, U}, #{usr := {U, S, _}}, _Host) ->
|
||||
lists:member(S, ?MYHOSTS);
|
||||
lists:member(S, ejabberd_config:get_myhosts());
|
||||
acl_rule_matches({server, S}, #{usr := {_, S, _}}, _Host) ->
|
||||
true;
|
||||
acl_rule_matches({resource, R}, #{usr := {_, _, R}}, _Host) ->
|
||||
@@ -467,7 +466,7 @@ acl_rule_matches({shared_group, G}, #{usr := {U, S, _}}, Host) ->
|
||||
acl_rule_matches({user_regexp, {UR, S}}, #{usr := {U, S, _}}, _Host) ->
|
||||
is_regexp_match(U, UR);
|
||||
acl_rule_matches({user_regexp, UR}, #{usr := {U, S, _}}, _Host) ->
|
||||
lists:member(S, ?MYHOSTS) andalso is_regexp_match(U, UR);
|
||||
lists:member(S, ejabberd_config:get_myhosts()) andalso is_regexp_match(U, UR);
|
||||
acl_rule_matches({server_regexp, SR}, #{usr := {_, S, _}}, _Host) ->
|
||||
is_regexp_match(S, SR);
|
||||
acl_rule_matches({resource_regexp, RR}, #{usr := {_, _, R}}, _Host) ->
|
||||
@@ -477,7 +476,7 @@ acl_rule_matches({node_regexp, {UR, SR}}, #{usr := {U, S, _}}, _Host) ->
|
||||
acl_rule_matches({user_glob, {UR, S}}, #{usr := {U, S, _}}, _Host) ->
|
||||
is_glob_match(U, UR);
|
||||
acl_rule_matches({user_glob, UR}, #{usr := {U, S, _}}, _Host) ->
|
||||
lists:member(S, ?MYHOSTS) andalso is_glob_match(U, UR);
|
||||
lists:member(S, ejabberd_config:get_myhosts()) andalso is_glob_match(U, UR);
|
||||
acl_rule_matches({server_glob, SR}, #{usr := {_, S, _}}, _Host) ->
|
||||
is_glob_match(S, SR);
|
||||
acl_rule_matches({resource_glob, RR}, #{usr := {_, _, R}}, _Host) ->
|
||||
@@ -549,18 +548,30 @@ is_glob_match(String, Glob) ->
|
||||
is_regexp_match(String,
|
||||
ejabberd_regexp:sh_to_awk(Glob)).
|
||||
|
||||
is_ip_match({_, _, _, _} = IP, {_, _, _, _} = Net, Mask) ->
|
||||
ip_matches_mask({_, _, _, _} = 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) ->
|
||||
ip_matches_mask({_, _, _, _, _, _, _, _} = 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(_, _, _) ->
|
||||
ip_matches_mask({_, _, _, _} = IP,
|
||||
{0, 0, 0, 0, 0, 16#FFFF, _, _} = Net, Mask) ->
|
||||
IPInt = ip_to_integer({0, 0, 0, 0, 0, 16#FFFF, 0, 0}) + ip_to_integer(IP),
|
||||
NetInt = ip_to_integer(Net),
|
||||
M = bnot (1 bsl (128 - Mask) - 1),
|
||||
IPInt band M =:= NetInt band M;
|
||||
ip_matches_mask({0, 0, 0, 0, 0, 16#FFFF, _, _} = IP,
|
||||
{_, _, _, _} = Net, Mask) ->
|
||||
IPInt = ip_to_integer(IP) - ip_to_integer({0, 0, 0, 0, 0, 16#FFFF, 0, 0}),
|
||||
NetInt = ip_to_integer(Net),
|
||||
M = bnot (1 bsl (32 - Mask) - 1),
|
||||
IPInt band M =:= NetInt band M;
|
||||
ip_matches_mask(_, _, _) ->
|
||||
false.
|
||||
|
||||
ip_to_integer({IP1, IP2, IP3, IP4}) ->
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
%% 3. tls-sni-01: https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-7.4
|
||||
%% 4. (?) oob-01: https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-7.5
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("xmpp.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
|
||||
-230
@@ -1,230 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : Cyrus SASL-like library
|
||||
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
-behaviour(gen_server).
|
||||
|
||||
-export([start_link/0, register_mechanism/3, listmech/1,
|
||||
server_new/7, server_start/3, server_step/2,
|
||||
get_mech/1, format_error/2]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
-record(sasl_mechanism,
|
||||
{mechanism = <<"">> :: mechanism() | '$1',
|
||||
module :: atom(),
|
||||
password_type = plain :: password_type() | '$2'}).
|
||||
|
||||
-type(mechanism() :: binary()).
|
||||
-type(mechanisms() :: [mechanism(),...]).
|
||||
-type(password_type() :: plain | digest | scram).
|
||||
-type sasl_property() :: {username, binary()} |
|
||||
{authzid, binary()} |
|
||||
{mechanism, binary()} |
|
||||
{auth_module, atom()}.
|
||||
-type sasl_return() :: {ok, [sasl_property()]} |
|
||||
{ok, [sasl_property()], binary()} |
|
||||
{continue, binary(), sasl_state()} |
|
||||
{error, atom(), binary()}.
|
||||
|
||||
-type(sasl_mechanism() :: #sasl_mechanism{}).
|
||||
-type error_reason() :: cyrsasl_digest:error_reason() |
|
||||
cyrsasl_oauth:error_reason() |
|
||||
cyrsasl_plain:error_reason() |
|
||||
cyrsasl_scram:error_reason() |
|
||||
unsupported_mechanism | nodeprep_failed |
|
||||
empty_username | aborted.
|
||||
-record(sasl_state,
|
||||
{
|
||||
service,
|
||||
myname,
|
||||
realm,
|
||||
get_password,
|
||||
check_password,
|
||||
check_password_digest,
|
||||
mech_name = <<"">>,
|
||||
mech_mod,
|
||||
mech_state
|
||||
}).
|
||||
-type sasl_state() :: #sasl_state{}.
|
||||
-export_type([mechanism/0, mechanisms/0, sasl_mechanism/0, error_reason/0,
|
||||
sasl_state/0, sasl_return/0, sasl_property/0]).
|
||||
|
||||
-callback start(list()) -> any().
|
||||
-callback stop() -> any().
|
||||
-callback mech_new(binary(), fun(), fun(), fun()) -> any().
|
||||
-callback mech_step(any(), binary()) -> sasl_return().
|
||||
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
init([]) ->
|
||||
ets:new(sasl_mechanism,
|
||||
[named_table, public,
|
||||
{keypos, #sasl_mechanism.mechanism}]),
|
||||
cyrsasl_plain:start([]),
|
||||
cyrsasl_digest:start([]),
|
||||
cyrsasl_scram:start([]),
|
||||
cyrsasl_anonymous:start([]),
|
||||
cyrsasl_oauth:start([]),
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
cyrsasl_plain:stop(),
|
||||
cyrsasl_digest:stop(),
|
||||
cyrsasl_scram:stop(),
|
||||
cyrsasl_anonymous:stop(),
|
||||
cyrsasl_oauth:stop().
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
-spec format_error(mechanism() | sasl_state(), error_reason()) -> {atom(), binary()}.
|
||||
format_error(_, unsupported_mechanism) ->
|
||||
{'invalid-mechanism', <<"Unsupported mechanism">>};
|
||||
format_error(_, nodeprep_failed) ->
|
||||
{'bad-protocol', <<"Nodeprep failed">>};
|
||||
format_error(_, empty_username) ->
|
||||
{'bad-protocol', <<"Empty username">>};
|
||||
format_error(_, aborted) ->
|
||||
{'aborted', <<"Aborted">>};
|
||||
format_error(#sasl_state{mech_mod = Mod}, Reason) ->
|
||||
Mod:format_error(Reason);
|
||||
format_error(Mech, Reason) ->
|
||||
case ets:lookup(sasl_mechanism, Mech) of
|
||||
[#sasl_mechanism{module = Mod}] ->
|
||||
Mod:format_error(Reason);
|
||||
[] ->
|
||||
{'invalid-mechanism', <<"Unsupported mechanism">>}
|
||||
end.
|
||||
|
||||
-spec register_mechanism(Mechanim :: mechanism(), Module :: module(),
|
||||
PasswordType :: password_type()) -> any().
|
||||
|
||||
register_mechanism(Mechanism, Module, PasswordType) ->
|
||||
ets:insert(sasl_mechanism,
|
||||
#sasl_mechanism{mechanism = Mechanism, module = Module,
|
||||
password_type = PasswordType}).
|
||||
|
||||
check_credentials(_State, Props) ->
|
||||
User = proplists:get_value(authzid, Props, <<>>),
|
||||
case jid:nodeprep(User) of
|
||||
error -> {error, nodeprep_failed};
|
||||
<<"">> -> {error, empty_username};
|
||||
_LUser -> ok
|
||||
end.
|
||||
|
||||
-spec listmech(Host ::binary()) -> Mechanisms::mechanisms().
|
||||
|
||||
listmech(Host) ->
|
||||
ets:select(sasl_mechanism,
|
||||
[{#sasl_mechanism{mechanism = '$1',
|
||||
password_type = '$2', _ = '_'},
|
||||
case catch ejabberd_auth:store_type(Host) of
|
||||
external -> [{'==', '$2', plain}];
|
||||
scram -> [{'/=', '$2', digest}];
|
||||
{'EXIT', {undef, [{Module, store_type, []} | _]}} ->
|
||||
?WARNING_MSG("~p doesn't implement the function store_type/0",
|
||||
[Module]),
|
||||
[];
|
||||
_Else -> []
|
||||
end,
|
||||
['$1']}]).
|
||||
|
||||
-spec server_new(binary(), binary(), binary(), term(),
|
||||
fun(), fun(), fun()) -> sasl_state().
|
||||
server_new(Service, ServerFQDN, UserRealm, _SecFlags,
|
||||
GetPassword, CheckPassword, CheckPasswordDigest) ->
|
||||
#sasl_state{service = Service, myname = ServerFQDN,
|
||||
realm = UserRealm, get_password = GetPassword,
|
||||
check_password = CheckPassword,
|
||||
check_password_digest = CheckPasswordDigest}.
|
||||
|
||||
-spec server_start(sasl_state(), mechanism(), binary()) -> sasl_return().
|
||||
server_start(State, Mech, ClientIn) ->
|
||||
case lists:member(Mech,
|
||||
listmech(State#sasl_state.myname))
|
||||
of
|
||||
true ->
|
||||
case ets:lookup(sasl_mechanism, Mech) of
|
||||
[#sasl_mechanism{module = Module}] ->
|
||||
{ok, MechState} =
|
||||
Module:mech_new(State#sasl_state.myname,
|
||||
State#sasl_state.get_password,
|
||||
State#sasl_state.check_password,
|
||||
State#sasl_state.check_password_digest),
|
||||
server_step(State#sasl_state{mech_mod = Module,
|
||||
mech_name = Mech,
|
||||
mech_state = MechState},
|
||||
ClientIn);
|
||||
_ -> {error, unsupported_mechanism, <<"">>}
|
||||
end;
|
||||
false -> {error, unsupported_mechanism, <<"">>}
|
||||
end.
|
||||
|
||||
-spec server_step(sasl_state(), binary()) -> sasl_return().
|
||||
server_step(State, ClientIn) ->
|
||||
Module = State#sasl_state.mech_mod,
|
||||
MechState = State#sasl_state.mech_state,
|
||||
case Module:mech_step(MechState, ClientIn) of
|
||||
{ok, Props} ->
|
||||
case check_credentials(State, Props) of
|
||||
ok -> {ok, Props};
|
||||
{error, Error} -> {error, Error, <<"">>}
|
||||
end;
|
||||
{ok, Props, ServerOut} ->
|
||||
case check_credentials(State, Props) of
|
||||
ok -> {ok, Props, ServerOut};
|
||||
{error, Error} -> {error, Error, <<"">>}
|
||||
end;
|
||||
{continue, ServerOut, NewMechState} ->
|
||||
{continue, ServerOut, State#sasl_state{mech_state = NewMechState}};
|
||||
{error, Error, Username} ->
|
||||
{error, Error, Username};
|
||||
{error, Error} ->
|
||||
{error, Error, <<"">>}
|
||||
end.
|
||||
|
||||
-spec get_mech(sasl_state()) -> binary().
|
||||
get_mech(#sasl_state{mech_name = Mech}) ->
|
||||
Mech.
|
||||
@@ -1,50 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl_anonymous.erl
|
||||
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%% Purpose : ANONYMOUS SASL mechanism
|
||||
%%% See http://www.ietf.org/internet-drafts/draft-ietf-sasl-anon-05.txt
|
||||
%%% Created : 23 Aug 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl_anonymous).
|
||||
|
||||
-protocol({xep, 175, '1.2'}).
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-record(state, {server = <<"">> :: binary()}).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism(<<"ANONYMOUS">>, ?MODULE, plain).
|
||||
|
||||
stop() -> ok.
|
||||
|
||||
mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) ->
|
||||
{ok, #state{server = Host}}.
|
||||
|
||||
mech_step(#state{}, _ClientIn) ->
|
||||
User = iolist_to_binary([randoms:get_string(),
|
||||
integer_to_binary(p1_time_compat:unique_integer([positive]))]),
|
||||
{ok, [{username, User},
|
||||
{authzid, User},
|
||||
{auth_module, ejabberd_auth_anonymous}]}.
|
||||
@@ -1,271 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl_digest.erl
|
||||
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
||||
%%% Purpose : DIGEST-MD5 SASL mechanism
|
||||
%%% Created : 11 Mar 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl_digest).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2,
|
||||
parse/1, format_error/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-type get_password_fun() :: fun((binary()) -> {false, any()} |
|
||||
{binary(), atom()}).
|
||||
-type check_password_fun() :: fun((binary(), binary(), binary(), binary(),
|
||||
fun((binary()) -> binary())) ->
|
||||
{boolean(), any()} |
|
||||
false).
|
||||
-type error_reason() :: parser_failed | invalid_digest_uri |
|
||||
not_authorized | unexpected_response.
|
||||
-export_type([error_reason/0]).
|
||||
|
||||
-record(state, {step = 1 :: 1 | 3 | 5,
|
||||
nonce = <<"">> :: binary(),
|
||||
username = <<"">> :: binary(),
|
||||
authzid = <<"">> :: binary(),
|
||||
get_password :: get_password_fun(),
|
||||
check_password :: check_password_fun(),
|
||||
auth_module :: atom(),
|
||||
host = <<"">> :: binary(),
|
||||
hostfqdn = [] :: [binary()]}).
|
||||
|
||||
start(_Opts) ->
|
||||
Fqdn = get_local_fqdn(),
|
||||
?DEBUG("FQDN used to check DIGEST-MD5 SASL authentication: ~s",
|
||||
[Fqdn]),
|
||||
cyrsasl:register_mechanism(<<"DIGEST-MD5">>, ?MODULE,
|
||||
digest).
|
||||
|
||||
stop() -> ok.
|
||||
|
||||
-spec format_error(error_reason()) -> {atom(), binary()}.
|
||||
format_error(parser_failed) ->
|
||||
{'bad-protocol', <<"Response decoding failed">>};
|
||||
format_error(invalid_digest_uri) ->
|
||||
{'bad-protocol', <<"Invalid digest URI">>};
|
||||
format_error(not_authorized) ->
|
||||
{'not-authorized', <<"Invalid username or password">>};
|
||||
format_error(unexpected_response) ->
|
||||
{'bad-protocol', <<"Unexpected response">>}.
|
||||
|
||||
mech_new(Host, GetPassword, _CheckPassword,
|
||||
CheckPasswordDigest) ->
|
||||
{ok,
|
||||
#state{step = 1, nonce = randoms:get_string(),
|
||||
host = Host, hostfqdn = get_local_fqdn(),
|
||||
get_password = GetPassword,
|
||||
check_password = CheckPasswordDigest}}.
|
||||
|
||||
mech_step(#state{step = 1, nonce = Nonce} = State, _) ->
|
||||
{continue,
|
||||
<<"nonce=\"", Nonce/binary,
|
||||
"\",qop=\"auth\",charset=utf-8,algorithm=md5-sess">>,
|
||||
State#state{step = 3}};
|
||||
mech_step(#state{step = 3, nonce = Nonce} = State,
|
||||
ClientIn) ->
|
||||
case parse(ClientIn) of
|
||||
bad -> {error, parser_failed};
|
||||
KeyVals ->
|
||||
DigestURI = proplists:get_value(<<"digest-uri">>, KeyVals, <<>>),
|
||||
UserName = proplists:get_value(<<"username">>, KeyVals, <<>>),
|
||||
case is_digesturi_valid(DigestURI, State#state.host,
|
||||
State#state.hostfqdn)
|
||||
of
|
||||
false ->
|
||||
?DEBUG("User login not authorized because digest-uri "
|
||||
"seems invalid: ~p (checking for Host "
|
||||
"~p, FQDN ~p)",
|
||||
[DigestURI, State#state.host, State#state.hostfqdn]),
|
||||
{error, invalid_digest_uri, UserName};
|
||||
true ->
|
||||
AuthzId = proplists:get_value(<<"authzid">>, KeyVals, <<>>),
|
||||
case (State#state.get_password)(UserName) of
|
||||
{false, _} -> {error, not_authorized, UserName};
|
||||
{Passwd, AuthModule} ->
|
||||
case (State#state.check_password)(UserName, UserName, <<"">>,
|
||||
proplists:get_value(<<"response">>, KeyVals, <<>>),
|
||||
fun (PW) ->
|
||||
response(KeyVals,
|
||||
UserName,
|
||||
PW,
|
||||
Nonce,
|
||||
AuthzId,
|
||||
<<"AUTHENTICATE">>)
|
||||
end)
|
||||
of
|
||||
{true, _} ->
|
||||
RspAuth = response(KeyVals, UserName, Passwd, Nonce,
|
||||
AuthzId, <<"">>),
|
||||
{continue, <<"rspauth=", RspAuth/binary>>,
|
||||
State#state{step = 5, auth_module = AuthModule,
|
||||
username = UserName,
|
||||
authzid = AuthzId}};
|
||||
false -> {error, not_authorized, UserName};
|
||||
{false, _} -> {error, not_authorized, UserName}
|
||||
end
|
||||
end
|
||||
end
|
||||
end;
|
||||
mech_step(#state{step = 5, auth_module = AuthModule,
|
||||
username = UserName, authzid = AuthzId},
|
||||
<<"">>) ->
|
||||
{ok,
|
||||
[{username, UserName}, {authzid, case AuthzId of
|
||||
<<"">> -> UserName;
|
||||
_ -> AuthzId
|
||||
end
|
||||
},
|
||||
{auth_module, AuthModule}]};
|
||||
mech_step(A, B) ->
|
||||
?DEBUG("SASL DIGEST: A ~p B ~p", [A, B]),
|
||||
{error, unexpected_response}.
|
||||
|
||||
parse(S) -> parse1(binary_to_list(S), "", []).
|
||||
|
||||
parse1([$= | Cs], S, Ts) ->
|
||||
parse2(Cs, lists:reverse(S), "", Ts);
|
||||
parse1([$, | Cs], [], Ts) -> parse1(Cs, [], Ts);
|
||||
parse1([$\s | Cs], [], Ts) -> parse1(Cs, [], Ts);
|
||||
parse1([C | Cs], S, Ts) -> parse1(Cs, [C | S], Ts);
|
||||
parse1([], [], T) -> lists:reverse(T);
|
||||
parse1([], _S, _T) -> bad.
|
||||
|
||||
parse2([$" | Cs], Key, Val, Ts) ->
|
||||
parse3(Cs, Key, Val, Ts);
|
||||
parse2([C | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, [C | Val], Ts);
|
||||
parse2([], _, _, _) -> bad.
|
||||
|
||||
parse3([$" | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, Val, Ts);
|
||||
parse3([$\\, C | Cs], Key, Val, Ts) ->
|
||||
parse3(Cs, Key, [C | Val], Ts);
|
||||
parse3([C | Cs], Key, Val, Ts) ->
|
||||
parse3(Cs, Key, [C | Val], Ts);
|
||||
parse3([], _, _, _) -> bad.
|
||||
|
||||
parse4([$, | Cs], Key, Val, Ts) ->
|
||||
parse1(Cs, "", [{list_to_binary(Key), list_to_binary(lists:reverse(Val))} | Ts]);
|
||||
parse4([$\s | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, Val, Ts);
|
||||
parse4([C | Cs], Key, Val, Ts) ->
|
||||
parse4(Cs, Key, [C | Val], Ts);
|
||||
parse4([], Key, Val, Ts) ->
|
||||
%% @doc Check if the digest-uri is valid.
|
||||
%% RFC-2831 allows to provide the IP address in Host,
|
||||
%% however ejabberd doesn't allow that.
|
||||
%% If the service (for example jabber.example.org)
|
||||
%% is provided by several hosts (being one of them server3.example.org),
|
||||
%% then acceptable digest-uris would be:
|
||||
%% xmpp/server3.example.org/jabber.example.org, xmpp/server3.example.org and
|
||||
%% xmpp/jabber.example.org
|
||||
%% The last version is not actually allowed by the RFC, but implemented by popular clients
|
||||
parse1([], "", [{list_to_binary(Key), list_to_binary(lists:reverse(Val))} | Ts]).
|
||||
|
||||
is_digesturi_valid(DigestURICase, JabberDomain,
|
||||
JabberFQDN) ->
|
||||
DigestURI = stringprep:tolower(DigestURICase),
|
||||
case catch str:tokens(DigestURI, <<"/">>) of
|
||||
[<<"xmpp">>, Host] ->
|
||||
IsHostFqdn = is_host_fqdn(Host, JabberFQDN),
|
||||
(Host == JabberDomain) or IsHostFqdn;
|
||||
[<<"xmpp">>, Host, ServName] ->
|
||||
IsHostFqdn = is_host_fqdn(Host, JabberFQDN),
|
||||
(ServName == JabberDomain) and IsHostFqdn;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
is_host_fqdn(_Host, []) ->
|
||||
false;
|
||||
is_host_fqdn(Host, [Fqdn | _FqdnTail]) when Host == Fqdn ->
|
||||
true;
|
||||
is_host_fqdn(Host, [Fqdn | FqdnTail]) when Host /= Fqdn ->
|
||||
is_host_fqdn(Host, FqdnTail).
|
||||
|
||||
get_local_fqdn() ->
|
||||
case ejabberd_config:get_option(fqdn) of
|
||||
undefined ->
|
||||
{ok, Hostname} = inet:gethostname(),
|
||||
{ok, {hostent, Fqdn, _, _, _, _}} = inet:gethostbyname(Hostname),
|
||||
[list_to_binary(Fqdn)];
|
||||
Fqdn ->
|
||||
Fqdn
|
||||
end.
|
||||
|
||||
hex(S) ->
|
||||
str:to_hexlist(S).
|
||||
|
||||
proplists_get_bin_value(Key, Pairs, Default) ->
|
||||
case proplists:get_value(Key, Pairs, Default) of
|
||||
L when is_list(L) ->
|
||||
list_to_binary(L);
|
||||
L2 ->
|
||||
L2
|
||||
end.
|
||||
|
||||
response(KeyVals, User, Passwd, Nonce, AuthzId,
|
||||
A2Prefix) ->
|
||||
Realm = proplists_get_bin_value(<<"realm">>, KeyVals, <<>>),
|
||||
CNonce = proplists_get_bin_value(<<"cnonce">>, KeyVals, <<>>),
|
||||
DigestURI = proplists_get_bin_value(<<"digest-uri">>, KeyVals, <<>>),
|
||||
NC = proplists_get_bin_value(<<"nc">>, KeyVals, <<>>),
|
||||
QOP = proplists_get_bin_value(<<"qop">>, KeyVals, <<>>),
|
||||
MD5Hash = erlang:md5(<<User/binary, ":", Realm/binary, ":",
|
||||
Passwd/binary>>),
|
||||
A1 = case AuthzId of
|
||||
<<"">> ->
|
||||
<<MD5Hash/binary, ":", Nonce/binary, ":", CNonce/binary>>;
|
||||
_ ->
|
||||
<<MD5Hash/binary, ":", Nonce/binary, ":", CNonce/binary, ":",
|
||||
AuthzId/binary>>
|
||||
end,
|
||||
A2 = case QOP of
|
||||
<<"auth">> ->
|
||||
<<A2Prefix/binary, ":", DigestURI/binary>>;
|
||||
_ ->
|
||||
<<A2Prefix/binary, ":", DigestURI/binary,
|
||||
":00000000000000000000000000000000">>
|
||||
end,
|
||||
T = <<(hex((erlang:md5(A1))))/binary, ":", Nonce/binary,
|
||||
":", NC/binary, ":", CNonce/binary, ":", QOP/binary,
|
||||
":", (hex((erlang:md5(A2))))/binary>>,
|
||||
hex((erlang:md5(T))).
|
||||
|
||||
-spec opt_type(fqdn) -> fun((binary() | [binary()]) -> [binary()]);
|
||||
(atom()) -> [atom()].
|
||||
opt_type(fqdn) ->
|
||||
fun(FQDN) when is_binary(FQDN) ->
|
||||
[FQDN];
|
||||
(FQDNs) when is_list(FQDNs) ->
|
||||
[iolist_to_binary(FQDN) || FQDN <- FQDNs]
|
||||
end;
|
||||
opt_type(_) -> [fqdn].
|
||||
@@ -1,104 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl_oauth.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : X-OAUTH2 SASL mechanism
|
||||
%%% Created : 17 Sep 2015 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl_oauth).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2, parse/1, format_error/1]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-record(state, {host}).
|
||||
-type error_reason() :: parser_failed | not_authorized.
|
||||
-export_type([error_reason/0]).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism(<<"X-OAUTH2">>, ?MODULE, plain).
|
||||
|
||||
stop() -> ok.
|
||||
|
||||
-spec format_error(error_reason()) -> {atom(), binary()}.
|
||||
format_error(parser_failed) ->
|
||||
{'bad-protocol', <<"Response decoding failed">>};
|
||||
format_error(not_authorized) ->
|
||||
{'not-authorized', <<"Invalid token">>}.
|
||||
|
||||
mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) ->
|
||||
{ok, #state{host = Host}}.
|
||||
|
||||
mech_step(State, ClientIn) ->
|
||||
case prepare(ClientIn) of
|
||||
[AuthzId, User, Token] ->
|
||||
case ejabberd_oauth:check_token(
|
||||
User, State#state.host, [<<"sasl_auth">>], Token) of
|
||||
true ->
|
||||
{ok,
|
||||
[{username, User}, {authzid, AuthzId},
|
||||
{auth_module, ejabberd_oauth}]};
|
||||
_ ->
|
||||
{error, not_authorized, User}
|
||||
end;
|
||||
_ -> {error, parser_failed}
|
||||
end.
|
||||
|
||||
prepare(ClientIn) ->
|
||||
case parse(ClientIn) of
|
||||
[<<"">>, UserMaybeDomain, Token] ->
|
||||
case parse_domain(UserMaybeDomain) of
|
||||
%% <NUL>login@domain<NUL>pwd
|
||||
[User, _Domain] -> [User, User, Token];
|
||||
%% <NUL>login<NUL>pwd
|
||||
[User] -> [User, User, Token]
|
||||
end;
|
||||
%% login@domain<NUL>login<NUL>pwd
|
||||
[AuthzId, User, Token] ->
|
||||
case parse_domain(AuthzId) of
|
||||
%% login@domain<NUL>login<NUL>pwd
|
||||
[AuthzUser, _Domain] -> [AuthzUser, User, Token];
|
||||
%% login<NUL>login<NUL>pwd
|
||||
[AuthzUser] -> [AuthzUser, User, Token]
|
||||
end;
|
||||
_ -> error
|
||||
end.
|
||||
|
||||
parse(S) -> parse1(binary_to_list(S), "", []).
|
||||
|
||||
parse1([0 | Cs], S, T) ->
|
||||
parse1(Cs, "", [list_to_binary(lists:reverse(S)) | T]);
|
||||
parse1([C | Cs], S, T) -> parse1(Cs, [C | S], T);
|
||||
%parse1([], [], T) ->
|
||||
% lists:reverse(T);
|
||||
parse1([], S, T) ->
|
||||
lists:reverse([list_to_binary(lists:reverse(S)) | T]).
|
||||
|
||||
parse_domain(S) -> parse_domain1(binary_to_list(S), "", []).
|
||||
|
||||
parse_domain1([$@ | Cs], S, T) ->
|
||||
parse_domain1(Cs, "", [list_to_binary(lists:reverse(S)) | T]);
|
||||
parse_domain1([C | Cs], S, T) ->
|
||||
parse_domain1(Cs, [C | S], T);
|
||||
parse_domain1([], S, T) ->
|
||||
lists:reverse([list_to_binary(lists:reverse(S)) | T]).
|
||||
@@ -1,94 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl_plain.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : PLAIN SASL mechanism
|
||||
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl_plain).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2, parse/1, format_error/1]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-record(state, {check_password}).
|
||||
-type error_reason() :: parser_failed | not_authorized.
|
||||
-export_type([error_reason/0]).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism(<<"PLAIN">>, ?MODULE, plain).
|
||||
|
||||
stop() -> ok.
|
||||
|
||||
-spec format_error(error_reason()) -> {atom(), binary()}.
|
||||
format_error(parser_failed) ->
|
||||
{'bad-protocol', <<"Response decoding failed">>};
|
||||
format_error(not_authorized) ->
|
||||
{'not-authorized', <<"Invalid username or password">>}.
|
||||
|
||||
mech_new(_Host, _GetPassword, CheckPassword, _CheckPasswordDigest) ->
|
||||
{ok, #state{check_password = CheckPassword}}.
|
||||
|
||||
mech_step(State, ClientIn) ->
|
||||
case prepare(ClientIn) of
|
||||
[AuthzId, User, Password] ->
|
||||
case (State#state.check_password)(User, AuthzId, Password) of
|
||||
{true, AuthModule} ->
|
||||
{ok,
|
||||
[{username, User}, {authzid, AuthzId},
|
||||
{auth_module, AuthModule}]};
|
||||
_ -> {error, not_authorized, User}
|
||||
end;
|
||||
_ -> {error, parser_failed}
|
||||
end.
|
||||
|
||||
prepare(ClientIn) ->
|
||||
case parse(ClientIn) of
|
||||
[<<"">>, UserMaybeDomain, Password] ->
|
||||
case parse_domain(UserMaybeDomain) of
|
||||
%% <NUL>login@domain<NUL>pwd
|
||||
[User, _Domain] -> [User, User, Password];
|
||||
%% <NUL>login<NUL>pwd
|
||||
[User] -> [User, User, Password]
|
||||
end;
|
||||
[AuthzId, User, Password] ->
|
||||
case parse_domain(AuthzId) of
|
||||
%% login@domain<NUL>login<NUL>pwd
|
||||
[AuthzUser, _Domain] -> [AuthzUser, User, Password];
|
||||
%% login<NUL>login<NUL>pwd
|
||||
[AuthzUser] -> [AuthzUser, User, Password]
|
||||
end;
|
||||
_ -> error
|
||||
end.
|
||||
|
||||
parse(S) ->
|
||||
binary:split(S, <<0>>, [global]).
|
||||
|
||||
parse_domain(S) -> parse_domain1(binary_to_list(S), "", []).
|
||||
|
||||
parse_domain1([$@ | Cs], S, T) ->
|
||||
parse_domain1(Cs, "", [list_to_binary(lists:reverse(S)) | T]);
|
||||
parse_domain1([C | Cs], S, T) ->
|
||||
parse_domain1(Cs, [C | S], T);
|
||||
parse_domain1([], S, T) ->
|
||||
lists:reverse([list_to_binary(lists:reverse(S)) | T]).
|
||||
@@ -1,249 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : cyrsasl_scram.erl
|
||||
%%% Author : Stephen Röttger <stephen.roettger@googlemail.com>
|
||||
%%% Purpose : SASL SCRAM authentication
|
||||
%%% Created : 7 Aug 2011 by Stephen Röttger <stephen.roettger@googlemail.com>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(cyrsasl_scram).
|
||||
|
||||
-author('stephen.roettger@googlemail.com').
|
||||
|
||||
-protocol({rfc, 5802}).
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2, format_error/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-record(state,
|
||||
{step = 2 :: 2 | 4,
|
||||
stored_key = <<"">> :: binary(),
|
||||
server_key = <<"">> :: binary(),
|
||||
username = <<"">> :: binary(),
|
||||
auth_module :: module(),
|
||||
get_password :: fun((binary()) ->
|
||||
{false | ejabberd_auth:password(), module()}),
|
||||
auth_message = <<"">> :: binary(),
|
||||
client_nonce = <<"">> :: binary(),
|
||||
server_nonce = <<"">> :: binary()}).
|
||||
|
||||
-define(SALT_LENGTH, 16).
|
||||
-define(NONCE_LENGTH, 16).
|
||||
|
||||
-type error_reason() :: unsupported_extension | bad_username |
|
||||
not_authorized | saslprep_failed |
|
||||
parser_failed | bad_attribute |
|
||||
nonce_mismatch | bad_channel_binding.
|
||||
|
||||
-export_type([error_reason/0]).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism(<<"SCRAM-SHA-1">>, ?MODULE,
|
||||
scram).
|
||||
|
||||
stop() -> ok.
|
||||
|
||||
-spec format_error(error_reason()) -> {atom(), binary()}.
|
||||
format_error(unsupported_extension) ->
|
||||
{'bad-protocol', <<"Unsupported extension">>};
|
||||
format_error(bad_username) ->
|
||||
{'invalid-authzid', <<"Malformed username">>};
|
||||
format_error(not_authorized) ->
|
||||
{'not-authorized', <<"Invalid username or password">>};
|
||||
format_error(saslprep_failed) ->
|
||||
{'not-authorized', <<"SASLprep failed">>};
|
||||
format_error(parser_failed) ->
|
||||
{'bad-protocol', <<"Response decoding failed">>};
|
||||
format_error(bad_attribute) ->
|
||||
{'bad-protocol', <<"Malformed or unexpected attribute">>};
|
||||
format_error(nonce_mismatch) ->
|
||||
{'bad-protocol', <<"Nonce mismatch">>};
|
||||
format_error(bad_channel_binding) ->
|
||||
{'bad-protocol', <<"Invalid channel binding">>}.
|
||||
|
||||
mech_new(_Host, GetPassword, _CheckPassword,
|
||||
_CheckPasswordDigest) ->
|
||||
{ok, #state{step = 2, get_password = GetPassword}}.
|
||||
|
||||
mech_step(#state{step = 2} = State, ClientIn) ->
|
||||
case re:split(ClientIn, <<",">>, [{return, binary}]) of
|
||||
[_CBind, _AuthorizationIdentity, _UserNameAttribute, _ClientNonceAttribute, ExtensionAttribute | _]
|
||||
when ExtensionAttribute /= <<"">> ->
|
||||
{error, unsupported_extension};
|
||||
[CBind, _AuthorizationIdentity, UserNameAttribute, ClientNonceAttribute | _]
|
||||
when (CBind == <<"y">>) or (CBind == <<"n">>) ->
|
||||
case parse_attribute(UserNameAttribute) of
|
||||
{error, Reason} -> {error, Reason};
|
||||
{_, EscapedUserName} ->
|
||||
case unescape_username(EscapedUserName) of
|
||||
error -> {error, bad_username};
|
||||
UserName ->
|
||||
case parse_attribute(ClientNonceAttribute) of
|
||||
{$r, ClientNonce} ->
|
||||
{Pass, AuthModule} = (State#state.get_password)(UserName),
|
||||
LPass = if is_binary(Pass) -> jid:resourceprep(Pass);
|
||||
true -> Pass
|
||||
end,
|
||||
if Pass == false ->
|
||||
{error, not_authorized, UserName};
|
||||
LPass == error ->
|
||||
{error, saslprep_failed, UserName};
|
||||
true ->
|
||||
{StoredKey, ServerKey, Salt, IterationCount} =
|
||||
if is_record(Pass, scram) ->
|
||||
{base64:decode(Pass#scram.storedkey),
|
||||
base64:decode(Pass#scram.serverkey),
|
||||
base64:decode(Pass#scram.salt),
|
||||
Pass#scram.iterationcount};
|
||||
true ->
|
||||
TempSalt =
|
||||
randoms:bytes(?SALT_LENGTH),
|
||||
SaltedPassword =
|
||||
scram:salted_password(Pass,
|
||||
TempSalt,
|
||||
?SCRAM_DEFAULT_ITERATION_COUNT),
|
||||
{scram:stored_key(scram:client_key(SaltedPassword)),
|
||||
scram:server_key(SaltedPassword),
|
||||
TempSalt,
|
||||
?SCRAM_DEFAULT_ITERATION_COUNT}
|
||||
end,
|
||||
ClientFirstMessageBare =
|
||||
str:substr(ClientIn,
|
||||
str:str(ClientIn, <<"n=">>)),
|
||||
ServerNonce =
|
||||
base64:encode(randoms:bytes(?NONCE_LENGTH)),
|
||||
ServerFirstMessage =
|
||||
iolist_to_binary(
|
||||
["r=",
|
||||
ClientNonce,
|
||||
ServerNonce,
|
||||
",", "s=",
|
||||
base64:encode(Salt),
|
||||
",", "i=",
|
||||
integer_to_list(IterationCount)]),
|
||||
{continue, ServerFirstMessage,
|
||||
State#state{step = 4, stored_key = StoredKey,
|
||||
server_key = ServerKey,
|
||||
auth_module = AuthModule,
|
||||
auth_message =
|
||||
<<ClientFirstMessageBare/binary,
|
||||
",", ServerFirstMessage/binary>>,
|
||||
client_nonce = ClientNonce,
|
||||
server_nonce = ServerNonce,
|
||||
username = UserName}}
|
||||
end;
|
||||
_ -> {error, bad_attribute}
|
||||
end
|
||||
end
|
||||
end;
|
||||
_Else -> {error, parser_failed}
|
||||
end;
|
||||
mech_step(#state{step = 4} = State, ClientIn) ->
|
||||
case str:tokens(ClientIn, <<",">>) of
|
||||
[GS2ChannelBindingAttribute, NonceAttribute,
|
||||
ClientProofAttribute] ->
|
||||
case parse_attribute(GS2ChannelBindingAttribute) of
|
||||
{$c, CVal} ->
|
||||
ChannelBindingSupport = try binary:first(base64:decode(CVal))
|
||||
catch _:badarg -> 0
|
||||
end,
|
||||
if (ChannelBindingSupport == $n)
|
||||
or (ChannelBindingSupport == $y) ->
|
||||
Nonce = <<(State#state.client_nonce)/binary,
|
||||
(State#state.server_nonce)/binary>>,
|
||||
case parse_attribute(NonceAttribute) of
|
||||
{$r, CompareNonce} when CompareNonce == Nonce ->
|
||||
case parse_attribute(ClientProofAttribute) of
|
||||
{$p, ClientProofB64} ->
|
||||
ClientProof = try base64:decode(ClientProofB64)
|
||||
catch _:badarg -> <<>>
|
||||
end,
|
||||
AuthMessage = iolist_to_binary(
|
||||
[State#state.auth_message,
|
||||
",",
|
||||
str:substr(ClientIn, 1,
|
||||
str:str(ClientIn, <<",p=">>)
|
||||
- 1)]),
|
||||
ClientSignature =
|
||||
scram:client_signature(State#state.stored_key,
|
||||
AuthMessage),
|
||||
ClientKey = scram:client_key(ClientProof,
|
||||
ClientSignature),
|
||||
CompareStoredKey = scram:stored_key(ClientKey),
|
||||
if CompareStoredKey == State#state.stored_key ->
|
||||
ServerSignature =
|
||||
scram:server_signature(State#state.server_key,
|
||||
AuthMessage),
|
||||
{ok, [{username, State#state.username},
|
||||
{auth_module, State#state.auth_module},
|
||||
{authzid, State#state.username}],
|
||||
<<"v=",
|
||||
(base64:encode(ServerSignature))/binary>>};
|
||||
true -> {error, not_authorized, State#state.username}
|
||||
end;
|
||||
_ -> {error, bad_attribute}
|
||||
end;
|
||||
{$r, _} -> {error, nonce_mismatch};
|
||||
_ -> {error, bad_attribute}
|
||||
end;
|
||||
true -> {error, bad_channel_binding}
|
||||
end;
|
||||
_ -> {error, bad_attribute}
|
||||
end;
|
||||
_ -> {error, parser_failed}
|
||||
end.
|
||||
|
||||
parse_attribute(<<Name, $=, Val/binary>>) when Val /= <<>> ->
|
||||
case is_alpha(Name) of
|
||||
true -> {Name, Val};
|
||||
false -> {error, bad_attribute}
|
||||
end;
|
||||
parse_attribute(_) ->
|
||||
{error, bad_attribute}.
|
||||
|
||||
unescape_username(<<"">>) -> <<"">>;
|
||||
unescape_username(EscapedUsername) ->
|
||||
Pos = str:str(EscapedUsername, <<"=">>),
|
||||
if Pos == 0 -> EscapedUsername;
|
||||
true ->
|
||||
Start = str:substr(EscapedUsername, 1, Pos - 1),
|
||||
End = str:substr(EscapedUsername, Pos),
|
||||
EndLen = byte_size(End),
|
||||
if EndLen < 3 -> error;
|
||||
true ->
|
||||
case str:substr(End, 1, 3) of
|
||||
<<"=2C">> ->
|
||||
<<Start/binary, ",",
|
||||
(unescape_username(str:substr(End, 4)))/binary>>;
|
||||
<<"=3D">> ->
|
||||
<<Start/binary, "=",
|
||||
(unescape_username(str:substr(End, 4)))/binary>>;
|
||||
_Else -> error
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
is_alpha(Char) when Char >= $a, Char =< $z -> true;
|
||||
is_alpha(Char) when Char >= $A, Char =< $Z -> true;
|
||||
is_alpha(_) -> false.
|
||||
+1
-1
@@ -143,7 +143,7 @@ exit_or_halt(Reason, StartFlag) ->
|
||||
end.
|
||||
|
||||
sleep(N) ->
|
||||
timer:sleep(randoms:uniform(N)).
|
||||
timer:sleep(p1_rand:uniform(N)).
|
||||
|
||||
get_module_file(App, Mod) ->
|
||||
BaseName = atom_to_list(Mod),
|
||||
|
||||
@@ -506,7 +506,7 @@ is_valid_command_name2(<<>>) ->
|
||||
true;
|
||||
is_valid_command_name2(<<K:8, Rest/binary>>) when (K >= $a andalso K =< $z)
|
||||
orelse (K >= $0 andalso K =< $9)
|
||||
orelse K == $_ ->
|
||||
orelse K == $_ orelse K == $- ->
|
||||
is_valid_command_name2(Rest);
|
||||
is_valid_command_name2(_) ->
|
||||
false.
|
||||
|
||||
+19
-15
@@ -3,7 +3,8 @@
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
%% ejabberdctl commands
|
||||
-export([get_certificates/1,
|
||||
-export([get_commands_spec/0,
|
||||
get_certificates/1,
|
||||
renew_certificates/0,
|
||||
list_certificates/1,
|
||||
revoke_certificate/1]).
|
||||
@@ -19,7 +20,6 @@
|
||||
terminate/2, code_change/3]).
|
||||
-export([start_link/0, opt_type/1, register_certfiles/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("xmpp.hrl").
|
||||
-include("ejabberd_commands.hrl").
|
||||
@@ -120,7 +120,7 @@ get_commands_spec() ->
|
||||
args = [],
|
||||
result = {certificates, string}},
|
||||
#ejabberd_commands{name = list_certificates, tags = [acme],
|
||||
desc = "Lists all curently handled certificates and "
|
||||
desc = "Lists all currently handled certificates and "
|
||||
"their respective domains in {plain|verbose} format",
|
||||
module = ?MODULE, function = list_certificates,
|
||||
args_desc = ["Whether to print the whole certificate "
|
||||
@@ -151,7 +151,8 @@ get_certificates(Domains) ->
|
||||
throw:Throw ->
|
||||
Throw;
|
||||
E:R ->
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]),
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
|
||||
{error, get_certificates}
|
||||
end;
|
||||
false ->
|
||||
@@ -243,7 +244,8 @@ get_certificate(CAUrl, DomainName, PrivateKey) ->
|
||||
throw:Throw ->
|
||||
Throw;
|
||||
E:R ->
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]),
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
|
||||
{error, DomainName, get_certificate}
|
||||
end.
|
||||
|
||||
@@ -382,7 +384,8 @@ renew_certificates() ->
|
||||
throw:Throw ->
|
||||
Throw;
|
||||
E:R ->
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]),
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
|
||||
{error, get_certificates}
|
||||
end.
|
||||
|
||||
@@ -447,7 +450,8 @@ list_certificates(Verbose) ->
|
||||
throw:Throw ->
|
||||
Throw;
|
||||
E:R ->
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]),
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
|
||||
{error, list_certificates}
|
||||
end;
|
||||
false ->
|
||||
@@ -489,7 +493,8 @@ format_certificate(DataCert, Verbose) ->
|
||||
end
|
||||
catch
|
||||
E:R ->
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]),
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
|
||||
fail_format_certificate(DomainName)
|
||||
end.
|
||||
|
||||
@@ -614,7 +619,8 @@ revoke_certificates(DomainOrFile) ->
|
||||
throw:Throw ->
|
||||
Throw;
|
||||
E:R ->
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]),
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
|
||||
{error, revoke_certificate}
|
||||
end.
|
||||
|
||||
@@ -1118,7 +1124,8 @@ save_certificate({ok, DomainName, Cert}) ->
|
||||
throw:Throw ->
|
||||
Throw;
|
||||
E:R ->
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, erlang:get_stacktrace()]),
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
|
||||
{error, DomainName, saving}
|
||||
end.
|
||||
|
||||
@@ -1215,15 +1222,12 @@ generate_key() ->
|
||||
%% Option Parsing Code
|
||||
%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
-spec opt_type(acme) -> fun((acme_config()) -> (acme_config()));
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(acme) ->
|
||||
fun(L) ->
|
||||
lists:map(
|
||||
fun({ca_url, URL}) ->
|
||||
URL1 = binary_to_list(URL),
|
||||
{ok, _} = http_uri:parse(URL1),
|
||||
{ca_url, URL1};
|
||||
{ca_url, misc:try_url(URL)};
|
||||
({contact, Contact}) ->
|
||||
[<<_, _/binary>>, <<_, _/binary>>] =
|
||||
binary:split(Contact, <<":">>),
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
%% delete_authz/3
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("xmpp.hrl").
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_commands.hrl").
|
||||
|
||||
@@ -276,6 +275,8 @@ get_commands_spec() ->
|
||||
|
||||
#ejabberd_commands{name = import_prosody, tags = [mnesia, sql, riak],
|
||||
desc = "Import data from Prosody",
|
||||
longdesc = "Note: this method requires ejabberd compiled with optional tools support "
|
||||
"and package must provide optional luerl dependency.",
|
||||
module = prosody2ejabberd, function = from_dir,
|
||||
args_desc = ["Full path to the Prosody data directory"],
|
||||
args_example = ["/var/lib/prosody/datadump/"],
|
||||
@@ -412,7 +413,7 @@ stop_kindly(DelaySeconds, AnnouncementTextString) ->
|
||||
ejabberd_listener, stop_listeners, []},
|
||||
{"Sending announcement to connected users",
|
||||
mod_announce, send_announcement_to_all,
|
||||
[?MYNAME, Subject, AnnouncementText]},
|
||||
[ejabberd_config:get_myname(), Subject, AnnouncementText]},
|
||||
{"Sending service message to MUC rooms",
|
||||
ejabberd_admin, send_service_message_all_mucs,
|
||||
[Subject, AnnouncementText]},
|
||||
@@ -446,7 +447,7 @@ send_service_message_all_mucs(Subject, AnnouncementText) ->
|
||||
ServerHost, mod_muc, <<"conference.@HOST@">>),
|
||||
mod_muc:broadcast_service_message(ServerHost, MUCHost, Message)
|
||||
end,
|
||||
?MYHOSTS).
|
||||
ejabberd_config:get_myhosts()).
|
||||
|
||||
%%%
|
||||
%%% ejabberd_update
|
||||
@@ -499,7 +500,7 @@ registered_users(Host) ->
|
||||
lists:map(fun({U, _S}) -> U end, SUsers).
|
||||
|
||||
registered_vhosts() ->
|
||||
?MYHOSTS.
|
||||
ejabberd_config:get_myhosts().
|
||||
|
||||
reload_config() ->
|
||||
ejabberd_config:reload_file().
|
||||
@@ -549,13 +550,13 @@ delete_expired_messages() ->
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
{atomic, ok} = mod_offline:remove_expired_messages(Host)
|
||||
end, ?MYHOSTS).
|
||||
end, ejabberd_config:get_myhosts()).
|
||||
|
||||
delete_old_messages(Days) ->
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
{atomic, _} = mod_offline:remove_old_messages(Days, Host)
|
||||
end, ?MYHOSTS).
|
||||
end, ejabberd_config:get_myhosts()).
|
||||
|
||||
%%%
|
||||
%%% Mnesia management
|
||||
@@ -622,13 +623,12 @@ keep_tables() ->
|
||||
%% loaded modules
|
||||
keep_modules_tables() ->
|
||||
lists:map(fun(Module) -> module_tables(Module) end,
|
||||
gen_mod:loaded_modules(?MYNAME)).
|
||||
gen_mod:loaded_modules(ejabberd_config:get_myname())).
|
||||
|
||||
%% TODO: This mapping should probably be moved to a callback function in each
|
||||
%% module.
|
||||
%% Mapping between modules and their tables
|
||||
module_tables(mod_announce) -> [motd, motd_users];
|
||||
module_tables(mod_irc) -> [irc_custom];
|
||||
module_tables(mod_last) -> [last_activity];
|
||||
module_tables(mod_muc) -> [muc_room, muc_registered];
|
||||
module_tables(mod_offline) -> [offline_msg];
|
||||
|
||||
+24
-18
@@ -31,7 +31,6 @@
|
||||
|
||||
-export([start/2, prep_stop/1, stop/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
%%%
|
||||
@@ -46,22 +45,28 @@ start(normal, _Args) ->
|
||||
start_elixir_application(),
|
||||
ejabberd:check_app(ejabberd),
|
||||
setup_if_elixir_conf_used(),
|
||||
ejabberd_config:start(),
|
||||
ejabberd_mnesia:start(),
|
||||
file_queue_init(),
|
||||
maybe_add_nameservers(),
|
||||
ejabberd_system_monitor:start(),
|
||||
case ejabberd_sup:start_link() of
|
||||
{ok, SupPid} ->
|
||||
register_elixir_config_hooks(),
|
||||
ejabberd_cluster:wait_for_sync(infinity),
|
||||
{T2, _} = statistics(wall_clock),
|
||||
?INFO_MSG("ejabberd ~s is started in the node ~p in ~.2fs",
|
||||
[?VERSION, node(), (T2-T1)/1000]),
|
||||
lists:foreach(fun erlang:garbage_collect/1, processes()),
|
||||
{ok, SupPid};
|
||||
Err ->
|
||||
?CRITICAL_MSG("Failed to start ejabberd application: ~p", [Err]),
|
||||
case ejabberd_config:start() of
|
||||
ok ->
|
||||
ejabberd_mnesia:start(),
|
||||
file_queue_init(),
|
||||
maybe_add_nameservers(),
|
||||
case ejabberd_sup:start_link() of
|
||||
{ok, SupPid} ->
|
||||
ejabberd_system_monitor:start(),
|
||||
register_elixir_config_hooks(),
|
||||
ejabberd_cluster:wait_for_sync(infinity),
|
||||
{T2, _} = statistics(wall_clock),
|
||||
?INFO_MSG("ejabberd ~s is started in the node ~p in ~.2fs",
|
||||
[ejabberd_config:get_version(),
|
||||
node(), (T2-T1)/1000]),
|
||||
lists:foreach(fun erlang:garbage_collect/1, processes()),
|
||||
{ok, SupPid};
|
||||
Err ->
|
||||
?CRITICAL_MSG("Failed to start ejabberd application: ~p", [Err]),
|
||||
ejabberd:halt()
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?CRITICAL_MSG("Failed to start ejabberd application: ~p", [Reason]),
|
||||
ejabberd:halt()
|
||||
end;
|
||||
start(_, _) ->
|
||||
@@ -78,7 +83,8 @@ prep_stop(State) ->
|
||||
|
||||
%% All the processes were killed when this function is called
|
||||
stop(_State) ->
|
||||
?INFO_MSG("ejabberd ~s is stopped in the node ~p", [?VERSION, node()]),
|
||||
?INFO_MSG("ejabberd ~s is stopped in the node ~p",
|
||||
[ejabberd_config:get_version(), node()]),
|
||||
delete_pid_file(),
|
||||
%%ejabberd_debug:stop(),
|
||||
ok.
|
||||
|
||||
+50
-34
@@ -48,7 +48,7 @@
|
||||
|
||||
-export([auth_modules/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("scram.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-define(AUTH_CACHE, auth_cache).
|
||||
@@ -69,6 +69,7 @@
|
||||
|
||||
-callback start(binary()) -> any().
|
||||
-callback stop(binary()) -> any().
|
||||
-callback reload(binary()) -> any().
|
||||
-callback plain_password_required(binary()) -> boolean().
|
||||
-callback store_type(binary()) -> plain | external | scram.
|
||||
-callback set_password(binary(), binary(), binary()) -> ok | {error, atom()}.
|
||||
@@ -82,7 +83,8 @@
|
||||
-callback use_cache(binary()) -> boolean().
|
||||
-callback cache_nodes(binary()) -> boolean().
|
||||
|
||||
-optional_callbacks([set_password/3,
|
||||
-optional_callbacks([reload/1,
|
||||
set_password/3,
|
||||
remove_user/2,
|
||||
user_exists/2,
|
||||
check_password/4,
|
||||
@@ -105,7 +107,7 @@ init([]) ->
|
||||
fun(Host, Acc) ->
|
||||
Modules = auth_modules(Host),
|
||||
maps:put(Host, Modules, Acc)
|
||||
end, #{}, ?MYHOSTS),
|
||||
end, #{}, ejabberd_config:get_myhosts()),
|
||||
lists:foreach(
|
||||
fun({Host, Modules}) ->
|
||||
start(Host, Modules)
|
||||
@@ -130,14 +132,16 @@ handle_cast({host_down, Host}, #state{host_modules = HostModules} = State) ->
|
||||
init_cache(NewHostModules),
|
||||
{noreply, State#state{host_modules = NewHostModules}};
|
||||
handle_cast(config_reloaded, #state{host_modules = HostModules} = State) ->
|
||||
NewHostModules = lists:foldl(
|
||||
fun(Host, Acc) ->
|
||||
OldModules = maps:get(Host, HostModules, []),
|
||||
NewModules = auth_modules(Host),
|
||||
start(Host, NewModules -- OldModules),
|
||||
stop(Host, OldModules -- NewModules),
|
||||
maps:put(Host, NewModules, Acc)
|
||||
end, HostModules, ?MYHOSTS),
|
||||
NewHostModules =
|
||||
lists:foldl(
|
||||
fun(Host, Acc) ->
|
||||
OldModules = maps:get(Host, HostModules, []),
|
||||
NewModules = auth_modules(Host),
|
||||
start(Host, NewModules -- OldModules),
|
||||
stop(Host, OldModules -- NewModules),
|
||||
reload(Host, misc:intersection(OldModules, NewModules)),
|
||||
maps:put(Host, NewModules, Acc)
|
||||
end, HostModules, ejabberd_config:get_myhosts()),
|
||||
init_cache(NewHostModules),
|
||||
{noreply, State#state{host_modules = NewHostModules}};
|
||||
handle_cast(Msg, State) ->
|
||||
@@ -165,6 +169,15 @@ start(Host, Modules) ->
|
||||
stop(Host, Modules) ->
|
||||
lists:foreach(fun(M) -> M:stop(Host) end, Modules).
|
||||
|
||||
reload(Host, Modules) ->
|
||||
lists:foreach(
|
||||
fun(M) ->
|
||||
case erlang:function_exported(M, reload, 1) of
|
||||
true -> M:reload(Host);
|
||||
false -> ok
|
||||
end
|
||||
end, Modules).
|
||||
|
||||
host_up(Host) ->
|
||||
gen_server:cast(?MODULE, {host_up, Host}).
|
||||
|
||||
@@ -217,17 +230,22 @@ check_password_with_authmodule(User, AuthzId, Server, Password) ->
|
||||
check_password_with_authmodule(User, AuthzId, Server, Password, Digest, DigestGen) ->
|
||||
case validate_credentials(User, Server) of
|
||||
{ok, LUser, LServer} ->
|
||||
lists:foldl(
|
||||
fun(Mod, false) ->
|
||||
case db_check_password(
|
||||
LUser, AuthzId, LServer, Password,
|
||||
Digest, DigestGen, Mod) of
|
||||
true -> {true, Mod};
|
||||
false -> false
|
||||
end;
|
||||
(_, Acc) ->
|
||||
Acc
|
||||
end, false, auth_modules(LServer));
|
||||
case jid:nodeprep(AuthzId) of
|
||||
error ->
|
||||
false;
|
||||
LAuthzId ->
|
||||
lists:foldl(
|
||||
fun(Mod, false) ->
|
||||
case db_check_password(
|
||||
LUser, LAuthzId, LServer, Password,
|
||||
Digest, DigestGen, Mod) of
|
||||
true -> {true, Mod};
|
||||
false -> false
|
||||
end;
|
||||
(_, Acc) ->
|
||||
Acc
|
||||
end, false, auth_modules(LServer))
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
@@ -545,8 +563,8 @@ db_user_exists(User, Server, Mod) ->
|
||||
{ok, _} ->
|
||||
true;
|
||||
error ->
|
||||
case Mod:store_type(Server) of
|
||||
external ->
|
||||
case {Mod:store_type(Server), use_cache(Mod, Server)} of
|
||||
{external, true} ->
|
||||
case ets_cache:lookup(
|
||||
?AUTH_CACHE, {User, Server},
|
||||
fun() ->
|
||||
@@ -559,8 +577,12 @@ db_user_exists(User, Server, Mod) ->
|
||||
{ok, _} ->
|
||||
true;
|
||||
error ->
|
||||
false
|
||||
false;
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end;
|
||||
{external, false} ->
|
||||
Mod:user_exists(User, Server);
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
@@ -673,7 +695,7 @@ password_to_scram(Password) ->
|
||||
password_to_scram(#scram{} = Password, _IterationCount) ->
|
||||
Password;
|
||||
password_to_scram(Password, IterationCount) ->
|
||||
Salt = randoms:bytes(?SALT_LENGTH),
|
||||
Salt = p1_rand:bytes(?SALT_LENGTH),
|
||||
SaltedPassword = scram:salted_password(Password, Salt, IterationCount),
|
||||
StoredKey = scram:stored_key(scram:client_key(SaltedPassword)),
|
||||
ServerKey = scram:server_key(SaltedPassword),
|
||||
@@ -744,7 +766,7 @@ auth_modules() ->
|
||||
lists:flatmap(
|
||||
fun(Host) ->
|
||||
[{Host, Mod} || Mod <- auth_modules(Host)]
|
||||
end, ?MYHOSTS).
|
||||
end, ejabberd_config:get_myhosts()).
|
||||
|
||||
-spec auth_modules(binary()) -> [module()].
|
||||
auth_modules(Server) ->
|
||||
@@ -829,13 +851,7 @@ import(Server, {sql, _}, riak, <<"users">>, Fields) ->
|
||||
import(_LServer, {sql, _}, sql, <<"users">>, _) ->
|
||||
ok.
|
||||
|
||||
-spec opt_type(auth_method) -> fun((atom() | [atom()]) -> [atom()]);
|
||||
(auth_password_format) -> fun((plain | scram) -> plain | scram);
|
||||
(auth_use_cache) -> fun((boolean()) -> boolean());
|
||||
(auth_cache_missed) -> fun((boolean()) -> boolean());
|
||||
(auth_cache_life_time) -> fun((timeout()) -> timeout());
|
||||
(auth_cache_size) -> fun((timeout()) -> timeout());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(auth_method) ->
|
||||
fun (V) when is_list(V) ->
|
||||
lists:map(fun(M) -> ejabberd_config:v_db(?MODULE, M) end, V);
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
-export([start/1,
|
||||
stop/1,
|
||||
use_cache/1,
|
||||
allow_anonymous/1,
|
||||
is_sasl_anonymous_enabled/1,
|
||||
is_login_anonymous_enabled/1,
|
||||
@@ -44,7 +45,6 @@
|
||||
get_users/2, count_users/2, store_type/1,
|
||||
plain_password_required/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("jid.hrl").
|
||||
|
||||
@@ -61,6 +61,9 @@ stop(Host) ->
|
||||
ejabberd_hooks:delete(sm_remove_connection_hook, Host,
|
||||
?MODULE, unregister_connection, 100).
|
||||
|
||||
use_cache(_) ->
|
||||
false.
|
||||
|
||||
%% Return true if anonymous is allowed for host or false otherwise
|
||||
allow_anonymous(Host) ->
|
||||
lists:member(?MODULE, ejabberd_auth:auth_modules(Host)).
|
||||
@@ -174,10 +177,7 @@ plain_password_required(_) ->
|
||||
store_type(_) ->
|
||||
external.
|
||||
|
||||
-spec opt_type(allow_multiple_connection) -> fun((boolean()) -> boolean());
|
||||
(anonymous_protocol) -> fun((sasl_anon | login_anon | both) ->
|
||||
sasl_anon | login_anon | both);
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(allow_multiple_connections) ->
|
||||
fun (V) when is_boolean(V) -> V end;
|
||||
opt_type(anonymous_protocol) ->
|
||||
|
||||
@@ -31,23 +31,24 @@
|
||||
|
||||
-behaviour(ejabberd_auth).
|
||||
|
||||
-export([start/1, stop/1, set_password/3, check_password/4,
|
||||
-export([start/1, stop/1, reload/1, set_password/3, check_password/4,
|
||||
try_register/3, user_exists/2, remove_user/2,
|
||||
store_type/1, plain_password_required/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(Host) ->
|
||||
Cmd = ejabberd_config:get_option({extauth_program, Host}, "extauth"),
|
||||
extauth:start(Host, Cmd).
|
||||
extauth:start(Host).
|
||||
|
||||
stop(Host) ->
|
||||
extauth:stop(Host).
|
||||
|
||||
reload(Host) ->
|
||||
extauth:reload(Host).
|
||||
|
||||
plain_password_required(_) -> true.
|
||||
|
||||
store_type(_) -> external.
|
||||
@@ -61,37 +62,48 @@ check_password(User, AuthzId, Server, Password) ->
|
||||
|
||||
set_password(User, Server, Password) ->
|
||||
case extauth:set_password(User, Server, Password) of
|
||||
true -> ok;
|
||||
_ -> {error, db_failure}
|
||||
Res when is_boolean(Res) -> ok;
|
||||
{error, Reason} -> failure(User, Server, set_password, Reason)
|
||||
end.
|
||||
|
||||
try_register(User, Server, Password) ->
|
||||
extauth:try_register(User, Server, Password).
|
||||
case extauth:try_register(User, Server, Password) of
|
||||
true -> ok;
|
||||
false -> {error, not_allowed};
|
||||
{error, Reason} -> failure(User, Server, try_register, Reason)
|
||||
end.
|
||||
|
||||
user_exists(User, Server) ->
|
||||
try extauth:user_exists(User, Server) of
|
||||
Res -> Res
|
||||
catch
|
||||
_:Error ->
|
||||
?ERROR_MSG("external authentication program failure: ~p",
|
||||
[Error]),
|
||||
{error, db_failure}
|
||||
case extauth:user_exists(User, Server) of
|
||||
Res when is_boolean(Res) -> Res;
|
||||
{error, Reason} -> failure(User, Server, user_exists, Reason)
|
||||
end.
|
||||
|
||||
remove_user(User, Server) ->
|
||||
case extauth:remove_user(User, Server) of
|
||||
false -> {error, not_allowed};
|
||||
true -> ok
|
||||
true -> ok;
|
||||
{error, Reason} -> failure(User, Server, remove_user, Reason)
|
||||
end.
|
||||
|
||||
check_password_extauth(User, _AuthzId, Server, Password) ->
|
||||
extauth:check_password(User, Server, Password) andalso
|
||||
Password /= <<"">>.
|
||||
if Password /= <<"">> ->
|
||||
case extauth:check_password(User, Server, Password) of
|
||||
Res when is_boolean(Res) -> Res;
|
||||
{error, Reason} ->
|
||||
failure(User, Server, check_password, Reason),
|
||||
false
|
||||
end;
|
||||
true ->
|
||||
false
|
||||
end.
|
||||
|
||||
-spec failure(binary(), binary(), atom(), any()) -> {error, db_failure}.
|
||||
failure(User, Server, Fun, Reason) ->
|
||||
?ERROR_MSG("External authentication program failed when calling "
|
||||
"'~s' for ~s@~s: ~p", [Fun, User, Server, Reason]),
|
||||
{error, db_failure}.
|
||||
|
||||
-spec opt_type(extauth_cache) -> fun((false | non_neg_integer()) ->
|
||||
false | non_neg_integer());
|
||||
(extauth_program) -> fun((binary()) -> string());
|
||||
(atom()) -> [atom()].
|
||||
opt_type(extauth_cache) ->
|
||||
?WARNING_MSG("option 'extauth_cache' is deprecated and has no effect, "
|
||||
"use authentication or global cache configuration "
|
||||
@@ -100,6 +112,17 @@ opt_type(extauth_cache) ->
|
||||
fun (false) -> false;
|
||||
(I) when is_integer(I), I >= 0 -> I
|
||||
end;
|
||||
opt_type(extauth_instances) ->
|
||||
?WARNING_MSG("option 'extauth_instances' is deprecated and has no effect, "
|
||||
"use 'extauth_pool_size'", []),
|
||||
fun (V) when is_integer(V), V > 0 -> V end;
|
||||
opt_type(extauth_program) ->
|
||||
fun (V) -> binary_to_list(iolist_to_binary(V)) end;
|
||||
opt_type(_) -> [extauth_cache, extauth_program].
|
||||
opt_type(extauth_pool_name) ->
|
||||
fun (V) -> iolist_to_binary(V) end;
|
||||
opt_type(extauth_pool_size) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
opt_type(_) ->
|
||||
[extauth_program, extauth_pool_size, extauth_pool_name,
|
||||
%% Deprecated:
|
||||
extauth_cache, extauth_instances].
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
store_type/1, plain_password_required/1,
|
||||
opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-include("eldap.hrl").
|
||||
@@ -363,10 +362,7 @@ parse_options(Host) ->
|
||||
sfilter = SearchFilter, lfilter = LocalFilter,
|
||||
dn_filter = DNFilter, dn_filter_attrs = DNFilterAttrs}.
|
||||
|
||||
-spec opt_type(ldap_dn_filter) -> fun(([{binary(), binary()}]) ->
|
||||
[{binary(), binary()}]);
|
||||
(ldap_local_filter) -> fun((any()) -> any());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(ldap_dn_filter) ->
|
||||
fun ([{DNF, DNFA}]) ->
|
||||
NewDNFA = case DNFA of
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
plain_password_required/1, use_cache/1]).
|
||||
-export([need_transform/1, transform/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("scram.hrl").
|
||||
-include("ejabberd_auth.hrl").
|
||||
|
||||
-record(reg_users_counter, {vhost = <<"">> :: binary(),
|
||||
|
||||
@@ -82,9 +82,7 @@ get_pam_service(Host) ->
|
||||
get_pam_userinfotype(Host) ->
|
||||
ejabberd_config:get_option({pam_userinfotype, Host}, username).
|
||||
|
||||
-spec opt_type(pam_service) -> fun((binary()) -> binary());
|
||||
(pam_userinfotype) -> fun((username | jid) -> username | jid);
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(pam_service) -> fun iolist_to_binary/1;
|
||||
opt_type(pam_userinfotype) ->
|
||||
fun (username) -> username;
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
plain_password_required/1]).
|
||||
-export([passwd_schema/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("ejabberd_sql_pt.hrl").
|
||||
-include("scram.hrl").
|
||||
-include("ejabberd_auth.hrl").
|
||||
|
||||
start(_Host) ->
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
remove_user/2, store_type/1, plain_password_required/1,
|
||||
convert_to_scram/1, opt_type/1, export/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("scram.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_sql_pt.hrl").
|
||||
-include("ejabberd_auth.hrl").
|
||||
@@ -324,8 +324,7 @@ export(_Server) ->
|
||||
[]
|
||||
end}].
|
||||
|
||||
-spec opt_type(pgsql_users_number_estimate) -> fun((boolean()) -> boolean());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(pgsql_users_number_estimate) ->
|
||||
fun (V) when is_boolean(V) -> V end;
|
||||
opt_type(_) -> [pgsql_users_number_estimate].
|
||||
|
||||
+82
-115
@@ -23,20 +23,18 @@
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(ejabberd_bosh).
|
||||
|
||||
-behaviour(xmpp_socket).
|
||||
-behaviour(p1_fsm).
|
||||
-protocol({xep, 124, '1.11'}).
|
||||
-protocol({xep, 206, '1.4'}).
|
||||
|
||||
-behaviour(p1_fsm).
|
||||
|
||||
%% API
|
||||
-export([start/2, start/3, start_link/3]).
|
||||
|
||||
-export([send_xml/2, setopts/2, controlling_process/2,
|
||||
migrate/3, become_controller/2,
|
||||
reset_stream/1, change_shaper/2, monitor/1, close/1,
|
||||
reset_stream/1, change_shaper/2, close/1,
|
||||
sockname/1, peername/1, process_request/3, send/2,
|
||||
change_controller/2]).
|
||||
get_transport/1, get_owner/1]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1, wait_for_session/2, wait_for_session/3,
|
||||
@@ -44,13 +42,9 @@
|
||||
handle_sync_event/4, handle_info/3, terminate/3,
|
||||
code_change/4]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-include("xmpp.hrl").
|
||||
|
||||
-include("ejabberd_http.hrl").
|
||||
|
||||
-include("bosh.hrl").
|
||||
|
||||
%%-define(DBGFSM, true).
|
||||
@@ -92,7 +86,7 @@
|
||||
sid = <<"">> :: binary(),
|
||||
el_ibuf :: p1_queue:queue(),
|
||||
el_obuf :: p1_queue:queue(),
|
||||
shaper_state = none :: shaper:shaper(),
|
||||
shaper_state = none :: ejabberd_shaper:shaper(),
|
||||
c2s_pid :: pid() | undefined,
|
||||
xmpp_ver = <<"">> :: binary(),
|
||||
inactivity_timer :: reference() | undefined,
|
||||
@@ -103,8 +97,8 @@
|
||||
prev_key = <<"">> :: binary(),
|
||||
prev_poll :: erlang:timestamp() | undefined,
|
||||
max_concat = unlimited :: unlimited | non_neg_integer(),
|
||||
responses = gb_trees:empty() :: ?TGB_TREE,
|
||||
receivers = gb_trees:empty() :: ?TGB_TREE,
|
||||
responses = gb_trees:empty() :: gb_trees:tree(),
|
||||
receivers = gb_trees:empty() :: gb_trees:tree(),
|
||||
shaped_receivers :: p1_queue:queue(),
|
||||
ip :: inet:ip_address(),
|
||||
max_requests = 1 :: non_neg_integer()}).
|
||||
@@ -171,22 +165,12 @@ setopts({http_bind, FsmRef, _IP}, Opts) ->
|
||||
|
||||
controlling_process(_Socket, _Pid) -> ok.
|
||||
|
||||
become_controller(FsmRef, C2SPid) ->
|
||||
p1_fsm:send_all_state_event(FsmRef,
|
||||
{become_controller, C2SPid}).
|
||||
|
||||
change_controller({http_bind, FsmRef, _IP}, C2SPid) ->
|
||||
become_controller(FsmRef, C2SPid).
|
||||
|
||||
reset_stream({http_bind, _FsmRef, _IP} = Socket) ->
|
||||
Socket.
|
||||
|
||||
change_shaper({http_bind, FsmRef, _IP}, Shaper) ->
|
||||
p1_fsm:send_all_state_event(FsmRef, {change_shaper, Shaper}).
|
||||
|
||||
monitor({http_bind, FsmRef, _IP}) ->
|
||||
erlang:monitor(process, FsmRef).
|
||||
|
||||
close({http_bind, FsmRef, _IP}) ->
|
||||
catch p1_fsm:sync_send_all_state_event(FsmRef,
|
||||
close).
|
||||
@@ -195,10 +179,11 @@ sockname(_Socket) -> {ok, {{0, 0, 0, 0}, 0}}.
|
||||
|
||||
peername({http_bind, _FsmRef, IP}) -> {ok, IP}.
|
||||
|
||||
migrate(FsmRef, Node, After) when node(FsmRef) == node() ->
|
||||
catch erlang:send_after(After, FsmRef, {migrate, Node});
|
||||
migrate(_FsmRef, _Node, _After) ->
|
||||
ok.
|
||||
get_transport(_Socket) ->
|
||||
http_bind.
|
||||
|
||||
get_owner({http_bind, FsmRef, _IP}) ->
|
||||
FsmRef.
|
||||
|
||||
process_request(Data, IP, Type) ->
|
||||
Opts1 = ejabberd_c2s_config:get_c2s_limits(),
|
||||
@@ -285,7 +270,7 @@ init([#body{attrs = Attrs}, IP, SID]) ->
|
||||
Opts1 = ejabberd_c2s_config:get_c2s_limits(),
|
||||
Opts2 = [{xml_socket, true} | Opts1],
|
||||
Shaper = none,
|
||||
ShaperState = shaper:new(Shaper),
|
||||
ShaperState = ejabberd_shaper:new(Shaper),
|
||||
Socket = make_socket(self(), IP),
|
||||
XMPPVer = get_attr('xmpp:version', Attrs),
|
||||
XMPPDomain = get_attr(to, Attrs),
|
||||
@@ -299,30 +284,26 @@ init([#body{attrs = Attrs}, IP, SID]) ->
|
||||
buf_new(XMPPDomain)),
|
||||
Opts2}
|
||||
end,
|
||||
xmpp_socket:start(ejabberd_c2s, ?MODULE, Socket,
|
||||
[{receiver, self()}|Opts]),
|
||||
Inactivity = gen_mod:get_module_opt(XMPPDomain,
|
||||
mod_bosh, max_inactivity),
|
||||
MaxConcat = gen_mod:get_module_opt(XMPPDomain, mod_bosh, max_concat),
|
||||
ShapedReceivers = buf_new(XMPPDomain, ?MAX_SHAPED_REQUESTS_QUEUE_LEN),
|
||||
State = #state{host = XMPPDomain, sid = SID, ip = IP,
|
||||
xmpp_ver = XMPPVer, el_ibuf = InBuf,
|
||||
max_concat = MaxConcat, el_obuf = buf_new(XMPPDomain),
|
||||
inactivity_timeout = Inactivity,
|
||||
shaped_receivers = ShapedReceivers,
|
||||
shaper_state = ShaperState},
|
||||
NewState = restart_inactivity_timer(State),
|
||||
mod_bosh:open_session(SID, self()),
|
||||
{ok, wait_for_session, NewState};
|
||||
init([StateName, State]) ->
|
||||
mod_bosh:open_session(State#state.sid, self()),
|
||||
case State#state.c2s_pid of
|
||||
C2SPid when is_pid(C2SPid) ->
|
||||
NewSocket = make_socket(self(), State#state.ip),
|
||||
C2SPid ! {change_socket, NewSocket},
|
||||
NewState = restart_inactivity_timer(State),
|
||||
{ok, StateName, NewState};
|
||||
_ -> {stop, normal}
|
||||
case ejabberd_c2s:start({?MODULE, Socket}, [{receiver, self()}|Opts]) of
|
||||
{ok, C2SPid} ->
|
||||
ejabberd_c2s:accept(C2SPid),
|
||||
Inactivity = gen_mod:get_module_opt(XMPPDomain,
|
||||
mod_bosh, max_inactivity),
|
||||
MaxConcat = gen_mod:get_module_opt(XMPPDomain, mod_bosh, max_concat),
|
||||
ShapedReceivers = buf_new(XMPPDomain, ?MAX_SHAPED_REQUESTS_QUEUE_LEN),
|
||||
State = #state{host = XMPPDomain, sid = SID, ip = IP,
|
||||
xmpp_ver = XMPPVer, el_ibuf = InBuf,
|
||||
max_concat = MaxConcat, el_obuf = buf_new(XMPPDomain),
|
||||
inactivity_timeout = Inactivity,
|
||||
shaped_receivers = ShapedReceivers,
|
||||
shaper_state = ShaperState},
|
||||
NewState = restart_inactivity_timer(State),
|
||||
mod_bosh:open_session(SID, self()),
|
||||
{ok, wait_for_session, NewState};
|
||||
{error, Reason} ->
|
||||
{stop, Reason};
|
||||
ignore ->
|
||||
ignore
|
||||
end.
|
||||
|
||||
wait_for_session(_Event, State) ->
|
||||
@@ -359,7 +340,7 @@ wait_for_session(#body{attrs = Attrs} = Req, From,
|
||||
{'xmlns:stream', ?NS_STREAM}, {from, State#state.host}
|
||||
| Polling]},
|
||||
{ShaperState, _} =
|
||||
shaper:update(State#state.shaper_state, Req#body.size),
|
||||
ejabberd_shaper:update(State#state.shaper_state, Req#body.size),
|
||||
State1 = State#state{wait_timeout = Wait,
|
||||
prev_rid = RID, prev_key = NewKey,
|
||||
prev_poll = PollTime, shaper_state = ShaperState,
|
||||
@@ -369,15 +350,22 @@ wait_for_session(#body{attrs = Attrs} = Req, From,
|
||||
{State3, RespEls} = get_response_els(State2),
|
||||
State4 = stop_inactivity_timer(State3),
|
||||
case RespEls of
|
||||
[] ->
|
||||
State5 = restart_wait_timer(State4),
|
||||
Receivers = gb_trees:insert(RID, {From, Resp},
|
||||
State5#state.receivers),
|
||||
{next_state, active,
|
||||
State5#state{receivers = Receivers}};
|
||||
_ ->
|
||||
reply_next_state(State4, Resp#body{els = RespEls}, RID,
|
||||
From)
|
||||
[{xmlstreamstart, _, _} = El1] ->
|
||||
OutBuf = buf_in([El1], State4#state.el_obuf),
|
||||
State5 = restart_wait_timer(State4),
|
||||
Receivers = gb_trees:insert(RID, {From, Resp},
|
||||
State5#state.receivers),
|
||||
{next_state, active,
|
||||
State5#state{receivers = Receivers, el_obuf = OutBuf}};
|
||||
[] ->
|
||||
State5 = restart_wait_timer(State4),
|
||||
Receivers = gb_trees:insert(RID, {From, Resp},
|
||||
State5#state.receivers),
|
||||
{next_state, active,
|
||||
State5#state{receivers = Receivers}};
|
||||
_ ->
|
||||
reply_next_state(State4, Resp#body{els = RespEls}, RID,
|
||||
From)
|
||||
end;
|
||||
wait_for_session(_Event, _From, State) ->
|
||||
?ERROR_MSG("unexpected sync event in 'wait_for_session': ~p",
|
||||
@@ -397,7 +385,7 @@ active(#body{attrs = Attrs, size = Size} = Req, From,
|
||||
"~p~n** State: ~p",
|
||||
[Req, From, State]),
|
||||
{ShaperState, Pause} =
|
||||
shaper:update(State#state.shaper_state, Size),
|
||||
ejabberd_shaper:update(State#state.shaper_state, Size),
|
||||
State1 = State#state{shaper_state = ShaperState},
|
||||
if Pause > 0 ->
|
||||
TRef = start_shaper_timer(Pause),
|
||||
@@ -408,7 +396,7 @@ active(#body{attrs = Attrs, size = Size} = Req, From,
|
||||
{next_state, active,
|
||||
State2#state{shaped_receivers = Q}}
|
||||
catch error:full ->
|
||||
cancel_timer(TRef),
|
||||
misc:cancel_timer(TRef),
|
||||
RID = get_attr(rid, Attrs),
|
||||
reply_stop(State1,
|
||||
#body{http_reason = <<"Too many requests">>,
|
||||
@@ -452,7 +440,7 @@ active1(#body{attrs = Attrs} = Req, From, State) ->
|
||||
{next_state, active,
|
||||
do_reply(State, From, PrevBody, RID)};
|
||||
none ->
|
||||
State1 = drop_holding_receiver(State),
|
||||
State1 = drop_holding_receiver(State, RID),
|
||||
State2 = stop_inactivity_timer(State1),
|
||||
State3 = restart_wait_timer(State2),
|
||||
Receivers = gb_trees:insert(RID, {From, Req},
|
||||
@@ -522,15 +510,13 @@ active1(#body{attrs = Attrs} = Req, From, State) ->
|
||||
end
|
||||
end.
|
||||
|
||||
handle_event({become_controller, C2SPid}, StateName,
|
||||
handle_event({activate, C2SPid}, StateName,
|
||||
State) ->
|
||||
State1 = route_els(State#state{c2s_pid = C2SPid}),
|
||||
{next_state, StateName, State1};
|
||||
handle_event({change_shaper, Shaper}, StateName,
|
||||
State) ->
|
||||
NewShaperState = shaper:new(Shaper),
|
||||
{next_state, StateName,
|
||||
State#state{shaper_state = NewShaperState}};
|
||||
{next_state, StateName, State#state{shaper_state = Shaper}};
|
||||
handle_event(_Event, StateName, State) ->
|
||||
?ERROR_MSG("unexpected event in '~s': ~p",
|
||||
[StateName, _Event]),
|
||||
@@ -558,7 +544,7 @@ handle_sync_event({send_xml, El}, _From, StateName,
|
||||
State2 = case p1_queue:out(State1#state.shaped_receivers)
|
||||
of
|
||||
{{value, {TRef, From, Body}}, Q} ->
|
||||
cancel_timer(TRef),
|
||||
misc:cancel_timer(TRef),
|
||||
p1_fsm:send_event(self(), {Body, From}),
|
||||
State1#state{shaped_receivers = Q};
|
||||
_ -> State1
|
||||
@@ -578,7 +564,8 @@ handle_sync_event(_Event, _From, StateName, State) ->
|
||||
|
||||
handle_info({timeout, TRef, wait_timeout}, StateName,
|
||||
#state{wait_timer = TRef} = State) ->
|
||||
{next_state, StateName, drop_holding_receiver(State)};
|
||||
State2 = State#state{wait_timer = undefined},
|
||||
{next_state, StateName, drop_holding_receiver(State2)};
|
||||
handle_info({timeout, TRef, inactive}, _StateName,
|
||||
#state{inactivity_timer = TRef} = State) ->
|
||||
{stop, normal, State};
|
||||
@@ -596,24 +583,11 @@ handle_info({timeout, TRef, shaper_timeout}, StateName,
|
||||
{stop, normal, State};
|
||||
_ -> {next_state, StateName, State}
|
||||
end;
|
||||
handle_info({migrate, Node}, StateName, State) ->
|
||||
if Node /= node() ->
|
||||
NewState = bounce_receivers(State, migrated),
|
||||
{migrate, NewState,
|
||||
{Node, ?MODULE, start, [StateName, NewState]}, 0};
|
||||
true -> {next_state, StateName, State}
|
||||
end;
|
||||
handle_info(_Info, StateName, State) ->
|
||||
?ERROR_MSG("unexpected info:~n** Msg: ~p~n** StateName: ~p",
|
||||
[_Info, StateName]),
|
||||
{next_state, StateName, State}.
|
||||
|
||||
terminate({migrated, ClonePid}, _StateName, State) ->
|
||||
?INFO_MSG("Migrating session \"~s\" (c2s_pid = "
|
||||
"~p) to ~p on node ~p",
|
||||
[State#state.sid, State#state.c2s_pid, ClonePid,
|
||||
node(ClonePid)]),
|
||||
mod_bosh:close_session(State#state.sid);
|
||||
terminate(_Reason, _StateName, State) ->
|
||||
mod_bosh:close_session(State#state.sid),
|
||||
case State#state.c2s_pid of
|
||||
@@ -688,15 +662,17 @@ reply_stop(State, Body, From, RID) ->
|
||||
{stop, normal, do_reply(State, From, Body, RID)}.
|
||||
|
||||
drop_holding_receiver(State) ->
|
||||
RID = State#state.prev_rid,
|
||||
drop_holding_receiver(State, State#state.prev_rid).
|
||||
drop_holding_receiver(State, RID) ->
|
||||
case gb_trees:lookup(RID, State#state.receivers) of
|
||||
{value, {From, Body}} ->
|
||||
State1 = restart_inactivity_timer(State),
|
||||
Receivers = gb_trees:delete_any(RID,
|
||||
State1#state.receivers),
|
||||
State2 = State1#state{receivers = Receivers},
|
||||
do_reply(State2, From, Body, RID);
|
||||
none -> State
|
||||
{value, {From, Body}} ->
|
||||
State1 = restart_inactivity_timer(State),
|
||||
Receivers = gb_trees:delete_any(RID,
|
||||
State1#state.receivers),
|
||||
State2 = State1#state{receivers = Receivers},
|
||||
do_reply(State2, From, Body, RID);
|
||||
none ->
|
||||
restart_inactivity_timer(State)
|
||||
end.
|
||||
|
||||
do_reply(State, From, Body, RID) ->
|
||||
@@ -714,7 +690,7 @@ do_reply(State, From, Body, RID) ->
|
||||
Responses2 = gb_trees:insert(RID, Body, Responses1),
|
||||
State#state{responses = Responses2}.
|
||||
|
||||
bounce_receivers(State, Reason) ->
|
||||
bounce_receivers(State, _Reason) ->
|
||||
Receivers = gb_trees:to_list(State#state.receivers),
|
||||
ShapedReceivers = lists:map(fun ({_, From,
|
||||
#body{attrs = Attrs} = Body}) ->
|
||||
@@ -722,18 +698,13 @@ bounce_receivers(State, Reason) ->
|
||||
{RID, {From, Body}}
|
||||
end,
|
||||
p1_queue:to_list(State#state.shaped_receivers)),
|
||||
lists:foldl(fun ({RID, {From, Body}}, AccState) ->
|
||||
NewBody = if Reason == closed ->
|
||||
#body{http_reason =
|
||||
<<"Session closed">>,
|
||||
attrs =
|
||||
[{type, <<"terminate">>},
|
||||
{condition,
|
||||
<<"other-request">>}]};
|
||||
Reason == migrated ->
|
||||
Body#body{http_reason =
|
||||
<<"Session migrated">>}
|
||||
end,
|
||||
lists:foldl(fun ({RID, {From, _Body}}, AccState) ->
|
||||
NewBody = #body{http_reason =
|
||||
<<"Session closed">>,
|
||||
attrs =
|
||||
[{type, <<"terminate">>},
|
||||
{condition,
|
||||
<<"other-request">>}]},
|
||||
do_reply(AccState, From, NewBody, RID)
|
||||
end,
|
||||
State, Receivers ++ ShapedReceivers).
|
||||
@@ -987,7 +958,7 @@ http_error(Status, Reason, Type) ->
|
||||
end,
|
||||
{Status, Reason, ?HEADER(CType), <<"">>}.
|
||||
|
||||
make_sid() -> str:sha(randoms:get_string()).
|
||||
make_sid() -> str:sha(p1_rand:get_string()).
|
||||
|
||||
-compile({no_auto_import, [{min, 2}]}).
|
||||
|
||||
@@ -1040,12 +1011,8 @@ buf_out(Buf, I, Els) ->
|
||||
{empty, _} -> buf_out(Buf, 0, Els)
|
||||
end.
|
||||
|
||||
cancel_timer(TRef) when is_reference(TRef) ->
|
||||
p1_fsm:cancel_timer(TRef);
|
||||
cancel_timer(_) -> false.
|
||||
|
||||
restart_timer(TRef, Timeout, Msg) ->
|
||||
cancel_timer(TRef),
|
||||
misc:cancel_timer(TRef),
|
||||
erlang:start_timer(timer:seconds(Timeout), self(), Msg).
|
||||
|
||||
restart_inactivity_timer(#state{inactivity_timeout =
|
||||
@@ -1062,7 +1029,7 @@ restart_inactivity_timer(#state{inactivity_timer =
|
||||
|
||||
stop_inactivity_timer(#state{inactivity_timer = TRef} =
|
||||
State) ->
|
||||
cancel_timer(TRef),
|
||||
misc:cancel_timer(TRef),
|
||||
State#state{inactivity_timer = undefined}.
|
||||
|
||||
restart_wait_timer(#state{wait_timer = TRef,
|
||||
@@ -1072,13 +1039,13 @@ restart_wait_timer(#state{wait_timer = TRef,
|
||||
State#state{wait_timer = NewTRef}.
|
||||
|
||||
stop_wait_timer(#state{wait_timer = TRef} = State) ->
|
||||
cancel_timer(TRef), State#state{wait_timer = undefined}.
|
||||
misc:cancel_timer(TRef), State#state{wait_timer = undefined}.
|
||||
|
||||
start_shaper_timer(Timeout) ->
|
||||
erlang:start_timer(Timeout, self(), shaper_timeout).
|
||||
|
||||
make_random_jid(Host) ->
|
||||
User = randoms:get_string(),
|
||||
jid:make(User, Host, randoms:get_string()).
|
||||
User = p1_rand:get_string(),
|
||||
jid:make(User, Host, p1_rand:get_string()).
|
||||
|
||||
make_socket(Pid, IP) -> {http_bind, Pid, IP}.
|
||||
|
||||
+91
-113
@@ -22,20 +22,20 @@
|
||||
-module(ejabberd_c2s).
|
||||
-behaviour(xmpp_stream_in).
|
||||
-behaviour(ejabberd_config).
|
||||
-behaviour(xmpp_socket).
|
||||
-behaviour(ejabberd_listener).
|
||||
|
||||
-protocol({rfc, 6121}).
|
||||
|
||||
%% xmpp_socket callbacks
|
||||
-export([start/2, start_link/2, socket_type/0]).
|
||||
%% ejabberd_listener callbacks
|
||||
-export([start/2, start_link/2, accept/1, listen_opt_type/1, listen_options/0]).
|
||||
%% ejabberd_config callbacks
|
||||
-export([opt_type/1, listen_opt_type/1, transform_listen_option/2]).
|
||||
-export([opt_type/1, transform_listen_option/2]).
|
||||
%% xmpp_stream_in callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3]).
|
||||
-export([tls_options/1, tls_required/1, tls_verify/1, tls_enabled/1,
|
||||
-export([tls_options/1, tls_required/1, tls_enabled/1,
|
||||
compress_methods/1, bind/2, sasl_mechanisms/2,
|
||||
get_password_fun/1, check_password_fun/1, check_password_digest_fun/1,
|
||||
get_password_fun/2, check_password_fun/2, check_password_digest_fun/2,
|
||||
unauthenticated_stream_features/1, authenticated_stream_features/1,
|
||||
handle_stream_start/2, handle_stream_end/2,
|
||||
handle_unauthenticated_packet/2, handle_authenticated_packet/2,
|
||||
@@ -51,7 +51,6 @@
|
||||
reply/2, copy_state/2, set_timeout/2, route/2,
|
||||
host_up/1, host_down/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("mod_roster.hrl").
|
||||
@@ -62,26 +61,18 @@
|
||||
-export_type([state/0]).
|
||||
|
||||
%%%===================================================================
|
||||
%%% xmpp_socket API
|
||||
%%% ejabberd_listener API
|
||||
%%%===================================================================
|
||||
start(SockData, Opts) ->
|
||||
case proplists:get_value(supervisor, Opts, true) of
|
||||
true ->
|
||||
case supervisor:start_child(ejabberd_c2s_sup, [SockData, Opts]) of
|
||||
{ok, undefined} -> ignore;
|
||||
Res -> Res
|
||||
end;
|
||||
_ ->
|
||||
xmpp_stream_in:start(?MODULE, [SockData, Opts],
|
||||
ejabberd_config:fsm_limit_opts(Opts))
|
||||
end.
|
||||
xmpp_stream_in:start(?MODULE, [SockData, Opts],
|
||||
ejabberd_config:fsm_limit_opts(Opts)).
|
||||
|
||||
start_link(SockData, Opts) ->
|
||||
xmpp_stream_in:start_link(?MODULE, [SockData, Opts],
|
||||
ejabberd_config:fsm_limit_opts(Opts)).
|
||||
|
||||
socket_type() ->
|
||||
xml_stream.
|
||||
accept(Ref) ->
|
||||
xmpp_stream_in:accept(Ref).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Common API
|
||||
@@ -105,7 +96,7 @@ get_presence(Ref) ->
|
||||
set_presence(Ref, Pres) ->
|
||||
call(Ref, {set_presence, Pres}, 1000).
|
||||
|
||||
-spec resend_presence(pid()) -> ok.
|
||||
-spec resend_presence(pid()) -> boolean().
|
||||
resend_presence(Pid) ->
|
||||
resend_presence(Pid, undefined).
|
||||
|
||||
@@ -273,7 +264,6 @@ process_terminated(#{sid := SID, socket := Socket,
|
||||
State1 = case maps:is_key(pres_last, State) of
|
||||
true ->
|
||||
Pres = #presence{type = unavailable,
|
||||
status = xmpp:mk_text(Status),
|
||||
from = JID,
|
||||
to = jid:remove_resource(JID)},
|
||||
ejabberd_sm:close_session_unset_presence(SID, U, S, R,
|
||||
@@ -340,9 +330,6 @@ tls_options(#{lserver := LServer, tls_options := DefaultOpts,
|
||||
tls_required(#{tls_required := TLSRequired}) ->
|
||||
TLSRequired.
|
||||
|
||||
tls_verify(#{tls_verify := TLSVerify}) ->
|
||||
TLSVerify.
|
||||
|
||||
tls_enabled(#{tls_enabled := TLSEnabled,
|
||||
tls_required := TLSRequired,
|
||||
tls_verify := TLSVerify}) ->
|
||||
@@ -359,25 +346,41 @@ unauthenticated_stream_features(#{lserver := LServer}) ->
|
||||
authenticated_stream_features(#{lserver := LServer}) ->
|
||||
ejabberd_hooks:run_fold(c2s_post_auth_features, LServer, [], [LServer]).
|
||||
|
||||
sasl_mechanisms(Mechs, #{lserver := LServer}) ->
|
||||
sasl_mechanisms(Mechs, #{lserver := LServer} = State) ->
|
||||
Type = ejabberd_auth:store_type(LServer),
|
||||
Mechs1 = ejabberd_config:get_option({disable_sasl_mechanisms, LServer}, []),
|
||||
Mechs2 = case ejabberd_auth_anonymous:is_sasl_anonymous_enabled(LServer) of
|
||||
true -> Mechs1;
|
||||
false -> [<<"ANONYMOUS">>|Mechs1]
|
||||
end,
|
||||
Mechs -- Mechs2.
|
||||
%% I re-created it from cyrsasl ets magic, but I think it's wrong
|
||||
%% TODO: need to check before 18.09 release
|
||||
lists:filter(
|
||||
fun(<<"ANONYMOUS">>) ->
|
||||
ejabberd_auth_anonymous:is_sasl_anonymous_enabled(LServer);
|
||||
(<<"DIGEST-MD5">>) -> Type == plain;
|
||||
(<<"SCRAM-SHA-1">>) -> Type /= external;
|
||||
(<<"PLAIN">>) -> true;
|
||||
(<<"X-OAUTH2">>) -> true;
|
||||
(<<"EXTERNAL">>) -> maps:get(tls_verify, State, false);
|
||||
(_) -> false
|
||||
end, Mechs -- Mechs1).
|
||||
|
||||
get_password_fun(#{lserver := LServer}) ->
|
||||
get_password_fun(_Mech, #{lserver := LServer}) ->
|
||||
fun(U) ->
|
||||
ejabberd_auth:get_password_with_authmodule(U, LServer)
|
||||
end.
|
||||
|
||||
check_password_fun(#{lserver := LServer}) ->
|
||||
check_password_fun(<<"X-OAUTH2">>, #{lserver := LServer}) ->
|
||||
fun(User, _AuthzId, Token) ->
|
||||
case ejabberd_oauth:check_token(
|
||||
User, LServer, [<<"sasl_auth">>], Token) of
|
||||
true -> {true, ejabberd_oauth};
|
||||
_ -> {false, ejabberd_oauth}
|
||||
end
|
||||
end;
|
||||
check_password_fun(_Mech, #{lserver := LServer}) ->
|
||||
fun(U, AuthzId, P) ->
|
||||
ejabberd_auth:check_password_with_authmodule(U, AuthzId, LServer, P)
|
||||
end.
|
||||
|
||||
check_password_digest_fun(#{lserver := LServer}) ->
|
||||
check_password_digest_fun(_Mech, #{lserver := LServer}) ->
|
||||
fun(U, AuthzId, P, D, DG) ->
|
||||
ejabberd_auth:check_password_with_authmodule(U, AuthzId, LServer, P, D, DG)
|
||||
end.
|
||||
@@ -405,8 +408,8 @@ bind(R, #{user := U, server := S, access := Access, lang := Lang,
|
||||
{ok, State2};
|
||||
deny ->
|
||||
ejabberd_hooks:run(forbidden_session_hook, LServer, [JID]),
|
||||
?INFO_MSG("(~s) Forbidden c2s session for ~s",
|
||||
[xmpp_socket:pp(Socket), jid:encode(JID)]),
|
||||
?WARNING_MSG("(~s) Forbidden c2s session for ~s",
|
||||
[xmpp_socket:pp(Socket), jid:encode(JID)]),
|
||||
Txt = <<"Access denied by service policy">>,
|
||||
{error, xmpp:err_not_allowed(Txt, Lang), State}
|
||||
end
|
||||
@@ -415,7 +418,7 @@ bind(R, #{user := U, server := S, access := Access, lang := Lang,
|
||||
handle_stream_start(StreamStart, #{lserver := LServer} = State) ->
|
||||
case ejabberd_router:is_my_host(LServer) of
|
||||
false ->
|
||||
send(State#{lserver => ?MYNAME}, xmpp:serr_host_unknown());
|
||||
send(State#{lserver => ejabberd_config:get_myname()}, xmpp:serr_host_unknown());
|
||||
true ->
|
||||
State1 = change_shaper(State),
|
||||
Opts = ejabberd_config:codec_options(LServer),
|
||||
@@ -441,12 +444,12 @@ handle_auth_success(User, Mech, AuthModule,
|
||||
handle_auth_failure(User, Mech, Reason,
|
||||
#{socket := Socket,
|
||||
ip := IP, lserver := LServer} = State) ->
|
||||
?INFO_MSG("(~s) Failed c2s ~s authentication ~sfrom ~s: ~s",
|
||||
[xmpp_socket:pp(Socket), Mech,
|
||||
if User /= <<"">> -> ["for ", User, "@", LServer, " "];
|
||||
true -> ""
|
||||
end,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)), Reason]),
|
||||
?WARNING_MSG("(~s) Failed c2s ~s authentication ~sfrom ~s: ~s",
|
||||
[xmpp_socket:pp(Socket), Mech,
|
||||
if User /= <<"">> -> ["for ", User, "@", LServer, " "];
|
||||
true -> ""
|
||||
end,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)), Reason]),
|
||||
ejabberd_hooks:run_fold(c2s_auth_result, LServer, State, [false, User]).
|
||||
|
||||
handle_unbinded_packet(Pkt, #{lserver := LServer} = State) ->
|
||||
@@ -526,9 +529,9 @@ init([State, Opts]) ->
|
||||
tls_verify => TLSVerify,
|
||||
pres_a => ?SETS:new(),
|
||||
zlib => Zlib,
|
||||
lang => ?MYLANG,
|
||||
server => ?MYNAME,
|
||||
lserver => ?MYNAME,
|
||||
lang => ejabberd_config:get_mylang(),
|
||||
server => ejabberd_config:get_myname(),
|
||||
lserver => ejabberd_config:get_myname(),
|
||||
access => Access,
|
||||
shaper => Shaper},
|
||||
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
|
||||
@@ -882,7 +885,7 @@ bounce_message_queue() ->
|
||||
-spec new_uniq_id() -> binary().
|
||||
new_uniq_id() ->
|
||||
iolist_to_binary(
|
||||
[randoms:get_string(),
|
||||
[p1_rand:get_string(),
|
||||
integer_to_binary(p1_time_compat:unique_integer([positive]))]).
|
||||
|
||||
-spec get_conn_type(state()) -> c2s | c2s_tls | c2s_compressed | websocket |
|
||||
@@ -921,7 +924,7 @@ change_shaper(#{shaper := ShaperName, ip := IP, lserver := LServer,
|
||||
Shaper = acl:access_matches(ShaperName,
|
||||
#{usr => jid:split(JID), ip => IP},
|
||||
LServer),
|
||||
xmpp_stream_in:change_shaper(State, Shaper).
|
||||
xmpp_stream_in:change_shaper(State, ejabberd_shaper:new(Shaper)).
|
||||
|
||||
-spec format_reason(state(), term()) -> binary().
|
||||
format_reason(#{stop_reason := Reason}, _) ->
|
||||
@@ -935,7 +938,7 @@ format_reason(_, {shutdown, _}) ->
|
||||
format_reason(_, _) ->
|
||||
<<"internal server error">>.
|
||||
|
||||
-spec get_certfile(binary()) -> file:filename_all().
|
||||
-spec get_certfile(binary()) -> file:filename_all() | undefined.
|
||||
get_certfile(LServer) ->
|
||||
case ejabberd_pkix:get_certfile(LServer) of
|
||||
{ok, CertFile} ->
|
||||
@@ -949,15 +952,7 @@ get_certfile(LServer) ->
|
||||
transform_listen_option(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
-type resource_conflict() :: setresource | closeold | closenew | acceptnew.
|
||||
-spec opt_type(c2s_ciphers) -> fun((binary()) -> binary());
|
||||
(c2s_dhfile) -> fun((binary()) -> binary());
|
||||
(c2s_cafile) -> fun((binary()) -> binary());
|
||||
(c2s_protocol_options) -> fun(([binary()]) -> binary());
|
||||
(c2s_tls_compression) -> fun((boolean()) -> boolean());
|
||||
(resource_conflict) -> fun((resource_conflict()) -> resource_conflict());
|
||||
(disable_sasl_mechanisms) -> fun((binary() | [binary()]) -> [binary()]);
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(c2s_ciphers) -> fun iolist_to_binary/1;
|
||||
opt_type(c2s_dhfile) -> fun misc:try_read_file/1;
|
||||
opt_type(c2s_cafile) -> fun misc:try_read_file/1;
|
||||
@@ -983,71 +978,54 @@ opt_type(_) ->
|
||||
c2s_protocol_options, c2s_tls_compression, resource_conflict,
|
||||
disable_sasl_mechanisms].
|
||||
|
||||
-spec listen_opt_type(access) -> fun((any()) -> any());
|
||||
(shaper) -> fun((any()) -> any());
|
||||
(certfile) -> fun((binary()) -> binary());
|
||||
(ciphers) -> fun((binary()) -> binary());
|
||||
(dhfile) -> fun((binary()) -> binary());
|
||||
(cafile) -> fun((binary()) -> binary());
|
||||
(protocol_options) -> fun(([binary()]) -> binary());
|
||||
(tls_compression) -> fun((boolean()) -> boolean());
|
||||
(tls) -> fun((boolean()) -> boolean());
|
||||
(starttls) -> fun((boolean()) -> boolean());
|
||||
(tls_verify) -> fun((boolean()) -> boolean());
|
||||
(zlib) -> fun((boolean()) -> boolean());
|
||||
(supervisor) -> fun((boolean()) -> boolean());
|
||||
(max_stanza_size) -> fun((timeout()) -> timeout());
|
||||
(max_fsm_queue) -> fun((timeout()) -> timeout());
|
||||
(stream_management) -> fun((boolean()) -> boolean());
|
||||
(inet) -> fun((boolean()) -> boolean());
|
||||
(inet6) -> fun((boolean()) -> boolean());
|
||||
(backlog) -> fun((timeout()) -> timeout());
|
||||
(atom()) -> [atom()].
|
||||
listen_opt_type(access) -> fun acl:access_rules_validator/1;
|
||||
listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
|
||||
listen_opt_type(certfile = Opt) ->
|
||||
fun(S) ->
|
||||
?WARNING_MSG("Listening option '~s' for ~s is deprecated, use "
|
||||
"'certfiles' global option instead", [Opt, ?MODULE]),
|
||||
ejabberd_pkix:add_certfile(S),
|
||||
ok = ejabberd_pkix:add_certfile(S),
|
||||
iolist_to_binary(S)
|
||||
end;
|
||||
listen_opt_type(ciphers) -> opt_type(c2s_ciphers);
|
||||
listen_opt_type(dhfile) -> opt_type(c2s_dhfile);
|
||||
listen_opt_type(cafile) -> opt_type(c2s_cafile);
|
||||
listen_opt_type(protocol_options) -> opt_type(c2s_protocol_options);
|
||||
listen_opt_type(tls_compression) -> opt_type(c2s_tls_compression);
|
||||
listen_opt_type(tls) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(starttls) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(starttls_required) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(tls_verify) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(zlib) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(supervisor) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(max_stanza_size) ->
|
||||
fun(I) when is_integer(I), I>0 -> I;
|
||||
(unlimited) -> infinity;
|
||||
(infinity) -> infinity
|
||||
listen_opt_type(zlib) ->
|
||||
fun(true) ->
|
||||
ejabberd:start_app(ezlib),
|
||||
true;
|
||||
(false) ->
|
||||
false
|
||||
end;
|
||||
listen_opt_type(max_fsm_queue) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(stream_management) ->
|
||||
?ERROR_MSG("listening option 'stream_management' is ignored: "
|
||||
"use mod_stream_mgmt module", []),
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(backlog) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
fun(B) when is_boolean(B) ->
|
||||
?ERROR_MSG("Listening option 'stream_management' is ignored: "
|
||||
"use mod_stream_mgmt module", []),
|
||||
B
|
||||
end;
|
||||
listen_opt_type(O) ->
|
||||
StreamOpts = mod_stream_mgmt:mod_options(?MYNAME),
|
||||
case lists:keyfind(O, 1, StreamOpts) of
|
||||
false ->
|
||||
[access, shaper, certfile, ciphers, dhfile, cafile,
|
||||
protocol_options, tls, tls_compression, starttls,
|
||||
starttls_required, tls_verify, zlib, max_fsm_queue,
|
||||
backlog, inet, inet6];
|
||||
_ ->
|
||||
?ERROR_MSG("Listening option '~s' is ignored: use '~s' "
|
||||
"option from mod_stream_mgmt module", [O, O]),
|
||||
mod_stream_mgmt:mod_opt_type(O)
|
||||
MgmtOpts = mod_stream_mgmt:mod_options(ejabberd_config:get_myname()),
|
||||
case lists:keymember(O, 1, MgmtOpts) of
|
||||
true ->
|
||||
fun(V) ->
|
||||
?ERROR_MSG("Listening option '~s' is ignored: use '~s' "
|
||||
"option from mod_stream_mgmt module", [O, O]),
|
||||
(mod_stream_mgmt:mod_opt_type(O))(V)
|
||||
end
|
||||
end.
|
||||
|
||||
listen_options() ->
|
||||
[{access, all},
|
||||
{shaper, none},
|
||||
{certfile, undefined},
|
||||
{ciphers, undefined},
|
||||
{dhfile, undefined},
|
||||
{cafile, undefined},
|
||||
{protocol_options, undefined},
|
||||
{tls, false},
|
||||
{tls_compression, false},
|
||||
{starttls, false},
|
||||
{starttls_required, false},
|
||||
{tls_verify, false},
|
||||
{zlib, false},
|
||||
{max_stanza_size, infinity},
|
||||
{max_fsm_queue, 5000}|
|
||||
mod_stream_mgmt:mod_options(ejabberd_config:get_myname())].
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
config_reloaded/0, process_iq/1]).
|
||||
|
||||
-include("xmpp.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
|
||||
@@ -90,7 +89,7 @@ mk_field(Type, Var, Value) ->
|
||||
create_captcha(SID, From, To, Lang, Limiter, Args) ->
|
||||
case create_image(Limiter) of
|
||||
{ok, Type, Key, Image} ->
|
||||
Id = <<(randoms:get_string())/binary>>,
|
||||
Id = <<(p1_rand:get_string())/binary>>,
|
||||
JID = jid:encode(From),
|
||||
CID = <<"sha1+", (str:sha(Image))/binary, "@bob.xmpp.org">>,
|
||||
Data = #bob_data{cid = CID, 'max-age' = 0, type = Type,
|
||||
@@ -121,7 +120,7 @@ create_captcha(SID, From, To, Lang, Limiter, Args) ->
|
||||
create_captcha_x(SID, To, Lang, Limiter, #xdata{fields = Fs} = X) ->
|
||||
case create_image(Limiter) of
|
||||
{ok, Type, Key, Image} ->
|
||||
Id = <<(randoms:get_string())/binary>>,
|
||||
Id = <<(p1_rand:get_string())/binary>>,
|
||||
CID = <<"sha1+", (str:sha(Image))/binary, "@bob.xmpp.org">>,
|
||||
Data = #bob_data{cid = CID, 'max-age' = 0, type = Type, data = Image},
|
||||
HelpTxt = translate:translate(Lang,
|
||||
@@ -231,7 +230,6 @@ process_iq(#iq{type = get, lang = Lang} = IQ) ->
|
||||
Txt = <<"Value 'get' of 'type' attribute is not allowed">>,
|
||||
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
|
||||
process_iq(#iq{lang = Lang} = IQ) ->
|
||||
?INFO_MSG("IQ = ~p", [IQ]),
|
||||
Txt = <<"No module is handling this query">>,
|
||||
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
|
||||
|
||||
@@ -365,19 +363,19 @@ terminate(_Reason, #state{enabled = Enabled}) ->
|
||||
register_handlers() ->
|
||||
ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
|
||||
ejabberd_hooks:add(host_down, ?MODULE, host_down, 50),
|
||||
lists:foreach(fun host_up/1, ?MYHOSTS).
|
||||
lists:foreach(fun host_up/1, ejabberd_config:get_myhosts()).
|
||||
|
||||
unregister_handlers() ->
|
||||
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50),
|
||||
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 50),
|
||||
lists:foreach(fun host_down/1, ?MYHOSTS).
|
||||
lists:foreach(fun host_down/1, ejabberd_config:get_myhosts()).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
|
||||
create_image() -> create_image(undefined).
|
||||
|
||||
create_image(Limiter) ->
|
||||
Key = str:substr(randoms:get_string(), 1, 6),
|
||||
Key = str:substr(p1_rand:get_string(), 1, 6),
|
||||
create_image(Limiter, Key).
|
||||
|
||||
create_image(Limiter, Key) ->
|
||||
@@ -445,7 +443,7 @@ get_url(Str) ->
|
||||
<<TransferProt/binary, ":", Host/binary, ":",
|
||||
PortString/binary, "/captcha/", Str/binary>>;
|
||||
_ ->
|
||||
<<"http://", (?MYNAME)/binary, "/captcha/", Str/binary>>
|
||||
<<"http://", (ejabberd_config:get_myname())/binary, "/captcha/", Str/binary>>
|
||||
end.
|
||||
|
||||
get_transfer_protocol(PortString) ->
|
||||
@@ -518,11 +516,7 @@ recv_data(Port, TRef, Buf) ->
|
||||
end.
|
||||
|
||||
return(Port, TRef, Result) ->
|
||||
case erlang:cancel_timer(TRef) of
|
||||
false ->
|
||||
receive {timeout, TRef, _} -> ok after 0 -> ok end;
|
||||
_ -> ok
|
||||
end,
|
||||
misc:cancel_timer(TRef),
|
||||
catch port_close(Port),
|
||||
Result.
|
||||
|
||||
@@ -562,7 +556,7 @@ check_captcha(Id, ProvidedKey) ->
|
||||
case ets:lookup(captcha, Id) of
|
||||
[#captcha{pid = Pid, args = Args, key = ValidKey, tref = Tref}] ->
|
||||
ets:delete(captcha, Id),
|
||||
erlang:cancel_timer(Tref),
|
||||
misc:cancel_timer(Tref),
|
||||
if ValidKey == ProvidedKey ->
|
||||
callback(captcha_succeed, Pid, Args),
|
||||
captcha_valid;
|
||||
@@ -596,10 +590,7 @@ callback(_, _, _) ->
|
||||
now_priority() ->
|
||||
-p1_time_compat:system_time(micro_seconds).
|
||||
|
||||
-spec opt_type(captcha_cmd) -> fun((binary()) -> binary());
|
||||
(captcha_host) -> fun((binary()) -> binary());
|
||||
(captcha_limit) -> fun((pos_integer()) -> pos_integer());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(captcha_cmd) ->
|
||||
fun (FileName) ->
|
||||
F = iolist_to_binary(FileName), if F /= <<"">> -> F end
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
get_known_nodes/0, node_id/0, get_node_by_id/1,
|
||||
send/2, wait_for_sync/1, subscribe/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-spec init() -> ok.
|
||||
|
||||
@@ -240,7 +240,6 @@
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
|
||||
@@ -620,9 +619,7 @@ permission_addon() ->
|
||||
[{access, ejabberd_config:get_option(commands_admin_access, none)}],
|
||||
{get_exposed_commands(), []}}}].
|
||||
|
||||
-spec opt_type(commands_admin_access) -> fun((any()) -> any());
|
||||
(commands) -> fun((list()) -> list());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(commands_admin_access) -> fun acl:access_rules_validator/1;
|
||||
opt_type(commands) ->
|
||||
fun(V) when is_list(V) -> V end;
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
-export([generate_md_output/3]).
|
||||
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-define(RAW(V), if HTMLOutput -> fxml:crypt(iolist_to_binary(V)); true -> iolist_to_binary(V) end).
|
||||
-define(TAG(N), if HTMLOutput -> [<<"<", ??N, "/>">>]; true -> md_tag(N, <<"">>) end).
|
||||
@@ -460,7 +459,8 @@ generate_md_output(File, RegExp, Languages) ->
|
||||
end, Cmds2),
|
||||
Cmds4 = [maybe_add_policy_arguments(Cmd) || Cmd <- Cmds3],
|
||||
Langs = binary:split(Languages, <<",">>, [global]),
|
||||
Header = <<"---\ntitle: Administration API reference\nbodyclass: nocomment\n---">>,
|
||||
Header = <<"---\ntitle: Administration API reference\ntoc: true\nmenu: Administration API\norder: 40\n"
|
||||
"// Autogenerated with 'ejabberdctl gen_markdown_doc_for_commands'\n---">>,
|
||||
Out = lists:map(fun(C) -> gen_doc(C, false, Langs) end, Cmds4),
|
||||
{ok, Fh} = file:open(File, [write]),
|
||||
io:format(Fh, "~s~s", [Header, Out]),
|
||||
|
||||
+153
-87
@@ -28,12 +28,13 @@
|
||||
|
||||
-export([start/0, load_file/1, reload_file/0, read_file/1,
|
||||
get_option/1, get_option/2, add_option/2, has_option/1,
|
||||
get_version/0, get_myhosts/0, get_mylang/0, get_lang/1,
|
||||
get_version/0, get_myhosts/0, get_myname/0,
|
||||
get_mylang/0, get_lang/1, get_uri/0, get_copyright/0,
|
||||
get_ejabberd_config_path/0, is_using_elixir_config/0,
|
||||
prepare_opt_val/4, transform_options/1, collect_options/1,
|
||||
convert_to_yaml/1, convert_to_yaml/2, v_db/2,
|
||||
env_binary_to_list/2, opt_type/1, may_hide_data/1,
|
||||
is_elixir_enabled/0, v_dbs/1, v_dbs_mods/1,
|
||||
is_elixir_enabled/0, v_dbs/1, v_dbs_mods/1, v_host/1, v_hosts/1,
|
||||
default_db/1, default_db/2, default_ram_db/1, default_ram_db/2,
|
||||
default_queue_type/1, queue_dir/0, fsm_limit_opts/1,
|
||||
use_cache/1, cache_size/1, cache_missed/1, cache_life_time/1,
|
||||
@@ -53,21 +54,16 @@
|
||||
{get_global_option, 3}, {get_local_option, 3},
|
||||
{get_option, 3}, {is_file_readable, 1}]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_config.hrl").
|
||||
-include_lib("kernel/include/file.hrl").
|
||||
-include_lib("kernel/include/inet.hrl").
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
|
||||
-callback opt_type(atom()) -> function() | [atom()].
|
||||
|
||||
%% @type macro() = {macro_key(), macro_value()}
|
||||
|
||||
%% @type macro_key() = atom().
|
||||
%% The atom must have all characters in uppercase.
|
||||
|
||||
%% @type macro_value() = term().
|
||||
-callback opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
-type bad_option() :: invalid_option | unknown_option.
|
||||
|
||||
-spec start() -> ok | {error, bad_option()}.
|
||||
start() ->
|
||||
ConfigFile = get_ejabberd_config_path(),
|
||||
?INFO_MSG("Loading configuration from ~s", [ConfigFile]),
|
||||
@@ -75,17 +71,23 @@ start() ->
|
||||
[named_table, public, {read_concurrency, true}]),
|
||||
catch ets:new(ejabberd_db_modules,
|
||||
[named_table, public, {read_concurrency, true}]),
|
||||
State1 = load_file(ConfigFile),
|
||||
UnixTime = p1_time_compat:system_time(seconds),
|
||||
SharedKey = case erlang:get_cookie() of
|
||||
nocookie ->
|
||||
str:sha(randoms:get_string());
|
||||
Cookie ->
|
||||
str:sha(misc:atom_to_binary(Cookie))
|
||||
end,
|
||||
State2 = set_option({node_start, global}, UnixTime, State1),
|
||||
State3 = set_option({shared_key, global}, SharedKey, State2),
|
||||
set_opts(State3).
|
||||
case load_file(ConfigFile) of
|
||||
{ok, State1} ->
|
||||
UnixTime = p1_time_compat:system_time(seconds),
|
||||
SharedKey = case erlang:get_cookie() of
|
||||
nocookie ->
|
||||
str:sha(p1_rand:get_string());
|
||||
Cookie ->
|
||||
str:sha(misc:atom_to_binary(Cookie))
|
||||
end,
|
||||
State2 = set_option({node_start, global}, UnixTime, State1),
|
||||
State3 = set_option({shared_key, global}, SharedKey, State2),
|
||||
set_opts(State3),
|
||||
ok;
|
||||
{error, _} = Err ->
|
||||
?ERROR_MSG("Failed to load configuration file ~s", [ConfigFile]),
|
||||
Err
|
||||
end.
|
||||
|
||||
%% When starting ejabberd for testing, we sometimes want to start a
|
||||
%% subset of hosts from the one define in the config file.
|
||||
@@ -114,7 +116,7 @@ start(Hosts, Opts) ->
|
||||
UnixTime = p1_time_compat:system_time(seconds),
|
||||
SharedKey = case erlang:get_cookie() of
|
||||
nocookie ->
|
||||
str:sha(randoms:get_string());
|
||||
str:sha(p1_rand:get_string());
|
||||
Cookie ->
|
||||
str:sha(misc:atom_to_binary(Cookie))
|
||||
end,
|
||||
@@ -135,7 +137,7 @@ get_ejabberd_config_path() ->
|
||||
undefined ->
|
||||
case os:getenv("EJABBERD_CONFIG_PATH") of
|
||||
false ->
|
||||
?CONFIG_PATH;
|
||||
"ejabberd.yml";
|
||||
Path ->
|
||||
Path
|
||||
end
|
||||
@@ -189,7 +191,7 @@ read_file(File, Opts) ->
|
||||
State1 = lists:foldl(fun process_term/2, State, Head ++ Tail),
|
||||
State1#state{opts = compact(State1#state.opts)}.
|
||||
|
||||
-spec load_file(string()) -> #state{}.
|
||||
-spec load_file(string()) -> {ok, #state{}} | {error, bad_option()}.
|
||||
|
||||
load_file(File) ->
|
||||
State0 = read_file(File),
|
||||
@@ -199,23 +201,27 @@ load_file(File) ->
|
||||
ModOpts = get_modules_with_options(AllMods),
|
||||
validate_opts(State1, ModOpts).
|
||||
|
||||
-spec reload_file() -> ok.
|
||||
-spec reload_file() -> ok | {error, bad_option()}.
|
||||
|
||||
reload_file() ->
|
||||
Config = get_ejabberd_config_path(),
|
||||
OldHosts = get_myhosts(),
|
||||
State = load_file(Config),
|
||||
set_opts(State),
|
||||
NewHosts = get_myhosts(),
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ejabberd_hooks:run(host_up, [Host])
|
||||
end, NewHosts -- OldHosts),
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ejabberd_hooks:run(host_down, [Host])
|
||||
end, OldHosts -- NewHosts),
|
||||
ejabberd_hooks:run(config_reloaded, []).
|
||||
case load_file(Config) of
|
||||
{error, _} = Err ->
|
||||
Err;
|
||||
{ok, State} ->
|
||||
set_opts(State),
|
||||
NewHosts = get_myhosts(),
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ejabberd_hooks:run(host_up, [Host])
|
||||
end, NewHosts -- OldHosts),
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ejabberd_hooks:run(host_down, [Host])
|
||||
end, OldHosts -- NewHosts),
|
||||
ejabberd_hooks:run(config_reloaded, [])
|
||||
end.
|
||||
|
||||
-spec convert_to_yaml(file:filename()) -> ok | {error, any()}.
|
||||
|
||||
@@ -777,8 +783,24 @@ set_opts(State) ->
|
||||
fun(#local_config{key = Key, value = Val}) ->
|
||||
{Key, Val}
|
||||
end, Opts)),
|
||||
set_fqdn(),
|
||||
set_log_level().
|
||||
|
||||
set_fqdn() ->
|
||||
FQDNs = case get_option(fqdn, []) of
|
||||
[] ->
|
||||
{ok, Hostname} = inet:gethostname(),
|
||||
case inet:gethostbyname(Hostname) of
|
||||
{ok, #hostent{h_name = FQDN}} ->
|
||||
[iolist_to_binary(FQDN)];
|
||||
{error, _} ->
|
||||
[]
|
||||
end;
|
||||
Domains ->
|
||||
Domains
|
||||
end,
|
||||
xmpp:set_config([{fqdn, FQDNs}]).
|
||||
|
||||
set_log_level() ->
|
||||
Level = get_option(loglevel, 4),
|
||||
ejabberd_logger:set(Level).
|
||||
@@ -949,6 +971,33 @@ v_dbs_mods(Mod) ->
|
||||
(atom_to_binary(M, utf8))/binary>>, utf8)
|
||||
end, v_dbs(Mod)).
|
||||
|
||||
-spec v_host(binary()) -> binary().
|
||||
v_host(Host) ->
|
||||
hd(v_hosts([Host])).
|
||||
|
||||
-spec v_hosts([binary()]) -> [binary()].
|
||||
v_hosts(Hosts) ->
|
||||
ServerHosts = get_myhosts(),
|
||||
lists:foldr(
|
||||
fun(Host, Acc) ->
|
||||
case lists:member(Host, ServerHosts) of
|
||||
true ->
|
||||
?ERROR_MSG("Failed to reuse route ~s because it's "
|
||||
"already registered on a virtual host",
|
||||
[Host]),
|
||||
erlang:error(badarg);
|
||||
false ->
|
||||
case lists:member(Host, Acc) of
|
||||
true ->
|
||||
?ERROR_MSG("Host ~s is defined multiple times",
|
||||
[Host]),
|
||||
erlang:error(badarg);
|
||||
false ->
|
||||
[Host|Acc]
|
||||
end
|
||||
end
|
||||
end, [], Hosts).
|
||||
|
||||
-spec default_db(module()) -> atom().
|
||||
default_db(Module) ->
|
||||
default_db(global, Module).
|
||||
@@ -1017,33 +1066,40 @@ get_modules_with_options(Modules) ->
|
||||
end
|
||||
end, dict:new(), Modules).
|
||||
|
||||
-spec validate_opts(#state{}, dict:dict()) -> {ok, #state{}} | {error, bad_option()}.
|
||||
validate_opts(#state{opts = Opts} = State, ModOpts) ->
|
||||
NewOpts = lists:filtermap(
|
||||
fun(#local_config{key = {Opt, _Host}, value = Val} = In) ->
|
||||
case dict:find(Opt, ModOpts) of
|
||||
{ok, [Mod|_]} ->
|
||||
VFun = Mod:opt_type(Opt),
|
||||
try VFun(Val) of
|
||||
NewVal ->
|
||||
{true, In#local_config{value = NewVal}}
|
||||
catch {invalid_syntax, Error} ->
|
||||
?ERROR_MSG("ignoring option '~s' with "
|
||||
"invalid value: ~p: ~s",
|
||||
[Opt, Val, Error]),
|
||||
false;
|
||||
_:_ ->
|
||||
?ERROR_MSG("ignoring option '~s' with "
|
||||
"invalid value: ~p",
|
||||
[Opt, Val]),
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
?ERROR_MSG("unknown option '~s' will be likely"
|
||||
" ignored", [Opt]),
|
||||
true
|
||||
end
|
||||
end, Opts),
|
||||
State#state{opts = NewOpts}.
|
||||
try
|
||||
NewOpts = lists:map(
|
||||
fun(#local_config{key = {Opt, _Host}, value = Val} = In) ->
|
||||
case dict:find(Opt, ModOpts) of
|
||||
{ok, [Mod|_]} ->
|
||||
VFun = Mod:opt_type(Opt),
|
||||
try VFun(Val) of
|
||||
NewVal ->
|
||||
In#local_config{value = NewVal}
|
||||
catch {invalid_syntax, Error} ->
|
||||
?ERROR_MSG("Invalid value for "
|
||||
"option '~s' (~s): ~s",
|
||||
[Opt, Error,
|
||||
misc:format_val({yaml, Val})]),
|
||||
erlang:error(invalid_option);
|
||||
_:R when R /= undef ->
|
||||
?ERROR_MSG("Invalid value for "
|
||||
"option '~s': ~s",
|
||||
[Opt, misc:format_val({yaml, Val})]),
|
||||
erlang:error(invalid_option)
|
||||
end;
|
||||
_ ->
|
||||
?ERROR_MSG("Unknown option '~s'", [Opt]),
|
||||
erlang:error(unknown_option)
|
||||
end
|
||||
end, Opts),
|
||||
{ok, State#state{opts = NewOpts}}
|
||||
catch _:invalid_option ->
|
||||
{error, invalid_option};
|
||||
_:unknown_option ->
|
||||
{error, unknown_option}
|
||||
end.
|
||||
|
||||
%% @spec (Path::string()) -> true | false
|
||||
is_file_readable(Path) ->
|
||||
@@ -1059,15 +1115,27 @@ is_file_readable(Path) ->
|
||||
end.
|
||||
|
||||
get_version() ->
|
||||
case application:get_key(ejabberd, vsn) of
|
||||
undefined -> "";
|
||||
{ok, Vsn} -> list_to_binary(Vsn)
|
||||
case application:get_env(ejabberd, custom_vsn) of
|
||||
{ok, Vsn0} when is_list(Vsn0) ->
|
||||
list_to_binary(Vsn0);
|
||||
{ok, Vsn1} when is_binary(Vsn1) ->
|
||||
Vsn1;
|
||||
_ ->
|
||||
case application:get_key(ejabberd, vsn) of
|
||||
undefined -> "";
|
||||
{ok, Vsn} -> list_to_binary(Vsn)
|
||||
end
|
||||
end.
|
||||
|
||||
-spec get_myhosts() -> [binary()].
|
||||
|
||||
get_myhosts() ->
|
||||
get_option(hosts).
|
||||
get_option(hosts, [<<"localhost">>]).
|
||||
|
||||
-spec get_myname() -> binary().
|
||||
|
||||
get_myname() ->
|
||||
hd(get_myhosts()).
|
||||
|
||||
-spec get_mylang() -> binary().
|
||||
|
||||
@@ -1078,10 +1146,17 @@ get_mylang() ->
|
||||
get_lang(Host) ->
|
||||
get_option({language, Host}, <<"en">>).
|
||||
|
||||
-spec get_uri() -> binary().
|
||||
get_uri() ->
|
||||
<<"http://www.process-one.net/en/ejabberd/">>.
|
||||
|
||||
-spec get_copyright() -> binary().
|
||||
get_copyright() ->
|
||||
<<"Copyright (c) ProcessOne">>.
|
||||
|
||||
replace_module(mod_announce_odbc) -> {mod_announce, sql};
|
||||
replace_module(mod_blocking_odbc) -> {mod_blocking, sql};
|
||||
replace_module(mod_caps_odbc) -> {mod_caps, sql};
|
||||
replace_module(mod_irc_odbc) -> {mod_irc, sql};
|
||||
replace_module(mod_last_odbc) -> {mod_last, sql};
|
||||
replace_module(mod_muc_odbc) -> {mod_muc, sql};
|
||||
replace_module(mod_offline_odbc) -> {mod_offline, sql};
|
||||
@@ -1215,7 +1290,7 @@ transform_terms(Terms) ->
|
||||
ejabberd_s2s,
|
||||
ejabberd_listener,
|
||||
ejabberd_sql_sup,
|
||||
shaper,
|
||||
ejabberd_shaper,
|
||||
ejabberd_s2s_out,
|
||||
acl,
|
||||
ejabberd_config],
|
||||
@@ -1364,22 +1439,7 @@ emit_deprecation_warning(Module, NewModule) ->
|
||||
now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
|
||||
MegaSecs * 1000000 + Secs.
|
||||
|
||||
-spec opt_type(hide_sensitive_log_data) -> fun((boolean()) -> boolean());
|
||||
(hosts) -> fun(([binary()]) -> [binary()]);
|
||||
(language) -> fun((binary()) -> binary());
|
||||
(max_fsm_queue) -> fun((pos_integer()) -> pos_integer());
|
||||
(default_db) -> fun((atom()) -> atom());
|
||||
(default_ram_db) -> fun((atom()) -> atom());
|
||||
(loglevel) -> fun((0..5) -> 0..5);
|
||||
(queue_dir) -> fun((binary()) -> binary());
|
||||
(queue_type) -> fun((ram | file) -> ram | file);
|
||||
(use_cache) -> fun((boolean()) -> boolean());
|
||||
(cache_size) -> fun((timeout()) -> timeout());
|
||||
(cache_missed) -> fun((boolean()) -> boolean());
|
||||
(cache_life_time) -> fun((timeout()) -> timeout());
|
||||
(shared_key) -> fun((binary()) -> binary());
|
||||
(node_start) -> fun((non_neg_integer()) -> non_neg_integer());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(hide_sensitive_log_data) ->
|
||||
fun (H) when is_boolean(H) -> H end;
|
||||
opt_type(hosts) ->
|
||||
@@ -1422,10 +1482,16 @@ opt_type(node_start) ->
|
||||
fun(I) when is_integer(I), I>=0 -> I end;
|
||||
opt_type(validate_stream) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
opt_type(fqdn) ->
|
||||
fun(Domain) when is_binary(Domain) ->
|
||||
[Domain];
|
||||
(Domains) ->
|
||||
[iolist_to_binary(Domain) || Domain <- Domains]
|
||||
end;
|
||||
opt_type(_) ->
|
||||
[hide_sensitive_log_data, hosts, language, max_fsm_queue,
|
||||
default_db, default_ram_db, queue_type, queue_dir, loglevel,
|
||||
use_cache, cache_size, cache_missed, cache_life_time,
|
||||
use_cache, cache_size, cache_missed, cache_life_time, fqdn,
|
||||
shared_key, node_start, validate_stream, negotiation_timeout].
|
||||
|
||||
-spec may_hide_data(any()) -> any().
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
|
||||
-include("ejabberd_ctl.hrl").
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-define(DEFAULT_VERSION, 1000000).
|
||||
@@ -169,15 +168,15 @@ process(["status"], _Version) ->
|
||||
{InternalStatus, ProvidedStatus} = init:get_status(),
|
||||
print("The node ~p is ~p with status: ~p~n",
|
||||
[node(), InternalStatus, ProvidedStatus]),
|
||||
case lists:keysearch(ejabberd, 1, application:which_applications()) of
|
||||
case lists:keymember(ejabberd, 1, application:which_applications()) of
|
||||
false ->
|
||||
EjabberdLogPath = ejabberd_logger:get_log_path(),
|
||||
print("ejabberd is not running in that node~n"
|
||||
"Check for error messages: ~s~n"
|
||||
"or other files in that directory.~n", [EjabberdLogPath]),
|
||||
?STATUS_ERROR;
|
||||
{value, {_, _, Version}} ->
|
||||
print("ejabberd ~s is running in that node~n", [Version]),
|
||||
true ->
|
||||
print("ejabberd ~s is running in that node~n", [ejabberd_config:get_version()]),
|
||||
?STATUS_SUCCESS
|
||||
end;
|
||||
|
||||
@@ -875,8 +874,7 @@ print(Format, Args) ->
|
||||
%% ["aaaa bbb ccc"].
|
||||
|
||||
|
||||
-spec opt_type(ejabberdctl_access_commands) -> fun((list()) -> list());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(ejabberdctl_access_commands) ->
|
||||
fun (V) when is_list(V) -> V end;
|
||||
opt_type(_) -> [ejabberdctl_access_commands].
|
||||
|
||||
@@ -380,11 +380,12 @@ safe_apply(Hook, Module, Function, Args) ->
|
||||
apply(Module, Function, Args)
|
||||
end
|
||||
catch E:R when E /= exit; R /= normal ->
|
||||
St = get_stacktrace(),
|
||||
?ERROR_MSG("Hook ~p crashed when running ~p:~p/~p:~n"
|
||||
"** Reason = ~p~n"
|
||||
"** Arguments = ~p",
|
||||
[Hook, Module, Function, length(Args),
|
||||
{E, R, get_stacktrace()}, Args]),
|
||||
{E, R, St}, Args]),
|
||||
'EXIT'
|
||||
end.
|
||||
|
||||
|
||||
+255
-191
@@ -24,24 +24,23 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_http).
|
||||
|
||||
-behaviour(ejabberd_listener).
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
%% External exports
|
||||
-export([start/2, start_link/2, become_controller/1,
|
||||
socket_type/0, receive_headers/1,
|
||||
transform_listen_option/2, listen_opt_type/1]).
|
||||
-export([start/2, start_link/2,
|
||||
accept/1, receive_headers/1, recv_file/2,
|
||||
transform_listen_option/2, listen_opt_type/1,
|
||||
listen_options/0]).
|
||||
|
||||
-export([init/2, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-include("xmpp.hrl").
|
||||
|
||||
-include("ejabberd_http.hrl").
|
||||
-include_lib("kernel/include/file.hrl").
|
||||
|
||||
-record(state, {sockmod,
|
||||
socket,
|
||||
@@ -50,7 +49,7 @@
|
||||
request_path,
|
||||
request_auth,
|
||||
request_keepalive,
|
||||
request_content_length,
|
||||
request_content_length = 0,
|
||||
request_lang = <<"en">>,
|
||||
%% XXX bard: request handlers are configured in
|
||||
%% ejabberd.cfg under the HTTP service. For example,
|
||||
@@ -85,6 +84,10 @@
|
||||
"org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
|
||||
"">>).
|
||||
|
||||
-define(RECV_BUF, 65536).
|
||||
-define(SEND_BUF, 65536).
|
||||
-define(MAX_POST_SIZE, 20971520). %% 20Mb
|
||||
|
||||
start(SockData, Opts) ->
|
||||
{ok,
|
||||
proc_lib:spawn(ejabberd_http, init,
|
||||
@@ -99,6 +102,7 @@ init({SockMod, Socket}, Opts) ->
|
||||
TLSEnabled = proplists:get_bool(tls, Opts),
|
||||
TLSOpts1 = lists:filter(fun ({ciphers, _}) -> true;
|
||||
({dhfile, _}) -> true;
|
||||
({cafile, _}) -> true;
|
||||
({protocol_options, _}) -> true;
|
||||
(_) -> false
|
||||
end,
|
||||
@@ -113,7 +117,7 @@ init({SockMod, Socket}, Opts) ->
|
||||
end,
|
||||
TLSOpts = [verify_none | TLSOpts3],
|
||||
{SockMod1, Socket1} = if TLSEnabled ->
|
||||
inet:setopts(Socket, [{recbuf, 8192}]),
|
||||
inet:setopts(Socket, [{recbuf, ?RECV_BUF}]),
|
||||
{ok, TLSSocket} = fast_tls:tcp_to_tls(Socket,
|
||||
TLSOpts),
|
||||
{fast_tls, TLSSocket};
|
||||
@@ -162,24 +166,47 @@ init({SockMod, Socket}, Opts) ->
|
||||
{error, _} -> State
|
||||
end.
|
||||
|
||||
become_controller(_Pid) ->
|
||||
accept(_Pid) ->
|
||||
ok.
|
||||
|
||||
socket_type() ->
|
||||
raw.
|
||||
|
||||
send_text(_State, none) ->
|
||||
ok;
|
||||
send_text(State, Text) ->
|
||||
case catch
|
||||
(State#state.sockmod):send(State#state.socket, Text)
|
||||
of
|
||||
ok -> ok;
|
||||
{error, timeout} ->
|
||||
?INFO_MSG("Timeout on ~p:send", [State#state.sockmod]),
|
||||
exit(normal);
|
||||
Error ->
|
||||
?DEBUG("Error in ~p:send: ~p",
|
||||
[State#state.sockmod, Error]),
|
||||
exit(normal)
|
||||
case (State#state.sockmod):send(State#state.socket, Text) of
|
||||
ok -> ok;
|
||||
{error, timeout} ->
|
||||
?INFO_MSG("Timeout on ~p:send", [State#state.sockmod]),
|
||||
exit(normal);
|
||||
Error ->
|
||||
?DEBUG("Error in ~p:send: ~p",
|
||||
[State#state.sockmod, Error]),
|
||||
exit(normal)
|
||||
end.
|
||||
|
||||
send_file(State, Fd, Size, FileName) ->
|
||||
try
|
||||
case State#state.sockmod of
|
||||
gen_tcp ->
|
||||
case file:sendfile(Fd, State#state.socket, 0, Size, []) of
|
||||
{ok, _} -> ok
|
||||
end;
|
||||
_ ->
|
||||
case file:read(Fd, ?SEND_BUF) of
|
||||
{ok, Data} ->
|
||||
send_text(State, Data),
|
||||
send_file(State, Fd, Size, FileName);
|
||||
eof ->
|
||||
ok
|
||||
end
|
||||
end
|
||||
catch _:{case_clause, {error, Why}} ->
|
||||
if Why /= closed ->
|
||||
?WARNING_MSG("Failed to read ~s: ~s",
|
||||
[FileName, file_format_error(Why)]),
|
||||
exit(normal);
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
end.
|
||||
|
||||
receive_headers(#state{trail = Trail} = State) ->
|
||||
@@ -348,8 +375,8 @@ get_transfer_protocol(RE, SockMod, HostPort) ->
|
||||
%% matches the requested URL path, and pass control to it. If none is
|
||||
%% found, answer with HTTP 404.
|
||||
|
||||
process([], _, _, _, _) -> ejabberd_web:error(not_found);
|
||||
process(Handlers, Request, Socket, SockMod, Trail) ->
|
||||
process([], _) -> ejabberd_web:error(not_found);
|
||||
process(Handlers, Request) ->
|
||||
{HandlerPathPrefix, HandlerModule, HandlerOpts, HandlersLeft} =
|
||||
case Handlers of
|
||||
[{Pfx, Mod} | Tail] ->
|
||||
@@ -369,14 +396,14 @@ process(Handlers, Request, Socket, SockMod, Trail) ->
|
||||
LocalPath = lists:nthtail(length(HandlerPathPrefix), Request#request.path),
|
||||
R = try
|
||||
HandlerModule:socket_handoff(
|
||||
LocalPath, Request, Socket, SockMod, Trail, HandlerOpts)
|
||||
LocalPath, Request, HandlerOpts)
|
||||
catch error:undef ->
|
||||
HandlerModule:process(LocalPath, Request)
|
||||
end,
|
||||
ejabberd_hooks:run(http_request_debug, [{LocalPath, Request}]),
|
||||
R;
|
||||
false ->
|
||||
process(HandlersLeft, Request, Socket, SockMod, Trail)
|
||||
process(HandlersLeft, Request)
|
||||
end.
|
||||
|
||||
extract_path_query(#state{request_method = Method,
|
||||
@@ -398,24 +425,29 @@ extract_path_query(#state{request_method = Method,
|
||||
extract_path_query(#state{request_method = Method,
|
||||
request_path = {abs_path, Path},
|
||||
request_content_length = Len,
|
||||
trail = Trail,
|
||||
sockmod = _SockMod,
|
||||
socket = _Socket} = State)
|
||||
when (Method =:= 'POST' orelse Method =:= 'PUT') andalso
|
||||
is_integer(Len) ->
|
||||
case recv_data(State, Len) of
|
||||
error -> {State, false};
|
||||
{NewState, Data} ->
|
||||
?DEBUG("client data: ~p~n", [Data]),
|
||||
when (Method =:= 'POST' orelse Method =:= 'PUT') andalso Len>0 ->
|
||||
case catch url_decode_q_split(Path) of
|
||||
{'EXIT', _} -> {NewState, false};
|
||||
{'EXIT', _} -> {State, false};
|
||||
{NPath, _Query} ->
|
||||
LPath = normalize_path([NPE
|
||||
|| NPE <- str:tokens(path_decode(NPath), <<"/">>)]),
|
||||
LQuery = case catch parse_urlencoded(Data) of
|
||||
{'EXIT', _Reason} -> [];
|
||||
LQ -> LQ
|
||||
end,
|
||||
{NewState, {LPath, LQuery, Data}}
|
||||
LPath = normalize_path(
|
||||
[NPE || NPE <- str:tokens(path_decode(NPath), <<"/">>)]),
|
||||
case Method of
|
||||
'PUT' ->
|
||||
{State, {LPath, [], Trail}};
|
||||
'POST' ->
|
||||
case recv_data(State) of
|
||||
{ok, Data} ->
|
||||
LQuery = case catch parse_urlencoded(Data) of
|
||||
{'EXIT', _Reason} -> [];
|
||||
LQ -> LQ
|
||||
end,
|
||||
{State, {LPath, LQuery, Data}};
|
||||
error ->
|
||||
{State, false}
|
||||
end
|
||||
end
|
||||
end;
|
||||
extract_path_query(State) ->
|
||||
@@ -428,16 +460,23 @@ process_request(#state{request_host = undefined,
|
||||
process_request(#state{request_method = Method,
|
||||
request_auth = Auth,
|
||||
request_lang = Lang,
|
||||
request_version = Version,
|
||||
sockmod = SockMod,
|
||||
socket = Socket,
|
||||
options = Options,
|
||||
request_host = Host,
|
||||
request_port = Port,
|
||||
request_tp = TP,
|
||||
request_content_length = Length,
|
||||
request_headers = RequestHeaders,
|
||||
request_handlers = RequestHandlers,
|
||||
custom_headers = CustomHeaders,
|
||||
trail = Trail} = State) ->
|
||||
custom_headers = CustomHeaders} = State) ->
|
||||
case proplists:get_value(<<"Expect">>, RequestHeaders, <<>>) of
|
||||
<<"100-", _/binary>> when Version == {1, 1} ->
|
||||
send_text(State, <<"HTTP/1.1 100 Continue\r\n\r\n">>);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
case extract_path_query(State) of
|
||||
{State2, false} ->
|
||||
{State2, make_bad_request(State)};
|
||||
@@ -459,7 +498,10 @@ process_request(#state{request_method = Method,
|
||||
path = LPath,
|
||||
q = LQuery,
|
||||
auth = Auth,
|
||||
data = Data,
|
||||
length = Length,
|
||||
sockmod = SockMod,
|
||||
socket = Socket,
|
||||
data = Data,
|
||||
lang = Lang,
|
||||
host = Host,
|
||||
port = Port,
|
||||
@@ -469,7 +511,7 @@ process_request(#state{request_method = Method,
|
||||
ip = IP},
|
||||
RequestHandlers1 = ejabberd_hooks:run_fold(
|
||||
http_request_handlers, RequestHandlers, [Host, Request]),
|
||||
Res = case process(RequestHandlers1, Request, Socket, SockMod, Trail) of
|
||||
Res = case process(RequestHandlers1, Request) of
|
||||
El when is_record(El, xmlel) ->
|
||||
make_xhtml_output(State, 200, CustomHeaders, El);
|
||||
{Status, Headers, El}
|
||||
@@ -482,6 +524,8 @@ process_request(#state{request_method = Method,
|
||||
when is_binary(Output) or is_list(Output) ->
|
||||
make_text_output(State, Status,
|
||||
Headers ++ CustomHeaders, Output);
|
||||
{Status, Headers, {file, FileName}} ->
|
||||
make_file_output(State, Status, Headers, FileName);
|
||||
{Status, Reason, Headers, Output}
|
||||
when is_binary(Output) or is_list(Output) ->
|
||||
make_text_output(State, Status, Reason,
|
||||
@@ -489,7 +533,7 @@ process_request(#state{request_method = Method,
|
||||
_ ->
|
||||
none
|
||||
end,
|
||||
{State2, Res}
|
||||
{State2#state{trail = <<>>}, Res}
|
||||
end.
|
||||
|
||||
make_bad_request(State) ->
|
||||
@@ -519,118 +563,98 @@ analyze_ip_xff({IPLast, Port}, XFF, Host) ->
|
||||
end,
|
||||
{IPClient, Port}.
|
||||
|
||||
is_ipchain_trusted([], _) -> false;
|
||||
is_ipchain_trusted(_UserIPs, all) -> true;
|
||||
is_ipchain_trusted(UserIPs, TrustedIPs) ->
|
||||
[] == UserIPs -- [<<"127.0.0.1">> | TrustedIPs].
|
||||
is_ipchain_trusted(UserIPs, Masks) ->
|
||||
lists:all(
|
||||
fun(IP) ->
|
||||
case inet:parse_address(binary_to_list(IP)) of
|
||||
{ok, IP2} ->
|
||||
lists:any(
|
||||
fun({Mask, MaskLen}) ->
|
||||
acl:ip_matches_mask(IP2, Mask, MaskLen)
|
||||
end, [{{127,0,0,1}, 8} | Masks]);
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end, UserIPs).
|
||||
|
||||
recv_data(State, Len) -> recv_data(State, Len, <<>>).
|
||||
|
||||
recv_data(State, 0, Acc) -> {State, Acc};
|
||||
recv_data(#state{trail = Trail} = State, Len, <<>>) when byte_size(Trail) > Len ->
|
||||
<<Data:Len/binary, Rest/binary>> = Trail,
|
||||
{State#state{trail = Rest}, Data};
|
||||
recv_data(State, Len, Acc) ->
|
||||
case State#state.trail of
|
||||
<<>> ->
|
||||
case (State#state.sockmod):recv(State#state.socket,
|
||||
min(Len, 16#4000000), 300000)
|
||||
of
|
||||
{ok, Data} ->
|
||||
recv_data(State, Len - byte_size(Data), <<Acc/binary, Data/binary>>);
|
||||
Err ->
|
||||
?DEBUG("Cannot receive HTTP data: ~p", [Err]),
|
||||
error
|
||||
recv_data(#state{request_content_length = Len}) when Len >= ?MAX_POST_SIZE ->
|
||||
error;
|
||||
recv_data(#state{request_content_length = Len, trail = Trail,
|
||||
sockmod = SockMod, socket = Socket}) ->
|
||||
NewLen = Len - byte_size(Trail),
|
||||
if NewLen > 0 ->
|
||||
case SockMod:recv(Socket, NewLen, 60000) of
|
||||
{ok, Data} -> {ok, <<Trail/binary, Data/binary>>};
|
||||
{error, _} -> error
|
||||
end;
|
||||
_ ->
|
||||
Trail = (State#state.trail),
|
||||
recv_data(State#state{trail = <<>>},
|
||||
Len - byte_size(Trail), <<Acc/binary, Trail/binary>>)
|
||||
true ->
|
||||
{ok, Trail}
|
||||
end.
|
||||
|
||||
make_xhtml_output(State, Status, Headers, XHTML) ->
|
||||
Data = case lists:member(html, Headers) of
|
||||
true ->
|
||||
iolist_to_binary([?HTML_DOCTYPE,
|
||||
fxml:element_to_binary(XHTML)]);
|
||||
_ ->
|
||||
iolist_to_binary([?XHTML_DOCTYPE,
|
||||
fxml:element_to_binary(XHTML)])
|
||||
end,
|
||||
Headers1 = case lists:keysearch(<<"Content-Type">>, 1,
|
||||
Headers)
|
||||
of
|
||||
{value, _} ->
|
||||
[{<<"Content-Length">>,
|
||||
integer_to_binary(byte_size(Data))}
|
||||
| Headers];
|
||||
_ ->
|
||||
[{<<"Content-Type">>, <<"text/html; charset=utf-8">>},
|
||||
{<<"Content-Length">>,
|
||||
integer_to_binary(byte_size(Data))}
|
||||
| Headers]
|
||||
end,
|
||||
HeadersOut = case {State#state.request_version,
|
||||
State#state.request_keepalive}
|
||||
of
|
||||
{{1, 1}, true} -> Headers1;
|
||||
{_, true} ->
|
||||
[{<<"Connection">>, <<"keep-alive">>} | Headers1];
|
||||
{_, false} ->
|
||||
[{<<"Connection">>, <<"close">>} | Headers1]
|
||||
end,
|
||||
Version = case State#state.request_version of
|
||||
{1, 1} -> <<"HTTP/1.1 ">>;
|
||||
_ -> <<"HTTP/1.0 ">>
|
||||
end,
|
||||
H = lists:map(fun ({Attr, Val}) ->
|
||||
[Attr, <<": ">>, Val, <<"\r\n">>];
|
||||
(_) -> []
|
||||
recv_file(#request{length = Len, data = Trail,
|
||||
sockmod = SockMod, socket = Socket}, Path) ->
|
||||
case file:open(Path, [write, exclusive, raw]) of
|
||||
{ok, Fd} ->
|
||||
Res = case file:write(Fd, Trail) of
|
||||
ok ->
|
||||
NewLen = max(0, Len - byte_size(Trail)),
|
||||
do_recv_file(NewLen, SockMod, Socket, Fd);
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end,
|
||||
HeadersOut),
|
||||
SL = [Version,
|
||||
integer_to_binary(Status), <<" ">>,
|
||||
code_to_phrase(Status), <<"\r\n">>],
|
||||
Data2 = case State#state.request_method of
|
||||
'HEAD' -> <<"">>;
|
||||
_ -> Data
|
||||
file:close(Fd),
|
||||
case Res of
|
||||
ok -> ok;
|
||||
{error, _} -> file:delete(Path)
|
||||
end,
|
||||
[SL, H, <<"\r\n">>, Data2].
|
||||
Res;
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
make_text_output(State, Status, Headers, Text) ->
|
||||
make_text_output(State, Status, <<"">>, Headers, Text).
|
||||
do_recv_file(0, _SockMod, _Socket, _Fd) ->
|
||||
ok;
|
||||
do_recv_file(Len, SockMod, Socket, Fd) ->
|
||||
ChunkLen = min(Len, ?RECV_BUF),
|
||||
case SockMod:recv(Socket, ChunkLen, timer:seconds(30)) of
|
||||
{ok, Data} ->
|
||||
case file:write(Fd, Data) of
|
||||
ok ->
|
||||
do_recv_file(Len-size(Data), SockMod, Socket, Fd);
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end;
|
||||
{error, _} ->
|
||||
{error, closed}
|
||||
end.
|
||||
|
||||
make_text_output(State, Status, Reason, Headers, Text) ->
|
||||
Data = iolist_to_binary(Text),
|
||||
Headers1 = case lists:keysearch(<<"Content-Type">>, 1,
|
||||
Headers)
|
||||
of
|
||||
{value, _} ->
|
||||
[{<<"Content-Length">>,
|
||||
integer_to_binary(byte_size(Data))}
|
||||
| Headers];
|
||||
_ ->
|
||||
[{<<"Content-Type">>, <<"text/html; charset=utf-8">>},
|
||||
{<<"Content-Length">>,
|
||||
integer_to_binary(byte_size(Data))}
|
||||
| Headers]
|
||||
make_headers(State, Status, Reason, Headers, Data) ->
|
||||
Len = if is_integer(Data) -> Data;
|
||||
true -> iolist_size(Data)
|
||||
end,
|
||||
Headers1 = [{<<"Content-Length">>, integer_to_binary(Len)} | Headers],
|
||||
Headers2 = case lists:keyfind(<<"Content-Type">>, 1, Headers) of
|
||||
{_, _} ->
|
||||
Headers1;
|
||||
false ->
|
||||
[{<<"Content-Type">>, <<"text/html; charset=utf-8">>}
|
||||
| Headers1]
|
||||
end,
|
||||
HeadersOut = case {State#state.request_version,
|
||||
State#state.request_keepalive}
|
||||
of
|
||||
{{1, 1}, true} -> Headers1;
|
||||
{_, true} ->
|
||||
[{<<"Connection">>, <<"keep-alive">>} | Headers1];
|
||||
{_, false} ->
|
||||
[{<<"Connection">>, <<"close">>} | Headers1]
|
||||
State#state.request_keepalive} of
|
||||
{{1, 1}, true} -> Headers2;
|
||||
{_, true} ->
|
||||
[{<<"Connection">>, <<"keep-alive">>} | Headers2];
|
||||
{_, false} ->
|
||||
[{<<"Connection">>, <<"close">>} | Headers2]
|
||||
end,
|
||||
Version = case State#state.request_version of
|
||||
{1, 1} -> <<"HTTP/1.1 ">>;
|
||||
_ -> <<"HTTP/1.0 ">>
|
||||
{1, 1} -> <<"HTTP/1.1 ">>;
|
||||
_ -> <<"HTTP/1.0 ">>
|
||||
end,
|
||||
H = lists:map(fun ({Attr, Val}) ->
|
||||
[Attr, <<": ">>, Val, <<"\r\n">>]
|
||||
end,
|
||||
HeadersOut),
|
||||
H = [[Attr, <<": ">>, Val, <<"\r\n">>] || {Attr, Val} <- HeadersOut],
|
||||
NewReason = case Reason of
|
||||
<<"">> -> code_to_phrase(Status);
|
||||
_ -> Reason
|
||||
@@ -638,11 +662,55 @@ make_text_output(State, Status, Reason, Headers, Text) ->
|
||||
SL = [Version,
|
||||
integer_to_binary(Status), <<" ">>,
|
||||
NewReason, <<"\r\n">>],
|
||||
[SL, H, <<"\r\n">>].
|
||||
|
||||
make_xhtml_output(State, Status, Headers, XHTML) ->
|
||||
Data = case State#state.request_method of
|
||||
'HEAD' -> <<"">>;
|
||||
_ ->
|
||||
DocType = case lists:member(html, Headers) of
|
||||
true -> ?HTML_DOCTYPE;
|
||||
false -> ?XHTML_DOCTYPE
|
||||
end,
|
||||
iolist_to_binary([DocType, fxml:element_to_binary(XHTML)])
|
||||
end,
|
||||
EncodedHdrs = make_headers(State, Status, <<"">>, Headers, Data),
|
||||
[EncodedHdrs, Data].
|
||||
|
||||
make_text_output(State, Status, Headers, Text) ->
|
||||
make_text_output(State, Status, <<"">>, Headers, Text).
|
||||
|
||||
make_text_output(State, Status, Reason, Headers, Text) ->
|
||||
Data = iolist_to_binary(Text),
|
||||
Data2 = case State#state.request_method of
|
||||
'HEAD' -> <<"">>;
|
||||
_ -> Data
|
||||
'HEAD' -> <<"">>;
|
||||
_ -> Data
|
||||
end,
|
||||
[SL, H, <<"\r\n">>, Data2].
|
||||
EncodedHdrs = make_headers(State, Status, Reason, Headers, Data2),
|
||||
[EncodedHdrs, Data2].
|
||||
|
||||
make_file_output(State, Status, Headers, FileName) ->
|
||||
case file:read_file_info(FileName) of
|
||||
{ok, #file_info{size = Size}} when State#state.request_method == 'HEAD' ->
|
||||
make_headers(State, Status, <<"">>, Headers, Size);
|
||||
{ok, #file_info{size = Size}} ->
|
||||
case file:open(FileName, [raw, read]) of
|
||||
{ok, Fd} ->
|
||||
EncodedHdrs = make_headers(State, Status, <<"">>, Headers, Size),
|
||||
send_text(State, EncodedHdrs),
|
||||
send_file(State, Fd, Size, FileName),
|
||||
file:close(Fd),
|
||||
none;
|
||||
{error, Why} ->
|
||||
Reason = file_format_error(Why),
|
||||
?ERROR_MSG("Failed to open ~s: ~s", [FileName, Reason]),
|
||||
make_text_output(State, 404, Reason, [], <<>>)
|
||||
end;
|
||||
{error, Why} ->
|
||||
Reason = file_format_error(Why),
|
||||
?ERROR_MSG("Failed to read info of ~s: ~s", [FileName, Reason]),
|
||||
make_text_output(State, 404, Reason, [], <<>>)
|
||||
end.
|
||||
|
||||
parse_lang(Langs) ->
|
||||
case str:tokens(Langs, <<",; ">>) of
|
||||
@@ -650,6 +718,12 @@ parse_lang(Langs) ->
|
||||
[] -> <<"en">>
|
||||
end.
|
||||
|
||||
file_format_error(Reason) ->
|
||||
case file:format_error(Reason) of
|
||||
"unknown POSIX error" -> atom_to_list(Reason);
|
||||
Text -> Text
|
||||
end.
|
||||
|
||||
% Code below is taken (with some modifications) from the yaws webserver, which
|
||||
% is distributed under the following license:
|
||||
%
|
||||
@@ -713,7 +787,8 @@ rest_dir(N, Path, <<_H, T/binary>>) -> rest_dir(N, Path, T).
|
||||
|
||||
expand_custom_headers(Headers) ->
|
||||
lists:map(fun({K, V}) ->
|
||||
{K, misc:expand_keyword(<<"@VERSION@">>, V, ?VERSION)}
|
||||
{K, misc:expand_keyword(<<"@VERSION@">>, V,
|
||||
ejabberd_config:get_version())}
|
||||
end, Headers).
|
||||
|
||||
code_to_phrase(100) -> <<"Continue">>;
|
||||
@@ -843,11 +918,11 @@ get_certfile(Opts) ->
|
||||
{_, CertFile} ->
|
||||
CertFile;
|
||||
false ->
|
||||
case ejabberd_pkix:get_certfile(?MYNAME) of
|
||||
case ejabberd_pkix:get_certfile(ejabberd_config:get_myname()) of
|
||||
{ok, CertFile} ->
|
||||
CertFile;
|
||||
error ->
|
||||
ejabberd_config:get_option({domain_certfile, ?MYNAME})
|
||||
ejabberd_config:get_option({domain_certfile, ejabberd_config:get_myname()})
|
||||
end
|
||||
end.
|
||||
|
||||
@@ -873,47 +948,26 @@ transform_listen_option({request_handlers, Hs}, Opts) ->
|
||||
transform_listen_option(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
-spec opt_type(trusted_proxies) -> fun((all | [binary()]) -> all | [binary()]);
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(trusted_proxies) ->
|
||||
fun (all) -> all;
|
||||
(TPs) -> [iolist_to_binary(TP) || TP <- TPs] end;
|
||||
(TPs) -> lists:filtermap(
|
||||
fun(TP) ->
|
||||
case acl:parse_ip_netmask(iolist_to_binary(TP)) of
|
||||
{ok, Ip, Mask} -> {true, {Ip, Mask}};
|
||||
_ -> false
|
||||
end
|
||||
end, TPs)
|
||||
end;
|
||||
opt_type(_) -> [trusted_proxies].
|
||||
|
||||
-spec listen_opt_type(tls) -> fun((boolean()) -> boolean());
|
||||
(certfile) -> fun((binary()) -> binary());
|
||||
(ciphers) -> fun((binary()) -> binary());
|
||||
(dhfile) -> fun((binary()) -> binary());
|
||||
(protocol_options) -> fun(([binary()]) -> binary());
|
||||
(tls_compression) -> fun((boolean()) -> boolean());
|
||||
(captcha) -> fun((boolean()) -> boolean());
|
||||
(register) -> fun((boolean()) -> boolean());
|
||||
(web_admin) -> fun((boolean()) -> boolean());
|
||||
(http_bind) -> fun((boolean()) -> boolean());
|
||||
(xmlrpc) -> fun((boolean()) -> boolean());
|
||||
(request_handlers) -> fun(([{binary(), atom()}]) ->
|
||||
[{binary(), atom()}]);
|
||||
(default_host) -> fun((binary()) -> binary());
|
||||
(custom_headers) -> fun(([{binary(), binary()}]) ->
|
||||
[{binary(), binary()}]);
|
||||
(atom()) -> [atom()].
|
||||
listen_opt_type(tls) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(certfile = Opt) ->
|
||||
fun(S) ->
|
||||
?WARNING_MSG("Listening option '~s' for ~s is deprecated, use "
|
||||
"'certfiles' global option instead", [Opt, ?MODULE]),
|
||||
ejabberd_pkix:add_certfile(S),
|
||||
ok = ejabberd_pkix:add_certfile(S),
|
||||
iolist_to_binary(S)
|
||||
end;
|
||||
listen_opt_type(ciphers) ->
|
||||
fun iolist_to_binary/1;
|
||||
listen_opt_type(dhfile) ->
|
||||
fun misc:try_read_file/1;
|
||||
listen_opt_type(protocol_options) ->
|
||||
fun(Options) -> str:join(Options, <<"|">>) end;
|
||||
listen_opt_type(tls_compression) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(captcha) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(register) ->
|
||||
@@ -940,13 +994,23 @@ listen_opt_type(request_handlers) ->
|
||||
end} || {Path, Mod} <- Hs2]
|
||||
end;
|
||||
listen_opt_type(default_host) ->
|
||||
fun(A) -> A end;
|
||||
fun iolist_to_binary/1;
|
||||
listen_opt_type(custom_headers) ->
|
||||
fun expand_custom_headers/1;
|
||||
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(backlog) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(_) ->
|
||||
%% TODO
|
||||
fun(A) -> A end.
|
||||
fun expand_custom_headers/1.
|
||||
|
||||
listen_options() ->
|
||||
[{certfile, undefined},
|
||||
{ciphers, undefined},
|
||||
{dhfile, undefined},
|
||||
{cafile, undefined},
|
||||
{protocol_options, undefined},
|
||||
{tls, false},
|
||||
{tls_compression, false},
|
||||
{captcha, false},
|
||||
{register, false},
|
||||
{web_admin, false},
|
||||
{http_bind, false},
|
||||
{xmlrpc, false},
|
||||
{request_handlers, []},
|
||||
{default_host, undefined},
|
||||
{custom_headers, []}].
|
||||
|
||||
+51
-63
@@ -23,21 +23,18 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
-module(ejabberd_http_ws).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('ecestari@process-one.net').
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
-behaviour(xmpp_socket).
|
||||
-behaviour(p1_fsm).
|
||||
|
||||
-export([start/1, start_link/1, init/1, handle_event/3,
|
||||
handle_sync_event/4, code_change/4, handle_info/3,
|
||||
terminate/3, send_xml/2, setopts/2, sockname/1,
|
||||
peername/1, controlling_process/2, become_controller/2,
|
||||
monitor/1, reset_stream/1, close/1, change_shaper/2,
|
||||
socket_handoff/6, opt_type/1]).
|
||||
peername/1, controlling_process/2, get_owner/1,
|
||||
reset_stream/1, close/1, change_shaper/2,
|
||||
socket_handoff/3, get_transport/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-include("xmpp.hrl").
|
||||
@@ -55,8 +52,8 @@
|
||||
timeout = ?WEBSOCKET_TIMEOUT :: non_neg_integer(),
|
||||
timer = make_ref() :: reference(),
|
||||
input = [] :: list(),
|
||||
waiting_input = false :: false | pid(),
|
||||
last_receiver = self() :: pid(),
|
||||
active = false :: boolean(),
|
||||
c2s_pid :: pid(),
|
||||
ws :: {#ws{}, pid()},
|
||||
rfc_compilant = undefined :: boolean() | undefined}).
|
||||
|
||||
@@ -105,15 +102,9 @@ peername({http_ws, _FsmRef, IP}) -> {ok, IP}.
|
||||
|
||||
controlling_process(_Socket, _Pid) -> ok.
|
||||
|
||||
become_controller(FsmRef, C2SPid) ->
|
||||
p1_fsm:send_all_state_event(FsmRef, {activate, C2SPid}).
|
||||
|
||||
close({http_ws, FsmRef, _IP}) ->
|
||||
catch p1_fsm:sync_send_all_state_event(FsmRef, close).
|
||||
|
||||
monitor({http_ws, FsmRef, _IP}) ->
|
||||
erlang:monitor(process, FsmRef).
|
||||
|
||||
reset_stream({http_ws, _FsmRef, _IP} = Socket) ->
|
||||
Socket.
|
||||
|
||||
@@ -121,9 +112,14 @@ change_shaper({http_ws, _FsmRef, _IP}, _Shaper) ->
|
||||
%% TODO???
|
||||
ok.
|
||||
|
||||
socket_handoff(LocalPath, Request, Socket, SockMod, Buf, Opts) ->
|
||||
ejabberd_websocket:socket_handoff(LocalPath, Request, Socket, SockMod,
|
||||
Buf, Opts, ?MODULE, fun get_human_html_xmlel/0).
|
||||
get_transport(_Socket) ->
|
||||
websocket.
|
||||
|
||||
get_owner({http_ws, FsmRef, _IP}) ->
|
||||
FsmRef.
|
||||
|
||||
socket_handoff(LocalPath, Request, Opts) ->
|
||||
ejabberd_websocket:socket_handoff(LocalPath, Request, Opts, ?MODULE, fun get_human_html_xmlel/0).
|
||||
|
||||
%%% Internal
|
||||
|
||||
@@ -139,39 +135,42 @@ init([{#ws{ip = IP, http_opts = HOpts}, _} = WS]) ->
|
||||
end, HOpts),
|
||||
Opts = ejabberd_c2s_config:get_c2s_limits() ++ SOpts,
|
||||
PingInterval = ejabberd_config:get_option(
|
||||
{websocket_ping_interval, ?MYNAME},
|
||||
{websocket_ping_interval, ejabberd_config:get_myname()},
|
||||
?PING_INTERVAL) * 1000,
|
||||
WSTimeout = ejabberd_config:get_option(
|
||||
{websocket_timeout, ?MYNAME},
|
||||
{websocket_timeout, ejabberd_config:get_myname()},
|
||||
?WEBSOCKET_TIMEOUT) * 1000,
|
||||
Socket = {http_ws, self(), IP},
|
||||
?DEBUG("Client connected through websocket ~p",
|
||||
[Socket]),
|
||||
xmpp_socket:start(ejabberd_c2s, ?MODULE, Socket,
|
||||
[{receiver, self()}|Opts]),
|
||||
Timer = erlang:start_timer(WSTimeout, self(), []),
|
||||
{ok, loop,
|
||||
#state{socket = Socket, timeout = WSTimeout,
|
||||
timer = Timer, ws = WS,
|
||||
ping_interval = PingInterval}}.
|
||||
|
||||
handle_event({activate, From}, StateName, StateData) ->
|
||||
case StateData#state.input of
|
||||
[] ->
|
||||
{next_state, StateName,
|
||||
StateData#state{waiting_input = From}};
|
||||
Input ->
|
||||
Receiver = From,
|
||||
lists:foreach(fun(I) when is_binary(I)->
|
||||
Receiver ! {tcp, StateData#state.socket, I};
|
||||
(I2) ->
|
||||
Receiver ! {tcp, StateData#state.socket, [I2]}
|
||||
end, Input),
|
||||
{next_state, StateName,
|
||||
StateData#state{input = [], waiting_input = false,
|
||||
last_receiver = Receiver}}
|
||||
case ejabberd_c2s:start({?MODULE, Socket}, [{receiver, self()}|Opts]) of
|
||||
{ok, C2SPid} ->
|
||||
ejabberd_c2s:accept(C2SPid),
|
||||
Timer = erlang:start_timer(WSTimeout, self(), []),
|
||||
{ok, loop,
|
||||
#state{socket = Socket, timeout = WSTimeout,
|
||||
timer = Timer, ws = WS, c2s_pid = C2SPid,
|
||||
ping_interval = PingInterval}};
|
||||
{error, Reason} ->
|
||||
{stop, Reason};
|
||||
ignore ->
|
||||
ignore
|
||||
end.
|
||||
|
||||
handle_event({activate, From}, StateName, State) ->
|
||||
State1 = case State#state.input of
|
||||
[] -> State#state{active = true};
|
||||
Input ->
|
||||
lists:foreach(
|
||||
fun(I) when is_binary(I)->
|
||||
From ! {tcp, State#state.socket, I};
|
||||
(I2) ->
|
||||
From ! {tcp, State#state.socket, [I2]}
|
||||
end, Input),
|
||||
State#state{active = false, input = []}
|
||||
end,
|
||||
{next_state, StateName, State1#state{c2s_pid = From}}.
|
||||
|
||||
handle_sync_event({send_xml, Packet}, _From, StateName,
|
||||
#state{ws = {_, WsPid}, rfc_compilant = R} = StateData) ->
|
||||
Packet2 = case {case R of undefined -> true; V -> V end, Packet} of
|
||||
@@ -235,14 +234,13 @@ handle_info(closed, _StateName, StateData) ->
|
||||
{stop, normal, StateData};
|
||||
handle_info({received, Packet}, StateName, StateDataI) ->
|
||||
{StateData, Parsed} = parse(StateDataI, Packet),
|
||||
SD = case StateData#state.waiting_input of
|
||||
SD = case StateData#state.active of
|
||||
false ->
|
||||
Input = StateData#state.input ++ if is_binary(Parsed) -> [Parsed]; true -> Parsed end,
|
||||
StateData#state{input = Input};
|
||||
Receiver ->
|
||||
Receiver ! {tcp, StateData#state.socket, Parsed},
|
||||
setup_timers(StateData#state{waiting_input = false,
|
||||
last_receiver = Receiver})
|
||||
true ->
|
||||
StateData#state.c2s_pid ! {tcp, StateData#state.socket, Parsed},
|
||||
setup_timers(StateData#state{active = false})
|
||||
end,
|
||||
{next_state, StateName, SD};
|
||||
handle_info(PingPong, StateName, StateData) when PingPong == ping orelse
|
||||
@@ -258,7 +256,7 @@ handle_info({timeout, Timer, _}, StateName,
|
||||
#state{ping_timer = Timer, ws = {_, WsPid}} = StateData) ->
|
||||
case StateData#state.pong_expected of
|
||||
false ->
|
||||
cancel_timer(StateData#state.ping_timer),
|
||||
misc:cancel_timer(StateData#state.ping_timer),
|
||||
PingTimer = erlang:start_timer(StateData#state.ping_interval,
|
||||
self(), []),
|
||||
WsPid ! {ping, <<>>},
|
||||
@@ -275,19 +273,13 @@ code_change(_OldVsn, StateName, StateData, _Extra) ->
|
||||
{ok, StateName, StateData}.
|
||||
|
||||
terminate(_Reason, _StateName, StateData) ->
|
||||
case StateData#state.waiting_input of
|
||||
false -> ok;
|
||||
Receiver ->
|
||||
?DEBUG("C2S Pid : ~p", [Receiver]),
|
||||
Receiver ! {tcp_closed, StateData#state.socket}
|
||||
end,
|
||||
ok.
|
||||
StateData#state.c2s_pid ! {tcp_closed, StateData#state.socket}.
|
||||
|
||||
setup_timers(StateData) ->
|
||||
cancel_timer(StateData#state.timer),
|
||||
misc:cancel_timer(StateData#state.timer),
|
||||
Timer = erlang:start_timer(StateData#state.timeout,
|
||||
self(), []),
|
||||
cancel_timer(StateData#state.ping_timer),
|
||||
misc:cancel_timer(StateData#state.ping_timer),
|
||||
PingTimer = case StateData#state.ping_interval of
|
||||
0 -> StateData#state.ping_timer;
|
||||
V -> erlang:start_timer(V, self(), [])
|
||||
@@ -295,10 +287,6 @@ setup_timers(StateData) ->
|
||||
StateData#state{timer = Timer, ping_timer = PingTimer,
|
||||
pong_expected = false}.
|
||||
|
||||
cancel_timer(Timer) ->
|
||||
erlang:cancel_timer(Timer),
|
||||
receive {timeout, Timer, _} -> ok after 0 -> ok end.
|
||||
|
||||
get_human_html_xmlel() ->
|
||||
Heading = <<"ejabberd ", (misc:atom_to_binary(?MODULE))/binary>>,
|
||||
#xmlel{name = <<"html">>,
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_idna.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : Support for IDNA (RFC3490)
|
||||
%%% Created : 10 Apr 2004 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_idna).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([domain_utf8_to_ascii/1,
|
||||
domain_ucs2_to_ascii/1,
|
||||
utf8_to_ucs2/1]).
|
||||
|
||||
-ifdef(TEST).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-endif.
|
||||
|
||||
-spec domain_utf8_to_ascii(binary()) -> false | binary().
|
||||
|
||||
domain_utf8_to_ascii(Domain) ->
|
||||
domain_ucs2_to_ascii(utf8_to_ucs2(Domain)).
|
||||
|
||||
utf8_to_ucs2(S) ->
|
||||
utf8_to_ucs2(binary_to_list(S), "").
|
||||
|
||||
utf8_to_ucs2([], R) -> lists:reverse(R);
|
||||
utf8_to_ucs2([C | S], R) when C < 128 ->
|
||||
utf8_to_ucs2(S, [C | R]);
|
||||
utf8_to_ucs2([C1, C2 | S], R) when C1 < 224 ->
|
||||
utf8_to_ucs2(S, [C1 band 31 bsl 6 bor C2 band 63 | R]);
|
||||
utf8_to_ucs2([C1, C2, C3 | S], R) when C1 < 240 ->
|
||||
utf8_to_ucs2(S,
|
||||
[C1 band 15 bsl 12 bor (C2 band 63 bsl 6) bor C3 band 63
|
||||
| R]).
|
||||
|
||||
-spec domain_ucs2_to_ascii(list()) -> false | binary().
|
||||
|
||||
domain_ucs2_to_ascii(Domain) ->
|
||||
case catch domain_ucs2_to_ascii1(Domain) of
|
||||
{'EXIT', _Reason} -> false;
|
||||
Res -> iolist_to_binary(Res)
|
||||
end.
|
||||
|
||||
domain_ucs2_to_ascii1(Domain) ->
|
||||
Parts = string:tokens(Domain,
|
||||
[46, 12290, 65294, 65377]),
|
||||
ASCIIParts = lists:map(fun (P) -> to_ascii(P) end,
|
||||
Parts),
|
||||
string:strip(lists:flatmap(fun (P) -> [$. | P] end,
|
||||
ASCIIParts),
|
||||
left, $.).
|
||||
|
||||
%% Domain names are already nameprep'ed in ejabberd, so we skiping this step
|
||||
to_ascii(Name) ->
|
||||
false = lists:any(fun (C)
|
||||
when (0 =< C) and (C =< 44) or
|
||||
(46 =< C) and (C =< 47)
|
||||
or (58 =< C) and (C =< 64)
|
||||
or (91 =< C) and (C =< 96)
|
||||
or (123 =< C) and (C =< 127) ->
|
||||
true;
|
||||
(_) -> false
|
||||
end,
|
||||
Name),
|
||||
case Name of
|
||||
[H | _] when H /= $- -> true = lists:last(Name) /= $-
|
||||
end,
|
||||
ASCIIName = case lists:any(fun (C) -> C > 127 end, Name)
|
||||
of
|
||||
true ->
|
||||
true = case Name of
|
||||
"xn--" ++ _ -> false;
|
||||
_ -> true
|
||||
end,
|
||||
"xn--" ++ punycode_encode(Name);
|
||||
false -> Name
|
||||
end,
|
||||
L = length(ASCIIName),
|
||||
true = (1 =< L) and (L =< 63),
|
||||
ASCIIName.
|
||||
|
||||
%%% PUNYCODE (RFC3492)
|
||||
|
||||
-define(BASE, 36).
|
||||
|
||||
-define(TMIN, 1).
|
||||
|
||||
-define(TMAX, 26).
|
||||
|
||||
-define(SKEW, 38).
|
||||
|
||||
-define(DAMP, 700).
|
||||
|
||||
-define(INITIAL_BIAS, 72).
|
||||
|
||||
-define(INITIAL_N, 128).
|
||||
|
||||
punycode_encode(Input) ->
|
||||
N = (?INITIAL_N),
|
||||
Delta = 0,
|
||||
Bias = (?INITIAL_BIAS),
|
||||
Basic = lists:filter(fun (C) -> C =< 127 end, Input),
|
||||
NonBasic = lists:filter(fun (C) -> C > 127 end, Input),
|
||||
L = length(Input),
|
||||
B = length(Basic),
|
||||
SNonBasic = lists:usort(NonBasic),
|
||||
Output1 = if B > 0 -> Basic ++ "-";
|
||||
true -> ""
|
||||
end,
|
||||
Output2 = punycode_encode1(Input, SNonBasic, B, B, L, N,
|
||||
Delta, Bias, ""),
|
||||
Output1 ++ Output2.
|
||||
|
||||
punycode_encode1(Input, [M | SNonBasic], B, H, L, N,
|
||||
Delta, Bias, Out)
|
||||
when H < L ->
|
||||
Delta1 = Delta + (M - N) * (H + 1),
|
||||
% let n = m
|
||||
{NewDelta, NewBias, NewH, NewOut} = lists:foldl(fun (C,
|
||||
{ADelta, ABias, AH,
|
||||
AOut}) ->
|
||||
if C < M ->
|
||||
{ADelta + 1,
|
||||
ABias, AH,
|
||||
AOut};
|
||||
C == M ->
|
||||
NewOut =
|
||||
punycode_encode_delta(ADelta,
|
||||
ABias,
|
||||
AOut),
|
||||
NewBias =
|
||||
adapt(ADelta,
|
||||
H +
|
||||
1,
|
||||
H
|
||||
==
|
||||
B),
|
||||
{0, NewBias,
|
||||
AH + 1,
|
||||
NewOut};
|
||||
true ->
|
||||
{ADelta,
|
||||
ABias, AH,
|
||||
AOut}
|
||||
end
|
||||
end,
|
||||
{Delta1, Bias, H, Out},
|
||||
Input),
|
||||
punycode_encode1(Input, SNonBasic, B, NewH, L, M + 1,
|
||||
NewDelta + 1, NewBias, NewOut);
|
||||
punycode_encode1(_Input, _SNonBasic, _B, _H, _L, _N,
|
||||
_Delta, _Bias, Out) ->
|
||||
lists:reverse(Out).
|
||||
|
||||
punycode_encode_delta(Delta, Bias, Out) ->
|
||||
punycode_encode_delta(Delta, Bias, Out, ?BASE).
|
||||
|
||||
punycode_encode_delta(Delta, Bias, Out, K) ->
|
||||
T = if K =< Bias -> ?TMIN;
|
||||
K >= Bias + (?TMAX) -> ?TMAX;
|
||||
true -> K - Bias
|
||||
end,
|
||||
if Delta < T -> [codepoint(Delta) | Out];
|
||||
true ->
|
||||
C = T + (Delta - T) rem ((?BASE) - T),
|
||||
punycode_encode_delta((Delta - T) div ((?BASE) - T),
|
||||
Bias, [codepoint(C) | Out], K + (?BASE))
|
||||
end.
|
||||
|
||||
adapt(Delta, NumPoints, FirstTime) ->
|
||||
Delta1 = if FirstTime -> Delta div (?DAMP);
|
||||
true -> Delta div 2
|
||||
end,
|
||||
Delta2 = Delta1 + Delta1 div NumPoints,
|
||||
adapt1(Delta2, 0).
|
||||
|
||||
adapt1(Delta, K) ->
|
||||
if Delta > ((?BASE) - (?TMIN)) * (?TMAX) div 2 ->
|
||||
adapt1(Delta div ((?BASE) - (?TMIN)), K + (?BASE));
|
||||
true ->
|
||||
K +
|
||||
((?BASE) - (?TMIN) + 1) * Delta div (Delta + (?SKEW))
|
||||
end.
|
||||
|
||||
codepoint(C) ->
|
||||
if (0 =< C) and (C =< 25) -> C + 97;
|
||||
(26 =< C) and (C =< 35) -> C + 22
|
||||
end.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Unit tests
|
||||
%%%===================================================================
|
||||
-ifdef(TEST).
|
||||
|
||||
acsii_test() ->
|
||||
?assertEqual(<<"test.org">>, domain_utf8_to_ascii(<<"test.org">>)).
|
||||
|
||||
utf8_test() ->
|
||||
?assertEqual(
|
||||
<<"xn--d1acufc.xn--p1ai">>,
|
||||
domain_utf8_to_ascii(
|
||||
<<208,180,208,190,208,188,208,181,208,189,46,209,128,209,132>>)).
|
||||
|
||||
-endif.
|
||||
+1
-1
@@ -49,7 +49,7 @@ start_link() ->
|
||||
-spec route(iq(), atom() | pid(), term(), non_neg_integer()) -> ok.
|
||||
route(#iq{type = T} = IQ, Proc, Ctx, Timeout) when T == set; T == get ->
|
||||
Expire = current_time() + Timeout,
|
||||
Rnd = randoms:get_string(),
|
||||
Rnd = p1_rand:get_string(),
|
||||
ID = encode_id(Expire, Rnd),
|
||||
ets:insert(?MODULE, {{Expire, Rnd}, Proc, Ctx}),
|
||||
gen_server:cast(?MODULE, {restart_timer, Expire}),
|
||||
|
||||
+627
-554
File diff suppressed because it is too large
Load Diff
+8
-60
@@ -32,10 +32,8 @@
|
||||
%% API
|
||||
-export([start/0, start_link/0]).
|
||||
|
||||
-export([route/1, process_iq/1,
|
||||
-export([route/1,
|
||||
get_features/1,
|
||||
register_iq_handler/4,
|
||||
unregister_iq_handler/2,
|
||||
bounce_resource_packet/1,
|
||||
host_up/1, host_down/1]).
|
||||
|
||||
@@ -47,15 +45,12 @@
|
||||
-export([route_iq/2, route_iq/3]).
|
||||
-deprecated([{route_iq, 2}, {route_iq, 3}]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
-include("xmpp.hrl").
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
-define(IQTABLE, local_iqtable).
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
@@ -72,36 +67,13 @@ start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [],
|
||||
[]).
|
||||
|
||||
-spec process_iq(iq()) -> any().
|
||||
process_iq(#iq{to = To, type = T, lang = Lang, sub_els = [El]} = Packet)
|
||||
when T == get; T == set ->
|
||||
XMLNS = xmpp:get_ns(El),
|
||||
Host = To#jid.lserver,
|
||||
case ets:lookup(?IQTABLE, {Host, XMLNS}) of
|
||||
[{_, Module, Function}] ->
|
||||
gen_iq_handler:handle(Host, Module, Function, Packet);
|
||||
[] ->
|
||||
Txt = <<"No module is handling this query">>,
|
||||
Err = xmpp:err_service_unavailable(Txt, Lang),
|
||||
ejabberd_router:route_error(Packet, Err)
|
||||
end;
|
||||
process_iq(#iq{type = T, lang = Lang, sub_els = SubEls} = Packet)
|
||||
when T == get; T == set ->
|
||||
Txt = case SubEls of
|
||||
[] -> <<"No child elements found">>;
|
||||
_ -> <<"Too many child elements">>
|
||||
end,
|
||||
Err = xmpp:err_bad_request(Txt, Lang),
|
||||
ejabberd_router:route_error(Packet, Err);
|
||||
process_iq(#iq{type = T}) when T == result; T == error ->
|
||||
ok.
|
||||
|
||||
-spec route(stanza()) -> any().
|
||||
route(Packet) ->
|
||||
try do_route(Packet)
|
||||
catch E:R ->
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("failed to route packet:~n~s~nReason = ~p",
|
||||
[xmpp:pp(Packet), {E, {R, erlang:get_stacktrace()}}])
|
||||
[xmpp:pp(Packet), {E, {R, St}}])
|
||||
end.
|
||||
|
||||
-spec route_iq(iq(), function()) -> ok.
|
||||
@@ -112,15 +84,6 @@ route_iq(IQ, Fun) ->
|
||||
route_iq(IQ, Fun, Timeout) ->
|
||||
ejabberd_router:route_iq(IQ, Fun, undefined, Timeout).
|
||||
|
||||
-spec register_iq_handler(binary(), binary(), module(), function()) -> ok.
|
||||
register_iq_handler(Host, XMLNS, Module, Fun) ->
|
||||
gen_server:cast(?MODULE,
|
||||
{register_iq_handler, Host, XMLNS, Module, Fun}).
|
||||
|
||||
-spec unregister_iq_handler(binary(), binary()) -> ok.
|
||||
unregister_iq_handler(Host, XMLNS) ->
|
||||
gen_server:cast(?MODULE, {unregister_iq_handler, Host, XMLNS}).
|
||||
|
||||
-spec bounce_resource_packet(stanza()) -> ok | stop.
|
||||
bounce_resource_packet(#presence{to = #jid{lresource = <<"">>}}) ->
|
||||
ok;
|
||||
@@ -135,12 +98,7 @@ bounce_resource_packet(Packet) ->
|
||||
|
||||
-spec get_features(binary()) -> [binary()].
|
||||
get_features(Host) ->
|
||||
get_features(ets:next(?IQTABLE, {Host, <<"">>}), Host, []).
|
||||
|
||||
get_features({Host, XMLNS}, Host, XMLNSs) ->
|
||||
get_features(ets:next(?IQTABLE, {Host, XMLNS}), Host, [XMLNS|XMLNSs]);
|
||||
get_features(_, _, XMLNSs) ->
|
||||
XMLNSs.
|
||||
gen_iq_handler:get_features(?MODULE, Host).
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
@@ -148,26 +106,16 @@ get_features(_, _, XMLNSs) ->
|
||||
|
||||
init([]) ->
|
||||
process_flag(trap_exit, true),
|
||||
lists:foreach(fun host_up/1, ?MYHOSTS),
|
||||
lists:foreach(fun host_up/1, ejabberd_config:get_myhosts()),
|
||||
ejabberd_hooks:add(host_up, ?MODULE, host_up, 10),
|
||||
ejabberd_hooks:add(host_down, ?MODULE, host_down, 100),
|
||||
catch ets:new(?IQTABLE, [named_table, public, ordered_set,
|
||||
{read_concurrency, true}]),
|
||||
gen_iq_handler:start(?MODULE),
|
||||
update_table(),
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok, {reply, Reply, State}.
|
||||
|
||||
handle_cast({register_iq_handler, Host, XMLNS, Module, Function},
|
||||
State) ->
|
||||
ets:insert(?IQTABLE,
|
||||
{{Host, XMLNS}, Module, Function}),
|
||||
{noreply, State};
|
||||
handle_cast({unregister_iq_handler, Host, XMLNS},
|
||||
State) ->
|
||||
ets:delete(?IQTABLE, {Host, XMLNS}),
|
||||
{noreply, State};
|
||||
handle_cast(_Msg, State) -> {noreply, State}.
|
||||
|
||||
handle_info({route, Packet}, State) ->
|
||||
@@ -178,7 +126,7 @@ handle_info(Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
lists:foreach(fun host_down/1, ?MYHOSTS),
|
||||
lists:foreach(fun host_down/1, ejabberd_config:get_myhosts()),
|
||||
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 10),
|
||||
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 100),
|
||||
ok.
|
||||
@@ -197,7 +145,7 @@ do_route(Packet) ->
|
||||
if To#jid.luser /= <<"">> ->
|
||||
ejabberd_sm:route(Packet);
|
||||
is_record(Packet, iq), To#jid.lresource == <<"">> ->
|
||||
process_iq(Packet);
|
||||
gen_iq_handler:handle(?MODULE, Packet);
|
||||
Type == result; Type == error ->
|
||||
ok;
|
||||
true ->
|
||||
|
||||
+17
-3
@@ -30,7 +30,6 @@
|
||||
-export([start/0, restart/0, reopen_log/0, rotate_log/0, get/0, set/1,
|
||||
get_log_path/0, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-type loglevel() :: 0 | 1 | 2 | 3 | 4 | 5.
|
||||
|
||||
@@ -59,7 +58,7 @@ get_log_path() ->
|
||||
undefined ->
|
||||
case os:getenv("EJABBERD_LOG_PATH") of
|
||||
false ->
|
||||
?LOG_PATH;
|
||||
"ejabberd.log";
|
||||
Path ->
|
||||
Path
|
||||
end
|
||||
@@ -144,10 +143,14 @@ do_start(Level) ->
|
||||
LogRotateSize = get_integer_env(log_rotate_size, 10*1024*1024),
|
||||
LogRotateCount = get_integer_env(log_rotate_count, 1),
|
||||
LogRateLimit = get_integer_env(log_rate_limit, 100),
|
||||
ConsoleLevel = case get_lager_version() >= "3.6.0" of
|
||||
true -> [{level, Level}];
|
||||
false -> Level
|
||||
end,
|
||||
application:set_env(lager, error_logger_hwm, LogRateLimit),
|
||||
application:set_env(
|
||||
lager, handlers,
|
||||
[{lager_console_backend, Level},
|
||||
[{lager_console_backend, ConsoleLevel},
|
||||
{lager_file_backend, [{file, ConsoleLog}, {level, Level}, {date, LogRotateDate},
|
||||
{count, LogRotateCount}, {size, LogRotateSize}]},
|
||||
{lager_file_backend, [{file, ErrorLog}, {level, error}, {date, LogRotateDate},
|
||||
@@ -215,6 +218,10 @@ set(LogLevel) when is_integer(LogLevel) ->
|
||||
ok
|
||||
end, gen_event:which_handlers(lager_event))
|
||||
end,
|
||||
case LogLevel of
|
||||
5 -> xmpp:set_config([{debug, true}]);
|
||||
_ -> ok
|
||||
end,
|
||||
{module, lager};
|
||||
set({_LogLevel, _}) ->
|
||||
error_logger:error_msg("custom loglevels are not supported for 'lager'"),
|
||||
@@ -249,3 +256,10 @@ get_lager_handlers() ->
|
||||
Result ->
|
||||
Result
|
||||
end.
|
||||
|
||||
get_lager_version() ->
|
||||
Apps = application:loaded_applications(),
|
||||
case lists:keyfind(lager, 1, Apps) of
|
||||
{_, _, Vsn} -> Vsn;
|
||||
false -> "0.0.0"
|
||||
end.
|
||||
|
||||
+3
-10
@@ -50,11 +50,11 @@
|
||||
config_reloaded/0,
|
||||
opt_type/1]).
|
||||
|
||||
-export([oauth_issue_token/3, oauth_list_tokens/0, oauth_revoke_token/1]).
|
||||
-export([get_commands_spec/0,
|
||||
oauth_issue_token/3, oauth_list_tokens/0, oauth_revoke_token/1]).
|
||||
|
||||
-include("xmpp.hrl").
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-include("ejabberd_http.hrl").
|
||||
@@ -646,14 +646,7 @@ logo() ->
|
||||
<<>>
|
||||
end.
|
||||
|
||||
-spec opt_type(oauth_expire) -> fun((non_neg_integer()) -> non_neg_integer());
|
||||
(oauth_access) -> fun((any()) -> any());
|
||||
(oauth_db_type) -> fun((atom()) -> atom());
|
||||
(oauth_cache_life_time) -> fun((timeout()) -> timeout());
|
||||
(oauth_cache_size) -> fun((timeout()) -> timeout());
|
||||
(oauth_use_cache) -> fun((boolean()) -> boolean());
|
||||
(oauth_cache_misse) -> fun((boolean()) -> boolean());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(oauth_expire) ->
|
||||
fun(I) when is_integer(I), I >= 0 -> I end;
|
||||
opt_type(oauth_access) ->
|
||||
|
||||
@@ -34,13 +34,12 @@
|
||||
clean/1,
|
||||
opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("ejabberd_oauth.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("jid.hrl").
|
||||
|
||||
init() ->
|
||||
rest:start(?MYNAME),
|
||||
rest:start(ejabberd_config:get_myname()),
|
||||
ok.
|
||||
|
||||
store(R) ->
|
||||
@@ -50,7 +49,7 @@ store(R) ->
|
||||
SJID = jid:encode({User, Server, <<"">>}),
|
||||
case rest:with_retry(
|
||||
post,
|
||||
[?MYNAME, Path, [],
|
||||
[ejabberd_config:get_myname(), Path, [],
|
||||
{[{<<"token">>, R#oauth_token.token},
|
||||
{<<"user">>, SJID},
|
||||
{<<"scope">>, R#oauth_token.scope},
|
||||
@@ -65,7 +64,7 @@ store(R) ->
|
||||
|
||||
lookup(Token) ->
|
||||
Path = path(<<"lookup">>),
|
||||
case rest:with_retry(post, [?MYNAME, Path, [],
|
||||
case rest:with_retry(post, [ejabberd_config:get_myname(), Path, [],
|
||||
{[{<<"token">>, Token}]}],
|
||||
2, 500) of
|
||||
{ok, 200, {Data}} ->
|
||||
@@ -93,8 +92,7 @@ path(Path) ->
|
||||
<<Base/binary, "/", Path/binary>>.
|
||||
|
||||
|
||||
-spec opt_type(ext_api_path_oauth) -> fun((binary()) -> binary());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(ext_api_path_oauth) ->
|
||||
fun (X) -> iolist_to_binary(X) end;
|
||||
opt_type(_) -> [ext_api_path_oauth].
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
clean/1]).
|
||||
|
||||
-include("ejabberd_oauth.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
-include("ejabberd_sql_pt.hrl").
|
||||
-include("jid.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -49,7 +48,7 @@ store(R) ->
|
||||
Scope = str:join(R#oauth_token.scope, <<" ">>),
|
||||
Expire = R#oauth_token.expire,
|
||||
case ?SQL_UPSERT(
|
||||
?MYNAME,
|
||||
ejabberd_config:get_myname(),
|
||||
"oauth_token",
|
||||
["!token=%(Token)s",
|
||||
"jid=%(SJID)s",
|
||||
@@ -63,7 +62,7 @@ store(R) ->
|
||||
|
||||
lookup(Token) ->
|
||||
case ejabberd_sql:sql_query(
|
||||
?MYNAME,
|
||||
ejabberd_config:get_myname(),
|
||||
?SQL("select @(jid)s, @(scope)s, @(expire)d"
|
||||
" from oauth_token where token=%(Token)s")) of
|
||||
{selected, [{SJID, Scope, Expire}]} ->
|
||||
@@ -79,6 +78,6 @@ lookup(Token) ->
|
||||
|
||||
clean(TS) ->
|
||||
ejabberd_sql:sql_query(
|
||||
?MYNAME,
|
||||
ejabberd_config:get_myname(),
|
||||
?SQL("delete from oauth_token where expire < %(TS)d")).
|
||||
|
||||
|
||||
+39
-20
@@ -40,7 +40,7 @@
|
||||
|
||||
-define(CHUNK_SIZE, 1024*20). %20k
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("scram.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("xmpp.hrl").
|
||||
-include("mod_privacy.hrl").
|
||||
@@ -92,7 +92,7 @@ import_file(FileName, State) ->
|
||||
|
||||
-spec export_server(binary()) -> any().
|
||||
export_server(Dir) ->
|
||||
export_hosts(?MYHOSTS, Dir).
|
||||
export_hosts(ejabberd_config:get_myhosts(), Dir).
|
||||
|
||||
-spec export_host(binary(), binary()) -> any().
|
||||
export_host(Dir, Host) ->
|
||||
@@ -404,6 +404,8 @@ process_user(#xmlel{name = <<"user">>, attrs = Attrs, children = Els},
|
||||
case ejabberd_auth:try_register(LUser, LServer, Pass) of
|
||||
ok ->
|
||||
process_user_els(Els, State#state{user = LUser});
|
||||
{error, invalid_password} when (Password == <<>>) ->
|
||||
process_user_els(Els, State#state{user = LUser});
|
||||
{error, Err} ->
|
||||
stop("Failed to create user '~s': ~p", [Name, Err])
|
||||
end
|
||||
@@ -433,7 +435,7 @@ process_user_el(#xmlel{name = Name, attrs = Attrs, children = Els} = El,
|
||||
{<<"query">>, ?NS_PRIVATE} ->
|
||||
process_private(xmpp:decode(El), State);
|
||||
{<<"vCard">>, ?NS_VCARD} ->
|
||||
process_vcard(El, State);
|
||||
process_vcard(xmpp:decode(El), State);
|
||||
{<<"offline-messages">>, NS} ->
|
||||
Msgs = [xmpp:decode(E, NS, [ignore_els]) || E <- Els],
|
||||
process_offline_msgs(Msgs, State);
|
||||
@@ -473,30 +475,47 @@ process_roster(RosterQuery, State = #state{user = U, server = S}) ->
|
||||
-spec process_privacy(privacy_query(), state()) -> {ok, state()} | {error, _}.
|
||||
process_privacy(#privacy_query{lists = Lists,
|
||||
default = Default,
|
||||
active = Active} = PrivacyQuery,
|
||||
active = Active},
|
||||
State = #state{user = U, server = S}) ->
|
||||
JID = jid:make(U, S),
|
||||
IQ = #iq{type = set, id = randoms:get_string(),
|
||||
from = JID, to = JID, sub_els = [PrivacyQuery]},
|
||||
case mod_privacy:process_iq(IQ) of
|
||||
#iq{type = error} = ResIQ ->
|
||||
#stanza_error{reason = Reason} = xmpp:get_error(ResIQ),
|
||||
if Reason == 'item-not-found', Lists == [],
|
||||
Active == undefined, Default /= undefined ->
|
||||
if Lists /= undefined ->
|
||||
process_privacy2(JID, #privacy_query{lists = Lists});
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
if Active /= undefined ->
|
||||
process_privacy2(JID, #privacy_query{active = Active});
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
if Default /= undefined ->
|
||||
process_privacy2(JID, #privacy_query{default = Default});
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
{ok, State}.
|
||||
|
||||
process_privacy2(JID, PQ) ->
|
||||
case mod_privacy:process_iq(#iq{type = set, id = p1_rand:get_string(),
|
||||
from = JID, to = JID,
|
||||
sub_els = [PQ]}) of
|
||||
#iq{type = error} = ResIQ ->
|
||||
#stanza_error{reason = Reason} = xmpp:get_error(ResIQ),
|
||||
if Reason /= 'item-not-found' ->
|
||||
%% Failed to set default list because there is no
|
||||
%% list with such name. We shouldn't stop here.
|
||||
{ok, State};
|
||||
true ->
|
||||
stop("Failed to write privacy: ~p", [Reason])
|
||||
end;
|
||||
_ ->
|
||||
{ok, State}
|
||||
end.
|
||||
stop("Failed to write default privacy: ~p", [Reason]);
|
||||
true ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec process_private(private(), state()) -> {ok, state()} | {error, _}.
|
||||
process_private(Private, State = #state{user = U, server = S}) ->
|
||||
JID = jid:make(U, S),
|
||||
IQ = #iq{type = set, id = randoms:get_string(),
|
||||
IQ = #iq{type = set, id = p1_rand:get_string(),
|
||||
from = JID, to = JID, sub_els = [Private]},
|
||||
case mod_private:process_sm_iq(IQ) of
|
||||
#iq{type = result} ->
|
||||
@@ -508,7 +527,7 @@ process_private(Private, State = #state{user = U, server = S}) ->
|
||||
-spec process_vcard(xmlel(), state()) -> {ok, state()} | {error, _}.
|
||||
process_vcard(El, State = #state{user = U, server = S}) ->
|
||||
JID = jid:make(U, S),
|
||||
IQ = #iq{type = set, id = randoms:get_string(),
|
||||
IQ = #iq{type = set, id = p1_rand:get_string(),
|
||||
from = JID, to = JID, sub_els = [El]},
|
||||
case mod_vcard:process_sm_iq(IQ) of
|
||||
#iq{type = result} ->
|
||||
|
||||
+170
-91
@@ -49,9 +49,10 @@
|
||||
-type bad_cert_reason() :: cert_expired | invalid_issuer | invalid_signature |
|
||||
name_not_permitted | missing_basic_constraint |
|
||||
invalid_key_usage | selfsigned_peer | unknown_sig_algo |
|
||||
unknown_ca | missing_priv_key.
|
||||
-type bad_cert() :: {bad_cert, bad_cert_reason()}.
|
||||
-type cert_error() :: not_cert | not_der | not_pem | encrypted.
|
||||
unknown_ca | missing_priv_key | unknown_key_algo |
|
||||
unknown_key_type | encrypted | not_der | not_cert |
|
||||
not_pem.
|
||||
-type cert_error() :: {bad_cert, bad_cert_reason()}.
|
||||
-export_type([cert_error/0]).
|
||||
|
||||
-define(CA_CACHE, ca_cache).
|
||||
@@ -59,12 +60,17 @@
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
-spec add_certfile(filename:filename())
|
||||
-spec add_certfile(file:filename())
|
||||
-> ok | {error, cert_error() | file:posix()}.
|
||||
add_certfile(Path) ->
|
||||
gen_server:call(?MODULE, {add_certfile, prep_path(Path)}).
|
||||
try gen_server:call(?MODULE, {add_certfile, prep_path(Path)})
|
||||
catch exit:{noproc, {gen_server, call, _}} ->
|
||||
%% This hack will be removed after moving
|
||||
%% the code into a separate repo
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec try_certfile(filename:filename()) -> binary().
|
||||
-spec try_certfile(file:filename()) -> binary().
|
||||
try_certfile(Path0) ->
|
||||
Path = prep_path(Path0),
|
||||
case load_certfile(Path) of
|
||||
@@ -76,13 +82,13 @@ route_registered(Route) ->
|
||||
gen_server:call(?MODULE, {route_registered, Route}).
|
||||
|
||||
-spec format_error(cert_error() | file:posix()) -> string().
|
||||
format_error(not_cert) ->
|
||||
format_error({bad_cert, not_cert}) ->
|
||||
"no PEM encoded certificates found";
|
||||
format_error(not_pem) ->
|
||||
format_error({bad_cert, not_pem}) ->
|
||||
"failed to decode from PEM format";
|
||||
format_error(not_der) ->
|
||||
format_error({bad_cert, not_der}) ->
|
||||
"failed to decode from DER format";
|
||||
format_error(encrypted) ->
|
||||
format_error({bad_cert, encrypted}) ->
|
||||
"encrypted certificate";
|
||||
format_error({bad_cert, cert_expired}) ->
|
||||
"certificate is no longer valid as its expiration date has passed";
|
||||
@@ -103,6 +109,10 @@ format_error({bad_cert, selfsigned_peer}) ->
|
||||
"self-signed certificate";
|
||||
format_error({bad_cert, unknown_sig_algo}) ->
|
||||
"certificate is signed using unknown algorithm";
|
||||
format_error({bad_cert, unknown_key_algo}) ->
|
||||
"unknown private key algorithm";
|
||||
format_error({bad_cert, unknown_key_type}) ->
|
||||
"private key is of unknown type";
|
||||
format_error({bad_cert, unknown_ca}) ->
|
||||
"certificate is signed by unknown CA";
|
||||
format_error({bad_cert, missing_priv_key}) ->
|
||||
@@ -128,7 +138,7 @@ get_certfile(Domain) ->
|
||||
|
||||
-spec get_certfile_no_default(binary()) -> {ok, binary()} | error.
|
||||
get_certfile_no_default(Domain) ->
|
||||
case ejabberd_idna:domain_utf8_to_ascii(Domain) of
|
||||
case xmpp_idna:domain_utf8_to_ascii(Domain) of
|
||||
false ->
|
||||
error;
|
||||
ASCIIDomain ->
|
||||
@@ -329,7 +339,8 @@ get_certfiles_from_config_options(_State) ->
|
||||
Host <- ejabberd_config:get_myhosts()]),
|
||||
[iolist_to_binary(P) || P <- lists:usort(Local ++ Global)].
|
||||
|
||||
-spec add_certfiles(state()) -> {ok, state()} | {error, bad_cert()}.
|
||||
-spec add_certfiles(state()) -> {ok, state()} |
|
||||
{error, cert_error() | file:posix()}.
|
||||
add_certfiles(State) ->
|
||||
?DEBUG("Reading certificates", []),
|
||||
Paths = get_certfiles_from_config_options(State),
|
||||
@@ -343,7 +354,8 @@ add_certfiles(State) ->
|
||||
{error, _} = Err -> Err
|
||||
end.
|
||||
|
||||
-spec add_certfiles(binary(), state()) -> {ok, state()} | {error, bad_cert()}.
|
||||
-spec add_certfiles(binary(), state()) -> {ok, state()} |
|
||||
{error, cert_error() | file:posix()}.
|
||||
add_certfiles(Host, State) ->
|
||||
State1 = lists:foldl(
|
||||
fun(Opt, AccState) ->
|
||||
@@ -363,8 +375,8 @@ add_certfiles(Host, State) ->
|
||||
{ok, State}
|
||||
end.
|
||||
|
||||
-spec add_certfile(file:filename_all(), state()) -> {ok, state()} |
|
||||
{{error, cert_error()}, state()}.
|
||||
-spec add_certfile(file:filename_all(), state()) ->
|
||||
{ok, state()} | {{error, cert_error() | file:posix()}, state()}.
|
||||
add_certfile(Path, State) ->
|
||||
case lists:member(Path, State#state.paths) of
|
||||
true ->
|
||||
@@ -386,30 +398,14 @@ add_certfile(Path, State) ->
|
||||
end
|
||||
end.
|
||||
|
||||
-spec build_chain_and_check(state()) -> ok | {error, bad_cert()}.
|
||||
-spec build_chain_and_check(state()) -> ok | {error, cert_error() | file:posix()}.
|
||||
build_chain_and_check(State) ->
|
||||
?DEBUG("Building certificates graph", []),
|
||||
CertPaths = get_cert_paths(maps:keys(State#state.certs), State#state.graph),
|
||||
?DEBUG("Finding matched certificate keys", []),
|
||||
case match_cert_keys(CertPaths, State#state.keys) of
|
||||
{ok, Chains} ->
|
||||
?DEBUG("Storing certificate chains", []),
|
||||
CertFilesWithDomains = store_certs(Chains, []),
|
||||
ets:delete_all_objects(?MODULE),
|
||||
lists:foreach(
|
||||
fun({Path, Domain}) ->
|
||||
fast_tls:add_certfile(Domain, Path),
|
||||
ets:insert(?MODULE, {Domain, Path})
|
||||
end, CertFilesWithDomains),
|
||||
?DEBUG("Validating certificates", []),
|
||||
Errors = validate(CertPaths, State#state.validate),
|
||||
?DEBUG("Subscribing to file events", []),
|
||||
lists:foreach(
|
||||
fun({Cert, Why}) ->
|
||||
Path = maps:get(Cert, State#state.certs),
|
||||
?WARNING_MSG("Failed to validate certificate from ~s: ~s",
|
||||
[Path, format_error(Why)])
|
||||
end, Errors);
|
||||
InvalidCerts = validate(CertPaths, State),
|
||||
SortedChains = sort_chains(Chains, InvalidCerts),
|
||||
store_certs(SortedChains, State);
|
||||
{error, Cert, Why} ->
|
||||
Path = maps:get(Cert, State#state.certs),
|
||||
?ERROR_MSG("Failed to build certificate chain for ~s: ~s",
|
||||
@@ -417,9 +413,35 @@ build_chain_and_check(State) ->
|
||||
{error, Why}
|
||||
end.
|
||||
|
||||
-spec store_certs([{[cert()], priv_key()}],
|
||||
[{binary(), binary()}]) -> [{binary(), binary()}].
|
||||
store_certs([{Certs, Key}|Chains], Acc) ->
|
||||
-spec store_certs([{[cert()], priv_key()}], state()) -> ok | {error, file:posix()}.
|
||||
store_certs(Chains, State) ->
|
||||
?DEBUG("Storing certificate chains", []),
|
||||
Res = lists:foldl(
|
||||
fun(_, {error, _} = Err) ->
|
||||
Err;
|
||||
({Certs, Key}, Acc) ->
|
||||
case store_cert(Certs, Key, State) of
|
||||
{ok, FileDoms} ->
|
||||
Acc ++ FileDoms;
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end
|
||||
end, [], Chains),
|
||||
case Res of
|
||||
{error, Why} ->
|
||||
{error, Why};
|
||||
FileDomains ->
|
||||
ets:delete_all_objects(?MODULE),
|
||||
lists:foreach(
|
||||
fun({Path, Domain}) ->
|
||||
fast_tls:add_certfile(Domain, Path),
|
||||
ets:insert(?MODULE, {Domain, Path})
|
||||
end, FileDomains)
|
||||
end.
|
||||
|
||||
-spec store_cert([cert()], priv_key(), state()) -> {ok, [{binary(), binary()}]} |
|
||||
{error, file:posix()}.
|
||||
store_cert(Certs, Key, State) ->
|
||||
CertPEMs = public_key:pem_encode(
|
||||
lists:map(
|
||||
fun(Cert) ->
|
||||
@@ -433,20 +455,51 @@ store_certs([{Certs, Key}|Chains], Acc) ->
|
||||
not_encrypted}]),
|
||||
PEMs = <<CertPEMs/binary, KeyPEM/binary>>,
|
||||
Cert = hd(Certs),
|
||||
Domains = xmpp_stream_pkix:get_cert_domains(Cert),
|
||||
FileName = filename:join(certs_dir(), str:sha(PEMs)),
|
||||
case file:write_file(FileName, PEMs) of
|
||||
ok ->
|
||||
file:change_mode(FileName, 8#600),
|
||||
NewAcc = [{FileName, Domain} || Domain <- Domains] ++ Acc,
|
||||
store_certs(Chains, NewAcc);
|
||||
{error, Why} ->
|
||||
case xmpp_stream_pkix:get_cert_domains(Cert) of
|
||||
[] ->
|
||||
Path = maps:get(Cert, State#state.certs),
|
||||
?WARNING_MSG("Certificate from ~s doesn't define "
|
||||
"any domain names", [Path]),
|
||||
{ok, [{FileName, <<"">>}]};
|
||||
Domains ->
|
||||
{ok, [{FileName, Domain} || Domain <- Domains]}
|
||||
end;
|
||||
{error, Why} = Err ->
|
||||
?ERROR_MSG("Failed to write to ~s: ~s",
|
||||
[FileName, file:format_error(Why)]),
|
||||
store_certs(Chains, [])
|
||||
end;
|
||||
store_certs([], Acc) ->
|
||||
Acc.
|
||||
Err
|
||||
end.
|
||||
|
||||
-spec sort_chains([{[cert()], priv_key()}], [cert()]) -> [{[cert()], priv_key()}].
|
||||
sort_chains(Chains, InvalidCerts) ->
|
||||
lists:sort(
|
||||
fun({[Cert1|_], _}, {[Cert2|_], _}) ->
|
||||
IsValid1 = not lists:member(Cert1, InvalidCerts),
|
||||
IsValid2 = not lists:member(Cert2, InvalidCerts),
|
||||
if IsValid1 and not IsValid2 ->
|
||||
false;
|
||||
IsValid2 and not IsValid1 ->
|
||||
true;
|
||||
true ->
|
||||
compare_expiration_date(Cert1, Cert2)
|
||||
end
|
||||
end, Chains).
|
||||
|
||||
%% Returns true if the first certificate has sooner expiration date
|
||||
-spec compare_expiration_date(cert(), cert()) -> boolean().
|
||||
compare_expiration_date(#'OTPCertificate'{
|
||||
tbsCertificate =
|
||||
#'OTPTBSCertificate'{
|
||||
validity = #'Validity'{notAfter = After1}}},
|
||||
#'OTPCertificate'{
|
||||
tbsCertificate =
|
||||
#'OTPTBSCertificate'{
|
||||
validity = #'Validity'{notAfter = After2}}}) ->
|
||||
get_timestamp(After1) =< get_timestamp(After2).
|
||||
|
||||
-spec load_certfile(file:filename_all()) -> {ok, [cert()], [priv_key()]} |
|
||||
{error, cert_error() | file:posix()}.
|
||||
@@ -472,57 +525,69 @@ pem_decode(Data) ->
|
||||
(_) -> false
|
||||
end, Objects) of
|
||||
{[], []} ->
|
||||
{error, not_cert};
|
||||
{error, {bad_cert, not_cert}};
|
||||
{Certs, PrivKeys} ->
|
||||
{ok, Certs, PrivKeys}
|
||||
end
|
||||
end
|
||||
catch _:_ ->
|
||||
{error, not_pem}
|
||||
catch E:R ->
|
||||
St = erlang:get_stacktrace(),
|
||||
?DEBUG("PEM decoding stacktrace: ~p", [{E, {R, St}}]),
|
||||
{error, {bad_cert, not_pem}}
|
||||
end.
|
||||
|
||||
-spec decode_certs([public_key:pem_entry()]) -> {[cert()], [priv_key()]} |
|
||||
{error, not_der | encrypted}.
|
||||
-spec decode_certs([public_key:pem_entry()]) -> [cert() | priv_key()] |
|
||||
{error, cert_error()}.
|
||||
decode_certs(PemEntries) ->
|
||||
try lists:foldr(
|
||||
fun(_, {error, _} = Err) ->
|
||||
Err;
|
||||
({_, _, Flag}, _) when Flag /= not_encrypted ->
|
||||
{error, encrypted};
|
||||
({'Certificate', Der, _}, Acc) ->
|
||||
[public_key:pkix_decode_cert(Der, otp)|Acc];
|
||||
({'PrivateKeyInfo', Der, not_encrypted}, Acc) ->
|
||||
#'PrivateKeyInfo'{privateKeyAlgorithm =
|
||||
#'PrivateKeyInfo_privateKeyAlgorithm'{
|
||||
algorithm = Algo},
|
||||
privateKey = Key} =
|
||||
public_key:der_decode('PrivateKeyInfo', Der),
|
||||
case Algo of
|
||||
?'rsaEncryption' ->
|
||||
[public_key:der_decode(
|
||||
'RSAPrivateKey', iolist_to_binary(Key))|Acc];
|
||||
?'id-dsa' ->
|
||||
[public_key:der_decode(
|
||||
'DSAPrivateKey', iolist_to_binary(Key))|Acc];
|
||||
?'id-ecPublicKey' ->
|
||||
[public_key:der_decode(
|
||||
'ECPrivateKey', iolist_to_binary(Key))|Acc];
|
||||
_ ->
|
||||
Acc
|
||||
end;
|
||||
({Tag, Der, _}, Acc) when Tag == 'RSAPrivateKey';
|
||||
Tag == 'DSAPrivateKey';
|
||||
Tag == 'ECPrivateKey' ->
|
||||
[public_key:der_decode(Tag, Der)|Acc];
|
||||
(_, Acc) ->
|
||||
Acc
|
||||
end, [], PemEntries)
|
||||
catch _:_ ->
|
||||
{error, not_der}
|
||||
try lists:flatmap(
|
||||
fun({Tag, Der, Flag}) ->
|
||||
decode_cert(Tag, Der, Flag)
|
||||
end, PemEntries)
|
||||
catch _:{bad_cert, _} = Err ->
|
||||
{error, Err};
|
||||
E:R ->
|
||||
St = erlang:get_stacktrace(),
|
||||
?DEBUG("DER decoding stacktrace: ~p", [{E, {R, St}}]),
|
||||
{error, {bad_cert, not_der}}
|
||||
end.
|
||||
|
||||
-spec validate([{path, [cert()]}], boolean()) -> [{cert(), bad_cert()}].
|
||||
validate(Paths, true) ->
|
||||
-spec decode_cert(atom(), binary(), atom()) -> [cert() | priv_key()].
|
||||
decode_cert(_, _, Flag) when Flag /= not_encrypted ->
|
||||
erlang:error({bad_cert, encrypted});
|
||||
decode_cert('Certificate', Der, _) ->
|
||||
[public_key:pkix_decode_cert(Der, otp)];
|
||||
decode_cert('PrivateKeyInfo', Der, not_encrypted) ->
|
||||
case public_key:der_decode('PrivateKeyInfo', Der) of
|
||||
#'PrivateKeyInfo'{privateKeyAlgorithm =
|
||||
#'PrivateKeyInfo_privateKeyAlgorithm'{
|
||||
algorithm = Algo},
|
||||
privateKey = Key} ->
|
||||
KeyBin = iolist_to_binary(Key),
|
||||
case Algo of
|
||||
?'rsaEncryption' ->
|
||||
[public_key:der_decode('RSAPrivateKey', KeyBin)];
|
||||
?'id-dsa' ->
|
||||
[public_key:der_decode('DSAPrivateKey', KeyBin)];
|
||||
?'id-ecPublicKey' ->
|
||||
[public_key:der_decode('ECPrivateKey', KeyBin)];
|
||||
_ ->
|
||||
erlang:error({bad_cert, unknown_key_algo})
|
||||
end;
|
||||
#'RSAPrivateKey'{} = Key -> [Key];
|
||||
#'DSAPrivateKey'{} = Key -> [Key];
|
||||
#'ECPrivateKey'{} = Key -> [Key];
|
||||
_ -> erlang:error({bad_cert, unknown_key_type})
|
||||
end;
|
||||
decode_cert(Tag, Der, _) when Tag == 'RSAPrivateKey';
|
||||
Tag == 'DSAPrivateKey';
|
||||
Tag == 'ECPrivateKey' ->
|
||||
[public_key:der_decode(Tag, Der)];
|
||||
decode_cert(_, _, _) ->
|
||||
[].
|
||||
|
||||
-spec validate([{path, [cert()]}], state()) -> [cert()].
|
||||
validate(Paths, #state{validate = true} = State) ->
|
||||
?DEBUG("Validating certificates", []),
|
||||
{ok, Re} = re:compile("^[a-f0-9]+\\.[0-9]+$", [unicode]),
|
||||
Hashes = case file:list_dir(ca_dir()) of
|
||||
{ok, Files} ->
|
||||
@@ -551,13 +616,16 @@ validate(Paths, true) ->
|
||||
ok ->
|
||||
false;
|
||||
{error, Cert, Reason} ->
|
||||
{true, {Cert, Reason}}
|
||||
File = maps:get(Cert, State#state.certs),
|
||||
?WARNING_MSG("Failed to validate certificate from ~s: ~s",
|
||||
[File, format_error(Reason)]),
|
||||
{true, Cert}
|
||||
end
|
||||
end, Paths);
|
||||
validate(_, _) ->
|
||||
[].
|
||||
|
||||
-spec validate_path([cert()], dict:dict()) -> ok | {error, cert(), bad_cert()}.
|
||||
-spec validate_path([cert()], dict:dict()) -> ok | {error, cert(), cert_error()}.
|
||||
validate_path([Cert|_] = Certs, Cache) ->
|
||||
case find_local_issuer(Cert, Cache) of
|
||||
{ok, IssuerCert} ->
|
||||
@@ -715,6 +783,7 @@ do_read_ca_file(Path) ->
|
||||
-spec match_cert_keys([{path, [cert()]}], [priv_key()])
|
||||
-> {ok, [{cert(), priv_key()}]} | {error, {bad_cert, missing_priv_key}}.
|
||||
match_cert_keys(CertPaths, PrivKeys) ->
|
||||
?DEBUG("Finding matched certificate keys", []),
|
||||
KeyPairs = [{pubkey_from_privkey(PrivKey), PrivKey} || PrivKey <- PrivKeys],
|
||||
match_cert_keys(CertPaths, KeyPairs, []).
|
||||
|
||||
@@ -763,6 +832,7 @@ pubkey_from_privkey(#'ECPrivateKey'{publicKey = Key}) ->
|
||||
|
||||
-spec get_cert_paths([cert()], digraph:graph()) -> [{path, [cert()]}].
|
||||
get_cert_paths(Certs, G) ->
|
||||
?DEBUG("Building certificates graph", []),
|
||||
{NewCerts, OldCerts} =
|
||||
lists:partition(
|
||||
fun(Cert) ->
|
||||
@@ -820,7 +890,7 @@ get_cert_path(G, [Root|_] = Acc) ->
|
||||
end, Es)
|
||||
end.
|
||||
|
||||
-spec prep_path(filename:filename()) -> binary().
|
||||
-spec prep_path(file:filename()) -> binary().
|
||||
prep_path(Path0) ->
|
||||
case filename:pathtype(Path0) of
|
||||
relative ->
|
||||
@@ -838,6 +908,15 @@ short_name_hash(_) ->
|
||||
"".
|
||||
-endif.
|
||||
|
||||
-spec get_timestamp({utcTime | generalTime, string()}) -> string().
|
||||
get_timestamp({utcTime, [Y1,Y2|T]}) ->
|
||||
case list_to_integer([Y1,Y2]) of
|
||||
N when N >= 50 -> [$1,$9,Y1,Y2|T];
|
||||
_ -> [$2,$0,Y1,Y2|T]
|
||||
end;
|
||||
get_timestamp({generalTime, TS}) ->
|
||||
TS.
|
||||
|
||||
wildcard(Path) when is_binary(Path) ->
|
||||
wildcard(binary_to_list(Path));
|
||||
wildcard(Path) ->
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
-export([start_link/0, init/1, opt_type/1,
|
||||
config_reloaded/0, start_host/1, stop_host/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
start_link() ->
|
||||
@@ -56,7 +55,7 @@ get_specs() ->
|
||||
{ok, Spec} -> [Spec];
|
||||
undefined -> []
|
||||
end
|
||||
end, ?MYHOSTS).
|
||||
end, ejabberd_config:get_myhosts()).
|
||||
|
||||
-spec get_spec(binary()) -> {ok, supervisor:child_spec()} | undefined.
|
||||
get_spec(Host) ->
|
||||
@@ -72,7 +71,7 @@ get_spec(Host) ->
|
||||
|
||||
-spec config_reloaded() -> ok.
|
||||
config_reloaded() ->
|
||||
lists:foreach(fun start_host/1, ?MYHOSTS).
|
||||
lists:foreach(fun reload_host/1, ejabberd_config:get_myhosts()).
|
||||
|
||||
-spec start_host(binary()) -> ok.
|
||||
start_host(Host) ->
|
||||
@@ -97,6 +96,10 @@ stop_host(Host) ->
|
||||
supervisor:delete_child(?MODULE, SupName),
|
||||
ok.
|
||||
|
||||
-spec reload_host(binary()) -> ok.
|
||||
reload_host(Host) ->
|
||||
ejabberd_sql_sup:reload(Host).
|
||||
|
||||
%% Returns {true, App} if we have configured sql for the given host
|
||||
needs_sql(Host) ->
|
||||
LHost = jid:nameprep(Host),
|
||||
@@ -109,9 +112,7 @@ needs_sql(Host) ->
|
||||
undefined -> false
|
||||
end.
|
||||
|
||||
-type sql_type() :: mysql | pgsql | sqlite | mssql | odbc.
|
||||
-spec opt_type(sql_type) -> fun((sql_type()) -> sql_type());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(sql_type) ->
|
||||
fun (mysql) -> mysql;
|
||||
(pgsql) -> pgsql;
|
||||
|
||||
+41
-5
@@ -33,10 +33,10 @@
|
||||
%% API
|
||||
-export([start_link/1, get_proc/1, get_connection/1, q/1, qp/1, format_error/1]).
|
||||
%% Commands
|
||||
-export([multi/1, get/1, set/2, del/1,
|
||||
-export([multi/1, get/1, set/2, del/1, info/1,
|
||||
sadd/2, srem/2, smembers/1, sismember/2, scard/1,
|
||||
hget/2, hset/3, hdel/2, hlen/1, hgetall/1, hkeys/1,
|
||||
subscribe/1, publish/2]).
|
||||
subscribe/1, publish/2, script_load/1, evalsha/3]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
@@ -50,7 +50,6 @@
|
||||
-define(CALL_TIMEOUT, 60*1000). %% 60 seconds
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
-record(state, {connection :: pid() | undefined,
|
||||
num :: pos_integer(),
|
||||
@@ -62,6 +61,9 @@
|
||||
-type redis_reply() :: binary() | [binary()].
|
||||
-type redis_command() :: [binary()].
|
||||
-type redis_pipeline() :: [redis_command()].
|
||||
-type redis_info() :: server | clients | memory | persistence |
|
||||
stats | replication | cpu | commandstats |
|
||||
cluster | keyspace | default | all.
|
||||
-type state() :: #state{}.
|
||||
|
||||
-export_type([error_reason/0]).
|
||||
@@ -317,6 +319,40 @@ publish(Channel, Data) ->
|
||||
tr_enq(Cmd, Stack)
|
||||
end.
|
||||
|
||||
-spec script_load(iodata()) -> {ok, binary()} | redis_error().
|
||||
script_load(Data) ->
|
||||
case erlang:get(?TR_STACK) of
|
||||
undefined ->
|
||||
q([<<"SCRIPT">>, <<"LOAD">>, Data]);
|
||||
_ ->
|
||||
erlang:error(transaction_unsupported)
|
||||
end.
|
||||
|
||||
-spec evalsha(binary(), [iodata()], [iodata()]) -> {ok, binary()} | redis_error().
|
||||
evalsha(SHA, Keys, Args) ->
|
||||
case erlang:get(?TR_STACK) of
|
||||
undefined ->
|
||||
q([<<"EVALSHA">>, SHA, length(Keys)|Keys ++ Args]);
|
||||
_ ->
|
||||
erlang:error(transaction_unsupported)
|
||||
end.
|
||||
|
||||
-spec info(redis_info()) -> {ok, [{atom(), binary()}]} | redis_error().
|
||||
info(Type) ->
|
||||
case erlang:get(?TR_STACK) of
|
||||
undefined ->
|
||||
case q([<<"INFO">>, misc:atom_to_binary(Type)]) of
|
||||
{ok, Info} ->
|
||||
Lines = binary:split(Info, <<"\r\n">>, [global]),
|
||||
KVs = [binary:split(Line, <<":">>) || Line <- Lines],
|
||||
{ok, [{misc:binary_to_atom(K), V} || [K, V] <- KVs]};
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end;
|
||||
_ ->
|
||||
erlang:error(transaction_unsupported)
|
||||
end.
|
||||
|
||||
%%%===================================================================
|
||||
%%% gen_server callbacks
|
||||
%%%===================================================================
|
||||
@@ -438,7 +474,7 @@ connect(#state{num = Num}) ->
|
||||
erlang:error(Why)
|
||||
end
|
||||
catch _:Reason ->
|
||||
Timeout = randoms:uniform(
|
||||
Timeout = p1_rand:uniform(
|
||||
min(10, ejabberd_redis_sup:get_pool_size())),
|
||||
?ERROR_MSG("Redis connection #~p at ~s:~p has failed: ~p; "
|
||||
"reconnecting in ~p seconds",
|
||||
@@ -503,7 +539,7 @@ log_error(Cmd, Reason) ->
|
||||
|
||||
-spec get_rnd_id() -> pos_integer().
|
||||
get_rnd_id() ->
|
||||
randoms:round_robin(ejabberd_redis_sup:get_pool_size() - 1) + 2.
|
||||
p1_rand:round_robin(ejabberd_redis_sup:get_pool_size() - 1) + 2.
|
||||
|
||||
-spec get_result([{error, atom() | binary()} | {ok, iodata()}]) ->
|
||||
{ok, [redis_reply()]} | {error, binary()}.
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
%% Supervisor callbacks
|
||||
-export([init/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-define(DEFAULT_POOL_SIZE, 10).
|
||||
@@ -98,7 +97,7 @@ init([]) ->
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
is_redis_configured() ->
|
||||
lists:any(fun is_redis_configured/1, ?MYHOSTS).
|
||||
lists:any(fun is_redis_configured/1, ejabberd_config:get_myhosts()).
|
||||
|
||||
is_redis_configured(Host) ->
|
||||
ServerConfigured = ejabberd_config:has_option({redis_server, Host}),
|
||||
@@ -127,14 +126,7 @@ get_pool_size() ->
|
||||
iolist_to_list(IOList) ->
|
||||
binary_to_list(iolist_to_binary(IOList)).
|
||||
|
||||
-spec opt_type(redis_connect_timeout) -> fun((pos_integer()) -> pos_integer());
|
||||
(redis_db) -> fun((non_neg_integer()) -> non_neg_integer());
|
||||
(redis_password) -> fun((binary()) -> binary());
|
||||
(redis_port) -> fun((0..65535) -> 0..65535);
|
||||
(redis_server) -> fun((binary()) -> binary());
|
||||
(redis_pool_size) -> fun((pos_integer()) -> pos_integer());
|
||||
(redis_queue_type) -> fun((ram | file) -> ram | file);
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(redis_connect_timeout) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(redis_db) ->
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-record(state, {pid = self() :: pid()}).
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
transform_options/1, get_random_pid/0,
|
||||
host_up/1, config_reloaded/0, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-define(DEFAULT_POOL_SIZE, 10).
|
||||
@@ -74,7 +73,7 @@ config_reloaded() ->
|
||||
end.
|
||||
|
||||
is_riak_configured() ->
|
||||
lists:any(fun is_riak_configured/1, ?MYHOSTS).
|
||||
lists:any(fun is_riak_configured/1, ejabberd_config:get_myhosts()).
|
||||
|
||||
is_riak_configured(Host) ->
|
||||
ServerConfigured = ejabberd_config:has_option({riak_server, Host}),
|
||||
@@ -163,7 +162,7 @@ get_pids() ->
|
||||
[ejabberd_riak:get_proc(I) || I <- lists:seq(1, get_pool_size())].
|
||||
|
||||
get_random_pid() ->
|
||||
I = randoms:round_robin(get_pool_size()) + 1,
|
||||
I = p1_rand:round_robin(get_pool_size()) + 1,
|
||||
ejabberd_riak:get_proc(I).
|
||||
|
||||
transform_options(Opts) ->
|
||||
@@ -174,14 +173,7 @@ transform_options({riak_server, {S, P}}, Opts) ->
|
||||
transform_options(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
-spec opt_type(riak_pool_size) -> fun((pos_integer()) -> pos_integer());
|
||||
(riak_port) -> fun((0..65535) -> 0..65535);
|
||||
(riak_server) -> fun((binary()) -> binary());
|
||||
(riak_start_interval) -> fun((pos_integer()) -> pos_integer());
|
||||
(riak_cacertfile) -> fun((binary()) -> binary());
|
||||
(riak_username) -> fun((binary()) -> binary());
|
||||
(riak_password) -> fun((binary()) -> binary());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(riak_pool_size) ->
|
||||
fun (N) when is_integer(N), N >= 1 -> N end;
|
||||
opt_type(riak_port) ->
|
||||
|
||||
+6
-19
@@ -68,7 +68,6 @@
|
||||
%% This value is used in SIP and Megaco for a transaction lifetime.
|
||||
-define(IQ_TIMEOUT, 32000).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_router.hrl").
|
||||
-include("xmpp.hrl").
|
||||
@@ -92,8 +91,9 @@ start_link() ->
|
||||
route(Packet) ->
|
||||
try do_route(Packet)
|
||||
catch E:R ->
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("failed to route packet:~n~s~nReason = ~p",
|
||||
[xmpp:pp(Packet), {E, {R, erlang:get_stacktrace()}}])
|
||||
[xmpp:pp(Packet), {E, {R, St}}])
|
||||
end.
|
||||
|
||||
-spec route(jid(), jid(), xmlel() | stanza()) -> ok.
|
||||
@@ -306,12 +306,8 @@ is_my_host(Domain) ->
|
||||
end.
|
||||
|
||||
-spec process_iq(iq()) -> any().
|
||||
process_iq(#iq{to = To} = IQ) ->
|
||||
if To#jid.luser == <<"">> ->
|
||||
ejabberd_local:process_iq(IQ);
|
||||
true ->
|
||||
ejabberd_sm:process_iq(IQ)
|
||||
end.
|
||||
process_iq(IQ) ->
|
||||
gen_iq_handler:handle(IQ).
|
||||
|
||||
-spec config_reloaded() -> ok.
|
||||
config_reloaded() ->
|
||||
@@ -484,7 +480,7 @@ cache_opts() ->
|
||||
end,
|
||||
[{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}].
|
||||
|
||||
-spec clean_cache(node()) -> ok.
|
||||
-spec clean_cache(node()) -> non_neg_integer().
|
||||
clean_cache(Node) ->
|
||||
ets_cache:filter(
|
||||
?ROUTES_CACHE,
|
||||
@@ -503,16 +499,7 @@ clean_cache(Node) ->
|
||||
clean_cache() ->
|
||||
ejabberd_cluster:eval_everywhere(?MODULE, clean_cache, [node()]).
|
||||
|
||||
-type domain_balancing() :: random | source | destination |
|
||||
bare_source | bare_destination.
|
||||
-spec opt_type(domain_balancing) -> fun((domain_balancing()) -> domain_balancing());
|
||||
(domain_balancing_component_number) -> fun((pos_integer()) -> pos_integer());
|
||||
(router_db_type) -> fun((atom()) -> atom());
|
||||
(router_use_cache) -> fun((boolean()) -> boolean());
|
||||
(router_cache_missed) -> fun((boolean()) -> boolean());
|
||||
(router_cache_size) -> fun((timeout()) -> timeout());
|
||||
(router_cache_life_time) -> fun((timeout()) -> timeout());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(domain_balancing) ->
|
||||
fun (random) -> random;
|
||||
(source) -> source;
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
-export([init/1, handle_cast/2, handle_call/3, handle_info/2,
|
||||
terminate/2, code_change/3, start_link/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("ejabberd_router.hrl").
|
||||
-include("logger.hrl").
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("xmpp.hrl").
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
-export([init/1, handle_cast/2, handle_call/3, handle_info/2,
|
||||
terminate/2, code_change/3, start_link/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_router.hrl").
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
-export([init/0, register_route/5, unregister_route/3, find_routes/1,
|
||||
get_all_routes/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_sql_pt.hrl").
|
||||
-include("ejabberd_router.hrl").
|
||||
@@ -41,7 +40,7 @@ init() ->
|
||||
Node = erlang:atom_to_binary(node(), latin1),
|
||||
?DEBUG("Cleaning SQL 'route' table...", []),
|
||||
case ejabberd_sql:sql_query(
|
||||
?MYNAME, ?SQL("delete from route where node=%(Node)s")) of
|
||||
ejabberd_config:get_myname(), ?SQL("delete from route where node=%(Node)s")) of
|
||||
{updated, _} ->
|
||||
ok;
|
||||
Err ->
|
||||
@@ -53,7 +52,7 @@ register_route(Domain, ServerHost, LocalHint, _, Pid) ->
|
||||
PidS = misc:encode_pid(Pid),
|
||||
LocalHintS = enc_local_hint(LocalHint),
|
||||
Node = erlang:atom_to_binary(node(Pid), latin1),
|
||||
case ?SQL_UPSERT(?MYNAME, "route",
|
||||
case ?SQL_UPSERT(ejabberd_config:get_myname(), "route",
|
||||
["!domain=%(Domain)s",
|
||||
"!server_host=%(ServerHost)s",
|
||||
"!node=%(Node)s",
|
||||
@@ -69,7 +68,7 @@ unregister_route(Domain, _, Pid) ->
|
||||
PidS = misc:encode_pid(Pid),
|
||||
Node = erlang:atom_to_binary(node(Pid), latin1),
|
||||
case ejabberd_sql:sql_query(
|
||||
?MYNAME,
|
||||
ejabberd_config:get_myname(),
|
||||
?SQL("delete from route where domain=%(Domain)s "
|
||||
"and pid=%(PidS)s and node=%(Node)s")) of
|
||||
{updated, _} ->
|
||||
@@ -80,7 +79,7 @@ unregister_route(Domain, _, Pid) ->
|
||||
|
||||
find_routes(Domain) ->
|
||||
case ejabberd_sql:sql_query(
|
||||
?MYNAME,
|
||||
ejabberd_config:get_myname(),
|
||||
?SQL("select @(server_host)s, @(node)s, @(pid)s, @(local_hint)s "
|
||||
"from route where domain=%(Domain)s")) of
|
||||
{selected, Rows} ->
|
||||
@@ -94,7 +93,7 @@ find_routes(Domain) ->
|
||||
|
||||
get_all_routes() ->
|
||||
case ejabberd_sql:sql_query(
|
||||
?MYNAME,
|
||||
ejabberd_config:get_myname(),
|
||||
?SQL("select @(domain)s from route where domain <> server_host")) of
|
||||
{selected, Domains} ->
|
||||
{ok, [Domain || {Domain} <- Domains]};
|
||||
@@ -123,10 +122,11 @@ row_to_route(Domain, {ServerHost, NodeS, PidS, LocalHintS} = Row) ->
|
||||
catch _:{bad_node, _} ->
|
||||
[];
|
||||
E:R ->
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("failed to decode row from 'route' table:~n"
|
||||
"Row = ~p~n"
|
||||
"Domain = ~s~n"
|
||||
"Reason = ~p",
|
||||
[Row, Domain, {E, {R, erlang:get_stacktrace()}}]),
|
||||
[Row, Domain, {E, {R, St}}]),
|
||||
[]
|
||||
end.
|
||||
|
||||
+12
-19
@@ -54,7 +54,6 @@
|
||||
-export([get_info_s2s_connections/1,
|
||||
transform_options/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-include("xmpp.hrl").
|
||||
@@ -96,8 +95,9 @@ start_link() ->
|
||||
route(Packet) ->
|
||||
try do_route(Packet)
|
||||
catch E:R ->
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("failed to route packet:~n~s~nReason = ~p",
|
||||
[xmpp:pp(Packet), {E, {R, erlang:get_stacktrace()}}])
|
||||
[xmpp:pp(Packet), {E, {R, St}}])
|
||||
end.
|
||||
|
||||
clean_temporarily_blocked_table() ->
|
||||
@@ -303,7 +303,7 @@ init([]) ->
|
||||
{attributes, record_info(fields, temporarily_blocked)}]),
|
||||
ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
|
||||
ejabberd_hooks:add(host_down, ?MODULE, host_down, 60),
|
||||
lists:foreach(fun host_up/1, ?MYHOSTS),
|
||||
lists:foreach(fun host_up/1, ejabberd_config:get_myhosts()),
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
@@ -322,7 +322,7 @@ handle_info(_Info, State) -> {noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ejabberd_commands:unregister_commands(get_commands_spec()),
|
||||
lists:foreach(fun host_down/1, ?MYHOSTS),
|
||||
lists:foreach(fun host_down/1, ejabberd_config:get_myhosts()),
|
||||
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50),
|
||||
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 60),
|
||||
ok.
|
||||
@@ -544,7 +544,7 @@ is_service(From, To) ->
|
||||
s2s -> % bypass RFC 3920 10.3
|
||||
false;
|
||||
local ->
|
||||
Hosts = (?MYHOSTS),
|
||||
Hosts = ejabberd_config:get_myhosts(),
|
||||
P = fun (ParentDomain) ->
|
||||
lists:member(ParentDomain, Hosts)
|
||||
end,
|
||||
@@ -708,19 +708,7 @@ get_s2s_state(S2sPid) ->
|
||||
end,
|
||||
[{s2s_pid, S2sPid} | Infos].
|
||||
|
||||
-type use_starttls() :: boolean() | optional | required | required_trusted.
|
||||
-spec opt_type(route_subdomains) -> fun((s2s | local) -> s2s | local);
|
||||
(s2s_access) -> fun((any()) -> any());
|
||||
(s2s_ciphers) -> fun((binary()) -> binary());
|
||||
(s2s_dhfile) -> fun((binary()) -> binary());
|
||||
(s2s_cafile) -> fun((binary()) -> binary());
|
||||
(s2s_protocol_options) -> fun(([binary()]) -> binary());
|
||||
(s2s_tls_compression) -> fun((boolean()) -> boolean());
|
||||
(s2s_use_starttls) -> fun((use_starttls()) -> use_starttls());
|
||||
(s2s_zlib) -> fun((boolean()) -> boolean());
|
||||
(s2s_timeout) -> fun((timeout()) -> timeout());
|
||||
(s2s_queue_type) -> fun((ram | file) -> ram | file);
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(route_subdomains) ->
|
||||
fun (s2s) -> s2s;
|
||||
(local) -> local
|
||||
@@ -750,7 +738,12 @@ opt_type(s2s_use_starttls) ->
|
||||
required_trusted
|
||||
end;
|
||||
opt_type(s2s_zlib) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
fun(true) ->
|
||||
ejabberd:start_app(ezlib),
|
||||
true;
|
||||
(false) ->
|
||||
false
|
||||
end;
|
||||
opt_type(s2s_timeout) ->
|
||||
fun(I) when is_integer(I), I >= 0 -> timer:seconds(I);
|
||||
(infinity) -> infinity;
|
||||
|
||||
+38
-70
@@ -21,17 +21,14 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(ejabberd_s2s_in).
|
||||
-behaviour(xmpp_stream_in).
|
||||
-behaviour(xmpp_socket).
|
||||
-behaviour(ejabberd_listener).
|
||||
|
||||
%% xmpp_socket callbacks
|
||||
-export([start/2, start_link/2, socket_type/0]).
|
||||
%% ejabberd_listener callbacks
|
||||
-export([listen_opt_type/1]).
|
||||
-export([start/2, start_link/2, accept/1, listen_opt_type/1, listen_options/0]).
|
||||
%% xmpp_stream_in callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3]).
|
||||
-export([tls_options/1, tls_required/1, tls_verify/1, tls_enabled/1,
|
||||
compress_methods/1,
|
||||
-export([tls_options/1, tls_required/1, tls_enabled/1, compress_methods/1,
|
||||
unauthenticated_stream_features/1, authenticated_stream_features/1,
|
||||
handle_stream_start/2, handle_stream_end/2,
|
||||
handle_stream_established/1, handle_auth_success/4,
|
||||
@@ -44,7 +41,6 @@
|
||||
-export([stop/1, close/1, close/2, send/2, update_state/2, establish/1,
|
||||
host_up/1, host_down/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
@@ -55,16 +51,8 @@
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
start(SockData, Opts) ->
|
||||
case proplists:get_value(supervisor, Opts, true) of
|
||||
true ->
|
||||
case supervisor:start_child(ejabberd_s2s_in_sup, [SockData, Opts]) of
|
||||
{ok, undefined} -> ignore;
|
||||
Res -> Res
|
||||
end;
|
||||
_ ->
|
||||
xmpp_stream_in:start(?MODULE, [SockData, Opts],
|
||||
ejabberd_config:fsm_limit_opts(Opts))
|
||||
end.
|
||||
xmpp_stream_in:start(?MODULE, [SockData, Opts],
|
||||
ejabberd_config:fsm_limit_opts(Opts)).
|
||||
|
||||
start_link(SockData, Opts) ->
|
||||
xmpp_stream_in:start_link(?MODULE, [SockData, Opts],
|
||||
@@ -79,8 +67,8 @@ close(Ref, Reason) ->
|
||||
stop(Ref) ->
|
||||
xmpp_stream_in:stop(Ref).
|
||||
|
||||
socket_type() ->
|
||||
xml_stream.
|
||||
accept(Ref) ->
|
||||
xmpp_stream_in:accept(Ref).
|
||||
|
||||
-spec send(pid(), xmpp_element()) -> ok;
|
||||
(state(), xmpp_element()) -> state().
|
||||
@@ -133,7 +121,15 @@ reject_unauthenticated_packet(State, _Pkt) ->
|
||||
Err = xmpp:serr_not_authorized(),
|
||||
send(State, Err).
|
||||
|
||||
process_closed(State, _Reason) ->
|
||||
process_closed(#{server := LServer} = State, Reason) ->
|
||||
RServer = case State of
|
||||
#{remote_server := Name} ->
|
||||
Name;
|
||||
#{ip := IP} ->
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP))
|
||||
end,
|
||||
?INFO_MSG("Closing inbound s2s connection ~s -> ~s: ~s",
|
||||
[RServer, LServer, xmpp_stream_out:format_error(Reason)]),
|
||||
stop(State).
|
||||
|
||||
%%%===================================================================
|
||||
@@ -145,9 +141,6 @@ tls_options(#{tls_options := TLSOpts, server_host := LServer}) ->
|
||||
tls_required(#{server_host := LServer}) ->
|
||||
ejabberd_s2s:tls_required(LServer).
|
||||
|
||||
tls_verify(#{server_host := LServer}) ->
|
||||
ejabberd_s2s:tls_verify(LServer).
|
||||
|
||||
tls_enabled(#{server_host := LServer}) ->
|
||||
ejabberd_s2s:tls_enabled(LServer).
|
||||
|
||||
@@ -202,9 +195,9 @@ handle_auth_failure(RServer, Mech, Reason,
|
||||
#{socket := Socket, ip := IP,
|
||||
server_host := ServerHost,
|
||||
lserver := LServer} = State) ->
|
||||
?INFO_MSG("(~s) Failed inbound s2s ~s authentication ~s -> ~s (~s): ~s",
|
||||
[xmpp_socket:pp(Socket), Mech, RServer, LServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)), Reason]),
|
||||
?WARNING_MSG("(~s) Failed inbound s2s ~s authentication ~s -> ~s (~s): ~s",
|
||||
[xmpp_socket:pp(Socket), Mech, RServer, LServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)), Reason]),
|
||||
ejabberd_hooks:run_fold(s2s_in_auth_result,
|
||||
ServerHost, State, [false, RServer]).
|
||||
|
||||
@@ -263,10 +256,10 @@ init([State, Opts]) ->
|
||||
State1 = State#{tls_options => TLSOpts2,
|
||||
auth_domains => sets:new(),
|
||||
xmlns => ?NS_SERVER,
|
||||
lang => ?MYLANG,
|
||||
server => ?MYNAME,
|
||||
lserver => ?MYNAME,
|
||||
server_host => ?MYNAME,
|
||||
lang => ejabberd_config:get_mylang(),
|
||||
server => ejabberd_config:get_myname(),
|
||||
lserver => ejabberd_config:get_myname(),
|
||||
server_host => ejabberd_config:get_myname(),
|
||||
established => false,
|
||||
shaper => Shaper},
|
||||
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
|
||||
@@ -345,49 +338,24 @@ set_idle_timeout(State) ->
|
||||
change_shaper(#{shaper := ShaperName, server_host := ServerHost} = State,
|
||||
RServer) ->
|
||||
Shaper = acl:match_rule(ServerHost, ShaperName, jid:make(RServer)),
|
||||
xmpp_stream_in:change_shaper(State, Shaper).
|
||||
xmpp_stream_in:change_shaper(State, ejabberd_shaper:new(Shaper)).
|
||||
|
||||
-spec listen_opt_type(shaper) -> fun((any()) -> any());
|
||||
(certfile) -> fun((binary()) -> binary());
|
||||
(ciphers) -> fun((binary()) -> binary());
|
||||
(dhfile) -> fun((binary()) -> binary());
|
||||
(cafile) -> fun((binary()) -> binary());
|
||||
(protocol_options) -> fun(([binary()]) -> binary());
|
||||
(tls_compression) -> fun((boolean()) -> boolean());
|
||||
(tls) -> fun((boolean()) -> boolean());
|
||||
(supervisor) -> fun((boolean()) -> boolean());
|
||||
(max_stanza_type) -> fun((timeout()) -> timeout());
|
||||
(max_fsm_queue) -> fun((pos_integer()) -> pos_integer());
|
||||
(inet) -> fun((boolean()) -> boolean());
|
||||
(inet6) -> fun((boolean()) -> boolean());
|
||||
(backlog) -> fun((timeout()) -> timeout());
|
||||
(atom()) -> [atom()].
|
||||
listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
|
||||
listen_opt_type(certfile = Opt) ->
|
||||
fun(S) ->
|
||||
?WARNING_MSG("Listening option '~s' for ~s is deprecated, use "
|
||||
"'certfiles' global option instead", [Opt, ?MODULE]),
|
||||
ejabberd_pkix:add_certfile(S),
|
||||
ok = ejabberd_pkix:add_certfile(S),
|
||||
iolist_to_binary(S)
|
||||
end;
|
||||
listen_opt_type(ciphers) -> ejabberd_s2s:opt_type(s2s_ciphers);
|
||||
listen_opt_type(dhfile) -> ejabberd_s2s:opt_type(s2s_dhfile);
|
||||
listen_opt_type(cafile) -> ejabberd_s2s:opt_type(s2s_cafile);
|
||||
listen_opt_type(protocol_options) -> ejabberd_s2s:opt_type(s2s_protocol_options);
|
||||
listen_opt_type(tls_compression) -> ejabberd_s2s:opt_type(s2s_tls_compression);
|
||||
listen_opt_type(tls) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(supervisor) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(max_stanza_size) ->
|
||||
fun(I) when is_integer(I), I>0 -> I;
|
||||
(unlimited) -> infinity;
|
||||
(infinity) -> infinity
|
||||
end;
|
||||
listen_opt_type(max_fsm_queue) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(backlog) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(_) ->
|
||||
[shaper, certfile, ciphers, dhfile, cafile, protocol_options,
|
||||
tls_compression, tls, max_fsm_queue, backlog, inet, inet6].
|
||||
end.
|
||||
|
||||
listen_options() ->
|
||||
[{shaper, none},
|
||||
{certfile, undefined},
|
||||
{ciphers, undefined},
|
||||
{dhfile, undefined},
|
||||
{cafile, undefined},
|
||||
{protocol_options, undefined},
|
||||
{tls, false},
|
||||
{tls_compression, false},
|
||||
{max_stanza_size, infinity},
|
||||
{max_fsm_queue, 5000}].
|
||||
|
||||
+15
-22
@@ -42,7 +42,6 @@
|
||||
-export([start/3, start_link/3, connect/1, close/1, close/2, stop/1, send/2,
|
||||
route/2, establish/1, update_state/2, host_up/1, host_down/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
@@ -61,12 +60,12 @@ start(From, To, Opts) ->
|
||||
Res -> Res
|
||||
end;
|
||||
_ ->
|
||||
xmpp_stream_out:start(?MODULE, [xmpp_socket, From, To, Opts],
|
||||
xmpp_stream_out:start(?MODULE, [From, To, Opts],
|
||||
ejabberd_config:fsm_limit_opts([]))
|
||||
end.
|
||||
|
||||
start_link(From, To, Opts) ->
|
||||
xmpp_stream_out:start_link(?MODULE, [xmpp_socket, From, To, Opts],
|
||||
xmpp_stream_out:start_link(?MODULE, [From, To, Opts],
|
||||
ejabberd_config:fsm_limit_opts([])).
|
||||
|
||||
-spec connect(pid()) -> ok.
|
||||
@@ -139,9 +138,9 @@ host_down(Host) ->
|
||||
process_auth_result(#{server := LServer, remote_server := RServer} = State,
|
||||
{false, Reason}) ->
|
||||
Delay = get_delay(),
|
||||
?INFO_MSG("Failed to establish outbound s2s connection ~s -> ~s: "
|
||||
"authentication failed; bouncing for ~p seconds",
|
||||
[LServer, RServer, Delay]),
|
||||
?WARNING_MSG("Failed to establish outbound s2s connection ~s -> ~s: "
|
||||
"authentication failed; bouncing for ~p seconds",
|
||||
[LServer, RServer, Delay]),
|
||||
State1 = State#{on_route => bounce, stop_reason => Reason},
|
||||
State2 = close(State1),
|
||||
State3 = bounce_queue(State2),
|
||||
@@ -158,9 +157,9 @@ process_closed(#{server := LServer, remote_server := RServer,
|
||||
process_closed(#{server := LServer, remote_server := RServer} = State,
|
||||
Reason) ->
|
||||
Delay = get_delay(),
|
||||
?INFO_MSG("Failed to establish outbound s2s connection ~s -> ~s: ~s; "
|
||||
"bouncing for ~p seconds",
|
||||
[LServer, RServer, format_error(Reason), Delay]),
|
||||
?WARNING_MSG("Failed to establish outbound s2s connection ~s -> ~s: ~s; "
|
||||
"bouncing for ~p seconds",
|
||||
[LServer, RServer, format_error(Reason), Delay]),
|
||||
State1 = State#{on_route => bounce},
|
||||
State2 = bounce_queue(State1),
|
||||
xmpp_stream_out:set_timeout(State2, timer:seconds(Delay)).
|
||||
@@ -224,10 +223,10 @@ handle_auth_failure(Mech, Reason,
|
||||
remote_server := RServer,
|
||||
server_host := ServerHost,
|
||||
server := LServer} = State) ->
|
||||
?INFO_MSG("(~s) Failed outbound s2s ~s authentication ~s -> ~s (~s): ~s",
|
||||
[xmpp_socket:pp(Socket), Mech, LServer, RServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)),
|
||||
xmpp_stream_out:format_error(Reason)]),
|
||||
?WARNING_MSG("(~s) Failed outbound s2s ~s authentication ~s -> ~s (~s): ~s",
|
||||
[xmpp_socket:pp(Socket), Mech, LServer, RServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)),
|
||||
xmpp_stream_out:format_error(Reason)]),
|
||||
ejabberd_hooks:run_fold(s2s_out_auth_result, ServerHost, State, [{false, Reason}]).
|
||||
|
||||
handle_packet(Pkt, #{server_host := ServerHost} = State) ->
|
||||
@@ -274,7 +273,7 @@ init([#{server := LServer, remote_server := RServer} = State, Opts]) ->
|
||||
State1 = State#{on_route => queue,
|
||||
queue => p1_queue:new(QueueType, QueueLimit),
|
||||
xmlns => ?NS_SERVER,
|
||||
lang => ?MYLANG,
|
||||
lang => ejabberd_config:get_mylang(),
|
||||
server_host => ServerHost,
|
||||
shaper => none},
|
||||
State2 = xmpp_stream_out:set_timeout(State1, Timeout),
|
||||
@@ -376,7 +375,7 @@ mk_bounce_error(_Lang, _State) ->
|
||||
-spec get_delay() -> non_neg_integer().
|
||||
get_delay() ->
|
||||
MaxDelay = ejabberd_config:get_option(s2s_max_retry_delay, 300),
|
||||
randoms:uniform(MaxDelay).
|
||||
p1_rand:uniform(MaxDelay).
|
||||
|
||||
-spec set_idle_timeout(state()) -> state().
|
||||
set_idle_timeout(#{on_route := send, server := LServer} = State) ->
|
||||
@@ -444,13 +443,7 @@ maybe_report_huge_timeout(Opt, T) when is_integer(T), T >= 1000 ->
|
||||
maybe_report_huge_timeout(_, _) ->
|
||||
ok.
|
||||
|
||||
-spec opt_type(outgoing_s2s_families) -> fun(([ipv4|ipv6]) -> [inet|inet6]);
|
||||
(outgoing_s2s_port) -> fun((0..65535) -> 0..65535);
|
||||
(outgoing_s2s_timeout) -> fun((timeout()) -> timeout());
|
||||
(s2s_dns_retries) -> fun((non_neg_integer()) -> non_neg_integer());
|
||||
(s2s_dns_timeout) -> fun((timeout()) -> timeout());
|
||||
(s2s_max_retry_delay) -> fun((pos_integer()) -> pos_integer());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(outgoing_s2s_families) ->
|
||||
fun(Families) ->
|
||||
lists:map(
|
||||
|
||||
+46
-71
@@ -21,22 +21,20 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(ejabberd_service).
|
||||
-behaviour(xmpp_stream_in).
|
||||
-behaviour(xmpp_socket).
|
||||
-behaviour(ejabberd_listener).
|
||||
|
||||
-protocol({xep, 114, '1.6'}).
|
||||
|
||||
%% xmpp_socket callbacks
|
||||
-export([start/2, start_link/2, socket_type/0, close/1, close/2]).
|
||||
%% ejabberd_listener callbacks
|
||||
-export([listen_opt_type/1, transform_listen_option/2]).
|
||||
-export([start/2, start_link/2, accept/1]).
|
||||
-export([listen_opt_type/1, listen_options/0, transform_listen_option/2]).
|
||||
%% xmpp_stream_in callbacks
|
||||
-export([init/1, handle_info/2, terminate/2, code_change/3]).
|
||||
-export([handle_stream_start/2, handle_auth_success/4, handle_auth_failure/4,
|
||||
handle_authenticated_packet/2, get_password_fun/1, tls_options/1]).
|
||||
%% API
|
||||
-export([send/2]).
|
||||
-export([send/2, close/1, close/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
@@ -54,8 +52,8 @@ start_link(SockData, Opts) ->
|
||||
xmpp_stream_in:start_link(?MODULE, [SockData, Opts],
|
||||
ejabberd_config:fsm_limit_opts(Opts)).
|
||||
|
||||
socket_type() ->
|
||||
xml_stream.
|
||||
accept(Ref) ->
|
||||
xmpp_stream_in:accept(Ref).
|
||||
|
||||
-spec send(pid(), xmpp_element()) -> ok;
|
||||
(state(), xmpp_element()) -> state().
|
||||
@@ -80,7 +78,8 @@ tls_options(#{tls_options := TLSOptions}) ->
|
||||
|
||||
init([State, Opts]) ->
|
||||
Access = proplists:get_value(access, Opts, all),
|
||||
Shaper = proplists:get_value(shaper_rule, Opts, none),
|
||||
Shaper = proplists:get_value(shaper, Opts,
|
||||
proplists:get_value(shaper_rule, Opts, none)),
|
||||
GlobalPassword = proplists:get_value(password, Opts, random_password()),
|
||||
HostOpts = proplists:get_value(hosts, Opts, [{global, GlobalPassword}]),
|
||||
HostOpts1 = lists:map(
|
||||
@@ -102,12 +101,12 @@ init([State, Opts]) ->
|
||||
end,
|
||||
GlobalRoutes = proplists:get_value(global_routes, Opts, true),
|
||||
Timeout = ejabberd_config:negotiation_timeout(),
|
||||
State1 = xmpp_stream_in:change_shaper(State, Shaper),
|
||||
State1 = xmpp_stream_in:change_shaper(State, ejabberd_shaper:new(Shaper)),
|
||||
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
|
||||
State3 = State2#{access => Access,
|
||||
xmlns => ?NS_COMPONENT,
|
||||
lang => ?MYLANG,
|
||||
server => ?MYNAME,
|
||||
lang => ejabberd_config:get_mylang(),
|
||||
server => ejabberd_config:get_myname(),
|
||||
host_opts => dict:from_list(HostOpts1),
|
||||
stream_version => undefined,
|
||||
tls_options => TLSOpts,
|
||||
@@ -147,10 +146,10 @@ get_password_fun(#{remote_server := RemoteServer,
|
||||
{ok, Password} ->
|
||||
{Password, undefined};
|
||||
error ->
|
||||
?INFO_MSG("(~s) Domain ~s is unconfigured for "
|
||||
"external component from ~s",
|
||||
[xmpp_socket:pp(Socket), RemoteServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
|
||||
?WARNING_MSG("(~s) Domain ~s is unconfigured for "
|
||||
"external component from ~s",
|
||||
[xmpp_socket:pp(Socket), RemoteServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
|
||||
{false, undefined}
|
||||
end
|
||||
end.
|
||||
@@ -170,7 +169,7 @@ handle_auth_success(_, Mech, _,
|
||||
end,
|
||||
lists:foreach(
|
||||
fun(H) ->
|
||||
ejabberd_router:register_route(H, ?MYNAME),
|
||||
ejabberd_router:register_route(H, ejabberd_config:get_myname()),
|
||||
ejabberd_hooks:run(component_connected, [H])
|
||||
end, Routes),
|
||||
State.
|
||||
@@ -178,11 +177,11 @@ handle_auth_success(_, Mech, _,
|
||||
handle_auth_failure(_, Mech, Reason,
|
||||
#{remote_server := RemoteServer,
|
||||
socket := Socket, ip := IP} = State) ->
|
||||
?INFO_MSG("(~s) Failed external component ~s authentication "
|
||||
"for ~s from ~s: ~s",
|
||||
[xmpp_socket:pp(Socket), Mech, RemoteServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)),
|
||||
Reason]),
|
||||
?WARNING_MSG("(~s) Failed external component ~s authentication "
|
||||
"for ~s from ~s: ~s",
|
||||
[xmpp_socket:pp(Socket), Mech, RemoteServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)),
|
||||
Reason]),
|
||||
State.
|
||||
|
||||
handle_authenticated_packet(Pkt0, #{ip := {IP, _}, lang := Lang} = State)
|
||||
@@ -262,7 +261,7 @@ check_from(From, #{host_opts := HostOpts}) ->
|
||||
dict:is_key(Server, HostOpts).
|
||||
|
||||
random_password() ->
|
||||
str:sha(randoms:bytes(20)).
|
||||
str:sha(p1_rand:bytes(20)).
|
||||
|
||||
transform_listen_option({hosts, Hosts, O}, Opts) ->
|
||||
case lists:keyfind(hosts, 1, Opts) of
|
||||
@@ -282,39 +281,12 @@ transform_listen_option({host, Host, Os}, Opts) ->
|
||||
transform_listen_option(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
-spec listen_opt_type(access) -> fun((any()) -> any());
|
||||
(shaper_rule) -> fun((any()) -> any());
|
||||
(certfile) -> fun((binary()) -> binary());
|
||||
(ciphers) -> fun((binary()) -> binary());
|
||||
(dhfile) -> fun((binary()) -> binary());
|
||||
(cafile) -> fun((binary()) -> binary());
|
||||
(protocol_options) -> fun(([binary()]) -> binary());
|
||||
(tls_compression) -> fun((boolean()) -> boolean());
|
||||
(tls) -> fun((boolean()) -> boolean());
|
||||
(check_from) -> fun((boolean()) -> boolean());
|
||||
(password) -> fun((boolean()) -> boolean());
|
||||
(hosts) -> fun(([{binary(), [{password, binary()}]}]) ->
|
||||
[{binary(), binary() | undefined}]);
|
||||
(max_stanza_type) -> fun((timeout()) -> timeout());
|
||||
(max_fsm_queue) -> fun((pos_integer()) -> pos_integer());
|
||||
(inet) -> fun((boolean()) -> boolean());
|
||||
(inet6) -> fun((boolean()) -> boolean());
|
||||
(backlog) -> fun((timeout()) -> timeout());
|
||||
(atom()) -> [atom()].
|
||||
listen_opt_type(access) -> fun acl:access_rules_validator/1;
|
||||
listen_opt_type(shaper_rule) -> fun acl:shaper_rules_validator/1;
|
||||
listen_opt_type(certfile) ->
|
||||
fun(S) ->
|
||||
ejabberd_pkix:add_certfile(S),
|
||||
iolist_to_binary(S)
|
||||
listen_opt_type(shaper_rule) ->
|
||||
fun(V) ->
|
||||
?WARNING_MSG("Listening option 'shaper_rule' of module ~s "
|
||||
"is renamed to 'shaper'", [?MODULE]),
|
||||
acl:shaper_rules_validator(V)
|
||||
end;
|
||||
listen_opt_type(ciphers) -> fun iolist_to_binary/1;
|
||||
listen_opt_type(dhfile) -> fun misc:try_read_file/1;
|
||||
listen_opt_type(cafile) -> fun misc:try_read_file/1;
|
||||
listen_opt_type(protocol_options) ->
|
||||
fun(Options) -> str:join(Options, <<"|">>) end;
|
||||
listen_opt_type(tls_compression) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(tls) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(check_from) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(password) -> fun iolist_to_binary/1;
|
||||
listen_opt_type(hosts) ->
|
||||
@@ -329,19 +301,22 @@ listen_opt_type(hosts) ->
|
||||
end, HostOpts)
|
||||
end;
|
||||
listen_opt_type(global_routes) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(max_stanza_size) ->
|
||||
fun(I) when is_integer(I) -> I;
|
||||
(unlimited) -> infinity;
|
||||
(infinity) -> infinity
|
||||
end;
|
||||
listen_opt_type(max_fsm_queue) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(backlog) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(_) ->
|
||||
[access, shaper_rule, certfile, ciphers, dhfile, cafile, tls,
|
||||
protocol_options, tls_compression, password, hosts, check_from,
|
||||
max_fsm_queue, global_routes, backlog, inet, inet6].
|
||||
fun(B) when is_boolean(B) -> B end.
|
||||
|
||||
listen_options() ->
|
||||
[{access, all},
|
||||
{shaper, none},
|
||||
{shaper_rule, none},
|
||||
{certfile, undefined},
|
||||
{ciphers, undefined},
|
||||
{dhfile, undefined},
|
||||
{cafile, undefined},
|
||||
{protocol_options, undefined},
|
||||
{tls, false},
|
||||
{tls_compression, false},
|
||||
{max_stanza_size, infinity},
|
||||
{max_fsm_queue, 5000},
|
||||
{password, undefined},
|
||||
{hosts, []},
|
||||
{check_from, true},
|
||||
{global_routes, true}].
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : shaper.erl
|
||||
%%% File : ejabberd_shaper.erl
|
||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
||||
%%% Purpose : Functions to control connections traffic
|
||||
%%% Created : 9 Feb 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
@@ -23,34 +23,29 @@
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(shaper).
|
||||
-module(ejabberd_shaper).
|
||||
|
||||
-behaviour(gen_server).
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start_link/0, new/1, new1/1, update/2,
|
||||
-export([start_link/0, new/1, update/2,
|
||||
get_max_rate/1, transform_options/1, load_from_config/0,
|
||||
opt_type/1]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-record(maxrate, {maxrate = 0 :: integer(),
|
||||
lastrate = 0.0 :: float(),
|
||||
lasttime = 0 :: integer()}).
|
||||
|
||||
-record(shaper, {name :: {atom(), global},
|
||||
maxrate :: integer()}).
|
||||
-record(shaper, {name :: {atom(), global},
|
||||
maxrate :: integer(),
|
||||
burst_size :: integer()}).
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
-type shaper() :: none | #maxrate{}.
|
||||
|
||||
-type shaper() :: none | p1_shaper:state().
|
||||
-export_type([shaper/0]).
|
||||
|
||||
-spec start_link() -> {ok, pid()} | {error, any()}.
|
||||
@@ -59,9 +54,9 @@ start_link() ->
|
||||
|
||||
init([]) ->
|
||||
ejabberd_mnesia:create(?MODULE, shaper,
|
||||
[{ram_copies, [node()]},
|
||||
{local_content, true},
|
||||
{attributes, record_info(fields, shaper)}]),
|
||||
[{ram_copies, [node()]},
|
||||
{local_content, true},
|
||||
{attributes, record_info(fields, shaper)}]),
|
||||
ejabberd_hooks:add(config_reloaded, ?MODULE, load_from_config, 20),
|
||||
load_from_config(),
|
||||
{ok, #state{}}.
|
||||
@@ -83,25 +78,26 @@ code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
-spec load_from_config() -> ok | {error, any()}.
|
||||
|
||||
load_from_config() ->
|
||||
Shapers = ejabberd_config:get_option(shaper, []),
|
||||
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}
|
||||
fun() ->
|
||||
lists:foreach(
|
||||
fun({Name, MaxRate, BurstSize}) ->
|
||||
mnesia:write(
|
||||
#shaper{name = {Name, global},
|
||||
maxrate = MaxRate,
|
||||
burst_size = BurstSize})
|
||||
end,
|
||||
Shapers)
|
||||
end) of
|
||||
{atomic, ok} ->
|
||||
ok;
|
||||
Err ->
|
||||
{error, Err}
|
||||
end.
|
||||
|
||||
-spec get_max_rate(atom()) -> none | non_neg_integer().
|
||||
|
||||
get_max_rate(none) ->
|
||||
none;
|
||||
get_max_rate(Name) ->
|
||||
@@ -113,62 +109,44 @@ get_max_rate(Name) ->
|
||||
end.
|
||||
|
||||
-spec new(atom()) -> shaper().
|
||||
|
||||
new(none) ->
|
||||
none;
|
||||
new(Name) ->
|
||||
MaxRate = case ets:lookup(shaper, {Name, global}) of
|
||||
[#shaper{maxrate = R}] ->
|
||||
R;
|
||||
[] ->
|
||||
none
|
||||
end,
|
||||
new1(MaxRate).
|
||||
|
||||
-spec new1(none | integer()) -> shaper().
|
||||
|
||||
new1(none) -> none;
|
||||
new1(MaxRate) ->
|
||||
#maxrate{maxrate = MaxRate, lastrate = 0.0,
|
||||
lasttime = p1_time_compat:system_time(micro_seconds)}.
|
||||
case ets:lookup(shaper, {Name, global}) of
|
||||
[#shaper{maxrate = R, burst_size = B}] ->
|
||||
p1_shaper:new(R, B);
|
||||
[] ->
|
||||
none
|
||||
end.
|
||||
|
||||
-spec update(shaper(), integer()) -> {shaper(), integer()}.
|
||||
|
||||
update(none, _Size) -> {none, 0};
|
||||
update(#maxrate{} = State, Size) ->
|
||||
MinInterv = 1000 * Size /
|
||||
(2 * State#maxrate.maxrate - State#maxrate.lastrate),
|
||||
Interv = (p1_time_compat:system_time(micro_seconds) - State#maxrate.lasttime) /
|
||||
1000,
|
||||
?DEBUG("State: ~p, Size=~p~nM=~p, I=~p~n",
|
||||
[State, Size, MinInterv, Interv]),
|
||||
Pause = if MinInterv > Interv ->
|
||||
1 + trunc(MinInterv - Interv);
|
||||
true -> 0
|
||||
end,
|
||||
NextNow = p1_time_compat:system_time(micro_seconds) + Pause * 1000,
|
||||
Div = case NextNow - State#maxrate.lasttime of
|
||||
0 -> 1;
|
||||
V -> V
|
||||
end,
|
||||
{State#maxrate{lastrate =
|
||||
(State#maxrate.lastrate +
|
||||
1000000 * Size / Div)
|
||||
/ 2,
|
||||
lasttime = NextNow},
|
||||
Pause}.
|
||||
update(Shaper, Size) ->
|
||||
Result = p1_shaper:update(Shaper, Size),
|
||||
?DEBUG("Shaper update:~n~s =>~n~s",
|
||||
[p1_shaper:pp(Shaper), p1_shaper:pp(Result)]),
|
||||
Result.
|
||||
|
||||
transform_options(Opts) ->
|
||||
lists:foldl(fun transform_options/2, [], Opts).
|
||||
|
||||
transform_options({OptName, Name, {maxrate, N}}, Opts) when OptName == shaper ->
|
||||
[{shaper, [{Name, N}]}|Opts];
|
||||
transform_options({OptName, Name, none}, Opts) when OptName == shaper ->
|
||||
[{shaper, [{Name, none}]}|Opts];
|
||||
transform_options({shaper, Name, {maxrate, N}}, Opts) ->
|
||||
[{shaper, [{Name, N}]} | Opts];
|
||||
transform_options({shaper, Name, none}, Opts) ->
|
||||
[{shaper, [{Name, none}]} | Opts];
|
||||
transform_options({shaper, List}, Opts) when is_list(List) ->
|
||||
R = lists:map(
|
||||
fun({Name, Args}) when is_list(Args) ->
|
||||
MaxRate = proplists:get_value(rate, Args, 1000),
|
||||
BurstSize = proplists:get_value(burst_size, Args, MaxRate),
|
||||
{Name, MaxRate, BurstSize};
|
||||
({Name, Val}) ->
|
||||
{Name, Val, Val}
|
||||
end, List),
|
||||
[{shaper, R} | Opts];
|
||||
transform_options(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
[Opt | Opts].
|
||||
|
||||
-spec opt_type(shaper) -> fun((any()) -> any());
|
||||
(atom()) -> [atom()].
|
||||
opt_type(shaper) -> fun (V) -> V end;
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(shaper) -> fun(V) -> V end;
|
||||
opt_type(_) -> [shaper].
|
||||
+25
-28
@@ -24,27 +24,29 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_sip).
|
||||
-behaviour(ejabberd_listener).
|
||||
|
||||
-ifndef(SIP).
|
||||
-include("logger.hrl").
|
||||
-export([socket_type/0, start/2, listen_opt_type/1]).
|
||||
log_error() ->
|
||||
?CRITICAL_MSG("ejabberd is not compiled with SIP support", []).
|
||||
socket_type() ->
|
||||
log_error(),
|
||||
raw.
|
||||
listen_opt_type(_) ->
|
||||
log_error(),
|
||||
[].
|
||||
-export([accept/1, start/2, start_link/2, listen_options/0]).
|
||||
fail() ->
|
||||
?CRITICAL_MSG("Listening module ~s is not available: "
|
||||
"ejabberd is not compiled with SIP support",
|
||||
[?MODULE]),
|
||||
erlang:error(sip_not_compiled).
|
||||
accept(_) ->
|
||||
fail().
|
||||
listen_options() ->
|
||||
fail().
|
||||
start(_, _) ->
|
||||
log_error(),
|
||||
{error, sip_not_compiled}.
|
||||
fail().
|
||||
start_link(_, _) ->
|
||||
fail().
|
||||
-else.
|
||||
%% API
|
||||
-export([tcp_init/2, udp_init/2, udp_recv/5, start/2,
|
||||
socket_type/0, listen_opt_type/1]).
|
||||
start_link/2, accept/1, listen_options/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
@@ -63,19 +65,22 @@ udp_recv(Sock, Addr, Port, Data, Opts) ->
|
||||
start(Opaque, Opts) ->
|
||||
esip_socket:start(Opaque, Opts).
|
||||
|
||||
socket_type() ->
|
||||
raw.
|
||||
start_link({gen_tcp, Sock}, Opts) ->
|
||||
esip_socket:start_link(Sock, Opts).
|
||||
|
||||
accept(_) ->
|
||||
ok.
|
||||
|
||||
set_certfile(Opts) ->
|
||||
case lists:keymember(certfile, 1, Opts) of
|
||||
true ->
|
||||
Opts;
|
||||
false ->
|
||||
case ejabberd_pkix:get_certfile(?MYNAME) of
|
||||
case ejabberd_pkix:get_certfile(ejabberd_config:get_myname()) of
|
||||
{ok, CertFile} ->
|
||||
[{certfile, CertFile}|Opts];
|
||||
error ->
|
||||
case ejabberd_config:get_option({domain_certfile, ?MYNAME}) of
|
||||
case ejabberd_config:get_option({domain_certfile, ejabberd_config:get_myname()}) of
|
||||
undefined ->
|
||||
Opts;
|
||||
CertFile ->
|
||||
@@ -84,17 +89,9 @@ set_certfile(Opts) ->
|
||||
end
|
||||
end.
|
||||
|
||||
listen_opt_type(certfile) ->
|
||||
fun(S) ->
|
||||
%% We cannot deprecate the option for now:
|
||||
%% I think SIP clients are too stupid to set SNI
|
||||
ejabberd_pkix:add_certfile(S),
|
||||
iolist_to_binary(S)
|
||||
end;
|
||||
listen_opt_type(tls) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(_) ->
|
||||
[tls, certfile].
|
||||
listen_options() ->
|
||||
[{tls, false},
|
||||
{certfile, undefined}].
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
|
||||
+50
-81
@@ -39,7 +39,6 @@
|
||||
stop/0,
|
||||
route/1,
|
||||
route/2,
|
||||
process_iq/1,
|
||||
open_session/5,
|
||||
open_session/6,
|
||||
close_session/4,
|
||||
@@ -58,13 +57,12 @@
|
||||
get_vh_session_list/1,
|
||||
get_vh_session_number/1,
|
||||
get_vh_by_backend/1,
|
||||
register_iq_handler/4,
|
||||
unregister_iq_handler/2,
|
||||
force_update_presence/1,
|
||||
connected_users/0,
|
||||
connected_users_number/0,
|
||||
user_resources/2,
|
||||
kick_user/2,
|
||||
kick_user/3,
|
||||
get_session_pid/3,
|
||||
get_session_sid/3,
|
||||
get_session_sids/2,
|
||||
@@ -87,7 +85,6 @@
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-include("xmpp.hrl").
|
||||
@@ -145,9 +142,10 @@ route(Packet) ->
|
||||
Packet1 ->
|
||||
try do_route(Packet1), ok
|
||||
catch E:R ->
|
||||
St = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("failed to route packet:~n~s~nReason = ~p",
|
||||
[xmpp:pp(Packet1),
|
||||
{E, {R, erlang:get_stacktrace()}}])
|
||||
{E, {R, St}}])
|
||||
end
|
||||
end.
|
||||
|
||||
@@ -245,10 +243,12 @@ get_user_info(User, Server) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
Mod = get_sm_backend(LServer),
|
||||
Ss = online(get_sessions(Mod, LUser, LServer)),
|
||||
[{LResource, [{node, node(Pid)}|Info]}
|
||||
[{LResource, [{node, node(Pid)}, {ts, Ts}, {pid, Pid},
|
||||
{priority, Priority} | Info]}
|
||||
|| #session{usr = {_, _, LResource},
|
||||
priority = Priority,
|
||||
info = Info,
|
||||
sid = {_, Pid}} <- clean_session_list(Ss)].
|
||||
sid = {Ts, Pid}} <- clean_session_list(Ss)].
|
||||
|
||||
-spec get_user_info(binary(), binary(), binary()) -> info() | offline.
|
||||
|
||||
@@ -262,8 +262,11 @@ get_user_info(User, Server, Resource) ->
|
||||
offline;
|
||||
Ss ->
|
||||
Session = lists:max(Ss),
|
||||
Node = node(element(2, Session#session.sid)),
|
||||
[{node, Node}|Session#session.info]
|
||||
{Ts, Pid} = Session#session.sid,
|
||||
Node = node(Pid),
|
||||
Priority = Session#session.priority,
|
||||
[{node, Node}, {ts, Ts}, {pid, Pid}, {priority, Priority}
|
||||
|Session#session.info]
|
||||
end.
|
||||
|
||||
-spec set_presence(sid(), binary(), binary(), binary(),
|
||||
@@ -397,17 +400,6 @@ get_vh_session_number(Server) ->
|
||||
Mod = get_sm_backend(LServer),
|
||||
length(online(get_sessions(Mod, LServer))).
|
||||
|
||||
-spec register_iq_handler(binary(), binary(), atom(), atom()) -> ok.
|
||||
|
||||
register_iq_handler(Host, XMLNS, Module, Fun) ->
|
||||
?GEN_SERVER:cast(?MODULE,
|
||||
{register_iq_handler, Host, XMLNS, Module, Fun}).
|
||||
|
||||
-spec unregister_iq_handler(binary(), binary()) -> ok.
|
||||
|
||||
unregister_iq_handler(Host, XMLNS) ->
|
||||
?GEN_SERVER:cast(?MODULE, {unregister_iq_handler, Host, XMLNS}).
|
||||
|
||||
%% Why the hell do we have so many similar kicks?
|
||||
c2s_handle_info(#{lang := Lang} = State, replaced) ->
|
||||
State1 = State#{replaced => true},
|
||||
@@ -435,28 +427,26 @@ config_reloaded() ->
|
||||
init([]) ->
|
||||
process_flag(trap_exit, true),
|
||||
init_cache(),
|
||||
lists:foreach(fun(Mod) -> Mod:init() end, get_sm_backends()),
|
||||
clean_cache(),
|
||||
ets:new(sm_iqtable, [named_table, public, {read_concurrency, true}]),
|
||||
ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
|
||||
ejabberd_hooks:add(host_down, ?MODULE, host_down, 60),
|
||||
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50),
|
||||
lists:foreach(fun host_up/1, ?MYHOSTS),
|
||||
ejabberd_commands:register_commands(get_commands_spec()),
|
||||
{ok, #state{}}.
|
||||
case lists:foldl(
|
||||
fun(Mod, ok) -> Mod:init();
|
||||
(_, Err) -> Err
|
||||
end, ok, get_sm_backends()) of
|
||||
ok ->
|
||||
clean_cache(),
|
||||
gen_iq_handler:start(?MODULE),
|
||||
ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
|
||||
ejabberd_hooks:add(host_down, ?MODULE, host_down, 60),
|
||||
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50),
|
||||
lists:foreach(fun host_up/1, ejabberd_config:get_myhosts()),
|
||||
ejabberd_commands:register_commands(get_commands_spec()),
|
||||
{ok, #state{}};
|
||||
{error, Why} ->
|
||||
{stop, Why}
|
||||
end.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok, {reply, Reply, State}.
|
||||
|
||||
handle_cast({register_iq_handler, Host, XMLNS, Module, Function},
|
||||
State) ->
|
||||
ets:insert(sm_iqtable,
|
||||
{{Host, XMLNS}, Module, Function}),
|
||||
{noreply, State};
|
||||
handle_cast({unregister_iq_handler, Host, XMLNS},
|
||||
State) ->
|
||||
ets:delete(sm_iqtable, {Host, XMLNS}),
|
||||
{noreply, State};
|
||||
handle_cast(_Msg, State) -> {noreply, State}.
|
||||
|
||||
handle_info({route, Packet}, State) ->
|
||||
@@ -467,7 +457,7 @@ handle_info(Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
lists:foreach(fun host_down/1, ?MYHOSTS),
|
||||
lists:foreach(fun host_down/1, ejabberd_config:get_myhosts()),
|
||||
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50),
|
||||
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 60),
|
||||
ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 50),
|
||||
@@ -664,7 +654,7 @@ do_route(#message{to = #jid{lresource = <<"">>}, type = T} = Packet) ->
|
||||
end;
|
||||
do_route(#iq{to = #jid{lresource = <<"">>}} = Packet) ->
|
||||
?DEBUG("processing IQ to bare JID:~n~s", [xmpp:pp(Packet)]),
|
||||
process_iq(Packet);
|
||||
gen_iq_handler:handle(?MODULE, Packet);
|
||||
do_route(Packet) ->
|
||||
?DEBUG("processing packet to full JID:~n~s", [xmpp:pp(Packet)]),
|
||||
To = xmpp:get_to(Packet),
|
||||
@@ -849,31 +839,6 @@ get_max_user_sessions(LUser, Host) ->
|
||||
end.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
-spec process_iq(iq()) -> any().
|
||||
process_iq(#iq{to = To, type = T, lang = Lang, sub_els = [El]} = Packet)
|
||||
when T == get; T == set ->
|
||||
XMLNS = xmpp:get_ns(El),
|
||||
Host = To#jid.lserver,
|
||||
case ets:lookup(sm_iqtable, {Host, XMLNS}) of
|
||||
[{_, Module, Function}] ->
|
||||
gen_iq_handler:handle(Host, Module, Function, Packet);
|
||||
[] ->
|
||||
Txt = <<"No module is handling this query">>,
|
||||
Err = xmpp:err_service_unavailable(Txt, Lang),
|
||||
ejabberd_router:route_error(Packet, Err)
|
||||
end;
|
||||
process_iq(#iq{type = T, lang = Lang, sub_els = SubEls} = Packet)
|
||||
when T == get; T == set ->
|
||||
Txt = case SubEls of
|
||||
[] -> <<"No child elements found">>;
|
||||
_ -> <<"Too many child elements">>
|
||||
end,
|
||||
Err = xmpp:err_bad_request(Txt, Lang),
|
||||
ejabberd_router:route_error(Packet, Err);
|
||||
process_iq(#iq{}) ->
|
||||
ok.
|
||||
|
||||
-spec force_update_presence({binary(), binary()}) -> ok.
|
||||
|
||||
force_update_presence({LUser, LServer}) ->
|
||||
@@ -895,7 +860,7 @@ get_sm_backend(Host) ->
|
||||
-spec get_sm_backends() -> [module()].
|
||||
|
||||
get_sm_backends() ->
|
||||
lists:usort([get_sm_backend(Host) || Host <- ?MYHOSTS]).
|
||||
lists:usort([get_sm_backend(Host) || Host <- ejabberd_config:get_myhosts()]).
|
||||
|
||||
-spec get_vh_by_backend(module()) -> [binary()].
|
||||
|
||||
@@ -903,7 +868,7 @@ get_vh_by_backend(Mod) ->
|
||||
lists:filter(
|
||||
fun(Host) ->
|
||||
get_sm_backend(Host) == Mod
|
||||
end, ?MYHOSTS).
|
||||
end, ejabberd_config:get_myhosts()).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Cache stuff
|
||||
@@ -933,7 +898,7 @@ cache_opts() ->
|
||||
end,
|
||||
[{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}].
|
||||
|
||||
-spec clean_cache(node()) -> ok.
|
||||
-spec clean_cache(node()) -> non_neg_integer().
|
||||
clean_cache(Node) ->
|
||||
ets_cache:filter(
|
||||
?SM_CACHE,
|
||||
@@ -966,7 +931,7 @@ use_cache() ->
|
||||
fun(Host) ->
|
||||
Mod = get_sm_backend(Host),
|
||||
use_cache(Mod, Host)
|
||||
end, ?MYHOSTS).
|
||||
end, ejabberd_config:get_myhosts()).
|
||||
|
||||
-spec cache_nodes(module(), binary()) -> [node()].
|
||||
cache_nodes(Mod, LServer) ->
|
||||
@@ -1026,24 +991,28 @@ user_resources(User, Server) ->
|
||||
Resources = get_user_resources(User, Server),
|
||||
lists:sort(Resources).
|
||||
|
||||
-spec kick_user(binary(), binary()) -> non_neg_integer().
|
||||
kick_user(User, Server) ->
|
||||
Resources = get_user_resources(User, Server),
|
||||
lists:foreach(
|
||||
fun(Resource) ->
|
||||
PID = get_session_pid(User, Server, Resource),
|
||||
ejabberd_c2s:route(PID, kick)
|
||||
end, Resources),
|
||||
length(Resources).
|
||||
lists:foldl(
|
||||
fun(Resource, Acc) ->
|
||||
case kick_user(User, Server, Resource) of
|
||||
false -> Acc;
|
||||
true -> Acc + 1
|
||||
end
|
||||
end, 0, Resources).
|
||||
|
||||
-spec kick_user(binary(), binary(), binary()) -> boolean().
|
||||
kick_user(User, Server, Resource) ->
|
||||
case get_session_pid(User, Server, Resource) of
|
||||
none -> false;
|
||||
Pid -> ejabberd_c2s:route(Pid, kick)
|
||||
end.
|
||||
|
||||
make_sid() ->
|
||||
{p1_time_compat:unique_timestamp(), self()}.
|
||||
|
||||
-spec opt_type(sm_db_type) -> fun((atom()) -> atom());
|
||||
(sm_use_cache) -> fun((boolean()) -> boolean());
|
||||
(sm_cache_missed) -> fun((boolean()) -> boolean());
|
||||
(sm_cache_size) -> fun((timeout()) -> timeout());
|
||||
(sm_cache_life_time) -> fun((timeout()) -> timeout());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(sm_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
||||
opt_type(O) when O == sm_use_cache; O == sm_cache_missed ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3, start_link/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("ejabberd_sm.hrl").
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
|
||||
|
||||
+79
-29
@@ -27,21 +27,20 @@
|
||||
-define(GEN_SERVER, p1_server).
|
||||
-endif.
|
||||
-behaviour(?GEN_SERVER).
|
||||
|
||||
-behaviour(ejabberd_sm).
|
||||
|
||||
-export([init/0, set_session/1, delete_session/1,
|
||||
get_sessions/0, get_sessions/1, get_sessions/2,
|
||||
cache_nodes/1]).
|
||||
cache_nodes/1, clean_table/1, clean_table/0]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_cast/2, handle_call/3, handle_info/2,
|
||||
terminate/2, code_change/3, start_link/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("ejabberd_sm.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-define(SM_KEY, <<"ejabberd:sm">>).
|
||||
-define(MIN_REDIS_VERSION, <<"3.2.0">>).
|
||||
-record(state, {}).
|
||||
|
||||
%%%===================================================================
|
||||
@@ -71,10 +70,14 @@ set_session(Session) ->
|
||||
SIDKey = sid_to_key(Session#session.sid),
|
||||
ServKey = server_to_key(element(2, Session#session.us)),
|
||||
USSIDKey = us_sid_to_key(Session#session.us, Session#session.sid),
|
||||
NodeHostKey = node_host_to_key(node(), element(2, Session#session.us)),
|
||||
case ejabberd_redis:multi(
|
||||
fun() ->
|
||||
ejabberd_redis:hset(USKey, SIDKey, T),
|
||||
ejabberd_redis:hset(ServKey, USSIDKey, T),
|
||||
ejabberd_redis:hset(NodeHostKey,
|
||||
<<USKey/binary, "||", SIDKey/binary>>,
|
||||
USSIDKey),
|
||||
ejabberd_redis:publish(
|
||||
?SM_KEY, term_to_binary({delete, Session#session.us}))
|
||||
end) of
|
||||
@@ -90,10 +93,13 @@ delete_session(#session{sid = SID} = Session) ->
|
||||
SIDKey = sid_to_key(SID),
|
||||
ServKey = server_to_key(element(2, Session#session.us)),
|
||||
USSIDKey = us_sid_to_key(Session#session.us, SID),
|
||||
NodeHostKey = node_host_to_key(node(), element(2, Session#session.us)),
|
||||
case ejabberd_redis:multi(
|
||||
fun() ->
|
||||
ejabberd_redis:hdel(USKey, [SIDKey]),
|
||||
ejabberd_redis:hdel(ServKey, [USSIDKey]),
|
||||
ejabberd_redis:hdel(NodeHostKey,
|
||||
[<<USKey/binary, "||", SIDKey/binary>>]),
|
||||
ejabberd_redis:publish(
|
||||
?SM_KEY,
|
||||
term_to_binary({delete, Session#session.us}))
|
||||
@@ -137,8 +143,10 @@ get_sessions(LUser, LServer) ->
|
||||
%%%===================================================================
|
||||
init([]) ->
|
||||
ejabberd_redis:subscribe([?SM_KEY]),
|
||||
clean_table(),
|
||||
{ok, #state{}}.
|
||||
case clean_table() of
|
||||
ok -> {ok, #state{}};
|
||||
{error, Why} -> {stop, Why}
|
||||
end.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
@@ -169,10 +177,10 @@ code_change(_OldVsn, State, _Extra) ->
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
us_to_key({LUser, LServer}) ->
|
||||
<<"ejabberd:sm:", LUser/binary, "@", LServer/binary>>.
|
||||
<<(?SM_KEY)/binary, ":", LUser/binary, "@", LServer/binary>>.
|
||||
|
||||
server_to_key(LServer) ->
|
||||
<<"ejabberd:sm:", LServer/binary>>.
|
||||
<<(?SM_KEY)/binary, ":", LServer/binary>>.
|
||||
|
||||
us_sid_to_key(US, SID) ->
|
||||
term_to_binary({US, SID}).
|
||||
@@ -180,33 +188,75 @@ us_sid_to_key(US, SID) ->
|
||||
sid_to_key(SID) ->
|
||||
term_to_binary(SID).
|
||||
|
||||
node_session_deletion_cursor(Node, Host) ->
|
||||
NodeName = node_host_to_key(Node, Host),
|
||||
<<NodeName/binary, ":deletioncursor">>.
|
||||
|
||||
node_host_to_key(Node, Host) when is_atom(Node) ->
|
||||
NodeBin = atom_to_binary(node(), utf8),
|
||||
node_host_to_key(NodeBin, Host);
|
||||
node_host_to_key(NodeBin, Host) ->
|
||||
HostKey = server_to_key(Host),
|
||||
<<HostKey/binary, ":node:", NodeBin/binary>>.
|
||||
|
||||
decode_session_list(Vals) ->
|
||||
[binary_to_term(Val) || {_, Val} <- Vals].
|
||||
|
||||
clean_table() ->
|
||||
?DEBUG("Cleaning Redis SM table...", []),
|
||||
clean_table(node()).
|
||||
|
||||
clean_table(Node) when is_atom(Node) ->
|
||||
clean_table(atom_to_binary(Node, utf8));
|
||||
clean_table(Node) ->
|
||||
?DEBUG("Cleaning Redis SM table... ", []),
|
||||
try
|
||||
lists:foreach(
|
||||
fun(LServer) ->
|
||||
ServKey = server_to_key(LServer),
|
||||
{ok, Vals} = ejabberd_redis:hkeys(ServKey),
|
||||
{ok, _} =
|
||||
ejabberd_redis:multi(
|
||||
fun() ->
|
||||
lists:foreach(
|
||||
fun(USSIDKey) ->
|
||||
{US, SID} = binary_to_term(USSIDKey),
|
||||
if node(element(2, SID)) == node() ->
|
||||
USKey = us_to_key(US),
|
||||
SIDKey = sid_to_key(SID),
|
||||
ejabberd_redis:hdel(ServKey, [USSIDKey]),
|
||||
ejabberd_redis:hdel(USKey, [SIDKey]);
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
end, Vals)
|
||||
end)
|
||||
fun(Host) ->
|
||||
ok = clean_node_sessions(Node, Host)
|
||||
end, ejabberd_sm:get_vh_by_backend(?MODULE))
|
||||
catch _:{badmatch, {error, _}} ->
|
||||
?ERROR_MSG("failed to clean redis c2s sessions", [])
|
||||
catch _:{badmatch, {error, _} = Err} ->
|
||||
?ERROR_MSG("Failed to clean Redis SM table", []),
|
||||
Err
|
||||
end.
|
||||
|
||||
clean_node_sessions(Node, Host) ->
|
||||
case load_script() of
|
||||
{ok, SHA} ->
|
||||
clean_node_sessions(Node, Host, SHA);
|
||||
Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
clean_node_sessions(Node, Host, SHA) ->
|
||||
Keys = [node_host_to_key(Node, Host),
|
||||
server_to_key(Host),
|
||||
node_session_deletion_cursor(Node, Host)],
|
||||
case ejabberd_redis:evalsha(SHA, Keys, [1000]) of
|
||||
{ok, <<"0">>} ->
|
||||
ok;
|
||||
{ok, _Cursor} ->
|
||||
clean_node_sessions(Node, Host, SHA);
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
load_script() ->
|
||||
case misc:read_lua("redis_sm.lua") of
|
||||
{ok, Data} ->
|
||||
case ejabberd_redis:info(server) of
|
||||
{ok, Info} ->
|
||||
case proplists:get_value(redis_version, Info) of
|
||||
V when V >= ?MIN_REDIS_VERSION ->
|
||||
ejabberd_redis:script_load(Data);
|
||||
V ->
|
||||
?CRITICAL_MSG("Unsupported Redis version: ~s. "
|
||||
"The version must be ~s or above",
|
||||
[V, ?MIN_REDIS_VERSION]),
|
||||
{error, unsupported_redis_version}
|
||||
end;
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end;
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
get_sessions/1,
|
||||
get_sessions/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("ejabberd_sm.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_sql_pt.hrl").
|
||||
@@ -56,7 +55,7 @@ init() ->
|
||||
ok;
|
||||
Err ->
|
||||
?ERROR_MSG("failed to clean 'sm' table: ~p", [Err]),
|
||||
Err
|
||||
{error, db_failure}
|
||||
end;
|
||||
(_, Err) ->
|
||||
Err
|
||||
|
||||
+72
-46
@@ -66,7 +66,6 @@
|
||||
session_established/2, session_established/3,
|
||||
opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_sql_pt.hrl").
|
||||
|
||||
@@ -137,7 +136,7 @@ start_link(Host, StartInterval) ->
|
||||
-spec sql_query(binary(), sql_query()) -> sql_query_result().
|
||||
|
||||
sql_query(Host, Query) ->
|
||||
check_error(sql_call(Host, {sql_query, Query}), Query).
|
||||
sql_call(Host, {sql_query, Query}).
|
||||
|
||||
%% SQL transaction based on a list of queries
|
||||
%% This function automatically
|
||||
@@ -173,10 +172,16 @@ sql_call(Host, Msg) ->
|
||||
end.
|
||||
|
||||
keep_alive(Host, PID) ->
|
||||
sync_send_event(PID,
|
||||
case sync_send_event(PID,
|
||||
{sql_cmd, {sql_query, ?KEEPALIVE_QUERY},
|
||||
p1_time_compat:monotonic_time(milli_seconds)},
|
||||
query_timeout(Host)).
|
||||
query_timeout(Host)) of
|
||||
{selected,[<<"1">>],[[<<"1">>]]} ->
|
||||
ok;
|
||||
_Err ->
|
||||
?ERROR_MSG("keep alive query failed, closing connection: ~p", [_Err]),
|
||||
sync_send_event(PID, force_timeout, query_timeout(Host))
|
||||
end.
|
||||
|
||||
sync_send_event(Pid, Msg, Timeout) ->
|
||||
try p1_fsm:sync_send_event(Pid, Msg, Timeout)
|
||||
@@ -336,10 +341,10 @@ connecting(connect, #state{host = Host} = State) ->
|
||||
State2 = get_db_version(State1),
|
||||
{next_state, session_established, State2};
|
||||
{error, Reason} ->
|
||||
?INFO_MSG("~p connection failed:~n** Reason: ~p~n** "
|
||||
"Retry after: ~p seconds",
|
||||
[State#state.db_type, Reason,
|
||||
State#state.start_interval div 1000]),
|
||||
?WARNING_MSG("~p connection failed:~n** Reason: ~p~n** "
|
||||
"Retry after: ~p seconds",
|
||||
[State#state.db_type, Reason,
|
||||
State#state.start_interval div 1000]),
|
||||
p1_fsm:send_event_after(State#state.start_interval,
|
||||
connect),
|
||||
{next_state, connecting, State}
|
||||
@@ -390,6 +395,8 @@ session_established(Request, {Who, _Ref}, State) ->
|
||||
session_established({sql_cmd, Command, From, Timestamp},
|
||||
State) ->
|
||||
run_sql_cmd(Command, From, State, Timestamp);
|
||||
session_established(force_timeout, State) ->
|
||||
{stop, timeout, State};
|
||||
session_established(Event, State) ->
|
||||
?WARNING_MSG("unexpected event in 'session_established': ~p",
|
||||
[Event]),
|
||||
@@ -581,18 +588,19 @@ sql_query_internal(#sql_query{} = Query) ->
|
||||
sqlite ->
|
||||
sqlite_sql_query(Query)
|
||||
end
|
||||
catch
|
||||
Class:Reason ->
|
||||
catch exit:{timeout, _} ->
|
||||
{error, <<"timed out">>};
|
||||
exit:{killed, _} ->
|
||||
{error, <<"killed">>};
|
||||
exit:{normal, _} ->
|
||||
{error, <<"terminated unexpectedly">>};
|
||||
Class:Reason ->
|
||||
ST = erlang:get_stacktrace(),
|
||||
?ERROR_MSG("Internal error while processing SQL query: ~p",
|
||||
[{Class, Reason, ST}]),
|
||||
{error, <<"internal error">>}
|
||||
end,
|
||||
case Res of
|
||||
{error, <<"No SQL-driver information available.">>} ->
|
||||
{updated, 0};
|
||||
_Else -> Res
|
||||
end;
|
||||
check_error(Res, Query);
|
||||
sql_query_internal(F) when is_function(F) ->
|
||||
case catch execute_fun(F) of
|
||||
{'EXIT', Reason} -> {error, Reason};
|
||||
@@ -617,17 +625,12 @@ sql_query_internal(Query) ->
|
||||
[Query], self(),
|
||||
[{timeout, QueryTimeout - 1000},
|
||||
{result_type, binary}])),
|
||||
%% ?INFO_MSG("MySQL, Received result~n~p~n", [R]),
|
||||
R;
|
||||
sqlite ->
|
||||
Host = State#state.host,
|
||||
sqlite_to_odbc(Host, sqlite3:sql_exec(sqlite_db(Host), Query))
|
||||
end,
|
||||
case Res of
|
||||
{error, <<"No SQL-driver information available.">>} ->
|
||||
{updated, 0};
|
||||
_Else -> Res
|
||||
end.
|
||||
check_error(Res, Query).
|
||||
|
||||
select_sql_query(Queries, State) ->
|
||||
select_sql_query(
|
||||
@@ -746,14 +749,23 @@ sql_query_to_iolist(SQLQuery) ->
|
||||
generic_sql_query_format(SQLQuery).
|
||||
|
||||
%% Generate the OTP callback return tuple depending on the driver result.
|
||||
abort_on_driver_error({error, <<"query timed out">>} =
|
||||
Reply,
|
||||
abort_on_driver_error({error,
|
||||
<<"query timed out">>} = Reply,
|
||||
From) ->
|
||||
p1_fsm:reply(From, Reply),
|
||||
{stop, timeout, get(?STATE_KEY)};
|
||||
abort_on_driver_error({error,
|
||||
<<"Failed sending data on socket", _/binary>>} =
|
||||
Reply,
|
||||
<<"Failed sending data on socket", _/binary>>} = Reply,
|
||||
From) ->
|
||||
p1_fsm:reply(From, Reply),
|
||||
{stop, closed, get(?STATE_KEY)};
|
||||
abort_on_driver_error({error,
|
||||
<<"SQL connection failed">>} = Reply,
|
||||
From) ->
|
||||
p1_fsm:reply(From, Reply),
|
||||
{stop, timeout, get(?STATE_KEY)};
|
||||
abort_on_driver_error({error,
|
||||
<<"Communication link failure">>} = Reply,
|
||||
From) ->
|
||||
p1_fsm:reply(From, Reply),
|
||||
{stop, closed, get(?STATE_KEY)};
|
||||
@@ -769,6 +781,7 @@ odbc_connect(SQLServer, Timeout) ->
|
||||
ejabberd:start_app(odbc),
|
||||
odbc:connect(binary_to_list(SQLServer),
|
||||
[{scrollable_cursors, off},
|
||||
{extended_errors, on},
|
||||
{tuple_row, off},
|
||||
{timeout, Timeout},
|
||||
{binary_strings, on}]).
|
||||
@@ -1030,6 +1043,7 @@ init_mssql(Host) ->
|
||||
FreeTDS = io_lib:fwrite("[~s]~n"
|
||||
"\thost = ~s~n"
|
||||
"\tport = ~p~n"
|
||||
"\tclient charset = UTF-8~n"
|
||||
"\ttds version = 7.1~n",
|
||||
[Host, Server, Port]),
|
||||
ODBCINST = io_lib:fwrite("[freetds]~n"
|
||||
@@ -1094,37 +1108,49 @@ query_timeout(LServer) ->
|
||||
timer:seconds(
|
||||
ejabberd_config:get_option({sql_query_timeout, LServer}, 60)).
|
||||
|
||||
%% ***IMPORTANT*** This error format requires extended_errors turned on.
|
||||
extended_error({"08S01", _, Reason}) ->
|
||||
% TCP Provider: The specified network name is no longer available
|
||||
?DEBUG("ODBC Link Failure: ~s", [Reason]),
|
||||
<<"Communication link failure">>;
|
||||
extended_error({"08001", _, Reason}) ->
|
||||
% Login timeout expired
|
||||
?DEBUG("ODBC Connect Timeout: ~s", [Reason]),
|
||||
<<"SQL connection failed">>;
|
||||
extended_error({"IMC01", _, Reason}) ->
|
||||
% The connection is broken and recovery is not possible
|
||||
?DEBUG("ODBC Link Failure: ~s", [Reason]),
|
||||
<<"Communication link failure">>;
|
||||
extended_error({"IMC06", _, Reason}) ->
|
||||
% The connection is broken and recovery is not possible
|
||||
?DEBUG("ODBC Link Failure: ~s", [Reason]),
|
||||
<<"Communication link failure">>;
|
||||
extended_error({Code, _, Reason}) ->
|
||||
?DEBUG("ODBC Error ~s: ~s", [Code, Reason]),
|
||||
iolist_to_binary(Reason);
|
||||
extended_error(Error) ->
|
||||
Error.
|
||||
|
||||
check_error({error, Why} = Err, _Query) when Why == killed ->
|
||||
Err;
|
||||
check_error({error, Why} = Err, #sql_query{} = Query) ->
|
||||
check_error({error, Why}, #sql_query{} = Query) ->
|
||||
Err = extended_error(Why),
|
||||
?ERROR_MSG("SQL query '~s' at ~p failed: ~p",
|
||||
[Query#sql_query.hash, Query#sql_query.loc, Why]),
|
||||
Err;
|
||||
check_error({error, Why} = Err, Query) ->
|
||||
[Query#sql_query.hash, Query#sql_query.loc, Err]),
|
||||
{error, Err};
|
||||
check_error({error, Why}, Query) ->
|
||||
Err = extended_error(Why),
|
||||
case catch iolist_to_binary(Query) of
|
||||
SQuery when is_binary(SQuery) ->
|
||||
?ERROR_MSG("SQL query '~s' failed: ~p", [SQuery, Why]);
|
||||
?ERROR_MSG("SQL query '~s' failed: ~p", [SQuery, Err]);
|
||||
_ ->
|
||||
?ERROR_MSG("SQL query ~p failed: ~p", [Query, Why])
|
||||
?ERROR_MSG("SQL query ~p failed: ~p", [Query, Err])
|
||||
end,
|
||||
Err;
|
||||
{error, Err};
|
||||
check_error(Result, _Query) ->
|
||||
Result.
|
||||
|
||||
-spec opt_type(sql_database) -> fun((binary()) -> binary());
|
||||
(sql_keepalive_interval) -> fun((pos_integer()) -> pos_integer());
|
||||
(sql_password) -> fun((binary()) -> binary());
|
||||
(sql_port) -> fun((0..65535) -> 0..65535);
|
||||
(sql_server) -> fun((binary()) -> binary());
|
||||
(sql_username) -> fun((binary()) -> binary());
|
||||
(sql_ssl) -> fun((boolean()) -> boolean());
|
||||
(sql_ssl_verify) -> fun((boolean()) -> boolean());
|
||||
(sql_ssl_certfile) -> fun((boolean()) -> boolean());
|
||||
(sql_ssl_cafile) -> fun((boolean()) -> boolean());
|
||||
(sql_query_timeout) -> fun((pos_integer()) -> pos_integer());
|
||||
(sql_connect_timeout) -> fun((pos_integer()) -> pos_integer());
|
||||
(sql_queue_type) -> fun((ram | file) -> ram | file);
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(sql_database) -> fun iolist_to_binary/1;
|
||||
opt_type(sql_keepalive_interval) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
|
||||
+47
-32
@@ -31,22 +31,19 @@
|
||||
|
||||
-export([start_link/1, init/1, add_pid/2, remove_pid/2,
|
||||
get_pids/1, get_random_pid/1, transform_options/1,
|
||||
opt_type/1]).
|
||||
reload/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
|
||||
-define(PGSQL_PORT, 5432).
|
||||
|
||||
-define(MYSQL_PORT, 3306).
|
||||
|
||||
-define(DEFAULT_POOL_SIZE, 10).
|
||||
|
||||
-define(DEFAULT_SQL_START_INTERVAL, 30).
|
||||
|
||||
-define(CONNECT_TIMEOUT, 500).
|
||||
|
||||
-record(sql_pool, {host, pid}).
|
||||
-record(sql_pool, {host :: binary(),
|
||||
pid :: pid()}).
|
||||
|
||||
start_link(Host) ->
|
||||
ejabberd_mnesia:create(?MODULE, sql_pool,
|
||||
@@ -60,9 +57,6 @@ start_link(Host) ->
|
||||
?MODULE, [Host]).
|
||||
|
||||
init([Host]) ->
|
||||
StartInterval = ejabberd_config:get_option(
|
||||
{sql_start_interval, Host},
|
||||
?DEFAULT_SQL_START_INTERVAL),
|
||||
Type = ejabberd_config:get_option({sql_type, Host}, odbc),
|
||||
PoolSize = get_pool_size(Type, Host),
|
||||
case Type of
|
||||
@@ -73,16 +67,37 @@ init([Host]) ->
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
{ok, {{one_for_one, PoolSize * 10, 1},
|
||||
[child_spec(I, Host) || I <- lists:seq(1, PoolSize)]}}.
|
||||
|
||||
{ok,
|
||||
{{one_for_one, PoolSize * 10, 1},
|
||||
lists:map(fun (I) ->
|
||||
{I,
|
||||
{ejabberd_sql, start_link,
|
||||
[Host, StartInterval * 1000]},
|
||||
transient, 2000, worker, [?MODULE]}
|
||||
end,
|
||||
lists:seq(1, PoolSize))}}.
|
||||
reload(Host) ->
|
||||
Type = ejabberd_config:get_option({sql_type, Host}, odbc),
|
||||
NewPoolSize = get_pool_size(Type, Host),
|
||||
OldPoolSize = ets:select_count(
|
||||
sql_pool,
|
||||
ets:fun2ms(
|
||||
fun(#sql_pool{host = H}) when H == Host ->
|
||||
true
|
||||
end)),
|
||||
reload(Host, NewPoolSize, OldPoolSize).
|
||||
|
||||
reload(Host, NewPoolSize, OldPoolSize) ->
|
||||
Sup = gen_mod:get_module_proc(Host, ?MODULE),
|
||||
if NewPoolSize == OldPoolSize ->
|
||||
ok;
|
||||
NewPoolSize > OldPoolSize ->
|
||||
lists:foreach(
|
||||
fun(I) ->
|
||||
Spec = child_spec(I, Host),
|
||||
supervisor:start_child(Sup, Spec)
|
||||
end, lists:seq(OldPoolSize+1, NewPoolSize));
|
||||
OldPoolSize > NewPoolSize ->
|
||||
lists:foreach(
|
||||
fun(I) ->
|
||||
supervisor:terminate_child(Sup, I),
|
||||
supervisor:delete_child(Sup, I)
|
||||
end, lists:seq(NewPoolSize+1, OldPoolSize))
|
||||
end.
|
||||
|
||||
get_pids(Host) ->
|
||||
Rs = mnesia:dirty_read(sql_pool, Host),
|
||||
@@ -92,7 +107,7 @@ get_random_pid(Host) ->
|
||||
case get_pids(Host) of
|
||||
[] -> none;
|
||||
Pids ->
|
||||
I = randoms:round_robin(length(Pids)) + 1,
|
||||
I = p1_rand:round_robin(length(Pids)) + 1,
|
||||
lists:nth(I, Pids)
|
||||
end.
|
||||
|
||||
@@ -124,6 +139,13 @@ get_pool_size(SQLType, Host) ->
|
||||
end,
|
||||
PoolSize.
|
||||
|
||||
child_spec(I, Host) ->
|
||||
StartInterval = ejabberd_config:get_option(
|
||||
{sql_start_interval, Host},
|
||||
?DEFAULT_SQL_START_INTERVAL),
|
||||
{I, {ejabberd_sql, start_link, [Host, timer:seconds(StartInterval)]},
|
||||
transient, 2000, worker, [?MODULE]}.
|
||||
|
||||
transform_options(Opts) ->
|
||||
lists:foldl(fun transform_options/2, [], Opts).
|
||||
|
||||
@@ -168,16 +190,11 @@ check_sqlite_db(Host) ->
|
||||
ok
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?INFO_MSG("Failed open sqlite database, reason ~p", [Reason])
|
||||
?WARNING_MSG("Failed open sqlite database, reason ~p", [Reason])
|
||||
end.
|
||||
|
||||
create_sqlite_tables(DB) ->
|
||||
SqlDir = case code:priv_dir(ejabberd) of
|
||||
{error, _} ->
|
||||
?SQL_DIR;
|
||||
PrivDir ->
|
||||
filename:join(PrivDir, "sql")
|
||||
end,
|
||||
SqlDir = misc:sql_dir(),
|
||||
File = filename:join(SqlDir, "lite.sql"),
|
||||
case file:open(File, [read, binary]) of
|
||||
{ok, Fd} ->
|
||||
@@ -186,8 +203,8 @@ create_sqlite_tables(DB) ->
|
||||
[ok = sqlite3:sql_exec(DB, Q) || Q <- Qs],
|
||||
ok = sqlite3:sql_exec(DB, "commit");
|
||||
{error, Reason} ->
|
||||
?INFO_MSG("Failed to read SQLite schema file: ~s",
|
||||
[file:format_error(Reason)])
|
||||
?WARNING_MSG("Failed to read SQLite schema file: ~s",
|
||||
[file:format_error(Reason)])
|
||||
end.
|
||||
|
||||
read_lines(Fd, File, Acc) ->
|
||||
@@ -218,9 +235,7 @@ read_lines(Fd, File, Acc) ->
|
||||
[]
|
||||
end.
|
||||
|
||||
-spec opt_type(sql_pool_size) -> fun((pos_integer()) -> pos_integer());
|
||||
(sql_start_interval) -> fun((pos_integer()) -> pos_integer());
|
||||
(atom()) -> [atom()].
|
||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
||||
opt_type(sql_pool_size) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(sql_start_interval) ->
|
||||
|
||||
+40
-41
@@ -24,29 +24,30 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_stun).
|
||||
|
||||
-behaviour(ejabberd_listener).
|
||||
-protocol({rfc, 5766}).
|
||||
-protocol({xep, 176, '1.0'}).
|
||||
|
||||
-ifndef(STUN).
|
||||
-include("logger.hrl").
|
||||
-export([socket_type/0, start/2, listen_opt_type/1]).
|
||||
log_error() ->
|
||||
?CRITICAL_MSG("ejabberd is not compiled with STUN/TURN support", []).
|
||||
socket_type() ->
|
||||
log_error(),
|
||||
raw.
|
||||
listen_opt_type(_) ->
|
||||
log_error(),
|
||||
[].
|
||||
-export([accept/1, start/2, start_link/2, listen_options/0]).
|
||||
fail() ->
|
||||
?CRITICAL_MSG("Listening module ~s is not available: "
|
||||
"ejabberd is not compiled with STUN/TURN support",
|
||||
[?MODULE]),
|
||||
erlang:error(stun_not_compiled).
|
||||
accept(_) ->
|
||||
fail().
|
||||
listen_options() ->
|
||||
fail().
|
||||
start(_, _) ->
|
||||
log_error(),
|
||||
{error, sip_not_compiled}.
|
||||
fail().
|
||||
start_link(_, _) ->
|
||||
fail().
|
||||
-else.
|
||||
-export([tcp_init/2, udp_init/2, udp_recv/5, start/2,
|
||||
socket_type/0, listen_opt_type/1]).
|
||||
start_link/2, accept/1, listen_opt_type/1, listen_options/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
%%%===================================================================
|
||||
@@ -66,8 +67,11 @@ udp_recv(Socket, Addr, Port, Packet, Opts) ->
|
||||
start(Opaque, Opts) ->
|
||||
stun:start(Opaque, Opts).
|
||||
|
||||
socket_type() ->
|
||||
raw.
|
||||
start_link({gen_tcp, Sock}, Opts) ->
|
||||
stun:start_link(Sock, Opts).
|
||||
|
||||
accept(_Pid) ->
|
||||
ok.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
@@ -79,7 +83,7 @@ prepare_turn_opts(Opts) ->
|
||||
prepare_turn_opts(Opts, _UseTurn = false) ->
|
||||
set_certfile(Opts);
|
||||
prepare_turn_opts(Opts, _UseTurn = true) ->
|
||||
NumberOfMyHosts = length(?MYHOSTS),
|
||||
NumberOfMyHosts = length(ejabberd_config:get_myhosts()),
|
||||
case proplists:get_value(turn_ip, Opts) of
|
||||
undefined ->
|
||||
?WARNING_MSG("option 'turn_ip' is undefined, "
|
||||
@@ -100,15 +104,15 @@ prepare_turn_opts(Opts, _UseTurn = true) ->
|
||||
"'auth_type' is set to 'user', "
|
||||
"more likely the TURN relay won't "
|
||||
"be working properly. Using ~s as "
|
||||
"a fallback", [?MYNAME]);
|
||||
"a fallback", [ejabberd_config:get_myname()]);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
[{auth_realm, ?MYNAME}];
|
||||
[{auth_realm, ejabberd_config:get_myname()}];
|
||||
_ ->
|
||||
[]
|
||||
end,
|
||||
MaxRate = shaper:get_max_rate(Shaper),
|
||||
MaxRate = ejabberd_shaper:get_max_rate(Shaper),
|
||||
Opts1 = Realm ++ [{auth_fun, AuthFun},{shaper, MaxRate} |
|
||||
lists:keydelete(shaper, 1, Opts)],
|
||||
set_certfile(Opts1).
|
||||
@@ -118,7 +122,7 @@ set_certfile(Opts) ->
|
||||
true ->
|
||||
Opts;
|
||||
false ->
|
||||
Realm = proplists:get_value(auth_realm, Opts, ?MYNAME),
|
||||
Realm = proplists:get_value(auth_realm, Opts, ejabberd_config:get_myname()),
|
||||
case ejabberd_pkix:get_certfile(Realm) of
|
||||
{ok, CertFile} ->
|
||||
[{certfile, CertFile}|Opts];
|
||||
@@ -139,27 +143,16 @@ listen_opt_type(turn_ip) ->
|
||||
{ok, Addr} = inet_parse:ipv4_address(binary_to_list(S)),
|
||||
Addr
|
||||
end;
|
||||
listen_opt_type(shaper) ->
|
||||
fun acl:shaper_rules_validator/1;
|
||||
listen_opt_type(auth_type) ->
|
||||
fun(anonymous) -> anonymous;
|
||||
(user) -> user
|
||||
end;
|
||||
listen_opt_type(auth_realm) ->
|
||||
fun iolist_to_binary/1;
|
||||
listen_opt_type(tls) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(certfile) ->
|
||||
fun(S) ->
|
||||
%% We cannot deprecate the option for now:
|
||||
%% I think STUN/TURN clients are too stupid to set SNI
|
||||
ejabberd_pkix:add_certfile(S),
|
||||
iolist_to_binary(S)
|
||||
end;
|
||||
listen_opt_type(turn_min_port) ->
|
||||
fun(P) when is_integer(P), P > 0, P =< 65535 -> P end;
|
||||
fun(P) when is_integer(P), P > 1024, P < 65536 -> P end;
|
||||
listen_opt_type(turn_max_port) ->
|
||||
fun(P) when is_integer(P), P > 0, P =< 65535 -> P end;
|
||||
fun(P) when is_integer(P), P > 1024, P < 65536 -> P end;
|
||||
listen_opt_type(turn_max_allocations) ->
|
||||
fun(I) when is_integer(I), I>0 -> I;
|
||||
(unlimited) -> infinity;
|
||||
@@ -171,11 +164,17 @@ listen_opt_type(turn_max_permissions) ->
|
||||
(infinity) -> infinity
|
||||
end;
|
||||
listen_opt_type(server_name) ->
|
||||
fun iolist_to_binary/1;
|
||||
listen_opt_type(backlog) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(_) ->
|
||||
[shaper, auth_type, auth_realm, tls, certfile, turn_min_port,
|
||||
turn_max_port, turn_max_allocations, turn_max_permissions,
|
||||
server_name, backlog].
|
||||
fun iolist_to_binary/1.
|
||||
|
||||
listen_options() ->
|
||||
[{shaper, none},
|
||||
{auth_type, user},
|
||||
{auth_realm, undefined},
|
||||
{tls, false},
|
||||
{certfile, undefined},
|
||||
{turn_min_port, 49152},
|
||||
{turn_max_port, 65535},
|
||||
{turn_max_allocations, 10},
|
||||
{turn_max_permissions, 10},
|
||||
{server_name, <<"ejabberd">>}].
|
||||
-endif.
|
||||
|
||||
@@ -39,7 +39,6 @@ init([]) ->
|
||||
{ok, {{one_for_one, 10, 1},
|
||||
[worker(ejabberd_hooks),
|
||||
worker(ejabberd_cluster),
|
||||
worker(cyrsasl),
|
||||
worker(translate),
|
||||
worker(ejabberd_access_permissions),
|
||||
worker(ejabberd_ctl),
|
||||
@@ -53,7 +52,7 @@ init([]) ->
|
||||
simple_supervisor(ejabberd_s2s_out),
|
||||
simple_supervisor(ejabberd_service),
|
||||
worker(acl),
|
||||
worker(shaper),
|
||||
worker(ejabberd_shaper),
|
||||
supervisor(ejabberd_backend_sup),
|
||||
supervisor(ejabberd_rdbms),
|
||||
supervisor(ejabberd_riak_sup),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user