Compare commits
226 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0bd4d1aade | |||
| d49aa429ca | |||
| 316a19d600 | |||
| c43037887a | |||
| 538e0d4844 | |||
| f3795e9d03 | |||
| 3df919244c | |||
| 67773c5174 | |||
| 61dee97738 | |||
| 6774418a7f | |||
| ad6fcc7865 | |||
| ca28faa51a | |||
| 5b730cdbf2 | |||
| 9ed0357760 | |||
| 06ce884aa8 | |||
| 3fc0eb4f5b | |||
| 3bfa683586 | |||
| 5be49cc0fa | |||
| 42c029d5f7 | |||
| a567abcfdf | |||
| 265c7b62c7 | |||
| 332567693c | |||
| de7dc4affa | |||
| 48c5ab59f1 | |||
| b2855d63a7 | |||
| 0282cf64a0 | |||
| e5cb9dad40 | |||
| ec819b4002 | |||
| acc162f4f4 | |||
| b8505f3e78 | |||
| 8a71e2e4f7 | |||
| a5284229cb | |||
| d0f36537fb | |||
| 3cf4fbc7b0 | |||
| fe4b1a492c | |||
| c3b4b4ce4f | |||
| 95244c3b6f | |||
| 89d91b609a | |||
| d28064518b | |||
| 7627575856 | |||
| 99444f2d0e | |||
| 4c0f87b2ff | |||
| 54363f8476 | |||
| 094f586811 | |||
| 45a3c7e0ce | |||
| e2652ce02f | |||
| df651d893e | |||
| a2e1f5c882 | |||
| 7f5796fe31 | |||
| 5f1191b9f5 | |||
| 0041a11c4a | |||
| e17a16a300 | |||
| 7b3d26992b | |||
| 9373ad20ca | |||
| b283cfa6f2 | |||
| a84771fd14 | |||
| 2bb6782bee | |||
| ae151927ae | |||
| dfbdffad44 | |||
| d71bc73271 | |||
| ea9c3fd8f7 | |||
| f3b3bffec0 | |||
| f39dbe6e49 | |||
| 75450a62b3 | |||
| a15039638b | |||
| 50de427570 | |||
| 55604b2d97 | |||
| 92bc5dc85b | |||
| baf2473688 | |||
| f5bab5d6c4 | |||
| b23be02dfe | |||
| 0bda169a5a | |||
| 96c183c04b | |||
| b293e99aee | |||
| b4b3ff50d6 | |||
| 8962397cf3 | |||
| b1ecd8ac01 | |||
| 4f1d7c4b66 | |||
| bb20e5f3fa | |||
| e1e7986918 | |||
| 7ba6fae67c | |||
| 7beb19b01e | |||
| a84dd0f627 | |||
| d8f9219b4f | |||
| ddc29d42de | |||
| e15595df64 | |||
| 99b41146b1 | |||
| 6b079c0ab3 | |||
| c5aea779b4 | |||
| be33c93344 | |||
| 2785f1dfd2 | |||
| b179874ec6 | |||
| c3eeb8624b | |||
| bc808ffcde | |||
| d9bf5a6865 | |||
| 67fe5d38a7 | |||
| 63dba3fd64 | |||
| da1a5036fe | |||
| 0d3637d18f | |||
| f2a3118ecc | |||
| dbf1cabdcd | |||
| ad0fd1eac1 | |||
| 3003307e60 | |||
| 76f827ac83 | |||
| b5138a8ddb | |||
| da81590fef | |||
| 8a41cfc0f5 | |||
| 5054a9933f | |||
| 5912c573ea | |||
| b2095ebcfe | |||
| 0a67cdfb16 | |||
| c2235860ab | |||
| 22e43ebd8a | |||
| 35be7d2718 | |||
| f7566bd00e | |||
| 5bf753fd2d | |||
| c1e5ae5308 | |||
| ea87bdfbe5 | |||
| 7a1ed065fe | |||
| ec0f0f7c72 | |||
| 0acc69e303 | |||
| 4bf4193d55 | |||
| d625e24029 | |||
| a875195940 | |||
| 06c480106f | |||
| e070e6bccb | |||
| de49e7631f | |||
| 25abf8b634 | |||
| ff06bdf144 | |||
| d5afc767e6 | |||
| 5704a980c5 | |||
| cffdb06b66 | |||
| f5d208441d | |||
| 71a856deaa | |||
| 7e1df0752a | |||
| f0ccdebf7f | |||
| 32e5a3255d | |||
| c102a45fac | |||
| 52ded14b7f | |||
| e5ba7c3f3c | |||
| 51aa9d98a7 | |||
| a65500b6aa | |||
| 032f796292 | |||
| 516f4d03a1 | |||
| 60a8623929 | |||
| 9dbdeba6c1 | |||
| 4632f5520f | |||
| ffe02c46e4 | |||
| 42794ce4e4 | |||
| 9188a7b838 | |||
| 66fc1bf3b6 | |||
| 11a58f8dff | |||
| 97f913b8d9 | |||
| 6c1a1bd000 | |||
| 9fb2253aa9 | |||
| 672c2f75d3 | |||
| 5c85106a41 | |||
| c2911222e4 | |||
| 232b66b0f4 | |||
| 3327da72a6 | |||
| c65dcfeda7 | |||
| 411d1711a0 | |||
| bef7d1ba66 | |||
| c990abf222 | |||
| 983aaac765 | |||
| 0f25e59143 | |||
| 2a77805072 | |||
| b86402f3e7 | |||
| f6ebbe4c78 | |||
| 485f8e48e5 | |||
| 51f652a1e1 | |||
| fc3c605945 | |||
| 69de43d5ee | |||
| ad4ffce788 | |||
| cd82a9d534 | |||
| 3b646cc2ec | |||
| bb58307190 | |||
| 53870c854e | |||
| b69fb5aae0 | |||
| ddf6076328 | |||
| d2974cf48a | |||
| dc601610b6 | |||
| 719dfe12f6 | |||
| c47366ba97 | |||
| 1b26c8d214 | |||
| f31782a252 | |||
| d0af61f488 | |||
| 795efb2ee1 | |||
| 6e5439db5c | |||
| 5d582080be | |||
| 4b012a99d2 | |||
| 7e561dd20a | |||
| 2afdde84ea | |||
| 2269d290d8 | |||
| 56d4224e08 | |||
| 666da60cba | |||
| e8f1de8785 | |||
| 1f6c0022dd | |||
| 6cdb7b4468 | |||
| a917f4d451 | |||
| ba2b650464 | |||
| 121d12f4d1 | |||
| 7abd13974b | |||
| ae86af9399 | |||
| a0c8cac1b6 | |||
| 818ff5a263 | |||
| c0ef054f6f | |||
| d35a8805b0 | |||
| 1daa7ef785 | |||
| 7b0fa7e6e2 | |||
| 8bdccc25ab | |||
| 59b7c89944 | |||
| a7639fd4ad | |||
| 99f8e58eaf | |||
| d3aab2ea18 | |||
| b970c88941 | |||
| 0f86559d83 | |||
| be592c9272 | |||
| 3df78d3a8f | |||
| d2427c98c3 | |||
| 84819ba0fe | |||
| f66a004821 | |||
| e1da673502 | |||
| 7d58b7a100 | |||
| f2c3fe8ac6 | |||
| 1c1b12fc64 |
+6
-16
@@ -6,7 +6,6 @@ otp_release:
|
||||
- 19.2
|
||||
|
||||
services:
|
||||
- riak
|
||||
- redis-server
|
||||
|
||||
before_install:
|
||||
@@ -31,16 +30,6 @@ install:
|
||||
|
||||
before_script:
|
||||
# Ulimit: See Travis-CI issue report: https://github.com/travis-ci/travis-ci/issues/3328
|
||||
- echo 'ulimit -n 4096' > riak
|
||||
- sudo mv riak /etc/default/riak
|
||||
- mkdir "$PWD/ebin"
|
||||
- sed 's/^storage_backend.*/storage_backend = leveldb/' /etc/riak/riak.conf > riak.conf
|
||||
- sudo mv riak.conf /etc/riak/riak.conf
|
||||
- echo "[{riak_kv, [{add_paths, [\"$PWD/ebin/\"]}]}]." > advanced.config
|
||||
- sudo mv advanced.config /etc/riak/advanced.config
|
||||
- sudo service riak restart
|
||||
- sudo riak-admin wait-for-service riak_kv 'riak@127.0.0.1'
|
||||
- sudo riak-admin test
|
||||
- mysql -u root -e "CREATE USER 'ejabberd_test'@'localhost' IDENTIFIED BY 'ejabberd_test';"
|
||||
- mysql -u root -e "CREATE DATABASE ejabberd_test;"
|
||||
- mysql -u root -e "GRANT ALL ON ejabberd_test.* TO 'ejabberd_test'@'localhost';"
|
||||
@@ -50,23 +39,24 @@ before_script:
|
||||
|
||||
script:
|
||||
- ./autogen.sh
|
||||
- ./configure --prefix=/tmp/ejabberd --enable-all --disable-odbc
|
||||
- ./configure --prefix=/tmp/ejabberd --enable-all --disable-odbc --disable-riak
|
||||
- make
|
||||
- make install
|
||||
- make install -s
|
||||
- make xref
|
||||
- sed -i -e 's/ct:pal/ct:log/' test/suite.erl
|
||||
- ln -sf ../sql priv/
|
||||
- echo "" >> rebar.config
|
||||
- echo '{ct_extra_params, "-verbosity 20"}.' >> rebar.config
|
||||
- escript ./rebar skip_deps=true ct -v
|
||||
- grep -q 'TEST COMPLETE, \([[:digit:]]*\) ok, .* of \1 ' logs/raw.log
|
||||
- grep -q 'TEST COMPLETE,.* 0 failed' logs/raw.log
|
||||
|
||||
after_script:
|
||||
- find logs -name suite.log -exec cat '{}' ';'
|
||||
|
||||
after_failure:
|
||||
- find logs -name exunit.log -exec cat '{}' ';'
|
||||
# Try checking Riak database logs
|
||||
- tail -n 100000 /var/log/riak/*.log
|
||||
- find logs -name ejabberd.log -exec cat '{}' ';'
|
||||
- find logs -name suite.log -exec cat '{}' ';' | awk 'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}
|
||||
|
||||
after_success:
|
||||
- coveralls-merge erlang.json
|
||||
|
||||
+8
-2
@@ -85,6 +85,11 @@ else
|
||||
CHOWN_OUTPUT=&1
|
||||
INIT_USER=$(INSTALLUSER)
|
||||
endif
|
||||
# if no group was enabled, don't set privileges or ownership
|
||||
INSTALLGROUP=@INSTALLGROUP@
|
||||
ifneq ($(INSTALLGROUP),)
|
||||
G_USER=-g $(INSTALLGROUP)
|
||||
endif
|
||||
|
||||
all: deps src
|
||||
|
||||
@@ -97,6 +102,7 @@ deps/.got:
|
||||
$(REBAR) get-deps && :> deps/.got
|
||||
|
||||
deps/.built: deps/.got
|
||||
$(REBAR) configure-deps
|
||||
$(REBAR) compile && :> deps/.built
|
||||
|
||||
src: deps/.built
|
||||
@@ -149,7 +155,7 @@ define DEP_VERSION_template
|
||||
DEP_$(1)_VERSION:=$(shell $(SED) -e '/vsn/!d;s/.*, *"/$(1)-/;s/".*//' $(2) 2>/dev/null)
|
||||
endef
|
||||
|
||||
DELETE_TARGET_SO=$(if $(subst X.soX,,X$(suffix $(1))X),,rm $(call TO_DEST,$(1));)
|
||||
DELETE_TARGET_SO=$(if $(subst X.soX,,X$(suffix $(1))X),,rm -f $(call TO_DEST,$(1));)
|
||||
|
||||
$(foreach DEP,$(DEPS),$(eval $(call DEP_VERSION_template,$(DEP),deps/$(DEP)/ebin/$(DEP).app)))
|
||||
$(eval $(call DEP_VERSION_template,ejabberd,ebin/ejabberd.app))
|
||||
@@ -159,7 +165,7 @@ $(call TO_DEST,$(1)): $(1) $(call TO_DEST,$(dir $(1))) ; $(call DELETE_TARGET_SO
|
||||
endef
|
||||
|
||||
define COPY_BINARY_template
|
||||
$(call TO_DEST,$(1)): $(1) $(call TO_DEST,$(dir $(1))) ; rm $(call TO_DEST,$(1)); $$(INSTALL) -m 755 $$(O_USER) $(1) $(call TO_DEST,$(1))
|
||||
$(call TO_DEST,$(1)): $(1) $(call TO_DEST,$(dir $(1))) ; rm -f $(call TO_DEST,$(1)); $$(INSTALL) -m 755 $$(O_USER) $(1) $(call TO_DEST,$(1))
|
||||
endef
|
||||
|
||||
$(foreach file,$(DEPS_FILES_FILTERED) $(MAIN_FILES),$(eval $(call COPY_template,$(file))))
|
||||
|
||||
+25
-9
@@ -244,14 +244,6 @@ AC_ARG_ENABLE(sip,
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-sip) ;;
|
||||
esac],[if test "x$sip" = "x"; then sip=false; fi])
|
||||
|
||||
AC_ARG_ENABLE(graphics,
|
||||
[AC_HELP_STRING([--enable-graphics], [enable support for graphic images manipulation (default: yes)])],
|
||||
[case "${enableval}" in
|
||||
yes) graphics=true ;;
|
||||
no) graphics=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-graphics) ;;
|
||||
esac],[if test "x$graphics" = "x"; then graphics=true; fi])
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
vars.config
|
||||
src/ejabberd.app.src])
|
||||
@@ -269,6 +261,19 @@ if test "$ENABLEUSER" != ""; then
|
||||
echo "allow this system user to start ejabberd: $ENABLEUSER"
|
||||
AC_SUBST([INSTALLUSER], [$ENABLEUSER])
|
||||
fi
|
||||
ENABLEGROUP=""
|
||||
AC_ARG_ENABLE(group,
|
||||
[AS_HELP_STRING([--enable-group[[[[=GROUP]]]]], [allow this system group to start ejabberd (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) ENABLEGROUP=`groups |head -n 1` ;;
|
||||
no) ENABLEGROUP="" ;;
|
||||
*) ENABLEGROUP=$enableval
|
||||
esac],
|
||||
[])
|
||||
if test "$ENABLEGROUP" != ""; then
|
||||
echo "allow this system group to start ejabberd: $ENABLEGROUP"
|
||||
AC_SUBST([INSTALLGROUP], [$ENABLEGROUP])
|
||||
fi
|
||||
|
||||
ERLANG_DEPRECATED_TYPES_CHECK
|
||||
|
||||
@@ -279,6 +284,17 @@ if test "$sqlite" = "true"; then
|
||||
fi
|
||||
fi
|
||||
|
||||
enabled_backends=""
|
||||
for backend in odbc mysql pgsql sqlite riak redis; do
|
||||
if eval test x\${$backend} = xtrue; then
|
||||
if test "x$enabled_backends" = "x"; then
|
||||
enabled_backends=$backend
|
||||
else
|
||||
enabled_backends="$enabled_backends, $backend"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
AC_SUBST(hipe)
|
||||
AC_SUBST(roster_gateway_workaround)
|
||||
AC_SUBST(new_sql_schema)
|
||||
@@ -297,12 +313,12 @@ AC_SUBST(iconv)
|
||||
AC_SUBST(stun)
|
||||
AC_SUBST(sip)
|
||||
AC_SUBST(debug)
|
||||
AC_SUBST(graphics)
|
||||
AC_SUBST(tools)
|
||||
AC_SUBST(latest_deps)
|
||||
AC_SUBST(system_deps)
|
||||
AC_SUBST(CFLAGS)
|
||||
AC_SUBST(CPPFLAGS)
|
||||
AC_SUBST(LDFLAGS)
|
||||
AC_SUBST(enabled_backends)
|
||||
|
||||
AC_OUTPUT
|
||||
|
||||
+34
-8
@@ -169,6 +169,20 @@ listen:
|
||||
max_stanza_size: 65536
|
||||
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
|
||||
-
|
||||
port: 5269
|
||||
ip: "::"
|
||||
@@ -185,6 +199,7 @@ listen:
|
||||
web_admin: true
|
||||
## register: true
|
||||
captcha: true
|
||||
|
||||
##
|
||||
## ejabberd_service: Interact with external components (transports, ...)
|
||||
##
|
||||
@@ -242,9 +257,9 @@ listen:
|
||||
## request_handlers:
|
||||
## "": mod_http_upload
|
||||
## tls: true
|
||||
## protocol_options: 'TLSOPTS'
|
||||
## dhfile: 'DHFILE'
|
||||
## ciphers: 'CIPHERS'
|
||||
## 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).
|
||||
@@ -430,6 +445,11 @@ auth_method: internal
|
||||
##
|
||||
## sql_keepalive_interval: undefined
|
||||
|
||||
##
|
||||
## Use the new SQL schema
|
||||
##
|
||||
## new_sql_schema: true
|
||||
|
||||
###. ===============
|
||||
###' TRAFFIC SHAPERS
|
||||
|
||||
@@ -722,7 +742,7 @@ modules:
|
||||
## mod_http_upload:
|
||||
## # docroot: "@HOME@/upload"
|
||||
## put_url: "https://@HOST@:5444"
|
||||
## thumbnail: false # otherwise needs the identify command from ImageMagick installed
|
||||
## thumbnail: false # otherwise needs ejabberd to be compiled with libgd support
|
||||
## mod_http_upload_quota:
|
||||
## max_days: 30
|
||||
mod_last: {}
|
||||
@@ -761,6 +781,14 @@ modules:
|
||||
- "flat"
|
||||
- "hometree"
|
||||
- "pep" # pep requires mod_caps
|
||||
force_node_config:
|
||||
## Avoid using OMEMO by default because it
|
||||
## introduces a lot of hard-to-track problems
|
||||
"eu.siacs.conversations.axolotl.*":
|
||||
access_model: whitelist
|
||||
## Avoid buggy clients to make their bookmarks public
|
||||
"storage:bookmarks":
|
||||
access_model: whitelist
|
||||
mod_push: {}
|
||||
mod_push_keepalive: {}
|
||||
## mod_register:
|
||||
@@ -803,10 +831,7 @@ modules:
|
||||
mod_vcard:
|
||||
search: false
|
||||
mod_vcard_xupdate: {}
|
||||
## Convert all avatars posted by Android clients from WebP to JPEG
|
||||
## mod_avatar: # this module needs compile option --enable-graphics
|
||||
## convert:
|
||||
## webp: jpeg
|
||||
mod_avatar: {}
|
||||
mod_version: {}
|
||||
mod_stream_mgmt: {}
|
||||
## Non-SASL Authentication (XEP-0078) is now disabled by default
|
||||
@@ -820,6 +845,7 @@ modules:
|
||||
## and check your accessibility at https://check.messaging.one/
|
||||
mod_s2s_dialback: {}
|
||||
mod_http_api: {}
|
||||
mod_fail2ban: {}
|
||||
|
||||
##
|
||||
## Enable modules with custom options in a specific virtual host
|
||||
|
||||
+11
-13
@@ -27,7 +27,7 @@ case $(id -un) in
|
||||
EXEC_CMD="as_install_user"
|
||||
else
|
||||
EXEC_CMD="as_current_user"
|
||||
echo "WARNING: This is not recommended to run ejabberd as root" >&2
|
||||
echo "WARNING: It is not recommended to run ejabberd as root" >&2
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
@@ -41,19 +41,17 @@ case $(id -un) in
|
||||
esac
|
||||
|
||||
# parse command line parameters
|
||||
for arg; do
|
||||
case $arg in
|
||||
-n|--node) ERLANG_NODE_ARG=$2; shift;;
|
||||
-s|--spool) SPOOL_DIR=$2; shift;;
|
||||
-l|--logs) LOGS_DIR=$2; shift;;
|
||||
-f|--config) EJABBERD_CONFIG_PATH=$2; shift;;
|
||||
-c|--ctl-config) EJABBERDCTL_CONFIG_PATH=$2; shift;;
|
||||
-d|--config-dir) ETC_DIR=$2; shift;;
|
||||
-t|--no-timeout) NO_TIMEOUT="--no-timeout";;
|
||||
--) :;;
|
||||
while [ $# -gt 0 ]; do
|
||||
case $1 in
|
||||
-n|--node) ERLANG_NODE_ARG=$2; shift 2;;
|
||||
-s|--spool) SPOOL_DIR=$2; shift 2;;
|
||||
-l|--logs) LOGS_DIR=$2; shift 2;;
|
||||
-f|--config) EJABBERD_CONFIG_PATH=$2; shift 2;;
|
||||
-c|--ctl-config) EJABBERDCTL_CONFIG_PATH=$2; shift 2;;
|
||||
-d|--config-dir) ETC_DIR=$2; shift 2;;
|
||||
-t|--no-timeout) NO_TIMEOUT="--no-timeout"; shift;;
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# define ejabberd variables if not already defined from the command line
|
||||
@@ -263,7 +261,7 @@ cd "$SPOOL_DIR" || {
|
||||
case $1 in
|
||||
start)
|
||||
check_start
|
||||
exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -noinput -detached
|
||||
exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -detached
|
||||
;;
|
||||
foreground)
|
||||
check_start
|
||||
|
||||
@@ -46,6 +46,6 @@
|
||||
buf :: binary(),
|
||||
http_opts = [] :: list()}).
|
||||
|
||||
-type method() :: 'GET' | 'HEAD' | 'DELETE' | 'OPTIONS' | 'PUT' | 'POST' | 'TRACE'.
|
||||
-type method() :: 'GET' | 'HEAD' | 'DELETE' | 'OPTIONS' | 'PUT' | 'POST' | 'TRACE' | 'PATCH'.
|
||||
-type protocol() :: http | https.
|
||||
-type http_request() :: #request{}.
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
max_users = ?MAX_USERS_DEFAULT :: non_neg_integer() | none,
|
||||
logging = false :: boolean(),
|
||||
vcard = <<"">> :: binary(),
|
||||
vcard_xupdate = undefined :: undefined | external | binary(),
|
||||
captcha_whitelist = (?SETS):empty() :: ?TGB_SET,
|
||||
mam = false :: boolean(),
|
||||
pubsub = <<"">> :: binary()
|
||||
|
||||
@@ -3,7 +3,7 @@ defmodule Ejabberd.Mixfile do
|
||||
|
||||
def project do
|
||||
[app: :ejabberd,
|
||||
version: "18.1.0",
|
||||
version: "18.4.0",
|
||||
description: description(),
|
||||
elixir: "~> 1.4",
|
||||
elixirc_paths: ["lib"],
|
||||
@@ -28,7 +28,8 @@ defmodule Ejabberd.Mixfile do
|
||||
applications: [:ssl, :os_mon],
|
||||
included_applications: [:lager, :mnesia, :inets, :p1_utils, :cache_tab,
|
||||
:fast_tls, :stringprep, :fast_xml, :xmpp,
|
||||
:stun, :fast_yaml, :esip, :jiffy, :p1_oauth2, :fs]
|
||||
:stun, :fast_yaml, :esip, :jiffy, :p1_oauth2,
|
||||
:eimp, :base64url, :jose]
|
||||
++ cond_apps()]
|
||||
end
|
||||
|
||||
@@ -73,7 +74,9 @@ defmodule Ejabberd.Mixfile do
|
||||
{:p1_oauth2, "~> 0.6.1"},
|
||||
{:distillery, "~> 1.0"},
|
||||
{:ex_doc, ">= 0.0.0", only: :dev},
|
||||
{:fs, "~> 3.4"}]
|
||||
{:eimp, "~> 1.0"},
|
||||
{:base64url, "~> 0.0.1"},
|
||||
{:jose, "~> 1.8"}]
|
||||
++ cond_deps()
|
||||
end
|
||||
|
||||
@@ -89,13 +92,12 @@ defmodule Ejabberd.Mixfile do
|
||||
for {:true, dep} <- [{config(:sqlite), {:sqlite3, "~> 1.1"}},
|
||||
{config(:riak), {:riakc, "~> 2.4"}},
|
||||
{config(:redis), {:eredis, "~> 1.0"}},
|
||||
{config(:zlib), {:ezlib, github: "processone/ezlib", tag: "1.0.3", override: true, manager: :rebar}},
|
||||
{config(:zlib), {:ezlib, "~> 1.0"}},
|
||||
{config(:iconv), {:iconv, "~> 1.0"}},
|
||||
{config(:pam), {:epam, "~> 1.0"}},
|
||||
{config(:tools), {:luerl, github: "rvirding/luerl", tag: "v0.2"}},
|
||||
{config(:tools), {:luerl, "~> 0.3.1"}},
|
||||
{config(:tools), {:meck, "~> 0.8.4"}},
|
||||
{config(:tools), {:moka, github: "processone/moka", tag: "1.0.5c"}},
|
||||
{config(:graphics), {:eimp, github: "processone/eimp", tag: "1.0.1"}}], do:
|
||||
{config(:tools), {:moka, github: "processone/moka", tag: "1.0.5c"}}], do:
|
||||
dep
|
||||
end
|
||||
|
||||
@@ -105,8 +107,7 @@ defmodule Ejabberd.Mixfile do
|
||||
{config(:pgsql), :p1_pgsql},
|
||||
{config(:sqlite), :sqlite3},
|
||||
{config(:zlib), :ezlib},
|
||||
{config(:iconv), :iconv},
|
||||
{config(:graphics), :eimp}], do:
|
||||
{config(:iconv), :iconv}], do:
|
||||
app
|
||||
end
|
||||
|
||||
|
||||
@@ -1,32 +1,35 @@
|
||||
%{"cache_tab": {:hex, :cache_tab, "1.0.12", "a06a4ffbd4da8469791ba941512a6a45ed8c11865b4606a368e21b332da3638a", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
%{
|
||||
"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": {:git, "https://github.com/processone/eimp.git", "23796118176be98195db9f831f17dde74d1553e1", [tag: "1.0.1"]},
|
||||
"epam": {:hex, :epam, "1.0.3", "3adcc148cdbaaa2bbe15dd661f0d74284e5749a815b4e480dbf94e8e023361b9", [:rebar3], [], "hexpm"},
|
||||
"eimp": {:hex, :eimp, "1.0.3", "e40108d622d672cf6003d279d98fc46a98df182dbe8756857896ffd28883090d", [:rebar3], [{:p1_utils, "1.0.11", [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.21", "711c704337d434db6d7c70bd0da868aaacd91b252c0bb63b4580e6c896164f1f", [:rebar3], [{:fast_tls, "1.0.20", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.20", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ezlib": {:git, "https://github.com/processone/ezlib.git", "ec6491d788436bb096022843e6ec7f58d2973ae3", [tag: "1.0.3"]},
|
||||
"fast_tls": {:hex, :fast_tls, "1.0.20", "edd241961ab20b71ec1e9f75a2a2c043128ff117adf3efd42e6cec94f1937539", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"fast_xml": {:hex, :fast_xml, "1.1.28", "31ce5cf44d20e900e1a499009f886ff74b589324d532ed0ed7a432e4f498beb1", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"fast_yaml": {:hex, :fast_yaml, "1.0.12", "ee8527d388255cf7a24fc1e6cb2d09dca4e506966dd9d86e61d3d90f236a3e2e", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"fs": {:hex, :fs, "3.4.0", "6d18575c250b415b3cad559e6f97a4c822516c7bc2c10bfbb2493a8f230f5132", [: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"},
|
||||
"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"},
|
||||
"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.6", "3b424a80039059767f1037dc6a49ff07c2f88df14068c16dc938c4f377a77b4c", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"iconv": {:hex, :iconv, "1.0.7", "f81eb6b8c977b1fd078515937fdce64292d64c6102353fbbfe57db580f4689d1", [:rebar3], [{:p1_utils, "1.0.11", [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": {:git, "https://github.com/rvirding/luerl.git", "f7b2cc0ab6fa4245ebeda0169fc994aff0628bf9", [tag: "v0.2"]},
|
||||
"luerl": {:hex, :luerl, "0.3.1", "5412807630aac1aaf59ffe5a1bc09259c447b4faeb1d3fe2d4ef41b87676cb04", [:rebar3], [], "hexpm"},
|
||||
"meck": {:hex, :meck, "0.8.9", "64c5c0bd8bcca3a180b44196265c8ed7594e16bcc845d0698ec6b4e577f48188", [:rebar3], [], "hexpm"},
|
||||
"moka": {:git, "https://github.com/processone/moka.git", "3eed3a6dd7dedb70a6cd18f86c7561a18626eb3b", [tag: "1.0.5c"]},
|
||||
"p1_mysql": {:hex, :p1_mysql, "1.0.4", "7b9d7957a9d031813a0e6bcea5a7f5e91b54db805a92709a445cf75cf934bc1d", [:rebar3], [], "hexpm"},
|
||||
"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.4", "eadbbddee8d52145694bf86bdfe8c1ae8353a55e152410146b8c2711756d6041", [:rebar3], [], "hexpm"},
|
||||
"p1_utils": {:hex, :p1_utils, "1.0.10", "a6d6927114bac79cf6468a10824125492034af7071adc6ed5ebc4ddb443845d4", [:rebar3], [], "hexpm"},
|
||||
"p1_pgsql": {:hex, :p1_pgsql, "1.1.5", "1e1bef6e6d906e10552a608b9fe5ef39b3099caf0f44c07d3d9e18ac4dee34d1", [:rebar3], [], "hexpm"},
|
||||
"p1_utils": {:hex, :p1_utils, "1.0.11", "a471f80644d4b464fa67572affddda7e95df5d4b099624b8907f5726e8a1769c", [: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.5", "794738b6d07b6d36ec6d42492cb9d629bad9cf3761617b8b8d728e765db19840", [:rebar3], [], "hexpm"},
|
||||
"stringprep": {:hex, :stringprep, "1.0.10", "552d784eb60652220fce9131f8bb0ebc62fdffd6482c4f08f2e7d61300227c28", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"stun": {:hex, :stun, "1.0.20", "6b156fa11606bebb6086d02cb2f6532c84effb59c95ba93d0e2d8e2510970253", [:rebar3], [{:fast_tls, "1.0.20", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"xmpp": {:hex, :xmpp, "1.1.19", "ca0a89c567e972d119204b1296ffe58ad5d3237738950ae2c61043fbaf5e150e", [:rebar3], [{:fast_xml, "1.1.28", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.10", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm"}}
|
||||
"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"},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
-module(configure_deps).
|
||||
-export(['configure-deps'/2]).
|
||||
|
||||
'configure-deps'(Config, Vals) ->
|
||||
{ok, Config}.
|
||||
@@ -8,7 +8,7 @@ override_opts(override, Config, Opts) ->
|
||||
override_opts(add, Config, Opts) ->
|
||||
lists:foldl(fun({Opt, Value}, Conf) ->
|
||||
V = rebar_config:get_local(Conf, Opt, []),
|
||||
rebar_config:set(Conf, Opt, [Value | V])
|
||||
rebar_config:set(Conf, Opt, V ++ Value)
|
||||
end, Config, Opts).
|
||||
|
||||
preprocess(Config, _Dirs) ->
|
||||
|
||||
+22
-22
@@ -20,33 +20,31 @@
|
||||
|
||||
{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.10"}}},
|
||||
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.12"}}},
|
||||
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.20"}}},
|
||||
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.10"}}},
|
||||
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.28"}}},
|
||||
{xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.1.19"}}},
|
||||
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.12"}}},
|
||||
{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"}}},
|
||||
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
|
||||
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.2"}}},
|
||||
{luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "v0.2"}}},
|
||||
{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"}}},
|
||||
{fs, ".*", {git, "https://github.com/synrc/fs", "bed9467"}},
|
||||
{if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.20"}}}},
|
||||
{if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.21"}}}},
|
||||
{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"}}}},
|
||||
{if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql",
|
||||
{tag, "1.0.4"}}}},
|
||||
{tag, "1.0.5"}}}},
|
||||
{if_var_true, pgsql, {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql",
|
||||
{tag, "1.1.4"}}}},
|
||||
{if_var_true, sqlite, {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3",
|
||||
{tag, "1.1.5"}}}},
|
||||
{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",
|
||||
{tag, "1.0.3"}}}},
|
||||
{tag, "1.0.4"}}}},
|
||||
{if_var_true, zlib, {ezlib, ".*", {git, "https://github.com/processone/ezlib",
|
||||
{tag, "1.0.3"}}}},
|
||||
{tag, "1.0.4"}}}},
|
||||
{if_var_true, riak, {riakc, ".*", {git, "https://github.com/processone/riak-erlang-client",
|
||||
{tag, {if_version_above, "19", "develop", "2.5.3"}}}}},
|
||||
{if_var_true, graphics, {eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.2"}}}},
|
||||
%% Elixir support, needed to run tests
|
||||
{if_var_true, elixir, {elixir, ".*", {git, "https://github.com/elixir-lang/elixir",
|
||||
{tag, {if_version_above, "17", "v1.4.4", "v1.1.1"}}}}},
|
||||
@@ -54,7 +52,9 @@
|
||||
{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.6"}}}},
|
||||
{tag, "1.0.7"}}}},
|
||||
{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",
|
||||
@@ -93,7 +93,6 @@
|
||||
{if_var_true, debug, debug_info},
|
||||
{if_var_true, sip, {d, 'SIP'}},
|
||||
{if_var_true, stun, {d, 'STUN'}},
|
||||
{if_var_true, graphics, {d, 'GRAPHICS'}},
|
||||
{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'}},
|
||||
@@ -112,7 +111,7 @@
|
||||
|
||||
{if_rebar3, {plugins, [rebar3_hex, {provider_asn1, "0.2.0"}]}}.
|
||||
{if_not_rebar3, {plugins, [
|
||||
deps_erl_opts, override_deps_versions, override_opts,
|
||||
deps_erl_opts, override_deps_versions, override_opts, configure_deps,
|
||||
{if_var_true, elixir, rebar_elixir_compiler},
|
||||
{if_var_true, elixir, rebar_exunit}
|
||||
]}}.
|
||||
@@ -154,15 +153,16 @@
|
||||
|
||||
{if_version_above, "17", {cover_enabled, true}}.
|
||||
{cover_export_enabled, true}.
|
||||
{recursive_cmds, ['configure-deps']}.
|
||||
|
||||
{post_hook_configure, [{"fast_tls", []},
|
||||
{"stringprep", []},
|
||||
{"fast_yaml", []},
|
||||
{"eimp", []},
|
||||
{if_var_true, sip, {"esip", []}},
|
||||
{"fast_xml", [{if_var_true, full_xml, "--enable-full-xml"}]},
|
||||
{if_var_true, pam, {"epam", []}},
|
||||
{if_var_true, zlib, {"ezlib", []}},
|
||||
{if_var_true, graphics, {"eimp", []}},
|
||||
{if_var_true, iconv, {"iconv", []}}]}.
|
||||
|
||||
{port_env, [{"CFLAGS", "-g -O2 -Wall"}]}.
|
||||
|
||||
+68
-49
@@ -17,7 +17,6 @@
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
Vars = case file:consult(filename:join([filename:dirname(SCRIPT),"vars.config"])) of
|
||||
{ok, Terms} ->
|
||||
Terms;
|
||||
@@ -30,7 +29,7 @@ Vars = case file:consult(filename:join([filename:dirname(SCRIPT),"vars.config"])
|
||||
{ldflags, LDFlags} = lists:keyfind(ldflags, 1, Vars),
|
||||
{system_deps, SystemDeps} = lists:keyfind(system_deps, 1, Vars),
|
||||
|
||||
GetCfg0 = fun(F, Cfg, [Key | Tail], Default) ->
|
||||
GetCfg = fun GetCfg(Cfg, [Key | Tail], Default) ->
|
||||
Val = case lists:keyfind(Key, 1, Cfg) of
|
||||
{Key, V1} -> V1;
|
||||
false -> Default
|
||||
@@ -39,10 +38,10 @@ GetCfg0 = fun(F, Cfg, [Key | Tail], Default) ->
|
||||
[] ->
|
||||
Val;
|
||||
_ ->
|
||||
F(F, Val, Tail, Default)
|
||||
GetCfg(Val, Tail, Default)
|
||||
end
|
||||
end,
|
||||
ModCfg0 = fun(F, Cfg, [Key | Tail], Op, Default) ->
|
||||
ModCfg = fun ModCfg(Cfg, [Key | Tail], Op, Default) ->
|
||||
{OldVal, PartCfg} = case lists:keytake(Key, 1, Cfg) of
|
||||
{value, {_, V1}, V2} -> {V1, V2};
|
||||
false -> {if Tail == [] -> Default; true -> [] end, Cfg}
|
||||
@@ -51,26 +50,26 @@ ModCfg0 = fun(F, Cfg, [Key | Tail], Op, Default) ->
|
||||
[] ->
|
||||
[{Key, Op(OldVal)} | PartCfg];
|
||||
_ ->
|
||||
[{Key, F(F, OldVal, Tail, Op, Default)} | PartCfg]
|
||||
[{Key, ModCfg(OldVal, Tail, Op, Default)} | PartCfg]
|
||||
end
|
||||
end,
|
||||
|
||||
FilterConfig = fun(F, Cfg, [{Path, true, ModFun, Default} | Tail]) ->
|
||||
F(F, ModCfg0(ModCfg0, Cfg, Path, ModFun, Default), Tail);
|
||||
(F, Cfg, [{Path, SourcePath, true, ModFun, Default, SourceDefault} | Tail]) ->
|
||||
SourceVal = GetCfg0(GetCfg0, Cfg, SourcePath, SourceDefault),
|
||||
FilterConfig = fun FilterConfig(Cfg, [{Path, true, ModFun, Default} | Tail]) ->
|
||||
FilterConfig(ModCfg(Cfg, Path, ModFun, Default), Tail);
|
||||
FilterConfig(Cfg, [{Path, SourcePath, true, ModFun, Default, SourceDefault} | Tail]) ->
|
||||
SourceVal = GetCfg(Cfg, SourcePath, SourceDefault),
|
||||
ModFun2 = fun(V) -> ModFun(V, SourceVal) end,
|
||||
F(F, ModCfg0(ModCfg0, Cfg, Path, ModFun2, Default), Tail);
|
||||
(F, Cfg, [_ | Tail]) ->
|
||||
F(F, Cfg, Tail);
|
||||
(_, Cfg, []) ->
|
||||
FilterConfig(ModCfg(Cfg, Path, ModFun2, Default), Tail);
|
||||
FilterConfig(Cfg, [_ | Tail]) ->
|
||||
FilterConfig(Cfg, Tail);
|
||||
FilterConfig(Cfg, []) ->
|
||||
Cfg
|
||||
end,
|
||||
|
||||
IsRebar3 = case application:get_key(rebar, vsn) of
|
||||
{ok, VSN} ->
|
||||
[VSN1 | _] = string:tokens(VSN, "-"),
|
||||
[Maj, _Min, _Patch] = string:tokens(VSN1, "."),
|
||||
[Maj|_] = string:tokens(VSN1, "."),
|
||||
(list_to_integer(Maj) >= 3);
|
||||
undefined ->
|
||||
lists:keymember(mix, 1, application:loaded_applications())
|
||||
@@ -79,15 +78,15 @@ IsRebar3 = case application:get_key(rebar, vsn) of
|
||||
SysVer = erlang:system_info(otp_release),
|
||||
|
||||
ProcessSingleVar = fun(F, Var, Tail) ->
|
||||
case F(F, [Var], []) of
|
||||
case F([Var], []) of
|
||||
[] -> Tail;
|
||||
[Val] -> [Val | Tail]
|
||||
end
|
||||
end,
|
||||
|
||||
ProcessVars = fun(_F, [], Acc) ->
|
||||
ProcessVars = fun F([], Acc) ->
|
||||
lists:reverse(Acc);
|
||||
(F, [{Type, Ver, Value} | Tail], Acc) when
|
||||
F([{Type, Ver, Value} | Tail], Acc) when
|
||||
Type == if_version_above orelse
|
||||
Type == if_version_below ->
|
||||
SysVer = erlang:system_info(otp_release),
|
||||
@@ -97,11 +96,11 @@ ProcessVars = fun(_F, [], Acc) ->
|
||||
SysVer < Ver
|
||||
end,
|
||||
if Include ->
|
||||
F(F, Tail, ProcessSingleVar(F, Value, Acc));
|
||||
F(Tail, ProcessSingleVar(F, Value, Acc));
|
||||
true ->
|
||||
F(F, Tail, Acc)
|
||||
F(Tail, Acc)
|
||||
end;
|
||||
(F, [{Type, Ver, Value, ElseValue} | Tail], Acc) when
|
||||
F([{Type, Ver, Value, ElseValue} | Tail], Acc) when
|
||||
Type == if_version_above orelse
|
||||
Type == if_version_below ->
|
||||
Include = if Type == if_version_above ->
|
||||
@@ -110,53 +109,53 @@ ProcessVars = fun(_F, [], Acc) ->
|
||||
SysVer < Ver
|
||||
end,
|
||||
if Include ->
|
||||
F(F, Tail, ProcessSingleVar(F, Value, Acc));
|
||||
F(Tail, ProcessSingleVar(F, Value, Acc));
|
||||
true ->
|
||||
F(F, Tail, ProcessSingleVar(F, ElseValue, Acc))
|
||||
F(Tail, ProcessSingleVar(F, ElseValue, Acc))
|
||||
end;
|
||||
(F, [{Type, Var, Value} | Tail], Acc) when
|
||||
F([{Type, Var, Value} | Tail], Acc) when
|
||||
Type == if_var_true orelse
|
||||
Type == if_var_false ->
|
||||
Flag = Type == if_var_true,
|
||||
case proplists:get_bool(Var, Vars) of
|
||||
V when V == Flag ->
|
||||
F(F, Tail, ProcessSingleVar(F, Value, Acc));
|
||||
F(Tail, ProcessSingleVar(F, Value, Acc));
|
||||
_ ->
|
||||
F(F, Tail, Acc)
|
||||
F(Tail, Acc)
|
||||
end;
|
||||
(F, [{Type, Value} | Tail], Acc) when
|
||||
F([{Type, Value} | Tail], Acc) when
|
||||
Type == if_rebar3 orelse
|
||||
Type == if_not_rebar3 ->
|
||||
Flag = Type == if_rebar3,
|
||||
case IsRebar3 == Flag of
|
||||
true ->
|
||||
F(F, Tail, ProcessSingleVar(F, Value, Acc));
|
||||
F(Tail, ProcessSingleVar(F, Value, Acc));
|
||||
_ ->
|
||||
F(F, Tail, Acc)
|
||||
F(Tail, Acc)
|
||||
end;
|
||||
(F, [{Type, Var, Match, Value} | Tail], Acc) when
|
||||
F([{Type, Var, Match, Value} | Tail], Acc) when
|
||||
Type == if_var_match orelse
|
||||
Type == if_var_no_match ->
|
||||
case proplists:get_value(Var, Vars) of
|
||||
V when V == Match ->
|
||||
F(F, Tail, ProcessSingleVar(F, Value, Acc));
|
||||
F(Tail, ProcessSingleVar(F, Value, Acc));
|
||||
_ ->
|
||||
F(F, Tail, Acc)
|
||||
F(Tail, Acc)
|
||||
end;
|
||||
(F, [{if_have_fun, MFA, Value} | Tail], Acc) ->
|
||||
F([{if_have_fun, MFA, Value} | Tail], Acc) ->
|
||||
{Mod, Fun, Arity} = MFA,
|
||||
code:ensure_loaded(Mod),
|
||||
case erlang:function_exported(Mod, Fun, Arity) of
|
||||
true ->
|
||||
F(F, Tail, ProcessSingleVar(F, Value, Acc));
|
||||
F(Tail, ProcessSingleVar(F, Value, Acc));
|
||||
false ->
|
||||
F(F, Tail, Acc)
|
||||
F(Tail, Acc)
|
||||
end;
|
||||
(F, [Other1 | Tail1], Acc) ->
|
||||
F(F, Tail1, [F(F, Other1, []) | Acc]);
|
||||
(F, Val, Acc) when is_tuple(Val) ->
|
||||
list_to_tuple(F(F, tuple_to_list(Val), Acc));
|
||||
(_F, Other2, _Acc) ->
|
||||
F([Other1 | Tail1], Acc) ->
|
||||
F(Tail1, [F(Other1, []) | Acc]);
|
||||
F(Val, Acc) when is_tuple(Val) ->
|
||||
list_to_tuple(F(tuple_to_list(Val), Acc));
|
||||
F(Other2, _Acc) ->
|
||||
Other2
|
||||
end,
|
||||
|
||||
@@ -205,15 +204,35 @@ fun(DepsList) ->
|
||||
end, DepsList)
|
||||
end,
|
||||
|
||||
DepAlts = fun("esip") -> ["esip", "p1_sip"];
|
||||
("xmpp") -> ["xmpp", "p1_xmpp"];
|
||||
("fast_xml") -> ["fast_xml", "p1_xml"];
|
||||
(Val) -> [Val]
|
||||
end,
|
||||
|
||||
LibDirInt = fun F([Dep|Rest], Suffix) ->
|
||||
case code:lib_dir(Dep) of
|
||||
{error, _} ->
|
||||
F(Rest, Suffix);
|
||||
V -> V ++ Suffix
|
||||
end;
|
||||
F([], _) ->
|
||||
error
|
||||
end,
|
||||
|
||||
LibDir = fun(Name, Suffix) ->
|
||||
LibDirInt(DepAlts(Name), Suffix)
|
||||
end,
|
||||
|
||||
GlobalDepsFilter =
|
||||
fun(Deps) ->
|
||||
DepNames = lists:map(fun({DepName, _, _}) -> DepName;
|
||||
({DepName, _}) -> DepName
|
||||
end, Deps),
|
||||
lists:filtermap(fun(Dep) ->
|
||||
case code:lib_dir(Dep) of
|
||||
{error, _} ->
|
||||
{true, "Unable to locate dep '" ++ atom_to_list(Dep) ++ "' in system deps."};
|
||||
case LibDir(atom_to_list(Dep), "") of
|
||||
error ->
|
||||
exit("Unable to locate dep '" ++ atom_to_list(Dep) ++ "' in system deps.");
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
@@ -233,9 +252,9 @@ ResolveDepPath = case {SystemDeps, IsRebar3} of
|
||||
{true, _} ->
|
||||
fun("deps/" ++ Rest) ->
|
||||
Slash = string:str(Rest, "/"),
|
||||
case code:lib_dir(string:sub_string(Rest, 1, Slash -1)) of
|
||||
{error, _} -> Rest;
|
||||
V -> V ++ string:sub_string(Rest, Slash)
|
||||
case LibDir(string:sub_string(Rest, 1, Slash -1), string:sub_string(Rest, Slash)) of
|
||||
error -> Rest;
|
||||
V -> V
|
||||
end;
|
||||
(Path) ->
|
||||
Path
|
||||
@@ -258,7 +277,7 @@ ResolveDepPath = case {SystemDeps, IsRebar3} of
|
||||
CtParams = fun(CompileOpts) ->
|
||||
["-ct_hooks cth_surefire ",
|
||||
lists:map(fun({i, IncPath}) ->
|
||||
[" -include ", filename:join([Cwd, ResolveDepPath(IncPath)])]
|
||||
[" -include ", filename:absname(ResolveDepPath(IncPath), Cwd)]
|
||||
end, CompileOpts),
|
||||
TestConfig]
|
||||
end,
|
||||
@@ -279,8 +298,8 @@ GenDepsConfigure =
|
||||
fun(Hooks) ->
|
||||
lists:map(fun({Pkg, Flags}) ->
|
||||
DepPath = ResolveDepPath("deps/" ++ Pkg ++ "/"),
|
||||
{add, list_to_atom(Pkg), [{pre_hooks, {'compile',
|
||||
lists:flatten(GenDepConfigureLine(DepPath, Flags))}}]}
|
||||
Line = lists:flatten(GenDepConfigureLine(DepPath, Flags)),
|
||||
{add, list_to_atom(Pkg), [{pre_hooks, [{'compile', Line}, {'configure-deps', Line}]}]}
|
||||
end, Hooks)
|
||||
end,
|
||||
|
||||
@@ -353,7 +372,7 @@ Rules = [
|
||||
],
|
||||
|
||||
Config = [{plugin_dir, filename:join([filename:dirname(SCRIPT),"plugins"])}]++
|
||||
FilterConfig(FilterConfig, ProcessVars(ProcessVars, CONFIG, []), Rules),
|
||||
FilterConfig(ProcessVars(CONFIG, []), Rules),
|
||||
|
||||
%io:format("ejabberd configuration:~n ~p~n", [Config]),
|
||||
|
||||
|
||||
+4
-4
@@ -109,9 +109,9 @@ CREATE TABLE archive (
|
||||
);
|
||||
|
||||
CREATE INDEX i_archive_sh_username_timestamp ON archive (server_host, username, timestamp);
|
||||
CREATE INDEX i_archive_sh_username_peer ON archive (server_host, username, peer);
|
||||
CREATE INDEX i_archive_sh_username_bare_peer ON archive (server_host, username, bare_peer);
|
||||
CREATE INDEX i_archive_sh_timestamp ON archive (server_host, timestamp);
|
||||
CREATE INDEX i_archive_sh_peer ON archive (server_host, peer);
|
||||
CREATE INDEX i_archive_sh_bare_peer ON archive (server_host, bare_peer);
|
||||
|
||||
CREATE TABLE archive_prefs (
|
||||
username text NOT NULL,
|
||||
@@ -259,8 +259,8 @@ CREATE TABLE pubsub_item (
|
||||
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
|
||||
itemid text NOT NULL,
|
||||
publisher text NOT NULL,
|
||||
creation text NOT NULL,
|
||||
modification text NOT NULL,
|
||||
creation varchar(32) NOT NULL,
|
||||
modification varchar(32) NOT NULL,
|
||||
payload text NOT NULL DEFAULT ''
|
||||
);
|
||||
CREATE INDEX i_pubsub_item_itemid ON pubsub_item (itemid);
|
||||
|
||||
+4
-4
@@ -98,9 +98,9 @@ CREATE TABLE archive (
|
||||
);
|
||||
|
||||
CREATE INDEX i_username_timestamp ON archive(username, timestamp);
|
||||
CREATE INDEX i_archive_username_peer ON archive (username, peer);
|
||||
CREATE INDEX i_archive_username_bare_peer ON archive (username, bare_peer);
|
||||
CREATE INDEX i_timestamp ON archive(timestamp);
|
||||
CREATE INDEX i_peer ON archive(peer);
|
||||
CREATE INDEX i_bare_peer ON archive(bare_peer);
|
||||
|
||||
CREATE TABLE archive_prefs (
|
||||
username text NOT NULL PRIMARY KEY,
|
||||
@@ -236,8 +236,8 @@ CREATE TABLE pubsub_item (
|
||||
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
|
||||
itemid text NOT NULL,
|
||||
publisher text NOT NULL,
|
||||
creation text NOT NULL,
|
||||
modification text NOT NULL,
|
||||
creation varchar(32) NOT NULL,
|
||||
modification varchar(32) NOT NULL,
|
||||
payload text NOT NULL DEFAULT ''
|
||||
);
|
||||
CREATE INDEX i_pubsub_item_itemid ON pubsub_item (itemid);
|
||||
|
||||
+8
-8
@@ -41,15 +41,15 @@ CREATE TABLE [dbo].[archive] (
|
||||
CREATE INDEX [archive_username_timestamp] ON [archive] (username, timestamp)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE INDEX [archive_username_peer] ON [archive] (username, peer)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE INDEX [archive_username_bare_peer] ON [archive] (username, bare_peer)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE INDEX [archive_timestamp] ON [archive] (timestamp)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE INDEX [archive_peer] ON [archive] (peer)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE INDEX [archive_bare_peer] ON [archive] (bare_peer)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE TABLE [dbo].[archive_prefs] (
|
||||
[username] [varchar] (250) NOT NULL,
|
||||
[def] [text] NOT NULL,
|
||||
@@ -220,8 +220,8 @@ CREATE TABLE [dbo].[pubsub_item] (
|
||||
[nodeid] [bigint] NULL,
|
||||
[itemid] [varchar] (255) NOT NULL,
|
||||
[publisher] [text] NOT NULL,
|
||||
[creation] [text] NOT NULL,
|
||||
[modification] [varchar] (255) NOT NULL,
|
||||
[creation] [varchar] (32) NOT NULL,
|
||||
[modification] [varchar] (32) NOT NULL,
|
||||
[payload] [text] NOT NULL DEFAULT ''
|
||||
) TEXTIMAGE_ON [PRIMARY];
|
||||
|
||||
|
||||
+7
-7
@@ -113,10 +113,10 @@ CREATE TABLE archive (
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE FULLTEXT INDEX i_text ON archive(txt);
|
||||
CREATE INDEX i_archive_sh_username_timestamp USING BTREE ON archive(server_host(191), username,timestamp);
|
||||
CREATE INDEX i_archive_sh_username_timestamp USING BTREE ON archive(server_host(191), username(191), timestamp);
|
||||
CREATE INDEX i_archive_sh_username_peer USING BTREE ON archive(server_host(191), username(191), peer(191));
|
||||
CREATE INDEX i_archive_sh_username_bare_peer USING BTREE ON archive(server_host(191), username(191), bare_peer(191));
|
||||
CREATE INDEX i_archive_sh_timestamp USING BTREE ON archive(server_host(191), timestamp);
|
||||
CREATE INDEX i_archive_sh_peer USING BTREE ON archive(server_host(191), peer);
|
||||
CREATE INDEX i_archive_sh_bare_peer USING BTREE ON archive(server_host(191), bare_peer);
|
||||
|
||||
CREATE TABLE archive_prefs (
|
||||
username varchar(191) NOT NULL,
|
||||
@@ -274,8 +274,8 @@ CREATE TABLE pubsub_item (
|
||||
nodeid bigint,
|
||||
itemid text NOT NULL,
|
||||
publisher text NOT NULL,
|
||||
creation text NOT NULL,
|
||||
modification text NOT NULL,
|
||||
creation varchar(32) NOT NULL,
|
||||
modification varchar(32) NOT NULL,
|
||||
payload text NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE INDEX i_pubsub_item_itemid ON pubsub_item(itemid(36));
|
||||
@@ -422,7 +422,7 @@ CREATE TABLE carboncopy (
|
||||
PRIMARY KEY (server_host(191), username(191), resource(191))
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE INDEX i_carboncopy_sh_user ON carboncopy (server_host, username(75));
|
||||
CREATE INDEX i_carboncopy_sh_user ON carboncopy (server_host(191), username(75));
|
||||
|
||||
CREATE TABLE proxy65 (
|
||||
sid text NOT NULL,
|
||||
@@ -446,4 +446,4 @@ CREATE TABLE push_session (
|
||||
PRIMARY KEY (server_host(191), username(191), timestamp)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_push_session_susn ON push_session (server_host, username(191), service(191), node(191));
|
||||
CREATE UNIQUE INDEX i_push_session_susn ON push_session (server_host(191), username(191), service(191), node(191));
|
||||
|
||||
+5
-5
@@ -102,10 +102,10 @@ CREATE TABLE archive (
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE FULLTEXT INDEX i_text ON archive(txt);
|
||||
CREATE INDEX i_username_timestamp USING BTREE ON archive(username,timestamp);
|
||||
CREATE INDEX i_username_timestamp USING BTREE ON archive(username(191), timestamp);
|
||||
CREATE INDEX i_username_peer USING BTREE ON archive(username(191), peer(191));
|
||||
CREATE INDEX i_username_bare_peer USING BTREE ON archive(username(191), bare_peer(191));
|
||||
CREATE INDEX i_timestamp USING BTREE ON archive(timestamp);
|
||||
CREATE INDEX i_peer USING BTREE ON archive(peer);
|
||||
CREATE INDEX i_bare_peer USING BTREE ON archive(bare_peer);
|
||||
|
||||
CREATE TABLE archive_prefs (
|
||||
username varchar(191) NOT NULL PRIMARY KEY,
|
||||
@@ -251,8 +251,8 @@ CREATE TABLE pubsub_item (
|
||||
nodeid bigint,
|
||||
itemid text NOT NULL,
|
||||
publisher text NOT NULL,
|
||||
creation text NOT NULL,
|
||||
modification text NOT NULL,
|
||||
creation varchar(32) NOT NULL,
|
||||
modification varchar(32) NOT NULL,
|
||||
payload text NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE INDEX i_pubsub_item_itemid ON pubsub_item(itemid(36));
|
||||
|
||||
+8
-9
@@ -61,15 +61,14 @@
|
||||
-- ALTER TABLE spool ALTER COLUMN server_host DROP DEFAULT;
|
||||
|
||||
-- ALTER TABLE archive ADD COLUMN server_host text NOT NULL DEFAULT '<HOST>';
|
||||
-- DROP INDEX i_username;
|
||||
-- DROP INDEX i_username_timestamp;
|
||||
-- DROP INDEX i_username_peer;
|
||||
-- DROP INDEX i_username_bare_peer;
|
||||
-- DROP INDEX i_timestamp;
|
||||
-- DROP INDEX i_peer;
|
||||
-- DROP INDEX i_bare_peer;
|
||||
-- CREATE INDEX i_archive_sh_username_timestamp ON archive USING btree (server_host, username, timestamp);
|
||||
-- CREATE INDEX i_archive_sh_username_peer ON archive USING btree (server_host, username, peer);
|
||||
-- CREATE INDEX i_archive_sh_username_bare_peer ON archive USING btree (server_host, username, bare_peer);
|
||||
-- CREATE INDEX i_archive_sh_timestamp ON archive USING btree (server_host, timestamp);
|
||||
-- CREATE INDEX i_archive_sh_peer ON archive USING btree (server_host, peer);
|
||||
-- CREATE INDEX i_archive_sh_bare_peer ON archive USING btree (server_host, bare_peer);
|
||||
-- ALTER TABLE archive ALTER COLUMN server_host DROP DEFAULT;
|
||||
|
||||
-- ALTER TABLE archive_prefs ADD COLUMN server_host text NOT NULL DEFAULT '<HOST>';
|
||||
@@ -265,9 +264,9 @@ CREATE TABLE archive (
|
||||
);
|
||||
|
||||
CREATE INDEX i_archive_sh_username_timestamp ON archive USING btree (server_host, username, timestamp);
|
||||
CREATE INDEX i_archive_sh_username_peer ON archive USING btree (server_host, username, peer);
|
||||
CREATE INDEX i_archive_sh_username_bare_peer ON archive USING btree (server_host, username, bare_peer);
|
||||
CREATE INDEX i_archive_sh_timestamp ON archive USING btree (server_host, timestamp);
|
||||
CREATE INDEX i_archive_sh_peer ON archive USING btree (server_host, peer);
|
||||
CREATE INDEX i_archive_sh_bare_peer ON archive USING btree (server_host, bare_peer);
|
||||
|
||||
CREATE TABLE archive_prefs (
|
||||
username text NOT NULL,
|
||||
@@ -429,8 +428,8 @@ CREATE TABLE pubsub_item (
|
||||
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
|
||||
itemid text NOT NULL,
|
||||
publisher text NOT NULL,
|
||||
creation text NOT NULL,
|
||||
modification text NOT NULL,
|
||||
creation varchar(32) NOT NULL,
|
||||
modification varchar(32) NOT NULL,
|
||||
payload text NOT NULL DEFAULT ''
|
||||
);
|
||||
CREATE INDEX i_pubsub_item_itemid ON pubsub_item USING btree (itemid);
|
||||
|
||||
+4
-4
@@ -102,9 +102,9 @@ CREATE TABLE archive (
|
||||
);
|
||||
|
||||
CREATE INDEX i_username_timestamp ON archive USING btree (username, timestamp);
|
||||
CREATE INDEX i_username_peer ON archive USING btree (username, peer);
|
||||
CREATE INDEX i_username_bare_peer ON archive USING btree (username, bare_peer);
|
||||
CREATE INDEX i_timestamp ON archive USING btree (timestamp);
|
||||
CREATE INDEX i_peer ON archive USING btree (peer);
|
||||
CREATE INDEX i_bare_peer ON archive USING btree (bare_peer);
|
||||
|
||||
CREATE TABLE archive_prefs (
|
||||
username text NOT NULL PRIMARY KEY,
|
||||
@@ -254,8 +254,8 @@ CREATE TABLE pubsub_item (
|
||||
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
|
||||
itemid text NOT NULL,
|
||||
publisher text NOT NULL,
|
||||
creation text NOT NULL,
|
||||
modification text NOT NULL,
|
||||
creation varchar(32) NOT NULL,
|
||||
modification varchar(32) NOT NULL,
|
||||
payload text NOT NULL DEFAULT ''
|
||||
);
|
||||
CREATE INDEX i_pubsub_item_itemid ON pubsub_item USING btree (itemid);
|
||||
|
||||
@@ -100,7 +100,7 @@ solve_challenge1(Chal = #challenge{type = <<"http-01">>, token=Tkn}, Key) ->
|
||||
ets_put_key_authorization(Tkn, KeyAuthz),
|
||||
{ok, Chal#challenge.uri, KeyAuthz};
|
||||
solve_challenge1(Challenge, _Key) ->
|
||||
?ERROR_MSG("Unkown Challenge Type: ~p", [Challenge]),
|
||||
?ERROR_MSG("Unknown Challenge Type: ~p", [Challenge]),
|
||||
{error, unknown_challenge}.
|
||||
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@
|
||||
|
||||
start(_Opts) ->
|
||||
Fqdn = get_local_fqdn(),
|
||||
?INFO_MSG("FQDN used to check DIGEST-MD5 SASL authentication: ~s",
|
||||
[Fqdn]),
|
||||
?DEBUG("FQDN used to check DIGEST-MD5 SASL authentication: ~s",
|
||||
[Fqdn]),
|
||||
cyrsasl:register_mechanism(<<"DIGEST-MD5">>, ?MODULE,
|
||||
digest).
|
||||
|
||||
|
||||
@@ -81,15 +81,8 @@ prepare(ClientIn) ->
|
||||
_ -> 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(S) ->
|
||||
binary:split(S, <<0>>, [global]).
|
||||
|
||||
parse_domain(S) -> parse_domain1(binary_to_list(S), "", []).
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
{modules, []},
|
||||
{registered, []},
|
||||
{applications, [kernel, stdlib]},
|
||||
{env, []},
|
||||
{env, [{enabled_backends, [@enabled_backends@]}]},
|
||||
{mod, {ejabberd_app, []}}]}.
|
||||
|
||||
|
||||
|
||||
+8
-3
@@ -25,6 +25,7 @@
|
||||
|
||||
-module(ejabberd).
|
||||
-author('alexey@process-one.net').
|
||||
-compile({no_auto_import, [{halt, 0}]}).
|
||||
|
||||
-protocol({xep, 4, '2.9'}).
|
||||
-protocol({xep, 86, '1.0'}).
|
||||
@@ -36,7 +37,7 @@
|
||||
-protocol({xep, 243, '1.0'}).
|
||||
-protocol({xep, 270, '1.0'}).
|
||||
|
||||
-export([start/0, stop/0, start_app/1, start_app/2,
|
||||
-export([start/0, stop/0, halt/0, start_app/1, start_app/2,
|
||||
get_pid_file/0, check_app/1, module_name/1]).
|
||||
|
||||
-include("logger.hrl").
|
||||
@@ -49,6 +50,11 @@ stop() ->
|
||||
application:stop(ejabberd).
|
||||
%%ejabberd_cover:stop().
|
||||
|
||||
halt() ->
|
||||
application:stop(lager),
|
||||
application:stop(sasl),
|
||||
erlang:halt(1, [{flush, true}]).
|
||||
|
||||
%% @spec () -> false | string()
|
||||
get_pid_file() ->
|
||||
case os:getenv("EJABBERD_PID_PATH") of
|
||||
@@ -131,8 +137,7 @@ exit_or_halt(Reason, StartFlag) ->
|
||||
?CRITICAL_MSG(Reason, []),
|
||||
if StartFlag ->
|
||||
%% Wait for the critical message is written in the console/log
|
||||
timer:sleep(1000),
|
||||
halt(string:substr(lists:flatten(Reason), 1, 199));
|
||||
halt();
|
||||
true ->
|
||||
erlang:error(application_start_failed)
|
||||
end.
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
-include("logger.hrl").
|
||||
|
||||
-behaviour(gen_server).
|
||||
-behavior(ejabberd_config).
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
%% API
|
||||
-export([start_link/0,
|
||||
@@ -393,7 +393,7 @@ parse_who(Name, Defs, ParseOauth) when is_list(Defs) ->
|
||||
{oauth, lists:foldl(fun({scope, S}, A) -> S ++ A end, [], Scopes), Rest}
|
||||
end;
|
||||
scope ->
|
||||
report_error(<<"Oauth rule can't be embeded inside other oauth rule in 'who' section for api_permission '~s'">>,
|
||||
report_error(<<"Oauth rule can't be embedded inside other oauth rule in 'who' section for api_permission '~s'">>,
|
||||
[Name])
|
||||
end;
|
||||
({scope, ScopeList}) ->
|
||||
@@ -492,6 +492,8 @@ parse_single_what(Binary) when is_binary(Binary) ->
|
||||
_ ->
|
||||
{error, <<"Invalid value">>}
|
||||
end;
|
||||
parse_single_what(Atom) when is_atom(Atom) ->
|
||||
parse_single_what(atom_to_binary(Atom, latin1));
|
||||
parse_single_what(_) ->
|
||||
{error, <<"Invalid value">>}.
|
||||
|
||||
@@ -502,7 +504,9 @@ is_valid_command_name(Val) ->
|
||||
|
||||
is_valid_command_name2(<<>>) ->
|
||||
true;
|
||||
is_valid_command_name2(<<K:8, Rest/binary>>) when K >= $a andalso K =< $z orelse K == $_ ->
|
||||
is_valid_command_name2(<<K:8, Rest/binary>>) when (K >= $a andalso K =< $z)
|
||||
orelse (K >= $0 andalso K =< $9)
|
||||
orelse K == $_ ->
|
||||
is_valid_command_name2(Rest);
|
||||
is_valid_command_name2(_) ->
|
||||
false.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
-module (ejabberd_acme).
|
||||
-behaviour(gen_server).
|
||||
-behavior(ejabberd_config).
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
%% ejabberdctl commands
|
||||
-export([get_certificates/1,
|
||||
|
||||
+2
-11
@@ -62,8 +62,7 @@ start(normal, _Args) ->
|
||||
{ok, SupPid};
|
||||
Err ->
|
||||
?CRITICAL_MSG("Failed to start ejabberd application: ~p", [Err]),
|
||||
timer:sleep(1000),
|
||||
halt("Refer to ejabberd log files to diagnose the problem")
|
||||
ejabberd:halt()
|
||||
end;
|
||||
start(_, _) ->
|
||||
{error, badarg}.
|
||||
@@ -150,7 +149,7 @@ start_apps() ->
|
||||
ejabberd:start_app(fast_tls),
|
||||
ejabberd:start_app(xmpp),
|
||||
ejabberd:start_app(cache_tab),
|
||||
start_eimp().
|
||||
ejabberd:start_app(eimp).
|
||||
|
||||
setup_if_elixir_conf_used() ->
|
||||
case ejabberd_config:is_using_elixir_config() of
|
||||
@@ -174,11 +173,3 @@ start_elixir_application() ->
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
-ifdef(GRAPHICS).
|
||||
start_eimp() ->
|
||||
ejabberd:start_app(eimp).
|
||||
-else.
|
||||
start_eimp() ->
|
||||
ok.
|
||||
-endif.
|
||||
|
||||
+19
-3
@@ -526,7 +526,10 @@ db_get_password(User, Server, Mod) ->
|
||||
UseCache = use_cache(Mod, Server),
|
||||
case erlang:function_exported(Mod, get_password, 2) of
|
||||
false when UseCache ->
|
||||
ets_cache:lookup(?AUTH_CACHE, {User, Server});
|
||||
case ets_cache:lookup(?AUTH_CACHE, {User, Server}) of
|
||||
{ok, exists} -> error;
|
||||
Other -> Other
|
||||
end;
|
||||
false ->
|
||||
error;
|
||||
true when UseCache ->
|
||||
@@ -544,7 +547,20 @@ db_user_exists(User, Server, Mod) ->
|
||||
error ->
|
||||
case Mod:store_type(Server) of
|
||||
external ->
|
||||
Mod:user_exists(User, Server);
|
||||
case ets_cache:lookup(
|
||||
?AUTH_CACHE, {User, Server},
|
||||
fun() ->
|
||||
case Mod:user_exists(User, Server) of
|
||||
true -> {ok, exists};
|
||||
false -> error;
|
||||
{error, _} = Err -> Err
|
||||
end
|
||||
end) of
|
||||
{ok, _} ->
|
||||
true;
|
||||
error ->
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
@@ -568,7 +584,7 @@ db_check_password(User, AuthzId, Server, ProvidedPassword,
|
||||
false ->
|
||||
error
|
||||
end
|
||||
end, cache_nodes(Mod, Server)) of
|
||||
end) of
|
||||
{ok, _} ->
|
||||
true;
|
||||
error ->
|
||||
|
||||
+8
-15
@@ -71,16 +71,12 @@
|
||||
-define(NS_HTTP_BIND,
|
||||
<<"http://jabber.org/protocol/httpbind">>).
|
||||
|
||||
-define(DEFAULT_MAXPAUSE, 120).
|
||||
|
||||
-define(DEFAULT_WAIT, 300).
|
||||
|
||||
-define(DEFAULT_HOLD, 1).
|
||||
|
||||
-define(DEFAULT_POLLING, 2).
|
||||
|
||||
-define(DEFAULT_INACTIVITY, 30).
|
||||
|
||||
-define(MAX_SHAPED_REQUESTS_QUEUE_LEN, 1000).
|
||||
|
||||
-define(SEND_TIMEOUT, 15000).
|
||||
@@ -102,7 +98,7 @@
|
||||
inactivity_timer :: reference() | undefined,
|
||||
wait_timer :: reference() | undefined,
|
||||
wait_timeout = ?DEFAULT_WAIT :: timeout(),
|
||||
inactivity_timeout = ?DEFAULT_INACTIVITY :: timeout(),
|
||||
inactivity_timeout :: timeout(),
|
||||
prev_rid = 0 :: non_neg_integer(),
|
||||
prev_key = <<"">> :: binary(),
|
||||
prev_poll :: erlang:timestamp() | undefined,
|
||||
@@ -294,7 +290,7 @@ init([#body{attrs = Attrs}, IP, SID]) ->
|
||||
XMPPVer = get_attr('xmpp:version', Attrs),
|
||||
XMPPDomain = get_attr(to, Attrs),
|
||||
{InBuf, Opts} = case gen_mod:get_module_opt(
|
||||
XMPPDomain, mod_bosh, prebind, false) of
|
||||
XMPPDomain, mod_bosh, prebind) of
|
||||
true ->
|
||||
JID = make_random_jid(XMPPDomain),
|
||||
{buf_new(XMPPDomain), [{jid, JID} | Opts2]};
|
||||
@@ -306,10 +302,8 @@ init([#body{attrs = Attrs}, IP, SID]) ->
|
||||
xmpp_socket:start(ejabberd_c2s, ?MODULE, Socket,
|
||||
[{receiver, self()}|Opts]),
|
||||
Inactivity = gen_mod:get_module_opt(XMPPDomain,
|
||||
mod_bosh, max_inactivity,
|
||||
?DEFAULT_INACTIVITY),
|
||||
MaxConcat = gen_mod:get_module_opt(XMPPDomain, mod_bosh, max_concat,
|
||||
unlimited),
|
||||
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,
|
||||
@@ -354,8 +348,7 @@ wait_for_session(#body{attrs = Attrs} = Req, From,
|
||||
true -> {undefined, []}
|
||||
end,
|
||||
MaxPause = gen_mod:get_module_opt(State#state.host,
|
||||
mod_bosh, max_pause,
|
||||
?DEFAULT_MAXPAUSE),
|
||||
mod_bosh, max_pause),
|
||||
Resp = #body{attrs =
|
||||
[{sid, State#state.sid}, {wait, Wait},
|
||||
{ver, ?BOSH_VERSION}, {polling, ?DEFAULT_POLLING},
|
||||
@@ -746,9 +739,10 @@ bounce_receivers(State, Reason) ->
|
||||
State, Receivers ++ ShapedReceivers).
|
||||
|
||||
bounce_els_from_obuf(State) ->
|
||||
Opts = ejabberd_config:codec_options(State#state.host),
|
||||
p1_queue:foreach(
|
||||
fun({xmlstreamelement, El}) ->
|
||||
try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of
|
||||
try xmpp:decode(El, ?NS_CLIENT, Opts) of
|
||||
Pkt when ?is_stanza(Pkt) ->
|
||||
case {xmpp:get_from(Pkt), xmpp:get_to(Pkt)} of
|
||||
{#jid{}, #jid{}} ->
|
||||
@@ -1028,8 +1022,7 @@ buf_new(Host) ->
|
||||
|
||||
buf_new(Host, Limit) ->
|
||||
QueueType = gen_mod:get_module_opt(
|
||||
Host, mod_bosh, queue_type,
|
||||
ejabberd_config:default_queue_type(Host)),
|
||||
Host, mod_bosh, queue_type),
|
||||
p1_queue:new(QueueType, Limit).
|
||||
|
||||
buf_in(Xs, Buf) ->
|
||||
|
||||
+32
-23
@@ -109,7 +109,7 @@ set_presence(Ref, Pres) ->
|
||||
resend_presence(Pid) ->
|
||||
resend_presence(Pid, undefined).
|
||||
|
||||
-spec resend_presence(pid(), jid() | undefined) -> ok.
|
||||
-spec resend_presence(pid(), jid() | undefined) -> boolean().
|
||||
resend_presence(Pid, To) ->
|
||||
route(Pid, {resend_presence, To}).
|
||||
|
||||
@@ -418,8 +418,10 @@ handle_stream_start(StreamStart, #{lserver := LServer} = State) ->
|
||||
send(State#{lserver => ?MYNAME}, xmpp:serr_host_unknown());
|
||||
true ->
|
||||
State1 = change_shaper(State),
|
||||
Opts = ejabberd_config:codec_options(LServer),
|
||||
State2 = State1#{codec_options => Opts},
|
||||
ejabberd_hooks:run_fold(
|
||||
c2s_stream_started, LServer, State1, [StreamStart])
|
||||
c2s_stream_started, LServer, State2, [StreamStart])
|
||||
end.
|
||||
|
||||
handle_stream_end(Reason, #{lserver := LServer} = State) ->
|
||||
@@ -517,6 +519,7 @@ init([State, Opts]) ->
|
||||
TLSRequired = proplists:get_bool(starttls_required, Opts),
|
||||
TLSVerify = proplists:get_bool(tls_verify, Opts),
|
||||
Zlib = proplists:get_bool(zlib, Opts),
|
||||
Timeout = ejabberd_config:negotiation_timeout(),
|
||||
State1 = State#{tls_options => TLSOpts2,
|
||||
tls_required => TLSRequired,
|
||||
tls_enabled => TLSEnabled,
|
||||
@@ -528,7 +531,8 @@ init([State, Opts]) ->
|
||||
lserver => ?MYNAME,
|
||||
access => Access,
|
||||
shaper => Shaper},
|
||||
ejabberd_hooks:run_fold(c2s_init, {ok, State1}, [Opts]).
|
||||
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
|
||||
ejabberd_hooks:run_fold(c2s_init, {ok, State2}, [Opts]).
|
||||
|
||||
handle_call(get_presence, From, #{jid := JID} = State) ->
|
||||
Pres = case maps:get(pres_last, State, error) of
|
||||
@@ -643,12 +647,12 @@ route_probe_reply(_, _) ->
|
||||
ok.
|
||||
|
||||
-spec process_presence_out(state(), presence()) -> state().
|
||||
process_presence_out(#{user := User, server := Server, lserver := LServer,
|
||||
jid := JID, lang := Lang, pres_a := PresA} = State,
|
||||
process_presence_out(#{lserver := LServer, jid := JID,
|
||||
lang := Lang, pres_a := PresA} = State,
|
||||
#presence{from = From, to = To, type = Type} = Pres) ->
|
||||
if Type == subscribe; Type == subscribed;
|
||||
Type == unsubscribe; Type == unsubscribed ->
|
||||
Access = gen_mod:get_module_opt(LServer, mod_roster, access, all),
|
||||
Access = gen_mod:get_module_opt(LServer, mod_roster, access),
|
||||
MyBareJID = jid:remove_resource(JID),
|
||||
case acl:match_rule(LServer, Access, MyBareJID) of
|
||||
deny ->
|
||||
@@ -656,9 +660,7 @@ process_presence_out(#{user := User, server := Server, lserver := LServer,
|
||||
AccessErr = xmpp:err_forbidden(AccessErrTxt, Lang),
|
||||
send_error(State, Pres, AccessErr);
|
||||
allow ->
|
||||
ejabberd_hooks:run(roster_out_subscription,
|
||||
LServer,
|
||||
[User, Server, To, Type])
|
||||
ejabberd_hooks:run(roster_out_subscription, LServer, [Pres])
|
||||
end;
|
||||
true -> ok
|
||||
end,
|
||||
@@ -839,9 +841,9 @@ route_multiple(#{lserver := LServer}, JIDs, Pkt) ->
|
||||
ejabberd_router_multicast:route_multicast(From, LServer, JIDs, Pkt).
|
||||
|
||||
get_subscription(#jid{luser = LUser, lserver = LServer}, JID) ->
|
||||
{Subscription, _} = ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, LServer, {none, []},
|
||||
[LUser, LServer, JID]),
|
||||
{Subscription, _, _} = ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, LServer, {none, none, []},
|
||||
[LUser, LServer, JID]),
|
||||
Subscription.
|
||||
|
||||
-spec resource_conflict_action(binary(), binary(), binary()) ->
|
||||
@@ -997,6 +999,9 @@ opt_type(_) ->
|
||||
(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;
|
||||
@@ -1025,20 +1030,24 @@ listen_opt_type(max_stanza_size) ->
|
||||
end;
|
||||
listen_opt_type(max_fsm_queue) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
%% The following hack should be removed in future releases: it is intended
|
||||
%% for backward compatibility with ejabberd 17.01 or older
|
||||
listen_opt_type(stream_management) ->
|
||||
?WARNING_MSG("listening option 'stream_management' is deprecated: "
|
||||
"use mod_stream_mgmt module", []),
|
||||
?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;
|
||||
listen_opt_type(O) ->
|
||||
case mod_stream_mgmt:mod_opt_type(O) of
|
||||
L when is_list(L) ->
|
||||
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];
|
||||
VFun ->
|
||||
?WARNING_MSG("listening option '~s' is deprecated: use '~s' "
|
||||
"option from mod_stream_mgmt module", [O, O]),
|
||||
VFun
|
||||
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)
|
||||
end.
|
||||
|
||||
+112
-39
@@ -41,7 +41,8 @@
|
||||
-export([create_captcha/6, build_captcha_html/2,
|
||||
check_captcha/2, process_reply/1, process/2,
|
||||
is_feature_available/0, create_captcha_x/5,
|
||||
opt_type/1]).
|
||||
opt_type/1, host_up/1, host_down/1,
|
||||
config_reloaded/0, process_iq/1]).
|
||||
|
||||
-include("xmpp.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
@@ -53,7 +54,8 @@
|
||||
|
||||
-type image_error() :: efbig | enodata | limit | malformed_image | timeout.
|
||||
|
||||
-record(state, {limits = treap:empty() :: treap:treap()}).
|
||||
-record(state, {limits = treap:empty() :: treap:treap(),
|
||||
enabled = false :: boolean()}).
|
||||
|
||||
-record(captcha, {id :: binary(),
|
||||
pid :: pid() | undefined,
|
||||
@@ -214,6 +216,25 @@ process_reply(#xcaptcha{xdata = #xdata{} = X}) ->
|
||||
process_reply(_) ->
|
||||
{error, malformed}.
|
||||
|
||||
process_iq(#iq{type = set, lang = Lang, sub_els = [#xcaptcha{} = El]} = IQ) ->
|
||||
case process_reply(El) of
|
||||
ok ->
|
||||
xmpp:make_iq_result(IQ);
|
||||
{error, malformed} ->
|
||||
Txt = <<"Incorrect CAPTCHA submit">>,
|
||||
xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang));
|
||||
{error, _} ->
|
||||
Txt = <<"The CAPTCHA verification has failed">>,
|
||||
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang))
|
||||
end;
|
||||
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)).
|
||||
|
||||
process(_Handlers,
|
||||
#request{method = 'GET', lang = Lang,
|
||||
path = [_, Id]}) ->
|
||||
@@ -261,12 +282,29 @@ process(_Handlers,
|
||||
process(_Handlers, _Request) ->
|
||||
ejabberd_web:error(not_found).
|
||||
|
||||
host_up(Host) ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_CAPTCHA,
|
||||
?MODULE, process_iq).
|
||||
|
||||
host_down(Host) ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_CAPTCHA).
|
||||
|
||||
config_reloaded() ->
|
||||
gen_server:call(?MODULE, config_reloaded, timer:minutes(1)).
|
||||
|
||||
init([]) ->
|
||||
mnesia:delete_table(captcha),
|
||||
ets:new(captcha,
|
||||
[named_table, public, {keypos, #captcha.id}]),
|
||||
check_captcha_setup(),
|
||||
{ok, #state{}}.
|
||||
ets:new(captcha, [named_table, public, {keypos, #captcha.id}]),
|
||||
case check_captcha_setup() of
|
||||
true ->
|
||||
register_handlers(),
|
||||
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50),
|
||||
{ok, #state{enabled = true}};
|
||||
false ->
|
||||
{ok, #state{enabled = false}};
|
||||
{error, Reason} ->
|
||||
{stop, Reason}
|
||||
end.
|
||||
|
||||
handle_call({is_limited, Limiter, RateLimit}, _From,
|
||||
State) ->
|
||||
@@ -285,6 +323,23 @@ handle_call({is_limited, Limiter, RateLimit}, _From,
|
||||
Limits),
|
||||
{reply, false, State#state{limits = NewLimits}}
|
||||
end;
|
||||
handle_call(config_reloaded, _From, #state{enabled = Enabled} = State) ->
|
||||
State1 = case is_feature_available() of
|
||||
true when not Enabled ->
|
||||
case check_captcha_setup() of
|
||||
true ->
|
||||
register_handlers(),
|
||||
State#state{enabled = true};
|
||||
_ ->
|
||||
State
|
||||
end;
|
||||
false when Enabled ->
|
||||
unregister_handlers(),
|
||||
State#state{enabled = false};
|
||||
_ ->
|
||||
State
|
||||
end,
|
||||
{reply, ok, State1};
|
||||
handle_call(_Request, _From, State) ->
|
||||
{reply, bad_request, State}.
|
||||
|
||||
@@ -293,17 +348,29 @@ handle_cast(_Msg, State) -> {noreply, State}.
|
||||
handle_info({remove_id, Id}, State) ->
|
||||
?DEBUG("captcha ~p timed out", [Id]),
|
||||
case ets:lookup(captcha, Id) of
|
||||
[#captcha{args = Args, pid = Pid}] ->
|
||||
if is_pid(Pid) -> Pid ! {captcha_failed, Args};
|
||||
true -> ok
|
||||
end,
|
||||
ets:delete(captcha, Id);
|
||||
_ -> ok
|
||||
[#captcha{args = Args, pid = Pid}] ->
|
||||
callback(captcha_failed, Pid, Args),
|
||||
ets:delete(captcha, Id);
|
||||
_ -> ok
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_info(_Info, State) -> {noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) -> ok.
|
||||
terminate(_Reason, #state{enabled = Enabled}) ->
|
||||
if Enabled -> unregister_handlers();
|
||||
true -> ok
|
||||
end,
|
||||
ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 50).
|
||||
|
||||
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).
|
||||
|
||||
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).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
|
||||
@@ -467,16 +534,18 @@ is_feature_available() ->
|
||||
|
||||
check_captcha_setup() ->
|
||||
case is_feature_available() of
|
||||
true ->
|
||||
case create_image() of
|
||||
{ok, _, _, _} -> ok;
|
||||
_Err ->
|
||||
?CRITICAL_MSG("Captcha is enabled in the option captcha_cmd, "
|
||||
"but it can't generate images.",
|
||||
[]),
|
||||
throw({error, captcha_cmd_enabled_but_fails})
|
||||
end;
|
||||
false -> ok
|
||||
true ->
|
||||
case create_image() of
|
||||
{ok, _, _, _} ->
|
||||
true;
|
||||
Err ->
|
||||
?CRITICAL_MSG("Captcha is enabled in the option captcha_cmd, "
|
||||
"but it can't generate images.",
|
||||
[]),
|
||||
Err
|
||||
end;
|
||||
false ->
|
||||
false
|
||||
end.
|
||||
|
||||
lookup_captcha(Id) ->
|
||||
@@ -491,22 +560,18 @@ lookup_captcha(Id) ->
|
||||
|
||||
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),
|
||||
if ValidKey == ProvidedKey ->
|
||||
if is_pid(Pid) -> Pid ! {captcha_succeed, Args};
|
||||
true -> ok
|
||||
end,
|
||||
captcha_valid;
|
||||
true ->
|
||||
if is_pid(Pid) -> Pid ! {captcha_failed, Args};
|
||||
true -> ok
|
||||
end,
|
||||
captcha_non_valid
|
||||
end;
|
||||
_ -> captcha_not_found
|
||||
[#captcha{pid = Pid, args = Args, key = ValidKey, tref = Tref}] ->
|
||||
ets:delete(captcha, Id),
|
||||
erlang:cancel_timer(Tref),
|
||||
if ValidKey == ProvidedKey ->
|
||||
callback(captcha_succeed, Pid, Args),
|
||||
captcha_valid;
|
||||
true ->
|
||||
callback(captcha_failed, Pid, Args),
|
||||
captcha_non_valid
|
||||
end;
|
||||
_ ->
|
||||
captcha_not_found
|
||||
end.
|
||||
|
||||
clean_treap(Treap, CleanPriority) ->
|
||||
@@ -520,6 +585,14 @@ clean_treap(Treap, CleanPriority) ->
|
||||
end
|
||||
end.
|
||||
|
||||
-spec callback(captcha_succeed | captcha_failed, pid(), term()) -> any().
|
||||
callback(Result, _Pid, F) when is_function(F) ->
|
||||
F(Result);
|
||||
callback(Result, Pid, Args) when is_pid(Pid) ->
|
||||
Pid ! {Result, Args};
|
||||
callback(_, _, _) ->
|
||||
ok.
|
||||
|
||||
now_priority() ->
|
||||
-p1_time_compat:system_time(micro_seconds).
|
||||
|
||||
|
||||
@@ -259,9 +259,9 @@ get_commands_spec() ->
|
||||
"documentation should be stored",
|
||||
"Regexp matching names of commands or modules "
|
||||
"that will be included inside generated document",
|
||||
"Comma separated list of languages (choosen from java, perl, xmlrpc, json)"
|
||||
"Comma separated list of languages (chosen from java, perl, xmlrpc, json)"
|
||||
"that will have example invocation include in markdown document"],
|
||||
result_desc = "0 if command failed, 1 when succedded",
|
||||
result_desc = "0 if command failed, 1 when succeeded",
|
||||
args_example = ["/home/me/docs/api.html", "mod_admin", "java,json"],
|
||||
result_example = ok},
|
||||
#ejabberd_commands{name = gen_markdown_doc_for_commands, tags = [documentation],
|
||||
@@ -273,9 +273,9 @@ get_commands_spec() ->
|
||||
"documentation should be stored",
|
||||
"Regexp matching names of commands or modules "
|
||||
"that will be included inside generated document",
|
||||
"Comma separated list of languages (choosen from java, perl, xmlrpc, json)"
|
||||
"Comma separated list of languages (chosen from java, perl, xmlrpc, json)"
|
||||
"that will have example invocation include in markdown document"],
|
||||
result_desc = "0 if command failed, 1 when succedded",
|
||||
result_desc = "0 if command failed, 1 when succeeded",
|
||||
args_example = ["/home/me/docs/api.html", "mod_admin", "java,json"],
|
||||
result_example = ok}].
|
||||
|
||||
|
||||
@@ -363,10 +363,6 @@ gen_doc(#ejabberd_commands{name=Name, tags=_Tags, desc=Desc, longdesc=LongDesc,
|
||||
args=Args, args_desc=ArgsDesc,
|
||||
result=Result, result_desc=ResultDesc}=Cmd, HTMLOutput, Langs) ->
|
||||
try
|
||||
LDesc = case LongDesc of
|
||||
"" -> Desc;
|
||||
_ -> LongDesc
|
||||
end,
|
||||
ArgsText = case ArgsDesc of
|
||||
none ->
|
||||
[?TAG(ul, "args-list", [gen_param(AName, Type, undefined, HTMLOutput)
|
||||
@@ -393,11 +389,15 @@ gen_doc(#ejabberd_commands{name=Name, tags=_Tags, desc=Desc, longdesc=LongDesc,
|
||||
end
|
||||
end,
|
||||
|
||||
[?TAG(h1, [?TAG(strong, atom_to_list(Name)), <<" - ">>, ?RAW(Desc)]),
|
||||
?TAG(p, ?RAW(LDesc)),
|
||||
?TAG(h2, <<"Arguments:">>), ArgsText,
|
||||
?TAG(h2, <<"Result:">>), ResultText,
|
||||
?TAG(h2, <<"Examples:">>), gen_calls(Cmd, HTMLOutput, Langs)]
|
||||
[?TAG(h1, atom_to_list(Name)),
|
||||
?TAG(p, ?RAW(Desc)),
|
||||
case LongDesc of
|
||||
"" -> [];
|
||||
_ -> ?TAG(p, ?RAW(LongDesc))
|
||||
end,
|
||||
?TAG(h2, <<"Arguments:">>), ArgsText,
|
||||
?TAG(h2, <<"Result:">>), ResultText,
|
||||
?TAG(h2, <<"Examples:">>), gen_calls(Cmd, HTMLOutput, Langs)]
|
||||
catch
|
||||
_:Ex ->
|
||||
throw(iolist_to_binary(io_lib:format(
|
||||
@@ -458,9 +458,10 @@ generate_md_output(File, RegExp, Languages) ->
|
||||
Cmds3 = lists:sort(fun(#ejabberd_commands{name=N1}, #ejabberd_commands{name=N2}) ->
|
||||
N1 =< N2
|
||||
end, Cmds2),
|
||||
Cmds4 = [maybe_add_policy_arguments(Cmd) || Cmd <- Cmds3],
|
||||
Langs = binary:split(Languages, <<",">>, [global]),
|
||||
Header = <<"---\ntitle: Administration API reference\nbodyclass: nocomment\n---">>,
|
||||
Out = lists:map(fun(C) -> gen_doc(C, false, Langs) end, Cmds3),
|
||||
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]),
|
||||
file:close(Fh),
|
||||
|
||||
+65
-64
@@ -28,7 +28,6 @@
|
||||
|
||||
-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_vh_by_auth_method/1,
|
||||
get_version/0, get_myhosts/0, get_mylang/0, get_lang/1,
|
||||
get_ejabberd_config_path/0, is_using_elixir_config/0,
|
||||
prepare_opt_val/4, transform_options/1, collect_options/1,
|
||||
@@ -37,7 +36,8 @@
|
||||
is_elixir_enabled/0, v_dbs/1, v_dbs_mods/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]).
|
||||
use_cache/1, cache_size/1, cache_missed/1, cache_life_time/1,
|
||||
codec_options/1, get_plain_terms_file/2, negotiation_timeout/0]).
|
||||
|
||||
-export([start/2]).
|
||||
|
||||
@@ -71,8 +71,10 @@
|
||||
start() ->
|
||||
ConfigFile = get_ejabberd_config_path(),
|
||||
?INFO_MSG("Loading configuration from ~s", [ConfigFile]),
|
||||
p1_options:start_link(ejabberd_options),
|
||||
p1_options:start_link(ejabberd_db_modules),
|
||||
catch ets:new(ejabberd_options,
|
||||
[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
|
||||
@@ -105,9 +107,21 @@ hosts_to_start(State) ->
|
||||
%% At the moment, these functions are mainly used to setup unit tests.
|
||||
-spec start(Hosts :: [binary()], Opts :: [acl:acl() | local_config()]) -> ok.
|
||||
start(Hosts, Opts) ->
|
||||
p1_options:start_link(ejabberd_options),
|
||||
p1_options:start_link(ejabberd_db_modules),
|
||||
set_opts(set_hosts_in_options(Hosts, #state{opts = Opts})),
|
||||
catch ets:new(ejabberd_options,
|
||||
[named_table, public, {read_concurrency, true}]),
|
||||
catch ets:new(ejabberd_db_modules,
|
||||
[named_table, public, {read_concurrency, true}]),
|
||||
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,
|
||||
State1 = #state{opts = Opts},
|
||||
State2 = set_option({node_start, global}, UnixTime, State1),
|
||||
State3 = set_option({shared_key, global}, SharedKey, State2),
|
||||
set_opts(set_hosts_in_options(Hosts, State3)),
|
||||
ok.
|
||||
|
||||
%% @doc Get the filename of the ejabberd configuration file.
|
||||
@@ -441,8 +455,7 @@ get_config_lines2(Fd, Data, CurrLine, [NextWanted | LNumbers], R) when is_list(D
|
||||
exit_or_halt(ExitText) ->
|
||||
case [Vsn || {ejabberd, _Desc, Vsn} <- application:which_applications()] of
|
||||
[] ->
|
||||
timer:sleep(1000),
|
||||
halt(string:substr(ExitText, 1, 199));
|
||||
ejabberd:halt();
|
||||
[_] ->
|
||||
exit(ExitText)
|
||||
end.
|
||||
@@ -758,17 +771,12 @@ append_option({Opt, Host}, Val, State) ->
|
||||
|
||||
set_opts(State) ->
|
||||
Opts = State#state.opts,
|
||||
ets:select_delete(ejabberd_options,
|
||||
ets:fun2ms(
|
||||
fun({{node_start, _}, _}) -> false;
|
||||
({{shared_key, _}, _}) -> false;
|
||||
(_) -> true
|
||||
end)),
|
||||
lists:foreach(
|
||||
fun(#local_config{key = {Opt, Host}, value = Val}) ->
|
||||
p1_options:insert(ejabberd_options, Opt, Host, Val)
|
||||
end, Opts),
|
||||
p1_options:compile(ejabberd_options),
|
||||
ets:insert(
|
||||
ejabberd_options,
|
||||
lists:map(
|
||||
fun(#local_config{key = Key, value = Val}) ->
|
||||
{Key, Val}
|
||||
end, Opts)),
|
||||
set_log_level().
|
||||
|
||||
set_log_level() ->
|
||||
@@ -784,8 +792,8 @@ add_local_option(Opt, Val) ->
|
||||
add_option(Opt, Val) when is_atom(Opt) ->
|
||||
add_option({Opt, global}, Val);
|
||||
add_option({Opt, Host}, Val) ->
|
||||
p1_options:insert(ejabberd_options, Opt, Host, Val),
|
||||
p1_options:compile(ejabberd_options).
|
||||
ets:insert(ejabberd_options, {{Opt, Host}, Val}),
|
||||
ok.
|
||||
|
||||
-spec prepare_opt_val(any(), any(), check_fun(), any()) -> any().
|
||||
|
||||
@@ -862,13 +870,12 @@ get_option(Opt, Default) ->
|
||||
"format. This is likely a bug", [Opt]),
|
||||
{undefined, global}
|
||||
end,
|
||||
case ejabberd_options:is_known(Key) of
|
||||
true ->
|
||||
case ejabberd_options:Key(Host) of
|
||||
{ok, Val} -> Val;
|
||||
undefined -> Default
|
||||
try ets:lookup_element(ejabberd_options, {Key, Host}, 2)
|
||||
catch _:badarg when Host /= global ->
|
||||
try ets:lookup_element(ejabberd_options, {Key, global}, 2)
|
||||
catch _:badarg -> Default
|
||||
end;
|
||||
false ->
|
||||
_:badarg ->
|
||||
Default
|
||||
end.
|
||||
|
||||
@@ -878,8 +885,8 @@ has_option(Opt) ->
|
||||
|
||||
init_module_db_table(Modules) ->
|
||||
%% Dirty hack for mod_pubsub
|
||||
p1_options:insert(ejabberd_db_modules, mod_pubsub, mnesia, true),
|
||||
p1_options:insert(ejabberd_db_modules, mod_pubsub, sql, true),
|
||||
ets:insert(ejabberd_db_modules, {{mod_pubsub, mnesia}, true}),
|
||||
ets:insert(ejabberd_db_modules, {{mod_pubsub, sql}, true}),
|
||||
lists:foreach(
|
||||
fun(M) ->
|
||||
case re:split(atom_to_list(M), "_", [{return, list}]) of
|
||||
@@ -891,14 +898,13 @@ init_module_db_table(Modules) ->
|
||||
BareMod = list_to_atom(string:join(lists:reverse(T), "_")),
|
||||
case is_behaviour(BareMod, M) of
|
||||
true ->
|
||||
p1_options:insert(ejabberd_db_modules,
|
||||
BareMod, Suffix, true);
|
||||
ets:insert(ejabberd_db_modules,
|
||||
{{BareMod, Suffix}, true});
|
||||
false ->
|
||||
ok
|
||||
end
|
||||
end
|
||||
end, Modules),
|
||||
p1_options:compile(ejabberd_db_modules).
|
||||
end, Modules).
|
||||
|
||||
is_behaviour(Behav, Mod) ->
|
||||
try Mod:module_info(attributes) of
|
||||
@@ -920,20 +926,20 @@ is_behaviour(Behav, Mod) ->
|
||||
v_db(Mod, internal) -> v_db(Mod, mnesia);
|
||||
v_db(Mod, odbc) -> v_db(Mod, sql);
|
||||
v_db(Mod, Type) ->
|
||||
case ejabberd_db_modules:is_known(Mod) of
|
||||
true ->
|
||||
case ejabberd_db_modules:Mod(Type) of
|
||||
{ok, _} -> Type;
|
||||
_ -> erlang:error(badarg)
|
||||
end;
|
||||
false ->
|
||||
erlang:error(badarg)
|
||||
case ets:member(ejabberd_db_modules, {Mod, Type}) of
|
||||
true -> Type;
|
||||
false -> erlang:error(badarg)
|
||||
end.
|
||||
|
||||
-spec v_dbs(module()) -> [atom()].
|
||||
|
||||
v_dbs(Mod) ->
|
||||
ejabberd_db_modules:get_scope(Mod).
|
||||
ets:select(
|
||||
ejabberd_db_modules,
|
||||
ets:fun2ms(
|
||||
fun({{M, Type}, _}) when M == Mod ->
|
||||
Type
|
||||
end)).
|
||||
|
||||
-spec v_dbs_mods(module()) -> [module()].
|
||||
|
||||
@@ -1039,26 +1045,6 @@ validate_opts(#state{opts = Opts} = State, ModOpts) ->
|
||||
end, Opts),
|
||||
State#state{opts = NewOpts}.
|
||||
|
||||
-spec get_vh_by_auth_method(atom()) -> [binary()].
|
||||
|
||||
%% Return the list of hosts with a given auth method
|
||||
get_vh_by_auth_method(AuthMethod) ->
|
||||
Hosts = ejabberd_options:get_scope(auth_method),
|
||||
get_vh_by_auth_method(AuthMethod, Hosts, []).
|
||||
|
||||
get_vh_by_auth_method(Method, [Host|Hosts], Result) ->
|
||||
Methods = get_option({auth_method, Host}, []),
|
||||
case lists:member(Method, Methods) of
|
||||
true when Host == global ->
|
||||
get_myhosts();
|
||||
true ->
|
||||
get_vh_by_auth_method(Method, Hosts, [Host|Result]);
|
||||
false ->
|
||||
get_vh_by_auth_method(Method, Hosts, Result)
|
||||
end;
|
||||
get_vh_by_auth_method(_, [], Result) ->
|
||||
Result.
|
||||
|
||||
%% @spec (Path::string()) -> true | false
|
||||
is_file_readable(Path) ->
|
||||
case file:read_file_info(Path) of
|
||||
@@ -1428,15 +1414,19 @@ opt_type(cache_life_time) ->
|
||||
(infinity) -> infinity;
|
||||
(unlimited) -> infinity
|
||||
end;
|
||||
opt_type(negotiation_timeout) ->
|
||||
fun(T) when T > 0 -> T end;
|
||||
opt_type(shared_key) ->
|
||||
fun iolist_to_binary/1;
|
||||
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(_) ->
|
||||
[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,
|
||||
shared_key, node_start].
|
||||
shared_key, node_start, validate_stream, negotiation_timeout].
|
||||
|
||||
-spec may_hide_data(any()) -> any().
|
||||
may_hide_data(Data) ->
|
||||
@@ -1483,3 +1473,14 @@ cache_missed(Host) ->
|
||||
%% NOTE: the integer value returned is in *seconds*
|
||||
cache_life_time(Host) ->
|
||||
get_option({cache_life_time, Host}, 3600).
|
||||
|
||||
-spec codec_options(binary() | global) -> [xmpp:decode_option()].
|
||||
codec_options(Host) ->
|
||||
case get_option({validate_stream, Host}, false) of
|
||||
true -> [];
|
||||
false -> [ignore_els]
|
||||
end.
|
||||
|
||||
-spec negotiation_timeout() -> pos_integer().
|
||||
negotiation_timeout() ->
|
||||
timer:seconds(get_option(negotiation_timeout, 30)).
|
||||
|
||||
@@ -133,7 +133,7 @@ code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%-----------------------------
|
||||
%% ejabberdctl Command managment
|
||||
%% ejabberdctl Command management
|
||||
%%-----------------------------
|
||||
|
||||
register_commands(CmdDescs, Module, Function) ->
|
||||
@@ -866,7 +866,7 @@ print(Format, Args) ->
|
||||
io:format(lists:flatten(Format), Args).
|
||||
|
||||
%%-----------------------------
|
||||
%% Command managment
|
||||
%% Command management
|
||||
%%-----------------------------
|
||||
|
||||
%%+++
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||
%%% @doc
|
||||
%%% This is a stub module which will be replaced during
|
||||
%%% configuration load via p1_options:compile/1
|
||||
%%% The only purpose of this file is to shut up xref/dialyzer
|
||||
%%% @end
|
||||
%%% Created : 27 Apr 2017 by Evgeny Khramtsov <ekhramtsov@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_db_modules).
|
||||
|
||||
%% API
|
||||
-export([is_known/1, get_scope/1]).
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
is_known(_) ->
|
||||
false.
|
||||
|
||||
get_scope(_) ->
|
||||
[].
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
+8
-55
@@ -31,7 +31,7 @@
|
||||
|
||||
%% External exports
|
||||
-export([start/2, start_link/2, become_controller/1,
|
||||
socket_type/0, receive_headers/1, url_encode/1,
|
||||
socket_type/0, receive_headers/1,
|
||||
transform_listen_option/2, listen_opt_type/1]).
|
||||
|
||||
-export([init/2, opt_type/1]).
|
||||
@@ -651,7 +651,7 @@ parse_lang(Langs) ->
|
||||
end.
|
||||
|
||||
% Code below is taken (with some modifications) from the yaws webserver, which
|
||||
% is distributed under the folowing license:
|
||||
% is distributed under the following license:
|
||||
%
|
||||
% This software (the yaws webserver) is free software.
|
||||
% Parts of this software is Copyright (c) Claes Wikstrom <klacke@hyber.org>
|
||||
@@ -679,7 +679,7 @@ url_decode_q_split(<<>>, Ack) ->
|
||||
path_decode(Path) -> path_decode(Path, <<>>).
|
||||
|
||||
path_decode(<<$%, Hi, Lo, Tail/binary>>, Acc) ->
|
||||
Hex = hex_to_integer([Hi, Lo]),
|
||||
Hex = list_to_integer([Hi, Lo], 16),
|
||||
if Hex == 0 -> exit(badurl);
|
||||
true -> ok
|
||||
end,
|
||||
@@ -716,22 +716,6 @@ expand_custom_headers(Headers) ->
|
||||
{K, misc:expand_keyword(<<"@VERSION@">>, V, ?VERSION)}
|
||||
end, Headers).
|
||||
|
||||
%% hex_to_integer
|
||||
|
||||
hex_to_integer(Hex) ->
|
||||
case catch list_to_integer(Hex, 16) of
|
||||
{'EXIT', _} -> old_hex_to_integer(Hex);
|
||||
X -> X
|
||||
end.
|
||||
|
||||
old_hex_to_integer(Hex) ->
|
||||
DEHEX = fun (H) when H >= $a, H =< $f -> H - $a + 10;
|
||||
(H) when H >= $A, H =< $F -> H - $A + 10;
|
||||
(H) when H >= $0, H =< $9 -> H - $0
|
||||
end,
|
||||
lists:foldl(fun (E, Acc) -> Acc * 16 + DEHEX(E) end, 0,
|
||||
Hex).
|
||||
|
||||
code_to_phrase(100) -> <<"Continue">>;
|
||||
code_to_phrase(101) -> <<"Switching Protocols ">>;
|
||||
code_to_phrase(200) -> <<"OK">>;
|
||||
@@ -802,7 +786,7 @@ parse_urlencoded(S) ->
|
||||
|
||||
parse_urlencoded(<<$%, Hi, Lo, Tail/binary>>, Last, Cur,
|
||||
State) ->
|
||||
Hex = hex_to_integer([Hi, Lo]),
|
||||
Hex = list_to_integer([Hi, Lo], 16),
|
||||
parse_urlencoded(Tail, Last, <<Cur/binary, Hex>>, State);
|
||||
parse_urlencoded(<<$&, Tail/binary>>, _Last, Cur, key) ->
|
||||
[{Cur, <<"">>} | parse_urlencoded(Tail,
|
||||
@@ -822,41 +806,6 @@ parse_urlencoded(<<>>, Last, Cur, _State) ->
|
||||
[{Last, Cur}];
|
||||
parse_urlencoded(undefined, _, _, _) -> [].
|
||||
|
||||
|
||||
url_encode(A) ->
|
||||
url_encode(A, <<>>).
|
||||
|
||||
url_encode(<<H:8, T/binary>>, Acc) when
|
||||
(H >= $a andalso H =< $z) orelse
|
||||
(H >= $A andalso H =< $Z) orelse
|
||||
(H >= $0 andalso H =< $9) orelse
|
||||
H == $_ orelse
|
||||
H == $. orelse
|
||||
H == $- orelse
|
||||
H == $/ orelse
|
||||
H == $: ->
|
||||
url_encode(T, <<Acc/binary, H>>);
|
||||
url_encode(<<H:8, T/binary>>, Acc) ->
|
||||
case integer_to_hex(H) of
|
||||
[X, Y] -> url_encode(T, <<Acc/binary, $%, X, Y>>);
|
||||
[X] -> url_encode(T, <<Acc/binary, $%, $0, X>>)
|
||||
end;
|
||||
url_encode(<<>>, Acc) ->
|
||||
Acc.
|
||||
|
||||
|
||||
integer_to_hex(I) ->
|
||||
case catch erlang:integer_to_list(I, 16) of
|
||||
{'EXIT', _} -> old_integer_to_hex(I);
|
||||
Int -> Int
|
||||
end.
|
||||
|
||||
old_integer_to_hex(I) when I < 10 -> integer_to_list(I);
|
||||
old_integer_to_hex(I) when I < 16 -> [I - 10 + $A];
|
||||
old_integer_to_hex(I) when I >= 16 ->
|
||||
N = trunc(I / 16),
|
||||
old_integer_to_hex(N) ++ old_integer_to_hex(I rem 16).
|
||||
|
||||
% The following code is mostly taken from yaws_ssl.erl
|
||||
|
||||
toupper(C) when C >= $a andalso C =< $z -> C - 32;
|
||||
@@ -994,6 +943,10 @@ listen_opt_type(default_host) ->
|
||||
fun(A) -> A end;
|
||||
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.
|
||||
|
||||
@@ -134,6 +134,7 @@ init([{#ws{ip = IP, http_opts = HOpts}, _} = WS]) ->
|
||||
({resume_timeout, _}) -> true;
|
||||
({max_resume_timeout, _}) -> true;
|
||||
({resend_on_timeout, _}) -> true;
|
||||
({access, _}) -> true;
|
||||
(_) -> false
|
||||
end, HOpts),
|
||||
Opts = ejabberd_c2s_config:get_c2s_limits() ++ SOpts,
|
||||
|
||||
+2
-2
@@ -144,7 +144,7 @@ noreply(#state{expire = Expire} = State) ->
|
||||
-spec encode_id(non_neg_integer(), binary()) -> binary().
|
||||
encode_id(Expire, Rnd) ->
|
||||
ExpireBin = integer_to_binary(Expire),
|
||||
Node = atom_to_binary(node(), utf8),
|
||||
Node = ejabberd_cluster:node_id(),
|
||||
CheckSum = calc_checksum(<<ExpireBin/binary, Rnd/binary, Node/binary>>),
|
||||
<<"rr-", ExpireBin/binary, $-, Rnd/binary, $-, CheckSum/binary, $-, Node/binary>>.
|
||||
|
||||
@@ -155,7 +155,7 @@ decode_id(<<"rr-", ID/binary>>) ->
|
||||
[Rnd, Rest] = binary:split(Tail, <<"-">>),
|
||||
[CheckSum, NodeBin] = binary:split(Rest, <<"-">>),
|
||||
CheckSum = calc_checksum(<<ExpireBin/binary, Rnd/binary, NodeBin/binary>>),
|
||||
Node = erlang:binary_to_existing_atom(NodeBin, utf8),
|
||||
Node = ejabberd_cluster:get_node_by_id(NodeBin),
|
||||
Expire = binary_to_integer(ExpireBin),
|
||||
{ok, Expire, Rnd, Node}
|
||||
catch _:{badmatch, _} ->
|
||||
|
||||
@@ -107,7 +107,7 @@ init_udp(PortIP, Module, Opts, SockOpts, Port, IPS) ->
|
||||
{reuseaddr, true} |
|
||||
SockOpts]) of
|
||||
{ok, Socket} ->
|
||||
%% Inform my parent that this port was opened succesfully
|
||||
%% Inform my parent that this port was opened successfully
|
||||
proc_lib:init_ack({ok, self()}),
|
||||
application:ensure_started(ejabberd),
|
||||
start_module_sup(Port, Module),
|
||||
@@ -133,7 +133,7 @@ init_udp(PortIP, Module, Opts, SockOpts, Port, IPS) ->
|
||||
|
||||
init_tcp(PortIP, Module, Opts, SockOpts, Port, IPS) ->
|
||||
ListenSocket = listen_tcp(PortIP, Module, SockOpts, Port, IPS),
|
||||
%% Inform my parent that this port was opened succesfully
|
||||
%% Inform my parent that this port was opened successfully
|
||||
proc_lib:init_ack({ok, self()}),
|
||||
application:ensure_started(ejabberd),
|
||||
start_module_sup(Port, Module),
|
||||
@@ -572,10 +572,18 @@ transform_options({listen, LOpts}, Opts) ->
|
||||
transform_options(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
known_listen_options(Module) ->
|
||||
try Module:listen_options() of
|
||||
Opts -> [element(1, Opt) || Opt <- Opts]
|
||||
catch _:undef ->
|
||||
Module:listen_opt_type('')
|
||||
end.
|
||||
|
||||
-spec validate_module_options(module(), [{atom(), any()}]) -> [{atom(), any()}].
|
||||
validate_module_options(Module, Opts) ->
|
||||
try Module:listen_opt_type('') of
|
||||
try known_listen_options(Module) of
|
||||
_ ->
|
||||
maybe_start_zlib(Opts),
|
||||
lists:filtermap(
|
||||
fun({Opt, Val}) ->
|
||||
case validate_module_option(Module, Opt, Val) of
|
||||
@@ -664,5 +672,13 @@ all_zero_ip(Opts) ->
|
||||
false -> {0,0,0,0}
|
||||
end.
|
||||
|
||||
maybe_start_zlib(Opts) ->
|
||||
case proplists:get_bool(zlib, Opts) of
|
||||
true ->
|
||||
ejabberd:start_app(ezlib);
|
||||
false ->
|
||||
ok
|
||||
end.
|
||||
|
||||
opt_type(listen) -> fun validate_cfg/1;
|
||||
opt_type(_) -> [listen].
|
||||
|
||||
+8
-15
@@ -34,7 +34,7 @@
|
||||
|
||||
-export([route/1, process_iq/1,
|
||||
get_features/1,
|
||||
register_iq_handler/5,
|
||||
register_iq_handler/4,
|
||||
unregister_iq_handler/2,
|
||||
bounce_resource_packet/1,
|
||||
host_up/1, host_down/1]).
|
||||
@@ -78,8 +78,8 @@ process_iq(#iq{to = To, type = T, lang = Lang, sub_els = [El]} = Packet)
|
||||
XMLNS = xmpp:get_ns(El),
|
||||
Host = To#jid.lserver,
|
||||
case ets:lookup(?IQTABLE, {Host, XMLNS}) of
|
||||
[{_, Module, Function, Opts}] ->
|
||||
gen_iq_handler:handle(Host, Module, Function, Opts, Packet);
|
||||
[{_, Module, Function}] ->
|
||||
gen_iq_handler:handle(Host, Module, Function, Packet);
|
||||
[] ->
|
||||
Txt = <<"No module is handling this query">>,
|
||||
Err = xmpp:err_service_unavailable(Txt, Lang),
|
||||
@@ -112,11 +112,10 @@ 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(),
|
||||
gen_iq_handler:opts()) -> ok.
|
||||
register_iq_handler(Host, XMLNS, Module, Fun, Opts) ->
|
||||
-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, Opts}).
|
||||
{register_iq_handler, Host, XMLNS, Module, Fun}).
|
||||
|
||||
-spec unregister_iq_handler(binary(), binary()) -> ok.
|
||||
unregister_iq_handler(Host, XMLNS) ->
|
||||
@@ -160,19 +159,13 @@ init([]) ->
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok, {reply, Reply, State}.
|
||||
|
||||
handle_cast({register_iq_handler, Host, XMLNS, Module,
|
||||
Function, Opts},
|
||||
handle_cast({register_iq_handler, Host, XMLNS, Module, Function},
|
||||
State) ->
|
||||
ets:insert(?IQTABLE,
|
||||
{{Host, XMLNS}, Module, Function, Opts}),
|
||||
{{Host, XMLNS}, Module, Function}),
|
||||
{noreply, State};
|
||||
handle_cast({unregister_iq_handler, Host, XMLNS},
|
||||
State) ->
|
||||
case ets:lookup(?IQTABLE, {Host, XMLNS}) of
|
||||
[{_, Module, Function, Opts}] ->
|
||||
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
|
||||
_ -> ok
|
||||
end,
|
||||
ets:delete(?IQTABLE, {Host, XMLNS}),
|
||||
{noreply, State};
|
||||
handle_cast(_Msg, State) -> {noreply, State}.
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||
%%% @doc
|
||||
%%% This is a stub module which will be replaced during
|
||||
%%% configuration load via p1_options:compile/1
|
||||
%%% The only purpose of this file is to shut up xref/dialyzer
|
||||
%%% @end
|
||||
%%% Created : 16 Apr 2017 by Evgeny Khramtsov <ekhramtsov@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_options).
|
||||
|
||||
%% API
|
||||
-export([is_known/1, get_scope/1]).
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
is_known(_) ->
|
||||
false.
|
||||
|
||||
get_scope(_) ->
|
||||
[].
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
@@ -277,7 +277,7 @@ get_roster(User, Server) ->
|
||||
get_private(User, Server) ->
|
||||
case mod_private:get_data(User, Server) of
|
||||
[_|_] = Els ->
|
||||
[xmpp:encode(#private{xml_els = Els})];
|
||||
[xmpp:encode(#private{sub_els = Els})];
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
+2
-52
@@ -37,7 +37,6 @@
|
||||
-include("logger.hrl").
|
||||
|
||||
-record(state, {validate = true :: boolean(),
|
||||
notify = false :: boolean(),
|
||||
paths = [] :: [file:filename()],
|
||||
certs = #{} :: map(),
|
||||
graph :: digraph:graph(),
|
||||
@@ -173,6 +172,7 @@ config_reloaded() ->
|
||||
true -> init_cache();
|
||||
false -> delete_cache()
|
||||
end,
|
||||
fast_tls:clear_cache(),
|
||||
gen_server:call(?MODULE, config_reloaded, 60000).
|
||||
|
||||
opt_type(ca_path) ->
|
||||
@@ -197,7 +197,6 @@ opt_type(_) ->
|
||||
%%% gen_server callbacks
|
||||
%%%===================================================================
|
||||
init([]) ->
|
||||
Notify = start_fs(),
|
||||
process_flag(trap_exit, true),
|
||||
ets:new(?MODULE, [named_table, public]),
|
||||
ejabberd_hooks:add(route_registered, ?MODULE, route_registered, 50),
|
||||
@@ -214,7 +213,7 @@ init([]) ->
|
||||
end,
|
||||
G = digraph:new([acyclic]),
|
||||
init_cache(),
|
||||
State = #state{validate = Validate, notify = Notify, graph = G},
|
||||
State = #state{validate = Validate, graph = G},
|
||||
case filelib:ensure_dir(filename:join(certs_dir(), "foo")) of
|
||||
ok ->
|
||||
clean_dir(certs_dir()),
|
||||
@@ -279,20 +278,6 @@ handle_call(_Request, _From, State) ->
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({_, {fs, file_event}, {File, Events}}, State) ->
|
||||
?DEBUG("got FS events for ~s: ~p", [File, Events]),
|
||||
Path = iolist_to_binary(File),
|
||||
case lists:member(modified, Events) of
|
||||
true ->
|
||||
case lists:member(Path, State#state.paths) of
|
||||
true ->
|
||||
handle_cast(config_reloaded, State);
|
||||
false ->
|
||||
{noreply, State}
|
||||
end;
|
||||
false ->
|
||||
{noreply, State}
|
||||
end;
|
||||
handle_info(_Info, State) ->
|
||||
?WARNING_MSG("unexpected info: ~p", [_Info]),
|
||||
{noreply, State}.
|
||||
@@ -419,7 +404,6 @@ build_chain_and_check(State) ->
|
||||
?DEBUG("Validating certificates", []),
|
||||
Errors = validate(CertPaths, State#state.validate),
|
||||
?DEBUG("Subscribing to file events", []),
|
||||
subscribe(State),
|
||||
lists:foreach(
|
||||
fun({Cert, Why}) ->
|
||||
Path = maps:get(Cert, State#state.certs),
|
||||
@@ -854,40 +838,6 @@ short_name_hash(_) ->
|
||||
"".
|
||||
-endif.
|
||||
|
||||
-spec subscribe(state()) -> ok.
|
||||
subscribe(#state{notify = true} = State) ->
|
||||
lists:foreach(
|
||||
fun(Path) ->
|
||||
Dir = filename:dirname(Path),
|
||||
Name = list_to_atom(integer_to_list(erlang:phash2(Dir))),
|
||||
case fs:start_link(Name, Dir) of
|
||||
{ok, _} ->
|
||||
?DEBUG("Subscribed to FS events from ~s", [Dir]),
|
||||
fs:subscribe(Name);
|
||||
{error, _} ->
|
||||
ok
|
||||
end
|
||||
end, State#state.paths);
|
||||
subscribe(_) ->
|
||||
ok.
|
||||
|
||||
-spec start_fs() -> boolean().
|
||||
start_fs() ->
|
||||
application:load(fs),
|
||||
application:set_env(fs, backwards_compatible, false),
|
||||
case application:ensure_all_started(fs) of
|
||||
{ok, _} -> true;
|
||||
{error, {already_loaded, _}} -> true;
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Failed to load 'fs' Erlang application: ~p; "
|
||||
"certificates change detection will be disabled. "
|
||||
"You should now manually run `ejabberdctl "
|
||||
"reload_config` whenever certificates are changed "
|
||||
"on disc",
|
||||
[Reason]),
|
||||
false
|
||||
end.
|
||||
|
||||
wildcard(Path) when is_binary(Path) ->
|
||||
wildcard(binary_to_list(Path));
|
||||
wildcard(Path) ->
|
||||
|
||||
+61
-7
@@ -85,11 +85,65 @@ greplace(String, Regexp, New) ->
|
||||
A -> A
|
||||
end.
|
||||
|
||||
-spec sh_to_awk(binary()) -> binary().
|
||||
|
||||
sh_to_awk(ShRegExp) ->
|
||||
case exec({xmerl_regexp, sh_to_awk, [binary_to_list(ShRegExp)]},
|
||||
{regexp, sh_to_awk, [binary_to_list(ShRegExp)]})
|
||||
of
|
||||
A -> iolist_to_binary(A)
|
||||
end.
|
||||
%% This code was copied and adapted from xmerl_regexp.erl
|
||||
|
||||
-spec sh_to_awk(binary()) -> binary().
|
||||
sh_to_awk(Sh) ->
|
||||
iolist_to_binary([<<"^(">>, sh_to_awk_1(Sh)]). %Fix the beginning
|
||||
|
||||
sh_to_awk_1(<<"*", Sh/binary>>) -> %This matches any string
|
||||
[<<".*">>, sh_to_awk_1(Sh)];
|
||||
sh_to_awk_1(<<"?", Sh/binary>>) -> %This matches any character
|
||||
[$., sh_to_awk_1(Sh)];
|
||||
sh_to_awk_1(<<"[^]", Sh/binary>>) -> %This takes careful handling
|
||||
[<<"\\^">>, sh_to_awk_1(Sh)];
|
||||
%% Must move '^' to end.
|
||||
sh_to_awk_1(<<"[^", Sh/binary>>) ->
|
||||
[$[, sh_to_awk_2(Sh, true)];
|
||||
sh_to_awk_1(<<"[!", Sh/binary>>) ->
|
||||
[<<"[^">>, sh_to_awk_2(Sh, false)];
|
||||
sh_to_awk_1(<<"[", Sh/binary>>) ->
|
||||
[$[, sh_to_awk_2(Sh, false)];
|
||||
sh_to_awk_1(<<C:8, Sh/binary>>) -> %% Unspecialise everything else which is not an escape character.
|
||||
case sh_special_char(C) of
|
||||
true -> [$\\,C|sh_to_awk_1(Sh)];
|
||||
false -> [C|sh_to_awk_1(Sh)]
|
||||
end;
|
||||
sh_to_awk_1(<<>>) ->
|
||||
<<")$">>. %Fix the end
|
||||
|
||||
sh_to_awk_2(<<"]", Sh/binary>>, UpArrow) ->
|
||||
[$]|sh_to_awk_3(Sh, UpArrow)];
|
||||
sh_to_awk_2(Sh, UpArrow) ->
|
||||
sh_to_awk_3(Sh, UpArrow).
|
||||
|
||||
sh_to_awk_3(<<"]", Sh/binary>>, true) ->
|
||||
[<<"^]">>, sh_to_awk_1(Sh)];
|
||||
sh_to_awk_3(<<"]", Sh/binary>>, false) ->
|
||||
[$]|sh_to_awk_1(Sh)];
|
||||
sh_to_awk_3(<<C:8, Sh/binary>>, UpArrow) ->
|
||||
[C|sh_to_awk_3(Sh, UpArrow)];
|
||||
sh_to_awk_3(<<>>, true) ->
|
||||
[$^|sh_to_awk_1([])];
|
||||
sh_to_awk_3(<<>>, false) ->
|
||||
sh_to_awk_1([]).
|
||||
|
||||
%% -type sh_special_char(char()) -> bool().
|
||||
%% Test if a character is a special character.
|
||||
|
||||
sh_special_char($|) -> true;
|
||||
sh_special_char($*) -> true;
|
||||
sh_special_char($+) -> true;
|
||||
sh_special_char($?) -> true;
|
||||
sh_special_char($() -> true;
|
||||
sh_special_char($)) -> true;
|
||||
sh_special_char($\\) -> true;
|
||||
sh_special_char($^) -> true;
|
||||
sh_special_char($$) -> true;
|
||||
sh_special_char($.) -> true;
|
||||
sh_special_char($[) -> true;
|
||||
sh_special_char($]) -> true;
|
||||
sh_special_char($") -> true;
|
||||
sh_special_char(_C) -> false.
|
||||
|
||||
|
||||
+13
-3
@@ -169,7 +169,8 @@ handle_stream_start(_StreamStart, #{lserver := LServer} = State) ->
|
||||
send(State, xmpp:serr_host_unknown());
|
||||
true ->
|
||||
ServerHost = ejabberd_router:host_of_route(LServer),
|
||||
State#{server_host => ServerHost}
|
||||
Opts = ejabberd_config:codec_options(LServer),
|
||||
State#{server_host => ServerHost, codec_options => Opts}
|
||||
end.
|
||||
|
||||
handle_stream_end(Reason, #{server_host := LServer} = State) ->
|
||||
@@ -258,6 +259,7 @@ init([State, Opts]) ->
|
||||
false -> [compression_none | TLSOpts1];
|
||||
true -> TLSOpts1
|
||||
end,
|
||||
Timeout = ejabberd_config:negotiation_timeout(),
|
||||
State1 = State#{tls_options => TLSOpts2,
|
||||
auth_domains => sets:new(),
|
||||
xmlns => ?NS_SERVER,
|
||||
@@ -267,7 +269,8 @@ init([State, Opts]) ->
|
||||
server_host => ?MYNAME,
|
||||
established => false,
|
||||
shaper => Shaper},
|
||||
ejabberd_hooks:run_fold(s2s_in_init, {ok, State1}, [Opts]).
|
||||
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
|
||||
ejabberd_hooks:run_fold(s2s_in_init, {ok, State2}, [Opts]).
|
||||
|
||||
handle_call(Request, From, #{server_host := LServer} = State) ->
|
||||
ejabberd_hooks:run_fold(s2s_in_handle_call, LServer, State, [Request, From]).
|
||||
@@ -355,6 +358,9 @@ change_shaper(#{shaper := ShaperName, server_host := ServerHost} = State,
|
||||
(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) ->
|
||||
@@ -378,6 +384,10 @@ listen_opt_type(max_stanza_size) ->
|
||||
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].
|
||||
tls_compression, tls, max_fsm_queue, backlog, inet, inet6].
|
||||
|
||||
@@ -254,10 +254,12 @@ handle_recv(El, Pkt, #{server_host := ServerHost} = State) ->
|
||||
handle_send(El, Pkt, #{server_host := ServerHost} = State) ->
|
||||
ejabberd_hooks:run_fold(s2s_out_handle_send, ServerHost, State, [El, Pkt]).
|
||||
|
||||
handle_timeout(#{on_route := Action} = State) ->
|
||||
handle_timeout(#{on_route := Action, lang := Lang} = State) ->
|
||||
case Action of
|
||||
bounce -> stop(State);
|
||||
_ -> send(State, xmpp:serr_connection_timeout())
|
||||
_ ->
|
||||
Txt = <<"Idle connection">>,
|
||||
send(State, xmpp:serr_connection_timeout(Txt, Lang))
|
||||
end.
|
||||
|
||||
init([#{server := LServer, remote_server := RServer} = State, Opts]) ->
|
||||
@@ -268,15 +270,17 @@ init([#{server := LServer, remote_server := RServer} = State, Opts]) ->
|
||||
{_, N} -> N;
|
||||
false -> unlimited
|
||||
end,
|
||||
Timeout = ejabberd_config:negotiation_timeout(),
|
||||
State1 = State#{on_route => queue,
|
||||
queue => p1_queue:new(QueueType, QueueLimit),
|
||||
xmlns => ?NS_SERVER,
|
||||
lang => ?MYLANG,
|
||||
server_host => ServerHost,
|
||||
shaper => none},
|
||||
State2 = xmpp_stream_out:set_timeout(State1, Timeout),
|
||||
?INFO_MSG("Outbound s2s connection started: ~s -> ~s",
|
||||
[LServer, RServer]),
|
||||
ejabberd_hooks:run_fold(s2s_out_init, ServerHost, {ok, State1}, [Opts]).
|
||||
ejabberd_hooks:run_fold(s2s_out_init, ServerHost, {ok, State2}, [Opts]).
|
||||
|
||||
handle_call(Request, From, #{server_host := ServerHost} = State) ->
|
||||
ejabberd_hooks:run_fold(s2s_out_handle_call, ServerHost, State, [Request, From]).
|
||||
|
||||
+72
-36
@@ -100,38 +100,43 @@ init([State, Opts]) ->
|
||||
false -> [compression_none | TLSOpts1];
|
||||
true -> TLSOpts1
|
||||
end,
|
||||
GlobalRoutes = proplists:get_value(global_routes, Opts, true),
|
||||
Timeout = ejabberd_config:negotiation_timeout(),
|
||||
State1 = xmpp_stream_in:change_shaper(State, Shaper),
|
||||
State2 = State1#{access => Access,
|
||||
xmlns => ?NS_COMPONENT,
|
||||
lang => ?MYLANG,
|
||||
server => ?MYNAME,
|
||||
host_opts => dict:from_list(HostOpts1),
|
||||
stream_version => undefined,
|
||||
tls_options => TLSOpts,
|
||||
check_from => CheckFrom},
|
||||
ejabberd_hooks:run_fold(component_init, {ok, State2}, [Opts]).
|
||||
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
|
||||
State3 = State2#{access => Access,
|
||||
xmlns => ?NS_COMPONENT,
|
||||
lang => ?MYLANG,
|
||||
server => ?MYNAME,
|
||||
host_opts => dict:from_list(HostOpts1),
|
||||
stream_version => undefined,
|
||||
tls_options => TLSOpts,
|
||||
global_routes => GlobalRoutes,
|
||||
check_from => CheckFrom},
|
||||
ejabberd_hooks:run_fold(component_init, {ok, State3}, [Opts]).
|
||||
|
||||
handle_stream_start(_StreamStart,
|
||||
#{remote_server := RemoteServer,
|
||||
lang := Lang,
|
||||
host_opts := HostOpts} = State) ->
|
||||
case ejabberd_router:is_my_host(RemoteServer) of
|
||||
true ->
|
||||
true ->
|
||||
Txt = <<"Unable to register route on existing local domain">>,
|
||||
xmpp_stream_in:send(State, xmpp:serr_conflict(Txt, Lang));
|
||||
false ->
|
||||
false ->
|
||||
NewHostOpts = case dict:is_key(RemoteServer, HostOpts) of
|
||||
true ->
|
||||
HostOpts;
|
||||
false ->
|
||||
case dict:find(global, HostOpts) of
|
||||
{ok, GlobalPass} ->
|
||||
{ok, GlobalPass} ->
|
||||
dict:from_list([{RemoteServer, GlobalPass}]);
|
||||
error ->
|
||||
error ->
|
||||
HostOpts
|
||||
end
|
||||
end,
|
||||
State#{host_opts => NewHostOpts}
|
||||
end
|
||||
end,
|
||||
CodecOpts = ejabberd_config:codec_options(global),
|
||||
State#{host_opts => NewHostOpts, codec_options => CodecOpts}
|
||||
end.
|
||||
|
||||
get_password_fun(#{remote_server := RemoteServer,
|
||||
@@ -139,7 +144,7 @@ get_password_fun(#{remote_server := RemoteServer,
|
||||
host_opts := HostOpts}) ->
|
||||
fun(_) ->
|
||||
case dict:find(RemoteServer, HostOpts) of
|
||||
{ok, Password} ->
|
||||
{ok, Password} ->
|
||||
{Password, undefined};
|
||||
error ->
|
||||
?INFO_MSG("(~s) Domain ~s is unconfigured for "
|
||||
@@ -152,16 +157,22 @@ get_password_fun(#{remote_server := RemoteServer,
|
||||
|
||||
handle_auth_success(_, Mech, _,
|
||||
#{remote_server := RemoteServer, host_opts := HostOpts,
|
||||
socket := Socket, ip := IP} = State) ->
|
||||
socket := Socket, ip := IP,
|
||||
global_routes := GlobalRoutes} = State) ->
|
||||
?INFO_MSG("(~s) Accepted external component ~s authentication "
|
||||
"for ~s from ~s",
|
||||
[xmpp_socket:pp(Socket), Mech, RemoteServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
|
||||
lists:foreach(
|
||||
fun (H) ->
|
||||
ejabberd_router:register_route(H, ?MYNAME),
|
||||
ejabberd_hooks:run(component_connected, [H])
|
||||
end, dict:fetch_keys(HostOpts)),
|
||||
Routes = if GlobalRoutes ->
|
||||
dict:fetch_keys(HostOpts);
|
||||
true ->
|
||||
[RemoteServer]
|
||||
end,
|
||||
lists:foreach(
|
||||
fun(H) ->
|
||||
ejabberd_router:register_route(H, ?MYNAME),
|
||||
ejabberd_hooks:run(component_connected, [H])
|
||||
end, Routes),
|
||||
State.
|
||||
|
||||
handle_auth_failure(_, Mech, Reason,
|
||||
@@ -179,11 +190,17 @@ handle_authenticated_packet(Pkt0, #{ip := {IP, _}, lang := Lang} = State)
|
||||
Pkt = xmpp:put_meta(Pkt0, ip, IP),
|
||||
From = xmpp:get_from(Pkt),
|
||||
case check_from(From, State) of
|
||||
true ->
|
||||
ejabberd_router:route(Pkt),
|
||||
State;
|
||||
false ->
|
||||
Txt = <<"Improper domain part of 'from' attribute">>,
|
||||
true ->
|
||||
{Pkt2, State2} = ejabberd_hooks:run_fold(component_send_packet, {Pkt, State}, []),
|
||||
case Pkt2 of
|
||||
drop ->
|
||||
ok;
|
||||
_ ->
|
||||
ejabberd_router:route(Pkt2)
|
||||
end,
|
||||
State2;
|
||||
false ->
|
||||
Txt = <<"Improper domain part of 'from' attribute">>,
|
||||
Err = xmpp:serr_invalid_from(Txt, Lang),
|
||||
xmpp_stream_in:send(State, Err)
|
||||
end;
|
||||
@@ -192,7 +209,7 @@ handle_authenticated_packet(_Pkt, State) ->
|
||||
|
||||
handle_info({route, Packet}, #{access := Access} = State) ->
|
||||
case acl:match_rule(global, Access, xmpp:get_from(Packet)) of
|
||||
allow ->
|
||||
allow ->
|
||||
xmpp_stream_in:send(State, Packet);
|
||||
deny ->
|
||||
Lang = xmpp:get_lang(Packet),
|
||||
@@ -204,17 +221,27 @@ handle_info(Info, State) ->
|
||||
?ERROR_MSG("Unexpected info: ~p", [Info]),
|
||||
State.
|
||||
|
||||
terminate(Reason, #{stream_state := StreamState, host_opts := HostOpts}) ->
|
||||
terminate(Reason, #{stream_state := StreamState,
|
||||
host_opts := HostOpts,
|
||||
remote_server := RemoteServer,
|
||||
global_routes := GlobalRoutes}) ->
|
||||
case StreamState of
|
||||
established ->
|
||||
Routes = if GlobalRoutes ->
|
||||
dict:fetch_keys(HostOpts);
|
||||
true ->
|
||||
[RemoteServer]
|
||||
end,
|
||||
lists:foreach(
|
||||
fun(H) ->
|
||||
ejabberd_router:unregister_route(H),
|
||||
ejabberd_router:unregister_route(H),
|
||||
ejabberd_hooks:run(component_disconnected, [H, Reason])
|
||||
end, dict:fetch_keys(HostOpts));
|
||||
end, Routes);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
ok
|
||||
end;
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
@@ -267,9 +294,12 @@ transform_listen_option(Opt, Opts) ->
|
||||
(check_from) -> fun((boolean()) -> boolean());
|
||||
(password) -> fun((boolean()) -> boolean());
|
||||
(hosts) -> fun(([{binary(), [{password, binary()}]}]) ->
|
||||
[{binary(), binary() | undefined}]);
|
||||
[{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;
|
||||
@@ -298,6 +328,8 @@ listen_opt_type(hosts) ->
|
||||
{iolist_to_binary(Host), Password}
|
||||
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;
|
||||
@@ -305,7 +337,11 @@ listen_opt_type(max_stanza_size) ->
|
||||
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].
|
||||
max_fsm_queue, global_routes, backlog, inet, inet6].
|
||||
|
||||
+17
-28
@@ -43,7 +43,7 @@
|
||||
open_session/5,
|
||||
open_session/6,
|
||||
close_session/4,
|
||||
check_in_subscription/6,
|
||||
check_in_subscription/2,
|
||||
bounce_offline_message/1,
|
||||
disconnect_removed_user/2,
|
||||
get_user_resources/2,
|
||||
@@ -58,7 +58,7 @@
|
||||
get_vh_session_list/1,
|
||||
get_vh_session_number/1,
|
||||
get_vh_by_backend/1,
|
||||
register_iq_handler/5,
|
||||
register_iq_handler/4,
|
||||
unregister_iq_handler/2,
|
||||
force_update_presence/1,
|
||||
connected_users/0,
|
||||
@@ -80,7 +80,8 @@
|
||||
host_down/1,
|
||||
make_sid/0,
|
||||
clean_cache/1,
|
||||
config_reloaded/0
|
||||
config_reloaded/0,
|
||||
is_online/1
|
||||
]).
|
||||
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
@@ -183,10 +184,9 @@ close_session(SID, User, Server, Resource) ->
|
||||
ejabberd_hooks:run(sm_remove_connection_hook,
|
||||
JID#jid.lserver, [SID, JID, Info]).
|
||||
|
||||
-spec check_in_subscription(boolean(), binary(), binary(), jid(),
|
||||
subscribe | subscribed | unsubscribe | unsubscribed,
|
||||
binary()) -> boolean() | {stop, false}.
|
||||
check_in_subscription(Acc, User, Server, _JID, _Type, _Reason) ->
|
||||
-spec check_in_subscription(boolean(), presence()) -> boolean() | {stop, false}.
|
||||
check_in_subscription(Acc, #presence{to = To}) ->
|
||||
#jid{user = User, server = Server} = To,
|
||||
case ejabberd_auth:user_exists(User, Server) of
|
||||
true -> Acc;
|
||||
false -> {stop, false}
|
||||
@@ -397,11 +397,11 @@ get_vh_session_number(Server) ->
|
||||
Mod = get_sm_backend(LServer),
|
||||
length(online(get_sessions(Mod, LServer))).
|
||||
|
||||
-spec register_iq_handler(binary(), binary(), atom(), atom(), list()) -> ok.
|
||||
-spec register_iq_handler(binary(), binary(), atom(), atom()) -> ok.
|
||||
|
||||
register_iq_handler(Host, XMLNS, Module, Fun, Opts) ->
|
||||
register_iq_handler(Host, XMLNS, Module, Fun) ->
|
||||
?GEN_SERVER:cast(?MODULE,
|
||||
{register_iq_handler, Host, XMLNS, Module, Fun, Opts}).
|
||||
{register_iq_handler, Host, XMLNS, Module, Fun}).
|
||||
|
||||
-spec unregister_iq_handler(binary(), binary()) -> ok.
|
||||
|
||||
@@ -448,19 +448,13 @@ init([]) ->
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok, {reply, Reply, State}.
|
||||
|
||||
handle_cast({register_iq_handler, Host, XMLNS, Module,
|
||||
Function, Opts},
|
||||
handle_cast({register_iq_handler, Host, XMLNS, Module, Function},
|
||||
State) ->
|
||||
ets:insert(sm_iqtable,
|
||||
{{Host, XMLNS}, Module, Function, Opts}),
|
||||
{{Host, XMLNS}, Module, Function}),
|
||||
{noreply, State};
|
||||
handle_cast({unregister_iq_handler, Host, XMLNS},
|
||||
State) ->
|
||||
case ets:lookup(sm_iqtable, {Host, XMLNS}) of
|
||||
[{_, Module, Function, Opts}] ->
|
||||
gen_iq_handler:stop_iq_handler(Module, Function, Opts);
|
||||
_ -> ok
|
||||
end,
|
||||
ets:delete(sm_iqtable, {Host, XMLNS}),
|
||||
{noreply, State};
|
||||
handle_cast(_Msg, State) -> {noreply, State}.
|
||||
@@ -627,19 +621,14 @@ do_route(To, Term) ->
|
||||
end.
|
||||
|
||||
-spec do_route(stanza()) -> any().
|
||||
do_route(#presence{from = From, to = To, type = T, status = Status} = Packet)
|
||||
do_route(#presence{to = To, type = T} = Packet)
|
||||
when T == subscribe; T == subscribed; T == unsubscribe; T == unsubscribed ->
|
||||
?DEBUG("processing subscription:~n~s", [xmpp:pp(Packet)]),
|
||||
#jid{user = User, server = Server,
|
||||
luser = LUser, lserver = LServer} = To,
|
||||
Reason = if T == subscribe -> xmpp:get_text(Status);
|
||||
true -> <<"">>
|
||||
end,
|
||||
#jid{luser = LUser, lserver = LServer} = To,
|
||||
case is_privacy_allow(Packet) andalso
|
||||
ejabberd_hooks:run_fold(
|
||||
roster_in_subscription,
|
||||
LServer, false,
|
||||
[User, Server, From, T, Reason]) of
|
||||
LServer, false, [Packet]) of
|
||||
true ->
|
||||
Mod = get_sm_backend(LServer),
|
||||
lists:foreach(
|
||||
@@ -867,8 +856,8 @@ process_iq(#iq{to = To, type = T, lang = Lang, sub_els = [El]} = Packet)
|
||||
XMLNS = xmpp:get_ns(El),
|
||||
Host = To#jid.lserver,
|
||||
case ets:lookup(sm_iqtable, {Host, XMLNS}) of
|
||||
[{_, Module, Function, Opts}] ->
|
||||
gen_iq_handler:handle(Host, Module, Function, Opts, Packet);
|
||||
[{_, Module, Function}] ->
|
||||
gen_iq_handler:handle(Host, Module, Function, Packet);
|
||||
[] ->
|
||||
Txt = <<"No module is handling this query">>,
|
||||
Err = xmpp:err_service_unavailable(Txt, Lang),
|
||||
|
||||
+13
-1
@@ -39,6 +39,7 @@
|
||||
sql_bloc/2,
|
||||
abort/1,
|
||||
restart/1,
|
||||
use_new_schema/0,
|
||||
sql_query_to_iolist/1,
|
||||
escape/1,
|
||||
standard_escape/1,
|
||||
@@ -95,6 +96,12 @@
|
||||
|
||||
-define(PREPARE_KEY, ejabberd_sql_prepare).
|
||||
|
||||
-ifdef(NEW_SQL_SCHEMA).
|
||||
-define(USE_NEW_SCHEMA_DEFAULT, true).
|
||||
-else.
|
||||
-define(USE_NEW_SCHEMA_DEFAULT, false).
|
||||
-endif.
|
||||
|
||||
%%-define(DBGFSM, true).
|
||||
|
||||
-ifdef(DBGFSM).
|
||||
@@ -272,6 +279,9 @@ sqlite_file(Host) ->
|
||||
binary_to_list(File)
|
||||
end.
|
||||
|
||||
use_new_schema() ->
|
||||
ejabberd_config:get_option(new_sql_schema, ?USE_NEW_SCHEMA_DEFAULT).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Callback functions from gen_fsm
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -1133,9 +1143,11 @@ opt_type(sql_connect_timeout) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(sql_queue_type) ->
|
||||
fun(ram) -> ram; (file) -> file end;
|
||||
opt_type(new_sql_schema) -> fun(B) when is_boolean(B) -> B end;
|
||||
opt_type(_) ->
|
||||
[sql_database, sql_keepalive_interval,
|
||||
sql_password, sql_port, sql_server,
|
||||
sql_username, sql_ssl, sql_ssl_verify, sql_ssl_certfile,
|
||||
sql_ssl_cafile, sql_queue_type, sql_query_timeout,
|
||||
sql_connect_timeout].
|
||||
sql_connect_timeout,
|
||||
new_sql_schema].
|
||||
|
||||
+105
-90
@@ -28,7 +28,7 @@
|
||||
%% API
|
||||
-export([parse_transform/2, format_error/1]).
|
||||
|
||||
-export([parse/2]).
|
||||
%-export([parse/2]).
|
||||
|
||||
-include("ejabberd_sql_pt.hrl").
|
||||
|
||||
@@ -41,7 +41,8 @@
|
||||
res_vars = [],
|
||||
res_pos = 0,
|
||||
server_host_used = false,
|
||||
used_vars = []}).
|
||||
used_vars = [],
|
||||
use_new_schema}).
|
||||
|
||||
-define(QUERY_RECORD, "sql_query").
|
||||
|
||||
@@ -88,26 +89,7 @@ transform(Form) ->
|
||||
[Arg] ->
|
||||
case erl_syntax:type(Arg) of
|
||||
string ->
|
||||
S = erl_syntax:string_value(Arg),
|
||||
Pos = erl_syntax:get_pos(Arg),
|
||||
ParseRes = parse(S, Pos),
|
||||
UnusedVars =
|
||||
case ParseRes#state.server_host_used of
|
||||
{true, SHVar} ->
|
||||
case ?USE_NEW_SCHEMA of
|
||||
true -> [];
|
||||
false -> [SHVar]
|
||||
end;
|
||||
false ->
|
||||
add_warning(
|
||||
Pos, no_server_host),
|
||||
[]
|
||||
end,
|
||||
set_pos(
|
||||
add_unused_vars(
|
||||
make_sql_query(ParseRes),
|
||||
UnusedVars),
|
||||
Pos);
|
||||
transform_sql(Arg);
|
||||
_ ->
|
||||
throw({error, erl_syntax:get_pos(Form),
|
||||
"?SQL argument must be "
|
||||
@@ -123,26 +105,7 @@ transform(Form) ->
|
||||
case {erl_syntax:type(TableArg),
|
||||
erl_syntax:is_proper_list(FieldsArg)}of
|
||||
{string, true} ->
|
||||
Table = erl_syntax:string_value(TableArg),
|
||||
ParseRes =
|
||||
parse_upsert(
|
||||
erl_syntax:list_elements(FieldsArg)),
|
||||
Pos = erl_syntax:get_pos(Form),
|
||||
case lists:keymember(
|
||||
"server_host", 1, ParseRes) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
add_warning(Pos, no_server_host)
|
||||
end,
|
||||
{ParseRes2, UnusedVars} =
|
||||
filter_upsert_sh(Table, ParseRes),
|
||||
set_pos(
|
||||
add_unused_vars(
|
||||
make_sql_upsert(Table, ParseRes2, Pos),
|
||||
UnusedVars
|
||||
),
|
||||
Pos);
|
||||
transform_upsert(Form, TableArg, FieldsArg);
|
||||
_ ->
|
||||
throw({error, erl_syntax:get_pos(Form),
|
||||
"?SQL_UPSERT arguments must be "
|
||||
@@ -158,26 +121,7 @@ transform(Form) ->
|
||||
case {erl_syntax:type(TableArg),
|
||||
erl_syntax:is_proper_list(FieldsArg)}of
|
||||
{string, true} ->
|
||||
Table = erl_syntax:string_value(TableArg),
|
||||
ParseRes =
|
||||
parse_insert(
|
||||
erl_syntax:list_elements(FieldsArg)),
|
||||
Pos = erl_syntax:get_pos(Form),
|
||||
case lists:keymember(
|
||||
"server_host", 1, ParseRes) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
add_warning(Pos, no_server_host)
|
||||
end,
|
||||
{ParseRes2, UnusedVars} =
|
||||
filter_upsert_sh(Table, ParseRes),
|
||||
set_pos(
|
||||
add_unused_vars(
|
||||
make_sql_insert(Table, ParseRes2),
|
||||
UnusedVars
|
||||
),
|
||||
Pos);
|
||||
transform_insert(Form, TableArg, FieldsArg);
|
||||
_ ->
|
||||
throw({error, erl_syntax:get_pos(Form),
|
||||
"?SQL_INSERT arguments must be "
|
||||
@@ -226,11 +170,81 @@ top_transform(Forms) when is_list(Forms) ->
|
||||
end
|
||||
end, Forms).
|
||||
|
||||
parse(S, Loc) ->
|
||||
parse1(S, [], #state{loc = Loc}).
|
||||
transform_sql(Arg) ->
|
||||
S = erl_syntax:string_value(Arg),
|
||||
Pos = erl_syntax:get_pos(Arg),
|
||||
ParseRes = parse(S, Pos, true),
|
||||
ParseResOld = parse(S, Pos, false),
|
||||
case ParseRes#state.server_host_used of
|
||||
{true, _SHVar} ->
|
||||
ok;
|
||||
false ->
|
||||
add_warning(
|
||||
Pos, no_server_host),
|
||||
[]
|
||||
end,
|
||||
set_pos(
|
||||
make_schema_check(
|
||||
make_sql_query(ParseRes),
|
||||
make_sql_query(ParseResOld)
|
||||
),
|
||||
Pos).
|
||||
|
||||
parse(S, ParamPos, Loc) ->
|
||||
parse1(S, [], #state{loc = Loc, param_pos = ParamPos}).
|
||||
transform_upsert(Form, TableArg, FieldsArg) ->
|
||||
Table = erl_syntax:string_value(TableArg),
|
||||
ParseRes =
|
||||
parse_upsert(
|
||||
erl_syntax:list_elements(FieldsArg)),
|
||||
Pos = erl_syntax:get_pos(Form),
|
||||
case lists:keymember(
|
||||
"server_host", 1, ParseRes) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
add_warning(Pos, no_server_host)
|
||||
end,
|
||||
ParseResOld =
|
||||
filter_upsert_sh(Table, ParseRes),
|
||||
set_pos(
|
||||
make_schema_check(
|
||||
make_sql_upsert(Table, ParseRes, Pos),
|
||||
make_sql_upsert(Table, ParseResOld, Pos)
|
||||
),
|
||||
Pos).
|
||||
|
||||
transform_insert(Form, TableArg, FieldsArg) ->
|
||||
Table = erl_syntax:string_value(TableArg),
|
||||
ParseRes =
|
||||
parse_insert(
|
||||
erl_syntax:list_elements(FieldsArg)),
|
||||
Pos = erl_syntax:get_pos(Form),
|
||||
case lists:keymember(
|
||||
"server_host", 1, ParseRes) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
add_warning(Pos, no_server_host)
|
||||
end,
|
||||
ParseResOld =
|
||||
filter_upsert_sh(Table, ParseRes),
|
||||
set_pos(
|
||||
make_schema_check(
|
||||
make_sql_insert(Table, ParseRes),
|
||||
make_sql_insert(Table, ParseResOld)
|
||||
),
|
||||
Pos).
|
||||
|
||||
|
||||
parse(S, Loc, UseNewSchema) ->
|
||||
parse1(S, [],
|
||||
#state{loc = Loc,
|
||||
use_new_schema = UseNewSchema}).
|
||||
|
||||
parse(S, ParamPos, Loc, UseNewSchema) ->
|
||||
parse1(S, [],
|
||||
#state{loc = Loc,
|
||||
param_pos = ParamPos,
|
||||
use_new_schema = UseNewSchema}).
|
||||
|
||||
parse1([], Acc, State) ->
|
||||
State1 = append_string(lists:reverse(Acc), State),
|
||||
@@ -274,7 +288,7 @@ parse1([$%, $( | S], Acc, State) ->
|
||||
State3 =
|
||||
State2#state{server_host_used = {true, Name},
|
||||
used_vars = [Name | State2#state.used_vars]},
|
||||
case ?USE_NEW_SCHEMA of
|
||||
case State#state.use_new_schema of
|
||||
true ->
|
||||
Convert =
|
||||
erl_syntax:application(
|
||||
@@ -350,7 +364,7 @@ make_var(V) ->
|
||||
|
||||
|
||||
make_sql_query(State) ->
|
||||
Hash = erlang:phash2(State#state{loc = undefined}),
|
||||
Hash = erlang:phash2(State#state{loc = undefined, use_new_schema = true}),
|
||||
SHash = <<"Q", (integer_to_binary(Hash))/binary>>,
|
||||
Query = pack_query(State#state.'query'),
|
||||
EQuery =
|
||||
@@ -442,7 +456,7 @@ parse_upsert_field1([], _Acc, _ParamPos, Loc) ->
|
||||
"?SQL_UPSERT fields must have the "
|
||||
"following form: \"[!-]name=value\""});
|
||||
parse_upsert_field1([$= | S], Acc, ParamPos, Loc) ->
|
||||
{lists:reverse(Acc), parse(S, ParamPos, Loc)};
|
||||
{lists:reverse(Acc), parse(S, ParamPos, Loc, true)};
|
||||
parse_upsert_field1([C | S], Acc, ParamPos, Loc) ->
|
||||
parse_upsert_field1(S, [C | Acc], ParamPos, Loc).
|
||||
|
||||
@@ -632,7 +646,7 @@ parse_insert_field1([], _Acc, _ParamPos, Loc) ->
|
||||
"?SQL_INSERT fields must have the "
|
||||
"following form: \"name=value\""});
|
||||
parse_insert_field1([$= | S], Acc, ParamPos, Loc) ->
|
||||
{lists:reverse(Acc), parse(S, ParamPos, Loc)};
|
||||
{lists:reverse(Acc), parse(S, ParamPos, Loc, true)};
|
||||
parse_insert_field1([C | S], Acc, ParamPos, Loc) ->
|
||||
parse_insert_field1(S, [C | Acc], ParamPos, Loc).
|
||||
|
||||
@@ -640,6 +654,23 @@ parse_insert_field1([C | S], Acc, ParamPos, Loc) ->
|
||||
make_sql_insert(Table, ParseRes) ->
|
||||
make_sql_query(make_sql_upsert_insert(Table, ParseRes)).
|
||||
|
||||
make_schema_check(Tree, Tree) ->
|
||||
Tree;
|
||||
make_schema_check(New, Old) ->
|
||||
erl_syntax:case_expr(
|
||||
erl_syntax:application(
|
||||
erl_syntax:atom(ejabberd_sql),
|
||||
erl_syntax:atom(use_new_schema),
|
||||
[]),
|
||||
[erl_syntax:clause(
|
||||
[erl_syntax:abstract(true)],
|
||||
none,
|
||||
[New]),
|
||||
erl_syntax:clause(
|
||||
[erl_syntax:abstract(false)],
|
||||
none,
|
||||
[Old])]).
|
||||
|
||||
|
||||
concat_states(States) ->
|
||||
lists:foldr(
|
||||
@@ -711,26 +742,10 @@ set_pos(Tree, Pos) ->
|
||||
end, Tree).
|
||||
|
||||
filter_upsert_sh(Table, ParseRes) ->
|
||||
case ?USE_NEW_SCHEMA of
|
||||
true ->
|
||||
{ParseRes, []};
|
||||
false ->
|
||||
lists:foldr(
|
||||
fun({Field, _Match, ST} = P, {Acc, Vars}) ->
|
||||
if
|
||||
Field /= "server_host" orelse Table == "route" ->
|
||||
{[P | Acc], Vars};
|
||||
true ->
|
||||
{Acc, ST#state.used_vars ++ Vars}
|
||||
end
|
||||
end, {[], []}, ParseRes)
|
||||
end.
|
||||
|
||||
add_unused_vars(Tree, []) ->
|
||||
Tree;
|
||||
add_unused_vars(Tree, Vars) ->
|
||||
erl_syntax:block_expr(
|
||||
lists:map(fun erl_syntax:variable/1, Vars) ++ [Tree]).
|
||||
lists:filter(
|
||||
fun({Field, _Match, _ST}) ->
|
||||
Field /= "server_host" orelse Table == "route"
|
||||
end, ParseRes).
|
||||
|
||||
-ifdef(ENABLE_PT_WARNINGS).
|
||||
|
||||
|
||||
@@ -172,8 +172,10 @@ listen_opt_type(turn_max_permissions) ->
|
||||
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].
|
||||
server_name, backlog].
|
||||
-endif.
|
||||
|
||||
+50
-151
@@ -30,159 +30,58 @@
|
||||
|
||||
-export([start_link/0, init/1]).
|
||||
|
||||
-define(SHUTDOWN_TIMEOUT, timer:seconds(30)).
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
init([]) ->
|
||||
Hooks =
|
||||
{ejabberd_hooks,
|
||||
{ejabberd_hooks, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_hooks]},
|
||||
Cluster = {ejabberd_cluster,
|
||||
{ejabberd_cluster, start_link, []},
|
||||
permanent,
|
||||
5000,
|
||||
worker,
|
||||
[ejabberd_cluster]},
|
||||
S2S =
|
||||
{ejabberd_s2s,
|
||||
{ejabberd_s2s, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_s2s]},
|
||||
Captcha =
|
||||
{ejabberd_captcha,
|
||||
{ejabberd_captcha, start_link, []},
|
||||
permanent,
|
||||
brutal_kill,
|
||||
worker,
|
||||
[ejabberd_captcha]},
|
||||
Listener =
|
||||
{ejabberd_listener,
|
||||
{ejabberd_listener, start_link, []},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_listener]},
|
||||
S2SInSupervisor =
|
||||
{ejabberd_s2s_in_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
[ejabberd_s2s_in_sup, ejabberd_s2s_in]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
S2SOutSupervisor =
|
||||
{ejabberd_s2s_out_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
[ejabberd_s2s_out_sup, ejabberd_s2s_out]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
ServiceSupervisor =
|
||||
{ejabberd_service_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
[ejabberd_service_sup, ejabberd_service]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
IQSupervisor =
|
||||
{ejabberd_iq_sup,
|
||||
{ejabberd_tmp_sup, start_link,
|
||||
[ejabberd_iq_sup, gen_iq_handler]},
|
||||
permanent,
|
||||
infinity,
|
||||
supervisor,
|
||||
[ejabberd_tmp_sup]},
|
||||
BackendSupervisor = {ejabberd_backend_sup,
|
||||
{ejabberd_backend_sup, start_link, []},
|
||||
permanent, infinity, supervisor,
|
||||
[ejabberd_backend_sup]},
|
||||
ACL = {acl, {acl, start_link, []},
|
||||
permanent, 5000, worker, [acl]},
|
||||
Shaper = {shaper, {shaper, start_link, []},
|
||||
permanent, 5000, worker, [shaper]},
|
||||
SQLSupervisor = {ejabberd_rdbms,
|
||||
{ejabberd_rdbms, start_link, []},
|
||||
permanent, infinity, supervisor, [ejabberd_rdbms]},
|
||||
RiakSupervisor = {ejabberd_riak_sup,
|
||||
{ejabberd_riak_sup, start_link, []},
|
||||
permanent, infinity, supervisor, [ejabberd_riak_sup]},
|
||||
RedisSupervisor = {ejabberd_redis_sup,
|
||||
{ejabberd_redis_sup, start_link, []},
|
||||
permanent, infinity, supervisor, [ejabberd_redis_sup]},
|
||||
Router = {ejabberd_router, {ejabberd_router, start_link, []},
|
||||
permanent, 5000, worker, [ejabberd_router]},
|
||||
RouterMulticast = {ejabberd_router_multicast,
|
||||
{ejabberd_router_multicast, start_link, []},
|
||||
permanent, 5000, worker, [ejabberd_router_multicast]},
|
||||
Local = {ejabberd_local, {ejabberd_local, start_link, []},
|
||||
permanent, 5000, worker, [ejabberd_local]},
|
||||
SM = {ejabberd_sm, {ejabberd_sm, start_link, []},
|
||||
permanent, 5000, worker, [ejabberd_sm]},
|
||||
GenModSupervisor = {ejabberd_gen_mod_sup, {gen_mod, start_link, []},
|
||||
permanent, infinity, supervisor, [gen_mod]},
|
||||
ExtMod = {ext_mod, {ext_mod, start_link, []},
|
||||
permanent, 5000, worker, [ext_mod]},
|
||||
Auth = {ejabberd_auth, {ejabberd_auth, start_link, []},
|
||||
permanent, 5000, worker, [ejabberd_auth]},
|
||||
OAuth = {ejabberd_oauth, {ejabberd_oauth, start_link, []},
|
||||
permanent, 5000, worker, [ejabberd_oauth]},
|
||||
Translation = {translate, {translate, start_link, []},
|
||||
permanent, 5000, worker, [translate]},
|
||||
AccessPerms = {ejabberd_access_permissions,
|
||||
{ejabberd_access_permissions, start_link, []},
|
||||
permanent, 5000, worker, [ejabberd_access_permissions]},
|
||||
Ctl = {ejabberd_ctl, {ejabberd_ctl, start_link, []},
|
||||
permanent, 5000, worker, [ejabberd_ctl]},
|
||||
Commands = {ejabberd_commands, {ejabberd_commands, start_link, []},
|
||||
permanent, 5000, worker, [ejabberd_commands]},
|
||||
Admin = {ejabberd_admin, {ejabberd_admin, start_link, []},
|
||||
permanent, 5000, worker, [ejabberd_admin]},
|
||||
CyrSASL = {cyrsasl, {cyrsasl, start_link, []},
|
||||
permanent, 5000, worker, [cyrsasl]},
|
||||
PKIX = {ejabberd_pkix, {ejabberd_pkix, start_link, []},
|
||||
permanent, 5000, worker, [ejabberd_pkix]},
|
||||
ACME = {ejabberd_acme, {ejabberd_acme, start_link, []},
|
||||
permanent, 5000, worker, [ejabberd_acme]},
|
||||
IQ = {ejabberd_iq, {ejabberd_iq, start_link, []},
|
||||
permanent, 5000, worker, [ejabberd_iq]},
|
||||
{ok, {{one_for_one, 10, 1},
|
||||
[Hooks,
|
||||
Cluster,
|
||||
CyrSASL,
|
||||
Translation,
|
||||
AccessPerms,
|
||||
Ctl,
|
||||
Commands,
|
||||
Admin,
|
||||
PKIX,
|
||||
ACME,
|
||||
Listener,
|
||||
S2S,
|
||||
Captcha,
|
||||
S2SInSupervisor,
|
||||
S2SOutSupervisor,
|
||||
ServiceSupervisor,
|
||||
IQSupervisor,
|
||||
ACL,
|
||||
Shaper,
|
||||
BackendSupervisor,
|
||||
SQLSupervisor,
|
||||
RiakSupervisor,
|
||||
RedisSupervisor,
|
||||
IQ,
|
||||
Router,
|
||||
RouterMulticast,
|
||||
Local,
|
||||
SM,
|
||||
ExtMod,
|
||||
GenModSupervisor,
|
||||
Auth,
|
||||
OAuth]}}.
|
||||
[worker(ejabberd_hooks),
|
||||
worker(ejabberd_cluster),
|
||||
worker(cyrsasl),
|
||||
worker(translate),
|
||||
worker(ejabberd_access_permissions),
|
||||
worker(ejabberd_ctl),
|
||||
worker(ejabberd_commands),
|
||||
worker(ejabberd_admin),
|
||||
worker(ejabberd_pkix),
|
||||
worker(ejabberd_acme),
|
||||
supervisor(ejabberd_listener),
|
||||
worker(ejabberd_s2s),
|
||||
simple_supervisor(ejabberd_s2s_in),
|
||||
simple_supervisor(ejabberd_s2s_out),
|
||||
simple_supervisor(ejabberd_service),
|
||||
worker(acl),
|
||||
worker(shaper),
|
||||
supervisor(ejabberd_backend_sup),
|
||||
supervisor(ejabberd_rdbms),
|
||||
supervisor(ejabberd_riak_sup),
|
||||
supervisor(ejabberd_redis_sup),
|
||||
worker(ejabberd_iq),
|
||||
worker(ejabberd_router),
|
||||
worker(ejabberd_router_multicast),
|
||||
worker(ejabberd_local),
|
||||
worker(ejabberd_sm),
|
||||
worker(ejabberd_captcha),
|
||||
worker(ext_mod),
|
||||
supervisor(ejabberd_gen_mod_sup, gen_mod),
|
||||
worker(ejabberd_auth),
|
||||
worker(ejabberd_oauth)]}}.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
worker(Mod) ->
|
||||
{Mod, {Mod, start_link, []}, permanent, ?SHUTDOWN_TIMEOUT, worker, [Mod]}.
|
||||
|
||||
supervisor(Mod) ->
|
||||
supervisor(Mod, Mod).
|
||||
|
||||
supervisor(Name, Mod) ->
|
||||
{Name, {Mod, start_link, []}, permanent, infinity, supervisor, [Mod]}.
|
||||
|
||||
simple_supervisor(Mod) ->
|
||||
Name = list_to_atom(atom_to_list(Mod) ++ "_sup"),
|
||||
{Name, {ejabberd_tmp_sup, start_link, [Name, Mod]},
|
||||
permanent, infinity, supervisor, [ejabberd_tmp_sup]}.
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
%%-include("logger.hrl").
|
||||
|
||||
-define(CHECK_INTERVAL, timer:seconds(30)).
|
||||
-define(DISK_FULL_THRES, 0.99).
|
||||
|
||||
-record(state, {tref :: reference(),
|
||||
mref :: reference()}).
|
||||
@@ -67,8 +66,7 @@ start() ->
|
||||
application:set_env(os_mon, start_cpu_sup, false),
|
||||
application:set_env(os_mon, start_os_sup, false),
|
||||
application:set_env(os_mon, start_memsup, true),
|
||||
application:set_env(os_mon, start_disksup, true),
|
||||
application:set_env(os_mon, disk_almost_full_threshold, ?DISK_FULL_THRES),
|
||||
application:set_env(os_mon, start_disksup, false),
|
||||
ejabberd:start_app(os_mon).
|
||||
|
||||
excluded_apps() ->
|
||||
@@ -81,16 +79,10 @@ init([]) ->
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_event({set_alarm, {system_memory_high_watermark, _}}, State) ->
|
||||
error_logger:warning_msg(
|
||||
"More than 80% of OS memory is allocated, "
|
||||
"starting OOM watchdog", []),
|
||||
handle_overload(State),
|
||||
{ok, restart_timer(State)};
|
||||
handle_event({clear_alarm, system_memory_high_watermark}, State) ->
|
||||
cancel_timer(State#state.tref),
|
||||
error_logger:info_msg(
|
||||
"Memory consumption is back to normal, "
|
||||
"stopping OOM watchdog", []),
|
||||
{ok, State#state{tref = undefined}};
|
||||
handle_event({set_alarm, {process_memory_high_watermark, Pid}}, State) ->
|
||||
case proc_stat(Pid, get_app_pids()) of
|
||||
@@ -105,12 +97,6 @@ handle_event({set_alarm, {process_memory_high_watermark, Pid}}, State) ->
|
||||
end;
|
||||
handle_event({clear_alarm, process_memory_high_watermark}, State) ->
|
||||
{ok, State};
|
||||
handle_event({set_alarm, {{disk_almost_full, MountPoint}, _}}, State) ->
|
||||
error_logger:warning_msg("Disk is almost full on ~p", [MountPoint]),
|
||||
{ok, State};
|
||||
handle_event({clear_alarm, {disk_almost_full, MountPoint}}, State) ->
|
||||
error_logger:info_msg("Disk usage is back to normal on ~p", [MountPoint]),
|
||||
{ok, State};
|
||||
handle_event(Event, State) ->
|
||||
error_logger:warning_msg("unexpected event: ~p", [Event]),
|
||||
{ok, State}.
|
||||
|
||||
@@ -74,27 +74,15 @@ get_acl_rule([<<"vhosts">>], _) ->
|
||||
%% The pages of a vhost are only accesible if the user is admin of that vhost:
|
||||
get_acl_rule([<<"server">>, VHost | _RPath], Method)
|
||||
when Method =:= 'GET' orelse Method =:= 'HEAD' ->
|
||||
AC = gen_mod:get_module_opt(VHost, ejabberd_web_admin,
|
||||
access, configure),
|
||||
ACR = gen_mod:get_module_opt(VHost, ejabberd_web_admin,
|
||||
access_readonly, webadmin_view),
|
||||
{VHost, [AC, ACR]};
|
||||
{VHost, [configure, webadmin_view]};
|
||||
get_acl_rule([<<"server">>, VHost | _RPath], 'POST') ->
|
||||
AC = gen_mod:get_module_opt(VHost, ejabberd_web_admin,
|
||||
access, configure),
|
||||
{VHost, [AC]};
|
||||
{VHost, [configure]};
|
||||
%% Default rule: only global admins can access any other random page
|
||||
get_acl_rule(_RPath, Method)
|
||||
when Method =:= 'GET' orelse Method =:= 'HEAD' ->
|
||||
AC = gen_mod:get_module_opt(global, ejabberd_web_admin,
|
||||
access, configure),
|
||||
ACR = gen_mod:get_module_opt(global, ejabberd_web_admin,
|
||||
access_readonly, webadmin_view),
|
||||
{global, [AC, ACR]};
|
||||
{global, [configure, webadmin_view]};
|
||||
get_acl_rule(_RPath, 'POST') ->
|
||||
AC = gen_mod:get_module_opt(global, ejabberd_web_admin,
|
||||
access, configure),
|
||||
{global, [AC]}.
|
||||
{global, [configure]}.
|
||||
|
||||
%%%==================================
|
||||
%%%% Menu Items Access
|
||||
@@ -275,7 +263,7 @@ get_auth_account(HostOfRule, AccessRule, User, Server,
|
||||
case ejabberd_auth:check_password(User, <<"">>, Server, Pass) of
|
||||
true ->
|
||||
case acl:any_rules_allowed(HostOfRule, AccessRule,
|
||||
jid:make(User, Server))
|
||||
jid:make(User, Server))
|
||||
of
|
||||
false -> {unauthorized, <<"unprivileged-account">>};
|
||||
true -> {ok, {User, Server}}
|
||||
@@ -1252,7 +1240,7 @@ list_given_users(Host, Users, Prefix, Lang, URLFunc) ->
|
||||
?XE(<<"tr">>,
|
||||
[?XE(<<"td">>,
|
||||
[?AC((URLFunc({user, Prefix,
|
||||
ejabberd_http:url_encode(User),
|
||||
misc:url_encode(User),
|
||||
Server})),
|
||||
(us_to_list(US)))]),
|
||||
?XE(<<"td">>, FQueueLen),
|
||||
@@ -1274,7 +1262,7 @@ get_offlinemsg_module(Server) ->
|
||||
end.
|
||||
|
||||
get_lastactivity_menuitem_list(Server) ->
|
||||
case gen_mod:db_type(Server, mod_last) of
|
||||
case gen_mod:get_module_opt(Server, mod_last, db_type) of
|
||||
mnesia -> [{<<"last-activity">>, <<"Last Activity">>}];
|
||||
_ -> []
|
||||
end.
|
||||
@@ -1331,7 +1319,7 @@ list_online_users(Host, _Lang) ->
|
||||
SUsers = lists:usort(Users),
|
||||
lists:flatmap(fun ({_S, U} = SU) ->
|
||||
[?AC(<<"../user/",
|
||||
(ejabberd_http:url_encode(U))/binary, "/">>,
|
||||
(misc:url_encode(U))/binary, "/">>,
|
||||
(su_to_list(SU))),
|
||||
?BR]
|
||||
end,
|
||||
|
||||
@@ -581,5 +581,9 @@ listen_opt_type(maxsessions) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(timeout) ->
|
||||
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_commands, maxsessions, timeout].
|
||||
[access_commands, maxsessions, timeout, backlog, inet, inet6].
|
||||
|
||||
+16
-5
@@ -1046,8 +1046,6 @@ polish([], Res, Ref) -> {Res, Ref}.
|
||||
%%-----------------------------------------------------------------------
|
||||
connect_bind(S) ->
|
||||
Host = next_host(S#eldap.host, S#eldap.hosts),
|
||||
?INFO_MSG("LDAP connection on ~s:~p",
|
||||
[Host, S#eldap.port]),
|
||||
Opts = if S#eldap.tls == tls ->
|
||||
[{packet, asn1}, {active, true}, {keepalive, true},
|
||||
binary
|
||||
@@ -1056,6 +1054,8 @@ connect_bind(S) ->
|
||||
[{packet, asn1}, {active, true}, {keepalive, true},
|
||||
{send_timeout, ?SEND_TIMEOUT}, binary]
|
||||
end,
|
||||
?DEBUG("Connecting to LDAP server at ~s:~p with options ~p",
|
||||
[Host, S#eldap.port, Opts]),
|
||||
HostS = binary_to_list(Host),
|
||||
SocketData = case S#eldap.tls of
|
||||
tls ->
|
||||
@@ -1080,9 +1080,8 @@ connect_bind(S) ->
|
||||
{ok, connecting, NewS#eldap{host = Host}}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("LDAP connection failed:~n** Server: "
|
||||
"~s:~p~n** Reason: ~p~n** Socket options: ~p",
|
||||
[Host, S#eldap.port, Reason, Opts]),
|
||||
?ERROR_MSG("LDAP connection to ~s:~b failed: ~s",
|
||||
[Host, S#eldap.port, format_error(SockMod, Reason)]),
|
||||
NewS = close_and_retry(S),
|
||||
{ok, connecting, NewS#eldap{host = Host}}
|
||||
end.
|
||||
@@ -1121,3 +1120,15 @@ bump_id(#eldap{id = Id})
|
||||
when Id > (?MAX_TRANSACTION_ID) ->
|
||||
?MIN_TRANSACTION_ID;
|
||||
bump_id(#eldap{id = Id}) -> Id + 1.
|
||||
|
||||
format_error(SockMod, Reason) ->
|
||||
Txt = case SockMod of
|
||||
ssl -> ssl:format_error(Reason);
|
||||
gen_tcp -> inet:format_error(Reason)
|
||||
end,
|
||||
case Txt of
|
||||
"unknown POSIX error" ->
|
||||
lists:flatten(io_lib:format("~p", [Reason]));
|
||||
_ ->
|
||||
Txt
|
||||
end.
|
||||
|
||||
@@ -64,7 +64,7 @@ parse(L) ->
|
||||
%%% RFC2254_Filter = RegExp = Value = string(),
|
||||
%%% N = integer().
|
||||
%%%
|
||||
%%% Description: The same as parse/1, but substitutes N or all occurences
|
||||
%%% Description: The same as parse/1, but substitutes N or all occurrences
|
||||
%%% of RegExp with Value *after* parsing.
|
||||
%%%
|
||||
%%% Example:
|
||||
|
||||
+45
-10
@@ -31,7 +31,8 @@
|
||||
-export([generate_subfilter/1, find_ldap_attrs/2, check_filter/1,
|
||||
get_ldap_attr/2, get_user_part/2, make_filter/2,
|
||||
get_state/2, case_insensitive_match/2, get_config/2,
|
||||
decode_octet_string/3, uids_domain_subst/2, opt_type/1]).
|
||||
decode_octet_string/3, uids_domain_subst/2, opt_type/1,
|
||||
options/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -180,12 +181,16 @@ get_config(Host, Opts) ->
|
||||
TLSCertFile = get_opt(ldap_tls_certfile, Host, Opts),
|
||||
TLSCAFile = get_opt(ldap_tls_cacertfile, Host, Opts),
|
||||
TLSDepth = get_opt(ldap_tls_depth, Host, Opts),
|
||||
Port = get_opt(ldap_port, Host, Opts,
|
||||
Port = case get_opt(ldap_port, Host, Opts) of
|
||||
undefined ->
|
||||
case Encrypt of
|
||||
tls -> ?LDAPS_PORT;
|
||||
starttls -> ?LDAP_PORT;
|
||||
_ -> ?LDAP_PORT
|
||||
end),
|
||||
end;
|
||||
P ->
|
||||
P
|
||||
end,
|
||||
RootDN = get_opt(ldap_rootdn, Host, Opts, <<"">>),
|
||||
Password = get_opt(ldap_password, Host, Opts, <<"">>),
|
||||
Base = get_opt(ldap_base, Host, Opts, <<"">>),
|
||||
@@ -348,7 +353,12 @@ collect_parts_bit([],Acc,Uacc) ->
|
||||
(ldap_uids) -> fun((uids()) -> uids());
|
||||
(atom()) -> [atom()].
|
||||
opt_type(deref_aliases) ->
|
||||
opt_type(ldap_deref_aliases);
|
||||
fun(unspecified) -> unspecified;
|
||||
(never) -> never;
|
||||
(searching) -> searching;
|
||||
(finding) -> finding;
|
||||
(always) -> always
|
||||
end;
|
||||
opt_type(ldap_backups) ->
|
||||
fun (L) -> [iolist_to_binary(H) || H <- L] end;
|
||||
opt_type(ldap_base) -> fun iolist_to_binary/1;
|
||||
@@ -365,25 +375,33 @@ opt_type(ldap_encrypt) ->
|
||||
end;
|
||||
opt_type(ldap_password) -> fun iolist_to_binary/1;
|
||||
opt_type(ldap_port) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
fun(undefined) -> undefined;
|
||||
(I) when is_integer(I), I > 0 -> I
|
||||
end;
|
||||
opt_type(ldap_rootdn) -> fun iolist_to_binary/1;
|
||||
opt_type(ldap_servers) ->
|
||||
fun (L) -> [iolist_to_binary(H) || H <- L] end;
|
||||
opt_type(ldap_tls_certfile) ->
|
||||
fun(S) ->
|
||||
binary_to_list(ejabberd_pkix:try_certfile(S))
|
||||
fun(undefined) -> undefined;
|
||||
(S) -> binary_to_list(ejabberd_pkix:try_certfile(S))
|
||||
end;
|
||||
opt_type(ldap_tls_cacertfile) ->
|
||||
fun(S) -> binary_to_list(misc:try_read_file(S)) end;
|
||||
fun(undefined) -> undefined;
|
||||
(S) -> binary_to_list(misc:try_read_file(S))
|
||||
end;
|
||||
opt_type(ldap_tls_depth) ->
|
||||
fun (I) when is_integer(I), I >= 0 -> I end;
|
||||
fun(undefined) -> undefined;
|
||||
(I) when is_integer(I), I >= 0 -> I
|
||||
end;
|
||||
opt_type(ldap_tls_verify) ->
|
||||
fun (hard) -> hard;
|
||||
(soft) -> soft;
|
||||
(false) -> false
|
||||
end;
|
||||
opt_type(ldap_filter) ->
|
||||
fun check_filter/1;
|
||||
fun(<<"">>) -> <<"">>;
|
||||
(F) -> check_filter(F)
|
||||
end;
|
||||
opt_type(ldap_uids) ->
|
||||
fun (Us) ->
|
||||
lists:map(fun ({U, P}) ->
|
||||
@@ -399,3 +417,20 @@ opt_type(_) ->
|
||||
ldap_port, ldap_rootdn, ldap_servers, ldap_filter,
|
||||
ldap_tls_certfile, ldap_tls_cacertfile, ldap_tls_depth,
|
||||
ldap_tls_verify].
|
||||
|
||||
options(_) ->
|
||||
[{deref_aliases, unspecified},
|
||||
{ldap_backups, []},
|
||||
{ldap_base, <<"">>},
|
||||
{ldap_uids, [{<<"uid">>, <<"%u">>}]},
|
||||
{ldap_deref_aliases, never},
|
||||
{ldap_encrypt, none},
|
||||
{ldap_password, <<"">>},
|
||||
{ldap_port, undefined},
|
||||
{ldap_rootdn, <<"">>},
|
||||
{ldap_servers, [<<"localhost">>]},
|
||||
{ldap_filter, <<"">>},
|
||||
{ldap_tls_certfile, undefined},
|
||||
{ldap_tls_cacertfile, undefined},
|
||||
{ldap_tls_depth, undefined},
|
||||
{ldap_tls_verify, false}].
|
||||
|
||||
+13
-13
@@ -56,7 +56,8 @@ init([]) ->
|
||||
process_flag(trap_exit, true),
|
||||
[code:add_patha(module_ebin_dir(Module))
|
||||
|| {Module, _} <- installed()],
|
||||
p1_http:start(),
|
||||
application:start(inets),
|
||||
inets:start(httpc, [{profile, ext_mod}]),
|
||||
ejabberd_commands:register_commands(get_commands_spec()),
|
||||
{ok, #state{}}.
|
||||
|
||||
@@ -313,23 +314,22 @@ check(Package) when is_binary(Package) ->
|
||||
%% -- archives and variables functions
|
||||
|
||||
geturl(Url) ->
|
||||
geturl(Url, []).
|
||||
geturl(Url, UsrOpts) ->
|
||||
geturl(Url, [], UsrOpts).
|
||||
geturl(Url, Hdrs, UsrOpts) ->
|
||||
Host = case getenv("PROXY_SERVER", "", ":") of
|
||||
[H, Port] -> [{proxy_host, H}, {proxy_port, list_to_integer(Port)}];
|
||||
[H] -> [{proxy_host, H}, {proxy_port, 8080}];
|
||||
_ -> []
|
||||
case getenv("PROXY_SERVER", "", ":") of
|
||||
[H, Port] ->
|
||||
httpc:set_options([{proxy, {{H, list_to_integer(Port)}, []}}], ext_mod);
|
||||
[H] ->
|
||||
httpc:set_options([{proxy, {{H, 8080}, []}}], ext_mod);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
User = case getenv("PROXY_USER", "", [4]) of
|
||||
[U, Pass] -> [{proxy_user, U}, {proxy_password, Pass}];
|
||||
[U, Pass] -> [{proxy_auth, {U, Pass}}];
|
||||
_ -> []
|
||||
end,
|
||||
case p1_http:request(get, Url, Hdrs, [], Host++User++UsrOpts++[{version, "HTTP/1.0"}]) of
|
||||
{ok, 200, Headers, Response} ->
|
||||
case httpc:request(get, {Url, []}, User, [{body_format, binary}], ext_mod) of
|
||||
{ok, {{_, 200, _}, Headers, Response}} ->
|
||||
{ok, Headers, Response};
|
||||
{ok, Code, _Headers, Response} ->
|
||||
{ok, {{_, Code, _}, _Headers, Response}} ->
|
||||
{error, {Code, Response}};
|
||||
{error, Reason} ->
|
||||
{error, Reason}
|
||||
|
||||
+26
-118
@@ -27,110 +27,38 @@
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-ifndef(GEN_SERVER).
|
||||
-define(GEN_SERVER, gen_server).
|
||||
-endif.
|
||||
-behaviour(?GEN_SERVER).
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
%% API
|
||||
-export([start_link/3, add_iq_handler/6,
|
||||
remove_iq_handler/3, stop_iq_handler/3, handle/5,
|
||||
-export([add_iq_handler/5, remove_iq_handler/3, handle/4,
|
||||
process_iq/4, check_type/1, transform_module_options/1,
|
||||
opt_type/1, iqdisc/1]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3]).
|
||||
opt_type/1]).
|
||||
%% Deprecated functions
|
||||
-export([add_iq_handler/6, handle/5, iqdisc/1]).
|
||||
-deprecated([{add_iq_handler, 6}, {handle, 5}, {iqdisc, 1}]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("xmpp.hrl").
|
||||
|
||||
-record(state, {host, module, function}).
|
||||
|
||||
-type component() :: ejabberd_sm | ejabberd_local.
|
||||
-type type() :: no_queue | one_queue | pos_integer() | parallel.
|
||||
-type opts() :: no_queue | {one_queue, pid()} | {queues, [pid()]} | parallel.
|
||||
-export_type([opts/0, type/0]).
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the server
|
||||
%%--------------------------------------------------------------------
|
||||
start_link(Host, Module, Function) ->
|
||||
?GEN_SERVER:start_link(?MODULE, [Host, Module, Function],
|
||||
[]).
|
||||
|
||||
-spec add_iq_handler(module(), binary(), binary(), module(), atom(), type()) -> ok.
|
||||
|
||||
add_iq_handler(Component, Host, NS, Module, Function,
|
||||
Type) ->
|
||||
case Type of
|
||||
no_queue ->
|
||||
Component:register_iq_handler(Host, NS, Module,
|
||||
Function, no_queue);
|
||||
one_queue ->
|
||||
{ok, Pid} = supervisor:start_child(ejabberd_iq_sup,
|
||||
[Host, Module, Function]),
|
||||
Component:register_iq_handler(Host, NS, Module,
|
||||
Function, {one_queue, Pid});
|
||||
N when is_integer(N) ->
|
||||
Pids = lists:map(fun (_) ->
|
||||
{ok, Pid} =
|
||||
supervisor:start_child(ejabberd_iq_sup,
|
||||
[Host, Module,
|
||||
Function]),
|
||||
Pid
|
||||
end,
|
||||
lists:seq(1, N)),
|
||||
Component:register_iq_handler(Host, NS, Module,
|
||||
Function, {queues, Pids});
|
||||
parallel ->
|
||||
Component:register_iq_handler(Host, NS, Module,
|
||||
Function, parallel)
|
||||
end.
|
||||
-spec add_iq_handler(module(), binary(), binary(), module(), atom()) -> ok.
|
||||
add_iq_handler(Component, Host, NS, Module, Function) ->
|
||||
Component:register_iq_handler(Host, NS, Module, Function).
|
||||
|
||||
-spec remove_iq_handler(component(), binary(), binary()) -> ok.
|
||||
|
||||
remove_iq_handler(Component, Host, NS) ->
|
||||
Component:unregister_iq_handler(Host, NS).
|
||||
|
||||
-spec stop_iq_handler(atom(), atom(), [pid()]) -> any().
|
||||
|
||||
stop_iq_handler(_Module, _Function, Opts) ->
|
||||
case Opts of
|
||||
{one_queue, Pid} -> ?GEN_SERVER:call(Pid, stop);
|
||||
{queues, Pids} ->
|
||||
lists:foreach(fun (Pid) ->
|
||||
catch ?GEN_SERVER:call(Pid, stop)
|
||||
end,
|
||||
Pids);
|
||||
_ -> ok
|
||||
end.
|
||||
|
||||
-spec handle(binary(), atom(), atom(), opts(), iq()) -> any().
|
||||
|
||||
handle(Host, Module, Function, Opts, IQ) ->
|
||||
case Opts of
|
||||
no_queue ->
|
||||
process_iq(Host, Module, Function, IQ);
|
||||
{one_queue, Pid} ->
|
||||
Pid ! {process_iq, IQ};
|
||||
{queues, Pids} ->
|
||||
Pid = lists:nth(erlang:phash(p1_time_compat:unique_integer(),
|
||||
length(Pids)), Pids),
|
||||
Pid ! {process_iq, IQ};
|
||||
parallel ->
|
||||
spawn(?MODULE, process_iq, [Host, Module, Function, IQ]);
|
||||
_ -> todo
|
||||
end.
|
||||
-spec handle(binary(), atom(), atom(), iq()) -> any().
|
||||
handle(Host, Module, Function, IQ) ->
|
||||
process_iq(Host, Module, Function, IQ).
|
||||
|
||||
-spec process_iq(binary(), atom(), atom(), iq()) -> any().
|
||||
|
||||
process_iq(_Host, Module, Function, IQ) ->
|
||||
try
|
||||
ResIQ = case erlang:function_exported(Module, Function, 1) of
|
||||
@@ -178,15 +106,14 @@ process_iq(Module, Function, From, To, IQ) ->
|
||||
To, From)
|
||||
end.
|
||||
|
||||
-spec check_type(type()) -> type().
|
||||
-spec check_type(any()) -> no_queue.
|
||||
check_type(_Type) ->
|
||||
?WARNING_MSG("Option 'iqdisc' is deprecated and has no effect anymore", []),
|
||||
no_queue.
|
||||
|
||||
check_type(no_queue) -> no_queue;
|
||||
check_type(one_queue) -> one_queue;
|
||||
check_type(N) when is_integer(N), N>0 -> N;
|
||||
check_type(parallel) -> parallel.
|
||||
|
||||
iqdisc(Host) ->
|
||||
ejabberd_config:get_option({iqdisc, Host}, no_queue).
|
||||
-spec iqdisc(binary() | global) -> no_queue.
|
||||
iqdisc(_Host) ->
|
||||
no_queue.
|
||||
|
||||
-spec transform_module_options([{atom(), any()}]) -> [{atom(), any()}].
|
||||
|
||||
@@ -198,37 +125,18 @@ transform_module_options(Opts) ->
|
||||
Opt
|
||||
end, Opts).
|
||||
|
||||
-spec opt_type(iqdisc) -> fun((type()) -> type());
|
||||
-spec opt_type(iqdisc) -> fun((any()) -> no_queue);
|
||||
(atom()) -> [atom()].
|
||||
opt_type(iqdisc) -> fun check_type/1;
|
||||
opt_type(_) -> [iqdisc].
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
%% Deprecated API
|
||||
%%====================================================================
|
||||
-spec add_iq_handler(module(), binary(), binary(), module(), atom(), any()) -> ok.
|
||||
add_iq_handler(Component, Host, NS, Module, Function, _Type) ->
|
||||
add_iq_handler(Component, Host, NS, Module, Function).
|
||||
|
||||
init([Host, Module, Function]) ->
|
||||
{ok,
|
||||
#state{host = Host, module = Module,
|
||||
function = Function}}.
|
||||
|
||||
handle_call(stop, _From, State) ->
|
||||
Reply = ok, {stop, normal, Reply, State}.
|
||||
|
||||
handle_cast(_Msg, State) -> {noreply, State}.
|
||||
|
||||
handle_info({process_iq, IQ},
|
||||
#state{host = Host, module = Module,
|
||||
function = Function} =
|
||||
State) ->
|
||||
process_iq(Host, Module, Function, IQ),
|
||||
{noreply, State};
|
||||
handle_info(_Info, State) -> {noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) -> ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
-spec handle(binary(), atom(), atom(), any(), iq()) -> any().
|
||||
handle(Host, Module, Function, _Opts, IQ) ->
|
||||
handle(Host, Module, Function, IQ).
|
||||
|
||||
+368
-174
@@ -32,20 +32,29 @@
|
||||
|
||||
-export([init/1, start_link/0, start_child/3, start_child/4,
|
||||
stop_child/1, stop_child/2, config_reloaded/0]).
|
||||
-export([start_module/2, start_module/3,
|
||||
stop_module/2, stop_module_keep_config/2,
|
||||
get_opt/2, get_opt/3, get_opt_host/3,
|
||||
get_opt_hosts/3, opt_type/1, is_equal_opt/4,
|
||||
get_module_opt/3, get_module_opt/4, get_module_opt_host/3,
|
||||
-export([start_module/2, stop_module/2, stop_module_keep_config/2,
|
||||
get_opt/2, get_opt_hosts/2, opt_type/1, is_equal_opt/3,
|
||||
get_module_opt/3, get_module_opt_host/3,
|
||||
loaded_modules/1, loaded_modules_with_opts/1,
|
||||
get_hosts/2, get_module_proc/2, is_loaded/2, is_loaded_elsewhere/2,
|
||||
start_modules/0, start_modules/1, stop_modules/0, stop_modules/1,
|
||||
db_mod/2, db_mod/3, ram_db_mod/2, ram_db_mod/3,
|
||||
db_type/2, db_type/3, ram_db_type/2, ram_db_type/3]).
|
||||
is_db_configured/2]).
|
||||
|
||||
%% Deprecated functions
|
||||
-export([get_opt/4, get_module_opt/5]).
|
||||
-deprecated([{get_opt, 4}, {get_module_opt, 5}]).
|
||||
-export([get_opt/3, get_opt/4, get_module_opt/4, get_module_opt/5,
|
||||
get_opt_host/3, get_opt_hosts/3, db_type/2, db_type/3,
|
||||
ram_db_type/2, ram_db_type/3]).
|
||||
-deprecated([{get_opt, 3},
|
||||
{get_opt, 4},
|
||||
{get_opt_host, 3},
|
||||
{get_opt_hosts, 3},
|
||||
{get_module_opt, 4},
|
||||
{get_module_opt, 5},
|
||||
{db_type, 2},
|
||||
{db_type, 3},
|
||||
{ram_db_type, 2},
|
||||
{ram_db_type, 3}]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -53,7 +62,8 @@
|
||||
|
||||
-record(ejabberd_module,
|
||||
{module_host = {undefined, <<"">>} :: {atom(), binary()},
|
||||
opts = [] :: opts() | '_' | '$2'}).
|
||||
opts = [] :: opts() | '_' | '$2',
|
||||
order = 0 :: integer()}).
|
||||
|
||||
-type opts() :: [{atom(), any()}].
|
||||
-type db_type() :: atom().
|
||||
@@ -62,9 +72,10 @@
|
||||
-callback stop(binary()) -> any().
|
||||
-callback reload(binary(), opts(), opts()) -> ok | {ok, pid()}.
|
||||
-callback mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
|
||||
-callback mod_options(binary()) -> opts().
|
||||
-callback depends(binary(), opts()) -> [{module(), hard | soft}].
|
||||
|
||||
-optional_callbacks([reload/3]).
|
||||
-optional_callbacks([mod_opt_type/1, reload/3]).
|
||||
|
||||
-export_type([opts/0]).
|
||||
-export_type([db_type/0]).
|
||||
@@ -122,7 +133,7 @@ start_modules() ->
|
||||
end, ?MYHOSTS).
|
||||
|
||||
get_modules_options(Host) ->
|
||||
ejabberd_config:get_option({modules, Host}, []).
|
||||
sort_modules(Host, ejabberd_config:get_option({modules, Host}, [])).
|
||||
|
||||
sort_modules(Host, ModOpts) ->
|
||||
G = digraph:new([acyclic]),
|
||||
@@ -135,15 +146,15 @@ sort_modules(Host, ModOpts) ->
|
||||
case lists:keyfind(DepMod, 1, ModOpts) of
|
||||
false when Type == hard ->
|
||||
ErrTxt = io_lib:format(
|
||||
"failed to load module '~s' "
|
||||
"Failed to load module '~s' "
|
||||
"because it depends on module '~s' "
|
||||
"which is not found in the config",
|
||||
[Mod, DepMod]),
|
||||
?ERROR_MSG(ErrTxt, []),
|
||||
digraph:del_vertex(G, Mod),
|
||||
maybe_halt_ejabberd(ErrTxt);
|
||||
maybe_halt_ejabberd();
|
||||
false when Type == soft ->
|
||||
?WARNING_MSG("module '~s' is recommended for "
|
||||
?WARNING_MSG("Module '~s' is recommended for "
|
||||
"module '~s' but is not found in "
|
||||
"the config",
|
||||
[DepMod, Mod]);
|
||||
@@ -151,7 +162,7 @@ sort_modules(Host, ModOpts) ->
|
||||
digraph:add_vertex(G, DepMod, DepOpts),
|
||||
case digraph:add_edge(G, DepMod, Mod) of
|
||||
{error, {bad_edge, Path}} ->
|
||||
?WARNING_MSG("cyclic dependency detected "
|
||||
?WARNING_MSG("Cyclic dependency detected "
|
||||
"between modules: ~p",
|
||||
[Path]);
|
||||
_ ->
|
||||
@@ -160,17 +171,21 @@ sort_modules(Host, ModOpts) ->
|
||||
end
|
||||
end, Deps)
|
||||
end, ModOpts),
|
||||
Result = [digraph:vertex(G, V) || V <- digraph_utils:topsort(G)],
|
||||
{Result, _} = lists:mapfoldl(
|
||||
fun(V, Order) ->
|
||||
{M, O} = digraph:vertex(G, V),
|
||||
{{M, O, Order}, Order+1}
|
||||
end, 1, digraph_utils:topsort(G)),
|
||||
digraph:delete(G),
|
||||
Result.
|
||||
|
||||
-spec start_modules(binary()) -> ok.
|
||||
|
||||
start_modules(Host) ->
|
||||
Modules = sort_modules(Host, get_modules_options(Host)),
|
||||
Modules = get_modules_options(Host),
|
||||
lists:foreach(
|
||||
fun({Module, Opts}) ->
|
||||
start_module(Host, Module, Opts)
|
||||
fun({Module, Opts, Order}) ->
|
||||
start_module(Host, Module, Opts, Order)
|
||||
end, Modules).
|
||||
|
||||
-spec start_module(binary(), atom()) -> ok | {ok, pid()} | {error, not_found_in_config}.
|
||||
@@ -178,51 +193,64 @@ start_modules(Host) ->
|
||||
start_module(Host, Module) ->
|
||||
Modules = get_modules_options(Host),
|
||||
case lists:keyfind(Module, 1, Modules) of
|
||||
{_, Opts} ->
|
||||
start_module(Host, Module, Opts);
|
||||
{_, Opts, Order} ->
|
||||
start_module(Host, Module, Opts, Order);
|
||||
false ->
|
||||
{error, not_found_in_config}
|
||||
end.
|
||||
|
||||
-spec start_module(binary(), atom(), opts()) -> ok | {ok, pid()}.
|
||||
start_module(Host, Module, Opts) ->
|
||||
start_module(Host, Module, Opts, true).
|
||||
-spec start_module(binary(), atom(), opts(), integer()) -> ok | {ok, pid()}.
|
||||
start_module(Host, Module, Opts, Order) ->
|
||||
start_module(Host, Module, Opts, Order, true).
|
||||
|
||||
-spec start_module(binary(), atom(), opts(), boolean()) -> ok | {ok, pid()}.
|
||||
start_module(Host, Module, Opts0, NeedValidation) ->
|
||||
?DEBUG("loading ~s at ~s", [Module, Host]),
|
||||
Opts = if NeedValidation ->
|
||||
validate_opts(Host, Module, Opts0);
|
||||
true ->
|
||||
Opts0
|
||||
end,
|
||||
store_options(Host, Module, Opts),
|
||||
try case Module:start(Host, Opts) of
|
||||
ok -> ok;
|
||||
{ok, Pid} when is_pid(Pid) -> {ok, Pid};
|
||||
Err -> erlang:error(Err)
|
||||
end
|
||||
catch Class:Reason ->
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
ErrorText =
|
||||
io_lib:format("Problem starting the module ~s for host "
|
||||
"~s ~n options: ~p~n ~p: ~p~n~p",
|
||||
[Module, Host, Opts, Class, Reason,
|
||||
erlang:get_stacktrace()]),
|
||||
?CRITICAL_MSG(ErrorText, []),
|
||||
maybe_halt_ejabberd(ErrorText),
|
||||
erlang:raise(Class, Reason, erlang:get_stacktrace())
|
||||
-spec start_module(binary(), atom(), opts(), integer(), boolean()) -> ok | {ok, pid()}.
|
||||
start_module(Host, Module, Opts0, Order, NeedValidation) ->
|
||||
?DEBUG("Loading ~s at ~s", [Module, Host]),
|
||||
Res = if NeedValidation ->
|
||||
validate_opts(Host, Module, Opts0);
|
||||
true ->
|
||||
{ok, Opts0}
|
||||
end,
|
||||
case Res of
|
||||
{ok, Opts} ->
|
||||
store_options(Host, Module, Opts, Order),
|
||||
try case Module:start(Host, Opts) of
|
||||
ok -> ok;
|
||||
{ok, Pid} when is_pid(Pid) -> {ok, Pid};
|
||||
Err -> erlang:error(Err)
|
||||
end
|
||||
catch Class:Reason ->
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
ErrorText =
|
||||
case Reason == undef andalso
|
||||
code:ensure_loaded(Module) /= {module, Module} of
|
||||
true ->
|
||||
io_lib:format("Failed to load unknown module "
|
||||
"~s for host ~s: make sure "
|
||||
"there is no typo and ~s.beam "
|
||||
"exists inside either ~s or ~s "
|
||||
"directory",
|
||||
[Module, Host, Module,
|
||||
filename:dirname(code:which(?MODULE)),
|
||||
ext_mod:modules_dir()]);
|
||||
false ->
|
||||
io_lib:format("Problem starting the module ~s for host "
|
||||
"~s ~n options: ~p~n ~p: ~p~n~p",
|
||||
[Module, Host, Opts, Class, Reason,
|
||||
erlang:get_stacktrace()])
|
||||
end,
|
||||
?CRITICAL_MSG(ErrorText, []),
|
||||
maybe_halt_ejabberd(),
|
||||
erlang:raise(Class, Reason, erlang:get_stacktrace())
|
||||
end;
|
||||
{error, _ErrorText} ->
|
||||
maybe_halt_ejabberd()
|
||||
end.
|
||||
|
||||
-spec reload_modules(binary()) -> ok.
|
||||
reload_modules(Host) ->
|
||||
NewMods = ejabberd_config:get_option({modules, Host}, []),
|
||||
OldMods = ets:select(
|
||||
ejabberd_modules,
|
||||
ets:fun2ms(
|
||||
fun(#ejabberd_module{module_host = {M, H}, opts = O})
|
||||
when H == Host -> {M, O}
|
||||
end)),
|
||||
NewMods = get_modules_options(Host),
|
||||
OldMods = lists:reverse(loaded_modules_with_opts(Host)),
|
||||
lists:foreach(
|
||||
fun({Mod, _Opts}) ->
|
||||
case lists:keymember(Mod, 1, NewMods) of
|
||||
@@ -233,10 +261,10 @@ reload_modules(Host) ->
|
||||
end
|
||||
end, OldMods),
|
||||
lists:foreach(
|
||||
fun({Mod, Opts}) ->
|
||||
fun({Mod, Opts, Order}) ->
|
||||
case lists:keymember(Mod, 1, OldMods) of
|
||||
false ->
|
||||
start_module(Host, Mod, Opts);
|
||||
start_module(Host, Mod, Opts, Order);
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
@@ -244,24 +272,26 @@ reload_modules(Host) ->
|
||||
lists:foreach(
|
||||
fun({Mod, OldOpts}) ->
|
||||
case lists:keyfind(Mod, 1, NewMods) of
|
||||
{_, NewOpts0} ->
|
||||
{_, NewOpts0, Order} ->
|
||||
case validate_opts(Host, Mod, NewOpts0) of
|
||||
OldOpts ->
|
||||
{ok, OldOpts} ->
|
||||
ok;
|
||||
NewOpts ->
|
||||
reload_module(Host, Mod, NewOpts, OldOpts)
|
||||
{ok, NewOpts} ->
|
||||
reload_module(Host, Mod, NewOpts, OldOpts, Order);
|
||||
{error, _} ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end, OldMods).
|
||||
|
||||
-spec reload_module(binary(), module(), opts(), opts()) -> ok | {ok, pid()}.
|
||||
reload_module(Host, Module, NewOpts, OldOpts) ->
|
||||
-spec reload_module(binary(), module(), opts(), opts(), integer()) -> ok | {ok, pid()}.
|
||||
reload_module(Host, Module, NewOpts, OldOpts, Order) ->
|
||||
case erlang:function_exported(Module, reload, 3) of
|
||||
true ->
|
||||
?DEBUG("reloading ~s at ~s", [Module, Host]),
|
||||
store_options(Host, Module, NewOpts),
|
||||
?DEBUG("Reloading ~s at ~s", [Module, Host]),
|
||||
store_options(Host, Module, NewOpts, Order),
|
||||
try case Module:reload(Host, NewOpts, OldOpts) of
|
||||
ok -> ok;
|
||||
{ok, Pid} when is_pid(Pid) -> {ok, Pid};
|
||||
@@ -269,33 +299,32 @@ reload_module(Host, Module, NewOpts, OldOpts) ->
|
||||
end
|
||||
catch Class:Reason ->
|
||||
StackTrace = erlang:get_stacktrace(),
|
||||
?CRITICAL_MSG("failed to reload module ~s at ~s:~n"
|
||||
?CRITICAL_MSG("Failed to reload module ~s at ~s:~n"
|
||||
"** Reason = ~p",
|
||||
[Module, Host,
|
||||
{Class, {Reason, StackTrace}}]),
|
||||
erlang:raise(Class, Reason, StackTrace)
|
||||
end;
|
||||
false ->
|
||||
?WARNING_MSG("module ~s doesn't support reloading "
|
||||
?WARNING_MSG("Module ~s doesn't support reloading "
|
||||
"and will be restarted", [Module]),
|
||||
stop_module(Host, Module),
|
||||
start_module(Host, Module, NewOpts, false)
|
||||
start_module(Host, Module, NewOpts, Order, false)
|
||||
end.
|
||||
|
||||
-spec store_options(binary(), module(), opts()) -> true.
|
||||
store_options(Host, Module, Opts) ->
|
||||
-spec store_options(binary(), module(), opts(), integer()) -> true.
|
||||
store_options(Host, Module, Opts, Order) ->
|
||||
ets:insert(ejabberd_modules,
|
||||
#ejabberd_module{module_host = {Module, Host},
|
||||
opts = Opts}).
|
||||
opts = Opts, order = Order}).
|
||||
|
||||
maybe_halt_ejabberd(ErrorText) ->
|
||||
maybe_halt_ejabberd() ->
|
||||
case is_app_running(ejabberd) of
|
||||
false ->
|
||||
?CRITICAL_MSG("ejabberd initialization was aborted "
|
||||
"because a module start failed.",
|
||||
[]),
|
||||
timer:sleep(3000),
|
||||
erlang:halt(string:substr(lists:flatten(ErrorText), 1, 199));
|
||||
ejabberd:halt();
|
||||
true ->
|
||||
ok
|
||||
end.
|
||||
@@ -316,7 +345,7 @@ stop_modules() ->
|
||||
-spec stop_modules(binary()) -> ok.
|
||||
|
||||
stop_modules(Host) ->
|
||||
Modules = get_modules_options(Host),
|
||||
Modules = lists:reverse(loaded_modules_with_opts(Host)),
|
||||
lists:foreach(
|
||||
fun({Module, _Args}) ->
|
||||
stop_module_keep_config(Host, Module)
|
||||
@@ -325,7 +354,6 @@ stop_modules(Host) ->
|
||||
-spec stop_module(binary(), atom()) -> error | {aborted, any()} | {atomic, any()}.
|
||||
|
||||
stop_module(Host, Module) ->
|
||||
?DEBUG("stopping ~s at ~s", [Module, Host]),
|
||||
case stop_module_keep_config(Host, Module) of
|
||||
error -> error;
|
||||
ok -> ok
|
||||
@@ -334,6 +362,7 @@ stop_module(Host, Module) ->
|
||||
-spec stop_module_keep_config(binary(), atom()) -> error | ok.
|
||||
|
||||
stop_module_keep_config(Host, Module) ->
|
||||
?DEBUG("Stopping ~s at ~s", [Module, Host]),
|
||||
case catch Module:stop(Host) of
|
||||
{'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]), error;
|
||||
{wait, ProcList} when is_list(ProcList) ->
|
||||
@@ -367,21 +396,19 @@ wait_for_stop1(MonitorReference) ->
|
||||
|
||||
-type check_fun() :: fun((any()) -> any()) | {module(), atom()}.
|
||||
|
||||
-spec get_opt(atom() | {atom(), binary() | global}, opts()) -> any().
|
||||
-spec get_opt(atom(), opts()) -> any().
|
||||
get_opt(Opt, Opts) ->
|
||||
get_opt(Opt, Opts, undefined).
|
||||
case lists:keyfind(Opt, 1, Opts) of
|
||||
{_, Val} -> Val;
|
||||
false ->
|
||||
?DEBUG("Attempt to read unspecified option ~s", [Opt]),
|
||||
undefined
|
||||
end.
|
||||
|
||||
-spec get_opt(atom() | {atom(), binary()|global}, opts(), check_fun() | any()) -> any().
|
||||
-spec get_opt(atom(), opts(), check_fun() | any()) -> any().
|
||||
|
||||
get_opt(Opt, Opts, F) when is_function(F) ->
|
||||
get_opt(Opt, Opts, undefined);
|
||||
get_opt({Opt, Host}, Opts, Default) ->
|
||||
case lists:keyfind(Opt, 1, Opts) of
|
||||
false ->
|
||||
ejabberd_config:get_option({Opt, Host}, Default);
|
||||
{_, Val} ->
|
||||
Val
|
||||
end;
|
||||
get_opt(Opt, Opts, Default) ->
|
||||
case lists:keyfind(Opt, 1, Opts) of
|
||||
false ->
|
||||
@@ -442,125 +469,255 @@ get_opt_host(Host, Opts, Default) ->
|
||||
Val = get_opt(host, Opts, Default),
|
||||
ejabberd_regexp:greplace(Val, <<"@HOST@">>, Host).
|
||||
|
||||
-spec get_opt_hosts(binary(), opts(), binary()) -> [binary()].
|
||||
-spec get_opt_hosts(binary(), opts()) -> [binary()].
|
||||
get_opt_hosts(Host, Opts) ->
|
||||
get_opt_hosts(Host, Opts, undefined).
|
||||
|
||||
-spec get_opt_hosts(binary(), opts(), binary()) -> [binary()].
|
||||
get_opt_hosts(Host, Opts, Default) ->
|
||||
Vals = case get_opt(host, Opts, undefined) of
|
||||
undefined ->
|
||||
case get_opt(hosts, Opts, []) of
|
||||
[] -> [Default];
|
||||
L -> L
|
||||
end;
|
||||
Val ->
|
||||
[Val]
|
||||
Vals = case get_opt(hosts, Opts) of
|
||||
L when L == [] orelse L == undefined ->
|
||||
case get_opt(host, Opts) of
|
||||
undefined -> [Default];
|
||||
H -> [H]
|
||||
end;
|
||||
L ->
|
||||
L
|
||||
end,
|
||||
[ejabberd_regexp:greplace(V, <<"@HOST@">>, Host) || V <- Vals].
|
||||
|
||||
-spec get_validators(binary(), module(), opts()) -> dict:dict() | undef.
|
||||
get_validators(Host, Module, Opts) ->
|
||||
try Module:mod_opt_type('') of
|
||||
L ->
|
||||
SubMods1 = case lists:member(db_type, L) of
|
||||
true -> [db_mod(Host, Opts, Module)];
|
||||
false -> []
|
||||
end,
|
||||
SubMods2 = case lists:member(ram_db_type, L) of
|
||||
true -> [ram_db_mod(Host, Opts, Module)];
|
||||
false -> []
|
||||
end,
|
||||
lists:foldl(
|
||||
fun(Mod, D) ->
|
||||
try Mod:mod_opt_type('') of
|
||||
Os ->
|
||||
lists:foldl(
|
||||
fun({Opt, SubOpt} = O, Acc) ->
|
||||
SubF = Mod:mod_opt_type(O),
|
||||
F = case Mod:mod_opt_type(Opt) of
|
||||
F1 when is_function(F1) ->
|
||||
F1;
|
||||
_ ->
|
||||
fun(X) -> X end
|
||||
end,
|
||||
dict:append_list(
|
||||
Opt, [F, {SubOpt, [SubF]}], Acc);
|
||||
(O, Acc) ->
|
||||
F = Mod:mod_opt_type(O),
|
||||
dict:store(O, [F], Acc)
|
||||
end, D, Os)
|
||||
catch _:undef ->
|
||||
D
|
||||
end
|
||||
end, dict:new(), [Module|SubMods1 ++ SubMods2])
|
||||
catch _:undef ->
|
||||
?WARNING_MSG("module '~s' doesn't export mod_opt_type/1",
|
||||
[Module]),
|
||||
undef
|
||||
-spec get_validators(binary(), {module(), [module()]}) -> list() | undef.
|
||||
get_validators(Host, {Module, SubMods}) ->
|
||||
Validators =
|
||||
dict:to_list(
|
||||
lists:foldl(
|
||||
fun(Mod, D) ->
|
||||
try list_known_opts(Host, Mod) of
|
||||
Os ->
|
||||
lists:foldl(
|
||||
fun({Opt, SubOpt} = O, Acc) ->
|
||||
SubF = Mod:mod_opt_type(O),
|
||||
F = try Mod:mod_opt_type(Opt)
|
||||
catch _:_ -> fun(X) -> X end
|
||||
end,
|
||||
dict:append_list(
|
||||
Opt, [F, {SubOpt, [SubF]}], Acc);
|
||||
(O, Acc) ->
|
||||
F = Mod:mod_opt_type(O),
|
||||
dict:store(O, [F], Acc)
|
||||
end, D, Os)
|
||||
catch _:undef ->
|
||||
D
|
||||
end
|
||||
end, dict:new(), [Module|SubMods])),
|
||||
case Validators of
|
||||
[] ->
|
||||
case have_validators(Module) of
|
||||
false ->
|
||||
case code:ensure_loaded(Module) of
|
||||
{module, _} ->
|
||||
?WARNING_MSG("Third-party module '~s' doesn't export "
|
||||
"options validator; consider to upgrade "
|
||||
"the module", [Module]);
|
||||
_ ->
|
||||
%% Silently ignore this, the error will be
|
||||
%% generated later
|
||||
ok
|
||||
end,
|
||||
undef;
|
||||
true ->
|
||||
[]
|
||||
end;
|
||||
_ ->
|
||||
Validators
|
||||
end.
|
||||
|
||||
-spec validate_opts(binary(), module(), opts()) -> opts().
|
||||
validate_opts(Host, Module, Opts) ->
|
||||
case get_validators(Host, Module, Opts) of
|
||||
undef ->
|
||||
Opts;
|
||||
Validators ->
|
||||
validate_opts(Host, Module, Opts, dict:to_list(Validators))
|
||||
-spec have_validators(module()) -> boolean().
|
||||
have_validators(Module) ->
|
||||
erlang:function_exported(Module, mod_options, 1)
|
||||
orelse erlang:function_exported(Module, mod_opt_type, 1).
|
||||
|
||||
-spec validate_opts(binary(), module(), opts()) -> {ok, opts()} | {error, string()}.
|
||||
validate_opts(Host, Module, Opts0) ->
|
||||
SubMods = get_submodules(Host, Module, Opts0),
|
||||
DefaultOpts = lists:flatmap(
|
||||
fun(M) ->
|
||||
try M:mod_options(Host)
|
||||
catch _:undef -> []
|
||||
end
|
||||
end, [Module|SubMods]),
|
||||
Required = lists:filter(fun is_atom/1, DefaultOpts),
|
||||
try
|
||||
Opts = merge_opts(Opts0, DefaultOpts, Module),
|
||||
{ok, case get_validators(Host, {Module, SubMods}) of
|
||||
undef ->
|
||||
Opts;
|
||||
Validators ->
|
||||
Opts1 = validate_opts(Host, Module, Opts, Required, Validators),
|
||||
remove_duplicated_opts(Opts1)
|
||||
end}
|
||||
catch _:{missing_required_option, Opt} ->
|
||||
ErrTxt = io_lib:format("Module '~s' is missing required option '~s'",
|
||||
[Module, Opt]),
|
||||
?ERROR_MSG(ErrTxt, []),
|
||||
{error, ErrTxt}
|
||||
end.
|
||||
|
||||
validate_opts(Host, Module, Opts, Validators) when is_list(Opts) ->
|
||||
validate_opts(Host, Module, Opts, Required, Validators) when is_list(Opts) ->
|
||||
lists:flatmap(
|
||||
fun({Opt, Val}) when is_atom(Opt) ->
|
||||
case lists:keyfind(Opt, 1, Validators) of
|
||||
{_, L} ->
|
||||
case lists:partition(fun is_function/1, L) of
|
||||
{[VFun|_], []} ->
|
||||
validate_opt(Module, Opt, Val, VFun);
|
||||
validate_opt(Module, Opt, Val, Required, VFun);
|
||||
{[VFun|_], SubValidators} ->
|
||||
try validate_opts(Host, Module, Val, SubValidators) of
|
||||
try validate_opts(Host, Module, Val, Required, SubValidators) of
|
||||
SubOpts ->
|
||||
validate_opt(Module, Opt, SubOpts, VFun)
|
||||
validate_opt(Module, Opt, SubOpts, Required, VFun)
|
||||
catch _:bad_option ->
|
||||
?ERROR_MSG("ignoring invalid value '~p' for "
|
||||
?ERROR_MSG("Ignoring invalid value '~p' for "
|
||||
"option '~s' of module '~s'",
|
||||
[Val, Opt, Module]),
|
||||
fail_if_option_is_required(Opt, Required),
|
||||
[]
|
||||
end
|
||||
end;
|
||||
false ->
|
||||
case Validators of
|
||||
[] ->
|
||||
?ERROR_MSG("unknown option '~s' for module '~s' "
|
||||
"will be likely ignored because the "
|
||||
"module doesn't have any options",
|
||||
?ERROR_MSG("Ignoring unknown option '~s' of '~s':"
|
||||
" the module doesn't have any options",
|
||||
[Opt, Module]);
|
||||
_ ->
|
||||
?ERROR_MSG("unknown option '~s' for module '~s' will be"
|
||||
" likely ignored, available options are: ~s",
|
||||
?ERROR_MSG("Ignoring unknown option '~s' of '~s',"
|
||||
" available options are: ~s",
|
||||
[Opt, Module,
|
||||
misc:join_atoms([K || {K, _} <- Validators],
|
||||
<<", ">>)])
|
||||
misc:join_atoms(
|
||||
[K || {K, _} <- Validators],
|
||||
<<", ">>)])
|
||||
end,
|
||||
[{Opt, Val}]
|
||||
[]
|
||||
end;
|
||||
(_) ->
|
||||
erlang:error(bad_option)
|
||||
end, Opts);
|
||||
validate_opts(_, _, _, _) ->
|
||||
validate_opts(_, _, _, _, _) ->
|
||||
erlang:error(bad_option).
|
||||
|
||||
-spec validate_opt(module(), atom(), any(),
|
||||
-spec validate_opt(module(), atom(), any(), [atom()],
|
||||
[{atom(), check_fun(), any()}]) -> [{atom(), any()}].
|
||||
validate_opt(Module, Opt, Val, VFun) ->
|
||||
validate_opt(Module, Opt, Val, Required, VFun) ->
|
||||
try VFun(Val) of
|
||||
NewVal -> [{Opt, NewVal}]
|
||||
catch {invalid_syntax, Error} ->
|
||||
?ERROR_MSG("ignoring invalid value '~p' for "
|
||||
?ERROR_MSG("Ignoring invalid value '~p' for "
|
||||
"option '~s' of module '~s': ~s",
|
||||
[Val, Opt, Module, Error]),
|
||||
fail_if_option_is_required(Opt, Required),
|
||||
[];
|
||||
_:_ ->
|
||||
?ERROR_MSG("ignoring invalid value '~p' for "
|
||||
?ERROR_MSG("Ignoring invalid value '~p' for "
|
||||
"option '~s' of module '~s'",
|
||||
[Val, Opt, Module]),
|
||||
fail_if_option_is_required(Opt, Required),
|
||||
[]
|
||||
end.
|
||||
|
||||
-spec fail_if_option_is_required(atom(), [atom()]) -> ok | no_return().
|
||||
fail_if_option_is_required(Opt, Required) ->
|
||||
case lists:member(Opt, Required) of
|
||||
true -> erlang:error({missing_required_option, Opt});
|
||||
false -> ok
|
||||
end.
|
||||
|
||||
-spec list_known_opts(binary(), module()) -> [atom() | {atom(), atom()}].
|
||||
list_known_opts(Host, Module) ->
|
||||
try Module:mod_options(Host) of
|
||||
DefaultOpts ->
|
||||
lists:flatmap(
|
||||
fun({Opt, [{A, _}|_] = Vals}) when is_atom(A) ->
|
||||
[{Opt, Val} || {Val, _} <- Vals];
|
||||
({Opt, _}) -> [Opt];
|
||||
(Opt) -> [Opt]
|
||||
end, DefaultOpts)
|
||||
catch _:undef ->
|
||||
Module:mod_opt_type('')
|
||||
end.
|
||||
|
||||
-spec merge_opts(opts(), opts(), module()) -> opts().
|
||||
merge_opts(Opts, DefaultOpts, Module) ->
|
||||
Result =
|
||||
lists:foldr(
|
||||
fun({Opt, Default}, Acc) ->
|
||||
case lists:keyfind(Opt, 1, Opts) of
|
||||
{_, Val} ->
|
||||
case Default of
|
||||
[{A, _}|_] when is_atom(A) andalso is_list(Val) ->
|
||||
case is_opt_list(Val) of
|
||||
true ->
|
||||
[{Opt, merge_opts(Val, Default, Module)}|Acc];
|
||||
false ->
|
||||
?ERROR_MSG(
|
||||
"Ignoring invalid value '~p' for "
|
||||
"option '~s' of module '~s'",
|
||||
[Val, Opt, Module]),
|
||||
[{Opt, Default}|Acc]
|
||||
end;
|
||||
Val ->
|
||||
[{Opt, Default}|Acc];
|
||||
_ ->
|
||||
[{Opt, Val}, {Opt, Default}|Acc]
|
||||
end;
|
||||
_ ->
|
||||
[{Opt, Default}|Acc]
|
||||
end;
|
||||
(Opt, Acc) ->
|
||||
case lists:keyfind(Opt, 1, Opts) of
|
||||
{_, Val} ->
|
||||
[{Opt, Val}|Acc];
|
||||
false ->
|
||||
erlang:error({missing_required_option, Opt})
|
||||
end
|
||||
end, [], DefaultOpts),
|
||||
lists:foldl(
|
||||
fun({Opt, Val}, Acc) ->
|
||||
case lists:keymember(Opt, 1, Result) of
|
||||
true -> Acc;
|
||||
false -> [{Opt, Val}|Acc]
|
||||
end
|
||||
end, Result, Opts).
|
||||
|
||||
remove_duplicated_opts([{Opt, Val}, {Opt, _Default}|Opts]) ->
|
||||
[{Opt, Val}|remove_duplicated_opts(Opts)];
|
||||
remove_duplicated_opts([{Opt, [{SubOpt, _}|_] = SubOpts}|Opts])
|
||||
when is_atom(SubOpt) ->
|
||||
[{Opt, remove_duplicated_opts(SubOpts)}|remove_duplicated_opts(Opts)];
|
||||
remove_duplicated_opts([OptVal|Opts]) ->
|
||||
[OptVal|remove_duplicated_opts(Opts)];
|
||||
remove_duplicated_opts([]) ->
|
||||
[].
|
||||
|
||||
-spec get_submodules(binary(), module(), opts()) -> [module()].
|
||||
get_submodules(Host, Module, Opts) ->
|
||||
try Module:mod_options(Host) of
|
||||
DefaultOpts ->
|
||||
Mod1 = case lists:keyfind(db_type, 1, DefaultOpts) of
|
||||
{_, T1} ->
|
||||
DBType = proplists:get_value(db_type, Opts, T1),
|
||||
[db_mod(DBType, Module)];
|
||||
false ->
|
||||
[]
|
||||
end,
|
||||
Mod2 = case lists:keyfind(ram_db_type, 1, DefaultOpts) of
|
||||
{_, T2} ->
|
||||
RamDBType = proplists:get_value(ram_db_type, Opts, T2),
|
||||
[ram_db_mod(RamDBType, Module)];
|
||||
false ->
|
||||
[]
|
||||
end,
|
||||
Mod1 ++ Mod2
|
||||
catch _:undef ->
|
||||
[]
|
||||
end.
|
||||
|
||||
@@ -630,20 +787,45 @@ ram_db_mod(Host, Module) when is_binary(Host) orelse Host == global ->
|
||||
ram_db_mod(Host, Opts, Module) when is_list(Opts) ->
|
||||
ram_db_mod(ram_db_type(Host, Opts, Module), Module).
|
||||
|
||||
is_db_configured(Type, Host) ->
|
||||
lists:any(
|
||||
fun(#ejabberd_module{module_host = {_, H}, opts = Opts})
|
||||
when H == Host orelse Host == global ->
|
||||
case lists:keyfind(db_type, 1, Opts) of
|
||||
{_, Type} -> true;
|
||||
_ ->
|
||||
case lists:keyfind(ram_db_type, 1, Opts) of
|
||||
{_, Type} -> true;
|
||||
_ -> false
|
||||
end
|
||||
end;
|
||||
(_) ->
|
||||
false
|
||||
end, ets:tab2list(ejabberd_modules)).
|
||||
|
||||
-spec loaded_modules(binary()) -> [atom()].
|
||||
|
||||
loaded_modules(Host) ->
|
||||
ets:select(ejabberd_modules,
|
||||
[{#ejabberd_module{_ = '_', module_host = {'$1', Host}},
|
||||
[], ['$1']}]).
|
||||
Mods = ets:select(
|
||||
ejabberd_modules,
|
||||
ets:fun2ms(
|
||||
fun(#ejabberd_module{module_host = {Mod, H},
|
||||
order = Order}) when H == Host ->
|
||||
{Mod, Order}
|
||||
end)),
|
||||
[Mod || {Mod, _} <- lists:keysort(2, Mods)].
|
||||
|
||||
-spec loaded_modules_with_opts(binary()) -> [{atom(), opts()}].
|
||||
|
||||
loaded_modules_with_opts(Host) ->
|
||||
ets:select(ejabberd_modules,
|
||||
[{#ejabberd_module{_ = '_', module_host = {'$1', Host},
|
||||
opts = '$2'},
|
||||
[], [{{'$1', '$2'}}]}]).
|
||||
Mods = ets:select(
|
||||
ejabberd_modules,
|
||||
ets:fun2ms(
|
||||
fun(#ejabberd_module{module_host = {Mod, H}, opts = Opts,
|
||||
order = Order}) when H == Host ->
|
||||
{Mod, Opts, Order}
|
||||
end)),
|
||||
[{Mod, Opts} || {Mod, Opts, _} <- lists:keysort(3, Mods)].
|
||||
|
||||
-spec get_hosts(opts(), binary()) -> [binary()].
|
||||
|
||||
@@ -660,9 +842,9 @@ get_hosts(Opts, Prefix) ->
|
||||
Hosts
|
||||
end.
|
||||
|
||||
-spec get_module_proc(binary(), {frontend, atom()} | atom()) -> atom().
|
||||
get_module_proc(Host, {frontend, Base}) ->
|
||||
get_module_proc(<<"frontend_", Host/binary>>, Base);
|
||||
-spec get_module_proc(binary() | global, atom()) -> atom().
|
||||
get_module_proc(global, Base) ->
|
||||
get_module_proc(<<"global">>, Base);
|
||||
get_module_proc(Host, Base) ->
|
||||
binary_to_atom(
|
||||
<<(erlang:atom_to_binary(Base, latin1))/binary, "_", Host/binary>>,
|
||||
@@ -689,23 +871,35 @@ config_reloaded() ->
|
||||
reload_modules(Host)
|
||||
end, ?MYHOSTS).
|
||||
|
||||
-spec is_equal_opt(atom(), opts(), opts(), any()) ->
|
||||
-spec is_equal_opt(atom(), opts(), opts()) ->
|
||||
true | {false, any(), any()}.
|
||||
is_equal_opt(Opt, NewOpts, OldOpts, Default) ->
|
||||
NewVal = get_opt(Opt, NewOpts, Default),
|
||||
OldVal = get_opt(Opt, OldOpts, Default),
|
||||
is_equal_opt(Opt, NewOpts, OldOpts) ->
|
||||
NewVal = get_opt(Opt, NewOpts),
|
||||
OldVal = get_opt(Opt, OldOpts),
|
||||
if NewVal /= OldVal ->
|
||||
{false, NewVal, OldVal};
|
||||
true ->
|
||||
true
|
||||
end.
|
||||
|
||||
-spec is_opt_list(term()) -> boolean().
|
||||
is_opt_list([]) ->
|
||||
true;
|
||||
is_opt_list(L) when is_list(L) ->
|
||||
lists:all(
|
||||
fun({Opt, _Val}) -> is_atom(Opt);
|
||||
(_) -> false
|
||||
end, L);
|
||||
is_opt_list(_) ->
|
||||
false.
|
||||
|
||||
-spec opt_type(modules) -> fun(([{atom(), list()}]) -> [{atom(), list()}]);
|
||||
(atom()) -> [atom()].
|
||||
opt_type(modules) ->
|
||||
fun(Mods) ->
|
||||
lists:map(
|
||||
fun({M, A}) when is_atom(M), is_list(A) ->
|
||||
fun({M, A}) when is_atom(M) ->
|
||||
true = is_opt_list(A),
|
||||
{M, A}
|
||||
end, Mods)
|
||||
end;
|
||||
|
||||
+26
-9
@@ -29,11 +29,11 @@
|
||||
|
||||
%% API
|
||||
-export([tolower/1, term_to_base64/1, base64_to_term/1, ip_to_list/1,
|
||||
hex_to_bin/1, hex_to_base64/1, expand_keyword/3,
|
||||
hex_to_bin/1, hex_to_base64/1, url_encode/1, expand_keyword/3,
|
||||
atom_to_binary/1, binary_to_atom/1, tuple_to_binary/1,
|
||||
l2i/1, i2l/1, i2l/2, expr_to_term/1, term_to_expr/1,
|
||||
now_to_usec/1, usec_to_now/1, encode_pid/1, decode_pid/2,
|
||||
compile_exprs/2, join_atoms/2, try_read_file/1, have_eimp/0,
|
||||
compile_exprs/2, join_atoms/2, try_read_file/1,
|
||||
css_dir/0, img_dir/0, js_dir/0, read_css/1, read_img/1, read_js/1]).
|
||||
|
||||
%% Deprecated functions
|
||||
@@ -65,7 +65,7 @@ term_to_base64(Term) ->
|
||||
base64_to_term(Base64) ->
|
||||
try binary_to_term(base64:decode(Base64), [safe]) of
|
||||
Term -> {term, Term}
|
||||
catch _:badarg ->
|
||||
catch _:_ ->
|
||||
error
|
||||
end.
|
||||
|
||||
@@ -105,6 +105,10 @@ hex_to_bin([H1, H2 | T], Acc) ->
|
||||
hex_to_base64(Hex) ->
|
||||
base64:encode(hex_to_bin(Hex)).
|
||||
|
||||
-spec url_encode(binary()) -> binary().
|
||||
url_encode(A) ->
|
||||
url_encode(A, <<>>).
|
||||
|
||||
-spec expand_keyword(binary(), binary(), binary()) -> binary().
|
||||
expand_keyword(Keyword, Input, Replacement) ->
|
||||
Parts = binary:split(Input, Keyword, [global]),
|
||||
@@ -214,12 +218,6 @@ try_read_file(Path) ->
|
||||
erlang:error(badarg)
|
||||
end.
|
||||
|
||||
-ifdef(GRAPHICS).
|
||||
have_eimp() -> true.
|
||||
-else.
|
||||
have_eimp() -> false.
|
||||
-endif.
|
||||
|
||||
-spec css_dir() -> file:filename().
|
||||
css_dir() ->
|
||||
case os:getenv("EJABBERD_CSS_PATH") of
|
||||
@@ -268,6 +266,25 @@ read_js(File) ->
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
-spec url_encode(binary(), binary()) -> binary().
|
||||
url_encode(<<H:8, T/binary>>, Acc) when
|
||||
(H >= $a andalso H =< $z) orelse
|
||||
(H >= $A andalso H =< $Z) orelse
|
||||
(H >= $0 andalso H =< $9) orelse
|
||||
H == $_ orelse
|
||||
H == $. orelse
|
||||
H == $- orelse
|
||||
H == $/ orelse
|
||||
H == $: ->
|
||||
url_encode(T, <<Acc/binary, H>>);
|
||||
url_encode(<<H:8, T/binary>>, Acc) ->
|
||||
case integer_to_list(H, 16) of
|
||||
[X, Y] -> url_encode(T, <<Acc/binary, $%, X, Y>>);
|
||||
[X] -> url_encode(T, <<Acc/binary, $%, $0, X>>)
|
||||
end;
|
||||
url_encode(<<>>, Acc) ->
|
||||
Acc.
|
||||
|
||||
-spec set_node_id(string(), binary()) -> pid().
|
||||
set_node_id(PidStr, NodeBin) ->
|
||||
ExtPidStr = erlang:pid_to_list(
|
||||
|
||||
+13
-23
@@ -35,19 +35,18 @@
|
||||
process_sm_iq/1, get_local_commands/5,
|
||||
get_local_identity/5, get_local_features/5,
|
||||
get_sm_commands/5, get_sm_identity/5, get_sm_features/5,
|
||||
ping_item/4, ping_command/4, mod_opt_type/1, depends/2]).
|
||||
ping_item/4, ping_command/4, mod_opt_type/1, depends/2,
|
||||
mod_options/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("xmpp.hrl").
|
||||
|
||||
start(Host, Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
|
||||
start(Host, _Opts) ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
||||
?NS_COMMANDS, ?MODULE, process_local_iq,
|
||||
IQDisc),
|
||||
?NS_COMMANDS, ?MODULE, process_local_iq),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||
?NS_COMMANDS, ?MODULE, process_sm_iq, IQDisc),
|
||||
?NS_COMMANDS, ?MODULE, process_sm_iq),
|
||||
ejabberd_hooks:add(disco_local_identity, Host, ?MODULE,
|
||||
get_local_identity, 99),
|
||||
ejabberd_hooks:add(disco_local_features, Host, ?MODULE,
|
||||
@@ -87,16 +86,8 @@ stop(Host) ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, Host,
|
||||
?NS_COMMANDS).
|
||||
|
||||
reload(Host, NewOpts, OldOpts) ->
|
||||
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
|
||||
{false, IQDisc, _} ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_COMMANDS,
|
||||
?MODULE, process_local_iq, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_COMMANDS,
|
||||
?MODULE, process_sm_iq, IQDisc);
|
||||
true ->
|
||||
ok
|
||||
end.
|
||||
reload(_Host, _NewOpts, _OldOpts) ->
|
||||
ok.
|
||||
|
||||
%-------------------------------------------------------------------------
|
||||
|
||||
@@ -104,8 +95,7 @@ get_local_commands(Acc, _From,
|
||||
#jid{server = Server, lserver = LServer} = _To, <<"">>,
|
||||
Lang) ->
|
||||
Display = gen_mod:get_module_opt(LServer, ?MODULE,
|
||||
report_commands_node,
|
||||
false),
|
||||
report_commands_node),
|
||||
case Display of
|
||||
false -> Acc;
|
||||
_ ->
|
||||
@@ -133,8 +123,7 @@ get_local_commands(Acc, _From, _To, _Node, _Lang) ->
|
||||
get_sm_commands(Acc, _From,
|
||||
#jid{lserver = LServer} = To, <<"">>, Lang) ->
|
||||
Display = gen_mod:get_module_opt(LServer, ?MODULE,
|
||||
report_commands_node,
|
||||
false),
|
||||
report_commands_node),
|
||||
case Display of
|
||||
false -> Acc;
|
||||
_ ->
|
||||
@@ -281,7 +270,8 @@ ping_command(Acc, _From, _To, _Request) -> Acc.
|
||||
depends(_Host, _Opts) ->
|
||||
[].
|
||||
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
mod_opt_type(report_commands_node) ->
|
||||
fun (B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(_) -> [iqdisc, report_commands_node].
|
||||
fun (B) when is_boolean(B) -> B end.
|
||||
|
||||
mod_options(_Host) ->
|
||||
[{report_commands_node, false}].
|
||||
|
||||
+30
-27
@@ -30,7 +30,7 @@
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
-export([start/2, stop/1, reload/3, mod_opt_type/1,
|
||||
-export([start/2, stop/1, reload/3, mod_options/1,
|
||||
get_commands_spec/0, depends/2]).
|
||||
|
||||
% Commands API
|
||||
@@ -59,6 +59,7 @@
|
||||
add_rosteritem/7, delete_rosteritem/4,
|
||||
process_rosteritems/5, get_roster/2, push_roster/3,
|
||||
push_roster_all/1, push_alltoall/2,
|
||||
push_roster_item/5, build_roster_item/3,
|
||||
|
||||
% Private storage
|
||||
private_get/4, private_set/3,
|
||||
@@ -224,7 +225,7 @@ get_commands_spec() ->
|
||||
result_desc = "Status code: 0 on success, 1 otherwise"},
|
||||
#ejabberd_commands{name = check_password_hash, tags = [accounts],
|
||||
desc = "Check if the password hash is correct",
|
||||
longdesc = "Allowed hash methods: md5, sha.",
|
||||
longdesc = "Allows hash methods from crypto application",
|
||||
module = ?MODULE, function = check_password_hash,
|
||||
args = [{user, binary}, {host, binary}, {passwordhash, binary},
|
||||
{hashmethod, binary}],
|
||||
@@ -785,24 +786,23 @@ get_cookie() ->
|
||||
restart_module(Host, Module) when is_binary(Module) ->
|
||||
restart_module(Host, misc:binary_to_atom(Module));
|
||||
restart_module(Host, Module) when is_atom(Module) ->
|
||||
List = gen_mod:loaded_modules_with_opts(Host),
|
||||
case proplists:get_value(Module, List) of
|
||||
undefined ->
|
||||
case gen_mod:is_loaded(Host, Module) of
|
||||
false ->
|
||||
% not a running module, force code reload anyway
|
||||
code:purge(Module),
|
||||
code:delete(Module),
|
||||
code:load_file(Module),
|
||||
1;
|
||||
Opts ->
|
||||
true ->
|
||||
gen_mod:stop_module(Host, Module),
|
||||
case code:soft_purge(Module) of
|
||||
true ->
|
||||
code:delete(Module),
|
||||
code:load_file(Module),
|
||||
gen_mod:start_module(Host, Module, Opts),
|
||||
gen_mod:start_module(Host, Module),
|
||||
0;
|
||||
false ->
|
||||
gen_mod:start_module(Host, Module, Opts),
|
||||
gen_mod:start_module(Host, Module),
|
||||
2
|
||||
end
|
||||
end.
|
||||
@@ -821,13 +821,15 @@ check_password(User, Host, Password) ->
|
||||
%% Copied some code from ejabberd_commands.erl
|
||||
check_password_hash(User, Host, PasswordHash, HashMethod) ->
|
||||
AccountPass = ejabberd_auth:get_password_s(User, Host),
|
||||
AccountPassHash = case {AccountPass, HashMethod} of
|
||||
Methods = lists:map(fun(A) -> atom_to_binary(A, latin1) end,
|
||||
proplists:get_value(hashs, crypto:supports())),
|
||||
MethodAllowed = lists:member(HashMethod, Methods),
|
||||
AccountPassHash = case {AccountPass, MethodAllowed} of
|
||||
{A, _} when is_tuple(A) -> scrammed;
|
||||
{_, <<"md5">>} -> get_md5(AccountPass);
|
||||
{_, <<"sha">>} -> get_sha(AccountPass);
|
||||
{_, Method} ->
|
||||
{_, true} -> get_hash(AccountPass, HashMethod);
|
||||
{_, false} ->
|
||||
?ERROR_MSG("check_password_hash called "
|
||||
"with hash method: ~p", [Method]),
|
||||
"with hash method: ~p", [HashMethod]),
|
||||
undefined
|
||||
end,
|
||||
case AccountPassHash of
|
||||
@@ -838,15 +840,14 @@ check_password_hash(User, Host, PasswordHash, HashMethod) ->
|
||||
PasswordHash -> ok;
|
||||
_ -> false
|
||||
end.
|
||||
get_md5(AccountPass) ->
|
||||
|
||||
get_hash(AccountPass, Method) ->
|
||||
iolist_to_binary([io_lib:format("~2.16.0B", [X])
|
||||
|| X <- binary_to_list(erlang:md5(AccountPass))]).
|
||||
get_sha(AccountPass) ->
|
||||
iolist_to_binary([io_lib:format("~2.16.0B", [X])
|
||||
|| X <- binary_to_list(crypto:hash(sha, AccountPass))]).
|
||||
|| X <- binary_to_list(
|
||||
crypto:hash(binary_to_atom(Method, latin1), AccountPass))]).
|
||||
|
||||
num_active_users(Host, Days) ->
|
||||
DB_Type = gen_mod:db_type(Host, mod_last),
|
||||
DB_Type = gen_mod:get_module_opt(Host, mod_last, db_type),
|
||||
list_last_activity(Host, true, Days, DB_Type).
|
||||
|
||||
%% Code based on ejabberd/src/web/ejabberd_web_admin.erl
|
||||
@@ -1025,7 +1026,7 @@ get_status_list(Host, Status_required) ->
|
||||
Sessions2 = [ {Session#session.usr, Session#session.sid, Session#session.priority} || Session <- Sessions],
|
||||
Fhost = case Host of
|
||||
<<"all">> ->
|
||||
%% All hosts are requested, so dont filter at all
|
||||
%% All hosts are requested, so don't filter at all
|
||||
fun(_, _) -> true end;
|
||||
_ ->
|
||||
%% Filter the list, only Host is interesting
|
||||
@@ -1461,7 +1462,7 @@ private_get(Username, Host, Element, Ns) ->
|
||||
ElementXml = #xmlel{name = Element, attrs = [{<<"xmlns">>, Ns}]},
|
||||
Els = mod_private:get_data(jid:nodeprep(Username), jid:nameprep(Host),
|
||||
[{Ns, ElementXml}]),
|
||||
binary_to_list(fxml:element_to_binary(xmpp:encode(#private{xml_els = Els}))).
|
||||
binary_to_list(fxml:element_to_binary(xmpp:encode(#private{sub_els = Els}))).
|
||||
|
||||
private_set(Username, Host, ElementString) ->
|
||||
case fxml_stream:parse_element(ElementString) of
|
||||
@@ -1506,11 +1507,12 @@ srg_get_info(Group, Host) ->
|
||||
Os when is_list(Os) -> Os;
|
||||
error -> []
|
||||
end,
|
||||
[{misc:atom_to_binary(Title), btl(Value)} || {Title, Value} <- Opts].
|
||||
[{misc:atom_to_binary(Title), to_list(Value)} || {Title, Value} <- Opts].
|
||||
|
||||
btl([]) -> [];
|
||||
btl([B|L]) -> [btl(B)|btl(L)];
|
||||
btl(B) -> binary_to_list(B).
|
||||
to_list([]) -> [];
|
||||
to_list([H|T]) -> [to_list(H)|to_list(T)];
|
||||
to_list(E) when is_atom(E) -> atom_to_list(E);
|
||||
to_list(E) -> binary_to_list(E).
|
||||
|
||||
srg_get_members(Group, Host) ->
|
||||
Members = mod_shared_roster:get_group_explicit_users(Host,Group),
|
||||
@@ -1549,7 +1551,8 @@ send_stanza(FromString, ToString, Stanza) ->
|
||||
#xmlel{} = El = fxml_stream:parse_element(Stanza),
|
||||
From = jid:decode(FromString),
|
||||
To = jid:decode(ToString),
|
||||
Pkt = xmpp:decode(El, ?NS_CLIENT, [ignore_els]),
|
||||
CodecOpts = ejabberd_config:codec_options(From#jid.lserver),
|
||||
Pkt = xmpp:decode(El, ?NS_CLIENT, CodecOpts),
|
||||
ejabberd_router:route(xmpp:set_from_to(Pkt, From, To))
|
||||
catch _:{xmpp_codec, Why} ->
|
||||
io:format("incorrect stanza: ~s~n", [xmpp:format_error(Why)]),
|
||||
@@ -1761,4 +1764,4 @@ is_glob_match(String, <<"!", Glob/binary>>) ->
|
||||
is_glob_match(String, Glob) ->
|
||||
is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)).
|
||||
|
||||
mod_opt_type(_) -> [].
|
||||
mod_options(_) -> [].
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2, stop/1, reload/3, mod_opt_type/1,
|
||||
-export([start/2, stop/1, reload/3, mod_options/1,
|
||||
get_commands_spec/0, depends/2]).
|
||||
|
||||
% Commands API
|
||||
@@ -362,4 +362,4 @@ sql_query(Host, Query) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
mod_opt_type(_) -> [].
|
||||
mod_options(_) -> [].
|
||||
|
||||
+22
-24
@@ -36,7 +36,7 @@
|
||||
import_start/2, import/5, announce/1, send_motd/1, disco_identity/5,
|
||||
disco_features/5, disco_items/5, depends/2,
|
||||
send_announcement_to_all/3, announce_commands/4,
|
||||
announce_items/4, mod_opt_type/1, clean_cache/1]).
|
||||
announce_items/4, mod_opt_type/1, mod_options/1, clean_cache/1]).
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3]).
|
||||
-export([announce_all/1,
|
||||
@@ -715,7 +715,8 @@ send_motd({#presence{type = available},
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case get_motd(Mod, LServer) of
|
||||
{ok, Packet} ->
|
||||
try xmpp:decode(Packet, ?NS_CLIENT, [ignore_els]) of
|
||||
CodecOpts = ejabberd_config:codec_options(LServer),
|
||||
try xmpp:decode(Packet, ?NS_CLIENT, CodecOpts) of
|
||||
Msg ->
|
||||
case is_motd_user(Mod, LUser, LServer) of
|
||||
false ->
|
||||
@@ -806,7 +807,8 @@ get_stored_motd(LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case get_motd(Mod, LServer) of
|
||||
{ok, Packet} ->
|
||||
try xmpp:decode(Packet, ?NS_CLIENT, [ignore_els]) of
|
||||
CodecOpts = ejabberd_config:codec_options(LServer),
|
||||
try xmpp:decode(Packet, ?NS_CLIENT, CodecOpts) of
|
||||
#message{body = Body, subject = Subject} ->
|
||||
{xmpp:get_text(Subject), xmpp:get_text(Body)}
|
||||
catch _:{xmpp_codec, Why} ->
|
||||
@@ -834,7 +836,7 @@ send_announcement_to_all(Host, SubjectS, BodyS) ->
|
||||
-spec get_access(global | binary()) -> atom().
|
||||
|
||||
get_access(Host) ->
|
||||
gen_mod:get_module_opt(Host, ?MODULE, access, none).
|
||||
gen_mod:get_module_opt(Host, ?MODULE, access).
|
||||
|
||||
-spec add_store_hint(stanza()) -> stanza().
|
||||
add_store_hint(El) ->
|
||||
@@ -850,23 +852,17 @@ route_forbidden_error(Packet) ->
|
||||
init_cache(Mod, Host, Opts) ->
|
||||
case use_cache(Mod, Host) of
|
||||
true ->
|
||||
CacheOpts = cache_opts(Host, Opts),
|
||||
CacheOpts = cache_opts(Opts),
|
||||
ets_cache:new(?MOTD_CACHE, CacheOpts);
|
||||
false ->
|
||||
ets_cache:delete(?MOTD_CACHE)
|
||||
end.
|
||||
|
||||
-spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()].
|
||||
cache_opts(Host, Opts) ->
|
||||
MaxSize = gen_mod:get_opt(
|
||||
cache_size, Opts,
|
||||
ejabberd_config:cache_size(Host)),
|
||||
CacheMissed = gen_mod:get_opt(
|
||||
cache_missed, Opts,
|
||||
ejabberd_config:cache_missed(Host)),
|
||||
LifeTime = case gen_mod:get_opt(
|
||||
cache_life_time, Opts,
|
||||
ejabberd_config:cache_life_time(Host)) of
|
||||
-spec cache_opts(gen_mod:opts()) -> [proplists:property()].
|
||||
cache_opts(Opts) ->
|
||||
MaxSize = gen_mod:get_opt(cache_size, Opts),
|
||||
CacheMissed = gen_mod:get_opt(cache_missed, Opts),
|
||||
LifeTime = case gen_mod:get_opt(cache_life_time, Opts) of
|
||||
infinity -> infinity;
|
||||
I -> timer:seconds(I)
|
||||
end,
|
||||
@@ -876,10 +872,7 @@ cache_opts(Host, Opts) ->
|
||||
use_cache(Mod, Host) ->
|
||||
case erlang:function_exported(Mod, use_cache, 1) of
|
||||
true -> Mod:use_cache(Host);
|
||||
false ->
|
||||
gen_mod:get_module_opt(
|
||||
Host, ?MODULE, use_cache,
|
||||
ejabberd_config:use_cache(Host))
|
||||
false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
|
||||
end.
|
||||
|
||||
-spec cache_nodes(module(), binary()) -> [node()].
|
||||
@@ -918,7 +911,12 @@ mod_opt_type(O) when O == cache_life_time; O == cache_size ->
|
||||
(infinity) -> infinity
|
||||
end;
|
||||
mod_opt_type(O) when O == use_cache; O == cache_missed ->
|
||||
fun (B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(_) ->
|
||||
[access, db_type, cache_life_time, cache_size,
|
||||
use_cache, cache_missed].
|
||||
fun (B) when is_boolean(B) -> B end.
|
||||
|
||||
mod_options(Host) ->
|
||||
[{access, none},
|
||||
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
|
||||
{use_cache, ejabberd_config:use_cache(Host)},
|
||||
{cache_size, ejabberd_config:cache_size(Host)},
|
||||
{cache_missed, ejabberd_config:cache_missed(Host)},
|
||||
{cache_life_time, ejabberd_config:cache_life_time(Host)}].
|
||||
|
||||
+69
-51
@@ -23,10 +23,13 @@
|
||||
-module(mod_avatar).
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-protocol({xep, 398, '0.2.0'}).
|
||||
|
||||
%% gen_mod API
|
||||
-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1]).
|
||||
-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1, mod_options/1]).
|
||||
%% Hooks
|
||||
-export([pubsub_publish_item/6, vcard_iq_convert/1, vcard_iq_publish/1]).
|
||||
-export([pubsub_publish_item/6, vcard_iq_convert/1, vcard_iq_publish/1,
|
||||
get_sm_features/5]).
|
||||
|
||||
-include("xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -38,27 +41,22 @@
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
start(Host, _Opts) ->
|
||||
case misc:have_eimp() of
|
||||
true ->
|
||||
ejabberd_hooks:add(pubsub_publish_item, Host, ?MODULE,
|
||||
pubsub_publish_item, 50),
|
||||
ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE,
|
||||
vcard_iq_convert, 30),
|
||||
ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE,
|
||||
vcard_iq_publish, 100);
|
||||
false ->
|
||||
?CRITICAL_MSG("ejabberd is built without "
|
||||
"graphics support: reconfigure it with "
|
||||
"--enable-graphics or disable '~s'",
|
||||
[?MODULE]),
|
||||
{error, graphics_not_compiled}
|
||||
end.
|
||||
ejabberd_hooks:add(pubsub_publish_item, Host, ?MODULE,
|
||||
pubsub_publish_item, 50),
|
||||
ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE,
|
||||
vcard_iq_convert, 30),
|
||||
ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE,
|
||||
vcard_iq_publish, 100),
|
||||
ejabberd_hooks:add(disco_sm_features, Host, ?MODULE,
|
||||
get_sm_features, 50).
|
||||
|
||||
stop(Host) ->
|
||||
ejabberd_hooks:delete(pubsub_publish_item, Host, ?MODULE,
|
||||
pubsub_publish_item, 50),
|
||||
ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, vcard_iq_convert, 30),
|
||||
ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, vcard_iq_publish, 100).
|
||||
ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, vcard_iq_publish, 100),
|
||||
ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE,
|
||||
get_sm_features, 50).
|
||||
|
||||
reload(_Host, _NewOpts, _OldOpts) ->
|
||||
ok.
|
||||
@@ -153,6 +151,20 @@ vcard_iq_publish(#iq{sub_els = [#vcard_temp{
|
||||
vcard_iq_publish(Acc) ->
|
||||
Acc.
|
||||
|
||||
-spec get_sm_features({error, stanza_error()} | empty | {result, [binary()]},
|
||||
jid(), jid(), binary(), binary()) ->
|
||||
{error, stanza_error()} | empty | {result, [binary()]}.
|
||||
get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc;
|
||||
get_sm_features(Acc, _From, _To, <<"">>, _Lang) ->
|
||||
{result, [?NS_DISCO_INFO, ?NS_PEP_VCARD_CONVERSION_0 |
|
||||
case Acc of
|
||||
{result, Features} -> Features;
|
||||
empty -> []
|
||||
end]};
|
||||
get_sm_features(Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
@@ -325,7 +337,10 @@ convert_avatar(LUser, LServer, Data, Rules) ->
|
||||
true ->
|
||||
?DEBUG("Converting avatar of ~s@~s: ~s -> ~s",
|
||||
[LUser, LServer, Type, NewType]),
|
||||
case eimp:convert(Data, NewType) of
|
||||
RateLimit = gen_mod:get_module_opt(LServer, ?MODULE, rate_limit),
|
||||
Opts = [{limit_by, {LUser, LServer}},
|
||||
{rate_limit, RateLimit}],
|
||||
case eimp:convert(Data, NewType, Opts) of
|
||||
{ok, NewData} ->
|
||||
{ok, encode_mime_type(NewType), NewData};
|
||||
{error, Reason} = Err ->
|
||||
@@ -384,7 +399,7 @@ stop_with_error(Lang, Reason) ->
|
||||
|
||||
-spec get_converting_rules(binary()) -> convert_rules().
|
||||
get_converting_rules(LServer) ->
|
||||
gen_mod:get_module_opt(LServer, ?MODULE, convert, []).
|
||||
gen_mod:get_module_opt(LServer, ?MODULE, convert).
|
||||
|
||||
-spec get_type(binary()) -> eimp:img_type() | unknown.
|
||||
get_type(Data) ->
|
||||
@@ -416,35 +431,38 @@ decode_mime_type(MimeType) ->
|
||||
encode_mime_type(Type) ->
|
||||
<<"image/", (atom_to_binary(Type, latin1))/binary>>.
|
||||
|
||||
mod_opt_type({convert, png}) ->
|
||||
fun(jpeg) -> jpeg;
|
||||
(webp) -> webp;
|
||||
(gif) -> gif
|
||||
-spec fail(atom()) -> no_return().
|
||||
fail(Format) ->
|
||||
FormatS = case Format of
|
||||
webp -> "WebP";
|
||||
png -> "PNG";
|
||||
jpeg -> "JPEG";
|
||||
gif -> "GIF";
|
||||
_ -> ""
|
||||
end,
|
||||
if FormatS /= "" ->
|
||||
?WARNING_MSG("ejabberd is not compiled with ~s support", [FormatS]);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
erlang:error(badarg).
|
||||
|
||||
mod_opt_type({convert, From}) ->
|
||||
fun(To) when is_atom(To), To /= From ->
|
||||
case eimp:is_supported(From) orelse From == default of
|
||||
false ->
|
||||
fail(From);
|
||||
true ->
|
||||
case eimp:is_supported(To) orelse To == undefined of
|
||||
false -> fail(To);
|
||||
true -> To
|
||||
end
|
||||
end
|
||||
end;
|
||||
mod_opt_type({convert, webp}) ->
|
||||
fun(jpeg) -> jpeg;
|
||||
(png) -> png;
|
||||
(gif) -> gif
|
||||
end;
|
||||
mod_opt_type({convert, jpeg}) ->
|
||||
fun(png) -> png;
|
||||
(webp) -> webp;
|
||||
(gif) -> gif
|
||||
end;
|
||||
mod_opt_type({convert, gif}) ->
|
||||
fun(png) -> png;
|
||||
(jpeg) -> jpeg;
|
||||
(webp) -> webp
|
||||
end;
|
||||
mod_opt_type({convert, default}) ->
|
||||
fun(png) -> png;
|
||||
(webp) -> webp;
|
||||
(jpeg) -> jpeg;
|
||||
(gif) -> gif
|
||||
end;
|
||||
mod_opt_type(_) ->
|
||||
[{convert, default},
|
||||
{convert, webp},
|
||||
{convert, png},
|
||||
{convert, gif},
|
||||
{convert, jpeg}].
|
||||
mod_opt_type(rate_limit) ->
|
||||
fun(I) when is_integer(I), I > 0 -> I end.
|
||||
|
||||
mod_options(_) ->
|
||||
[{rate_limit, 10},
|
||||
{convert,
|
||||
[{T, undefined} || T <- [default|eimp:supported_formats()]]}].
|
||||
|
||||
+134
-39
@@ -30,9 +30,9 @@
|
||||
|
||||
%% API
|
||||
-export([start/2, stop/1, reload/3,
|
||||
depends/2, mod_opt_type/1]).
|
||||
depends/2, mod_opt_type/1, mod_options/1]).
|
||||
|
||||
-export([filter_packet/1, filter_offline_msg/1]).
|
||||
-export([filter_packet/1, filter_offline_msg/1, filter_subscription/2]).
|
||||
|
||||
-include("xmpp.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
@@ -40,15 +40,22 @@
|
||||
|
||||
-define(SETS, gb_sets).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Callbacks and hooks
|
||||
%%%===================================================================
|
||||
start(Host, _Opts) ->
|
||||
ejabberd_hooks:add(user_receive_packet, Host,
|
||||
?MODULE, filter_packet, 25),
|
||||
ejabberd_hooks:add(roster_in_subscription, Host,
|
||||
?MODULE, filter_subscription, 25),
|
||||
ejabberd_hooks:add(offline_message_hook, Host,
|
||||
?MODULE, filter_offline_msg, 25).
|
||||
|
||||
stop(Host) ->
|
||||
ejabberd_hooks:delete(user_receive_packet, Host,
|
||||
?MODULE, filter_packet, 25),
|
||||
ejabberd_hooks:delete(roster_in_subscription, Host,
|
||||
?MODULE, filter_subscription, 25),
|
||||
ejabberd_hooks:delete(offline_message_hook, Host,
|
||||
?MODULE, filter_offline_msg, 25).
|
||||
|
||||
@@ -79,19 +86,74 @@ filter_offline_msg({_Action, #message{} = Msg} = Acc) ->
|
||||
deny -> {stop, {drop, Msg}}
|
||||
end.
|
||||
|
||||
filter_subscription(Acc, #presence{meta = #{captcha := passed}}) ->
|
||||
Acc;
|
||||
filter_subscription(Acc, #presence{from = From, to = To, lang = Lang,
|
||||
id = SID, type = subscribe} = Pres) ->
|
||||
LServer = To#jid.lserver,
|
||||
case gen_mod:get_module_opt(LServer, ?MODULE, drop) andalso
|
||||
gen_mod:get_module_opt(LServer, ?MODULE, captcha) andalso
|
||||
need_check(Pres) of
|
||||
true ->
|
||||
case check_subscription(From, To) of
|
||||
false ->
|
||||
BFrom = jid:remove_resource(From),
|
||||
BTo = jid:remove_resource(To),
|
||||
Limiter = jid:tolower(BFrom),
|
||||
case ejabberd_captcha:create_captcha(
|
||||
SID, BTo, BFrom, Lang, Limiter,
|
||||
fun(Res) -> handle_captcha_result(Res, Pres) end) of
|
||||
{ok, ID, Body, CaptchaEls} ->
|
||||
Msg = #message{from = BTo, to = From,
|
||||
id = ID, body = Body,
|
||||
sub_els = CaptchaEls},
|
||||
case gen_mod:get_module_opt(LServer, ?MODULE, log) of
|
||||
true ->
|
||||
?INFO_MSG("Challenge subscription request "
|
||||
"from stranger ~s to ~s with "
|
||||
"CAPTCHA",
|
||||
[jid:encode(From), jid:encode(To)]);
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
ejabberd_router:route(Msg);
|
||||
{error, limit} ->
|
||||
ErrText = <<"Too many CAPTCHA requests">>,
|
||||
Err = xmpp:err_resource_constraint(ErrText, Lang),
|
||||
ejabberd_router:route_error(Pres, Err);
|
||||
_ ->
|
||||
ErrText = <<"Unable to generate a CAPTCHA">>,
|
||||
Err = xmpp:err_internal_server_error(ErrText, Lang),
|
||||
ejabberd_router:route_error(Pres, Err)
|
||||
end,
|
||||
{stop, false};
|
||||
true ->
|
||||
Acc
|
||||
end;
|
||||
false ->
|
||||
Acc
|
||||
end;
|
||||
filter_subscription(Acc, _) ->
|
||||
Acc.
|
||||
|
||||
handle_captcha_result(captcha_succeed, Pres) ->
|
||||
Pres1 = xmpp:put_meta(Pres, captcha, passed),
|
||||
ejabberd_router:route(Pres1);
|
||||
handle_captcha_result(captcha_failed, #presence{lang = Lang} = Pres) ->
|
||||
Txt = <<"The CAPTCHA verification has failed">>,
|
||||
ejabberd_router:route_error(Pres, xmpp:err_not_allowed(Txt, Lang)).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
check_message(#message{from = From, to = To, lang = Lang} = Msg) ->
|
||||
LServer = To#jid.lserver,
|
||||
AllowLocalUsers =
|
||||
gen_mod:get_module_opt(LServer, ?MODULE, allow_local_users, true),
|
||||
case (Msg#message.body == [] andalso
|
||||
Msg#message.subject == [])
|
||||
orelse ((AllowLocalUsers orelse From#jid.luser == <<"">>) andalso
|
||||
ejabberd_router:is_my_host(From#jid.lserver)) of
|
||||
false ->
|
||||
case need_check(Msg) of
|
||||
true ->
|
||||
case check_subscription(From, To) of
|
||||
none ->
|
||||
Drop = gen_mod:get_module_opt(LServer, ?MODULE, drop, true),
|
||||
Log = gen_mod:get_module_opt(LServer, ?MODULE, log, false),
|
||||
false ->
|
||||
Drop = gen_mod:get_module_opt(LServer, ?MODULE, drop),
|
||||
Log = gen_mod:get_module_opt(LServer, ?MODULE, log),
|
||||
if
|
||||
Log ->
|
||||
?INFO_MSG("~s message from stranger ~s to ~s",
|
||||
@@ -106,46 +168,67 @@ check_message(#message{from = From, to = To, lang = Lang} = Msg) ->
|
||||
Drop ->
|
||||
Txt = <<"Messages from strangers are rejected">>,
|
||||
Err = xmpp:err_policy_violation(Txt, Lang),
|
||||
ejabberd_router:route_error(Msg, Err),
|
||||
Msg1 = maybe_adjust_from(Msg),
|
||||
ejabberd_router:route_error(Msg1, Err),
|
||||
deny;
|
||||
true ->
|
||||
allow
|
||||
end;
|
||||
some ->
|
||||
true ->
|
||||
allow
|
||||
end;
|
||||
true ->
|
||||
false ->
|
||||
allow
|
||||
end.
|
||||
|
||||
-spec check_subscription(jid(), jid()) -> none | some.
|
||||
-spec maybe_adjust_from(message()) -> message().
|
||||
maybe_adjust_from(#message{type = groupchat, from = From} = Msg) ->
|
||||
Msg#message{from = jid:remove_resource(From)};
|
||||
maybe_adjust_from(#message{} = Msg) ->
|
||||
Msg.
|
||||
|
||||
-spec need_check(presence() | message()) -> boolean().
|
||||
need_check(Pkt) ->
|
||||
To = xmpp:get_to(Pkt),
|
||||
From = xmpp:get_from(Pkt),
|
||||
LServer = To#jid.lserver,
|
||||
IsEmpty = case Pkt of
|
||||
#message{body = [], subject = []} ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end,
|
||||
AllowLocalUsers = gen_mod:get_module_opt(LServer, ?MODULE, allow_local_users),
|
||||
Access = gen_mod:get_module_opt(LServer, ?MODULE, access),
|
||||
not (IsEmpty orelse acl:match_rule(LServer, Access, From) == allow
|
||||
orelse ((AllowLocalUsers orelse From#jid.luser == <<"">>)
|
||||
andalso ejabberd_router:is_my_host(From#jid.lserver))).
|
||||
|
||||
-spec check_subscription(jid(), jid()) -> boolean().
|
||||
check_subscription(From, To) ->
|
||||
{LocalUser, LocalServer, _} = jid:tolower(To),
|
||||
{RemoteUser, RemoteServer, _} = jid:tolower(From),
|
||||
case ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, LocalServer,
|
||||
{none, []}, [LocalUser, LocalServer, From]) of
|
||||
{none, _} when RemoteUser == <<"">> ->
|
||||
none;
|
||||
{none, _} ->
|
||||
case gen_mod:get_module_opt(LocalServer, ?MODULE,
|
||||
allow_transports, true) of
|
||||
true ->
|
||||
%% Check if the contact's server is in the roster
|
||||
case ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, LocalServer,
|
||||
{none, []},
|
||||
[LocalUser, LocalServer, jid:make(RemoteServer)]) of
|
||||
{none, _} -> none;
|
||||
_ -> some
|
||||
end;
|
||||
false ->
|
||||
none
|
||||
end;
|
||||
_ ->
|
||||
some
|
||||
case has_subscription(LocalUser, LocalServer, From) of
|
||||
false when RemoteUser == <<"">> ->
|
||||
false;
|
||||
false ->
|
||||
%% Check if the contact's server is in the roster
|
||||
gen_mod:get_module_opt(LocalServer, ?MODULE, allow_transports)
|
||||
andalso has_subscription(LocalUser, LocalServer,
|
||||
jid:make(RemoteServer));
|
||||
true ->
|
||||
true
|
||||
end.
|
||||
|
||||
-spec has_subscription(binary(), binary(), jid()) -> boolean().
|
||||
has_subscription(User, Server, JID) ->
|
||||
{Sub, Ask, _} = ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, Server,
|
||||
{none, none, []},
|
||||
[User, Server, JID]),
|
||||
(Sub /= none) orelse (Ask == subscribe)
|
||||
orelse (Ask == out) orelse (Ask == both).
|
||||
|
||||
sets_bare_member({U, S, <<"">>} = LBJID, Set) ->
|
||||
case ?SETS:next(sets_iterator_from(LBJID, Set)) of
|
||||
{{U, S, _}, _} -> true;
|
||||
@@ -183,4 +266,16 @@ mod_opt_type(allow_local_users) ->
|
||||
fun (B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(allow_transports) ->
|
||||
fun (B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(_) -> [drop, log, allow_local_users, allow_transports].
|
||||
mod_opt_type(captcha) ->
|
||||
fun (B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(access) ->
|
||||
fun acl:access_rules_validator/1.
|
||||
|
||||
|
||||
mod_options(_) ->
|
||||
[{access, none},
|
||||
{drop, true},
|
||||
{log, false},
|
||||
{captcha, false},
|
||||
{allow_local_users, true},
|
||||
{allow_transports, true}].
|
||||
|
||||
+10
-16
@@ -29,8 +29,8 @@
|
||||
|
||||
-protocol({xep, 191, '1.2'}).
|
||||
|
||||
-export([start/2, stop/1, reload/3, process_iq/1, mod_opt_type/1, depends/2,
|
||||
disco_features/5]).
|
||||
-export([start/2, stop/1, reload/3, process_iq/1, depends/2,
|
||||
disco_features/5, mod_options/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -39,24 +39,17 @@
|
||||
|
||||
-include("mod_privacy.hrl").
|
||||
|
||||
start(Host, Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
|
||||
start(Host, _Opts) ->
|
||||
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||
?NS_BLOCKING, ?MODULE, process_iq, IQDisc).
|
||||
?NS_BLOCKING, ?MODULE, process_iq).
|
||||
|
||||
stop(Host) ->
|
||||
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 50),
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING).
|
||||
|
||||
reload(Host, NewOpts, OldOpts) ->
|
||||
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
|
||||
{false, IQDisc, _} ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING,
|
||||
?MODULE, process_iq, IQDisc);
|
||||
true ->
|
||||
ok
|
||||
end.
|
||||
reload(_Host, _NewOpts, _OldOpts) ->
|
||||
ok.
|
||||
|
||||
depends(_Host, _Opts) ->
|
||||
[{mod_privacy, hard}].
|
||||
@@ -241,10 +234,11 @@ process_unblock(#iq{from = From} = IQ, LJIDs) ->
|
||||
|
||||
-spec broadcast_event(jid(), block() | unblock()) -> ok.
|
||||
broadcast_event(#jid{luser = LUser, lserver = LServer} = From, Event) ->
|
||||
BFrom = jid:remove_resource(From),
|
||||
lists:foreach(
|
||||
fun(R) ->
|
||||
To = jid:replace_resource(From, R),
|
||||
IQ = #iq{type = set, from = From, to = To,
|
||||
IQ = #iq{type = set, from = BFrom, to = To,
|
||||
id = <<"push", (randoms:get_string())/binary>>,
|
||||
sub_els = [Event]},
|
||||
ejabberd_router:route(IQ)
|
||||
@@ -267,5 +261,5 @@ err_db_failure(#iq{lang = Lang} = IQ) ->
|
||||
Txt = <<"Database failure">>,
|
||||
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)).
|
||||
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
mod_opt_type(_) -> [iqdisc].
|
||||
mod_options(_Host) ->
|
||||
[].
|
||||
|
||||
+20
-19
@@ -36,7 +36,7 @@
|
||||
-export([start/2, stop/1, reload/3, process/2, open_session/2,
|
||||
close_session/1, find_session/1, clean_cache/1]).
|
||||
|
||||
-export([depends/2, mod_opt_type/1]).
|
||||
-export([depends/2, mod_opt_type/1, mod_options/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -144,7 +144,7 @@ reload(_Host, NewOpts, _OldOpts) ->
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
start_jiffy(Opts) ->
|
||||
case gen_mod:get_opt(json, Opts, false) of
|
||||
case gen_mod:get_opt(json, Opts) of
|
||||
false ->
|
||||
ok;
|
||||
true ->
|
||||
@@ -194,10 +194,20 @@ mod_opt_type(O) when O == cache_size; O == cache_life_time ->
|
||||
fun(I) when is_integer(I), I>0 -> I;
|
||||
(unlimited) -> infinity;
|
||||
(infinity) -> infinity
|
||||
end;
|
||||
mod_opt_type(_) ->
|
||||
[json, max_concat, max_inactivity, max_pause, prebind, ram_db_type,
|
||||
queue_type, use_cache, cache_size, cache_missed, cache_life_time].
|
||||
end.
|
||||
|
||||
mod_options(Host) ->
|
||||
[{json, false},
|
||||
{max_concat, unlimited},
|
||||
{max_inactivity, 30},
|
||||
{max_pause, 120},
|
||||
{prebind, false},
|
||||
{ram_db_type, ejabberd_config:default_ram_db(Host, ?MODULE)},
|
||||
{queue_type, ejabberd_config:default_queue_type(Host)},
|
||||
{use_cache, ejabberd_config:use_cache(Host)},
|
||||
{cache_size, ejabberd_config:cache_size(Host)},
|
||||
{cache_missed, ejabberd_config:cache_missed(Host)},
|
||||
{cache_life_time, ejabberd_config:cache_life_time(Host)}].
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Cache stuff
|
||||
@@ -215,10 +225,7 @@ init_cache(Mod) ->
|
||||
use_cache(Mod) ->
|
||||
case erlang:function_exported(Mod, use_cache, 0) of
|
||||
true -> Mod:use_cache();
|
||||
false ->
|
||||
gen_mod:get_module_opt(
|
||||
global, ?MODULE, use_cache,
|
||||
ejabberd_config:use_cache(global))
|
||||
false -> gen_mod:get_module_opt(global, ?MODULE, use_cache)
|
||||
end.
|
||||
|
||||
-spec cache_nodes(module()) -> [node()].
|
||||
@@ -239,15 +246,9 @@ delete_cache(Mod, SID) ->
|
||||
|
||||
-spec cache_opts() -> [proplists:property()].
|
||||
cache_opts() ->
|
||||
MaxSize = gen_mod:get_module_opt(
|
||||
global, ?MODULE, cache_size,
|
||||
ejabberd_config:cache_size(global)),
|
||||
CacheMissed = gen_mod:get_module_opt(
|
||||
global, ?MODULE, cache_missed,
|
||||
ejabberd_config:cache_missed(global)),
|
||||
LifeTime = case gen_mod:get_module_opt(
|
||||
global, ?MODULE, cache_life_time,
|
||||
ejabberd_config:cache_life_time(global)) of
|
||||
MaxSize = gen_mod:get_module_opt(global, ?MODULE, cache_size),
|
||||
CacheMissed = gen_mod:get_module_opt(global, ?MODULE, cache_missed),
|
||||
LifeTime = case gen_mod:get_module_opt(global, ?MODULE, cache_life_time) of
|
||||
infinity -> infinity;
|
||||
I -> timer:seconds(I)
|
||||
end,
|
||||
|
||||
+79
-70
@@ -38,7 +38,8 @@
|
||||
-export([read_caps/1, list_features/1, caps_stream_features/2,
|
||||
disco_features/5, disco_identity/5, disco_info/5,
|
||||
get_features/2, export/1, import_info/0, import/5,
|
||||
get_user_caps/2, import_start/2, import_stop/2]).
|
||||
get_user_caps/2, import_start/2, import_stop/2,
|
||||
compute_disco_hash/2, is_valid_node/1]).
|
||||
|
||||
%% gen_mod callbacks
|
||||
-export([start/2, stop/1, reload/3, depends/2]).
|
||||
@@ -48,7 +49,7 @@
|
||||
handle_cast/2, terminate/2, code_change/3]).
|
||||
|
||||
-export([user_send_packet/1, user_receive_packet/1,
|
||||
c2s_presence_in/2, mod_opt_type/1]).
|
||||
c2s_presence_in/2, mod_opt_type/1, mod_options/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -66,6 +67,9 @@
|
||||
{ok, non_neg_integer() | [binary()]} | error.
|
||||
-callback caps_write(binary(), {binary(), binary()},
|
||||
non_neg_integer() | [binary()]) -> any().
|
||||
-callback use_cache(binary()) -> boolean().
|
||||
|
||||
-optional_callbacks([use_cache/1]).
|
||||
|
||||
start(Host, Opts) ->
|
||||
gen_mod:start_child(?MODULE, Host, Opts).
|
||||
@@ -78,17 +82,23 @@ get_features(_Host, nothing) -> [];
|
||||
get_features(Host, #caps{node = Node, version = Version,
|
||||
exts = Exts}) ->
|
||||
SubNodes = [Version | Exts],
|
||||
lists:foldl(fun (SubNode, Acc) ->
|
||||
NodePair = {Node, SubNode},
|
||||
case ets_cache:lookup(caps_features_cache, NodePair,
|
||||
caps_read_fun(Host, NodePair))
|
||||
of
|
||||
{ok, Features} when is_list(Features) ->
|
||||
Features ++ Acc;
|
||||
_ -> Acc
|
||||
end
|
||||
end,
|
||||
[], SubNodes).
|
||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||
lists:foldl(
|
||||
fun(SubNode, Acc) ->
|
||||
NodePair = {Node, SubNode},
|
||||
Res = case use_cache(Mod, Host) of
|
||||
true ->
|
||||
ets_cache:lookup(caps_features_cache, NodePair,
|
||||
caps_read_fun(Host, NodePair));
|
||||
false ->
|
||||
Mod:caps_read(Host, NodePair)
|
||||
end,
|
||||
case Res of
|
||||
{ok, Features} when is_list(Features) ->
|
||||
Features ++ Acc;
|
||||
_ -> Acc
|
||||
end
|
||||
end, [], SubNodes).
|
||||
|
||||
-spec list_features(ejabberd_c2s:state()) -> [{ljid(), caps()}].
|
||||
list_features(C2SState) ->
|
||||
@@ -136,7 +146,7 @@ user_receive_packet({#presence{from = From, type = available} = Pkt,
|
||||
case read_caps(Pkt) of
|
||||
nothing -> ok;
|
||||
#caps{version = Version, exts = Exts} = Caps ->
|
||||
feature_request(LServer, From, To, Caps, [Version | Exts])
|
||||
feature_request(LServer, To, From, Caps, [Version | Exts])
|
||||
end;
|
||||
true -> ok
|
||||
end,
|
||||
@@ -145,16 +155,15 @@ user_receive_packet(Acc) ->
|
||||
Acc.
|
||||
|
||||
-spec caps_stream_features([xmpp_element()], binary()) -> [xmpp_element()].
|
||||
|
||||
caps_stream_features(Acc, MyHost) ->
|
||||
case gen_mod:is_loaded(MyHost, ?MODULE) of
|
||||
true ->
|
||||
case make_my_disco_hash(MyHost) of
|
||||
case make_my_disco_hash(MyHost) of
|
||||
<<"">> ->
|
||||
Acc;
|
||||
Hash ->
|
||||
Hash ->
|
||||
[#caps{hash = <<"sha-1">>, node = ?EJABBERD_URI,
|
||||
version = Hash}|Acc]
|
||||
version = Hash} | Acc]
|
||||
end;
|
||||
false ->
|
||||
Acc
|
||||
@@ -203,13 +212,14 @@ disco_info(Acc, _, _, _Node, _Lang) ->
|
||||
-spec c2s_presence_in(ejabberd_c2s:state(), presence()) -> ejabberd_c2s:state().
|
||||
c2s_presence_in(C2SState,
|
||||
#presence{from = From, to = To, type = Type} = Presence) ->
|
||||
{Subscription, _} = ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, To#jid.lserver,
|
||||
{none, []}, [To#jid.luser, To#jid.lserver, From]),
|
||||
{Subscription, _, _} = ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, To#jid.lserver,
|
||||
{none, none, []},
|
||||
[To#jid.luser, To#jid.lserver, From]),
|
||||
ToSelf = (From#jid.luser == To#jid.luser)
|
||||
and (From#jid.lserver == To#jid.lserver),
|
||||
Insert = (Type == available)
|
||||
and ((Subscription == both) or (Subscription == to) or ToSelf),
|
||||
and ((Subscription == both) or (Subscription == from) or ToSelf),
|
||||
Delete = (Type == unavailable) or (Type == error),
|
||||
if Insert or Delete ->
|
||||
LFrom = jid:tolower(From),
|
||||
@@ -250,30 +260,12 @@ reload(Host, NewOpts, OldOpts) ->
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
case gen_mod:is_equal_opt(cache_size, NewOpts, OldOpts,
|
||||
ejabberd_config:cache_size(Host)) of
|
||||
{false, MaxSize, _} ->
|
||||
ets_cache:setopts(caps_features_cache, [{max_size, MaxSize}]),
|
||||
ets_cache:setopts(caps_requests_cache, [{max_size, MaxSize}]);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
case gen_mod:is_equal_opt(cache_life_time, NewOpts, OldOpts,
|
||||
ejabberd_config:cache_life_time(Host)) of
|
||||
{false, Time, _} ->
|
||||
LifeTime = case Time of
|
||||
infinity -> infinity;
|
||||
_ -> timer:seconds(Time)
|
||||
end,
|
||||
ets_cache:setopts(caps_features_cache, [{life_time, LifeTime}]);
|
||||
true ->
|
||||
ok
|
||||
end.
|
||||
init_cache(NewMod, Host, NewOpts).
|
||||
|
||||
init([Host, Opts]) ->
|
||||
process_flag(trap_exit, true),
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
init_cache(Host, Opts),
|
||||
init_cache(Mod, Host, Opts),
|
||||
Mod:init(Host, Opts),
|
||||
ejabberd_hooks:add(c2s_presence_in, Host, ?MODULE,
|
||||
c2s_presence_in, 75),
|
||||
@@ -334,8 +326,15 @@ feature_request(Host, From, To, Caps,
|
||||
[SubNode | Tail] = SubNodes) ->
|
||||
Node = Caps#caps.node,
|
||||
NodePair = {Node, SubNode},
|
||||
case ets_cache:lookup(caps_features_cache, NodePair,
|
||||
caps_read_fun(Host, NodePair)) of
|
||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||
Res = case use_cache(Mod, Host) of
|
||||
true ->
|
||||
ets_cache:lookup(caps_features_cache, NodePair,
|
||||
caps_read_fun(Host, NodePair));
|
||||
false ->
|
||||
Mod:caps_read(Host, NodePair)
|
||||
end,
|
||||
case Res of
|
||||
{ok, Fs} when is_list(Fs) ->
|
||||
feature_request(Host, From, To, Caps, Tail);
|
||||
_ ->
|
||||
@@ -370,7 +369,12 @@ feature_response(#iq{type = result, sub_els = [El]},
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case Mod:caps_write(LServer, NodePair, Features) of
|
||||
ok ->
|
||||
ets_cache:delete(caps_features_cache, NodePair);
|
||||
case use_cache(Mod, LServer) of
|
||||
true ->
|
||||
ets_cache:delete(caps_features_cache, NodePair);
|
||||
false ->
|
||||
ok
|
||||
end;
|
||||
{error, _} ->
|
||||
ok
|
||||
end;
|
||||
@@ -409,13 +413,13 @@ make_my_disco_hash(Host) ->
|
||||
DiscoInfo = #disco_info{identities = Identities,
|
||||
features = Feats,
|
||||
xdata = Info},
|
||||
make_disco_hash(DiscoInfo, sha);
|
||||
compute_disco_hash(DiscoInfo, sha);
|
||||
_Err -> <<"">>
|
||||
end.
|
||||
|
||||
-type digest_type() :: md5 | sha | sha224 | sha256 | sha384 | sha512.
|
||||
-spec make_disco_hash(disco_info(), digest_type()) -> binary().
|
||||
make_disco_hash(DiscoInfo, Algo) ->
|
||||
-spec compute_disco_hash(disco_info(), digest_type()) -> binary().
|
||||
compute_disco_hash(DiscoInfo, Algo) ->
|
||||
Concat = list_to_binary([concat_identities(DiscoInfo),
|
||||
concat_features(DiscoInfo), concat_info(DiscoInfo)]),
|
||||
base64:encode(case Algo of
|
||||
@@ -431,17 +435,17 @@ make_disco_hash(DiscoInfo, Algo) ->
|
||||
check_hash(Caps, DiscoInfo) ->
|
||||
case Caps#caps.hash of
|
||||
<<"md5">> ->
|
||||
Caps#caps.version == make_disco_hash(DiscoInfo, md5);
|
||||
Caps#caps.version == compute_disco_hash(DiscoInfo, md5);
|
||||
<<"sha-1">> ->
|
||||
Caps#caps.version == make_disco_hash(DiscoInfo, sha);
|
||||
Caps#caps.version == compute_disco_hash(DiscoInfo, sha);
|
||||
<<"sha-224">> ->
|
||||
Caps#caps.version == make_disco_hash(DiscoInfo, sha224);
|
||||
Caps#caps.version == compute_disco_hash(DiscoInfo, sha224);
|
||||
<<"sha-256">> ->
|
||||
Caps#caps.version == make_disco_hash(DiscoInfo, sha256);
|
||||
Caps#caps.version == compute_disco_hash(DiscoInfo, sha256);
|
||||
<<"sha-384">> ->
|
||||
Caps#caps.version == make_disco_hash(DiscoInfo, sha384);
|
||||
Caps#caps.version == compute_disco_hash(DiscoInfo, sha384);
|
||||
<<"sha-512">> ->
|
||||
Caps#caps.version == make_disco_hash(DiscoInfo, sha512);
|
||||
Caps#caps.version == compute_disco_hash(DiscoInfo, sha512);
|
||||
_ -> true
|
||||
end.
|
||||
|
||||
@@ -478,29 +482,29 @@ is_valid_node(Node) ->
|
||||
false
|
||||
end.
|
||||
|
||||
init_cache(Host, Opts) ->
|
||||
CacheOpts = cache_opts(Host, Opts),
|
||||
case use_cache(Host, Opts) of
|
||||
init_cache(Mod, Host, Opts) ->
|
||||
CacheOpts = cache_opts(Opts),
|
||||
case use_cache(Mod, Host) of
|
||||
true ->
|
||||
ets_cache:new(caps_features_cache, CacheOpts);
|
||||
false ->
|
||||
ok
|
||||
ets_cache:delete(caps_features_cache)
|
||||
end,
|
||||
CacheSize = proplists:get_value(max_size, CacheOpts),
|
||||
ets_cache:new(caps_requests_cache,
|
||||
[{max_size, CacheSize},
|
||||
{life_time, timer:seconds(?BAD_HASH_LIFETIME)}]).
|
||||
|
||||
use_cache(Host, Opts) ->
|
||||
gen_mod:get_opt(use_cache, Opts, ejabberd_config:use_cache(Host)).
|
||||
use_cache(Mod, Host) ->
|
||||
case erlang:function_exported(Mod, use_cache, 1) of
|
||||
true -> Mod:use_cache(Host);
|
||||
false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
|
||||
end.
|
||||
|
||||
cache_opts(Host, Opts) ->
|
||||
MaxSize = gen_mod:get_opt(cache_size, Opts,
|
||||
ejabberd_config:cache_size(Host)),
|
||||
CacheMissed = gen_mod:get_opt(cache_missed, Opts,
|
||||
ejabberd_config:cache_missed(Host)),
|
||||
LifeTime = case gen_mod:get_opt(cache_life_time, Opts,
|
||||
ejabberd_config:cache_life_time(Host)) of
|
||||
cache_opts(Opts) ->
|
||||
MaxSize = gen_mod:get_opt(cache_size, Opts),
|
||||
CacheMissed = gen_mod:get_opt(cache_missed, Opts),
|
||||
LifeTime = case gen_mod:get_opt(cache_life_time, Opts) of
|
||||
infinity -> infinity;
|
||||
I -> timer:seconds(I)
|
||||
end,
|
||||
@@ -547,6 +551,11 @@ mod_opt_type(O) when O == cache_life_time; O == cache_size ->
|
||||
end;
|
||||
mod_opt_type(O) when O == use_cache; O == cache_missed ->
|
||||
fun (B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
||||
mod_opt_type(_) ->
|
||||
[cache_life_time, cache_size, use_cache, cache_missed, db_type].
|
||||
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end.
|
||||
|
||||
mod_options(Host) ->
|
||||
[{db_type, ejabberd_config:default_db(Host, ?MODULE)},
|
||||
{use_cache, ejabberd_config:use_cache(Host)},
|
||||
{cache_size, ejabberd_config:cache_size(Host)},
|
||||
{cache_missed, ejabberd_config:cache_missed(Host)},
|
||||
{cache_life_time, ejabberd_config:cache_life_time(Host)}].
|
||||
|
||||
+31
-40
@@ -29,14 +29,15 @@
|
||||
-author ('ecestari@process-one.net').
|
||||
-protocol({xep, 280, '0.8'}).
|
||||
|
||||
-behavior(gen_mod).
|
||||
-behaviour(gen_mod).
|
||||
|
||||
%% API:
|
||||
-export([start/2, stop/1, reload/3]).
|
||||
|
||||
-export([user_send_packet/1, user_receive_packet/1,
|
||||
iq_handler/1, remove_connection/4, disco_features/5,
|
||||
is_carbon_copy/1, mod_opt_type/1, depends/2, clean_cache/1]).
|
||||
is_carbon_copy/1, mod_opt_type/1, depends/2, clean_cache/1,
|
||||
mod_options/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -61,7 +62,6 @@ is_carbon_copy(_) ->
|
||||
false.
|
||||
|
||||
start(Host, Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
|
||||
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50),
|
||||
Mod = gen_mod:ram_db_mod(Host, ?MODULE),
|
||||
init_cache(Mod, Host, Opts),
|
||||
@@ -71,7 +71,7 @@ start(Host, Opts) ->
|
||||
%% why priority 89: to define clearly that we must run BEFORE mod_logdb hook (90)
|
||||
ejabberd_hooks:add(user_send_packet,Host, ?MODULE, user_send_packet, 89),
|
||||
ejabberd_hooks:add(user_receive_packet,Host, ?MODULE, user_receive_packet, 89),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_CARBONS_2, ?MODULE, iq_handler, IQDisc).
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_CARBONS_2, ?MODULE, iq_handler).
|
||||
|
||||
stop(Host) ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_CARBONS_2),
|
||||
@@ -91,16 +91,9 @@ reload(Host, NewOpts, OldOpts) ->
|
||||
end,
|
||||
case use_cache(NewMod, Host) of
|
||||
true ->
|
||||
ets_cache:new(?CARBONCOPY_CACHE, cache_opts(Host, NewOpts));
|
||||
ets_cache:new(?CARBONCOPY_CACHE, cache_opts(NewOpts));
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
|
||||
{false, IQDisc, _} ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_CARBONS_2,
|
||||
?MODULE, iq_handler, IQDisc);
|
||||
true ->
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec disco_features({error, stanza_error()} | {result, [binary()]} | empty,
|
||||
@@ -122,10 +115,10 @@ iq_handler(#iq{type = set, lang = Lang, from = From,
|
||||
{U, S, R} = jid:tolower(From),
|
||||
Result = case El of
|
||||
#carbons_enable{} ->
|
||||
?INFO_MSG("carbons enabled for user ~s@~s/~s", [U,S,R]),
|
||||
?DEBUG("Carbons enabled for user ~s@~s/~s", [U,S,R]),
|
||||
enable(S, U, R, ?NS_CARBONS_2);
|
||||
#carbons_disable{} ->
|
||||
?INFO_MSG("carbons disabled for user ~s@~s/~s", [U,S,R]),
|
||||
?DEBUG("Carbons disabled for user ~s@~s/~s", [U,S,R]),
|
||||
disable(S, U, R)
|
||||
end,
|
||||
case Result of
|
||||
@@ -171,9 +164,9 @@ user_receive_packet({Packet, #{jid := JID} = C2SState}) ->
|
||||
stanza() | {stop, stanza()}.
|
||||
check_and_forward(JID, To, Packet, Direction)->
|
||||
case is_chat_message(Packet) andalso
|
||||
not is_muc_pm(To, Packet) andalso
|
||||
xmpp:has_subtag(Packet, #carbons_private{}) == false andalso
|
||||
xmpp:has_subtag(Packet, #hint{type = 'no-copy'}) == false of
|
||||
not is_received_muc_pm(To, Packet, Direction) andalso
|
||||
not xmpp:has_subtag(Packet, #carbons_private{}) andalso
|
||||
not xmpp:has_subtag(Packet, #hint{type = 'no-copy'}) of
|
||||
true ->
|
||||
case is_carbon_copy(Packet) of
|
||||
false ->
|
||||
@@ -246,7 +239,7 @@ send_copies(JID, To, Packet, Direction)->
|
||||
|
||||
-spec build_forward_packet(jid(), message(), jid(), jid(), direction()) -> message().
|
||||
build_forward_packet(JID, #message{type = T} = Msg, Sender, Dest, Direction) ->
|
||||
Forwarded = #forwarded{xml_els = [xmpp:encode(complete_packet(JID, Msg, Direction))]},
|
||||
Forwarded = #forwarded{sub_els = [complete_packet(JID, Msg, Direction)]},
|
||||
Carbon = case Direction of
|
||||
sent -> #carbons_sent{forwarded = Forwarded};
|
||||
received -> #carbons_received{forwarded = Forwarded}
|
||||
@@ -289,9 +282,12 @@ is_chat_message(#message{type = normal, body = [_|_]}) ->
|
||||
is_chat_message(_) ->
|
||||
false.
|
||||
|
||||
is_muc_pm(#jid{lresource = <<>>}, _Packet) ->
|
||||
-spec is_received_muc_pm(jid(), message(), direction()) -> boolean().
|
||||
is_received_muc_pm(#jid{lresource = <<>>}, _Packet, _Direction) ->
|
||||
false;
|
||||
is_muc_pm(_To, Packet) ->
|
||||
is_received_muc_pm(_To, _Packet, sent) ->
|
||||
false;
|
||||
is_received_muc_pm(_To, Packet, received) ->
|
||||
xmpp:has_subtag(Packet, #muc_user{}).
|
||||
|
||||
-spec list(binary(), binary()) -> [{Resource :: binary(), Namespace :: binary()}].
|
||||
@@ -321,22 +317,16 @@ list(User, Server) ->
|
||||
init_cache(Mod, Host, Opts) ->
|
||||
case use_cache(Mod, Host) of
|
||||
true ->
|
||||
ets_cache:new(?CARBONCOPY_CACHE, cache_opts(Host, Opts));
|
||||
ets_cache:new(?CARBONCOPY_CACHE, cache_opts(Opts));
|
||||
false ->
|
||||
ets_cache:delete(?CARBONCOPY_CACHE)
|
||||
end.
|
||||
|
||||
-spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()].
|
||||
cache_opts(Host, Opts) ->
|
||||
MaxSize = gen_mod:get_opt(
|
||||
cache_size, Opts,
|
||||
ejabberd_config:cache_size(Host)),
|
||||
CacheMissed = gen_mod:get_opt(
|
||||
cache_missed, Opts,
|
||||
ejabberd_config:cache_missed(Host)),
|
||||
LifeTime = case gen_mod:get_opt(
|
||||
cache_life_time, Opts,
|
||||
ejabberd_config:cache_life_time(Host)) of
|
||||
-spec cache_opts(gen_mod:opts()) -> [proplists:property()].
|
||||
cache_opts(Opts) ->
|
||||
MaxSize = gen_mod:get_opt(cache_size, Opts),
|
||||
CacheMissed = gen_mod:get_opt(cache_missed, Opts),
|
||||
LifeTime = case gen_mod:get_opt(cache_life_time, Opts) of
|
||||
infinity -> infinity;
|
||||
I -> timer:seconds(I)
|
||||
end,
|
||||
@@ -346,10 +336,7 @@ cache_opts(Host, Opts) ->
|
||||
use_cache(Mod, Host) ->
|
||||
case erlang:function_exported(Mod, use_cache, 1) of
|
||||
true -> Mod:use_cache(Host);
|
||||
false ->
|
||||
gen_mod:get_module_opt(
|
||||
Host, ?MODULE, use_cache,
|
||||
ejabberd_config:use_cache(Host))
|
||||
false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
|
||||
end.
|
||||
|
||||
-spec cache_nodes(module(), binary()) -> [node()].
|
||||
@@ -386,7 +373,6 @@ delete_cache(Mod, User, Server) ->
|
||||
depends(_Host, _Opts) ->
|
||||
[].
|
||||
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
mod_opt_type(ram_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
||||
mod_opt_type(O) when O == use_cache; O == cache_missed ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
@@ -394,6 +380,11 @@ mod_opt_type(O) when O == cache_size; O == cache_life_time ->
|
||||
fun(I) when is_integer(I), I>0 -> I;
|
||||
(unlimited) -> infinity;
|
||||
(infinity) -> infinity
|
||||
end;
|
||||
mod_opt_type(_) ->
|
||||
[ram_db_type, iqdisc, use_cache, cache_size, cache_missed, cache_life_time].
|
||||
end.
|
||||
|
||||
mod_options(Host) ->
|
||||
[{ram_db_type, ejabberd_config:default_ram_db(Host, ?MODULE)},
|
||||
{use_cache, ejabberd_config:use_cache(Host)},
|
||||
{cache_size, ejabberd_config:cache_size(Host)},
|
||||
{cache_missed, ejabberd_config:cache_missed(Host)},
|
||||
{cache_life_time, ejabberd_config:cache_life_time(Host)}].
|
||||
|
||||
+34
-20
@@ -28,17 +28,17 @@
|
||||
-protocol({xep, 85, '2.1'}).
|
||||
-protocol({xep, 352, '0.1'}).
|
||||
|
||||
-behavior(gen_mod).
|
||||
-behaviour(gen_mod).
|
||||
|
||||
%% gen_mod callbacks.
|
||||
-export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2]).
|
||||
-export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2, mod_options/1]).
|
||||
|
||||
%% ejabberd_hooks callbacks.
|
||||
-export([filter_presence/1, filter_chat_states/1,
|
||||
filter_pep/1, filter_other/1,
|
||||
c2s_stream_started/2, add_stream_feature/2,
|
||||
c2s_copy_session/2, c2s_authenticated_packet/2,
|
||||
c2s_session_resumed/1]).
|
||||
c2s_authenticated_packet/2, csi_activity/2,
|
||||
c2s_copy_session/2, c2s_session_resumed/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -59,9 +59,9 @@
|
||||
%%--------------------------------------------------------------------
|
||||
-spec start(binary(), gen_mod:opts()) -> ok.
|
||||
start(Host, Opts) ->
|
||||
QueuePresence = gen_mod:get_opt(queue_presence, Opts, true),
|
||||
QueueChatStates = gen_mod:get_opt(queue_chat_states, Opts, true),
|
||||
QueuePEP = gen_mod:get_opt(queue_pep, Opts, true),
|
||||
QueuePresence = gen_mod:get_opt(queue_presence, Opts),
|
||||
QueueChatStates = gen_mod:get_opt(queue_chat_states, Opts),
|
||||
QueuePEP = gen_mod:get_opt(queue_pep, Opts),
|
||||
if QueuePresence; QueueChatStates; QueuePEP ->
|
||||
register_hooks(Host),
|
||||
if QueuePresence ->
|
||||
@@ -84,9 +84,9 @@ start(Host, Opts) ->
|
||||
|
||||
-spec stop(binary()) -> ok.
|
||||
stop(Host) ->
|
||||
QueuePresence = gen_mod:get_module_opt(Host, ?MODULE, queue_presence, true),
|
||||
QueueChatStates = gen_mod:get_module_opt(Host, ?MODULE, queue_chat_states, true),
|
||||
QueuePEP = gen_mod:get_module_opt(Host, ?MODULE, queue_pep, true),
|
||||
QueuePresence = gen_mod:get_module_opt(Host, ?MODULE, queue_presence),
|
||||
QueueChatStates = gen_mod:get_module_opt(Host, ?MODULE, queue_chat_states),
|
||||
QueuePEP = gen_mod:get_module_opt(Host, ?MODULE, queue_pep),
|
||||
if QueuePresence; QueueChatStates; QueuePEP ->
|
||||
unregister_hooks(Host),
|
||||
if QueuePresence ->
|
||||
@@ -109,9 +109,9 @@ stop(Host) ->
|
||||
|
||||
-spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok.
|
||||
reload(Host, NewOpts, _OldOpts) ->
|
||||
QueuePresence = gen_mod:get_opt(queue_presence, NewOpts, true),
|
||||
QueueChatStates = gen_mod:get_opt(queue_chat_states, NewOpts, true),
|
||||
QueuePEP = gen_mod:get_opt(queue_pep, NewOpts, true),
|
||||
QueuePresence = gen_mod:get_opt(queue_presence, NewOpts),
|
||||
QueueChatStates = gen_mod:get_opt(queue_chat_states, NewOpts),
|
||||
QueuePEP = gen_mod:get_opt(queue_pep, NewOpts),
|
||||
if QueuePresence; QueueChatStates; QueuePEP ->
|
||||
register_hooks(Host);
|
||||
true ->
|
||||
@@ -145,8 +145,12 @@ mod_opt_type(queue_presence) ->
|
||||
mod_opt_type(queue_chat_states) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(queue_pep) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(_) -> [queue_presence, queue_chat_states, queue_pep].
|
||||
fun(B) when is_boolean(B) -> B end.
|
||||
|
||||
mod_options(_) ->
|
||||
[{queue_presence, true},
|
||||
{queue_chat_states, true},
|
||||
{queue_pep, true}].
|
||||
|
||||
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
|
||||
depends(_Host, _Opts) ->
|
||||
@@ -160,6 +164,8 @@ register_hooks(Host) ->
|
||||
add_stream_feature, 50),
|
||||
ejabberd_hooks:add(c2s_authenticated_packet, Host, ?MODULE,
|
||||
c2s_authenticated_packet, 50),
|
||||
ejabberd_hooks:add(csi_activity, Host, ?MODULE,
|
||||
csi_activity, 50),
|
||||
ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE,
|
||||
c2s_copy_session, 50),
|
||||
ejabberd_hooks:add(c2s_session_resumed, Host, ?MODULE,
|
||||
@@ -175,6 +181,8 @@ unregister_hooks(Host) ->
|
||||
add_stream_feature, 50),
|
||||
ejabberd_hooks:delete(c2s_authenticated_packet, Host, ?MODULE,
|
||||
c2s_authenticated_packet, 50),
|
||||
ejabberd_hooks:delete(csi_activity, Host, ?MODULE,
|
||||
csi_activity, 50),
|
||||
ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE,
|
||||
c2s_copy_session, 50),
|
||||
ejabberd_hooks:delete(c2s_session_resumed, Host, ?MODULE,
|
||||
@@ -190,14 +198,20 @@ c2s_stream_started(State, _) ->
|
||||
init_csi_state(State).
|
||||
|
||||
-spec c2s_authenticated_packet(c2s_state(), xmpp_element()) -> c2s_state().
|
||||
c2s_authenticated_packet(C2SState, #csi{type = active}) ->
|
||||
C2SState1 = C2SState#{csi_state => active},
|
||||
flush_queue(C2SState1);
|
||||
c2s_authenticated_packet(C2SState, #csi{type = inactive}) ->
|
||||
C2SState#{csi_state => inactive};
|
||||
c2s_authenticated_packet(#{lserver := LServer} = C2SState, #csi{type = active}) ->
|
||||
ejabberd_hooks:run_fold(csi_activity, LServer, C2SState, [active]);
|
||||
c2s_authenticated_packet(#{lserver := LServer} = C2SState, #csi{type = inactive}) ->
|
||||
ejabberd_hooks:run_fold(csi_activity, LServer, C2SState, [inactive]);
|
||||
c2s_authenticated_packet(C2SState, _) ->
|
||||
C2SState.
|
||||
|
||||
-spec csi_activity(c2s_state(), active | inactive) -> c2s_state().
|
||||
csi_activity(C2SState, active) ->
|
||||
C2SState1 = C2SState#{csi_state => active},
|
||||
flush_queue(C2SState1);
|
||||
csi_activity(C2SState, inactive) ->
|
||||
C2SState#{csi_state => inactive}.
|
||||
|
||||
-spec c2s_copy_session(c2s_state(), c2s_state()) -> c2s_state().
|
||||
c2s_copy_session(C2SState, #{csi_queue := Q}) ->
|
||||
C2SState#{csi_queue => Q};
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
get_local_features/5, get_local_items/5,
|
||||
adhoc_local_items/4, adhoc_local_commands/4,
|
||||
get_sm_identity/5, get_sm_features/5, get_sm_items/5,
|
||||
adhoc_sm_items/4, adhoc_sm_commands/4, mod_opt_type/1,
|
||||
adhoc_sm_items/4, adhoc_sm_commands/4, mod_options/1,
|
||||
depends/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@@ -1528,8 +1528,11 @@ set_form(From, Host, ?NS_ADMINL(<<"add-user">>), _Lang,
|
||||
true = lists:member(Server, ?MYHOSTS),
|
||||
true = Server == Host orelse
|
||||
get_permission_level(From) == global,
|
||||
ejabberd_auth:try_register(User, Server, Password),
|
||||
{result, undefined};
|
||||
case ejabberd_auth:try_register(User, Server, Password) of
|
||||
ok -> {result, undefined};
|
||||
{error, exists} -> {error, xmpp:err_conflict()};
|
||||
{error, not_allowed} -> {error, xmpp:err_not_allowed()}
|
||||
end;
|
||||
set_form(From, Host, ?NS_ADMINL(<<"delete-user">>),
|
||||
_Lang, XData) ->
|
||||
AccountStringList = get_values(<<"accountjids">>,
|
||||
@@ -1809,4 +1812,4 @@ set_sm_form(User, Server, <<"config">>,
|
||||
set_sm_form(_User, _Server, _Node, _Request) ->
|
||||
{error, xmpp:err_service_unavailable()}.
|
||||
|
||||
mod_opt_type(_) -> [].
|
||||
mod_options(_) -> [].
|
||||
|
||||
+11
-10
@@ -31,7 +31,7 @@
|
||||
-behaviour(gen_mod).
|
||||
|
||||
%% API
|
||||
-export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2]).
|
||||
-export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2, mod_options/1]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
@@ -61,7 +61,6 @@ stop(Host) ->
|
||||
reload(_Host, _NewOpts, _OldOpts) ->
|
||||
ok.
|
||||
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
mod_opt_type(namespaces) ->
|
||||
fun(L) ->
|
||||
lists:map(
|
||||
@@ -70,9 +69,10 @@ mod_opt_type(namespaces) ->
|
||||
Access = proplists:get_value(access, Opts, none),
|
||||
{NS, Attrs, Access}
|
||||
end, L)
|
||||
end;
|
||||
mod_opt_type(_) ->
|
||||
[namespaces, iqdisc].
|
||||
end.
|
||||
|
||||
mod_options(_Host) ->
|
||||
[{namespaces, []}].
|
||||
|
||||
depends(_, _) ->
|
||||
[].
|
||||
@@ -151,7 +151,7 @@ handle_cast({component_connected, Host}, State) ->
|
||||
ServerHost = State#state.server_host,
|
||||
To = jid:make(Host),
|
||||
NSAttrsAccessList = gen_mod:get_module_opt(
|
||||
ServerHost, ?MODULE, namespaces, []),
|
||||
ServerHost, ?MODULE, namespaces),
|
||||
lists:foreach(
|
||||
fun({NS, _Attrs, Access}) ->
|
||||
case acl:match_rule(ServerHost, Access, To) of
|
||||
@@ -240,7 +240,7 @@ process_iq(#iq{to = To, lang = Lang, sub_els = [SubEl]} = IQ, Type) ->
|
||||
case dict:find({NS, Type}, Delegations) of
|
||||
{ok, {Host, _}} ->
|
||||
Delegation = #delegation{
|
||||
forwarded = #forwarded{xml_els = [xmpp:encode(IQ)]}},
|
||||
forwarded = #forwarded{sub_els = [IQ]}},
|
||||
NewFrom = jid:make(LServer),
|
||||
NewTo = jid:make(Host),
|
||||
ejabberd_router:route_iq(
|
||||
@@ -259,9 +259,10 @@ process_iq(#iq{to = To, lang = Lang, sub_els = [SubEl]} = IQ, Type) ->
|
||||
process_iq_result(#iq{from = From, to = To, id = ID, lang = Lang} = IQ,
|
||||
#iq{type = result} = ResIQ) ->
|
||||
try
|
||||
#delegation{forwarded = #forwarded{xml_els = [SubEl]}} =
|
||||
CodecOpts = ejabberd_config:codec_options(To#jid.lserver),
|
||||
#delegation{forwarded = #forwarded{sub_els = [SubEl]}} =
|
||||
xmpp:get_subtag(ResIQ, #delegation{}),
|
||||
case xmpp:decode(SubEl, ?NS_CLIENT, [ignore_els]) of
|
||||
case xmpp:decode(SubEl, ?NS_CLIENT, CodecOpts) of
|
||||
#iq{from = To, to = From, type = Type, id = ID} = Reply
|
||||
when Type == error; Type == result ->
|
||||
ejabberd_router:route(Reply)
|
||||
@@ -292,7 +293,7 @@ process_disco_info(State, Type, Host, NS, Info) ->
|
||||
sub_els = [#delegation{delegated = [#delegated{ns = NS}]}]},
|
||||
Delegations = dict:store({NS, Type}, {Host, Info}, State#state.delegations),
|
||||
gen_iq_handler:add_iq_handler(Type, State#state.server_host, NS,
|
||||
?MODULE, Type, gen_iq_handler:iqdisc(Host)),
|
||||
?MODULE, Type),
|
||||
ejabberd_router:route(Msg),
|
||||
?INFO_MSG("Namespace '~s' is delegated to external component '~s'",
|
||||
[NS, Host]),
|
||||
|
||||
+16
-32
@@ -37,7 +37,8 @@
|
||||
get_local_features/5, get_local_services/5,
|
||||
process_sm_iq_items/1, process_sm_iq_info/1,
|
||||
get_sm_identity/5, get_sm_features/5, get_sm_items/5,
|
||||
get_info/5, transform_module_options/1, mod_opt_type/1, depends/2]).
|
||||
get_info/5, transform_module_options/1, mod_opt_type/1,
|
||||
mod_options/1, depends/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -50,23 +51,20 @@
|
||||
-type items_acc() :: {error, stanza_error()} | {result, [disco_item()]} | empty.
|
||||
|
||||
start(Host, Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
||||
?NS_DISCO_ITEMS, ?MODULE,
|
||||
process_local_iq_items, IQDisc),
|
||||
process_local_iq_items),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
||||
?NS_DISCO_INFO, ?MODULE,
|
||||
process_local_iq_info, IQDisc),
|
||||
process_local_iq_info),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||
?NS_DISCO_ITEMS, ?MODULE, process_sm_iq_items,
|
||||
IQDisc),
|
||||
?NS_DISCO_ITEMS, ?MODULE, process_sm_iq_items),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||
?NS_DISCO_INFO, ?MODULE, process_sm_iq_info,
|
||||
IQDisc),
|
||||
?NS_DISCO_INFO, ?MODULE, process_sm_iq_info),
|
||||
catch ets:new(disco_extra_domains,
|
||||
[named_table, ordered_set, public,
|
||||
{heir, erlang:group_leader(), none}]),
|
||||
ExtraDomains = gen_mod:get_opt(extra_domains, Opts, []),
|
||||
ExtraDomains = gen_mod:get_opt(extra_domains, Opts),
|
||||
lists:foreach(fun (Domain) ->
|
||||
register_extra_domain(Host, Domain)
|
||||
end,
|
||||
@@ -115,7 +113,7 @@ stop(Host) ->
|
||||
ok.
|
||||
|
||||
reload(Host, NewOpts, OldOpts) ->
|
||||
case gen_mod:is_equal_opt(extra_domains, NewOpts, OldOpts, []) of
|
||||
case gen_mod:is_equal_opt(extra_domains, NewOpts, OldOpts) of
|
||||
{false, NewDomains, OldDomains} ->
|
||||
lists:foreach(
|
||||
fun(Domain) ->
|
||||
@@ -127,23 +125,6 @@ reload(Host, NewOpts, OldOpts) ->
|
||||
end, OldDomains -- NewDomains);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
|
||||
{false, IQDisc, _} ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
||||
?NS_DISCO_ITEMS, ?MODULE,
|
||||
process_local_iq_items, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
||||
?NS_DISCO_INFO, ?MODULE,
|
||||
process_local_iq_info, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||
?NS_DISCO_ITEMS, ?MODULE, process_sm_iq_items,
|
||||
IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||
?NS_DISCO_INFO, ?MODULE, process_sm_iq_info,
|
||||
IQDisc);
|
||||
true ->
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec register_extra_domain(binary(), binary()) -> true.
|
||||
@@ -197,7 +178,7 @@ process_local_iq_info(#iq{type = get, lang = Lang,
|
||||
binary(), binary()) -> [identity()].
|
||||
get_local_identity(Acc, _From, To, <<"">>, _Lang) ->
|
||||
Host = To#jid.lserver,
|
||||
Name = gen_mod:get_module_opt(Host, ?MODULE, name, ?T("ejabberd")),
|
||||
Name = gen_mod:get_module_opt(Host, ?MODULE, name),
|
||||
Acc ++ [#identity{category = <<"server">>,
|
||||
type = <<"im">>,
|
||||
name = Name}];
|
||||
@@ -440,7 +421,7 @@ get_info(Acc, _, _, _Node, _) -> Acc.
|
||||
|
||||
-spec get_fields(binary(), module()) -> [xdata_field()].
|
||||
get_fields(Host, Module) ->
|
||||
Fields = gen_mod:get_module_opt(Host, ?MODULE, server_info, []),
|
||||
Fields = gen_mod:get_module_opt(Host, ?MODULE, server_info),
|
||||
Fields1 = lists:filter(fun ({Modules, _, _}) ->
|
||||
case Modules of
|
||||
all -> true;
|
||||
@@ -457,7 +438,6 @@ depends(_Host, _Opts) ->
|
||||
|
||||
mod_opt_type(extra_domains) ->
|
||||
fun (Hs) -> [iolist_to_binary(H) || H <- Hs] end;
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
mod_opt_type(name) -> fun iolist_to_binary/1;
|
||||
mod_opt_type(server_info) ->
|
||||
fun (L) ->
|
||||
@@ -468,5 +448,9 @@ mod_opt_type(server_info) ->
|
||||
{Mods, Name, URLs}
|
||||
end,
|
||||
L)
|
||||
end;
|
||||
mod_opt_type(_) -> [extra_domains, iqdisc, server_info, name].
|
||||
end.
|
||||
|
||||
mod_options(_Host) ->
|
||||
[{extra_domains, []},
|
||||
{server_info, []},
|
||||
{name, ?T("ejabberd")}].
|
||||
|
||||
+8
-9
@@ -36,7 +36,7 @@
|
||||
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3,
|
||||
mod_opt_type/1, depends/2]).
|
||||
mod_opt_type/1, depends/2, mod_options/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -63,8 +63,10 @@ depends(_Host, _Opts) ->
|
||||
|
||||
mod_opt_type(host) -> fun iolist_to_binary/1;
|
||||
mod_opt_type(hosts) ->
|
||||
fun(L) -> lists:map(fun iolist_to_binary/1, L) end;
|
||||
mod_opt_type(_) -> [host, hosts].
|
||||
fun(L) -> lists:map(fun iolist_to_binary/1, L) end.
|
||||
|
||||
mod_options(_Host) ->
|
||||
[{host, <<"echo.@HOST@">>}, {hosts, []}].
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
@@ -79,8 +81,7 @@ mod_opt_type(_) -> [host, hosts].
|
||||
%%--------------------------------------------------------------------
|
||||
init([Host, Opts]) ->
|
||||
process_flag(trap_exit, true),
|
||||
Hosts = gen_mod:get_opt_hosts(Host, Opts,
|
||||
<<"echo.@HOST@">>),
|
||||
Hosts = gen_mod:get_opt_hosts(Host, Opts),
|
||||
lists:foreach(
|
||||
fun(H) ->
|
||||
ejabberd_router:register_route(H, Host)
|
||||
@@ -106,10 +107,8 @@ handle_call(stop, _From, State) ->
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_cast({reload, Host, NewOpts, OldOpts}, State) ->
|
||||
NewMyHosts = gen_mod:get_opt_hosts(Host, NewOpts,
|
||||
<<"echo.@HOST@">>),
|
||||
OldMyHosts = gen_mod:get_opt_hosts(Host, OldOpts,
|
||||
<<"echo.@HOST@">>),
|
||||
NewMyHosts = gen_mod:get_opt_hosts(Host, NewOpts),
|
||||
OldMyHosts = gen_mod:get_opt_hosts(Host, OldOpts),
|
||||
lists:foreach(
|
||||
fun(H) ->
|
||||
ejabberd_router:unregister_route(H)
|
||||
|
||||
+10
-11
@@ -34,15 +34,13 @@
|
||||
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3,
|
||||
mod_opt_type/1, depends/2]).
|
||||
mod_opt_type/1, mod_options/1, depends/2]).
|
||||
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("xmpp.hrl").
|
||||
|
||||
-define(C2S_AUTH_BAN_LIFETIME, 3600). %% 1 hour
|
||||
-define(C2S_MAX_AUTH_FAILURES, 20).
|
||||
-define(CLEAN_INTERVAL, timer:minutes(10)).
|
||||
|
||||
-record(state, {host = <<"">> :: binary()}).
|
||||
@@ -58,11 +56,9 @@ c2s_auth_result(#{ip := {Addr, _}, lserver := LServer} = State, false, _User) ->
|
||||
State;
|
||||
false ->
|
||||
BanLifetime = gen_mod:get_module_opt(
|
||||
LServer, ?MODULE, c2s_auth_ban_lifetime,
|
||||
?C2S_AUTH_BAN_LIFETIME),
|
||||
LServer, ?MODULE, c2s_auth_ban_lifetime),
|
||||
MaxFailures = gen_mod:get_module_opt(
|
||||
LServer, ?MODULE, c2s_max_auth_failures,
|
||||
?C2S_MAX_AUTH_FAILURES),
|
||||
LServer, ?MODULE, c2s_max_auth_failures),
|
||||
UnbanTS = p1_time_compat:system_time(seconds) + BanLifetime,
|
||||
Attempts = case ets:lookup(failed_auth, Addr) of
|
||||
[{Addr, N, _, _}] ->
|
||||
@@ -179,7 +175,7 @@ log_and_disconnect(#{ip := {Addr, _}, lang := Lang} = State, Attempts, UnbanTS)
|
||||
{stop, ejabberd_c2s:send(State, Err)}.
|
||||
|
||||
is_whitelisted(Host, Addr) ->
|
||||
Access = gen_mod:get_module_opt(Host, ?MODULE, access, none),
|
||||
Access = gen_mod:get_module_opt(Host, ?MODULE, access),
|
||||
acl:match_rule(Host, Access, Addr) == allow.
|
||||
|
||||
seconds_to_now(Secs) ->
|
||||
@@ -194,6 +190,9 @@ mod_opt_type(access) ->
|
||||
mod_opt_type(c2s_auth_ban_lifetime) ->
|
||||
fun (T) when is_integer(T), T > 0 -> T end;
|
||||
mod_opt_type(c2s_max_auth_failures) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
mod_opt_type(_) ->
|
||||
[access, c2s_auth_ban_lifetime, c2s_max_auth_failures].
|
||||
fun (I) when is_integer(I), I > 0 -> I end.
|
||||
|
||||
mod_options(_Host) ->
|
||||
[{access, none},
|
||||
{c2s_auth_ban_lifetime, 3600}, %% one hour
|
||||
{c2s_max_auth_failures, 20}].
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
%% add_commands allow exporting a class of commands, from
|
||||
%% open: methods is not risky and can be called by without any access check
|
||||
%% restricted (default): the same, but will appear only in ejabberdctl list.
|
||||
%% admin – auth is required with XMLRPC and HTTP API and checked for admin priviledges, works as usual in ejabberdctl.
|
||||
%% admin – auth is required with XMLRPC and HTTP API and checked for admin privileges, works as usual in ejabberdctl.
|
||||
%% user - can be used through XMLRPC and HTTP API, even by user. Only admin can use the commands for other users.
|
||||
%%
|
||||
%% Then to perform an action, send a POST request to the following URL:
|
||||
@@ -74,7 +74,8 @@
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2, stop/1, reload/3, process/2, mod_opt_type/1, depends/2]).
|
||||
-export([start/2, stop/1, reload/3, process/2, mod_opt_type/1, depends/2,
|
||||
mod_options/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("xmpp.hrl").
|
||||
@@ -300,7 +301,7 @@ handle(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) ->
|
||||
throw:{not_allowed, Msg} ->
|
||||
{401, iolist_to_binary(Msg)};
|
||||
throw:{error, account_unprivileged} ->
|
||||
{403, 31, <<"Command need to be run with admin priviledge.">>};
|
||||
{403, 31, <<"Command need to be run with admin privilege.">>};
|
||||
throw:{error, access_rules_unauthorized} ->
|
||||
{403, 32, <<"AccessRules: Account does not have the right to perform the operation.">>};
|
||||
throw:{invalid_parameter, Msg} ->
|
||||
@@ -551,7 +552,7 @@ hide_sensitive_args(NonListArgs) ->
|
||||
NonListArgs.
|
||||
|
||||
permission_addon() ->
|
||||
Access = gen_mod:get_module_opt(global, ?MODULE, admin_ip_access, none),
|
||||
Access = gen_mod:get_module_opt(global, ?MODULE, admin_ip_access),
|
||||
Rules = acl:resolve_access(Access, global),
|
||||
R = case Rules of
|
||||
all ->
|
||||
@@ -576,5 +577,5 @@ permission_addon() ->
|
||||
end, {1, []}, R),
|
||||
Res.
|
||||
|
||||
mod_opt_type(admin_ip_access) -> fun acl:access_rules_validator/1;
|
||||
mod_opt_type(_) -> [admin_ip_access].
|
||||
mod_opt_type(admin_ip_access) -> fun acl:access_rules_validator/1.
|
||||
mod_options(_) -> [{admin_ip_access, none}].
|
||||
|
||||
+33
-52
@@ -43,7 +43,7 @@
|
||||
%% utility for other http modules
|
||||
-export([content_type/3]).
|
||||
|
||||
-export([reopen_log/0, mod_opt_type/1, depends/2]).
|
||||
-export([reopen_log/0, mod_opt_type/1, mod_options/1, depends/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -69,9 +69,6 @@
|
||||
-define(HTTP_ERR_HOST_UNKNOWN,
|
||||
{-1, 410, [], <<"Host unknown">>}).
|
||||
|
||||
-define(DEFAULT_CONTENT_TYPE,
|
||||
<<"application/octet-stream">>).
|
||||
|
||||
-define(DEFAULT_CONTENT_TYPES,
|
||||
[{<<".css">>, <<"text/css">>},
|
||||
{<<".gif">>, <<"image/gif">>},
|
||||
@@ -126,24 +123,19 @@ init([Host, Opts]) ->
|
||||
|
||||
initialize(Host, Opts) ->
|
||||
DocRoot = gen_mod:get_opt(docroot, Opts),
|
||||
check_docroot_defined(DocRoot, Host),
|
||||
DRInfo = check_docroot_exists(DocRoot),
|
||||
check_docroot_is_dir(DRInfo, DocRoot),
|
||||
check_docroot_is_readable(DRInfo, DocRoot),
|
||||
AccessLog = gen_mod:get_opt(accesslog, Opts),
|
||||
AccessLogFD = try_open_log(AccessLog, Host),
|
||||
DirectoryIndices = gen_mod:get_opt(directory_indices, Opts, []),
|
||||
CustomHeaders = gen_mod:get_opt(custom_headers, Opts, []),
|
||||
DefaultContentType = gen_mod:get_opt(default_content_type, Opts,
|
||||
?DEFAULT_CONTENT_TYPE),
|
||||
UserAccess0 = gen_mod:get_opt(must_authenticate_with, Opts, []),
|
||||
DirectoryIndices = gen_mod:get_opt(directory_indices, Opts),
|
||||
CustomHeaders = gen_mod:get_opt(custom_headers, Opts),
|
||||
DefaultContentType = gen_mod:get_opt(default_content_type, Opts),
|
||||
UserAccess0 = gen_mod:get_opt(must_authenticate_with, Opts),
|
||||
UserAccess = case UserAccess0 of
|
||||
[] -> none;
|
||||
_ ->
|
||||
dict:from_list(UserAccess0)
|
||||
end,
|
||||
ContentTypes = build_list_content_types(
|
||||
gen_mod:get_opt(content_types, Opts, []),
|
||||
gen_mod:get_opt(content_types, Opts),
|
||||
?DEFAULT_CONTENT_TYPES),
|
||||
?DEBUG("known content types: ~s",
|
||||
[str:join([[$*, K, " -> ", V] || {K, V} <- ContentTypes],
|
||||
@@ -173,37 +165,6 @@ build_list_content_types(AdminCTsUnsorted, DefaultCTsUnsorted) ->
|
||||
|| {Extension, Value} <- CTsUnfiltered,
|
||||
Value /= undefined].
|
||||
|
||||
check_docroot_defined(DocRoot, Host) ->
|
||||
case DocRoot of
|
||||
undefined -> throw({undefined_docroot_option, Host});
|
||||
_ -> ok
|
||||
end.
|
||||
|
||||
check_docroot_exists(DocRoot) ->
|
||||
case filelib:ensure_dir(filename:join(DocRoot, "foo")) of
|
||||
ok ->
|
||||
case file:read_file_info(DocRoot) of
|
||||
{error, Reason} ->
|
||||
throw({error_access_docroot, DocRoot, Reason});
|
||||
{ok, FI} -> FI
|
||||
end;
|
||||
{error, Reason} ->
|
||||
throw({error_access_docroot, DocRoot, Reason})
|
||||
end.
|
||||
|
||||
check_docroot_is_dir(DRInfo, DocRoot) ->
|
||||
case DRInfo#file_info.type of
|
||||
directory -> ok;
|
||||
_ -> throw({docroot_not_directory, DocRoot})
|
||||
end.
|
||||
|
||||
check_docroot_is_readable(DRInfo, DocRoot) ->
|
||||
case DRInfo#file_info.access of
|
||||
read -> ok;
|
||||
read_write -> ok;
|
||||
_ -> throw({docroot_not_readable, DocRoot})
|
||||
end.
|
||||
|
||||
try_open_log(undefined, _Host) ->
|
||||
undefined;
|
||||
try_open_log(FN, _Host) ->
|
||||
@@ -504,7 +465,10 @@ ip_to_string(Address) when size(Address) == 8 ->
|
||||
Parts = lists:map(fun (Int) -> io_lib:format("~.16B", [Int]) end, tuple_to_list(Address)),
|
||||
string:to_lower(lists:flatten(join(Parts, ":"))).
|
||||
|
||||
mod_opt_type(accesslog) -> fun iolist_to_binary/1;
|
||||
mod_opt_type(accesslog) ->
|
||||
fun(undefined) -> undefined;
|
||||
(File) -> iolist_to_binary(File)
|
||||
end;
|
||||
mod_opt_type(content_types) ->
|
||||
fun(L) when is_list(L) ->
|
||||
lists:map(
|
||||
@@ -519,15 +483,32 @@ mod_opt_type(default_content_type) ->
|
||||
fun iolist_to_binary/1;
|
||||
mod_opt_type(directory_indices) ->
|
||||
fun (L) when is_list(L) -> L end;
|
||||
mod_opt_type(docroot) -> fun (A) -> A end;
|
||||
mod_opt_type(docroot) ->
|
||||
fun(S) ->
|
||||
Path = iolist_to_binary(S),
|
||||
case filelib:ensure_dir(filename:join(Path, "foo")) of
|
||||
ok ->
|
||||
Path;
|
||||
{error, Why} ->
|
||||
?ERROR_MSG("Failed to create directory ~s: ~s",
|
||||
[Path, file:format_error(Why)]),
|
||||
erlang:error(badarg)
|
||||
end
|
||||
end;
|
||||
mod_opt_type(must_authenticate_with) ->
|
||||
fun (L) when is_list(L) ->
|
||||
lists:map(fun(UP) when is_binary(UP) ->
|
||||
[K, V] = binary:split(UP, <<":">>),
|
||||
{K, V}
|
||||
end, L)
|
||||
end;
|
||||
mod_opt_type(_) ->
|
||||
[accesslog, content_types, custom_headers,
|
||||
default_content_type, directory_indices, docroot,
|
||||
must_authenticate_with].
|
||||
end.
|
||||
|
||||
mod_options(_) ->
|
||||
[{accesslog, undefined},
|
||||
{content_types, []},
|
||||
{default_content_type, <<"application/octet-stream">>},
|
||||
{custom_headers, []},
|
||||
{directory_indices, []},
|
||||
{must_authenticate_with, []},
|
||||
%% Required option
|
||||
docroot].
|
||||
|
||||
+88
-126
@@ -31,7 +31,7 @@
|
||||
-define(SERVICE_REQUEST_TIMEOUT, 5000). % 5 seconds.
|
||||
-define(SLOT_TIMEOUT, 18000000). % 5 hours.
|
||||
-define(FORMAT(Error), file:format_error(Error)).
|
||||
-define(URL_ENC(URL), binary_to_list(ejabberd_http:url_encode(URL))).
|
||||
-define(URL_ENC(URL), binary_to_list(misc:url_encode(URL))).
|
||||
-define(ADDR_TO_STR(IP), ejabberd_config:may_hide_data(misc:ip_to_list(IP))).
|
||||
-define(STR_TO_INT(Str, B), binary_to_integer(iolist_to_binary(Str), B)).
|
||||
-define(DEFAULT_CONTENT_TYPE, <<"application/octet-stream">>).
|
||||
@@ -43,6 +43,7 @@
|
||||
{<<".gz">>, <<"application/x-gzip">>},
|
||||
{<<".jpeg">>, <<"image/jpeg">>},
|
||||
{<<".jpg">>, <<"image/jpeg">>},
|
||||
{<<".m4a">>, <<"audio/mp4">>},
|
||||
{<<".mp3">>, <<"audio/mpeg">>},
|
||||
{<<".mp4">>, <<"video/mp4">>},
|
||||
{<<".mpeg">>, <<"video/mpeg">>},
|
||||
@@ -66,7 +67,8 @@
|
||||
-export([start/2,
|
||||
stop/1,
|
||||
depends/2,
|
||||
mod_opt_type/1]).
|
||||
mod_opt_type/1,
|
||||
mod_options/1]).
|
||||
|
||||
%% gen_server callbacks.
|
||||
-export([init/1,
|
||||
@@ -124,9 +126,8 @@
|
||||
%% gen_mod/supervisor callbacks.
|
||||
%%--------------------------------------------------------------------
|
||||
-spec start(binary(), gen_mod:opts()) -> {ok, pid()}.
|
||||
|
||||
start(ServerHost, Opts) ->
|
||||
case gen_mod:get_opt(rm_on_unregister, Opts, true) of
|
||||
case gen_mod:get_opt(rm_on_unregister, Opts) of
|
||||
true ->
|
||||
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE,
|
||||
remove_user, 50);
|
||||
@@ -137,9 +138,8 @@ start(ServerHost, Opts) ->
|
||||
gen_mod:start_child(?MODULE, ServerHost, Opts, Proc).
|
||||
|
||||
-spec stop(binary()) -> ok | {error, any()}.
|
||||
|
||||
stop(ServerHost) ->
|
||||
case gen_mod:get_module_opt(ServerHost, ?MODULE, rm_on_unregister, true) of
|
||||
case gen_mod:get_module_opt(ServerHost, ?MODULE, rm_on_unregister) of
|
||||
true ->
|
||||
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE,
|
||||
remove_user, 50);
|
||||
@@ -150,7 +150,6 @@ stop(ServerHost) ->
|
||||
gen_mod:stop_child(Proc).
|
||||
|
||||
-spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
|
||||
|
||||
mod_opt_type(host) ->
|
||||
fun iolist_to_binary/1;
|
||||
mod_opt_type(hosts) ->
|
||||
@@ -170,9 +169,13 @@ mod_opt_type(jid_in_url) ->
|
||||
(node) -> node
|
||||
end;
|
||||
mod_opt_type(file_mode) ->
|
||||
fun(Mode) -> ?STR_TO_INT(Mode, 8) end;
|
||||
fun(undefined) -> undefined;
|
||||
(Mode) -> ?STR_TO_INT(Mode, 8)
|
||||
end;
|
||||
mod_opt_type(dir_mode) ->
|
||||
fun(Mode) -> ?STR_TO_INT(Mode, 8) end;
|
||||
fun(undefined) -> undefined;
|
||||
(Mode) -> ?STR_TO_INT(Mode, 8)
|
||||
end;
|
||||
mod_opt_type(docroot) ->
|
||||
fun iolist_to_binary/1;
|
||||
mod_opt_type(put_url) ->
|
||||
@@ -181,11 +184,13 @@ mod_opt_type(put_url) ->
|
||||
end;
|
||||
mod_opt_type(get_url) ->
|
||||
fun(<<"http://", _/binary>> = URL) -> URL;
|
||||
(<<"https://", _/binary>> = URL) -> URL
|
||||
(<<"https://", _/binary>> = URL) -> URL;
|
||||
(undefined) -> undefined
|
||||
end;
|
||||
mod_opt_type(service_url) ->
|
||||
fun(<<"http://", _/binary>> = URL) -> URL;
|
||||
(<<"https://", _/binary>> = URL) -> URL
|
||||
(<<"https://", _/binary>> = URL) -> URL;
|
||||
(undefined) -> undefined
|
||||
end;
|
||||
mod_opt_type(custom_headers) ->
|
||||
fun(Headers) ->
|
||||
@@ -196,39 +201,66 @@ mod_opt_type(custom_headers) ->
|
||||
mod_opt_type(rm_on_unregister) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(thumbnail) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(_) ->
|
||||
[host, hosts, name, access, max_size, secret_length, jid_in_url, file_mode,
|
||||
dir_mode, docroot, put_url, get_url, service_url, custom_headers,
|
||||
rm_on_unregister, thumbnail].
|
||||
fun(true) ->
|
||||
case eimp:supported_formats() of
|
||||
[] ->
|
||||
?WARNING_MSG("ejabberd is built without image converter "
|
||||
"support, option '~s' is ignored",
|
||||
[thumbnail]),
|
||||
erlang:error(badarg);
|
||||
_ ->
|
||||
true
|
||||
end;
|
||||
(false) ->
|
||||
false
|
||||
end.
|
||||
|
||||
-spec mod_options(binary()) -> [{atom(), any()}].
|
||||
mod_options(_Host) ->
|
||||
[{host, <<"upload.@HOST@">>},
|
||||
{hosts, []},
|
||||
{name, ?T("HTTP File Upload")},
|
||||
{access, local},
|
||||
{max_size, 104857600},
|
||||
{secret_length, 40},
|
||||
{jid_in_url, sha1},
|
||||
{file_mode, undefined},
|
||||
{dir_mode, undefined},
|
||||
{docroot, <<"@HOME@/upload">>},
|
||||
{put_url, <<"http://@HOST@:5444">>},
|
||||
{get_url, undefined},
|
||||
{service_url, undefined},
|
||||
{custom_headers, []},
|
||||
{rm_on_unregister, true},
|
||||
{thumbnail, true}].
|
||||
|
||||
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
|
||||
|
||||
depends(_Host, _Opts) ->
|
||||
[].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server callbacks.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec init(list()) -> {ok, state()}.
|
||||
|
||||
init([ServerHost, Opts]) ->
|
||||
process_flag(trap_exit, true),
|
||||
Hosts = gen_mod:get_opt_hosts(ServerHost, Opts, <<"upload.@HOST@">>),
|
||||
Name = gen_mod:get_opt(name, Opts, ?T("HTTP File Upload")),
|
||||
Access = gen_mod:get_opt(access, Opts, local),
|
||||
MaxSize = gen_mod:get_opt(max_size, Opts, 104857600),
|
||||
SecretLength = gen_mod:get_opt(secret_length, Opts, 40),
|
||||
JIDinURL = gen_mod:get_opt(jid_in_url, Opts, sha1),
|
||||
DocRoot = gen_mod:get_opt(docroot, Opts, <<"@HOME@/upload">>),
|
||||
Hosts = gen_mod:get_opt_hosts(ServerHost, Opts),
|
||||
Name = gen_mod:get_opt(name, Opts),
|
||||
Access = gen_mod:get_opt(access, Opts),
|
||||
MaxSize = gen_mod:get_opt(max_size, Opts),
|
||||
SecretLength = gen_mod:get_opt(secret_length, Opts),
|
||||
JIDinURL = gen_mod:get_opt(jid_in_url, Opts),
|
||||
DocRoot = gen_mod:get_opt(docroot, Opts),
|
||||
FileMode = gen_mod:get_opt(file_mode, Opts),
|
||||
DirMode = gen_mod:get_opt(dir_mode, Opts),
|
||||
PutURL = gen_mod:get_opt(put_url, Opts, <<"http://@HOST@:5444">>),
|
||||
GetURL = gen_mod:get_opt(get_url, Opts, PutURL),
|
||||
PutURL = gen_mod:get_opt(put_url, Opts),
|
||||
GetURL = case gen_mod:get_opt(get_url, Opts) of
|
||||
undefined -> PutURL;
|
||||
URL -> URL
|
||||
end,
|
||||
ServiceURL = gen_mod:get_opt(service_url, Opts),
|
||||
Thumbnail = gen_mod:get_opt(thumbnail, Opts, true),
|
||||
CustomHeaders = gen_mod:get_opt(custom_headers, Opts, []),
|
||||
Thumbnail = gen_mod:get_opt(thumbnail, Opts),
|
||||
CustomHeaders = gen_mod:get_opt(custom_headers, Opts),
|
||||
DocRoot1 = expand_home(str:strip(DocRoot, right, $/)),
|
||||
DocRoot2 = expand_host(DocRoot1, ServerHost),
|
||||
case DirMode of
|
||||
@@ -237,20 +269,6 @@ init([ServerHost, Opts]) ->
|
||||
Mode ->
|
||||
file:change_mode(DocRoot2, Mode)
|
||||
end,
|
||||
case Thumbnail of
|
||||
true ->
|
||||
case misc:have_eimp() of
|
||||
false ->
|
||||
?ERROR_MSG("ejabberd is built without graphics support, "
|
||||
"please rebuild it with --enable-graphics or "
|
||||
"set 'thumbnail: false' for module '~s' in "
|
||||
"ejabberd.yml", [?MODULE]);
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ejabberd_router:register_route(Host, ServerHost)
|
||||
@@ -271,7 +289,6 @@ init([ServerHost, Opts]) ->
|
||||
pos_integer() | undefined,
|
||||
pos_integer() | undefined}, state()} |
|
||||
{reply, {error, atom()}, state()} | {noreply, state()}.
|
||||
|
||||
handle_call({use_slot, Slot, Size}, _From,
|
||||
#state{file_mode = FileMode,
|
||||
dir_mode = DirMode,
|
||||
@@ -301,13 +318,11 @@ handle_call(Request, From, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
-spec handle_cast(_, state()) -> {noreply, state()}.
|
||||
|
||||
handle_cast(Request, State) ->
|
||||
?ERROR_MSG("Got unexpected request: ~p", [Request]),
|
||||
{noreply, State}.
|
||||
|
||||
-spec handle_info(timeout | _, state()) -> {noreply, state()}.
|
||||
|
||||
handle_info({route, #iq{lang = Lang} = Packet}, State) ->
|
||||
try xmpp:decode_els(Packet) of
|
||||
IQ ->
|
||||
@@ -339,13 +354,11 @@ handle_info(Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
-spec terminate(normal | shutdown | {shutdown, _} | _, state()) -> ok.
|
||||
|
||||
terminate(Reason, #state{server_host = ServerHost, hosts = Hosts}) ->
|
||||
?DEBUG("Stopping HTTP upload process for ~s: ~p", [ServerHost, Reason]),
|
||||
lists:foreach(fun ejabberd_router:unregister_route/1, Hosts).
|
||||
|
||||
-spec code_change({down, _} | _, state(), _) -> {ok, state()}.
|
||||
|
||||
code_change(_OldVsn, #state{server_host = ServerHost} = State, _Extra) ->
|
||||
?DEBUG("Updating HTTP upload process for ~s", [ServerHost]),
|
||||
{ok, State}.
|
||||
@@ -353,10 +366,8 @@ code_change(_OldVsn, #state{server_host = ServerHost} = State, _Extra) ->
|
||||
%%--------------------------------------------------------------------
|
||||
%% ejabberd_http callback.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec process([binary()], #request{})
|
||||
-> {pos_integer(), [{binary(), binary()}], binary()}.
|
||||
|
||||
process(LocalPath, #request{method = Method, host = Host, ip = IP})
|
||||
when length(LocalPath) < 3,
|
||||
Method == 'PUT' orelse
|
||||
@@ -461,25 +472,20 @@ process(_LocalPath, #request{method = Method, host = Host, ip = IP}) ->
|
||||
%%--------------------------------------------------------------------
|
||||
%% Exported utility functions.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec get_proc_name(binary(), atom()) -> atom().
|
||||
|
||||
get_proc_name(ServerHost, ModuleName) ->
|
||||
PutURL = gen_mod:get_module_opt(ServerHost, ?MODULE, put_url,
|
||||
<<"http://@HOST@">>),
|
||||
PutURL = gen_mod:get_module_opt(ServerHost, ?MODULE, put_url),
|
||||
{ok, {_Scheme, _UserInfo, Host, _Port, Path, _Query}} =
|
||||
http_uri:parse(binary_to_list(expand_host(PutURL, ServerHost))),
|
||||
ProcPrefix = list_to_binary(string:strip(Host ++ Path, right, $/)),
|
||||
gen_mod:get_module_proc(ProcPrefix, ModuleName).
|
||||
|
||||
-spec expand_home(binary()) -> binary().
|
||||
|
||||
expand_home(Input) ->
|
||||
{ok, [[Home]]} = init:get_argument(home),
|
||||
misc:expand_keyword(<<"@HOME@">>, Input, Home).
|
||||
|
||||
-spec expand_host(binary(), binary()) -> binary().
|
||||
|
||||
expand_host(Input, Host) ->
|
||||
misc:expand_keyword(<<"@HOST@">>, Input, Host).
|
||||
|
||||
@@ -490,7 +496,6 @@ expand_host(Input, Host) ->
|
||||
%% XMPP request handling.
|
||||
|
||||
-spec process_iq(iq(), state()) -> {iq(), state()} | iq() | not_request.
|
||||
|
||||
process_iq(#iq{type = get, lang = Lang, sub_els = [#disco_info{}]} = IQ,
|
||||
#state{server_host = ServerHost, name = Name}) ->
|
||||
AddInfo = ejabberd_hooks:run_fold(disco_info, ServerHost, [],
|
||||
@@ -518,7 +523,6 @@ process_iq(#iq{}, _State) ->
|
||||
|
||||
-spec process_slot_request(iq(), binary(), pos_integer(), binary(), binary(),
|
||||
state()) -> {iq(), state()} | iq().
|
||||
|
||||
process_slot_request(#iq{lang = Lang, from = From} = IQ,
|
||||
File, Size, CType, XMLNS,
|
||||
#state{server_host = ServerHost,
|
||||
@@ -549,7 +553,6 @@ process_slot_request(#iq{lang = Lang, from = From} = IQ,
|
||||
|
||||
-spec create_slot(state(), jid(), binary(), pos_integer(), binary(), binary())
|
||||
-> {ok, slot()} | {ok, binary(), binary()} | {error, xmlel()}.
|
||||
|
||||
create_slot(#state{service_url = undefined, max_size = MaxSize},
|
||||
JID, File, Size, _ContentType, Lang) when MaxSize /= infinity,
|
||||
Size > MaxSize ->
|
||||
@@ -568,7 +571,7 @@ create_slot(#state{service_url = undefined,
|
||||
case ejabberd_hooks:run_fold(http_upload_slot_request, ServerHost, allow,
|
||||
[JID, UserDir, Size, Lang]) of
|
||||
allow ->
|
||||
RandStr = make_rand_string(SecretLength),
|
||||
RandStr = randoms:get_alphanum_string(SecretLength),
|
||||
FileStr = make_file_string(File),
|
||||
?INFO_MSG("Got HTTP upload slot for ~s (file: ~s)",
|
||||
[jid:encode(JID), File]),
|
||||
@@ -626,76 +629,56 @@ create_slot(#state{service_url = ServiceURL},
|
||||
end.
|
||||
|
||||
-spec add_slot(slot(), pos_integer(), timer:tref(), state()) -> state().
|
||||
|
||||
add_slot(Slot, Size, Timer, #state{slots = Slots} = State) ->
|
||||
NewSlots = maps:put(Slot, {Size, Timer}, Slots),
|
||||
State#state{slots = NewSlots}.
|
||||
|
||||
-spec get_slot(slot(), state()) -> {ok, {pos_integer(), timer:tref()}} | error.
|
||||
|
||||
get_slot(Slot, #state{slots = Slots}) ->
|
||||
maps:find(Slot, Slots).
|
||||
|
||||
-spec del_slot(slot(), state()) -> state().
|
||||
|
||||
del_slot(Slot, #state{slots = Slots} = State) ->
|
||||
NewSlots = maps:remove(Slot, Slots),
|
||||
State#state{slots = NewSlots}.
|
||||
|
||||
-spec mk_slot(slot(), state(), binary()) -> upload_slot();
|
||||
(binary(), binary(), binary()) -> upload_slot().
|
||||
|
||||
mk_slot(Slot, #state{put_url = PutPrefix, get_url = GetPrefix}, XMLNS) ->
|
||||
PutURL = str:join([PutPrefix | Slot], <<$/>>),
|
||||
GetURL = str:join([GetPrefix | Slot], <<$/>>),
|
||||
mk_slot(PutURL, GetURL, XMLNS);
|
||||
mk_slot(PutURL, GetURL, ?NS_HTTP_UPLOAD_0) ->
|
||||
#upload_slot_0{get = GetURL, put = PutURL, xmlns = ?NS_HTTP_UPLOAD_0};
|
||||
#upload_slot_0{get = misc:url_encode(GetURL),
|
||||
put = misc:url_encode(PutURL),
|
||||
xmlns = ?NS_HTTP_UPLOAD_0};
|
||||
mk_slot(PutURL, GetURL, XMLNS) ->
|
||||
#upload_slot{get = GetURL, put = PutURL, xmlns = XMLNS}.
|
||||
#upload_slot{get = misc:url_encode(GetURL),
|
||||
put = misc:url_encode(PutURL),
|
||||
xmlns = XMLNS}.
|
||||
|
||||
-spec make_user_string(jid(), sha1 | node) -> binary().
|
||||
|
||||
make_user_string(#jid{luser = U, lserver = S}, sha1) ->
|
||||
str:sha(<<U/binary, $@, S/binary>>);
|
||||
make_user_string(#jid{luser = U}, node) ->
|
||||
re:replace(U, <<"[^a-zA-Z0-9_.-]">>, <<$_>>, [global, {return, binary}]).
|
||||
replace_special_chars(U).
|
||||
|
||||
-spec make_file_string(binary()) -> binary().
|
||||
|
||||
make_file_string(File) ->
|
||||
re:replace(File, <<"[^a-zA-Z0-9_.-]">>, <<$_>>, [global, {return, binary}]).
|
||||
replace_special_chars(File).
|
||||
|
||||
-spec make_rand_string(non_neg_integer()) -> binary().
|
||||
|
||||
make_rand_string(Length) ->
|
||||
list_to_binary(make_rand_string([], Length)).
|
||||
|
||||
-spec make_rand_string(string(), non_neg_integer()) -> string().
|
||||
|
||||
make_rand_string(S, 0) -> S;
|
||||
make_rand_string(S, N) -> make_rand_string([make_rand_char() | S], N - 1).
|
||||
|
||||
-spec make_rand_char() -> char().
|
||||
|
||||
make_rand_char() ->
|
||||
map_int_to_char(randoms:uniform(0, 61)).
|
||||
|
||||
-spec map_int_to_char(0..61) -> char().
|
||||
|
||||
map_int_to_char(N) when N =< 9 -> N + 48; % Digit.
|
||||
map_int_to_char(N) when N =< 35 -> N + 55; % Upper-case character.
|
||||
map_int_to_char(N) when N =< 61 -> N + 61. % Lower-case character.
|
||||
-spec replace_special_chars(binary()) -> binary().
|
||||
replace_special_chars(S) ->
|
||||
re:replace(S, <<"[^\\p{Xan}_.-]">>, <<$_>>,
|
||||
[unicode, global, {return, binary}]).
|
||||
|
||||
-spec yield_content_type(binary()) -> binary().
|
||||
|
||||
yield_content_type(<<"">>) -> ?DEFAULT_CONTENT_TYPE;
|
||||
yield_content_type(Type) -> Type.
|
||||
|
||||
-spec iq_disco_info(binary(), binary(), binary(), [xdata()]) -> disco_info().
|
||||
|
||||
iq_disco_info(Host, Lang, Name, AddInfo) ->
|
||||
Form = case gen_mod:get_module_opt(Host, ?MODULE, max_size, 104857600) of
|
||||
Form = case gen_mod:get_module_opt(Host, ?MODULE, max_size) of
|
||||
infinity ->
|
||||
AddInfo;
|
||||
MaxSize ->
|
||||
@@ -726,7 +709,6 @@ iq_disco_info(Host, Lang, Name, AddInfo) ->
|
||||
%% HTTP request handling.
|
||||
|
||||
-spec parse_http_request(#request{}) -> {atom(), slot()}.
|
||||
|
||||
parse_http_request(#request{host = Host, path = Path}) ->
|
||||
PrefixLength = length(Path) - 3,
|
||||
{ProcURL, Slot} = if PrefixLength > 0 ->
|
||||
@@ -743,7 +725,6 @@ parse_http_request(#request{host = Host, path = Path}) ->
|
||||
integer() | undefined,
|
||||
binary(), slot(), boolean())
|
||||
-> ok | {ok, [{binary(), binary()}], binary()} | {error, term()}.
|
||||
|
||||
store_file(Path, Data, FileMode, DirMode, GetPrefix, Slot, Thumbnail) ->
|
||||
case do_store_file(Path, Data, FileMode, DirMode) of
|
||||
ok when Thumbnail ->
|
||||
@@ -776,7 +757,6 @@ store_file(Path, Data, FileMode, DirMode, GetPrefix, Slot, Thumbnail) ->
|
||||
integer() | undefined,
|
||||
integer() | undefined)
|
||||
-> ok | {error, term()}.
|
||||
|
||||
do_store_file(Path, Data, FileMode, DirMode) ->
|
||||
try
|
||||
ok = filelib:ensure_dir(Path),
|
||||
@@ -805,7 +785,6 @@ do_store_file(Path, Data, FileMode, DirMode) ->
|
||||
end.
|
||||
|
||||
-spec guess_content_type(binary()) -> binary().
|
||||
|
||||
guess_content_type(FileName) ->
|
||||
mod_http_fileserver:content_type(FileName,
|
||||
?DEFAULT_CONTENT_TYPE,
|
||||
@@ -813,20 +792,17 @@ guess_content_type(FileName) ->
|
||||
|
||||
-spec http_response(100..599)
|
||||
-> {pos_integer(), [{binary(), binary()}], binary()}.
|
||||
|
||||
http_response(Code) ->
|
||||
http_response(Code, []).
|
||||
|
||||
-spec http_response(100..599, [{binary(), binary()}])
|
||||
-> {pos_integer(), [{binary(), binary()}], binary()}.
|
||||
|
||||
http_response(Code, ExtraHeaders) ->
|
||||
Message = <<(code_to_message(Code))/binary, $\n>>,
|
||||
http_response(Code, ExtraHeaders, Message).
|
||||
|
||||
-spec http_response(100..599, [{binary(), binary()}], binary())
|
||||
-> {pos_integer(), [{binary(), binary()}], binary()}.
|
||||
|
||||
http_response(Code, ExtraHeaders, Body) ->
|
||||
Headers = case proplists:is_defined(<<"Content-Type">>, ExtraHeaders) of
|
||||
true ->
|
||||
@@ -837,7 +813,6 @@ http_response(Code, ExtraHeaders, Body) ->
|
||||
{Code, Headers, Body}.
|
||||
|
||||
-spec code_to_message(100..599) -> binary().
|
||||
|
||||
code_to_message(201) -> <<"Upload successful.">>;
|
||||
code_to_message(403) -> <<"Forbidden.">>;
|
||||
code_to_message(404) -> <<"Not found.">>;
|
||||
@@ -849,29 +824,21 @@ code_to_message(_Code) -> <<"">>.
|
||||
%%--------------------------------------------------------------------
|
||||
%% Image manipulation stuff.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec identify(binary(), binary()) -> {ok, media_info()} | pass.
|
||||
|
||||
identify(Path, Data) ->
|
||||
case misc:have_eimp() of
|
||||
true ->
|
||||
case eimp:identify(Data) of
|
||||
{ok, Info} ->
|
||||
{ok, #media_info{
|
||||
type = proplists:get_value(type, Info),
|
||||
width = proplists:get_value(width, Info),
|
||||
height = proplists:get_value(height, Info)}};
|
||||
{error, Why} ->
|
||||
?DEBUG("Cannot identify type of ~s: ~s",
|
||||
[Path, eimp:format_error(Why)]),
|
||||
pass
|
||||
end;
|
||||
false ->
|
||||
case eimp:identify(Data) of
|
||||
{ok, Info} ->
|
||||
{ok, #media_info{
|
||||
type = proplists:get_value(type, Info),
|
||||
width = proplists:get_value(width, Info),
|
||||
height = proplists:get_value(height, Info)}};
|
||||
{error, Why} ->
|
||||
?DEBUG("Cannot identify type of ~s: ~s",
|
||||
[Path, eimp:format_error(Why)]),
|
||||
pass
|
||||
end.
|
||||
|
||||
-spec convert(binary(), binary(), media_info()) -> {ok, binary(), media_info()} | pass.
|
||||
|
||||
convert(Path, Data, #media_info{type = T, width = W, height = H} = Info) ->
|
||||
if W * H >= 25000000 ->
|
||||
?DEBUG("The image ~s is more than 25 Mpix", [Path]),
|
||||
@@ -906,7 +873,6 @@ convert(Path, Data, #media_info{type = T, width = W, height = H} = Info) ->
|
||||
end.
|
||||
|
||||
-spec thumb_el(media_info(), binary()) -> xmlel().
|
||||
|
||||
thumb_el(#media_info{type = T, height = H, width = W}, URI) ->
|
||||
MimeType = <<"image/", (atom_to_binary(T, latin1))/binary>>,
|
||||
Thumb = #thumbnail{'media-type' = MimeType, uri = URI,
|
||||
@@ -916,14 +882,11 @@ thumb_el(#media_info{type = T, height = H, width = W}, URI) ->
|
||||
%%--------------------------------------------------------------------
|
||||
%% Remove user.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec remove_user(binary(), binary()) -> ok.
|
||||
|
||||
remove_user(User, Server) ->
|
||||
ServerHost = jid:nameprep(Server),
|
||||
DocRoot = gen_mod:get_module_opt(ServerHost, ?MODULE, docroot,
|
||||
<<"@HOME@/upload">>),
|
||||
JIDinURL = gen_mod:get_module_opt(ServerHost, ?MODULE, jid_in_url, sha1),
|
||||
DocRoot = gen_mod:get_module_opt(ServerHost, ?MODULE, docroot),
|
||||
JIDinURL = gen_mod:get_module_opt(ServerHost, ?MODULE, jid_in_url),
|
||||
DocRoot1 = expand_host(expand_home(DocRoot), ServerHost),
|
||||
UserStr = make_user_string(jid:make(User, Server), JIDinURL),
|
||||
UserDir = str:join([DocRoot1, UserStr], <<$/>>),
|
||||
@@ -939,7 +902,6 @@ remove_user(User, Server) ->
|
||||
ok.
|
||||
|
||||
-spec del_tree(file:filename_all()) -> ok | {error, term()}.
|
||||
|
||||
del_tree(Dir) when is_binary(Dir) ->
|
||||
del_tree(binary_to_list(Dir));
|
||||
del_tree(Dir) ->
|
||||
|
||||
@@ -37,7 +37,8 @@
|
||||
-export([start/2,
|
||||
stop/1,
|
||||
depends/2,
|
||||
mod_opt_type/1]).
|
||||
mod_opt_type/1,
|
||||
mod_options/1]).
|
||||
|
||||
%% gen_server callbacks.
|
||||
-export([init/1,
|
||||
@@ -69,19 +70,16 @@
|
||||
%% gen_mod/supervisor callbacks.
|
||||
%%--------------------------------------------------------------------
|
||||
-spec start(binary(), gen_mod:opts()) -> {ok, pid()}.
|
||||
|
||||
start(ServerHost, Opts) ->
|
||||
Proc = mod_http_upload:get_proc_name(ServerHost, ?MODULE),
|
||||
gen_mod:start_child(?MODULE, ServerHost, Opts, Proc).
|
||||
|
||||
-spec stop(binary()) -> ok | {error, any()}.
|
||||
|
||||
stop(ServerHost) ->
|
||||
Proc = mod_http_upload:get_proc_name(ServerHost, ?MODULE),
|
||||
gen_mod:stop_child(Proc).
|
||||
|
||||
-spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
|
||||
|
||||
mod_opt_type(access_soft_quota) ->
|
||||
fun acl:shaper_rules_validator/1;
|
||||
mod_opt_type(access_hard_quota) ->
|
||||
@@ -89,28 +87,28 @@ mod_opt_type(access_hard_quota) ->
|
||||
mod_opt_type(max_days) ->
|
||||
fun(I) when is_integer(I), I > 0 -> I;
|
||||
(infinity) -> infinity
|
||||
end;
|
||||
mod_opt_type(_) ->
|
||||
[access_soft_quota, access_hard_quota, max_days].
|
||||
end.
|
||||
|
||||
-spec mod_options(binary()) -> [{atom(), any()}].
|
||||
mod_options(_) ->
|
||||
[{access_soft_quota, soft_upload_quota},
|
||||
{access_hard_quota, hard_upload_quota},
|
||||
{max_days, infinity}].
|
||||
|
||||
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
|
||||
|
||||
depends(_Host, _Opts) ->
|
||||
[{mod_http_upload, hard}].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server callbacks.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec init(list()) -> {ok, state()}.
|
||||
init([ServerHost, Opts]) ->
|
||||
process_flag(trap_exit, true),
|
||||
AccessSoftQuota = gen_mod:get_opt(access_soft_quota, Opts,
|
||||
soft_upload_quota),
|
||||
AccessHardQuota = gen_mod:get_opt(access_hard_quota, Opts,
|
||||
hard_upload_quota),
|
||||
MaxDays = gen_mod:get_opt(max_days, Opts, infinity),
|
||||
DocRoot1 = gen_mod:get_module_opt(ServerHost, mod_http_upload, docroot,
|
||||
<<"@HOME@/upload">>),
|
||||
AccessSoftQuota = gen_mod:get_opt(access_soft_quota, Opts),
|
||||
AccessHardQuota = gen_mod:get_opt(access_hard_quota, Opts),
|
||||
MaxDays = gen_mod:get_opt(max_days, Opts),
|
||||
DocRoot1 = gen_mod:get_module_opt(ServerHost, mod_http_upload, docroot),
|
||||
DocRoot2 = mod_http_upload:expand_home(str:strip(DocRoot1, right, $/)),
|
||||
DocRoot3 = mod_http_upload:expand_host(DocRoot2, ServerHost),
|
||||
Timers = if MaxDays == infinity -> [];
|
||||
@@ -129,13 +127,11 @@ init([ServerHost, Opts]) ->
|
||||
timers = Timers}}.
|
||||
|
||||
-spec handle_call(_, {pid(), _}, state()) -> {noreply, state()}.
|
||||
|
||||
handle_call(Request, From, State) ->
|
||||
?ERROR_MSG("Got unexpected request from ~p: ~p", [From, Request]),
|
||||
{noreply, State}.
|
||||
|
||||
-spec handle_cast(_, state()) -> {noreply, state()}.
|
||||
|
||||
handle_cast({handle_slot_request, #jid{user = U, server = S} = JID, Path, Size},
|
||||
#state{server_host = ServerHost,
|
||||
access_soft_quota = AccessSoftQuota,
|
||||
@@ -193,7 +189,6 @@ handle_cast(Request, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
-spec handle_info(_, state()) -> {noreply, state()}.
|
||||
|
||||
handle_info(sweep, #state{server_host = ServerHost,
|
||||
docroot = DocRoot,
|
||||
max_days = MaxDays} = State)
|
||||
@@ -220,7 +215,6 @@ handle_info(Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
-spec terminate(normal | shutdown | {shutdown, _} | _, state()) -> ok.
|
||||
|
||||
terminate(Reason, #state{server_host = ServerHost, timers = Timers}) ->
|
||||
?DEBUG("Stopping upload quota process for ~s: ~p", [ServerHost, Reason]),
|
||||
ejabberd_hooks:delete(http_upload_slot_request, ServerHost, ?MODULE,
|
||||
@@ -228,7 +222,6 @@ terminate(Reason, #state{server_host = ServerHost, timers = Timers}) ->
|
||||
lists:foreach(fun timer:cancel/1, Timers).
|
||||
|
||||
-spec code_change({down, _} | _, state(), _) -> {ok, state()}.
|
||||
|
||||
code_change(_OldVsn, #state{server_host = ServerHost} = State, _Extra) ->
|
||||
?DEBUG("Updating upload quota process for ~s", [ServerHost]),
|
||||
{ok, State}.
|
||||
@@ -236,10 +229,8 @@ code_change(_OldVsn, #state{server_host = ServerHost} = State, _Extra) ->
|
||||
%%--------------------------------------------------------------------
|
||||
%% ejabberd_hooks callback.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec handle_slot_request(allow | deny, jid(), binary(),
|
||||
non_neg_integer(), binary()) -> allow | deny.
|
||||
|
||||
handle_slot_request(allow, #jid{lserver = ServerHost} = JID, Path, Size,
|
||||
_Lang) ->
|
||||
Proc = mod_http_upload:get_proc_name(ServerHost, ?MODULE),
|
||||
@@ -250,12 +241,10 @@ handle_slot_request(Acc, _JID, _Path, _Size, _Lang) -> Acc.
|
||||
%%--------------------------------------------------------------------
|
||||
%% Internal functions.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec enforce_quota(file:filename_all(), non_neg_integer(),
|
||||
non_neg_integer() | undefined, non_neg_integer(),
|
||||
non_neg_integer())
|
||||
-> non_neg_integer().
|
||||
|
||||
enforce_quota(_UserDir, SlotSize, OldSize, _MinSize, MaxSize)
|
||||
when is_integer(OldSize), OldSize + SlotSize =< MaxSize ->
|
||||
OldSize + SlotSize;
|
||||
@@ -281,7 +270,6 @@ enforce_quota(UserDir, SlotSize, _OldSize, MinSize, MaxSize) ->
|
||||
end.
|
||||
|
||||
-spec delete_old_files(file:filename_all(), integer()) -> ok.
|
||||
|
||||
delete_old_files(UserDir, CutOff) ->
|
||||
FileInfo = gather_file_info(UserDir),
|
||||
case [Path || {Path, _Size, Time} <- FileInfo, Time < CutOff] of
|
||||
@@ -294,7 +282,6 @@ delete_old_files(UserDir, CutOff) ->
|
||||
|
||||
-spec gather_file_info(file:filename_all())
|
||||
-> [{binary(), non_neg_integer(), non_neg_integer()}].
|
||||
|
||||
gather_file_info(Dir) when is_binary(Dir) ->
|
||||
gather_file_info(binary_to_list(Dir));
|
||||
gather_file_info(Dir) ->
|
||||
@@ -329,7 +316,6 @@ gather_file_info(Dir) ->
|
||||
end.
|
||||
|
||||
-spec del_file_and_dir(file:name_all()) -> ok.
|
||||
|
||||
del_file_and_dir(File) ->
|
||||
case file:delete(File) of
|
||||
ok ->
|
||||
@@ -346,7 +332,6 @@ del_file_and_dir(File) ->
|
||||
end.
|
||||
|
||||
-spec secs_since_epoch() -> non_neg_integer().
|
||||
|
||||
secs_since_epoch() ->
|
||||
{MegaSecs, Secs, _MicroSecs} = os:timestamp(),
|
||||
MegaSecs * 1000000 + Secs.
|
||||
|
||||
+33
-39
@@ -39,7 +39,7 @@
|
||||
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3,
|
||||
mod_opt_type/1, depends/2]).
|
||||
mod_opt_type/1, mod_options/1, depends/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -47,14 +47,8 @@
|
||||
-include("mod_irc.hrl").
|
||||
-include("translate.hrl").
|
||||
|
||||
-define(DEFAULT_IRC_ENCODING, <<"iso8859-15">>).
|
||||
|
||||
-define(DEFAULT_IRC_PORT, 6667).
|
||||
|
||||
-define(DEFAULT_REALNAME, <<"WebIRC-User">>).
|
||||
|
||||
-define(DEFAULT_WEBIRC_PASSWORD, <<"">>).
|
||||
|
||||
-define(POSSIBLE_ENCODINGS,
|
||||
[<<"koi8-r">>, <<"iso8859-15">>, <<"iso8859-1">>, <<"iso8859-2">>,
|
||||
<<"utf-8">>, <<"utf-8+latin-1">>]).
|
||||
@@ -100,17 +94,16 @@ depends(_Host, _Opts) ->
|
||||
init([Host, Opts]) ->
|
||||
process_flag(trap_exit, true),
|
||||
ejabberd:start_app(iconv),
|
||||
MyHosts = gen_mod:get_opt_hosts(Host, Opts, <<"irc.@HOST@">>),
|
||||
MyHosts = gen_mod:get_opt_hosts(Host, Opts),
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, Opts),
|
||||
Access = gen_mod:get_opt(access, Opts, all),
|
||||
Access = gen_mod:get_opt(access, Opts),
|
||||
catch ets:new(irc_connection,
|
||||
[named_table, public,
|
||||
{keypos, #irc_connection.jid_server_host}]),
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
|
||||
lists:foreach(
|
||||
fun(MyHost) ->
|
||||
register_hooks(MyHost, IQDisc),
|
||||
register_hooks(MyHost),
|
||||
ejabberd_router:register_route(MyHost, Host)
|
||||
end, MyHosts),
|
||||
{ok,
|
||||
@@ -136,37 +129,27 @@ handle_call(stop, _From, State) ->
|
||||
%% Description: Handling cast messages
|
||||
%%--------------------------------------------------------------------
|
||||
handle_cast({reload, ServerHost, NewOpts, OldOpts}, State) ->
|
||||
NewHosts = gen_mod:get_opt_hosts(ServerHost, NewOpts, <<"irc.@HOST@">>),
|
||||
OldHosts = gen_mod:get_opt_hosts(ServerHost, OldOpts, <<"irc.@HOST@">>),
|
||||
NewIQDisc = gen_mod:get_opt(iqdisc, NewOpts, gen_iq_handler:iqdisc(ServerHost)),
|
||||
OldIQDisc = gen_mod:get_opt(iqdisc, OldOpts, gen_iq_handler:iqdisc(ServerHost)),
|
||||
NewHosts = gen_mod:get_opt_hosts(ServerHost, NewOpts),
|
||||
OldHosts = gen_mod:get_opt_hosts(ServerHost, OldOpts),
|
||||
NewMod = gen_mod:db_mod(ServerHost, NewOpts, ?MODULE),
|
||||
OldMod = gen_mod:db_mod(ServerHost, OldOpts, ?MODULE),
|
||||
Access = gen_mod:get_opt(access, NewOpts, all),
|
||||
Access = gen_mod:get_opt(access, NewOpts),
|
||||
if NewMod /= OldMod ->
|
||||
NewMod:init(ServerHost, NewOpts);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
if (NewIQDisc /= OldIQDisc) ->
|
||||
lists:foreach(
|
||||
fun(NewHost) ->
|
||||
register_hooks(NewHost, NewIQDisc)
|
||||
end, NewHosts -- (NewHosts -- OldHosts));
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
lists:foreach(
|
||||
fun(NewHost) ->
|
||||
ejabberd_router:register_route(NewHost, ServerHost),
|
||||
register_hooks(NewHost, NewIQDisc)
|
||||
register_hooks(NewHost)
|
||||
end, NewHosts -- OldHosts),
|
||||
lists:foreach(
|
||||
fun(OldHost) ->
|
||||
ejabberd_router:unregister_route(OldHost),
|
||||
unregister_hooks(OldHost)
|
||||
end, OldHosts -- NewHosts),
|
||||
Access = gen_mod:get_opt(access, NewOpts, all),
|
||||
Access = gen_mod:get_opt(access, NewOpts),
|
||||
{noreply, State#state{hosts = NewHosts, access = Access}};
|
||||
handle_cast(Msg, State) ->
|
||||
?WARNING_MSG("unexpected cast: ~p", [Msg]),
|
||||
@@ -213,17 +196,17 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
%%--------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
register_hooks(Host, IQDisc) ->
|
||||
register_hooks(Host) ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO,
|
||||
?MODULE, process_disco_info, IQDisc),
|
||||
?MODULE, process_disco_info),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS,
|
||||
?MODULE, process_disco_items, IQDisc),
|
||||
?MODULE, process_disco_items),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_REGISTER,
|
||||
?MODULE, process_register, IQDisc),
|
||||
?MODULE, process_register),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD,
|
||||
?MODULE, process_vcard, IQDisc),
|
||||
?MODULE, process_vcard),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_COMMANDS,
|
||||
?MODULE, process_command, IQDisc).
|
||||
?MODULE, process_command).
|
||||
|
||||
unregister_hooks(Host) ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO),
|
||||
@@ -434,7 +417,7 @@ closed_connection(Host, From, Server) ->
|
||||
ets:delete(irc_connection, {From, Server, Host}).
|
||||
|
||||
iq_disco(ServerHost, <<"">>, Lang) ->
|
||||
Name = gen_mod:get_module_opt(ServerHost, ?MODULE, name, ?T("IRC Transport")),
|
||||
Name = gen_mod:get_module_opt(ServerHost, ?MODULE, name),
|
||||
#disco_info{
|
||||
identities = [#identity{category = <<"conference">>,
|
||||
type = <<"irc">>,
|
||||
@@ -595,18 +578,17 @@ get_connection_params(Host, From, IRCServer) ->
|
||||
IRCServer).
|
||||
|
||||
get_default_encoding(ServerHost) ->
|
||||
Result = gen_mod:get_module_opt(ServerHost, ?MODULE, default_encoding,
|
||||
?DEFAULT_IRC_ENCODING),
|
||||
Result = gen_mod:get_module_opt(ServerHost, ?MODULE, default_encoding),
|
||||
?INFO_MSG("The default_encoding configured for "
|
||||
"host ~p is: ~p~n",
|
||||
[ServerHost, Result]),
|
||||
Result.
|
||||
|
||||
get_realname(ServerHost) ->
|
||||
gen_mod:get_module_opt(ServerHost, ?MODULE, realname, ?DEFAULT_REALNAME).
|
||||
gen_mod:get_module_opt(ServerHost, ?MODULE, realname).
|
||||
|
||||
get_webirc_password(ServerHost) ->
|
||||
gen_mod:get_module_opt(ServerHost, ?MODULE, webirc_password, ?DEFAULT_WEBIRC_PASSWORD).
|
||||
gen_mod:get_module_opt(ServerHost, ?MODULE, webirc_password).
|
||||
|
||||
get_connection_params(Host, ServerHost, From,
|
||||
IRCServer) ->
|
||||
@@ -993,8 +975,20 @@ mod_opt_type(name) ->
|
||||
mod_opt_type(host) -> fun iolist_to_binary/1;
|
||||
mod_opt_type(hosts) ->
|
||||
fun (L) -> lists:map(fun iolist_to_binary/1, L) end;
|
||||
mod_opt_type(_) ->
|
||||
[access, db_type, default_encoding, host, hosts, name].
|
||||
mod_opt_type(realname) ->
|
||||
fun iolist_to_binary/1;
|
||||
mod_opt_type(webirc_password) ->
|
||||
fun iolist_to_binary/1.
|
||||
|
||||
mod_options(Host) ->
|
||||
[{access, all},
|
||||
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
|
||||
{default_encoding, <<"iso8859-15">>},
|
||||
{host, <<"irc.@HOST@">>},
|
||||
{hosts, []},
|
||||
{realname, <<"WebIRC-User">>},
|
||||
{webirc_password, <<"">>},
|
||||
{name, ?T("IRC Transport")}].
|
||||
|
||||
-spec extract_ident(stanza()) -> binary().
|
||||
extract_ident(Packet) ->
|
||||
|
||||
+27
-40
@@ -34,7 +34,7 @@
|
||||
-export([start/2, stop/1, reload/3, process_local_iq/1, export/1,
|
||||
process_sm_iq/1, on_presence_update/4, import_info/0,
|
||||
import/5, import_start/2, store_last_info/4, get_last_info/2,
|
||||
remove_user/2, mod_opt_type/1,
|
||||
remove_user/2, mod_opt_type/1, mod_options/1,
|
||||
register_user/2, depends/2, privacy_check_packet/4]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@@ -59,14 +59,13 @@
|
||||
-optional_callbacks([use_cache/1, cache_nodes/1]).
|
||||
|
||||
start(Host, Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, Opts),
|
||||
init_cache(Mod, Host, Opts),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
||||
?NS_LAST, ?MODULE, process_local_iq, IQDisc),
|
||||
?NS_LAST, ?MODULE, process_local_iq),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||
?NS_LAST, ?MODULE, process_sm_iq, IQDisc),
|
||||
?NS_LAST, ?MODULE, process_sm_iq),
|
||||
ejabberd_hooks:add(privacy_check_packet, Host, ?MODULE,
|
||||
privacy_check_packet, 30),
|
||||
ejabberd_hooks:add(register_user, Host, ?MODULE,
|
||||
@@ -98,16 +97,7 @@ reload(Host, NewOpts, OldOpts) ->
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
init_cache(NewMod, Host, NewOpts),
|
||||
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
|
||||
{false, IQDisc, _} ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST,
|
||||
?MODULE, process_local_iq, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_LAST,
|
||||
?MODULE, process_sm_iq, IQDisc);
|
||||
true ->
|
||||
ok
|
||||
end.
|
||||
init_cache(NewMod, Host, NewOpts).
|
||||
|
||||
%%%
|
||||
%%% Uptime of ejabberd node
|
||||
@@ -142,9 +132,9 @@ process_sm_iq(#iq{type = set, lang = Lang} = IQ) ->
|
||||
process_sm_iq(#iq{from = From, to = To, lang = Lang} = IQ) ->
|
||||
User = To#jid.luser,
|
||||
Server = To#jid.lserver,
|
||||
{Subscription, _Groups} =
|
||||
{Subscription, _Ask, _Groups} =
|
||||
ejabberd_hooks:run_fold(roster_get_jid_info, Server,
|
||||
{none, []}, [User, Server, From]),
|
||||
{none, none, []}, [User, Server, From]),
|
||||
if (Subscription == both) or (Subscription == from) or
|
||||
(From#jid.luser == To#jid.luser) and
|
||||
(From#jid.lserver == To#jid.lserver) ->
|
||||
@@ -166,9 +156,9 @@ privacy_check_packet(allow, C2SState,
|
||||
case xmpp:has_subtag(IQ, #last{}) of
|
||||
true ->
|
||||
#jid{luser = LUser, lserver = LServer} = To,
|
||||
{Sub, _} = ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, LServer,
|
||||
{none, []}, [LUser, LServer, From]),
|
||||
{Sub, _, _} = ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, LServer,
|
||||
{none, none, []}, [LUser, LServer, From]),
|
||||
if Sub == from; Sub == both ->
|
||||
Pres = #presence{from = To, to = From},
|
||||
case ejabberd_hooks:run_fold(
|
||||
@@ -198,7 +188,9 @@ get_last(LUser, LServer) ->
|
||||
?LAST_CACHE, {LUser, LServer},
|
||||
fun() -> Mod:get_last(LUser, LServer) end);
|
||||
false ->
|
||||
Mod:get_last(LUser, LServer)
|
||||
Mod:get_last(LUser, LServer);
|
||||
undefined ->
|
||||
error
|
||||
end,
|
||||
case Res of
|
||||
{ok, {TimeStamp, Status}} -> {ok, TimeStamp, Status};
|
||||
@@ -275,23 +267,17 @@ remove_user(User, Server) ->
|
||||
init_cache(Mod, Host, Opts) ->
|
||||
case use_cache(Mod, Host) of
|
||||
true ->
|
||||
CacheOpts = cache_opts(Host, Opts),
|
||||
CacheOpts = cache_opts(Opts),
|
||||
ets_cache:new(?LAST_CACHE, CacheOpts);
|
||||
false ->
|
||||
ets_cache:delete(?LAST_CACHE)
|
||||
end.
|
||||
|
||||
-spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()].
|
||||
cache_opts(Host, Opts) ->
|
||||
MaxSize = gen_mod:get_opt(
|
||||
cache_size, Opts,
|
||||
ejabberd_config:cache_size(Host)),
|
||||
CacheMissed = gen_mod:get_opt(
|
||||
cache_missed, Opts,
|
||||
ejabberd_config:cache_missed(Host)),
|
||||
LifeTime = case gen_mod:get_opt(
|
||||
cache_life_time, Opts,
|
||||
ejabberd_config:cache_life_time(Host)) of
|
||||
-spec cache_opts(gen_mod:opts()) -> [proplists:property()].
|
||||
cache_opts(Opts) ->
|
||||
MaxSize = gen_mod:get_opt(cache_size, Opts),
|
||||
CacheMissed = gen_mod:get_opt(cache_missed, Opts),
|
||||
LifeTime = case gen_mod:get_opt(cache_life_time, Opts) of
|
||||
infinity -> infinity;
|
||||
I -> timer:seconds(I)
|
||||
end,
|
||||
@@ -301,10 +287,7 @@ cache_opts(Host, Opts) ->
|
||||
use_cache(Mod, Host) ->
|
||||
case erlang:function_exported(Mod, use_cache, 1) of
|
||||
true -> Mod:use_cache(Host);
|
||||
false ->
|
||||
gen_mod:get_module_opt(
|
||||
Host, ?MODULE, use_cache,
|
||||
ejabberd_config:use_cache(Host))
|
||||
false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
|
||||
end.
|
||||
|
||||
-spec cache_nodes(module(), binary()) -> [node()].
|
||||
@@ -340,12 +323,16 @@ depends(_Host, _Opts) ->
|
||||
[].
|
||||
|
||||
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
mod_opt_type(O) when O == cache_life_time; O == cache_size ->
|
||||
fun (I) when is_integer(I), I > 0 -> I;
|
||||
(infinity) -> infinity
|
||||
end;
|
||||
mod_opt_type(O) when O == use_cache; O == cache_missed ->
|
||||
fun (B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(_) ->
|
||||
[db_type, iqdisc, cache_life_time, cache_size, use_cache, cache_missed].
|
||||
fun (B) when is_boolean(B) -> B end.
|
||||
|
||||
mod_options(Host) ->
|
||||
[{db_type, ejabberd_config:default_db(Host, ?MODULE)},
|
||||
{use_cache, ejabberd_config:use_cache(Host)},
|
||||
{cache_size, ejabberd_config:cache_size(Host)},
|
||||
{cache_missed, ejabberd_config:cache_missed(Host)},
|
||||
{cache_life_time, ejabberd_config:cache_life_time(Host)}].
|
||||
|
||||
@@ -45,9 +45,7 @@ init(_Host, _Opts) ->
|
||||
use_cache(Host) ->
|
||||
case mnesia:table_info(last_activity, storage_type) of
|
||||
disc_only_copies ->
|
||||
gen_mod:get_module_opt(
|
||||
Host, mod_last, use_cache,
|
||||
ejabberd_config:use_cache(Host));
|
||||
gen_mod:get_module_opt(Host, mod_last, use_cache);
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
-protocol({xep, 78, '2.5'}).
|
||||
|
||||
%% gen_mod API
|
||||
-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1]).
|
||||
-export([start/2, stop/1, reload/3, depends/2, mod_options/1]).
|
||||
%% hooks
|
||||
-export([c2s_unauthenticated_packet/2, c2s_stream_features/2]).
|
||||
|
||||
@@ -54,7 +54,7 @@ reload(_Host, _NewOpts, _OldOpts) ->
|
||||
depends(_Host, _Opts) ->
|
||||
[].
|
||||
|
||||
mod_opt_type(_) ->
|
||||
mod_options(_) ->
|
||||
[].
|
||||
|
||||
-spec c2s_unauthenticated_packet(c2s_state(), iq()) ->
|
||||
|
||||
+149
-71
@@ -39,7 +39,8 @@
|
||||
disco_sm_features/5, remove_user/2, remove_room/3, mod_opt_type/1,
|
||||
muc_process_iq/2, muc_filter_message/3, message_is_archived/3,
|
||||
delete_old_messages/2, get_commands_spec/0, msg_to_el/4,
|
||||
get_room_config/4, set_room_option/3, offline_message/1, export/1]).
|
||||
get_room_config/4, set_room_option/3, offline_message/1, export/1,
|
||||
mod_options/1, remove_mam_for_user_with_peer/3, remove_mam_for_user/2]).
|
||||
|
||||
-include("xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -66,19 +67,20 @@
|
||||
-callback select(binary(), jid(), jid(), mam_query:result(),
|
||||
#rsm_set{} | undefined, chat | groupchat) ->
|
||||
{[{binary(), non_neg_integer(), xmlel()}], boolean(), non_neg_integer()}.
|
||||
-callback use_cache(binary(), gen_mod:opts()) -> boolean().
|
||||
-callback use_cache(binary()) -> boolean().
|
||||
-callback cache_nodes(binary()) -> [node()].
|
||||
-callback remove_from_archive(binary(), binary(), jid() | none) -> ok | {error, any()}.
|
||||
|
||||
-optional_callbacks([use_cache/2]).
|
||||
-optional_callbacks([use_cache/1, cache_nodes/1]).
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
start(Host, Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, Opts),
|
||||
init_cache(Host, Opts),
|
||||
register_iq_handlers(Host, IQDisc),
|
||||
init_cache(Mod, Host, Opts),
|
||||
register_iq_handlers(Host),
|
||||
ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE,
|
||||
sm_receive_packet, 50),
|
||||
ejabberd_hooks:add(user_receive_packet, Host, ?MODULE,
|
||||
@@ -103,7 +105,7 @@ start(Host, Opts) ->
|
||||
get_room_config, 50),
|
||||
ejabberd_hooks:add(set_room_option, Host, ?MODULE,
|
||||
set_room_option, 50),
|
||||
case gen_mod:get_opt(assume_mam_usage, Opts, false) of
|
||||
case gen_mod:get_opt(assume_mam_usage, Opts) of
|
||||
true ->
|
||||
ejabberd_hooks:add(message_is_archived, Host, ?MODULE,
|
||||
message_is_archived, 50);
|
||||
@@ -113,30 +115,30 @@ start(Host, Opts) ->
|
||||
ejabberd_commands:register_commands(get_commands_spec()),
|
||||
ok.
|
||||
|
||||
use_cache(Host, Opts) ->
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
use_cache(Mod, Host) ->
|
||||
case erlang:function_exported(Mod, use_cache, 2) of
|
||||
true -> Mod:use_cache(Host, Opts);
|
||||
false ->
|
||||
gen_mod:get_opt(use_cache, Opts,
|
||||
ejabberd_config:use_cache(Host))
|
||||
true -> Mod:use_cache(Host);
|
||||
false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
|
||||
end.
|
||||
|
||||
init_cache(Host, Opts) ->
|
||||
case use_cache(Host, Opts) of
|
||||
cache_nodes(Mod, Host) ->
|
||||
case erlang:function_exported(Mod, cache_nodes, 1) of
|
||||
true -> Mod:cache_nodes(Host);
|
||||
false -> ejabberd_cluster:get_nodes()
|
||||
end.
|
||||
|
||||
init_cache(Mod, Host, Opts) ->
|
||||
case use_cache(Mod, Host) of
|
||||
true ->
|
||||
ets_cache:new(archive_prefs_cache, cache_opts(Host, Opts));
|
||||
ets_cache:new(archive_prefs_cache, cache_opts(Opts));
|
||||
false ->
|
||||
ok
|
||||
ets_cache:delete(archive_prefs_cache)
|
||||
end.
|
||||
|
||||
cache_opts(Host, Opts) ->
|
||||
MaxSize = gen_mod:get_opt(cache_size, Opts,
|
||||
ejabberd_config:cache_size(Host)),
|
||||
CacheMissed = gen_mod:get_opt(cache_missed, Opts,
|
||||
ejabberd_config:cache_missed(Host)),
|
||||
LifeTime = case gen_mod:get_opt(cache_life_time, Opts,
|
||||
ejabberd_config:cache_life_time(Host)) of
|
||||
cache_opts(Opts) ->
|
||||
MaxSize = gen_mod:get_opt(cache_size, Opts),
|
||||
CacheMissed = gen_mod:get_opt(cache_missed, Opts),
|
||||
LifeTime = case gen_mod:get_opt(cache_life_time, Opts) of
|
||||
infinity -> infinity;
|
||||
I -> timer:seconds(I)
|
||||
end,
|
||||
@@ -168,7 +170,7 @@ stop(Host) ->
|
||||
get_room_config, 50),
|
||||
ejabberd_hooks:delete(set_room_option, Host, ?MODULE,
|
||||
set_room_option, 50),
|
||||
case gen_mod:get_module_opt(Host, ?MODULE, assume_mam_usage, false) of
|
||||
case gen_mod:get_module_opt(Host, ?MODULE, assume_mam_usage) of
|
||||
true ->
|
||||
ejabberd_hooks:delete(message_is_archived, Host, ?MODULE,
|
||||
message_is_archived, 50);
|
||||
@@ -190,14 +192,8 @@ reload(Host, NewOpts, OldOpts) ->
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
ets_cache:setopts(archive_prefs_cache, cache_opts(Host, NewOpts)),
|
||||
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
|
||||
{false, IQDisc, _} ->
|
||||
register_iq_handlers(Host, IQDisc);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
case gen_mod:is_equal_opt(assume_mam_usage, NewOpts, OldOpts, false) of
|
||||
init_cache(NewMod, Host, NewOpts),
|
||||
case gen_mod:is_equal_opt(assume_mam_usage, NewOpts, OldOpts) of
|
||||
{false, true, _} ->
|
||||
ejabberd_hooks:add(message_is_archived, Host, ?MODULE,
|
||||
message_is_archived, 50);
|
||||
@@ -211,24 +207,24 @@ reload(Host, NewOpts, OldOpts) ->
|
||||
depends(_Host, _Opts) ->
|
||||
[].
|
||||
|
||||
-spec register_iq_handlers(binary(), gen_iq_handler:type()) -> ok.
|
||||
register_iq_handlers(Host, IQDisc) ->
|
||||
-spec register_iq_handlers(binary()) -> ok.
|
||||
register_iq_handlers(Host) ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MAM_TMP,
|
||||
?MODULE, process_iq_v0_2, IQDisc),
|
||||
?MODULE, process_iq_v0_2),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_TMP,
|
||||
?MODULE, process_iq_v0_2, IQDisc),
|
||||
?MODULE, process_iq_v0_2),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MAM_0,
|
||||
?MODULE, process_iq_v0_3, IQDisc),
|
||||
?MODULE, process_iq_v0_3),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_0, ?MODULE,
|
||||
process_iq_v0_3, IQDisc),
|
||||
process_iq_v0_3),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MAM_1,
|
||||
?MODULE, process_iq_v0_3, IQDisc),
|
||||
?MODULE, process_iq_v0_3),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_1,
|
||||
?MODULE, process_iq_v0_3, IQDisc),
|
||||
?MODULE, process_iq_v0_3),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MAM_2,
|
||||
?MODULE, process_iq_v0_3, IQDisc),
|
||||
?MODULE, process_iq_v0_3),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_2,
|
||||
?MODULE, process_iq_v0_3, IQDisc).
|
||||
?MODULE, process_iq_v0_3).
|
||||
|
||||
-spec unregister_iq_handlers(binary()) -> ok.
|
||||
unregister_iq_handlers(Host) ->
|
||||
@@ -247,8 +243,13 @@ remove_user(User, Server) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:remove_user(LUser, LServer),
|
||||
ets_cache:delete(archive_prefs_cache, {LUser, LServer},
|
||||
ejabberd_cluster:get_nodes()).
|
||||
case use_cache(Mod, LServer) of
|
||||
true ->
|
||||
ets_cache:delete(archive_prefs_cache, {LUser, LServer},
|
||||
cache_nodes(Mod, LServer));
|
||||
false ->
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec remove_room(binary(), binary(), binary()) -> ok.
|
||||
remove_room(LServer, Name, Host) ->
|
||||
@@ -258,6 +259,41 @@ remove_room(LServer, Name, Host) ->
|
||||
Mod:remove_room(LServer, LName, LHost),
|
||||
ok.
|
||||
|
||||
-spec remove_mam_for_user(binary(), binary()) ->
|
||||
{ok, binary()} | {error, binary()}.
|
||||
remove_mam_for_user(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case Mod:remove_from_archive(LUser, LServer, none) of
|
||||
ok ->
|
||||
{ok, <<"MAM archive removed">>};
|
||||
{error, Bin} when is_binary(Bin) ->
|
||||
{error, Bin};
|
||||
{error, _} ->
|
||||
{error, <<"Db returned error">>}
|
||||
end.
|
||||
|
||||
-spec remove_mam_for_user_with_peer(binary(), binary(), binary()) ->
|
||||
{ok, binary()} | {error, binary()}.
|
||||
remove_mam_for_user_with_peer(User, Server, Peer) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
try jid:decode(Peer) of
|
||||
Jid ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case Mod:remove_from_archive(LUser, LServer, Jid) of
|
||||
ok ->
|
||||
{ok, <<"MAM archive removed">>};
|
||||
{error, Bin} when is_binary(Bin) ->
|
||||
{error, Bin};
|
||||
{error, _} ->
|
||||
{error, <<"Db returned error">>}
|
||||
end
|
||||
catch _:_ ->
|
||||
{error, <<"Invalid peer JID">>}
|
||||
end.
|
||||
|
||||
-spec get_room_config([muc_roomconfig:property()], mod_muc_room:state(),
|
||||
jid(), binary()) -> [muc_roomconfig:property()].
|
||||
get_room_config(Fields, RoomState, _From, _Lang) ->
|
||||
@@ -463,7 +499,7 @@ disco_sm_features(Acc, _From, _To, _Node, _Lang) ->
|
||||
message_is_archived(true, _C2SState, _Pkt) ->
|
||||
true;
|
||||
message_is_archived(false, #{lserver := LServer}, Pkt) ->
|
||||
case gen_mod:get_module_opt(LServer, ?MODULE, assume_mam_usage, false) of
|
||||
case gen_mod:get_module_opt(LServer, ?MODULE, assume_mam_usage) of
|
||||
true ->
|
||||
is_archived(Pkt, LServer);
|
||||
false ->
|
||||
@@ -480,7 +516,7 @@ delete_old_messages(TypeBin, Days) when TypeBin == <<"chat">>;
|
||||
DBTypes = lists:usort(
|
||||
lists:map(
|
||||
fun(Host) ->
|
||||
case gen_mod:db_type(Host, ?MODULE) of
|
||||
case gen_mod:get_module_opt(Host, ?MODULE, db_type) of
|
||||
sql -> {sql, Host};
|
||||
Other -> {Other, global}
|
||||
end
|
||||
@@ -676,10 +712,10 @@ should_archive_peer(LUser, LServer,
|
||||
always -> true;
|
||||
never -> false;
|
||||
roster ->
|
||||
{Sub, _} = ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info,
|
||||
LServer, {none, []},
|
||||
[LUser, LServer, Peer]),
|
||||
{Sub, _, _} = ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info,
|
||||
LServer, {none, none, []},
|
||||
[LUser, LServer, Peer]),
|
||||
Sub == both orelse Sub == from orelse Sub == to
|
||||
end
|
||||
end
|
||||
@@ -803,29 +839,39 @@ write_prefs(LUser, LServer, Host, Default, Always, Never) ->
|
||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||
case Mod:write_prefs(LUser, LServer, Prefs, Host) of
|
||||
ok ->
|
||||
ets_cache:delete(archive_prefs_cache, {LUser, LServer},
|
||||
ejabberd_cluster:get_nodes());
|
||||
case use_cache(Mod, LServer) of
|
||||
true ->
|
||||
ets_cache:delete(archive_prefs_cache, {LUser, LServer},
|
||||
cache_nodes(Mod, LServer));
|
||||
false ->
|
||||
ok
|
||||
end;
|
||||
_Err ->
|
||||
{error, db_failure}
|
||||
end.
|
||||
|
||||
get_prefs(LUser, LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Res = ets_cache:lookup(archive_prefs_cache, {LUser, LServer},
|
||||
fun() -> Mod:get_prefs(LUser, LServer) end),
|
||||
Res = case use_cache(Mod, LServer) of
|
||||
true ->
|
||||
ets_cache:lookup(archive_prefs_cache, {LUser, LServer},
|
||||
fun() -> Mod:get_prefs(LUser, LServer) end);
|
||||
false ->
|
||||
Mod:get_prefs(LUser, LServer)
|
||||
end,
|
||||
case Res of
|
||||
{ok, Prefs} ->
|
||||
Prefs;
|
||||
error ->
|
||||
ActivateOpt = gen_mod:get_module_opt(
|
||||
LServer, ?MODULE,
|
||||
request_activates_archiving, false),
|
||||
request_activates_archiving),
|
||||
case ActivateOpt of
|
||||
true ->
|
||||
#archive_prefs{us = {LUser, LServer}, default = never};
|
||||
false ->
|
||||
Default = gen_mod:get_module_opt(
|
||||
LServer, ?MODULE, default, never),
|
||||
LServer, ?MODULE, default),
|
||||
#archive_prefs{us = {LUser, LServer}, default = Default}
|
||||
end
|
||||
end.
|
||||
@@ -838,20 +884,26 @@ prefs_el(Default, Always, Never, NS) ->
|
||||
|
||||
maybe_activate_mam(LUser, LServer) ->
|
||||
ActivateOpt = gen_mod:get_module_opt(
|
||||
LServer, ?MODULE, request_activates_archiving, false),
|
||||
LServer, ?MODULE, request_activates_archiving),
|
||||
case ActivateOpt of
|
||||
true ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Res = ets_cache:lookup(archive_prefs_cache, {LUser, LServer},
|
||||
fun() ->
|
||||
Mod:get_prefs(LUser, LServer)
|
||||
end),
|
||||
Res = case use_cache(Mod, LServer) of
|
||||
true ->
|
||||
ets_cache:lookup(archive_prefs_cache,
|
||||
{LUser, LServer},
|
||||
fun() ->
|
||||
Mod:get_prefs(LUser, LServer)
|
||||
end);
|
||||
false ->
|
||||
Mod:get_prefs(LUser, LServer)
|
||||
end,
|
||||
case Res of
|
||||
{ok, _Prefs} ->
|
||||
ok;
|
||||
error ->
|
||||
Default = gen_mod:get_module_opt(
|
||||
LServer, ?MODULE, default, never),
|
||||
LServer, ?MODULE, default),
|
||||
write_prefs(LUser, LServer, LServer, Default, [], [])
|
||||
end;
|
||||
false ->
|
||||
@@ -922,13 +974,14 @@ select(LServer, JidRequestor, JidArchive, Query, RSM, MsgType) ->
|
||||
msg_to_el(#archive_msg{timestamp = TS, packet = El, nick = Nick,
|
||||
peer = Peer, id = ID},
|
||||
MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) ->
|
||||
try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of
|
||||
CodecOpts = ejabberd_config:codec_options(LServer),
|
||||
try xmpp:decode(El, ?NS_CLIENT, CodecOpts) of
|
||||
Pkt1 ->
|
||||
Pkt2 = set_stanza_id(Pkt1, JidArchive, ID),
|
||||
Pkt3 = maybe_update_from_to(
|
||||
Pkt2, JidRequestor, JidArchive, Peer, MsgType, Nick),
|
||||
Delay = #delay{stamp = TS, from = jid:make(LServer)},
|
||||
{ok, #forwarded{xml_els = [xmpp:encode(Pkt3)], delay = Delay}}
|
||||
{ok, #forwarded{sub_els = [Pkt3], delay = Delay}}
|
||||
catch _:{xmpp_codec, Why} ->
|
||||
?ERROR_MSG("Failed to decode raw element ~p from message "
|
||||
"archive of user ~s: ~s",
|
||||
@@ -1063,7 +1116,26 @@ get_commands_spec() ->
|
||||
"Days to keep messages"],
|
||||
args_example = [<<"all">>, 31],
|
||||
args = [{type, binary}, {days, integer}],
|
||||
result = {res, rescode}}].
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = remove_mam_for_user, tags = [mam],
|
||||
desc = "Remove mam archive for user",
|
||||
module = ?MODULE, function = remove_mam_for_user,
|
||||
args = [{user, binary}, {server, binary}],
|
||||
args_desc = ["Username", "Server"],
|
||||
args_example = [<<"bob">>, <<"example.com">>],
|
||||
result = {res, restuple},
|
||||
result_desc = "Result tuple",
|
||||
result_example = {ok, <<"MAM archive removed">>}},
|
||||
#ejabberd_commands{name = remove_mam_for_user_with_peer, tags = [mam],
|
||||
desc = "Remove mam archive for user with peer",
|
||||
module = ?MODULE, function = remove_mam_for_user_with_peer,
|
||||
args = [{user, binary}, {server, binary}, {with, binary}],
|
||||
args_desc = ["Username", "Server", "Peer"],
|
||||
args_example = [<<"bob">>, <<"example.com">>, <<"anne@example.com">>],
|
||||
result = {res, restuple},
|
||||
result_desc = "Result tuple",
|
||||
result_example = {ok, <<"MAM archive removed">>}}
|
||||
].
|
||||
|
||||
mod_opt_type(assume_mam_usage) ->
|
||||
fun (B) when is_boolean(B) -> B end;
|
||||
@@ -1079,9 +1151,15 @@ mod_opt_type(default) ->
|
||||
(never) -> never;
|
||||
(roster) -> roster
|
||||
end;
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
mod_opt_type(request_activates_archiving) ->
|
||||
fun (B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(_) ->
|
||||
[assume_mam_usage, cache_life_time, cache_size, use_cache, cache_missed,
|
||||
db_type, default, iqdisc, request_activates_archiving].
|
||||
fun (B) when is_boolean(B) -> B end.
|
||||
|
||||
mod_options(Host) ->
|
||||
[{assume_mam_usage, false},
|
||||
{default, never},
|
||||
{request_activates_archiving, false},
|
||||
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
|
||||
{use_cache, ejabberd_config:use_cache(Host)},
|
||||
{cache_size, ejabberd_config:cache_size(Host)},
|
||||
{cache_missed, ejabberd_config:cache_missed(Host)},
|
||||
{cache_life_time, ejabberd_config:cache_life_time(Host)}].
|
||||
|
||||
+19
-1
@@ -28,7 +28,7 @@
|
||||
|
||||
%% API
|
||||
-export([init/2, remove_user/2, remove_room/3, delete_old_messages/3,
|
||||
extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/6]).
|
||||
extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/6, remove_from_archive/3]).
|
||||
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
-include("xmpp.hrl").
|
||||
@@ -67,6 +67,24 @@ remove_user(LUser, LServer) ->
|
||||
remove_room(_LServer, LName, LHost) ->
|
||||
remove_user(LName, LHost).
|
||||
|
||||
remove_from_archive(LUser, LServer, none) ->
|
||||
US = {LUser, LServer},
|
||||
case mnesia:transaction(fun () -> mnesia:delete({archive_msg, US}) end) of
|
||||
{atomic, _} -> ok;
|
||||
{aborted, Reason} -> {error, Reason}
|
||||
end;
|
||||
remove_from_archive(LUser, LServer, WithJid) ->
|
||||
US = {LUser, LServer},
|
||||
Peer = jid:remove_resource(jid:split(WithJid)),
|
||||
F = fun () ->
|
||||
Msgs = mnesia:match_object(#archive_msg{us = US, bare_peer = Peer, _ = '_'}),
|
||||
lists:foreach(fun mnesia:delete_object/1, Msgs)
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, _} -> ok;
|
||||
{aborted, Reason} -> {error, Reason}
|
||||
end.
|
||||
|
||||
delete_old_messages(global, TimeStamp, Type) ->
|
||||
mnesia:change_table_copy_type(archive_msg, node(), disc_copies),
|
||||
Result = delete_old_user_messages(mnesia:dirty_first(archive_msg), TimeStamp, Type),
|
||||
|
||||
+17
-9
@@ -30,7 +30,7 @@
|
||||
|
||||
%% API
|
||||
-export([init/2, remove_user/2, remove_room/3, delete_old_messages/3,
|
||||
extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/6, export/1]).
|
||||
extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/6, export/1, remove_from_archive/3]).
|
||||
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
-include("xmpp.hrl").
|
||||
@@ -38,12 +38,6 @@
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_sql_pt.hrl").
|
||||
|
||||
-ifdef(NEW_SQL_SCHEMA).
|
||||
-define(USE_NEW_SCHEMA, true).
|
||||
-else.
|
||||
-define(USE_NEW_SCHEMA, false).
|
||||
-endif.
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
@@ -62,6 +56,20 @@ remove_room(LServer, LName, LHost) ->
|
||||
LUser = jid:encode({LName, LHost, <<>>}),
|
||||
remove_user(LUser, LServer).
|
||||
|
||||
remove_from_archive(LUser, LServer, none) ->
|
||||
case ejabberd_sql:sql_query(LServer,
|
||||
?SQL("delete from archive where username=%(LUser)s and %(LServer)H")) of
|
||||
{error, Reason} -> {error, Reason};
|
||||
_ -> ok
|
||||
end;
|
||||
remove_from_archive(LUser, LServer, WithJid) ->
|
||||
Peer = jid:encode(jid:remove_resource(WithJid)),
|
||||
case ejabberd_sql:sql_query(LServer,
|
||||
?SQL("delete from archive where username=%(LUser)s and %(LServer)H and bare_peer=%(Peer)s")) of
|
||||
{error, Reason} -> {error, Reason};
|
||||
_ -> ok
|
||||
end.
|
||||
|
||||
delete_old_messages(ServerHost, TimeStamp, Type) ->
|
||||
TS = now_to_usec(TimeStamp),
|
||||
case Type of
|
||||
@@ -332,7 +340,7 @@ make_sql_query(User, LServer, MAMQuery, RSM) ->
|
||||
SServer = Escape(LServer),
|
||||
|
||||
Query =
|
||||
case ?USE_NEW_SCHEMA of
|
||||
case ejabberd_sql:use_new_schema() of
|
||||
true ->
|
||||
[<<"SELECT ">>, TopClause,
|
||||
<<" timestamp, xml, peer, kind, nick"
|
||||
@@ -361,7 +369,7 @@ make_sql_query(User, LServer, MAMQuery, RSM) ->
|
||||
[Query, <<" ORDER BY timestamp ASC ">>,
|
||||
LimitClause, <<";">>]
|
||||
end,
|
||||
case ?USE_NEW_SCHEMA of
|
||||
case ejabberd_sql:use_new_schema() of
|
||||
true ->
|
||||
{QueryPage,
|
||||
[<<"SELECT COUNT(*) FROM archive WHERE username='">>,
|
||||
|
||||
+7
-6
@@ -32,7 +32,7 @@
|
||||
-include("logger.hrl").
|
||||
-include("xmpp.hrl").
|
||||
|
||||
-export([start/2, stop/1, mod_opt_type/1, depends/2, reload/3]).
|
||||
-export([start/2, stop/1, mod_opt_type/1, mod_options/1, depends/2, reload/3]).
|
||||
|
||||
-export([offline_message_hook/1,
|
||||
sm_register_connection_hook/3, sm_remove_connection_hook/3,
|
||||
@@ -127,8 +127,8 @@ register_user(_User, Server) ->
|
||||
%%====================================================================
|
||||
|
||||
push(Host, Probe) ->
|
||||
IP = gen_mod:get_module_opt(Host, ?MODULE, ip, {127,0,0,1}),
|
||||
Port = gen_mod:get_module_opt(Host, ?MODULE, port, 11111),
|
||||
IP = gen_mod:get_module_opt(Host, ?MODULE, ip),
|
||||
Port = gen_mod:get_module_opt(Host, ?MODULE, port),
|
||||
send_metrics(Host, Probe, IP, Port).
|
||||
|
||||
send_metrics(Host, Probe, Peer, Port) ->
|
||||
@@ -184,6 +184,7 @@ mod_opt_type(ip) ->
|
||||
IP
|
||||
end;
|
||||
mod_opt_type(port) ->
|
||||
fun(I) when is_integer(I), I>0, I<65536 -> I end;
|
||||
mod_opt_type(_) ->
|
||||
[ip, port].
|
||||
fun(I) when is_integer(I), I>0, I<65536 -> I end.
|
||||
|
||||
mod_options(_) ->
|
||||
[{ip, <<"127.0.0.1">>}, {port, 11111}].
|
||||
|
||||
+13
-12
@@ -30,7 +30,7 @@
|
||||
%% API
|
||||
-export([start/2, stop/1, process_iq/1,
|
||||
disco_items/5, disco_identity/5, disco_info/5,
|
||||
disco_features/5, mod_opt_type/1, depends/2]).
|
||||
disco_features/5, mod_opt_type/1, mod_options/1, depends/2]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
@@ -124,8 +124,7 @@ process_iq(#iq{lang = Lang} = IQ) ->
|
||||
%%%===================================================================
|
||||
init([ServerHost, Opts]) ->
|
||||
process_flag(trap_exit, true),
|
||||
Hosts = gen_mod:get_opt_hosts(ServerHost, Opts, <<"mix.@HOST@">>),
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(ServerHost)),
|
||||
Hosts = gen_mod:get_opt_hosts(ServerHost, Opts),
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ConfigTab = gen_mod:get_module_proc(Host, config),
|
||||
@@ -140,20 +139,20 @@ init([ServerHost, Opts]) ->
|
||||
ejabberd_hooks:add(disco_info, Host, ?MODULE, disco_info, 100),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
||||
?NS_DISCO_ITEMS, mod_disco,
|
||||
process_local_iq_items, IQDisc),
|
||||
process_local_iq_items),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
||||
?NS_DISCO_INFO, mod_disco,
|
||||
process_local_iq_info, IQDisc),
|
||||
process_local_iq_info),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||
?NS_DISCO_ITEMS, mod_disco,
|
||||
process_local_iq_items, IQDisc),
|
||||
process_local_iq_items),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||
?NS_DISCO_INFO, mod_disco,
|
||||
process_local_iq_info, IQDisc),
|
||||
process_local_iq_info),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||
?NS_PUBSUB, mod_pubsub, iq_sm, IQDisc),
|
||||
?NS_PUBSUB, mod_pubsub, iq_sm),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||
?NS_MIX_0, ?MODULE, process_iq, IQDisc),
|
||||
?NS_MIX_0, ?MODULE, process_iq),
|
||||
ejabberd_router:register_route(Host, ServerHost)
|
||||
end, Hosts),
|
||||
{ok, #state{server_host = ServerHost, hosts = Hosts}}.
|
||||
@@ -316,8 +315,10 @@ is_not_subscribed({error, StanzaError}) ->
|
||||
depends(_Host, _Opts) ->
|
||||
[{mod_pubsub, hard}].
|
||||
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
mod_opt_type(host) -> fun iolist_to_binary/1;
|
||||
mod_opt_type(hosts) ->
|
||||
fun (L) -> lists:map(fun iolist_to_binary/1, L) end;
|
||||
mod_opt_type(_) -> [host, hosts, iqdisc].
|
||||
fun (L) -> lists:map(fun iolist_to_binary/1, L) end.
|
||||
|
||||
mod_options(_Host) ->
|
||||
[{host, <<"mix.@HOST@">>},
|
||||
{hosts, []}].
|
||||
|
||||
+109
-87
@@ -69,7 +69,7 @@
|
||||
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3,
|
||||
mod_opt_type/1, depends/2]).
|
||||
mod_opt_type/1, mod_options/1, depends/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -107,8 +107,7 @@
|
||||
-callback unregister_online_user(binary(), ljid(), binary(), binary()) -> any().
|
||||
-callback count_online_rooms_by_user(binary(), binary(), binary()) -> non_neg_integer().
|
||||
-callback get_online_rooms_by_user(binary(), binary(), binary()) -> [{binary(), binary()}].
|
||||
-callback get_subscribed_rooms(binary(), binary(), jid()) ->
|
||||
{ok, [{ljid(), binary(), [binary()]}]} | {error, any()}.
|
||||
-callback get_subscribed_rooms(binary(), binary(), jid()) -> [ljid()] | [].
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
@@ -233,7 +232,6 @@ get_online_rooms_by_user(ServerHost, LUser, LServer) ->
|
||||
|
||||
init([Host, Opts]) ->
|
||||
process_flag(trap_exit, true),
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
|
||||
#state{access = Access, hosts = MyHosts,
|
||||
history_size = HistorySize, queue_type = QueueType,
|
||||
room_shaper = RoomShaper} = State = init_state(Host, Opts),
|
||||
@@ -243,7 +241,7 @@ init([Host, Opts]) ->
|
||||
RMod:init(Host, [{hosts, MyHosts}|Opts]),
|
||||
lists:foreach(
|
||||
fun(MyHost) ->
|
||||
register_iq_handlers(MyHost, IQDisc),
|
||||
register_iq_handlers(MyHost),
|
||||
ejabberd_router:register_route(MyHost, Host),
|
||||
load_permanent_rooms(MyHost, Host, Access, HistorySize,
|
||||
RoomShaper, QueueType)
|
||||
@@ -273,8 +271,6 @@ handle_call({create, Room, Host, From, Nick, Opts}, _From,
|
||||
{reply, ok, State}.
|
||||
|
||||
handle_cast({reload, ServerHost, NewOpts, OldOpts}, #state{hosts = OldHosts}) ->
|
||||
NewIQDisc = gen_mod:get_opt(iqdisc, NewOpts, gen_iq_handler:iqdisc(ServerHost)),
|
||||
OldIQDisc = gen_mod:get_opt(iqdisc, OldOpts, gen_iq_handler:iqdisc(ServerHost)),
|
||||
NewMod = gen_mod:db_mod(ServerHost, NewOpts, ?MODULE),
|
||||
NewRMod = gen_mod:ram_db_mod(ServerHost, NewOpts, ?MODULE),
|
||||
OldMod = gen_mod:db_mod(ServerHost, OldOpts, ?MODULE),
|
||||
@@ -290,18 +286,10 @@ handle_cast({reload, ServerHost, NewOpts, OldOpts}, #state{hosts = OldHosts}) ->
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
if (NewIQDisc /= OldIQDisc) ->
|
||||
lists:foreach(
|
||||
fun(NewHost) ->
|
||||
register_iq_handlers(NewHost, NewIQDisc)
|
||||
end, NewHosts -- (NewHosts -- OldHosts));
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
lists:foreach(
|
||||
fun(NewHost) ->
|
||||
ejabberd_router:register_route(NewHost, ServerHost),
|
||||
register_iq_handlers(NewHost, NewIQDisc)
|
||||
register_iq_handlers(NewHost)
|
||||
end, NewHosts -- OldHosts),
|
||||
lists:foreach(
|
||||
fun(OldHost) ->
|
||||
@@ -353,18 +341,16 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
init_state(Host, Opts) ->
|
||||
MyHosts = gen_mod:get_opt_hosts(Host, Opts,
|
||||
<<"conference.@HOST@">>),
|
||||
Access = gen_mod:get_opt(access, Opts, all),
|
||||
AccessCreate = gen_mod:get_opt(access_create, Opts, all),
|
||||
AccessAdmin = gen_mod:get_opt(access_admin, Opts, none),
|
||||
AccessPersistent = gen_mod:get_opt(access_persistent, Opts, all),
|
||||
HistorySize = gen_mod:get_opt(history_size, Opts, 20),
|
||||
MaxRoomsDiscoItems = gen_mod:get_opt(max_rooms_discoitems, Opts, 100),
|
||||
DefRoomOpts = gen_mod:get_opt(default_room_options, Opts, []),
|
||||
QueueType = gen_mod:get_opt(queue_type, Opts,
|
||||
ejabberd_config:default_queue_type(Host)),
|
||||
RoomShaper = gen_mod:get_opt(room_shaper, Opts, none),
|
||||
MyHosts = gen_mod:get_opt_hosts(Host, Opts),
|
||||
Access = gen_mod:get_opt(access, Opts),
|
||||
AccessCreate = gen_mod:get_opt(access_create, Opts),
|
||||
AccessAdmin = gen_mod:get_opt(access_admin, Opts),
|
||||
AccessPersistent = gen_mod:get_opt(access_persistent, Opts),
|
||||
HistorySize = gen_mod:get_opt(history_size, Opts),
|
||||
MaxRoomsDiscoItems = gen_mod:get_opt(max_rooms_discoitems, Opts),
|
||||
DefRoomOpts = gen_mod:get_opt(default_room_options, Opts),
|
||||
QueueType = gen_mod:get_opt(queue_type, Opts),
|
||||
RoomShaper = gen_mod:get_opt(room_shaper, Opts),
|
||||
#state{hosts = MyHosts,
|
||||
server_host = Host,
|
||||
access = {Access, AccessCreate, AccessAdmin, AccessPersistent},
|
||||
@@ -374,19 +360,19 @@ init_state(Host, Opts) ->
|
||||
max_rooms_discoitems = MaxRoomsDiscoItems,
|
||||
room_shaper = RoomShaper}.
|
||||
|
||||
register_iq_handlers(Host, IQDisc) ->
|
||||
register_iq_handlers(Host) ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_REGISTER,
|
||||
?MODULE, process_register, IQDisc),
|
||||
?MODULE, process_register),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD,
|
||||
?MODULE, process_vcard, IQDisc),
|
||||
?MODULE, process_vcard),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MUCSUB,
|
||||
?MODULE, process_mucsub, IQDisc),
|
||||
?MODULE, process_mucsub),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_MUC_UNIQUE,
|
||||
?MODULE, process_muc_unique, IQDisc),
|
||||
?MODULE, process_muc_unique),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO,
|
||||
?MODULE, process_disco_info, IQDisc),
|
||||
?MODULE, process_disco_info),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS,
|
||||
?MODULE, process_disco_items, IQDisc).
|
||||
?MODULE, process_disco_items).
|
||||
|
||||
unregister_iq_handlers(Host) ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_REGISTER),
|
||||
@@ -490,19 +476,28 @@ process_vcard(#iq{lang = Lang} = IQ) ->
|
||||
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
|
||||
|
||||
-spec process_register(iq()) -> iq().
|
||||
process_register(#iq{type = get, from = From, to = To, lang = Lang,
|
||||
sub_els = [#register{}]} = IQ) ->
|
||||
process_register(#iq{type = Type, from = From, to = To, lang = Lang,
|
||||
sub_els = [El = #register{}]} = IQ) ->
|
||||
Host = To#jid.lserver,
|
||||
ServerHost = ejabberd_router:host_of_route(Host),
|
||||
xmpp:make_iq_result(IQ, iq_get_register_info(ServerHost, Host, From, Lang));
|
||||
process_register(#iq{type = set, from = From, to = To,
|
||||
lang = Lang, sub_els = [El = #register{}]} = IQ) ->
|
||||
Host = To#jid.lserver,
|
||||
ServerHost = ejabberd_router:host_of_route(Host),
|
||||
case process_iq_register_set(ServerHost, Host, From, El, Lang) of
|
||||
{result, Result} ->
|
||||
xmpp:make_iq_result(IQ, Result);
|
||||
{error, Err} ->
|
||||
AccessRegister = gen_mod:get_module_opt(ServerHost, ?MODULE, access_register),
|
||||
case acl:match_rule(ServerHost, AccessRegister, From) of
|
||||
allow ->
|
||||
case Type of
|
||||
get ->
|
||||
xmpp:make_iq_result(
|
||||
IQ, iq_get_register_info(ServerHost, Host, From, Lang));
|
||||
set ->
|
||||
case process_iq_register_set(ServerHost, Host, From, El, Lang) of
|
||||
{result, Result} ->
|
||||
xmpp:make_iq_result(IQ, Result);
|
||||
{error, Err} ->
|
||||
xmpp:make_error(IQ, Err)
|
||||
end
|
||||
end;
|
||||
deny ->
|
||||
ErrText = <<"Access denied by service policy">>,
|
||||
Err = xmpp:err_forbidden(ErrText, Lang),
|
||||
xmpp:make_error(IQ, Err)
|
||||
end.
|
||||
|
||||
@@ -510,10 +505,11 @@ process_register(#iq{type = set, from = From, to = To,
|
||||
process_disco_info(#iq{type = set, lang = Lang} = IQ) ->
|
||||
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
||||
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
|
||||
process_disco_info(#iq{type = get, to = To, lang = Lang,
|
||||
process_disco_info(#iq{type = get, from = From, to = To, lang = Lang,
|
||||
sub_els = [#disco_info{node = <<"">>}]} = IQ) ->
|
||||
ServerHost = ejabberd_router:host_of_route(To#jid.lserver),
|
||||
RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE),
|
||||
AccessRegister = gen_mod:get_module_opt(ServerHost, ?MODULE, access_register),
|
||||
X = ejabberd_hooks:run_fold(disco_info, ServerHost, [],
|
||||
[ServerHost, ?MODULE, <<"">>, Lang]),
|
||||
MAMFeatures = case gen_mod:is_loaded(ServerHost, mod_mam) of
|
||||
@@ -524,10 +520,14 @@ process_disco_info(#iq{type = get, to = To, lang = Lang,
|
||||
true -> [?NS_RSM];
|
||||
false -> []
|
||||
end,
|
||||
RegisterFeatures = case acl:match_rule(ServerHost, AccessRegister, From) of
|
||||
allow -> [?NS_REGISTER];
|
||||
deny -> []
|
||||
end,
|
||||
Features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS,
|
||||
?NS_REGISTER, ?NS_MUC, ?NS_VCARD, ?NS_MUCSUB, ?NS_MUC_UNIQUE
|
||||
| RSMFeatures ++ MAMFeatures],
|
||||
Name = gen_mod:get_module_opt(ServerHost, ?MODULE, name, ?T("Chatrooms")),
|
||||
?NS_MUC, ?NS_VCARD, ?NS_MUCSUB, ?NS_MUC_UNIQUE
|
||||
| RegisterFeatures ++ RSMFeatures ++ MAMFeatures],
|
||||
Name = gen_mod:get_module_opt(ServerHost, ?MODULE, name),
|
||||
Identity = #identity{category = <<"conference">>,
|
||||
type = <<"text">>,
|
||||
name = translate:translate(Lang, Name)},
|
||||
@@ -551,8 +551,7 @@ process_disco_items(#iq{type = get, from = From, to = To, lang = Lang,
|
||||
Host = To#jid.lserver,
|
||||
ServerHost = ejabberd_router:host_of_route(Host),
|
||||
MaxRoomsDiscoItems = gen_mod:get_module_opt(
|
||||
ServerHost, ?MODULE, max_rooms_discoitems,
|
||||
100),
|
||||
ServerHost, ?MODULE, max_rooms_discoitems),
|
||||
case iq_disco_items(ServerHost, Host, From, Lang,
|
||||
MaxRoomsDiscoItems, Node, RSM) of
|
||||
{error, Err} ->
|
||||
@@ -605,8 +604,8 @@ check_user_can_create_room(ServerHost, AccessCreate,
|
||||
end.
|
||||
|
||||
check_create_roomid(ServerHost, RoomID) ->
|
||||
Max = gen_mod:get_module_opt(ServerHost, ?MODULE, max_room_id, infinity),
|
||||
Regexp = gen_mod:get_module_opt(ServerHost, ?MODULE, regexp_room_id, ""),
|
||||
Max = gen_mod:get_module_opt(ServerHost, ?MODULE, max_room_id),
|
||||
Regexp = gen_mod:get_module_opt(ServerHost, ?MODULE, regexp_room_id),
|
||||
(byte_size(RoomID) =< Max) and
|
||||
(re:run(RoomID, Regexp, [unicode, {capture, none}]) == match).
|
||||
|
||||
@@ -741,7 +740,7 @@ iq_get_register_info(ServerHost, Host, From, Lang) ->
|
||||
instructions = [Inst], fields = Fields},
|
||||
#register{nick = Nick,
|
||||
registered = Registered,
|
||||
instructions =
|
||||
instructions =
|
||||
translate:translate(
|
||||
Lang, <<"You need a client that supports x:data "
|
||||
"to register the nickname">>),
|
||||
@@ -877,6 +876,8 @@ mod_opt_type(access_create) ->
|
||||
fun acl:access_rules_validator/1;
|
||||
mod_opt_type(access_persistent) ->
|
||||
fun acl:access_rules_validator/1;
|
||||
mod_opt_type(access_register) ->
|
||||
fun acl:access_rules_validator/1;
|
||||
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
||||
mod_opt_type(ram_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
||||
mod_opt_type(history_size) ->
|
||||
@@ -975,35 +976,56 @@ mod_opt_type({default_room_options, presence_broadcast}) ->
|
||||
(participant) -> participant;
|
||||
(visitor) -> visitor
|
||||
end, L)
|
||||
end;
|
||||
mod_opt_type(_) ->
|
||||
[access, access_admin, access_create, access_persistent,
|
||||
db_type, ram_db_type, history_size, host, hosts, name,
|
||||
max_room_desc, max_room_id, max_room_name,
|
||||
max_rooms_discoitems, max_user_conferences, max_users,
|
||||
max_users_admin_threshold, max_users_presence,
|
||||
min_message_interval, min_presence_interval, queue_type,
|
||||
regexp_room_id, room_shaper, user_message_shaper, user_presence_shaper,
|
||||
{default_room_options, allow_change_subj},
|
||||
{default_room_options, allow_private_messages},
|
||||
{default_room_options, allow_query_users},
|
||||
{default_room_options, allow_user_invites},
|
||||
{default_room_options, allow_visitor_nickchange},
|
||||
{default_room_options, allow_visitor_status},
|
||||
{default_room_options, anonymous},
|
||||
{default_room_options, captcha_protected},
|
||||
{default_room_options, logging},
|
||||
{default_room_options, members_by_default},
|
||||
{default_room_options, members_only},
|
||||
{default_room_options, moderated},
|
||||
{default_room_options, password_protected},
|
||||
{default_room_options, persistent},
|
||||
{default_room_options, public},
|
||||
{default_room_options, public_list},
|
||||
{default_room_options, mam},
|
||||
{default_room_options, allow_subscription},
|
||||
{default_room_options, password},
|
||||
{default_room_options, title},
|
||||
{default_room_options, allow_private_messages_from_visitors},
|
||||
{default_room_options, max_users},
|
||||
{default_room_options, presence_broadcast}].
|
||||
end.
|
||||
|
||||
mod_options(Host) ->
|
||||
[{access, all},
|
||||
{access_admin, none},
|
||||
{access_create, all},
|
||||
{access_persistent, all},
|
||||
{access_register, all},
|
||||
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
|
||||
{ram_db_type, ejabberd_config:default_ram_db(Host, ?MODULE)},
|
||||
{history_size, 20},
|
||||
{host, <<"conference.@HOST@">>},
|
||||
{hosts, []},
|
||||
{name, ?T("Chatrooms")},
|
||||
{max_room_desc, infinity},
|
||||
{max_room_id, infinity},
|
||||
{max_room_name, infinity},
|
||||
{max_rooms_discoitems, 100},
|
||||
{max_user_conferences, 10},
|
||||
{max_users, 200},
|
||||
{max_users_admin_threshold, 5},
|
||||
{max_users_presence, 1000},
|
||||
{min_message_interval, 0},
|
||||
{min_presence_interval, 0},
|
||||
{queue_type, ejabberd_config:default_queue_type(Host)},
|
||||
{regexp_room_id, <<"">>},
|
||||
{room_shaper, none},
|
||||
{user_message_shaper, none},
|
||||
{user_presence_shaper, none},
|
||||
{default_room_options,
|
||||
[{allow_change_subj,true},
|
||||
{allow_private_messages,true},
|
||||
{allow_query_users,true},
|
||||
{allow_user_invites,false},
|
||||
{allow_visitor_nickchange,true},
|
||||
{allow_visitor_status,true},
|
||||
{anonymous,true},
|
||||
{captcha_protected,false},
|
||||
{logging,false},
|
||||
{members_by_default,true},
|
||||
{members_only,false},
|
||||
{moderated,true},
|
||||
{password_protected,false},
|
||||
{persistent,false},
|
||||
{public,true},
|
||||
{public_list,true},
|
||||
{mam,false},
|
||||
{allow_subscription,false},
|
||||
{password,<<>>},
|
||||
{title,<<>>},
|
||||
{allow_private_messages_from_visitors,anyone},
|
||||
{max_users,200},
|
||||
{presence_broadcast,[moderator,participant,visitor]}]}].
|
||||
|
||||
+46
-17
@@ -37,10 +37,10 @@
|
||||
get_user_rooms/2, get_room_occupants/2,
|
||||
get_room_occupants_number/2, send_direct_invitation/5,
|
||||
change_room_option/4, get_room_options/2,
|
||||
set_room_affiliation/4, get_room_affiliations/2,
|
||||
set_room_affiliation/4, get_room_affiliations/2, get_room_affiliation/3,
|
||||
web_menu_main/2, web_page_main/2, web_menu_host/3,
|
||||
subscribe_room/4, unsubscribe_room/2, get_subscribers/2,
|
||||
web_page_host/3, mod_opt_type/1, get_commands_spec/0]).
|
||||
web_page_host/3, mod_options/1, get_commands_spec/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -313,8 +313,17 @@ get_commands_spec() ->
|
||||
{affiliation, atom},
|
||||
{reason, string}
|
||||
]}}
|
||||
}}}
|
||||
].
|
||||
}}},
|
||||
#ejabberd_commands{name = get_room_affiliation, tags = [muc_room],
|
||||
desc = "Get affiliation of a user in MUC room",
|
||||
module = ?MODULE, function = get_room_affiliation,
|
||||
args_desc = ["Room name", "MUC service", "User JID"],
|
||||
args_example = ["room1", "muc.example.com", "user1@example.com"],
|
||||
result_desc = "Affiliation of the user",
|
||||
result_example = member,
|
||||
args = [{name, binary}, {service, binary}, {jid, binary}],
|
||||
result = {affiliation, atom}}
|
||||
].
|
||||
|
||||
|
||||
%%%
|
||||
@@ -569,8 +578,10 @@ prepare_room_info(Room_info) ->
|
||||
%% ok | error
|
||||
%% @doc Create a room immediately with the default options.
|
||||
create_room(Name1, Host1, ServerHost) ->
|
||||
create_room_with_opts(Name1, Host1, ServerHost, []),
|
||||
change_room_option(Name1, Host1, <<"persistent">>, <<"true">>).
|
||||
case create_room_with_opts(Name1, Host1, ServerHost, []) of
|
||||
ok -> change_room_option(Name1, Host1, <<"persistent">>, <<"true">>);
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
create_room_with_opts(Name1, Host1, ServerHost, CustomRoomOpts) ->
|
||||
true = (error /= (Name = jid:nodeprep(Name1))),
|
||||
@@ -578,7 +589,7 @@ create_room_with_opts(Name1, Host1, ServerHost, CustomRoomOpts) ->
|
||||
|
||||
%% Get the default room options from the muc configuration
|
||||
DefRoomOpts = gen_mod:get_module_opt(ServerHost, mod_muc,
|
||||
default_room_options, []),
|
||||
default_room_options),
|
||||
%% Change default room options as required
|
||||
FormattedRoomOpts = [format_room_option(Opt, Val) || {Opt, Val}<-CustomRoomOpts],
|
||||
RoomOpts = lists:ukeymerge(1,
|
||||
@@ -589,14 +600,13 @@ create_room_with_opts(Name1, Host1, ServerHost, CustomRoomOpts) ->
|
||||
mod_muc:store_room(ServerHost, Host, Name, RoomOpts),
|
||||
|
||||
%% Get all remaining mod_muc parameters that might be utilized
|
||||
Access = gen_mod:get_module_opt(ServerHost, mod_muc, access, all),
|
||||
AcCreate = gen_mod:get_module_opt(ServerHost, mod_muc, access_create, all),
|
||||
AcAdmin = gen_mod:get_module_opt(ServerHost, mod_muc, access_admin, none),
|
||||
AcPer = gen_mod:get_module_opt(ServerHost, mod_muc, access_persistent, all),
|
||||
HistorySize = gen_mod:get_module_opt(ServerHost, mod_muc, history_size, 20),
|
||||
RoomShaper = gen_mod:get_module_opt(ServerHost, mod_muc, room_shaper, none),
|
||||
QueueType = gen_mod:get_module_opt(ServerHost, mod_muc, queue_type,
|
||||
ejabberd_config:default_queue_type(ServerHost)),
|
||||
Access = gen_mod:get_module_opt(ServerHost, mod_muc, access),
|
||||
AcCreate = gen_mod:get_module_opt(ServerHost, mod_muc, access_create),
|
||||
AcAdmin = gen_mod:get_module_opt(ServerHost, mod_muc, access_admin),
|
||||
AcPer = gen_mod:get_module_opt(ServerHost, mod_muc, access_persistent),
|
||||
HistorySize = gen_mod:get_module_opt(ServerHost, mod_muc, history_size),
|
||||
RoomShaper = gen_mod:get_module_opt(ServerHost, mod_muc, room_shaper),
|
||||
QueueType = gen_mod:get_module_opt(ServerHost, mod_muc, queue_type),
|
||||
|
||||
%% If the room does not exist yet in the muc_online_room
|
||||
case mod_muc:find_online_room(Name, Host) of
|
||||
@@ -698,7 +708,7 @@ create_rooms_file(Filename) ->
|
||||
file:close(F),
|
||||
%% Read the default room options defined for the first virtual host
|
||||
DefRoomOpts = gen_mod:get_module_opt(?MYNAME, mod_muc,
|
||||
default_room_options, []),
|
||||
default_room_options),
|
||||
[muc_create_room(?MYNAME, A, DefRoomOpts) || A <- Rooms],
|
||||
ok.
|
||||
|
||||
@@ -1033,6 +1043,25 @@ get_room_affiliations(Name, Service) ->
|
||||
throw({error, "The room does not exist."})
|
||||
end.
|
||||
|
||||
%%----------------------------
|
||||
%% Get Room Affiliation
|
||||
%%----------------------------
|
||||
|
||||
%% @spec(Name::binary(), Service::binary(), JID::binary()) ->
|
||||
%% {Affiliation::string()}
|
||||
%% @doc Get affiliation of a user in the room Name@Service.
|
||||
|
||||
get_room_affiliation(Name, Service, JID) ->
|
||||
case mod_muc:find_online_room(Name, Service) of
|
||||
{ok, Pid} ->
|
||||
%% Get the PID of the online room, then request its state
|
||||
{ok, StateData} = p1_fsm:sync_send_all_state_event(Pid, get_state),
|
||||
UserJID = jid:decode(JID),
|
||||
mod_muc_room:get_affiliation(UserJID, StateData);
|
||||
error ->
|
||||
throw({error, "The room does not exist."})
|
||||
end.
|
||||
|
||||
%%----------------------------
|
||||
%% Change Room Affiliation
|
||||
%%----------------------------
|
||||
@@ -1215,4 +1244,4 @@ find_hosts(ServerHost) ->
|
||||
[]
|
||||
end.
|
||||
|
||||
mod_opt_type(_) -> [].
|
||||
mod_options(_) -> [].
|
||||
|
||||
+37
-24
@@ -39,7 +39,7 @@
|
||||
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3,
|
||||
mod_opt_type/1, depends/2]).
|
||||
mod_opt_type/1, mod_options/1, depends/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -138,16 +138,16 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||
%%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
init_state(Host, Opts) ->
|
||||
OutDir = gen_mod:get_opt(outdir, Opts, <<"www/muc">>),
|
||||
DirType = gen_mod:get_opt(dirtype, Opts, subdirs),
|
||||
DirName = gen_mod:get_opt(dirname, Opts, room_jid),
|
||||
FileFormat = gen_mod:get_opt(file_format, Opts, html),
|
||||
FilePermissions = gen_mod:get_opt(file_permissions, Opts, {644, 33}),
|
||||
CSSFile = gen_mod:get_opt(cssfile, Opts, false),
|
||||
AccessLog = gen_mod:get_opt(access_log, Opts, muc_admin),
|
||||
Timezone = gen_mod:get_opt(timezone, Opts, local),
|
||||
Top_link = gen_mod:get_opt(top_link, Opts, {<<"/">>, <<"Home">>}),
|
||||
NoFollow = gen_mod:get_opt(spam_prevention, Opts, true),
|
||||
OutDir = gen_mod:get_opt(outdir, Opts),
|
||||
DirType = gen_mod:get_opt(dirtype, Opts),
|
||||
DirName = gen_mod:get_opt(dirname, Opts),
|
||||
FileFormat = gen_mod:get_opt(file_format, Opts),
|
||||
FilePermissions = gen_mod:get_opt(file_permissions, Opts),
|
||||
CSSFile = gen_mod:get_opt(cssfile, Opts),
|
||||
AccessLog = gen_mod:get_opt(access_log, Opts),
|
||||
Timezone = gen_mod:get_opt(timezone, Opts),
|
||||
Top_link = gen_mod:get_opt(top_link, Opts),
|
||||
NoFollow = gen_mod:get_opt(spam_prevention, Opts),
|
||||
Lang = ejabberd_config:get_lang(Host),
|
||||
#logstate{host = Host, out_dir = OutDir,
|
||||
dir_type = DirType, dir_name = DirName,
|
||||
@@ -375,31 +375,31 @@ add_message_to_log(Nick1, Message, RoomJID, Opts,
|
||||
io_lib:format("<font class=\"ml\">~s ~s: ~s</font><br/>",
|
||||
[Nick, ?T(<<"leaves the room">>),
|
||||
htmlize(Reason, NoFollow, FileFormat)]);
|
||||
{kickban, <<"301">>, <<"">>} ->
|
||||
{kickban, 301, <<"">>} ->
|
||||
io_lib:format("<font class=\"mb\">~s ~s</font><br/>",
|
||||
[Nick, ?T(<<"has been banned">>)]);
|
||||
{kickban, <<"301">>, Reason} ->
|
||||
{kickban, 301, Reason} ->
|
||||
io_lib:format("<font class=\"mb\">~s ~s: ~s</font><br/>",
|
||||
[Nick, ?T(<<"has been banned">>),
|
||||
htmlize(Reason, FileFormat)]);
|
||||
{kickban, <<"307">>, <<"">>} ->
|
||||
{kickban, 307, <<"">>} ->
|
||||
io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
|
||||
[Nick, ?T(<<"has been kicked">>)]);
|
||||
{kickban, <<"307">>, Reason} ->
|
||||
{kickban, 307, Reason} ->
|
||||
io_lib:format("<font class=\"mk\">~s ~s: ~s</font><br/>",
|
||||
[Nick, ?T(<<"has been kicked">>),
|
||||
htmlize(Reason, FileFormat)]);
|
||||
{kickban, <<"321">>, <<"">>} ->
|
||||
{kickban, 321, <<"">>} ->
|
||||
io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
|
||||
[Nick,
|
||||
?T(<<"has been kicked because of an affiliation "
|
||||
"change">>)]);
|
||||
{kickban, <<"322">>, <<"">>} ->
|
||||
{kickban, 322, <<"">>} ->
|
||||
io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
|
||||
[Nick,
|
||||
?T(<<"has been kicked because the room has "
|
||||
"been changed to members-only">>)]);
|
||||
{kickban, <<"332">>, <<"">>} ->
|
||||
{kickban, 332, <<"">>} ->
|
||||
io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
|
||||
[Nick,
|
||||
?T(<<"has been kicked because of a system "
|
||||
@@ -931,7 +931,10 @@ has_no_permanent_store_hint(Packet) ->
|
||||
|
||||
mod_opt_type(access_log) ->
|
||||
fun acl:access_rules_validator/1;
|
||||
mod_opt_type(cssfile) -> fun misc:try_read_file/1;
|
||||
mod_opt_type(cssfile) ->
|
||||
fun(false) -> false;
|
||||
(File) -> misc:try_read_file(File)
|
||||
end;
|
||||
mod_opt_type(dirname) ->
|
||||
fun (room_jid) -> room_jid;
|
||||
(room_name) -> room_name
|
||||
@@ -963,8 +966,18 @@ mod_opt_type(timezone) ->
|
||||
mod_opt_type(top_link) ->
|
||||
fun ([{S1, S2}]) ->
|
||||
{iolist_to_binary(S1), iolist_to_binary(S2)}
|
||||
end;
|
||||
mod_opt_type(_) ->
|
||||
[access_log, cssfile, dirname, dirtype, file_format,
|
||||
{file_permissions, mode}, {file_permissions, group},
|
||||
outdir, spam_prevention, timezone, top_link].
|
||||
end.
|
||||
|
||||
mod_options(_) ->
|
||||
[{access_log, muc_admin},
|
||||
{cssfile, false},
|
||||
{dirname, room_jid},
|
||||
{dirtype, subdirs},
|
||||
{file_format, html},
|
||||
{file_permissions,
|
||||
[{mode, 644},
|
||||
{group, 33}]},
|
||||
{outdir, <<"www/muc">>},
|
||||
{spam_prevention, true},
|
||||
{timezone, local},
|
||||
{top_link, [{<<"/">>, <<"Home">>}]}].
|
||||
|
||||
+349
-138
@@ -37,7 +37,9 @@
|
||||
get_role/2,
|
||||
get_affiliation/2,
|
||||
is_occupant_or_admin/2,
|
||||
route/2]).
|
||||
route/2,
|
||||
expand_opts/1,
|
||||
config_fields/0]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1,
|
||||
@@ -164,8 +166,8 @@ normal_state({route, <<"">>,
|
||||
Now = p1_time_compat:system_time(micro_seconds),
|
||||
MinMessageInterval = trunc(gen_mod:get_module_opt(
|
||||
StateData#state.server_host,
|
||||
mod_muc, min_message_interval,
|
||||
0) * 1000000),
|
||||
mod_muc, min_message_interval)
|
||||
* 1000000),
|
||||
Size = element_size(Packet),
|
||||
{MessageShaper, MessageShaperInterval} =
|
||||
shaper:update(Activity#activity.message_shaper, Size),
|
||||
@@ -288,7 +290,7 @@ normal_state({route, <<"">>,
|
||||
process_iq_admin(From, IQ, StateData);
|
||||
?NS_MUC_OWNER ->
|
||||
process_iq_owner(From, IQ, StateData);
|
||||
?NS_DISCO_INFO when SubEl#disco_info.node == <<>> ->
|
||||
?NS_DISCO_INFO ->
|
||||
process_iq_disco_info(From, IQ, StateData);
|
||||
?NS_DISCO_ITEMS ->
|
||||
process_iq_disco_items(From, IQ, StateData);
|
||||
@@ -346,8 +348,8 @@ normal_state({route, Nick, #presence{from = From} = Packet}, StateData) ->
|
||||
Now = p1_time_compat:system_time(micro_seconds),
|
||||
MinPresenceInterval =
|
||||
trunc(gen_mod:get_module_opt(StateData#state.server_host,
|
||||
mod_muc, min_presence_interval,
|
||||
0) * 1000000),
|
||||
mod_muc, min_presence_interval)
|
||||
* 1000000),
|
||||
if (Now >= Activity#activity.presence_time + MinPresenceInterval)
|
||||
and (Activity#activity.presence == undefined) ->
|
||||
NewActivity = Activity#activity{presence_time = Now},
|
||||
@@ -500,13 +502,13 @@ handle_event(destroy, StateName, StateData) ->
|
||||
handle_event({destroy, <<"">>}, StateName, StateData);
|
||||
handle_event({set_affiliations, Affiliations},
|
||||
StateName, StateData) ->
|
||||
{next_state, StateName,
|
||||
StateData#state{affiliations = Affiliations}};
|
||||
NewStateData = set_affiliations(Affiliations, StateData),
|
||||
{next_state, StateName, NewStateData};
|
||||
handle_event(_Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
handle_sync_event({get_disco_item, Filter, JID, Lang}, _From, StateName, StateData) ->
|
||||
Len = ?DICT:size(StateData#state.users),
|
||||
Len = ?DICT:size(StateData#state.nicks),
|
||||
Reply = case (Filter == all) or (Filter == Len) or ((Filter /= 0) and (Len /= 0)) of
|
||||
true ->
|
||||
get_roomdesc_reply(JID, StateData,
|
||||
@@ -1262,58 +1264,132 @@ set_affiliation(JID, Affiliation, StateData) ->
|
||||
set_affiliation(JID, Affiliation, StateData, <<"">>).
|
||||
|
||||
-spec set_affiliation(jid(), affiliation(), state(), binary()) -> state().
|
||||
set_affiliation(JID, Affiliation,
|
||||
#state{config = #config{persistent = false}} = StateData,
|
||||
Reason) ->
|
||||
set_affiliation_fallback(JID, Affiliation, StateData, Reason);
|
||||
set_affiliation(JID, Affiliation, StateData, Reason) ->
|
||||
ServerHost = StateData#state.server_host,
|
||||
Room = StateData#state.room,
|
||||
Host = StateData#state.host,
|
||||
Mod = gen_mod:db_mod(ServerHost, mod_muc),
|
||||
case Mod:set_affiliation(ServerHost, Room, Host, JID, Affiliation, Reason) of
|
||||
ok ->
|
||||
StateData;
|
||||
{error, _} ->
|
||||
set_affiliation_fallback(JID, Affiliation, StateData, Reason)
|
||||
end.
|
||||
|
||||
-spec set_affiliation_fallback(jid(), affiliation(), state(), binary()) -> state().
|
||||
set_affiliation_fallback(JID, Affiliation, StateData, Reason) ->
|
||||
LJID = jid:remove_resource(jid:tolower(JID)),
|
||||
Affiliations = case Affiliation of
|
||||
none ->
|
||||
(?DICT):erase(LJID, StateData#state.affiliations);
|
||||
_ ->
|
||||
(?DICT):store(LJID, {Affiliation, Reason},
|
||||
StateData#state.affiliations)
|
||||
none ->
|
||||
(?DICT):erase(LJID, StateData#state.affiliations);
|
||||
_ ->
|
||||
(?DICT):store(LJID, {Affiliation, Reason},
|
||||
StateData#state.affiliations)
|
||||
end,
|
||||
StateData#state{affiliations = Affiliations}.
|
||||
|
||||
-spec get_affiliation(jid(), state()) -> affiliation().
|
||||
get_affiliation(JID, StateData) ->
|
||||
{_AccessRoute, _AccessCreate, AccessAdmin,
|
||||
_AccessPersistent} =
|
||||
StateData#state.access,
|
||||
Res = case acl:match_rule(StateData#state.server_host,
|
||||
AccessAdmin, JID)
|
||||
of
|
||||
allow -> owner;
|
||||
_ ->
|
||||
LJID = jid:tolower(JID),
|
||||
case (?DICT):find(LJID, StateData#state.affiliations) of
|
||||
{ok, Affiliation} -> Affiliation;
|
||||
_ ->
|
||||
LJID1 = jid:remove_resource(LJID),
|
||||
case (?DICT):find(LJID1, StateData#state.affiliations)
|
||||
of
|
||||
{ok, Affiliation} -> Affiliation;
|
||||
_ ->
|
||||
LJID2 = setelement(1, LJID, <<"">>),
|
||||
case (?DICT):find(LJID2,
|
||||
StateData#state.affiliations)
|
||||
of
|
||||
{ok, Affiliation} -> Affiliation;
|
||||
_ ->
|
||||
LJID3 = jid:remove_resource(LJID2),
|
||||
case (?DICT):find(LJID3,
|
||||
StateData#state.affiliations)
|
||||
of
|
||||
{ok, Affiliation} -> Affiliation;
|
||||
_ -> none
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
case Res of
|
||||
{A, _Reason} -> A;
|
||||
_ -> Res
|
||||
-spec set_affiliations(?TDICT, state()) -> state().
|
||||
set_affiliations(Affiliations,
|
||||
#state{config = #config{persistent = false}} = StateData) ->
|
||||
set_affiliations_fallback(Affiliations, StateData);
|
||||
set_affiliations(Affiliations, StateData) ->
|
||||
Room = StateData#state.room,
|
||||
Host = StateData#state.host,
|
||||
ServerHost = StateData#state.server_host,
|
||||
Mod = gen_mod:db_mod(ServerHost, mod_muc),
|
||||
case Mod:set_affiliations(ServerHost, Room, Host, Affiliations) of
|
||||
ok ->
|
||||
StateData;
|
||||
{error, _} ->
|
||||
set_affiliations_fallback(Affiliations, StateData)
|
||||
end.
|
||||
|
||||
-spec set_affiliations_fallback(?TDICT, state()) -> state().
|
||||
set_affiliations_fallback(Affiliations, StateData) ->
|
||||
StateData#state{affiliations = Affiliations}.
|
||||
|
||||
-spec get_affiliation(ljid() | jid(), state()) -> affiliation().
|
||||
get_affiliation(#jid{} = JID, StateData) ->
|
||||
case get_service_affiliation(JID, StateData) of
|
||||
owner ->
|
||||
owner;
|
||||
none ->
|
||||
case do_get_affiliation(JID, StateData) of
|
||||
{Affiliation, _Reason} -> Affiliation;
|
||||
Affiliation -> Affiliation
|
||||
end
|
||||
end;
|
||||
get_affiliation(LJID, StateData) ->
|
||||
get_affiliation(jid:make(LJID), StateData).
|
||||
|
||||
-spec do_get_affiliation(jid(), state()) -> affiliation().
|
||||
do_get_affiliation(JID, #state{config = #config{persistent = false}} = StateData) ->
|
||||
do_get_affiliation_fallback(JID, StateData);
|
||||
do_get_affiliation(JID, StateData) ->
|
||||
Room = StateData#state.room,
|
||||
Host = StateData#state.host,
|
||||
LServer = JID#jid.lserver,
|
||||
LUser = JID#jid.luser,
|
||||
ServerHost = StateData#state.server_host,
|
||||
Mod = gen_mod:db_mod(ServerHost, mod_muc),
|
||||
case Mod:get_affiliation(ServerHost, Room, Host, LUser, LServer) of
|
||||
{error, _} ->
|
||||
do_get_affiliation_fallback(JID, StateData);
|
||||
{ok, Affiliation} ->
|
||||
Affiliation
|
||||
end.
|
||||
|
||||
-spec do_get_affiliation_fallback(jid(), state()) -> affiliation().
|
||||
do_get_affiliation_fallback(JID, StateData) ->
|
||||
LJID = jid:tolower(JID),
|
||||
case (?DICT):find(LJID, StateData#state.affiliations) of
|
||||
{ok, Affiliation} -> Affiliation;
|
||||
_ ->
|
||||
LJID1 = jid:remove_resource(LJID),
|
||||
case (?DICT):find(LJID1, StateData#state.affiliations)
|
||||
of
|
||||
{ok, Affiliation} -> Affiliation;
|
||||
_ ->
|
||||
LJID2 = setelement(1, LJID, <<"">>),
|
||||
case (?DICT):find(LJID2,
|
||||
StateData#state.affiliations)
|
||||
of
|
||||
{ok, Affiliation} -> Affiliation;
|
||||
_ ->
|
||||
LJID3 = jid:remove_resource(LJID2),
|
||||
case (?DICT):find(LJID3,
|
||||
StateData#state.affiliations)
|
||||
of
|
||||
{ok, Affiliation} -> Affiliation;
|
||||
_ -> none
|
||||
end
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
-spec get_affiliations(state()) -> ?TDICT.
|
||||
get_affiliations(#state{config = #config{persistent = false}} = StateData) ->
|
||||
get_affiliations_callback(StateData);
|
||||
get_affiliations(StateData) ->
|
||||
Room = StateData#state.room,
|
||||
Host = StateData#state.host,
|
||||
ServerHost = StateData#state.server_host,
|
||||
Mod = gen_mod:db_mod(ServerHost, mod_muc),
|
||||
case Mod:get_affiliations(ServerHost, Room, Host) of
|
||||
{error, _} ->
|
||||
get_affiliations_callback(StateData);
|
||||
{ok, Affiliations} ->
|
||||
Affiliations
|
||||
end.
|
||||
|
||||
-spec get_affiliations_callback(state()) -> ?TDICT.
|
||||
get_affiliations_callback(StateData) ->
|
||||
StateData#state.affiliations.
|
||||
|
||||
-spec get_service_affiliation(jid(), state()) -> owner | none.
|
||||
get_service_affiliation(JID, StateData) ->
|
||||
{_AccessRoute, _AccessCreate, AccessAdmin,
|
||||
@@ -1421,30 +1497,28 @@ get_max_users(StateData) ->
|
||||
-spec get_service_max_users(state()) -> pos_integer().
|
||||
get_service_max_users(StateData) ->
|
||||
gen_mod:get_module_opt(StateData#state.server_host,
|
||||
mod_muc, max_users,
|
||||
?MAX_USERS_DEFAULT).
|
||||
mod_muc, max_users).
|
||||
|
||||
-spec get_max_users_admin_threshold(state()) -> pos_integer().
|
||||
get_max_users_admin_threshold(StateData) ->
|
||||
gen_mod:get_module_opt(StateData#state.server_host,
|
||||
mod_muc, max_users_admin_threshold,
|
||||
5).
|
||||
mod_muc, max_users_admin_threshold).
|
||||
|
||||
-spec room_queue_new(binary(), shaper:shaper(), _) -> p1_queue:queue().
|
||||
room_queue_new(ServerHost, Shaper, QueueType) ->
|
||||
HaveRoomShaper = Shaper /= none,
|
||||
HaveMessageShaper = gen_mod:get_module_opt(
|
||||
ServerHost, mod_muc, user_message_shaper,
|
||||
none) /= none,
|
||||
ServerHost, mod_muc,
|
||||
user_message_shaper) /= none,
|
||||
HavePresenceShaper = gen_mod:get_module_opt(
|
||||
ServerHost, mod_muc, user_presence_shaper,
|
||||
none) /= none,
|
||||
ServerHost, mod_muc,
|
||||
user_presence_shaper) /= none,
|
||||
HaveMinMessageInterval = gen_mod:get_module_opt(
|
||||
ServerHost, mod_muc, min_message_interval,
|
||||
0) /= 0,
|
||||
ServerHost, mod_muc,
|
||||
min_message_interval) /= 0,
|
||||
HaveMinPresenceInterval = gen_mod:get_module_opt(
|
||||
ServerHost, mod_muc, min_presence_interval,
|
||||
0) /= 0,
|
||||
ServerHost, mod_muc,
|
||||
min_presence_interval) /= 0,
|
||||
if HaveRoomShaper or HaveMessageShaper or HavePresenceShaper
|
||||
or HaveMinMessageInterval or HaveMinPresenceInterval ->
|
||||
p1_queue:new(QueueType);
|
||||
@@ -1461,12 +1535,10 @@ get_user_activity(JID, StateData) ->
|
||||
error ->
|
||||
MessageShaper =
|
||||
shaper:new(gen_mod:get_module_opt(StateData#state.server_host,
|
||||
mod_muc, user_message_shaper,
|
||||
none)),
|
||||
mod_muc, user_message_shaper)),
|
||||
PresenceShaper =
|
||||
shaper:new(gen_mod:get_module_opt(StateData#state.server_host,
|
||||
mod_muc, user_presence_shaper,
|
||||
none)),
|
||||
mod_muc, user_presence_shaper)),
|
||||
#activity{message_shaper = MessageShaper,
|
||||
presence_shaper = PresenceShaper}
|
||||
end.
|
||||
@@ -1475,12 +1547,12 @@ get_user_activity(JID, StateData) ->
|
||||
store_user_activity(JID, UserActivity, StateData) ->
|
||||
MinMessageInterval =
|
||||
trunc(gen_mod:get_module_opt(StateData#state.server_host,
|
||||
mod_muc, min_message_interval,
|
||||
0) * 1000),
|
||||
mod_muc, min_message_interval)
|
||||
* 1000),
|
||||
MinPresenceInterval =
|
||||
trunc(gen_mod:get_module_opt(StateData#state.server_host,
|
||||
mod_muc, min_presence_interval,
|
||||
0) * 1000),
|
||||
mod_muc, min_presence_interval)
|
||||
* 1000),
|
||||
Key = jid:tolower(JID),
|
||||
Now = p1_time_compat:system_time(micro_seconds),
|
||||
Activity1 = clean_treap(StateData#state.activity,
|
||||
@@ -1788,8 +1860,7 @@ add_new_user(From, Nick, Packet, StateData) ->
|
||||
NConferences = tab_count_user(From, StateData),
|
||||
MaxConferences =
|
||||
gen_mod:get_module_opt(StateData#state.server_host,
|
||||
mod_muc, max_user_conferences,
|
||||
10),
|
||||
mod_muc, max_user_conferences),
|
||||
Collision = nick_collision(From, Nick, StateData),
|
||||
IsSubscribeRequest = not is_record(Packet, presence),
|
||||
case {(ServiceAffiliation == owner orelse
|
||||
@@ -2060,8 +2131,7 @@ filter_history(Queue, Now, Nick,
|
||||
is_room_overcrowded(StateData) ->
|
||||
MaxUsersPresence = gen_mod:get_module_opt(
|
||||
StateData#state.server_host,
|
||||
mod_muc, max_users_presence,
|
||||
?DEFAULT_MAX_USERS_PRESENCE),
|
||||
mod_muc, max_users_presence),
|
||||
(?DICT):size(StateData#state.users) > MaxUsersPresence.
|
||||
|
||||
-spec presence_broadcast_allowed(jid(), state()) -> boolean().
|
||||
@@ -2072,12 +2142,30 @@ presence_broadcast_allowed(JID, StateData) ->
|
||||
-spec send_initial_presences_and_messages(
|
||||
jid(), binary(), presence(), state(), state()) -> ok.
|
||||
send_initial_presences_and_messages(From, Nick, Presence, NewState, OldState) ->
|
||||
send_self_presence(From, NewState),
|
||||
send_existing_presences(From, NewState),
|
||||
send_initial_presence(From, NewState, OldState),
|
||||
History = get_history(Nick, Presence, NewState),
|
||||
send_history(From, History, NewState),
|
||||
send_subject(From, OldState).
|
||||
|
||||
-spec send_self_presence(jid(), state()) -> ok.
|
||||
send_self_presence(JID, State) ->
|
||||
AvatarHash = (State#state.config)#config.vcard_xupdate,
|
||||
DiscoInfo = make_disco_info(JID, State),
|
||||
DiscoHash = mod_caps:compute_disco_hash(DiscoInfo, sha),
|
||||
Els1 = [#caps{hash = <<"sha-1">>,
|
||||
node = ?EJABBERD_URI,
|
||||
version = DiscoHash}],
|
||||
Els2 = if is_binary(AvatarHash) ->
|
||||
[#vcard_xupdate{hash = AvatarHash}|Els1];
|
||||
true ->
|
||||
Els1
|
||||
end,
|
||||
ejabberd_router:route(#presence{from = State#state.jid, to = JID,
|
||||
id = randoms:get_string(),
|
||||
sub_els = Els2}).
|
||||
|
||||
-spec send_initial_presence(jid(), state(), state()) -> ok.
|
||||
send_initial_presence(NJID, StateData, OldStateData) ->
|
||||
send_new_presence1(NJID, <<"">>, true, StateData, OldStateData).
|
||||
@@ -2584,16 +2672,35 @@ search_role(Role, StateData) ->
|
||||
(?DICT):to_list(StateData#state.users)).
|
||||
|
||||
-spec search_affiliation(affiliation(), state()) ->
|
||||
[{ljid(),
|
||||
affiliation() | {affiliation(), binary()}}].
|
||||
[{ljid(),
|
||||
affiliation() | {affiliation(), binary()}}].
|
||||
search_affiliation(Affiliation,
|
||||
#state{config = #config{persistent = false}} = StateData) ->
|
||||
search_affiliation_fallback(Affiliation, StateData);
|
||||
search_affiliation(Affiliation, StateData) ->
|
||||
lists:filter(fun ({_, A}) ->
|
||||
case A of
|
||||
{A1, _Reason} -> Affiliation == A1;
|
||||
_ -> Affiliation == A
|
||||
end
|
||||
end,
|
||||
(?DICT):to_list(StateData#state.affiliations)).
|
||||
Room = StateData#state.room,
|
||||
Host = StateData#state.host,
|
||||
ServerHost = StateData#state.server_host,
|
||||
Mod = gen_mod:db_mod(ServerHost, mod_muc),
|
||||
case Mod:search_affiliation(ServerHost, Room, Host, Affiliation) of
|
||||
{ok, AffiliationList} ->
|
||||
AffiliationList;
|
||||
{error, _} ->
|
||||
search_affiliation_fallback(Affiliation, StateData)
|
||||
end.
|
||||
|
||||
-spec search_affiliation_fallback(affiliation(), state()) ->
|
||||
[{ljid(),
|
||||
affiliation() | {affiliation(), binary()}}].
|
||||
search_affiliation_fallback(Affiliation, StateData) ->
|
||||
lists:filter(
|
||||
fun({_, A}) ->
|
||||
case A of
|
||||
{A1, _Reason} -> Affiliation == A1;
|
||||
_ -> Affiliation == A
|
||||
end
|
||||
end,
|
||||
(?DICT):to_list(StateData#state.affiliations)).
|
||||
|
||||
-spec process_admin_items_set(jid(), [muc_item()], binary(),
|
||||
#state{}) -> {result, undefined, #state{}} |
|
||||
@@ -3126,12 +3233,10 @@ is_allowed_room_name_desc_limits(Options, StateData) ->
|
||||
RoomDesc = proplists:get_value(roomdesc, Options, <<"">>),
|
||||
MaxRoomName = gen_mod:get_module_opt(
|
||||
StateData#state.server_host,
|
||||
mod_muc, max_room_name,
|
||||
infinity),
|
||||
mod_muc, max_room_name),
|
||||
MaxRoomDesc = gen_mod:get_module_opt(
|
||||
StateData#state.server_host,
|
||||
mod_muc, max_room_desc,
|
||||
infinity),
|
||||
mod_muc, max_room_desc),
|
||||
(byte_size(RoomName) =< MaxRoomName)
|
||||
andalso (byte_size(RoomDesc) =< MaxRoomDesc).
|
||||
|
||||
@@ -3156,8 +3261,7 @@ is_password_settings_correct(Options, StateData) ->
|
||||
get_default_room_maxusers(RoomState) ->
|
||||
DefRoomOpts =
|
||||
gen_mod:get_module_opt(RoomState#state.server_host,
|
||||
mod_muc, default_room_options,
|
||||
[]),
|
||||
mod_muc, default_room_options),
|
||||
RoomState2 = set_opts(DefRoomOpts, RoomState),
|
||||
(RoomState2#state.config)#config.max_users.
|
||||
|
||||
@@ -3316,23 +3420,36 @@ set_config(Opts, Config, ServerHost, Lang) ->
|
||||
-spec change_config(#config{}, state()) -> {result, undefined, state()}.
|
||||
change_config(Config, StateData) ->
|
||||
send_config_change_info(Config, StateData),
|
||||
NSD = remove_subscriptions(StateData#state{config = Config}),
|
||||
case {(StateData#state.config)#config.persistent,
|
||||
Config#config.persistent}
|
||||
of
|
||||
{_, true} ->
|
||||
store_room(NSD);
|
||||
{true, false} ->
|
||||
mod_muc:forget_room(NSD#state.server_host,
|
||||
NSD#state.host, NSD#state.room);
|
||||
{false, false} -> ok
|
||||
end,
|
||||
StateData0 = StateData#state{config = Config},
|
||||
StateData1 = remove_subscriptions(StateData0),
|
||||
StateData2 =
|
||||
case {(StateData#state.config)#config.persistent,
|
||||
Config#config.persistent} of
|
||||
{WasPersistent, true} ->
|
||||
if not WasPersistent ->
|
||||
set_affiliations(StateData1#state.affiliations,
|
||||
StateData1);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
store_room(StateData1),
|
||||
StateData1;
|
||||
{true, false} ->
|
||||
Affiliations = get_affiliations(StateData),
|
||||
mod_muc:forget_room(StateData1#state.server_host,
|
||||
StateData1#state.host,
|
||||
StateData1#state.room),
|
||||
StateData1#state{affiliations = Affiliations};
|
||||
{false, false} ->
|
||||
StateData1
|
||||
end,
|
||||
case {(StateData#state.config)#config.members_only,
|
||||
Config#config.members_only}
|
||||
of
|
||||
{false, true} ->
|
||||
NSD1 = remove_nonmembers(NSD), {result, undefined, NSD1};
|
||||
_ -> {result, undefined, NSD}
|
||||
Config#config.members_only} of
|
||||
{false, true} ->
|
||||
StateData3 = remove_nonmembers(StateData2),
|
||||
{result, undefined, StateData3};
|
||||
_ ->
|
||||
{result, undefined, StateData2}
|
||||
end.
|
||||
|
||||
-spec send_config_change_info(#config{}, state()) -> ok.
|
||||
@@ -3351,12 +3468,15 @@ send_config_change_info(New, #state{config = Old} = StateData) ->
|
||||
end
|
||||
++
|
||||
case Old#config{anonymous = New#config.anonymous,
|
||||
vcard = New#config.vcard,
|
||||
logging = New#config.logging} of
|
||||
New -> [];
|
||||
_ -> [104]
|
||||
end,
|
||||
if Codes /= [] ->
|
||||
lists:foreach(
|
||||
fun({_LJID, #user{jid = JID}}) ->
|
||||
send_self_presence(JID, StateData#state{config = New})
|
||||
end, ?DICT:to_list(StateData#state.users)),
|
||||
Message = #message{type = groupchat,
|
||||
id = randoms:get_string(),
|
||||
sub_els = [#muc_user{status_codes = Codes}]},
|
||||
@@ -3384,7 +3504,8 @@ remove_nonmembers(StateData) ->
|
||||
StateData, (?DICT):to_list(get_users_and_subscribers(StateData))).
|
||||
|
||||
-spec set_opts([{atom(), any()}], state()) -> state().
|
||||
set_opts([], StateData) -> StateData;
|
||||
set_opts([], StateData) ->
|
||||
set_vcard_xupdate(StateData);
|
||||
set_opts([{Opt, Val} | Opts], StateData) ->
|
||||
NSD = case Opt of
|
||||
title ->
|
||||
@@ -3499,6 +3620,10 @@ set_opts([{Opt, Val} | Opts], StateData) ->
|
||||
StateData#state{config =
|
||||
(StateData#state.config)#config{vcard =
|
||||
Val}};
|
||||
vcard_xupdate ->
|
||||
StateData#state{config =
|
||||
(StateData#state.config)#config{vcard_xupdate =
|
||||
Val}};
|
||||
pubsub ->
|
||||
StateData#state{config =
|
||||
(StateData#state.config)#config{pubsub = Val}};
|
||||
@@ -3533,6 +3658,20 @@ set_opts([{Opt, Val} | Opts], StateData) ->
|
||||
end,
|
||||
set_opts(Opts, NSD).
|
||||
|
||||
set_vcard_xupdate(#state{config =
|
||||
#config{vcard = VCardRaw,
|
||||
vcard_xupdate = undefined} = Config} = State)
|
||||
when VCardRaw /= <<"">> ->
|
||||
case fxml_stream:parse_element(VCardRaw) of
|
||||
{error, _} ->
|
||||
State;
|
||||
El ->
|
||||
Hash = mod_vcard_xupdate:compute_hash(El),
|
||||
State#state{config = Config#config{vcard_xupdate = Hash}}
|
||||
end;
|
||||
set_vcard_xupdate(State) ->
|
||||
State.
|
||||
|
||||
-define(MAKE_CONFIG_OPT(Opt),
|
||||
{get_config_opt_name(Opt), element(Opt, Config)}).
|
||||
|
||||
@@ -3568,6 +3707,7 @@ make_opts(StateData) ->
|
||||
?MAKE_CONFIG_OPT(#config.presence_broadcast),
|
||||
?MAKE_CONFIG_OPT(#config.voice_request_min_interval),
|
||||
?MAKE_CONFIG_OPT(#config.vcard),
|
||||
?MAKE_CONFIG_OPT(#config.vcard_xupdate),
|
||||
?MAKE_CONFIG_OPT(#config.pubsub),
|
||||
{captcha_whitelist,
|
||||
(?SETS):to_list((StateData#state.config)#config.captcha_whitelist)},
|
||||
@@ -3577,6 +3717,35 @@ make_opts(StateData) ->
|
||||
{subject_author, StateData#state.subject_author},
|
||||
{subscribers, Subscribers}].
|
||||
|
||||
expand_opts(CompactOpts) ->
|
||||
DefConfig = #config{},
|
||||
Fields = record_info(fields, config),
|
||||
{_, Opts1} =
|
||||
lists:foldl(
|
||||
fun(Field, {Pos, Opts}) ->
|
||||
case lists:keyfind(Field, 1, CompactOpts) of
|
||||
false ->
|
||||
DefV = element(Pos, DefConfig),
|
||||
DefVal = case (?SETS):is_set(DefV) of
|
||||
true -> (?SETS):to_list(DefV);
|
||||
false -> DefV
|
||||
end,
|
||||
{Pos+1, [{Field, DefVal}|Opts]};
|
||||
{_, Val} ->
|
||||
{Pos+1, [{Field, Val}|Opts]}
|
||||
end
|
||||
end, {2, []}, Fields),
|
||||
SubjectAuthor = proplists:get_value(subject_author, CompactOpts, <<"">>),
|
||||
Subject = proplists:get_value(subject, CompactOpts, <<"">>),
|
||||
Subscribers = proplists:get_value(subscribers, CompactOpts, []),
|
||||
[{subject, Subject},
|
||||
{subject_author, SubjectAuthor},
|
||||
{subscribers, Subscribers}
|
||||
| lists:reverse(Opts1)].
|
||||
|
||||
config_fields() ->
|
||||
[subject, subject_author, subscribers | record_info(fields, config)].
|
||||
|
||||
-spec destroy_room(muc_destroy(), state()) -> {result, undefined, stop}.
|
||||
destroy_room(DEl, StateData) ->
|
||||
Destroy = DEl#muc_destroy{xmlns = ?NS_MUC_USER},
|
||||
@@ -3611,12 +3780,8 @@ destroy_room(DEl, StateData) ->
|
||||
false -> Fiffalse
|
||||
end).
|
||||
|
||||
-spec process_iq_disco_info(jid(), iq(), state()) ->
|
||||
{result, disco_info()} | {error, stanza_error()}.
|
||||
process_iq_disco_info(_From, #iq{type = set, lang = Lang}, _StateData) ->
|
||||
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
||||
{error, xmpp:err_not_allowed(Txt, Lang)};
|
||||
process_iq_disco_info(_From, #iq{type = get, lang = Lang}, StateData) ->
|
||||
-spec make_disco_info(jid(), state()) -> disco_info().
|
||||
make_disco_info(_From, StateData) ->
|
||||
Config = StateData#state.config,
|
||||
Feats = [?NS_VCARD, ?NS_MUC,
|
||||
?CONFIG_OPT_TO_FEATURE((Config#config.public),
|
||||
@@ -3642,16 +3807,40 @@ process_iq_disco_info(_From, #iq{type = get, lang = Lang}, StateData) ->
|
||||
_ ->
|
||||
[]
|
||||
end,
|
||||
{result, #disco_info{xdata = [iq_disco_info_extras(Lang, StateData)],
|
||||
identities = [#identity{category = <<"conference">>,
|
||||
type = <<"text">>,
|
||||
name = get_title(StateData)}],
|
||||
features = Feats}}.
|
||||
#disco_info{identities = [#identity{category = <<"conference">>,
|
||||
type = <<"text">>,
|
||||
name = get_title(StateData)}],
|
||||
features = Feats}.
|
||||
|
||||
-spec process_iq_disco_info(jid(), iq(), state()) ->
|
||||
{result, disco_info()} | {error, stanza_error()}.
|
||||
process_iq_disco_info(_From, #iq{type = set, lang = Lang}, _StateData) ->
|
||||
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
||||
{error, xmpp:err_not_allowed(Txt, Lang)};
|
||||
process_iq_disco_info(From, #iq{type = get, lang = Lang,
|
||||
sub_els = [#disco_info{node = <<>>}]},
|
||||
StateData) ->
|
||||
DiscoInfo = make_disco_info(From, StateData),
|
||||
Extras = iq_disco_info_extras(Lang, StateData),
|
||||
{result, DiscoInfo#disco_info{xdata = [Extras]}};
|
||||
process_iq_disco_info(From, #iq{type = get, lang = Lang,
|
||||
sub_els = [#disco_info{node = Node}]},
|
||||
StateData) ->
|
||||
try
|
||||
true = mod_caps:is_valid_node(Node),
|
||||
DiscoInfo = make_disco_info(From, StateData),
|
||||
Hash = mod_caps:compute_disco_hash(DiscoInfo, sha),
|
||||
Node = <<(?EJABBERD_URI)/binary, $#, Hash/binary>>,
|
||||
{result, DiscoInfo#disco_info{node = Node}}
|
||||
catch _:{badmatch, _} ->
|
||||
Txt = <<"Invalid node name">>,
|
||||
{error, xmpp:err_item_not_found(Txt, Lang)}
|
||||
end.
|
||||
|
||||
-spec iq_disco_info_extras(binary(), state()) -> xdata().
|
||||
iq_disco_info_extras(Lang, StateData) ->
|
||||
Fs1 = [{description, (StateData#state.config)#config.description},
|
||||
{occupants, ?DICT:size(StateData#state.users)},
|
||||
{occupants, ?DICT:size(StateData#state.nicks)},
|
||||
{contactjid, get_owners(StateData)}],
|
||||
Fs2 = case (StateData#state.config)#config.pubsub of
|
||||
Node when is_binary(Node), Node /= <<"">> ->
|
||||
@@ -3712,13 +3901,15 @@ process_iq_vcard(_From, #iq{type = get}, StateData) ->
|
||||
{error, _} ->
|
||||
{error, xmpp:err_item_not_found()}
|
||||
end;
|
||||
process_iq_vcard(From, #iq{type = set, lang = Lang, sub_els = [SubEl]},
|
||||
process_iq_vcard(From, #iq{type = set, lang = Lang, sub_els = [Pkt]},
|
||||
StateData) ->
|
||||
case get_affiliation(From, StateData) of
|
||||
owner ->
|
||||
VCardRaw = fxml:element_to_binary(xmpp:encode(SubEl)),
|
||||
SubEl = xmpp:encode(Pkt),
|
||||
VCardRaw = fxml:element_to_binary(SubEl),
|
||||
Hash = mod_vcard_xupdate:compute_hash(SubEl),
|
||||
Config = StateData#state.config,
|
||||
NewConfig = Config#config{vcard = VCardRaw},
|
||||
NewConfig = Config#config{vcard = VCardRaw, vcard_xupdate = Hash},
|
||||
change_config(NewConfig, StateData);
|
||||
_ ->
|
||||
ErrText = <<"Owner privileges required">>,
|
||||
@@ -3884,20 +4075,18 @@ get_roomdesc_tail(StateData, Lang) ->
|
||||
true -> <<"">>;
|
||||
_ -> translate:translate(Lang, <<"private, ">>)
|
||||
end,
|
||||
Len = (?DICT):size(StateData#state.users),
|
||||
Len = (?DICT):size(StateData#state.nicks),
|
||||
<<" (", Desc/binary, (integer_to_binary(Len))/binary, ")">>.
|
||||
|
||||
-spec get_mucroom_disco_items(state()) -> disco_items().
|
||||
get_mucroom_disco_items(StateData) ->
|
||||
Items = lists:map(
|
||||
fun({_LJID, Info}) ->
|
||||
Nick = Info#user.nick,
|
||||
#disco_item{jid = jid:make(StateData#state.room,
|
||||
StateData#state.host,
|
||||
Nick),
|
||||
name = Nick}
|
||||
end,
|
||||
(?DICT):to_list(StateData#state.users)),
|
||||
Items = ?DICT:fold(
|
||||
fun(Nick, _, Acc) ->
|
||||
[#disco_item{jid = jid:make(StateData#state.room,
|
||||
StateData#state.host,
|
||||
Nick),
|
||||
name = Nick}|Acc]
|
||||
end, [], StateData#state.nicks),
|
||||
#disco_items{items = Items}.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
@@ -4111,7 +4300,7 @@ send_subscriptions_change_notifications(From, Nick, Type, State) ->
|
||||
node = ?NS_MUCSUB_NODES_SUBSCRIBERS,
|
||||
items = [#ps_item{
|
||||
id = randoms:get_string(),
|
||||
xml_els = [xmpp:encode(Payload)]}]}}]},
|
||||
sub_els = [Payload]}]}}]},
|
||||
ejabberd_router:route(xmpp:set_from_to(Packet, From, JID));
|
||||
false ->
|
||||
ok
|
||||
@@ -4142,19 +4331,41 @@ send_wrapped(From, To, Packet, Node, State) ->
|
||||
ok
|
||||
end;
|
||||
true ->
|
||||
case Packet of
|
||||
#presence{type = unavailable} ->
|
||||
case xmpp:get_subtag(Packet, #muc_user{}) of
|
||||
#muc_user{destroy = Destroy,
|
||||
status_codes = Codes} ->
|
||||
case Destroy /= undefined orelse
|
||||
(lists:member(110,Codes) andalso
|
||||
not lists:member(303, Codes)) of
|
||||
true ->
|
||||
ejabberd_router:route(
|
||||
#presence{from = State#state.jid, to = To,
|
||||
id = randoms:get_string(),
|
||||
type = unavailable});
|
||||
false ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
ejabberd_router:route(xmpp:set_from_to(Packet, From, To))
|
||||
end.
|
||||
|
||||
-spec wrap(jid(), jid(), stanza(), binary()) -> message().
|
||||
wrap(From, To, Packet, Node) ->
|
||||
El = xmpp:encode(xmpp:set_from_to(Packet, From, To)),
|
||||
El = xmpp:set_from_to(Packet, From, To),
|
||||
#message{
|
||||
sub_els = [#ps_event{
|
||||
items = #ps_items{
|
||||
node = Node,
|
||||
items = [#ps_item{
|
||||
id = randoms:get_string(),
|
||||
xml_els = [El]}]}}]}.
|
||||
sub_els = [El]}]}}]}.
|
||||
|
||||
%% -spec send_multiple(jid(), binary(), [#user{}], stanza()) -> ok.
|
||||
%% send_multiple(From, Server, Users, Packet) ->
|
||||
|
||||
+249
-276
@@ -40,59 +40,43 @@
|
||||
-export([init/1, handle_info/2, handle_call/3,
|
||||
handle_cast/2, terminate/2, code_change/3]).
|
||||
|
||||
-export([purge_loop/1, mod_opt_type/1, depends/2]).
|
||||
-export([purge_loop/1, mod_opt_type/1, mod_options/1, depends/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("translate.hrl").
|
||||
-include("xmpp.hrl").
|
||||
|
||||
-record(state,
|
||||
{lserver, lservice, access, service_limits}).
|
||||
-record(multicastc, {rserver :: binary(),
|
||||
response,
|
||||
ts :: integer()}).
|
||||
|
||||
-record(dest, {jid_string :: binary() | none,
|
||||
jid_jid :: xmpp:jid(),
|
||||
type :: to | cc | bcc,
|
||||
address :: address()}).
|
||||
|
||||
-type limit_value() :: {default | custom, integer()}.
|
||||
-record(limits, {message :: limit_value(),
|
||||
presence :: limit_value()}).
|
||||
|
||||
-record(service_limits, {local :: #limits{},
|
||||
remote :: #limits{}}).
|
||||
|
||||
-type routing() :: route_single | {route_multicast, binary(), #service_limits{}}.
|
||||
|
||||
-record(group, {server :: binary(),
|
||||
dests :: [#dest{}],
|
||||
multicast :: routing(),
|
||||
others :: [#address{}],
|
||||
addresses :: [#address{}]}).
|
||||
|
||||
-record(state, {lserver :: binary(),
|
||||
lservice :: binary(),
|
||||
access :: atom(),
|
||||
service_limits :: #service_limits{}}).
|
||||
-type state() :: #state{}.
|
||||
|
||||
-record(multicastc, {rserver, response, ts}).
|
||||
|
||||
%% ts: timestamp (in seconds) when the cache item was last updated
|
||||
|
||||
-record(dest, {jid_string = none :: binary(),
|
||||
jid_jid :: jid(),
|
||||
type :: atom(),
|
||||
full_xml :: address()}).
|
||||
|
||||
%% jid_string = string()
|
||||
%% jid_jid = jid()
|
||||
%% full_xml = xml()
|
||||
|
||||
-record(group,
|
||||
{server, dests, multicast, others, addresses}).
|
||||
|
||||
%% server = string()
|
||||
%% dests = [string()]
|
||||
%% multicast = {cached, local_server} | {cached, string()} | {cached, not_supported} | {obsolete, not_supported} | {obsolete, string()} | not_cached
|
||||
%% after being updated, possible values are: local | multicast_not_supported | {multicast_supported, string(), limits()}
|
||||
%% others = [xml()]
|
||||
%% packet = xml()
|
||||
|
||||
-record(waiter,
|
||||
{awaiting, group, renewal = false, sender, packet,
|
||||
aattrs, addresses}).
|
||||
|
||||
%% awaiting = {[Remote_service], Local_service, Type_awaiting}
|
||||
%% Remote_service = Local_service = string()
|
||||
%% Type_awaiting = info | items
|
||||
%% group = #group
|
||||
%% renewal = true | false
|
||||
%% sender = From
|
||||
%% packet = xml()
|
||||
%% aattrs = [xml()]
|
||||
|
||||
-record(limits, {message, presence}).
|
||||
|
||||
%% message = presence = integer() | infinite
|
||||
|
||||
-record(service_limits, {local, remote}).
|
||||
|
||||
%% All the elements are of type value()
|
||||
|
||||
-define(VERSION_MULTICAST, <<"$Revision: 440 $ ">>).
|
||||
@@ -104,6 +88,8 @@
|
||||
|
||||
-define(MAXTIME_CACHE_NEGATIVE, 86400).
|
||||
|
||||
-define(MAXTIME_CACHE_NEGOTIATING, 600).
|
||||
|
||||
-define(CACHE_PURGE_TIMER, 86400000).
|
||||
|
||||
-define(DISCO_QUERY_TIMEOUT, 10000).
|
||||
@@ -130,15 +116,14 @@ reload(LServerS, NewOpts, OldOpts) ->
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
-spec init(list()) -> {ok, state()}.
|
||||
init([LServerS, Opts]) ->
|
||||
process_flag(trap_exit, true),
|
||||
LServiceS = gen_mod:get_opt_host(LServerS, Opts,
|
||||
<<"multicast.@HOST@">>),
|
||||
Access = gen_mod:get_opt(access, Opts, all),
|
||||
SLimits = build_service_limit_record(gen_mod:get_opt(limits, Opts, [])),
|
||||
[LServiceS|_] = gen_mod:get_opt_hosts(LServerS, Opts),
|
||||
Access = gen_mod:get_opt(access, Opts),
|
||||
SLimits = build_service_limit_record(gen_mod:get_opt(limits, Opts)),
|
||||
create_cache(),
|
||||
try_start_loop(),
|
||||
create_pool(),
|
||||
ejabberd_router_multicast:register_route(LServerS),
|
||||
ejabberd_router:register_route(LServiceS, LServerS),
|
||||
{ok,
|
||||
@@ -150,10 +135,9 @@ handle_call(stop, _From, State) ->
|
||||
|
||||
handle_cast({reload, NewOpts, NewOpts},
|
||||
#state{lserver = LServerS, lservice = OldLServiceS} = State) ->
|
||||
Access = gen_mod:get_opt(access, NewOpts, all),
|
||||
SLimits = build_service_limit_record(gen_mod:get_opt(limits, NewOpts, [])),
|
||||
NewLServiceS = gen_mod:get_opt_host(LServerS, NewOpts,
|
||||
<<"multicast.@HOST@">>),
|
||||
Access = gen_mod:get_opt(access, NewOpts),
|
||||
SLimits = build_service_limit_record(gen_mod:get_opt(limits, NewOpts)),
|
||||
[NewLServiceS|_] = gen_mod:get_opt_hosts(LServerS, NewOpts),
|
||||
if NewLServiceS /= OldLServiceS ->
|
||||
ejabberd_router:register_route(NewLServiceS, LServerS),
|
||||
ejabberd_router:unregister_route(OldLServiceS);
|
||||
@@ -261,8 +245,7 @@ process_iq(_, _) ->
|
||||
-define(FEATURE(Feat), Feat).
|
||||
|
||||
iq_disco_info(From, Lang, State) ->
|
||||
Name = gen_mod:get_module_opt(State#state.lserver, ?MODULE,
|
||||
name, ?T("Multicast")),
|
||||
Name = gen_mod:get_module_opt(State#state.lserver, ?MODULE, name),
|
||||
#disco_info{
|
||||
identities = [#identity{category = <<"service">>,
|
||||
type = <<"multicast">>,
|
||||
@@ -280,21 +263,22 @@ iq_vcard(Lang) ->
|
||||
%%% Route
|
||||
%%%-------------------------
|
||||
|
||||
-spec route_trusted(binary(), binary(), jid(), [jid()], stanza()) -> 'ok'.
|
||||
route_trusted(LServiceS, LServerS, FromJID,
|
||||
Destinations, Packet) ->
|
||||
Packet_stripped = Packet,
|
||||
AAttrs = [],
|
||||
Delivereds = [],
|
||||
Dests2 = lists:map(
|
||||
fun(D) ->
|
||||
#dest{jid_string = jid:encode(D),
|
||||
jid_jid = D, type = bcc,
|
||||
full_xml = #address{type = bcc, jid = D}}
|
||||
jid_jid = D, type = bcc,
|
||||
address = #address{type = bcc, jid = D}}
|
||||
end, Destinations),
|
||||
Groups = group_dests(Dests2),
|
||||
route_common(LServerS, LServiceS, FromJID, Groups,
|
||||
Delivereds, Packet_stripped, AAttrs).
|
||||
Delivereds, Packet_stripped).
|
||||
|
||||
-spec route_untrusted(binary(), binary(), atom(), #service_limits{}, stanza()) -> 'ok'.
|
||||
route_untrusted(LServiceS, LServerS, Access, SLimits, Packet) ->
|
||||
try route_untrusted2(LServiceS, LServerS, Access,
|
||||
SLimits, Packet)
|
||||
@@ -324,6 +308,7 @@ route_untrusted(LServiceS, LServerS, Access, SLimits, Packet) ->
|
||||
<<"Unknown problem">>)
|
||||
end.
|
||||
|
||||
-spec route_untrusted2(binary(), binary(), atom(), #service_limits{}, stanza()) -> 'ok'.
|
||||
route_untrusted2(LServiceS, LServerS, Access, SLimits, Packet) ->
|
||||
FromJID = xmpp:get_from(Packet),
|
||||
ok = check_access(LServerS, Access, FromJID),
|
||||
@@ -336,53 +321,40 @@ route_untrusted2(LServiceS, LServerS, Access, SLimits, Packet) ->
|
||||
Groups = group_dests(Dests2),
|
||||
ok = check_relay(FromJID#jid.server, LServerS, Groups),
|
||||
route_common(LServerS, LServiceS, FromJID, Groups,
|
||||
Delivereds, Packet_stripped, []).
|
||||
Delivereds, Packet_stripped).
|
||||
|
||||
-spec route_common(binary(), binary(), jid(), [#group{}],
|
||||
[address()], stanza(), list()) -> any().
|
||||
[address()], stanza()) -> 'ok'.
|
||||
route_common(LServerS, LServiceS, FromJID, Groups,
|
||||
Delivereds, Packet_stripped, AAttrs) ->
|
||||
Groups2 = look_cached_servers(LServerS, Groups),
|
||||
Delivereds, Packet_stripped) ->
|
||||
Groups2 = look_cached_servers(LServerS, LServiceS, Groups),
|
||||
Groups3 = build_others_xml(Groups2),
|
||||
Groups4 = add_addresses(Delivereds, Groups3),
|
||||
AGroups = decide_action_groups(Groups4),
|
||||
act_groups(FromJID, Packet_stripped, AAttrs, LServiceS,
|
||||
act_groups(FromJID, Packet_stripped, LServiceS,
|
||||
AGroups).
|
||||
|
||||
act_groups(FromJID, Packet_stripped, AAttrs, LServiceS,
|
||||
AGroups) ->
|
||||
[perform(FromJID, Packet_stripped, AAttrs, LServiceS,
|
||||
AGroup)
|
||||
|| AGroup <- AGroups].
|
||||
-spec act_groups(jid(), stanza(), binary(), [{routing(), #group{}}]) -> 'ok'.
|
||||
act_groups(FromJID, Packet_stripped, LServiceS, AGroups) ->
|
||||
lists:foreach(
|
||||
fun(AGroup) ->
|
||||
perform(FromJID, Packet_stripped, LServiceS,
|
||||
AGroup)
|
||||
end, AGroups).
|
||||
|
||||
perform(From, Packet, AAttrs, _,
|
||||
-spec perform(jid(), stanza(), binary(),
|
||||
{routing(), #group{}}) -> 'ok'.
|
||||
perform(From, Packet, _,
|
||||
{route_single, Group}) ->
|
||||
[route_packet(From, ToUser, Packet, AAttrs,
|
||||
Group#group.others, Group#group.addresses)
|
||||
|| ToUser <- Group#group.dests];
|
||||
perform(From, Packet, AAttrs, _,
|
||||
lists:foreach(
|
||||
fun(ToUser) ->
|
||||
route_packet(From, ToUser, Packet,
|
||||
Group#group.others, Group#group.addresses)
|
||||
end, Group#group.dests);
|
||||
perform(From, Packet, _,
|
||||
{{route_multicast, JID, RLimits}, Group}) ->
|
||||
route_packet_multicast(From, JID, Packet, AAttrs,
|
||||
Group#group.dests, Group#group.addresses, RLimits);
|
||||
perform(From, Packet, AAttrs, LServiceS,
|
||||
{{ask, Old_service, renewal}, Group}) ->
|
||||
send_query_info(Old_service, LServiceS),
|
||||
add_waiter(#waiter{awaiting =
|
||||
{[Old_service], LServiceS, info},
|
||||
group = Group, renewal = true, sender = From,
|
||||
packet = Packet, aattrs = AAttrs,
|
||||
addresses = Group#group.addresses});
|
||||
perform(_From, _Packet, _AAttrs, LServiceS,
|
||||
{{ask, LServiceS, _}, _Group}) ->
|
||||
ok;
|
||||
perform(From, Packet, AAttrs, LServiceS,
|
||||
{{ask, Server, not_renewal}, Group}) ->
|
||||
send_query_info(Server, LServiceS),
|
||||
add_waiter(#waiter{awaiting =
|
||||
{[Server], LServiceS, info},
|
||||
group = Group, renewal = false, sender = From,
|
||||
packet = Packet, aattrs = AAttrs,
|
||||
addresses = Group#group.addresses}).
|
||||
route_packet_multicast(From, JID, Packet,
|
||||
Group#group.dests, Group#group.addresses, RLimits).
|
||||
|
||||
%%%-------------------------
|
||||
%%% Check access permission
|
||||
@@ -430,7 +402,7 @@ split_addresses_todeliver(Addresses) ->
|
||||
%%% Check does not exceed limit of destinations
|
||||
%%%-------------------------
|
||||
|
||||
-spec check_limit_dests(_, jid(), stanza(), [address()]) -> ok.
|
||||
-spec check_limit_dests(#service_limits{}, jid(), stanza(), [address()]) -> ok.
|
||||
check_limit_dests(SLimits, FromJID, Packet,
|
||||
Addresses) ->
|
||||
SenderT = sender_type(FromJID),
|
||||
@@ -451,10 +423,10 @@ check_limit_dests(SLimits, FromJID, Packet,
|
||||
convert_dest_record(Addrs) ->
|
||||
lists:map(
|
||||
fun(#address{jid = undefined} = Addr) ->
|
||||
#dest{jid_string = none, full_xml = Addr};
|
||||
#dest{jid_string = none, address = Addr};
|
||||
(#address{jid = JID, type = Type} = Addr) ->
|
||||
#dest{jid_string = jid:encode(JID), jid_jid = JID,
|
||||
type = Type, full_xml = Addr}
|
||||
type = Type, address = Addr}
|
||||
end, Addrs).
|
||||
|
||||
%%%-------------------------
|
||||
@@ -472,9 +444,9 @@ split_dests_jid(Dests) ->
|
||||
end,
|
||||
Dests).
|
||||
|
||||
-spec report_not_jid(jid(), stanza(), #dest{}) -> any().
|
||||
-spec report_not_jid(jid(), stanza(), [#dest{}]) -> any().
|
||||
report_not_jid(From, Packet, Dests) ->
|
||||
Dests2 = [fxml:element_to_binary(xmpp:encode(Dest#dest.full_xml))
|
||||
Dests2 = [fxml:element_to_binary(xmpp:encode(Dest#dest.address))
|
||||
|| Dest <- Dests],
|
||||
[route_error(xmpp:set_from_to(Packet, From, From), jid_malformed,
|
||||
<<"This service can not process the address: ",
|
||||
@@ -500,14 +472,14 @@ group_dests(Dests) ->
|
||||
%%% Look for cached responses
|
||||
%%%-------------------------
|
||||
|
||||
look_cached_servers(LServerS, Groups) ->
|
||||
[look_cached(LServerS, Group) || Group <- Groups].
|
||||
look_cached_servers(LServerS, LServiceS, Groups) ->
|
||||
[look_cached(LServerS, LServiceS, Group) || Group <- Groups].
|
||||
|
||||
look_cached(LServerS, G) ->
|
||||
look_cached(LServerS, LServiceS, G) ->
|
||||
Maxtime_positive = (?MAXTIME_CACHE_POSITIVE),
|
||||
Maxtime_negative = (?MAXTIME_CACHE_NEGATIVE),
|
||||
Cached_response = search_server_on_cache(G#group.server,
|
||||
LServerS,
|
||||
LServerS, LServiceS,
|
||||
{Maxtime_positive,
|
||||
Maxtime_negative}),
|
||||
G#group{multicast = Cached_response}.
|
||||
@@ -523,7 +495,7 @@ build_others_xml(Groups) ->
|
||||
|
||||
build_other_xml(Dests) ->
|
||||
lists:foldl(fun (Dest, R) ->
|
||||
XML = Dest#dest.full_xml,
|
||||
XML = Dest#dest.address,
|
||||
case Dest#dest.type of
|
||||
to -> [add_delivered(XML) | R];
|
||||
cc -> [add_delivered(XML) | R];
|
||||
@@ -557,53 +529,38 @@ add_addresses2(Delivereds, [Group | Groups], Res, Pa,
|
||||
%%% Decide action groups
|
||||
%%%-------------------------
|
||||
|
||||
-spec decide_action_groups([#group{}]) -> [{routing(), #group{}}].
|
||||
decide_action_groups(Groups) ->
|
||||
[{decide_action_group(Group), Group}
|
||||
[{Group#group.multicast, Group}
|
||||
|| Group <- Groups].
|
||||
|
||||
decide_action_group(Group) ->
|
||||
Server = Group#group.server,
|
||||
case Group#group.multicast of
|
||||
{cached, local_server} ->
|
||||
%% Send a copy of the packet to each local user on Dests
|
||||
route_single;
|
||||
{cached, not_supported} ->
|
||||
%% Send a copy of the packet to each remote user on Dests
|
||||
route_single;
|
||||
{cached, {multicast_supported, JID, RLimits}} ->
|
||||
{route_multicast, JID, RLimits};
|
||||
{obsolete,
|
||||
{multicast_supported, Old_service, _RLimits}} ->
|
||||
{ask, Old_service, renewal};
|
||||
{obsolete, not_supported} -> {ask, Server, not_renewal};
|
||||
not_cached -> {ask, Server, not_renewal}
|
||||
end.
|
||||
|
||||
%%%-------------------------
|
||||
%%% Route packet
|
||||
%%%-------------------------
|
||||
|
||||
route_packet(From, ToDest, Packet, AAttrs, Others, Addresses) ->
|
||||
-spec route_packet(jid(), #dest{}, xmpp:stanza(), [addresses()], [addresses()]) -> 'ok'.
|
||||
route_packet(From, ToDest, Packet, Others, Addresses) ->
|
||||
Dests = case ToDest#dest.type of
|
||||
bcc -> [];
|
||||
_ -> [ToDest]
|
||||
end,
|
||||
route_packet2(From, ToDest#dest.jid_string, Dests,
|
||||
Packet, AAttrs, {Others, Addresses}).
|
||||
Packet, {Others, Addresses}).
|
||||
|
||||
route_packet_multicast(From, ToS, Packet, AAttrs, Dests,
|
||||
-spec route_packet_multicast(jid(), binary(), xmpp:stanza(), [#dest{}], [address()], #limits{}) -> 'ok'.
|
||||
route_packet_multicast(From, ToS, Packet, Dests,
|
||||
Addresses, Limits) ->
|
||||
Type_of_stanza = type_of_stanza(Packet),
|
||||
{_Type, Limit_number} = get_limit_number(Type_of_stanza,
|
||||
Limits),
|
||||
Fragmented_dests = fragment_dests(Dests, Limit_number),
|
||||
[route_packet2(From, ToS, DFragment, Packet, AAttrs,
|
||||
Addresses)
|
||||
|| DFragment <- Fragmented_dests].
|
||||
lists:foreach(fun(DFragment) ->
|
||||
route_packet2(From, ToS, DFragment, Packet,
|
||||
Addresses)
|
||||
end, Fragmented_dests).
|
||||
|
||||
-spec route_packet2(jid(), binary(), [#dest{}], stanza(), list(), [address()]) -> ok.
|
||||
route_packet2(From, ToS, Dests, Packet, _AAttrs,
|
||||
Addresses) ->
|
||||
-spec route_packet2(jid(), binary(), [#dest{}], xmpp:stanza(), {[address()], [address()]} | [address()]) -> 'ok'.
|
||||
route_packet2(From, ToS, Dests, Packet, Addresses) ->
|
||||
Els = case append_dests(Dests, Addresses) of
|
||||
[] ->
|
||||
xmpp:get_els(Packet);
|
||||
@@ -616,10 +573,10 @@ route_packet2(From, ToS, Dests, Packet, _AAttrs,
|
||||
|
||||
-spec append_dests([#dest{}], {[address()], [address()]} | [address()]) -> [address()].
|
||||
append_dests(_Dests, {Others, Addresses}) ->
|
||||
Addresses++Others;
|
||||
Addresses ++ Others;
|
||||
append_dests([], Addresses) -> Addresses;
|
||||
append_dests([Dest | Dests], Addresses) ->
|
||||
append_dests(Dests, [Dest#dest.full_xml | Addresses]).
|
||||
append_dests(Dests, [Dest#dest.address | Addresses]).
|
||||
|
||||
%%%-------------------------
|
||||
%%% Check relay
|
||||
@@ -650,20 +607,22 @@ check_relay_required(LServerS, Groups) ->
|
||||
%%% Check protocol support: Send request
|
||||
%%%-------------------------
|
||||
|
||||
send_query_info(RServerS, LServiceS) ->
|
||||
-spec send_query_info(binary(), binary(), binary()) -> ok.
|
||||
send_query_info(RServerS, LServiceS, ID) ->
|
||||
case str:str(RServerS, <<"echo.">>) of
|
||||
1 -> false;
|
||||
_ -> send_query(RServerS, LServiceS, #disco_info{})
|
||||
1 -> ok;
|
||||
_ -> send_query(RServerS, LServiceS, ID, #disco_info{})
|
||||
end.
|
||||
|
||||
send_query_items(RServerS, LServiceS) ->
|
||||
send_query(RServerS, LServiceS, #disco_items{}).
|
||||
-spec send_query_items(binary(), binary(), binary()) -> ok.
|
||||
send_query_items(RServerS, LServiceS, ID) ->
|
||||
send_query(RServerS, LServiceS, ID, #disco_items{}).
|
||||
|
||||
-spec send_query(binary(), binary(), [disco_info()|disco_items()]) -> ok.
|
||||
send_query(RServerS, LServiceS, SubEl) ->
|
||||
-spec send_query(binary(), binary(), binary(), disco_info()|disco_items()) -> ok.
|
||||
send_query(RServerS, LServiceS, ID, SubEl) ->
|
||||
Packet = #iq{from = stj(LServiceS),
|
||||
to = stj(RServerS),
|
||||
id = randoms:get_string(),
|
||||
id = ID,
|
||||
type = get, sub_els = [SubEl]},
|
||||
ejabberd_router:route(Packet).
|
||||
|
||||
@@ -673,10 +632,31 @@ send_query(RServerS, LServiceS, SubEl) ->
|
||||
|
||||
process_iqreply_error(LServiceS, Packet) ->
|
||||
FromS = jts(xmpp:get_from(Packet)),
|
||||
case search_waiter(FromS, LServiceS, info) of
|
||||
{found_waiter, Waiter} ->
|
||||
received_awaiter(FromS, Waiter, LServiceS);
|
||||
_ -> ok
|
||||
ID = Packet#iq.id,
|
||||
case str:tokens(ID, <<"/">>) of
|
||||
[RServer, _] ->
|
||||
case look_server(RServer) of
|
||||
{cached, {_Response, {wait_for_info, ID}}, _TS}
|
||||
when RServer == FromS ->
|
||||
add_response(RServer, not_supported, cached);
|
||||
{cached, {_Response, {wait_for_items, ID}}, _TS}
|
||||
when RServer == FromS ->
|
||||
add_response(RServer, not_supported, cached);
|
||||
{cached, {Response, {wait_for_items_info, ID, Items}},
|
||||
_TS} ->
|
||||
case lists:member(FromS, Items) of
|
||||
true ->
|
||||
received_awaiter(
|
||||
FromS, RServer, Response, ID, Items,
|
||||
LServiceS);
|
||||
false ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%%%-------------------------
|
||||
@@ -684,12 +664,12 @@ process_iqreply_error(LServiceS, Packet) ->
|
||||
%%%-------------------------
|
||||
|
||||
-spec process_iqreply_result(binary(), iq()) -> any().
|
||||
process_iqreply_result(LServiceS, #iq{from = From, sub_els = [SubEl]}) ->
|
||||
process_iqreply_result(LServiceS, #iq{from = From, id = ID, sub_els = [SubEl]}) ->
|
||||
case SubEl of
|
||||
#disco_info{} ->
|
||||
process_discoinfo_result(From, LServiceS, SubEl);
|
||||
process_discoinfo_result(From, LServiceS, ID, SubEl);
|
||||
#disco_items{} ->
|
||||
process_discoitems_result(From, LServiceS, SubEl);
|
||||
process_discoitems_result(From, LServiceS, ID, SubEl);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
@@ -698,46 +678,53 @@ process_iqreply_result(LServiceS, #iq{from = From, sub_els = [SubEl]}) ->
|
||||
%%% Check protocol support: Receive response: Disco Info
|
||||
%%%-------------------------
|
||||
|
||||
process_discoinfo_result(From, LServiceS, DiscoInfo) ->
|
||||
process_discoinfo_result(From, LServiceS, ID, DiscoInfo) ->
|
||||
FromS = jts(From),
|
||||
case search_waiter(FromS, LServiceS, info) of
|
||||
{found_waiter, Waiter} ->
|
||||
process_discoinfo_result2(From, FromS, LServiceS, DiscoInfo,
|
||||
Waiter);
|
||||
_ -> ok
|
||||
case str:tokens(ID, <<"/">>) of
|
||||
[RServer, _] ->
|
||||
case look_server(RServer) of
|
||||
{cached, {Response, {wait_for_info, ID} = ST}, _TS}
|
||||
when RServer == FromS ->
|
||||
process_discoinfo_result2(
|
||||
From, FromS, LServiceS, DiscoInfo,
|
||||
RServer, Response, ST);
|
||||
{cached, {Response, {wait_for_items_info, ID, Items} = ST},
|
||||
_TS} ->
|
||||
case lists:member(FromS, Items) of
|
||||
true ->
|
||||
process_discoinfo_result2(
|
||||
From, FromS, LServiceS, DiscoInfo,
|
||||
RServer, Response, ST);
|
||||
false ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
process_discoinfo_result2(From, FromS, LServiceS,
|
||||
#disco_info{features = Feats} = DiscoInfo,
|
||||
Waiter) ->
|
||||
RServer, Response, ST) ->
|
||||
Multicast_support = lists:member(?NS_ADDRESS, Feats),
|
||||
Group = Waiter#waiter.group,
|
||||
RServer = Group#group.server,
|
||||
case Multicast_support of
|
||||
true ->
|
||||
SenderT = sender_type(From),
|
||||
RLimits = get_limits_xml(DiscoInfo, SenderT),
|
||||
add_response(RServer, {multicast_supported, FromS, RLimits}),
|
||||
FromM = Waiter#waiter.sender,
|
||||
DestsM = Group#group.dests,
|
||||
PacketM = Waiter#waiter.packet,
|
||||
AAttrsM = Waiter#waiter.aattrs,
|
||||
AddressesM = Waiter#waiter.addresses,
|
||||
RServiceM = FromS,
|
||||
route_packet_multicast(FromM, RServiceM, PacketM,
|
||||
AAttrsM, DestsM, AddressesM, RLimits),
|
||||
delo_waiter(Waiter);
|
||||
add_response(RServer, {multicast_supported, FromS, RLimits}, cached);
|
||||
false ->
|
||||
case FromS of
|
||||
RServer ->
|
||||
send_query_items(FromS, LServiceS),
|
||||
delo_waiter(Waiter),
|
||||
add_waiter(Waiter#waiter{awaiting =
|
||||
{[FromS], LServiceS, items},
|
||||
renewal = false});
|
||||
%% We asked a component, and it does not support XEP33
|
||||
_ -> received_awaiter(FromS, Waiter, LServiceS)
|
||||
end
|
||||
case ST of
|
||||
{wait_for_info, _ID} ->
|
||||
Random = randoms:get_string(),
|
||||
ID = <<RServer/binary, $/, Random/binary>>,
|
||||
send_query_items(FromS, LServiceS, ID),
|
||||
add_response(RServer, Response, {wait_for_items, ID});
|
||||
%% We asked a component, and it does not support XEP33
|
||||
{wait_for_items_info, ID, Items} ->
|
||||
received_awaiter(FromS, RServer, Response, ID, Items, LServiceS)
|
||||
end
|
||||
end.
|
||||
|
||||
get_limits_xml(DiscoInfo, SenderT) ->
|
||||
@@ -781,26 +768,32 @@ get_limits_values(Fields) ->
|
||||
%%% Check protocol support: Receive response: Disco Items
|
||||
%%%-------------------------
|
||||
|
||||
process_discoitems_result(From, LServiceS, #disco_items{items = Items}) ->
|
||||
process_discoitems_result(From, LServiceS, ID, #disco_items{items = Items}) ->
|
||||
FromS = jts(From),
|
||||
case search_waiter(FromS, LServiceS, items) of
|
||||
{found_waiter, Waiter} ->
|
||||
List = lists:flatmap(
|
||||
fun(#disco_item{jid = #jid{luser = <<"">>,
|
||||
lresource = <<"">>} = J}) ->
|
||||
[J];
|
||||
(_) ->
|
||||
[]
|
||||
end, Items),
|
||||
case List of
|
||||
[] ->
|
||||
received_awaiter(FromS, Waiter, LServiceS);
|
||||
case str:tokens(ID, <<"/">>) of
|
||||
[FromS = RServer, _] ->
|
||||
case look_server(RServer) of
|
||||
{cached, {Response, {wait_for_items, ID}}, _TS} ->
|
||||
List = lists:flatmap(
|
||||
fun(#disco_item{jid = #jid{luser = <<"">>,
|
||||
lserver = LServer,
|
||||
lresource = <<"">>}}) ->
|
||||
[LServer];
|
||||
(_) ->
|
||||
[]
|
||||
end, Items),
|
||||
case List of
|
||||
[] ->
|
||||
add_response(RServer, not_supported, cached);
|
||||
_ ->
|
||||
Random = randoms:get_string(),
|
||||
ID2 = <<RServer/binary, $/, Random/binary>>,
|
||||
[send_query_info(Item, LServiceS, ID2) || Item <- List],
|
||||
add_response(RServer, Response,
|
||||
{wait_for_items_info, ID2, List})
|
||||
end;
|
||||
_ ->
|
||||
[send_query_info(Item, LServiceS) || Item <- List],
|
||||
delo_waiter(Waiter),
|
||||
add_waiter(Waiter#waiter{awaiting =
|
||||
{List, LServiceS, info},
|
||||
renewal = false})
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
@@ -810,33 +803,12 @@ process_discoitems_result(From, LServiceS, #disco_items{items = Items}) ->
|
||||
%%% Check protocol support: Receive response: Received awaiter
|
||||
%%%-------------------------
|
||||
|
||||
received_awaiter(JID, Waiter, LServiceS) ->
|
||||
{JIDs, LServiceS, _} = Waiter#waiter.awaiting,
|
||||
delo_waiter(Waiter),
|
||||
Group = Waiter#waiter.group,
|
||||
RServer = Group#group.server,
|
||||
received_awaiter(JID, RServer, Response, ID, JIDs, _LServiceS) ->
|
||||
case lists:delete(JID, JIDs) of
|
||||
[] ->
|
||||
case Waiter#waiter.renewal of
|
||||
false ->
|
||||
add_response(RServer, not_supported),
|
||||
From = Waiter#waiter.sender,
|
||||
Packet = Waiter#waiter.packet,
|
||||
AAttrs = Waiter#waiter.aattrs,
|
||||
Others = Group#group.others,
|
||||
Addresses = Waiter#waiter.addresses,
|
||||
[route_packet(From, ToUser, Packet, AAttrs, Others, Addresses)
|
||||
|| ToUser <- Group#group.dests];
|
||||
true ->
|
||||
send_query_info(RServer, LServiceS),
|
||||
add_waiter(Waiter#waiter{awaiting =
|
||||
{[RServer], LServiceS, info},
|
||||
renewal = false})
|
||||
end;
|
||||
JIDs2 ->
|
||||
add_waiter(Waiter#waiter{awaiting =
|
||||
{JIDs2, LServiceS, info},
|
||||
renewal = false})
|
||||
[] ->
|
||||
add_response(RServer, not_supported, cached);
|
||||
JIDs2 ->
|
||||
add_response(RServer, Response, {wait_for_items_info, ID, JIDs2})
|
||||
end.
|
||||
|
||||
%%%-------------------------
|
||||
@@ -848,25 +820,52 @@ create_cache() ->
|
||||
[{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, multicastc)}]).
|
||||
|
||||
add_response(RServer, Response) ->
|
||||
add_response(RServer, Response, State) ->
|
||||
Secs = calendar:datetime_to_gregorian_seconds(calendar:local_time()),
|
||||
mnesia:dirty_write(#multicastc{rserver = RServer,
|
||||
response = Response, ts = Secs}).
|
||||
response = {Response, State}, ts = Secs}).
|
||||
|
||||
search_server_on_cache(RServer, LServerS, _Maxmins)
|
||||
search_server_on_cache(RServer, LServerS, _LServiceS, _Maxmins)
|
||||
when RServer == LServerS ->
|
||||
{cached, local_server};
|
||||
search_server_on_cache(RServer, _LServerS, Maxmins) ->
|
||||
route_single;
|
||||
search_server_on_cache(RServer, _LServerS, LServiceS, Maxmins) ->
|
||||
case look_server(RServer) of
|
||||
not_cached -> not_cached;
|
||||
{cached, Response, Ts} ->
|
||||
Now = calendar:datetime_to_gregorian_seconds(calendar:local_time()),
|
||||
case is_obsolete(Response, Ts, Now, Maxmins) of
|
||||
false -> {cached, Response};
|
||||
true -> {obsolete, Response}
|
||||
end
|
||||
not_cached ->
|
||||
query_info(RServer, LServiceS, not_supported),
|
||||
route_single;
|
||||
{cached, {Response, State}, TS} ->
|
||||
Now = calendar:datetime_to_gregorian_seconds(calendar:local_time()),
|
||||
Response2 =
|
||||
case State of
|
||||
cached ->
|
||||
case is_obsolete(Response, TS, Now, Maxmins) of
|
||||
false -> ok;
|
||||
true ->
|
||||
query_info(RServer, LServiceS, Response)
|
||||
end,
|
||||
Response;
|
||||
_ ->
|
||||
if
|
||||
Now - TS > ?MAXTIME_CACHE_NEGOTIATING ->
|
||||
query_info(RServer, LServiceS, not_supported),
|
||||
not_supported;
|
||||
true ->
|
||||
Response
|
||||
end
|
||||
end,
|
||||
case Response2 of
|
||||
not_supported -> route_single;
|
||||
{multicast_supported, Service, Limits} ->
|
||||
{route_multicast, Service, Limits}
|
||||
end
|
||||
end.
|
||||
|
||||
query_info(RServer, LServiceS, Response) ->
|
||||
Random = randoms:get_string(),
|
||||
ID = <<RServer/binary, $/, Random/binary>>,
|
||||
send_query_info(RServer, LServiceS, ID),
|
||||
add_response(RServer, Response, {wait_for_info, ID}).
|
||||
|
||||
look_server(RServer) ->
|
||||
case mnesia:dirty_read(multicastc, RServer) of
|
||||
[] -> not_cached;
|
||||
@@ -937,44 +936,6 @@ purge_loop(NM) ->
|
||||
try_stop -> purge_loop_finished
|
||||
end.
|
||||
|
||||
%%%-------------------------
|
||||
%%% Pool
|
||||
%%%-------------------------
|
||||
|
||||
create_pool() ->
|
||||
catch
|
||||
begin
|
||||
ets:new(multicastp,
|
||||
[duplicate_bag, public, named_table, {keypos, 2}]),
|
||||
ets:give_away(multicastp, whereis(ejabberd), ok)
|
||||
end.
|
||||
|
||||
add_waiter(Waiter) ->
|
||||
true = ets:insert(multicastp, Waiter).
|
||||
|
||||
delo_waiter(Waiter) ->
|
||||
true = ets:delete_object(multicastp, Waiter).
|
||||
|
||||
-spec search_waiter(binary(), binary(), info | items) ->
|
||||
{found_waiter, #waiter{}} | waiter_not_found.
|
||||
|
||||
search_waiter(JID, LServiceS, Type) ->
|
||||
Rs = ets:foldl(fun (W, Res) ->
|
||||
{JIDs, LServiceS1, Type1} = W#waiter.awaiting,
|
||||
case lists:member(JID, JIDs) and
|
||||
(LServiceS == LServiceS1)
|
||||
and (Type1 == Type)
|
||||
of
|
||||
true -> Res ++ [W];
|
||||
false -> Res
|
||||
end
|
||||
end,
|
||||
[], multicastp),
|
||||
case Rs of
|
||||
[R | _] -> {found_waiter, R};
|
||||
[] -> waiter_not_found
|
||||
end.
|
||||
|
||||
%%%-------------------------
|
||||
%%% Limits: utils
|
||||
%%%-------------------------
|
||||
@@ -1008,11 +969,13 @@ get_from_limitopts(LimitOpts, SenderT) ->
|
||||
build_remote_limit_record(LimitOpts, SenderT) ->
|
||||
build_limit_record(LimitOpts, SenderT).
|
||||
|
||||
-spec build_limit_record(any(), local | remote) -> #limits{}.
|
||||
build_limit_record(LimitOpts, SenderT) ->
|
||||
Limits = [get_limit_value(Name, Default, LimitOpts)
|
||||
|| {Name, Default} <- list_of_limits(SenderT)],
|
||||
list_to_tuple([limits | Limits]).
|
||||
|
||||
-spec get_limit_value(atom(), integer(), any()) -> limit_value().
|
||||
get_limit_value(Name, Default, LimitOpts) ->
|
||||
case lists:keysearch(Name, 1, LimitOpts) of
|
||||
{value, {Name, Number}} -> {custom, Number};
|
||||
@@ -1021,11 +984,13 @@ get_limit_value(Name, Default, LimitOpts) ->
|
||||
|
||||
type_of_stanza(Stanza) -> element(1, Stanza).
|
||||
|
||||
-spec get_limit_number(message | presence, #limits{}) -> limit_value().
|
||||
get_limit_number(message, Limits) ->
|
||||
Limits#limits.message;
|
||||
get_limit_number(presence, Limits) ->
|
||||
Limits#limits.presence.
|
||||
|
||||
-spec get_slimit_group(local | remote, #service_limits{}) -> #limits{}.
|
||||
get_slimit_group(local, SLimits) ->
|
||||
SLimits#service_limits.local;
|
||||
get_slimit_group(remote, SLimits) ->
|
||||
@@ -1121,6 +1086,8 @@ depends(_Host, _Opts) ->
|
||||
mod_opt_type(access) ->
|
||||
fun acl:access_rules_validator/1;
|
||||
mod_opt_type(host) -> fun iolist_to_binary/1;
|
||||
mod_opt_type(hosts) ->
|
||||
fun(L) -> lists:map(fun iolist_to_binary/1, L) end;
|
||||
mod_opt_type(name) -> fun iolist_to_binary/1;
|
||||
mod_opt_type({limits, Type}) when (Type == local) or (Type == remote) ->
|
||||
fun(L) ->
|
||||
@@ -1130,5 +1097,11 @@ mod_opt_type({limits, Type}) when (Type == local) or (Type == remote) ->
|
||||
({message, I} = O) when is_integer(I) -> O;
|
||||
({presence, I} = O) when is_integer(I) -> O
|
||||
end, L)
|
||||
end;
|
||||
mod_opt_type(_) -> [access, host, {limits, local}, {limits, remote}, name].
|
||||
end.
|
||||
|
||||
mod_options(_Host) ->
|
||||
[{access, all},
|
||||
{host, <<"multicast.@HOST@">>},
|
||||
{hosts, []},
|
||||
{limits, [{local, []}, {remote, []}]},
|
||||
{name, ?T("Multicast")}].
|
||||
|
||||
+14
-19
@@ -63,7 +63,7 @@
|
||||
webadmin_user/4,
|
||||
webadmin_user_parse_query/5]).
|
||||
|
||||
-export([mod_opt_type/1, depends/2]).
|
||||
-export([mod_opt_type/1, mod_options/1, depends/2]).
|
||||
|
||||
-deprecated({get_queue_length,2}).
|
||||
|
||||
@@ -108,7 +108,6 @@ depends(_Host, _Opts) ->
|
||||
start(Host, Opts) ->
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, Opts),
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
|
||||
ejabberd_hooks:add(offline_message_hook, Host, ?MODULE,
|
||||
store_packet, 50),
|
||||
ejabberd_hooks:add(c2s_self_presence, Host, ?MODULE, c2s_self_presence, 50),
|
||||
@@ -132,7 +131,7 @@ start(Host, Opts) ->
|
||||
ejabberd_hooks:add(webadmin_user_parse_query, Host,
|
||||
?MODULE, webadmin_user_parse_query, 50),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_FLEX_OFFLINE,
|
||||
?MODULE, handle_offline_query, IQDisc).
|
||||
?MODULE, handle_offline_query).
|
||||
|
||||
stop(Host) ->
|
||||
ejabberd_hooks:delete(offline_message_hook, Host,
|
||||
@@ -162,13 +161,6 @@ reload(Host, NewOpts, OldOpts) ->
|
||||
NewMod:init(Host, NewOpts);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
|
||||
{false, IQDisc, _} ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_FLEX_OFFLINE,
|
||||
?MODULE, handle_offline_query, IQDisc);
|
||||
true ->
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec store_offline_msg(#offline_msg{}) -> ok | {error, full | any()}.
|
||||
@@ -187,8 +179,7 @@ store_offline_msg(#offline_msg{us = {User, Server}} = Msg) ->
|
||||
end.
|
||||
|
||||
get_max_user_messages(User, Server) ->
|
||||
Access = gen_mod:get_module_opt(Server, ?MODULE, access_max_user_messages,
|
||||
max_user_offline_messages),
|
||||
Access = gen_mod:get_module_opt(Server, ?MODULE, access_max_user_messages),
|
||||
case acl:match_rule(Server, Access, jid:make(User, Server)) of
|
||||
Max when is_integer(Max) -> Max;
|
||||
infinity -> infinity;
|
||||
@@ -388,8 +379,7 @@ need_to_store(LServer, #message{type = Type} = Packet) ->
|
||||
false;
|
||||
none ->
|
||||
case gen_mod:get_module_opt(
|
||||
LServer, ?MODULE, store_empty_body,
|
||||
unless_chat_state) of
|
||||
LServer, ?MODULE, store_empty_body) of
|
||||
true ->
|
||||
true;
|
||||
false ->
|
||||
@@ -598,7 +588,8 @@ get_offline_els(LUser, LServer) ->
|
||||
-spec offline_msg_to_route(binary(), #offline_msg{}) ->
|
||||
{route, message()} | error.
|
||||
offline_msg_to_route(LServer, #offline_msg{from = From, to = To} = R) ->
|
||||
try xmpp:decode(R#offline_msg.packet, ?NS_CLIENT, [ignore_els]) of
|
||||
CodecOpts = ejabberd_config:codec_options(LServer),
|
||||
try xmpp:decode(R#offline_msg.packet, ?NS_CLIENT, CodecOpts) of
|
||||
Pkt ->
|
||||
Pkt1 = xmpp:set_from_to(Pkt, From, To),
|
||||
Pkt2 = add_delay_info(Pkt1, LServer, R#offline_msg.timestamp),
|
||||
@@ -613,10 +604,11 @@ offline_msg_to_route(LServer, #offline_msg{from = From, to = To} = R) ->
|
||||
-spec read_messages(binary(), binary()) -> [{binary(), message()}].
|
||||
read_messages(LUser, LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
CodecOpts = ejabberd_config:codec_options(LServer),
|
||||
lists:flatmap(
|
||||
fun({Seq, From, To, TS, El}) ->
|
||||
Node = integer_to_binary(Seq),
|
||||
try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of
|
||||
try xmpp:decode(El, ?NS_CLIENT, CodecOpts) of
|
||||
Pkt ->
|
||||
Node = integer_to_binary(Seq),
|
||||
Pkt1 = add_delay_info(Pkt, LServer, TS),
|
||||
@@ -849,6 +841,9 @@ mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
||||
mod_opt_type(store_empty_body) ->
|
||||
fun (V) when is_boolean(V) -> V;
|
||||
(unless_chat_state) -> unless_chat_state
|
||||
end;
|
||||
mod_opt_type(_) ->
|
||||
[access_max_user_messages, db_type, store_empty_body].
|
||||
end.
|
||||
|
||||
mod_options(Host) ->
|
||||
[{db_type, ejabberd_config:default_db(Host, ?MODULE)},
|
||||
{access_max_user_messages, max_user_offline_messages},
|
||||
{store_empty_body, unless_chat_state}].
|
||||
|
||||
+26
-30
@@ -29,19 +29,15 @@
|
||||
|
||||
-protocol({xep, 199, '2.0'}).
|
||||
|
||||
-behavior(gen_mod).
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-behavior(gen_server).
|
||||
-behaviour(gen_server).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-include("xmpp.hrl").
|
||||
|
||||
-define(DEFAULT_SEND_PINGS, false).
|
||||
|
||||
-define(DEFAULT_PING_INTERVAL, 60).
|
||||
|
||||
%% API
|
||||
-export([start_ping/2, stop_ping/2]).
|
||||
|
||||
@@ -53,14 +49,14 @@
|
||||
handle_cast/2, handle_info/2, code_change/3]).
|
||||
|
||||
-export([iq_ping/1, user_online/3, user_offline/3,
|
||||
user_send/1, mod_opt_type/1, depends/2]).
|
||||
user_send/1, mod_opt_type/1, mod_options/1, depends/2]).
|
||||
|
||||
-record(state,
|
||||
{host = <<"">>,
|
||||
send_pings = ?DEFAULT_SEND_PINGS :: boolean(),
|
||||
ping_interval = ?DEFAULT_PING_INTERVAL :: non_neg_integer(),
|
||||
ping_ack_timeout = undefined :: non_neg_integer(),
|
||||
timeout_action = none :: none | kill,
|
||||
{host = <<"">> :: binary(),
|
||||
send_pings :: boolean(),
|
||||
ping_interval :: non_neg_integer(),
|
||||
ping_ack_timeout :: undefined | non_neg_integer(),
|
||||
timeout_action ::none | kill,
|
||||
timers = maps:new() :: map()}).
|
||||
|
||||
%%====================================================================
|
||||
@@ -95,8 +91,7 @@ reload(Host, NewOpts, OldOpts) ->
|
||||
init([Host, Opts]) ->
|
||||
process_flag(trap_exit, true),
|
||||
State = init_state(Host, Opts),
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
|
||||
register_iq_handlers(Host, IQDisc),
|
||||
register_iq_handlers(Host),
|
||||
case State#state.send_pings of
|
||||
true -> register_hooks(Host);
|
||||
false -> ok
|
||||
@@ -112,12 +107,8 @@ handle_call(stop, _From, State) ->
|
||||
handle_call(_Req, _From, State) ->
|
||||
{reply, {error, badarg}, State}.
|
||||
|
||||
handle_cast({reload, Host, NewOpts, OldOpts},
|
||||
handle_cast({reload, Host, NewOpts, _OldOpts},
|
||||
#state{timers = Timers} = OldState) ->
|
||||
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
|
||||
{false, IQDisc, _} -> register_iq_handlers(Host, IQDisc);
|
||||
true -> ok
|
||||
end,
|
||||
NewState = init_state(Host, NewOpts),
|
||||
case {NewState#state.send_pings, OldState#state.send_pings} of
|
||||
{true, false} -> register_hooks(Host);
|
||||
@@ -196,10 +187,10 @@ user_send({Packet, #{jid := JID} = C2SState}) ->
|
||||
%% Internal functions
|
||||
%%====================================================================
|
||||
init_state(Host, Opts) ->
|
||||
SendPings = gen_mod:get_opt(send_pings, Opts, ?DEFAULT_SEND_PINGS),
|
||||
PingInterval = gen_mod:get_opt(ping_interval, Opts, ?DEFAULT_PING_INTERVAL),
|
||||
SendPings = gen_mod:get_opt(send_pings, Opts),
|
||||
PingInterval = gen_mod:get_opt(ping_interval, Opts),
|
||||
PingAckTimeout = gen_mod:get_opt(ping_ack_timeout, Opts),
|
||||
TimeoutAction = gen_mod:get_opt(timeout_action, Opts, none),
|
||||
TimeoutAction = gen_mod:get_opt(timeout_action, Opts),
|
||||
#state{host = Host,
|
||||
send_pings = SendPings,
|
||||
ping_interval = PingInterval,
|
||||
@@ -223,11 +214,11 @@ unregister_hooks(Host) ->
|
||||
ejabberd_hooks:delete(user_send_packet, Host, ?MODULE,
|
||||
user_send, 100).
|
||||
|
||||
register_iq_handlers(Host, IQDisc) ->
|
||||
register_iq_handlers(Host) ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PING,
|
||||
?MODULE, iq_ping, IQDisc),
|
||||
?MODULE, iq_ping),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_PING,
|
||||
?MODULE, iq_ping, IQDisc).
|
||||
?MODULE, iq_ping).
|
||||
|
||||
unregister_iq_handlers(Host) ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_PING),
|
||||
@@ -267,16 +258,21 @@ cancel_timer(TRef) ->
|
||||
depends(_Host, _Opts) ->
|
||||
[].
|
||||
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
mod_opt_type(ping_interval) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
mod_opt_type(ping_ack_timeout) ->
|
||||
fun(I) when is_integer(I), I>0 -> timer:seconds(I) end;
|
||||
fun(undefined) -> undefined;
|
||||
(I) when is_integer(I), I>0 -> timer:seconds(I)
|
||||
end;
|
||||
mod_opt_type(send_pings) ->
|
||||
fun (B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(timeout_action) ->
|
||||
fun (none) -> none;
|
||||
(kill) -> kill
|
||||
end;
|
||||
mod_opt_type(_) ->
|
||||
[iqdisc, ping_interval, ping_ack_timeout, send_pings, timeout_action].
|
||||
end.
|
||||
|
||||
mod_options(_Host) ->
|
||||
[{ping_interval, 60},
|
||||
{ping_ack_timeout, undefined},
|
||||
{send_pings, false},
|
||||
{timeout_action, none}].
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user