diff options
43 files changed, 896 insertions, 921 deletions
diff --git a/Makefile.in b/Makefile.in index 8557e51d2..3ebef18b9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 diff --git a/configure.ac b/configure.ac index b4952a424..1fe20b6bc 100644 --- a/configure.ac +++ b/configure.ac @@ -261,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 diff --git a/ejabberd.yml.example b/ejabberd.yml.example index 49506ed9c..fe146946c 100644 --- a/ejabberd.yml.example +++ b/ejabberd.yml.example @@ -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,20 +199,6 @@ listen: web_admin: true ## register: true captcha: true - ## - ## 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 ## ## ejabberd_service: Interact with external components (transports, ...) @@ -445,6 +445,11 @@ auth_method: internal ## ## sql_keepalive_interval: undefined +## +## Use the new SQL schema +## +## new_sql_schema: true + ###. =============== ###' TRAFFIC SHAPERS @@ -737,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: {} @@ -776,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: @@ -832,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 diff --git a/include/ejabberd_http.hrl b/include/ejabberd_http.hrl index 5718ff033..3c38969ca 100644 --- a/include/ejabberd_http.hrl +++ b/include/ejabberd_http.hrl @@ -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{}. @@ -3,7 +3,7 @@ defmodule Ejabberd.Mixfile do def project do [app: :ejabberd, - version: "18.1.0", + version: "18.3.0", description: description(), elixir: "~> 1.4", elixirc_paths: ["lib"], @@ -29,7 +29,7 @@ defmodule Ejabberd.Mixfile do included_applications: [:lager, :mnesia, :inets, :p1_utils, :cache_tab, :fast_tls, :stringprep, :fast_xml, :xmpp, :stun, :fast_yaml, :esip, :jiffy, :p1_oauth2, - :eimp, :fs, :base64url, :jose] + :eimp, :base64url, :jose] ++ cond_apps()] end @@ -74,10 +74,9 @@ defmodule Ejabberd.Mixfile do {:p1_oauth2, "~> 0.6.1"}, {:distillery, "~> 1.0"}, {:ex_doc, ">= 0.0.0", only: :dev}, - {:eimp, github: "processone/eimp", ref: "c7201d18"}, + {:eimp, "~> 1.0"}, {:base64url, "~> 0.0.1"}, - {:jose, "~> 1.8"}, - {:fs, "~> 3.4"}] + {:jose, "~> 1.8"}] ++ cond_deps() end @@ -1,36 +1,35 @@ %{ "base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"}, - "cache_tab": {:hex, :cache_tab, "1.0.12", "a06a4ffbd4da8469791ba941512a6a45ed8c11865b4606a368e21b332da3638a", [:rebar3], [{:p1_utils, "1.0.10", [hex: :p1_utils, repo: "hexpm", optional: false]}], "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", "c7201d18c360b89faf2c827bf3ab2acc1c10b812", [ref: "c7201d18"]}, - "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": {:hex, :ezlib, "1.0.3", "c402c839ff5eab5b8a69efd9f60885c3ab3ca2489a6758bf8a67c914297597c5", [:rebar3], [], "hexpm"}, - "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"}, } diff --git a/rebar.config b/rebar.config index 098b19cff..823bc3824 100644 --- a/rebar.config +++ b/rebar.config @@ -20,30 +20,29 @@ {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, "dbf173f"}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "2bb82b29"}}}, - {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", "a166f0e"}}, + {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.11"}}}, + {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.29"}}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "bb88d59"}}, + {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.13"}}}, {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}}, {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.2"}}}, {jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.8.4"}}}, - {fs, ".*", {git, "https://github.com/synrc/fs", "bed9467"}}, - {eimp, ".*", {git, "https://github.com/processone/eimp", "6858909d"}}, - {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.4"}}}, + {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.21"}}}}, + {if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.22"}}}}, {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"}}}}, + {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"}}}}}, %% Elixir support, needed to run tests @@ -53,7 +52,7 @@ {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", diff --git a/src/ejabberd.erl b/src/ejabberd.erl index 356fd66c7..4740bd034 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -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. diff --git a/src/ejabberd_access_permissions.erl b/src/ejabberd_access_permissions.erl index a863e1557..3a7f81d53 100644 --- a/src/ejabberd_access_permissions.erl +++ b/src/ejabberd_access_permissions.erl @@ -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. diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl index a7e03d990..f1066815f 100644 --- a/src/ejabberd_app.erl +++ b/src/ejabberd_app.erl @@ -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}. diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index cf3d099cc..3f88df59a 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -455,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. diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index 474304a5d..dee16927e 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -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]). @@ -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; diff --git a/src/ejabberd_http_ws.erl b/src/ejabberd_http_ws.erl index 1edaeb073..0c15ab6c0 100644 --- a/src/ejabberd_http_ws.erl +++ b/src/ejabberd_http_ws.erl @@ -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, diff --git a/src/ejabberd_pkix.erl b/src/ejabberd_pkix.erl index 217c27d55..229492bea 100644 --- a/src/ejabberd_pkix.erl +++ b/src/ejabberd_pkix.erl @@ -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) -> diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl index 03b768bdf..6445406d2 100644 --- a/src/ejabberd_service.erl +++ b/src/ejabberd_service.erl @@ -191,8 +191,14 @@ handle_authenticated_packet(Pkt0, #{ip := {IP, _}, lang := Lang} = State) From = xmpp:get_from(Pkt), case check_from(From, State) of true -> - ejabberd_router:route(Pkt), - State; + {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), diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 1db61b70d..675941aea 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -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]. diff --git a/src/ejabberd_sql_pt.erl b/src/ejabberd_sql_pt.erl index 10697f692..eb7905bf0 100644 --- a/src/ejabberd_sql_pt.erl +++ b/src/ejabberd_sql_pt.erl @@ -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}). - -parse(S, ParamPos, Loc) -> - parse1(S, [], #state{loc = Loc, param_pos = ParamPos}). +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). + +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). diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index da9664a48..524c80f50 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -74,21 +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 = ejabberd_config:get_option({access, VHost}, configure), - ACR = ejabberd_config:get_option({access_readonly, VHost}, webadmin_view), - {VHost, [AC, ACR]}; + {VHost, [configure, webadmin_view]}; get_acl_rule([<<"server">>, VHost | _RPath], 'POST') -> - AC = ejabberd_config:get_option({access, VHost}, 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 = ejabberd_config:get_option(access, configure), - ACR = ejabberd_config:get_option(access_readonly, webadmin_view), - {global, [AC, ACR]}; + {global, [configure, webadmin_view]}; get_acl_rule(_RPath, 'POST') -> - AC = ejabberd_config:get_option(access, configure), - {global, [AC]}. + {global, [configure]}. %%%================================== %%%% Menu Items Access @@ -269,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}} @@ -1246,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), @@ -1325,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, diff --git a/src/gen_mod.erl b/src/gen_mod.erl index a477ec295..3b63ed74a 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -32,8 +32,7 @@ -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, +-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, @@ -63,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(). @@ -152,7 +152,7 @@ sort_modules(Host, ModOpts) -> [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 " "module '~s' but is not found in " @@ -171,7 +171,11 @@ 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. @@ -180,8 +184,8 @@ sort_modules(Host, ModOpts) -> start_modules(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}. @@ -189,18 +193,18 @@ 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) -> +-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); @@ -209,7 +213,7 @@ start_module(Host, Module, Opts0, NeedValidation) -> end, case Res of {ok, Opts} -> - store_options(Host, Module, Opts), + store_options(Host, Module, Opts, Order), try case Module:start(Host, Opts) of ok -> ok; {ok, Pid} when is_pid(Pid) -> {ok, Pid}; @@ -236,22 +240,17 @@ start_module(Host, Module, Opts0, NeedValidation) -> erlang:get_stacktrace()]) end, ?CRITICAL_MSG(ErrorText, []), - maybe_halt_ejabberd(ErrorText), + maybe_halt_ejabberd(), erlang:raise(Class, Reason, erlang:get_stacktrace()) end; - {error, ErrorText} -> - maybe_halt_ejabberd(ErrorText) + {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 @@ -262,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 @@ -273,12 +272,12 @@ 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 {ok, OldOpts} -> ok; {ok, NewOpts} -> - reload_module(Host, Mod, NewOpts, OldOpts); + reload_module(Host, Mod, NewOpts, OldOpts, Order); {error, _} -> ok end; @@ -287,12 +286,12 @@ reload_modules(Host) -> 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), + store_options(Host, Module, NewOpts, Order), try case Module:reload(Host, NewOpts, OldOpts) of ok -> ok; {ok, Pid} when is_pid(Pid) -> {ok, Pid}; @@ -310,23 +309,22 @@ reload_module(Host, Module, NewOpts, OldOpts) -> ?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. @@ -347,7 +345,7 @@ stop_modules() -> -spec stop_modules(binary()) -> ok. stop_modules(Host) -> - Modules = lists:reverse(get_modules_options(Host)), + Modules = lists:reverse(loaded_modules_with_opts(Host)), lists:foreach( fun({Module, _Args}) -> stop_module_keep_config(Host, Module) @@ -555,7 +553,8 @@ validate_opts(Host, Module, Opts0) -> undef -> Opts; Validators -> - validate_opts(Host, Module, Opts, Required, 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'", @@ -680,6 +679,16 @@ merge_opts(Opts, DefaultOpts) -> 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 @@ -788,17 +797,26 @@ is_db_configured(Type, Host) -> -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()]. @@ -815,9 +833,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>>, diff --git a/src/misc.erl b/src/misc.erl index 9f3210d5e..cd8641e61 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -29,7 +29,7 @@ %% 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, @@ -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]), @@ -262,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( diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 1f3ec0397..251c09614 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -225,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}], @@ -786,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. @@ -822,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 @@ -839,12 +840,11 @@ check_password_hash(User, Host, PasswordHash, HashMethod) -> PasswordHash -> ok; _ -> false end. -get_md5(AccountPass) -> - iolist_to_binary([io_lib:format("~2.16.0B", [X]) - || X <- binary_to_list(erlang:md5(AccountPass))]). -get_sha(AccountPass) -> + +get_hash(AccountPass, Method) -> 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:get_module_opt(Host, mod_last, db_type), diff --git a/src/mod_avatar.erl b/src/mod_avatar.erl index 640f5f6b4..e92a87594 100644 --- a/src/mod_avatar.erl +++ b/src/mod_avatar.erl @@ -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, 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"). @@ -43,13 +46,17 @@ start(Host, _Opts) -> ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE, vcard_iq_convert, 30), ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE, - vcard_iq_publish, 100). + 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. @@ -144,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 %%%=================================================================== diff --git a/src/mod_client_state.erl b/src/mod_client_state.erl index dda22ed2b..3ed52bd24 100644 --- a/src/mod_client_state.erl +++ b/src/mod_client_state.erl @@ -37,8 +37,8 @@ -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"). @@ -164,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, @@ -179,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, @@ -194,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}; diff --git a/src/mod_configure.erl b/src/mod_configure.erl index db0780834..471e2bcdc 100644 --- a/src/mod_configure.erl +++ b/src/mod_configure.erl @@ -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">>, diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index 9a5952926..4e522e9b1 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -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">>). @@ -671,21 +671,31 @@ mk_slot(Slot, #state{put_url = PutPrefix, get_url = GetPrefix}, XMLNS) -> 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 replace_special_chars(binary()) -> binary(). + +replace_special_chars(S) -> + re:replace(S, <<"[^\\p{Xan}_.-]">>, <<$_>>, + [unicode, global, {return, binary}]). -spec yield_content_type(binary()) -> binary(). diff --git a/src/mod_last.erl b/src/mod_last.erl index 7e53fe5dc..ae02b15ad 100644 --- a/src/mod_last.erl +++ b/src/mod_last.erl @@ -188,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}; diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index 174cc8de1..1609ff79e 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -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 %%%=================================================================== @@ -332,7 +326,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 +355,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='">>, diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index bf88b2d8b..4a936c66e 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -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 " diff --git a/src/mod_multicast.erl b/src/mod_multicast.erl index e6b2a93d2..846049a55 100644 --- a/src/mod_multicast.erl +++ b/src/mod_multicast.erl @@ -47,52 +47,36 @@ -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,6 +116,7 @@ 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_hosts(LServerS, Opts), @@ -137,7 +124,6 @@ init([LServerS, 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, @@ -277,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) @@ -321,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), @@ -333,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]. - -perform(From, Packet, AAttrs, _, +-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). + +-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 @@ -427,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), @@ -448,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). %%%------------------------- @@ -469,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: ", @@ -497,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}. @@ -520,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]; @@ -554,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); @@ -613,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 @@ -647,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). @@ -670,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. %%%------------------------- @@ -681,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. @@ -695,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) -> @@ -778,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 @@ -807,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. %%%------------------------- @@ -845,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; @@ -935,44 +937,6 @@ purge_loop(NM) -> 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 %%%------------------------- @@ -1005,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}; @@ -1018,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) -> diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index cd9aedef2..5d02d6518 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -261,8 +261,8 @@ init([ServerHost, Opts]) -> {Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts), DefaultModule = plugin(Host, hd(Plugins)), DefaultNodeCfg = merge_config( - gen_mod:get_opt(default_node_config, Opts), - DefaultModule:options()), + [gen_mod:get_opt(default_node_config, Opts), + DefaultModule:options()]), lists:foreach( fun(H) -> T = gen_mod:get_module_proc(H, config), @@ -373,13 +373,9 @@ init_plugins(Host, ServerHost, Opts) -> PluginsOK = lists:foldl( fun (Name, Acc) -> Plugin = plugin(Host, Name), - case catch apply(Plugin, init, [Host, ServerHost, Opts]) of - {'EXIT', _Error} -> - Acc; - _ -> - ?DEBUG("** init ~s plugin", [Name]), - [Name | Acc] - end + apply(Plugin, init, [Host, ServerHost, Opts]), + ?DEBUG("** init ~s plugin", [Name]), + [Name | Acc] end, [], Plugins), {lists:reverse(PluginsOK), TreePlugin, PepMapping}. @@ -689,12 +685,17 @@ remove_user(User, Server) -> ({#pubsub_node{nodeid = {H, N}, type = Type}, owner}) when N == HomeTreeBase, Type == <<"hometree">> -> delete_node(H, N, Entity); - ({#pubsub_node{id = Nidx}, publisher}) -> + ({#pubsub_node{id = Nidx}, _}) -> + {result, State} = node_action(Host, PType, + get_state, + [Nidx, jid:tolower(Entity)]), + ItemIds = State#pubsub_state.items, node_action(Host, PType, - set_affiliation, - [Nidx, Entity, none]); - (_) -> - ok + remove_extra_items, + [Nidx, 0, ItemIds]), + node_action(Host, PType, + set_affiliation, + [Nidx, Entity, none]) end, Affs) end, @@ -1116,7 +1117,7 @@ iq_pubsub(Host, Access, #iq{from = From, type = IQType, lang = Lang, Payload, PubOpts, Access) end; [] -> - {error, extended_error(xmpp:err_bad_request(), err_item_required())}; + publish_item(Host, ServerHost, Node, From, <<>>, [], [], Access); _ -> {error, extended_error(xmpp:err_bad_request(), err_invalid_payload())} end; @@ -1480,7 +1481,9 @@ create_node(Host, ServerHost, <<>>, Owner, Type, Access, Configuration) -> end; create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) -> Type = select_type(ServerHost, Host, Node, GivenType), - NodeOptions = merge_config(Configuration, node_options(Host, Type)), + NodeOptions = merge_config( + [node_config(Node, ServerHost), + Configuration, node_options(Host, Type)]), CreateNode = fun() -> Parent = case node_call(Host, Type, node_to_path, [Node]) of @@ -1701,7 +1704,7 @@ subscribe_node(Host, Node, From, JID, Configuration) -> Nidx = TNode#pubsub_node.id, Type = TNode#pubsub_node.type, Options = TNode#pubsub_node.options, - send_items(Host, Node, Nidx, Type, Options, Subscriber, 1), + send_items(Host, Node, Nidx, Type, Options, Subscriber, last), ServerHost = serverhost(Host), ejabberd_hooks:run(pubsub_subscribe_node, ServerHost, [ServerHost, Host, Node, Subscriber, SubId]), @@ -1787,19 +1790,15 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload, PubOpts, Access PayloadSize > PayloadMaxSize -> {error, extended_error(xmpp:err_not_acceptable(), err_payload_too_big())}; - (PayloadCount == 0) and (Payload == []) -> + (DeliverPayloads or PersistItems) and (PayloadCount == 0) -> {error, extended_error(xmpp:err_bad_request(), - err_payload_required())}; - (PayloadCount > 1) or (PayloadCount == 0) -> + err_item_required())}; + (DeliverPayloads or PersistItems) and (PayloadCount > 1) -> {error, extended_error(xmpp:err_bad_request(), err_invalid_payload())}; - (DeliverPayloads == false) and (PersistItems == false) and - (PayloadSize > 0) -> + (not (DeliverPayloads or PersistItems)) and (PayloadCount > 0) -> {error, extended_error(xmpp:err_bad_request(), err_item_forbidden())}; - ((DeliverPayloads == true) or (PersistItems == true)) and (PayloadSize == 0) -> - {error, extended_error(xmpp:err_bad_request(), - err_item_required())}; true -> node_call(Host, Type, publish_item, [Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload, PubOpts]) @@ -2005,8 +2004,13 @@ get_items(Host, Node, From, SubId, _MaxItems, ItemIds, RSM) -> Host, From, Owners, AccessModel, AllowedGroups), case ItemIds of [ItemId] -> - node_call(Host, Type, get_item, - [Nidx, ItemId, From, AccessModel, PS, RG, undefined]); + NotFound = xmpp:err_item_not_found(), + case node_call(Host, Type, get_item, + [Nidx, ItemId, From, AccessModel, PS, RG, undefined]) + of + {error, NotFound} -> {result, {[], undefined}}; + Result -> Result + end; _ -> node_call(Host, Type, get_items, [Nidx, From, AccessModel, PS, RG, SubId, RSM]) @@ -2014,7 +2018,7 @@ get_items(Host, Node, From, SubId, _MaxItems, ItemIds, RSM) -> end end, case transaction(Host, Node, Action, sync_dirty) of - {result, {_, {Items, RsmOut}}} -> + {result, {TNode, {Items, RsmOut}}} -> SendItems = case ItemIds of [] -> Items; @@ -2024,14 +2028,12 @@ get_items(Host, Node, From, SubId, _MaxItems, ItemIds, RSM) -> lists:member(ItemId, ItemIds) end, Items) end, - {result, - #pubsub{items = #ps_items{node = Node, - items = itemsEls(SendItems)}, - rsm = RsmOut}}; - {result, {_, Item}} -> - {result, - #pubsub{items = #ps_items{node = Node, - items = itemsEls([Item])}}}; + Options = TNode#pubsub_node.options, + {result, #pubsub{items = items_els(Node, Options, SendItems), + rsm = RsmOut}}; + {result, {TNode, Item}} -> + Options = TNode#pubsub_node.options, + {result, #pubsub{items = items_els(Node, Options, [Item])}}; Error -> Error end. @@ -2065,6 +2067,9 @@ get_allowed_items_call(Host, Nidx, From, Type, Options, Owners, RSM) -> {PS, RG} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups), node_call(Host, Type, get_items, [Nidx, From, AccessModel, PS, RG, undefined, RSM]). +get_last_items(Host, Type, Nidx, LJID, last) -> + % hack to handle section 6.1.7 of XEP-0060 + get_last_items(Host, Type, Nidx, LJID, 1); get_last_items(Host, Type, Nidx, LJID, 1) -> case get_cached_item(Host, Nidx) of undefined -> @@ -2631,45 +2636,37 @@ payload_xmlelements([#xmlel{} | Tail], Count) -> payload_xmlelements([_ | Tail], Count) -> payload_xmlelements(Tail, Count). -items_event_stanza(Node, Options, Items) -> - MoreEls = case Items of - [LastItem] -> - {ModifNow, ModifUSR} = LastItem#pubsub_item.modification, - [#delay{stamp = ModifNow, from = jid:make(ModifUSR)}]; +items_els(Node, Options, Items) -> + Els = case get_option(Options, itemreply) of + publisher -> + [#ps_item{id = ItemId, sub_els = Payload, publisher = jid:encode(USR)} + || #pubsub_item{itemid = {ItemId, _}, payload = Payload, modification = {_, USR}} + <- Items]; _ -> - [] + [#ps_item{id = ItemId, sub_els = Payload} + || #pubsub_item{itemid = {ItemId, _}, payload = Payload} + <- Items] end, - BaseStanza = #message{ - sub_els = [#ps_event{items = #ps_items{ - node = Node, - items = itemsEls(Items)}} - | MoreEls]}, - NotificationType = get_option(Options, notification_type, headline), - add_message_type(BaseStanza, NotificationType). + #ps_items{node = Node, items = Els}. %%%%%% broadcast functions broadcast_publish_item(Host, Node, Nidx, Type, NodeOptions, ItemId, From, Payload, Removed) -> case get_collection_subscriptions(Host, Node) of SubsByDepth when is_list(SubsByDepth) -> - EventItem0 = case get_option(NodeOptions, deliver_payloads) of - true -> #ps_item{sub_els = Payload, id = ItemId}; - false -> #ps_item{id = ItemId} - end, - EventItem = case get_option(NodeOptions, itemreply, none) of - owner -> %% owner not supported - EventItem0; - publisher -> - EventItem0#ps_item{ - publisher = jid:encode(From)}; - none -> - EventItem0 - end, - Stanza = #message{ - sub_els = - [#ps_event{items = - #ps_items{node = Node, - items = [EventItem]}}]}, + ItemPublisher = case get_option(NodeOptions, itemreply) of + publisher -> jid:encode(From); + _ -> <<>> + end, + ItemPayload = case get_option(NodeOptions, deliver_payloads) of + true -> Payload; + false -> [] + end, + ItemsEls = #ps_items{node = Node, + items = [#ps_item{id = ItemId, + publisher = ItemPublisher, + sub_els = ItemPayload}]}, + Stanza = #message{ sub_els = [#ps_event{items = ItemsEls}]}, broadcast_stanza(Host, From, Node, Nidx, Type, NodeOptions, SubsByDepth, items, Stanza, true), case Removed of @@ -2896,8 +2893,20 @@ send_items(Host, Node, Nidx, Type, Options, Publisher, SubLJID, ToLJID, Number) [] -> ok; Items -> - Stanza = items_event_stanza(Node, Options, Items), - send_stanza(Publisher, ToLJID, Node, Stanza) + Delay = case Number of + last -> % handle section 6.1.7 of XEP-0060 + [Last] = Items, + {Stamp, _USR} = Last#pubsub_item.modification, + [#delay{stamp = Stamp}]; + _ -> + [] + end, + Stanza = #message{ + sub_els = [#ps_event{items = items_els(Node, Options, Items)} + | Delay]}, + NotificationType = get_option(Options, notification_type, headline), + send_stanza(Publisher, ToLJID, Node, + add_message_type(Stanza, NotificationType)) end. send_stanza({LUser, LServer, _} = Publisher, USR, Node, BaseStanza) -> @@ -3153,6 +3162,20 @@ node_owners_call(Host, Type, Nidx, []) -> node_owners_call(_Host, _Type, _Nidx, Owners) -> Owners. +node_config(Node, ServerHost) -> + Opts = gen_mod:get_module_opt(ServerHost, ?MODULE, force_node_config), + node_config(Node, ServerHost, Opts). + +node_config(Node, ServerHost, [{RE, Opts}|NodeOpts]) -> + case re:run(Node, RE) of + {match, _} -> + Opts; + nomatch -> + node_config(Node, ServerHost, NodeOpts) + end; +node_config(_, _, []) -> + []. + %% @spec (Host, Options) -> MaxItems %% Host = host() %% Options = [Option] @@ -3215,7 +3238,9 @@ set_configure(Host, Node, From, Config, Lang) -> [] -> node_options(Host, Type); _ -> Options end, - NewOpts = merge_config(Config, OldOpts), + NewOpts = merge_config( + [node_config(Node, serverhost(Host)), + Config, OldOpts]), case tree_call(Host, set_node, [N#pubsub_node{options = NewOpts}]) of @@ -3238,12 +3263,9 @@ set_configure(Host, Node, From, Config, Lang) -> Other end. --spec merge_config([proplists:property()], [proplists:property()]) -> [proplists:property()]. -merge_config(CustomConfig, DefaultConfig) -> - lists:foldl( - fun({Opt, Val}, Acc) -> - lists:keystore(Opt, 1, Acc, {Opt, Val}) - end, DefaultConfig, CustomConfig). +-spec merge_config([[proplists:property()]]) -> [proplists:property()]. +merge_config(ListOfConfigs) -> + lists:ukeysort(1, lists:flatten(ListOfConfigs)). -spec decode_node_config(undefined | xdata(), binary(), binary()) -> pubsub_node_config:result() | @@ -3512,7 +3534,7 @@ tree_call({_User, Server, _Resource}, Function, Args) -> tree_call(Host, Function, Args) -> Tree = tree(Host), ?DEBUG("tree_call apply(~s, ~s, ~p) @ ~s", [Tree, Function, Args, Host]), - catch apply(Tree, Function, Args). + apply(Tree, Function, Args). tree_action(Host, Function, Args) -> ?DEBUG("tree_action ~p ~p ~p", [Host, Function, Args]), @@ -3520,9 +3542,9 @@ tree_action(Host, Function, Args) -> Fun = fun () -> tree_call(Host, Function, Args) end, case gen_mod:get_module_opt(ServerHost, ?MODULE, db_type) of mnesia -> - catch mnesia:sync_dirty(Fun); + mnesia:sync_dirty(Fun); sql -> - case catch ejabberd_sql:sql_bloc(ServerHost, Fun) of + case ejabberd_sql:sql_bloc(ServerHost, Fun) of {atomic, Result} -> Result; {aborted, Reason} -> @@ -3530,15 +3552,8 @@ tree_action(Host, Function, Args) -> ErrTxt = <<"Database failure">>, {error, xmpp:err_internal_server_error(ErrTxt, ?MYLANG)} end; - Other -> - case catch Fun() of - {'EXIT', _} -> - ?ERROR_MSG("unsupported backend: ~p~n", [Other]), - ErrTxt = <<"Database failure">>, - {error, xmpp:err_internal_server_error(ErrTxt, ?MYLANG)}; - Result -> - Result - end + _ -> + Fun() end. %% @doc <p>node plugin call.</p> @@ -3587,26 +3602,20 @@ transaction(Host, Node, Action, Trans) -> transaction(Host, Fun, Trans) -> ServerHost = serverhost(Host), DBType = gen_mod:get_module_opt(ServerHost, ?MODULE, db_type), - Retry = case DBType of - sql -> 2; - _ -> 1 - end, - transaction_retry(Host, ServerHost, Fun, Trans, DBType, Retry). + do_transaction(ServerHost, Fun, Trans, DBType). -transaction_retry(_Host, _ServerHost, _Fun, _Trans, _DBType, 0) -> - {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)}; -transaction_retry(Host, ServerHost, Fun, Trans, DBType, Count) -> +do_transaction(ServerHost, Fun, Trans, DBType) -> Res = case DBType of mnesia -> - catch mnesia:Trans(Fun); + mnesia:Trans(Fun); sql -> SqlFun = case Trans of transaction -> sql_transaction; _ -> sql_bloc end, - catch ejabberd_sql:SqlFun(ServerHost, Fun); + ejabberd_sql:SqlFun(ServerHost, Fun); _ -> - catch Fun() + Fun() end, case Res of {result, Result} -> @@ -3620,12 +3629,6 @@ transaction_retry(Host, ServerHost, Fun, Trans, DBType, Count) -> {aborted, Reason} -> ?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]), {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)}; - {'EXIT', {timeout, _} = Reason} -> - ?ERROR_MSG("transaction return internal error: ~p~n", [Reason]), - transaction_retry(Host, ServerHost, Fun, Trans, DBType, Count - 1); - {'EXIT', Reason} -> - ?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]), - {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)}; Other -> ?ERROR_MSG("transaction return internal error: ~p~n", [Other]), {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)} @@ -3735,11 +3738,6 @@ uniqid() -> {T1, T2, T3} = p1_time_compat:timestamp(), (str:format("~.16B~.16B~.16B", [T1, T2, T3])). --spec itemsEls([#pubsub_item{}]) -> [ps_item()]. -itemsEls(Items) -> - [#ps_item{id = ItemId, sub_els = Payload} - || #pubsub_item{itemid = {ItemId, _}, payload = Payload} <- Items]. - -spec add_message_type(message(), message_type()) -> message(). add_message_type(#message{} = Message, Type) -> Message#message{type = Type}. @@ -3863,6 +3861,15 @@ mod_opt_type(max_subscriptions_node) -> fun(A) when is_integer(A) andalso A >= 0 -> A; (undefined) -> undefined end; +mod_opt_type(force_node_config) -> + fun(NodeOpts) -> + lists:map( + fun({Node, Opts}) -> + {ok, RE} = re:compile( + ejabberd_regexp:sh_to_awk(Node)), + {RE, lists:keysort(1, Opts)} + end, NodeOpts) + end; mod_opt_type(default_node_config) -> fun (A) when is_list(A) -> A end; mod_opt_type(nodetree) -> @@ -3885,4 +3892,5 @@ mod_options(Host) -> {pep_mapping, []}, {plugins, [?STDNODE]}, {max_subscriptions_node, undefined}, - {default_node_config, []}]. + {default_node_config, []}, + {force_node_config, []}]. diff --git a/src/mod_push_keepalive.erl b/src/mod_push_keepalive.erl index 35932a815..750427ee1 100644 --- a/src/mod_push_keepalive.erl +++ b/src/mod_push_keepalive.erl @@ -156,9 +156,15 @@ c2s_session_resumed(State) -> -spec c2s_copy_session(c2s_state(), c2s_state()) -> c2s_state(). c2s_copy_session(State, #{push_enabled := true, push_resume_timeout := ResumeTimeout, - push_wake_on_timeout := WakeOnTimeout}) -> - State#{push_resume_timeout => ResumeTimeout, - push_wake_on_timeout => WakeOnTimeout}; + push_wake_on_timeout := WakeOnTimeout} = OldState) -> + State1 = case maps:find(push_resume_timeout_orig, OldState) of + {ok, Val} -> + State#{push_resume_timeout_orig => Val}; + error -> + State + end, + State1#{push_resume_timeout => ResumeTimeout, + push_wake_on_timeout => WakeOnTimeout}; c2s_copy_session(State, _) -> State. diff --git a/src/mod_register.erl b/src/mod_register.erl index 38df8e225..5c17c5d33 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -326,7 +326,8 @@ try_register(User, Server, Password, SourceRaw, Lang) -> ?INFO_MSG("The account ~s was registered " "from IP address ~s", [jid:encode({User, Server, <<"">>}), - ip_to_string(Source)]), + ejabberd_config:may_hide_data( + ip_to_string(Source))]), send_welcome_message(JID), send_registration_notifications( ?MODULE, JID, Source), @@ -387,7 +388,7 @@ send_welcome_message(JID) -> send_registration_notifications(Mod, UJID, Source) -> Host = UJID#jid.lserver, - case gen_mod:get_module_opt(Host, Mod, registration_watchers) of + case gen_mod:get_module_opt(Host, ?MODULE, registration_watchers) of [] -> ok; JIDs when is_list(JIDs) -> Body = @@ -395,8 +396,9 @@ send_registration_notifications(Mod, UJID, Source) -> "IP address ~s on node ~w using ~p.", [get_time_string(), jid:encode(UJID), - ip_to_string(Source), node(), - Mod])), + ejabberd_config:may_hide_data( + ip_to_string(Source)), + node(), Mod])), lists:foreach( fun(JID) -> ejabberd_router:route( diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 3b893d21d..d35b7dc97 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -42,7 +42,7 @@ -export([start/2, stop/1, reload/3, process_iq/1, export/1, import_info/0, process_local_iq/1, get_user_roster/2, - import/5, get_roster/2, + import/5, get_roster/2, push_item/3, import_start/2, import_stop/2, c2s_self_presence/1, in_subscription/2, out_subscription/1, set_items/3, remove_user/2, @@ -173,9 +173,9 @@ process_local_iq(#iq{type = set, from = From, lang = Lang, Txt = <<"Duplicated groups are not allowed by RFC6121">>, xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); false -> - #jid{server = Server} = From, - Access = gen_mod:get_module_opt(Server, ?MODULE, access), - case acl:match_rule(Server, Access, From) of + #jid{lserver = LServer} = From, + Access = gen_mod:get_module_opt(LServer, ?MODULE, access), + case acl:match_rule(LServer, Access, From) of deny -> Txt = <<"Access denied by service policy">>, xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); @@ -443,7 +443,7 @@ decode_item(Item, R, Managed) -> process_iq_set(#iq{from = _From, to = To, sub_els = [#roster_query{items = [QueryItem]}]} = IQ) -> - #jid{user = User, luser = LUser, lserver = LServer} = To, + #jid{luser = LUser, lserver = LServer} = To, LJID = jid:tolower(QueryItem#roster_item.jid), F = fun () -> Item = get_roster_item(LUser, LServer, LJID), @@ -463,7 +463,7 @@ process_iq_set(#iq{from = _From, to = To, end, case transaction(LUser, LServer, [LJID], F) of {atomic, {OldItem, Item}} -> - push_item(User, LServer, To, OldItem, Item), + push_item(To, OldItem, Item), case Item#roster.subscription of remove -> send_unsubscribing_presence(To, OldItem); @@ -477,36 +477,26 @@ process_iq_set(#iq{from = _From, to = To, xmpp:make_error(IQ, xmpp:err_internal_server_error()) end. -push_item(User, Server, From, OldItem, NewItem) -> - case roster_versioning_enabled(Server) of - true -> - push_item_version(Server, User, From, OldItem, NewItem, - roster_version(Server, User)); - false -> - lists:foreach( - fun(Resource) -> - push_item(User, Server, Resource, From, OldItem, NewItem) - end, ejabberd_sm:get_user_resources(User, Server)) - end. - -push_item(User, Server, Resource, From, OldItem, NewItem) -> - push_item(User, Server, Resource, From, OldItem, NewItem, undefined). - -push_item(User, Server, Resource, From, OldItem, NewItem, Ver) -> - To = jid:make(User, Server, Resource), - route_presence_change(To, OldItem, NewItem), - ResIQ = #iq{type = set, from = From, to = To, - id = <<"push", (randoms:get_string())/binary>>, - sub_els = [#roster_query{ver = Ver, - items = [encode_item(NewItem)]}]}, - ejabberd_router:route(ResIQ). - -push_item_version(Server, User, From, OldItem, NewItem, RosterVersion) -> +push_item(To, OldItem, NewItem) -> + #jid{luser = LUser, lserver = LServer} = To, + Ver = case roster_versioning_enabled(LServer) of + true -> roster_version(LServer, LUser); + false -> undefined + end, lists:foreach( fun(Resource) -> - push_item(User, Server, Resource, From, - OldItem, NewItem, RosterVersion) - end, ejabberd_sm:get_user_resources(User, Server)). + To1 = jid:replace_resource(To, Resource), + push_item(To1, OldItem, NewItem, Ver) + end, ejabberd_sm:get_user_resources(LUser, LServer)). + +push_item(To, OldItem, NewItem, Ver) -> + route_presence_change(To, OldItem, NewItem), + IQ = #iq{type = set, to = To, + from = jid:remove_resource(To), + id = <<"push", (randoms:get_string())/binary>>, + sub_els = [#roster_query{ver = Ver, + items = [encode_item(NewItem)]}]}, + ejabberd_router:route(IQ). -spec route_presence_change(jid(), #roster{}, #roster{}) -> ok. route_presence_change(From, OldItem, NewItem) -> @@ -630,8 +620,7 @@ process_subscription(Direction, User, Server, JID1, NewItem#roster.ask == in -> ok; true -> - push_item(User, Server, - jid:make(User, Server), OldItem, NewItem) + push_item(jid:make(User, Server), OldItem, NewItem) end, true; none -> @@ -1174,18 +1163,18 @@ import_stop(_LServer, _DBType) -> ets:delete(rostergroups_tmp), ok. --ifdef(NEW_SQL_SCHEMA). --define(ROW_LENGTH, 10). --else. --define(ROW_LENGTH, 9). --endif. +row_length() -> + case ejabberd_sql:use_new_schema() of + true -> 10; + false -> 9 + end. import(LServer, {sql, _}, _DBType, <<"rostergroups">>, [LUser, SJID, Group]) -> LJID = jid:tolower(jid:decode(SJID)), ets:insert(rostergroups_tmp, {{LUser, LServer, LJID}, Group}), ok; import(LServer, {sql, _}, DBType, <<"rosterusers">>, Row) -> - I = mod_roster_sql:raw_to_record(LServer, lists:sublist(Row, ?ROW_LENGTH)), + I = mod_roster_sql:raw_to_record(LServer, lists:sublist(Row, row_length())), Groups = [G || {_, G} <- ets:lookup(rostergroups_tmp, I#roster.usj)], RosterItem = I#roster{groups = Groups}, Mod = gen_mod:db_mod(DBType, ?MODULE), diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index a1fda9438..84ab34286 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -553,8 +553,6 @@ add_user_to_group(Host, US, Group) -> DisplayedGroups = get_displayed_groups(Group, LServer), push_user_to_displayed(LUser, LServer, Group, Host, both, DisplayedToGroups), push_displayed_to_user(LUser, LServer, Host, both, DisplayedGroups), - broadcast_user_to_displayed(LUser, LServer, Host, both, DisplayedToGroups), - broadcast_displayed_to_user(LUser, LServer, Host, both, DisplayedGroups), Mod = gen_mod:db_mod(Host, ?MODULE), Mod:add_user_to_group(Host, US, Group) end. @@ -564,11 +562,6 @@ get_displayed_groups(Group, LServer) -> GroupOpts = proplists:get_value(Group, GroupsOpts, []), proplists:get_value(displayed_groups, GroupOpts, []). -broadcast_displayed_to_user(LUser, LServer, Host, Subscription, DisplayedGroups) -> - [broadcast_members_to_user(LUser, LServer, DGroup, Host, - Subscription) - || DGroup <- DisplayedGroups]. - push_displayed_to_user(LUser, LServer, Host, Subscription, DisplayedGroups) -> [push_members_to_user(LUser, LServer, DGroup, Host, Subscription) @@ -612,13 +605,6 @@ push_members_to_user(LUser, LServer, Group, Host, end, Members). -broadcast_members_to_user(LUser, LServer, Group, Host, Subscription) -> - Members = get_group_users(Host, Group), - lists:foreach( - fun({U, S}) -> - broadcast_subscription(U, S, {LUser, LServer, <<"">>}, Subscription) - end, Members). - -spec register_user(binary(), binary()) -> ok. register_user(User, Server) -> Groups = get_user_groups({User, Server}), @@ -665,10 +651,6 @@ push_user_to_displayed(LUser, LServer, Group, Host, Subscription, DisplayedToGro GroupName, Subscription) || GroupD <- DisplayedToGroupsOpts]. -broadcast_user_to_displayed(LUser, LServer, Host, Subscription, DisplayedToGroupsOpts) -> - [broadcast_user_to_group(LUser, LServer, GroupD, Host, Subscription) - || GroupD <- DisplayedToGroupsOpts]. - push_user_to_group(LUser, LServer, Group, Host, GroupName, Subscription) -> lists:foreach(fun ({U, S}) @@ -680,13 +662,6 @@ push_user_to_group(LUser, LServer, Group, Host, end, get_group_users(Host, Group)). -broadcast_user_to_group(LUser, LServer, Group, Host, Subscription) -> - lists:foreach( - fun({U, S}) when (U == LUser) and (S == LServer) -> ok; - ({U, S}) -> - broadcast_subscription(LUser, LServer, {U, S, <<"">>}, Subscription) - end, get_group_users(Host, Group)). - %% Get list of groups to which this group is displayed displayed_to_groups(GroupName, LServer) -> GroupsOpts = groups_with_opts(LServer), @@ -699,15 +674,9 @@ displayed_to_groups(GroupName, LServer) -> [Name || {Name, _} <- Gs]. push_item(User, Server, Item) -> - Stanza = #iq{type = set, id = <<"push", (randoms:get_string())/binary>>, - sub_els = [#roster_query{ - items = [mod_roster:encode_item(Item)]}]}, - lists:foreach(fun (Resource) -> - JID = jid:make(User, Server, Resource), - ejabberd_router:route( - xmpp:set_from_to(Stanza, jid:remove_resource(JID), JID)) - end, - ejabberd_sm:get_user_resources(User, Server)). + mod_roster:push_item(jid:make(User, Server), + Item#roster{subscription = none}, + Item). push_roster_item(User, Server, ContactU, ContactS, GroupName, Subscription) -> @@ -720,31 +689,6 @@ push_roster_item(User, Server, ContactU, ContactS, -spec c2s_self_presence({presence(), ejabberd_c2s:state()}) -> {presence(), ejabberd_c2s:state()}. -c2s_self_presence({_, #{pres_last := _}} = Acc) -> - %% This is just a presence update, nothing to do - Acc; -c2s_self_presence({#presence{type = available}, #{jid := New}} = Acc) -> - LUser = New#jid.luser, - LServer = New#jid.lserver, - Resources = ejabberd_sm:get_user_resources(LUser, LServer), - ?DEBUG("user_available for ~p @ ~p (~p resources)", - [LUser, LServer, length(Resources)]), - case length(Resources) of - %% first session for this user - 1 -> - UserGroups = get_user_groups({LUser, LServer}), - lists:foreach(fun (OG) -> - ?DEBUG("user_available: pushing ~p @ ~p grp ~p", - [LUser, LServer, OG]), - DisplayedToGroups = displayed_to_groups(OG, LServer), - DisplayedGroups = get_displayed_groups(OG, LServer), - broadcast_displayed_to_user(LUser, LServer, LServer, both, DisplayedGroups), - broadcast_user_to_displayed(LUser, LServer, LServer, both, DisplayedToGroups) - end, - UserGroups); - _ -> ok - end, - Acc; c2s_self_presence(Acc) -> Acc. @@ -1020,21 +964,11 @@ split_grouphost(Host, Group) -> [_] -> {Host, Group} end. -broadcast_subscription(User, Server, ContactJid, Subscription) -> - ejabberd_sm:route(jid:make(User, Server), - {item, ContactJid, Subscription}). - displayed_groups_update(Members, DisplayedGroups, Subscription) -> - lists:foreach(fun({U, S}) -> - push_displayed_to_user(U, S, S, Subscription, DisplayedGroups), - case Subscription of - both -> - broadcast_displayed_to_user(U, S, S, to, DisplayedGroups), - broadcast_displayed_to_user(U, S, S, from, DisplayedGroups); - Subscr -> - broadcast_displayed_to_user(U, S, S, Subscr, DisplayedGroups) - end - end, Members). + lists:foreach( + fun({U, S}) -> + push_displayed_to_user(U, S, S, Subscription, DisplayedGroups) + end, Members). opts_to_binary(Opts) -> lists:map( diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 6ca5a8b39..a8aeaaef0 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -664,6 +664,8 @@ inherit_session_state(#{user := U, server := S, exit:{normal, _} -> {error, <<"Previous session PID has exited">>}; exit:{timeout, _} -> + ejabberd_sm:close_session(OldSID, U, S, R), + ejabberd_c2s:stop(OldPID), {error, <<"Session state copying timed out">>} end end; @@ -733,7 +735,9 @@ bounce_message_queue() -> need_to_enqueue(State, Pkt) when ?is_stanza(Pkt) -> {not xmpp:get_meta(Pkt, mgmt_is_resent, false), State}; need_to_enqueue(#{mgmt_force_enqueue := true} = State, #xmlel{}) -> - {true, maps:remove(mgmt_is_resent, State)}; + State1 = maps:remove(mgmt_force_enqueue, State), + State2 = maps:remove(mgmt_is_resent, State1), + {true, State2}; need_to_enqueue(State, _) -> {false, State}. diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl index ce4fcd702..51de22e4c 100644 --- a/src/mod_vcard_ldap.erl +++ b/src/mod_vcard_ldap.erl @@ -143,7 +143,7 @@ search_items(Entries, State) -> #eldap_entry{attributes = Attrs} = E, Attrs end, Entries), - lists:flatmap( + lists:filtermap( fun(Attrs) -> case eldap_utils:find_ldap_attrs(UIDs, Attrs) of {U, UIDAttrFormat} -> @@ -163,15 +163,15 @@ search_items(Entries, State) -> end, SearchReported), J = <<Username/binary, $@, LServer/binary>>, - [{<<"jid">>, J} | RFields]; + {true, [{<<"jid">>, J} | RFields]}; _ -> - [] + false end; _ -> - [] + false end; <<"">> -> - [] + false end end, Attributes). diff --git a/src/mod_vcard_sql.erl b/src/mod_vcard_sql.erl index ce7506b6b..57d2052a0 100644 --- a/src/mod_vcard_sql.erl +++ b/src/mod_vcard_sql.erl @@ -39,12 +39,6 @@ -include("ejabberd_sql_pt.hrl"). -include("translate.hrl"). --ifdef(NEW_SQL_SCHEMA). --define(USE_NEW_SCHEMA, true). --else. --define(USE_NEW_SCHEMA, false). --endif. - %%%=================================================================== %%% API %%%=================================================================== @@ -268,7 +262,7 @@ make_matchspec(LServer, Data) -> filter_fields(Data, <<"">>, LServer). filter_fields([], Match, LServer) -> - case ?USE_NEW_SCHEMA of + case ejabberd_sql:use_new_schema() of true -> SServer = ejabberd_sql:escape(LServer), case Match of diff --git a/src/mod_vcard_xupdate.erl b/src/mod_vcard_xupdate.erl index a44b8ced8..5c10a5a7e 100644 --- a/src/mod_vcard_xupdate.erl +++ b/src/mod_vcard_xupdate.erl @@ -24,9 +24,10 @@ %%%---------------------------------------------------------------------- -module(mod_vcard_xupdate). - -behaviour(gen_mod). +-protocol({xep, 398, '0.2.0'}). + %% gen_mod callbacks -export([start/2, stop/1, reload/3]). @@ -77,11 +78,18 @@ depends(_Host, _Opts) -> -> {presence(), ejabberd_c2s:state()}. update_presence({#presence{type = available} = Pres, #{jid := #jid{luser = LUser, lserver = LServer}} = State}) -> - Pres1 = case get_xupdate(LUser, LServer) of - undefined -> xmpp:remove_subtag(Pres, #vcard_xupdate{}); - XUpdate -> xmpp:set_subtag(Pres, XUpdate) - end, - {Pres1, State}; + case xmpp:get_subtag(Pres, #vcard_xupdate{}) of + #vcard_xupdate{hash = <<>>} -> + %% XEP-0398 forbids overwriting vcard:x:update + %% tags with empty <photo/> element + {Pres, State}; + _ -> + Pres1 = case get_xupdate(LUser, LServer) of + undefined -> xmpp:remove_subtag(Pres, #vcard_xupdate{}); + XUpdate -> xmpp:set_subtag(Pres, XUpdate) + end, + {Pres1, State} + end; update_presence(Acc) -> Acc. diff --git a/src/prosody2ejabberd.erl b/src/prosody2ejabberd.erl index 62cb32417..cfee9ae01 100644 --- a/src/prosody2ejabberd.erl +++ b/src/prosody2ejabberd.erl @@ -38,23 +38,30 @@ %%% API %%%=================================================================== from_dir(ProsodyDir) -> - case file:list_dir(ProsodyDir) of - {ok, HostDirs} -> - lists:foreach( - fun(HostDir) -> - Host = list_to_binary(HostDir), - lists:foreach( - fun(SubDir) -> - Path = filename:join( - [ProsodyDir, HostDir, SubDir]), - convert_dir(Path, Host, SubDir) - end, ["vcard", "accounts", "roster", - "private", "config", "offline", - "privacy", "pep", "pubsub"]) - end, HostDirs); - {error, Why} = Err -> - ?ERROR_MSG("failed to list ~s: ~s", - [ProsodyDir, file:format_error(Why)]), + case code:ensure_loaded(luerl) of + {module, _} -> + case file:list_dir(ProsodyDir) of + {ok, HostDirs} -> + lists:foreach( + fun(HostDir) -> + Host = list_to_binary(HostDir), + lists:foreach( + fun(SubDir) -> + Path = filename:join( + [ProsodyDir, HostDir, SubDir]), + convert_dir(Path, Host, SubDir) + end, ["vcard", "accounts", "roster", + "private", "config", "offline", + "privacy", "pep", "pubsub"]) + end, HostDirs); + {error, Why} = Err -> + ?ERROR_MSG("failed to list ~s: ~s", + [ProsodyDir, file:format_error(Why)]), + Err + end; + {error, _} = Err -> + ?INFO_MSG("The file 'luerl.beam' is not found: maybe " + "ejabberd is not compiled with Lua support", []), Err end. diff --git a/src/pubsub_migrate.erl b/src/pubsub_migrate.erl index 6b5900279..0728da765 100644 --- a/src/pubsub_migrate.erl +++ b/src/pubsub_migrate.erl @@ -529,5 +529,4 @@ report_and_stop(Tab, Err) -> "Failed to convert '~s' table to binary: ~p", [Tab, Err])), ?CRITICAL_MSG(ErrTxt, []), - timer:sleep(1000), - halt(string:substr(ErrTxt, 1, 199)). + ejabberd:halt(). diff --git a/src/rest.erl b/src/rest.erl index 16cf09a34..7e509dec8 100644 --- a/src/rest.erl +++ b/src/rest.erl @@ -172,7 +172,7 @@ url(Server, Path, Params) -> Base = base_url(Server, Path), [<<$&, ParHead/binary>> | ParTail] = [<<"&", (iolist_to_binary(Key))/binary, "=", - (ejabberd_http:url_encode(Value))/binary>> + (misc:url_encode(Value))/binary>> || {Key, Value} <- Params], Tail = iolist_to_binary([ParHead | ParTail]), binary_to_list(<<Base/binary, $?, Tail/binary>>). diff --git a/test/csi_tests.erl b/test/csi_tests.erl index f9d47d55f..2b346e275 100644 --- a/test/csi_tests.erl +++ b/test/csi_tests.erl @@ -55,7 +55,7 @@ master_slave_cases() -> all_master(Config) -> Peer = ?config(peer, Config), Presence = #presence{to = Peer}, - ChatState = #message{to = Peer, thread = <<"1">>, + ChatState = #message{to = Peer, thread = #message_thread{data = <<"1">>}, sub_els = [#chatstate{type = active}]}, Message = ChatState#message{body = [#text{data = <<"body">>}]}, PepPayload = xmpp:encode(#presence{}), @@ -133,15 +133,15 @@ all_slave(Config) -> [#ps_item{ id = <<"pep-2">>}]}}, #delay{}]} = recv_message(Config), - #message{from = Peer, thread = <<"1">>, + #message{from = Peer, thread = #message_thread{data = <<"1">>}, sub_els = [#chatstate{type = composing}, #delay{}]} = recv_message(Config), - #message{from = Peer, thread = <<"1">>, + #message{from = Peer, thread = #message_thread{data = <<"1">>}, body = [#text{data = <<"body">>}], sub_els = [#chatstate{type = active}]} = recv_message(Config), change_client_state(Config, active), wait_for_master(Config), - #message{from = Peer, thread = <<"1">>, + #message{from = Peer, thread = #message_thread{data = <<"1">>}, sub_els = [#chatstate{type = active}]} = recv_message(Config), disconnect(Config). diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index dcc17d0f5..23f3f2db3 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -1083,9 +1083,21 @@ create_sql_tables(sqlite, _BaseDir) -> create_sql_tables(Type, BaseDir) -> {VHost, File} = case Type of mysql -> - {?MYSQL_VHOST, "mysql.sql"}; + Path = case ejabberd_sql:use_new_schema() of + true -> + "mysql.new.sql"; + false -> + "mysql.sql" + end, + {?MYSQL_VHOST, Path}; pgsql -> - {?PGSQL_VHOST, "pg.sql"} + Path = case ejabberd_sql:use_new_schema() of + true -> + "pg.new.sql"; + false -> + "pg.sql" + end, + {?PGSQL_VHOST, Path} end, SQLFile = filename:join([BaseDir, "sql", File]), CreationQueries = read_sql_queries(SQLFile), |