Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d2f92eecd4 | |||
| 8918bfb55b | |||
| ce8bd343de | |||
| 2eaad21863 | |||
| 9b2a44e750 | |||
| aefe2fd640 | |||
| a8f336421e | |||
| f2e8754586 | |||
| 5770946f03 | |||
| 2e1e128833 | |||
| faefad746e | |||
| b7e296857c | |||
| 75fe6f44d2 | |||
| 30346a56de | |||
| a616cc04cb | |||
| d5e0ccb04a | |||
| ea09497038 | |||
| f5156390bb | |||
| 12691e738b | |||
| 6d9be82e1b | |||
| fa00caced4 | |||
| bacaae7873 | |||
| 81ae691738 | |||
| 9805b1a73a | |||
| 68ba109889 | |||
| 4cdb4c2090 | |||
| 1162137d5d | |||
| e227940b85 | |||
| 0fe1e40a9d | |||
| f48b4124b1 | |||
| fd4c5edc23 | |||
| d8433d1644 | |||
| 0f864d97f6 | |||
| b1c10d2a03 | |||
| 56b4d3902a | |||
| 533a4eec96 | |||
| bd83bb0790 | |||
| 7b79921734 | |||
| 017f60d33c | |||
| bf6fc75ada | |||
| 6d83cbd1a8 | |||
| 21aa344ed5 | |||
| a8c6eec28c | |||
| 6d08ecc41f | |||
| 9a8245b8e6 | |||
| e50ecf9e96 | |||
| e14f206615 | |||
| c0f8e5d7c0 | |||
| 1fd75265d6 | |||
| 94d50a447d | |||
| 12c4dfd955 | |||
| a51aa2bc44 | |||
| 4a9b1e94f3 | |||
| 07ef1775cd | |||
| 9ec69b8d62 | |||
| 0bb14bdc0b | |||
| a157e22c2f | |||
| a1dfb7fbe6 | |||
| 8f8f53a595 | |||
| d796dcace5 | |||
| dd8c468de3 | |||
| ced62e0df8 | |||
| 28feb90175 | |||
| 950c209310 | |||
| 77acbab965 | |||
| 934392fd7e |
@@ -1,21 +1,30 @@
|
||||
Environment
|
||||
-----------
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: Kind:Bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Environment
|
||||
|
||||
- ejabberd version: 18.09
|
||||
- Erlang version: `erl +V`
|
||||
- OS: Linux (Debian)
|
||||
- Installed from: source | distro package | official deb/rpm | official binary installer | other
|
||||
|
||||
Configuration (only if needed): grep -Ev '^$|^\s*#' ejabberd.yml
|
||||
---------------------------------------------------------------------------
|
||||
## Configuration (only if needed): grep -Ev '^$|^\s*#' ejabberd.yml
|
||||
|
||||
```yaml
|
||||
loglevel: 4
|
||||
...
|
||||
```
|
||||
|
||||
Errors from error.log/crash.log
|
||||
-------------------------------
|
||||
## Errors from error.log/crash.log
|
||||
|
||||
No errors
|
||||
|
||||
Bug description
|
||||
---------------
|
||||
Nothing works, plz halp :(
|
||||
## Bug description
|
||||
|
||||
Please, give us a precise description (what does not work, what is expected, etc.)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
labels:
|
||||
title: ''
|
||||
labels: Kind:Feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,3 +1,56 @@
|
||||
# Version 19.09
|
||||
|
||||
* Admin
|
||||
- The minimum required Erlang/OTP version is now 19.3
|
||||
- Fix API call using OAuth (#2982)
|
||||
- Rename MUC command arguments from Host to Service (#2976)
|
||||
|
||||
* Webadmin
|
||||
- Don't treat 'Host' header as a virtual XMPP host (#2989)
|
||||
- Fix some links to Guide in WebAdmin and add new ones (#3003)
|
||||
- Use select fields to input host in WebAdmin Backup (#3000)
|
||||
- Check account auth provided in WebAdmin is a local host (#3000)
|
||||
|
||||
* ACME
|
||||
- Improve ACME implementation
|
||||
- Fix IDA support in ACME requests
|
||||
- Fix unicode formatting in ACME module
|
||||
- Log an error message on IDNA failure
|
||||
- Support IDN hostnames in ACME requests
|
||||
- Don't attempt to create ACME directory on ejabberd startup
|
||||
- Don't allow requesting certificates for localhost or IP-like domains
|
||||
- Don't auto request certificate for localhost and IP-like domains
|
||||
- Add listener for ACME challenge in example config
|
||||
|
||||
* Authentication
|
||||
- JWT-only authentication for some users (#3012)
|
||||
|
||||
* MUC
|
||||
- Apply default role after revoking admin affiliation (#3023)
|
||||
- Custom exit message is not broadcast (#3004)
|
||||
- Revert "Affiliations other than admin and owner cannot invite to members_only rooms" (#2987)
|
||||
- When join new room with password, set pass and password_protected (#2668)
|
||||
- Improve rooms_* commands to accept 'global' as MUC service argument (#2976)
|
||||
- Rename MUC command arguments from Host to Service (#2976)
|
||||
|
||||
* SQL
|
||||
- Fix transactions for Microsoft SQL Server (#2978)
|
||||
- Spawn SQL connections on demand only
|
||||
|
||||
* Misc
|
||||
- Add support for XEP-0328: JID Prep
|
||||
- Added gsfonts for captcha
|
||||
- Log Mnesia table type on creation
|
||||
- Replicate Mnesia 'bosh' table when nodes are joined
|
||||
- Fix certificate selection for s2s (#3015)
|
||||
- Provide meaningful error when adding non-local users to shared roster (#3000)
|
||||
- Websocket: don't treat 'Host' header as a virtual XMPP host (#2989)
|
||||
- Fix sm ack related c2s error (#2984)
|
||||
- Don't hide the reason why c2s connection has failed
|
||||
- Unicode support
|
||||
- Correctly handle unicode in log messages
|
||||
- Fix unicode processing in ejabberd.yml
|
||||
|
||||
# Version 19.08
|
||||
|
||||
* Administration
|
||||
|
||||
@@ -111,7 +111,8 @@ To compile ejabberd you need:
|
||||
- OpenSSL ≥ 1.0.0.
|
||||
- Zlib ≥ 1.2.3, for Stream Compression support (XEP-0138). Optional.
|
||||
- PAM library. Optional. For Pluggable Authentication Modules (PAM).
|
||||
- ImageMagick's Convert program. Optional. For CAPTCHA challenges.
|
||||
- ImageMagick's Convert program and Ghostscript fonts. Optional. For CAPTCHA
|
||||
challenges.
|
||||
|
||||
If your system splits packages in libraries and development headers, you must
|
||||
install the development packages also.
|
||||
|
||||
+12
-10
@@ -23,9 +23,10 @@ log_rotate_date: ""
|
||||
log_rotate_count: 1
|
||||
log_rate_limit: 100
|
||||
|
||||
certfiles:
|
||||
- /etc/letsencrypt/live/localhost/fullchain.pem
|
||||
- /etc/letsencrypt/live/localhost/privkey.pem
|
||||
## If you already have certificates, list them here
|
||||
# certfiles:
|
||||
# - /etc/letsencrypt/live/domain.tld/fullchain.pem
|
||||
# - /etc/letsencrypt/live/domain.tld/privkey.pem
|
||||
|
||||
listen:
|
||||
-
|
||||
@@ -47,18 +48,19 @@ listen:
|
||||
module: ejabberd_http
|
||||
tls: true
|
||||
request_handlers:
|
||||
"/admin": ejabberd_web_admin
|
||||
"/api": mod_http_api
|
||||
"/bosh": mod_bosh
|
||||
"/captcha": ejabberd_captcha
|
||||
"/upload": mod_http_upload
|
||||
"/ws": ejabberd_http_ws
|
||||
/admin: ejabberd_web_admin
|
||||
/api: mod_http_api
|
||||
/bosh: mod_bosh
|
||||
/captcha: ejabberd_captcha
|
||||
/upload: mod_http_upload
|
||||
/ws: ejabberd_http_ws
|
||||
-
|
||||
port: 5280
|
||||
ip: "::"
|
||||
module: ejabberd_http
|
||||
request_handlers:
|
||||
"/admin": ejabberd_web_admin
|
||||
/admin: ejabberd_web_admin
|
||||
/.well-known/acme-challenge: ejabberd_acme
|
||||
-
|
||||
port: 1883
|
||||
ip: "::"
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
|
||||
-record(challenge, {
|
||||
type = <<"http-01">> :: bitstring(),
|
||||
status = pending :: pending | valid | invalid,
|
||||
uri = "" :: url(),
|
||||
token = <<"">> :: bitstring()
|
||||
}).
|
||||
|
||||
-record(data_acc, {
|
||||
id :: list(),
|
||||
ca_url :: url(),
|
||||
key :: jose_jwk:key()
|
||||
}).
|
||||
-type data_acc() :: #data_acc{}.
|
||||
|
||||
-record(data_cert, {
|
||||
domain :: bitstring(),
|
||||
pem :: pem(),
|
||||
path :: string()
|
||||
}).
|
||||
-type data_cert() :: #data_cert{}.
|
||||
|
||||
%%
|
||||
%% Types
|
||||
%%
|
||||
|
||||
%% Acme configuration
|
||||
-type acme_config() :: [{ca_url, url()} | {contact, bitstring()}].
|
||||
|
||||
%% The main data type that ejabberd_acme keeps
|
||||
-type acme_data() :: proplist().
|
||||
|
||||
%% The list of certificates kept in data
|
||||
-type data_certs() :: proplist(bitstring(), data_cert()).
|
||||
|
||||
%% The certificate saved in pem format
|
||||
-type pem() :: bitstring().
|
||||
|
||||
-type nonce() :: string().
|
||||
-type url() :: string().
|
||||
-type proplist() :: [{_, _}].
|
||||
-type proplist(X,Y) :: [{X,Y}].
|
||||
-type dirs() :: #{string() => url()}.
|
||||
-type jws() :: map().
|
||||
-type handle_resp_fun() :: fun(({ok, proplist(), proplist()}) -> {ok, _, nonce()}).
|
||||
|
||||
-type acme_challenge() :: #challenge{}.
|
||||
|
||||
%% Options
|
||||
-type account_opt() :: string().
|
||||
-type verbose_opt() :: string().
|
||||
-type domains_opt() :: string().
|
||||
|
||||
@@ -3,7 +3,7 @@ defmodule Ejabberd.Mixfile do
|
||||
|
||||
def project do
|
||||
[app: :ejabberd,
|
||||
version: "19.8.0",
|
||||
version: "19.9.0",
|
||||
description: description(),
|
||||
elixir: "~> 1.4",
|
||||
elixirc_paths: ["lib"],
|
||||
@@ -29,7 +29,8 @@ defmodule Ejabberd.Mixfile do
|
||||
included_applications: [:lager, :mnesia, :inets, :p1_utils, :cache_tab,
|
||||
:fast_tls, :stringprep, :fast_xml, :xmpp, :mqtree,
|
||||
:stun, :fast_yaml, :esip, :jiffy, :p1_oauth2,
|
||||
:eimp, :base64url, :jose, :pkix, :os_mon, :yconf]
|
||||
:eimp, :base64url, :jose, :pkix, :os_mon, :yconf,
|
||||
:p1_acme, :idna]
|
||||
++ cond_apps()]
|
||||
end
|
||||
|
||||
@@ -74,14 +75,14 @@ defmodule Ejabberd.Mixfile do
|
||||
{:xmpp, "~> 1.4.0"},
|
||||
{:cache_tab, "~> 1.0"},
|
||||
{:stringprep, "~> 1.0"},
|
||||
{:fast_yaml, "~> 1.0", override: true},
|
||||
{:fast_yaml, "~> 1.0"},
|
||||
{:fast_tls, "~> 1.1"},
|
||||
{:stun, "~> 1.0"},
|
||||
{:esip, "~> 1.0"},
|
||||
{:p1_mysql, "~> 1.0"},
|
||||
{:mqtree, "~> 1.0"},
|
||||
{:p1_pgsql, "~> 1.1"},
|
||||
{:jiffy, "~> 0.14.7"},
|
||||
{:jiffy, "~> 1.0"},
|
||||
{:p1_oauth2, "~> 0.6.1"},
|
||||
{:distillery, "~> 2.0"},
|
||||
{:pkix, "~> 1.0"},
|
||||
@@ -89,7 +90,9 @@ defmodule Ejabberd.Mixfile do
|
||||
{:eimp, "~> 1.0"},
|
||||
{:base64url, "~> 0.0.1"},
|
||||
{:yconf, "~> 1.0"},
|
||||
{:jose, "~> 1.8"}]
|
||||
{:jose, "~> 1.8"},
|
||||
{:idna, "~> 6.0"},
|
||||
{:p1_acme, "~> 1.0"}]
|
||||
++ cond_deps()
|
||||
end
|
||||
|
||||
|
||||
@@ -3,34 +3,36 @@
|
||||
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
|
||||
"cache_tab": {:hex, :cache_tab, "1.0.20", "00a09975fa6d7ad30407b551ca62f0e901bf36cf4e18bd24cd8c1c517d25b5d4", [:rebar3], [{:p1_utils, "1.0.16", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"distillery": {:hex, :distillery, "2.1.1", "f9332afc2eec8a1a2b86f22429e068ef35f84a93ea1718265e740d90dd367814", [:mix], [{:artificery, "~> 0.2", [hex: :artificery, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"earmark": {:hex, :earmark, "1.3.5", "0db71c8290b5bc81cb0101a2a507a76dca659513984d683119ee722828b424f6", [:mix], [], "hexpm"},
|
||||
"earmark": {:hex, :earmark, "1.4.1", "07bb382826ee8d08d575a1981f971ed41bd5d7e86b917fd012a93c51b5d28727", [:mix], [], "hexpm"},
|
||||
"eimp": {:hex, :eimp, "1.0.12", "c00cdc0ef7159f07e8ec50826d1de9bd051a9538bac772e455927d7f6165abb4", [:rebar3], [{:p1_utils, "1.0.16", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"epam": {:hex, :epam, "1.0.6", "6e57e1f5a330fa02a08ee0d4b16d9161f95177351e48c6dfede2f89b7e2f589f", [:rebar3], [], "hexpm"},
|
||||
"eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm"},
|
||||
"esip": {:hex, :esip, "1.0.30", "23d020270590cd6e2785fb33c8bb954e992ed3d469a5b668ceccb56c703888db", [:rebar3], [{:fast_tls, "1.1.2", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.16", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.29", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.21.1", "5ac36660846967cd869255f4426467a11672fec3d8db602c429425ce5b613b90", [:mix], [{:earmark, "~> 1.3", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.21.2", "caca5bc28ed7b3bdc0b662f8afe2bee1eedb5c3cf7b322feeeb7c6ebbde089d6", [:mix], [{:earmark, "~> 1.3.3 or ~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ezlib": {:hex, :ezlib, "1.0.6", "d43a3377006f91c853f65d5efd563d61bbc289f0115a311657c728f5e6e8c39f", [:rebar3], [], "hexpm"},
|
||||
"fast_tls": {:hex, :fast_tls, "1.1.2", "ec3b5ba9c5e87f66190196ee8cedffe8f02b9c5b76bb5bc6e81f93d00013ae3a", [:rebar3], [{:p1_utils, "1.0.16", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"fast_xml": {:hex, :fast_xml, "1.1.37", "e5276cd18d5ce5b179da34b6a1232805956f3057ee45833a0d62a9e414c86e07", [:rebar3], [{:p1_utils, "1.0.16", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"fast_yaml": {:hex, :fast_yaml, "1.0.20", "df9efa0722e99cff12b76c2175fbca1e268ef8949b4b7922e2f966bd2370bd13", [:rebar3], [{:p1_utils, "1.0.16", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"fast_yaml": {:hex, :fast_yaml, "1.0.21", "3aea577eb57fbb62df79e7c406a453d54d450d35626a19998830e37c767eda67", [:rebar3], [{:p1_utils, "1.0.16", [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"},
|
||||
"jiffy": {:hex, :jiffy, "0.14.13", "225a9a35e26417832c611526567194b4d3adc4f0dfa5f2f7008f4684076f2a01", [:rebar3], [], "hexpm"},
|
||||
"jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"jiffy": {:hex, :jiffy, "1.0.1", "4f25639772ca41202f41ba9c8f6ca0933554283dd4742c90651e03471c55e341", [: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.6.10", "6172b43ab720ac33914ccd0aeb21fdbdf88213847707d4b91e6af57b2ae5c4d2", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"luerl": {:hex, :luerl, "0.3.1", "5412807630aac1aaf59ffe5a1bc09259c447b4faeb1d3fe2d4ef41b87676cb04", [:rebar3], [], "hexpm"},
|
||||
"makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"mqtree": {:hex, :mqtree, "1.0.4", "69e9f2b5ea31a7fcad9ab3b43e3579e0d4564d682b41906d803f457523f210af", [:rebar3], [{:p1_utils, "1.0.16", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"mqtree": {:hex, :mqtree, "1.0.5", "9448fd262ac5fd6b502c30abac00978779ae402558c7934b36cad481b2b7ebcd", [:rebar3], [{:p1_utils, "1.0.16", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "0.5.1", "c90796ecee0289dbb5ad16d3ad06f957b0cd1199769641c961cfe0b97db190e0", [:mix], [], "hexpm"},
|
||||
"p1_acme": {:hex, :p1_acme, "1.0.1", "c67acfa201b77de1eac47e4ed54308a3046da221ae63ba2eff6ecfb0340a5272", [:rebar3], [{:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "1.0.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "1.8.4", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "1.0.1", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"p1_mysql": {:hex, :p1_mysql, "1.0.11", "ae20e1daa2c0634bb61c1529d8401b08b855297b1c7d9af980b2e063d8b58482", [:rebar3], [], "hexpm"},
|
||||
"p1_oauth2": {:hex, :p1_oauth2, "0.6.5", "a39db41de0287d4d1af3190beaa80edf17335b20f1d0ccace6c09580e0853987", [:rebar3], [], "hexpm"},
|
||||
"p1_pgsql": {:hex, :p1_pgsql, "1.1.7", "ef64d34adbbe08258cc10b1532649446d8c086ff8663d44f430d837ec31a89f8", [:rebar3], [], "hexpm"},
|
||||
"p1_utils": {:hex, :p1_utils, "1.0.16", "05b5d4fb1f002d827b0d0d344eecdb4208b535bf95264d44f588affec644212b", [:rebar3], [], "hexpm"},
|
||||
"pkix": {:hex, :pkix, "1.0.3", "c40648c74abb745163135d6a41f320f2804a5373ac3dcd5bf69987406d427d39", [:rebar3], [], "hexpm"},
|
||||
"pkix": {:hex, :pkix, "1.0.4", "81d552f736b1cadb278069a332cc94891a1c3095eb6281b340969ee455bd6fea", [:rebar3], [], "hexpm"},
|
||||
"sqlite3": {:hex, :sqlite3, "1.1.6", "4ea71af0b45908b5f02c9b09e4c87177039ef404f20accb35049cd8924cc417c", [:rebar3], [], "hexpm"},
|
||||
"stringprep": {:hex, :stringprep, "1.0.17", "bf962fe2a4d01298d220b6474689755103f703942a043908ca6cd323e8fa0947", [:rebar3], [{:p1_utils, "1.0.16", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"stun": {:hex, :stun, "1.0.29", "9678aa90302bda43af86949a6253b82c84535bd1aacdd8de7f052b68234f91b3", [:rebar3], [{:fast_tls, "1.1.2", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.16", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"xmpp": {:hex, :xmpp, "1.4.0", "03f3d83e48f7d94d7ea306fd1757a0e640c6a6db1d96bbe72abce9cab78c1860", [:rebar3], [{:ezlib, "1.0.6", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.2", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.37", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.16", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.17", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"yconf": {:hex, :yconf, "1.0.0", "dcf83fd0b8dcd944c33debe00a6cae6684ce135a9c2c1bb6fbdd02258b6895c2", [:rebar3], [{:fast_yaml, "1.0.20", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
|
||||
"xmpp": {:hex, :xmpp, "1.4.2", "7a41bbeaebaceadcc16128449e1b6d535bb1b9479739064288abbe93f8870170", [:rebar3], [{:ezlib, "1.0.6", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.2", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.37", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.16", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.17", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"yconf": {:hex, :yconf, "1.0.1", "970fbbe53df7113914488909943abbbe788fff94166b4e39afaebc3e73f2a4f7", [:rebar3], [{:fast_yaml, "1.0.21", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
}
|
||||
|
||||
+9
-6
@@ -24,15 +24,17 @@
|
||||
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.2"}}},
|
||||
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.17"}}},
|
||||
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.37"}}},
|
||||
{xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.4.0"}}},
|
||||
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.20"}}},
|
||||
{yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.0"}}},
|
||||
{idna, ".*", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}},
|
||||
{xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.4.2"}}},
|
||||
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.21"}}},
|
||||
{yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.1"}}},
|
||||
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
|
||||
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.5"}}},
|
||||
{pkix, ".*", {git, "https://github.com/processone/pkix", {tag, "1.0.3"}}},
|
||||
{pkix, ".*", {git, "https://github.com/processone/pkix", {tag, "1.0.4"}}},
|
||||
{jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.8.4"}}},
|
||||
{eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.12"}}},
|
||||
{mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.4"}}},
|
||||
{mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.5"}}},
|
||||
{p1_acme, ".*", {git, "https://github.com/processone/p1_acme.git", {tag, "1.0.1"}}},
|
||||
{if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.29"}}}},
|
||||
{if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.30"}}}},
|
||||
{if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql",
|
||||
@@ -74,7 +76,8 @@
|
||||
eimp,
|
||||
mqtree,
|
||||
pkix,
|
||||
yconf]}}.
|
||||
yconf,
|
||||
p1_acme]}}.
|
||||
|
||||
{erl_first_files, ["src/ejabberd_sql_pt.erl", "src/ejabberd_config.erl",
|
||||
"src/gen_mod.erl", "src/mod_muc_room.erl",
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
-module(acme_challenge).
|
||||
|
||||
-export ([key_authorization/2,
|
||||
solve_challenge/3,
|
||||
process/2,
|
||||
register_hooks/1,
|
||||
unregister_hooks/1,
|
||||
acme_handler/3
|
||||
]).
|
||||
%% Challenge Types
|
||||
%% ================
|
||||
%% 1. http-01: https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-7.2
|
||||
%% 2. dns-01: https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-7.3
|
||||
%% 3. tls-sni-01: https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-7.4
|
||||
%% 4. (?) oob-01: https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-7.5
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("xmpp.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
-include("ejabberd_acme.hrl").
|
||||
|
||||
%% This is the default endpoint for the http challenge
|
||||
%% This hooks is called from ejabberd_http
|
||||
acme_handler(Handlers, _Host, Request) ->
|
||||
case Request#request.path of
|
||||
[<<".well-known">>|_] ->
|
||||
[{[<<".well-known">>],acme_challenge}|Handlers];
|
||||
_ ->
|
||||
Handlers
|
||||
end.
|
||||
|
||||
%% TODO: Maybe validate request here??
|
||||
process(LocalPath, _Request) ->
|
||||
Result = ets_get_key_authorization(LocalPath),
|
||||
{200,
|
||||
[{<<"Content-Type">>, <<"text/plain">>}],
|
||||
Result}.
|
||||
|
||||
register_hooks(_Domain) ->
|
||||
?INFO_MSG("Registering hook for ACME HTTP headers", []),
|
||||
ejabberd_hooks:add(http_request_handlers, ?MODULE, acme_handler, 50).
|
||||
|
||||
unregister_hooks(_Domain) ->
|
||||
?INFO_MSG("Unregistering hook for ACME HTTP headers", []),
|
||||
ejabberd_hooks:delete(http_request_handlers, ?MODULE, acme_handler, 50).
|
||||
|
||||
-spec key_authorization(bitstring(), jose_jwk:key()) -> bitstring().
|
||||
key_authorization(Token, Key) ->
|
||||
Thumbprint = jose_jwk:thumbprint(Key),
|
||||
KeyAuthorization = erlang:iolist_to_binary([Token, <<".">>, Thumbprint]),
|
||||
KeyAuthorization.
|
||||
|
||||
-spec parse_challenge({proplist()}) -> {ok, acme_challenge()} | {error, _}.
|
||||
parse_challenge(Challenge0) ->
|
||||
try
|
||||
{Challenge} = Challenge0,
|
||||
{<<"type">>,Type} = proplists:lookup(<<"type">>, Challenge),
|
||||
{<<"status">>,Status} = proplists:lookup(<<"status">>, Challenge),
|
||||
{<<"uri">>,Uri} = proplists:lookup(<<"uri">>, Challenge),
|
||||
{<<"token">>,Token} = proplists:lookup(<<"token">>, Challenge),
|
||||
Res =
|
||||
#challenge{
|
||||
type = Type,
|
||||
status = list_to_atom(bitstring_to_list(Status)),
|
||||
uri = bitstring_to_list(Uri),
|
||||
token = Token
|
||||
},
|
||||
{ok, Res}
|
||||
catch
|
||||
_:Error ->
|
||||
{error, Error}
|
||||
end.
|
||||
|
||||
|
||||
|
||||
-spec solve_challenge(bitstring(), [{proplist()}], _) ->
|
||||
{ok, url(), bitstring()} | {error, _}.
|
||||
solve_challenge(ChallengeType, Challenges, Options) ->
|
||||
ParsedChallenges = [parse_challenge(Chall) || Chall <- Challenges],
|
||||
case lists:any(fun is_error/1, ParsedChallenges) of
|
||||
true ->
|
||||
?ERROR_MSG("Error parsing challenges: ~p~n", [Challenges]),
|
||||
{error, parse_challenge};
|
||||
false ->
|
||||
case [C || {ok, C} <- ParsedChallenges, is_challenge_type(ChallengeType, C)] of
|
||||
[Challenge] ->
|
||||
solve_challenge1(Challenge, Options);
|
||||
_ ->
|
||||
?ERROR_MSG("Challenge ~p not found in challenges: ~p~n", [ChallengeType, Challenges]),
|
||||
{error, not_found}
|
||||
end
|
||||
end.
|
||||
|
||||
-spec solve_challenge1(acme_challenge(), {jose_jwk:key(), string()}) ->
|
||||
{ok, url(), bitstring()} | {error, _}.
|
||||
solve_challenge1(Chal = #challenge{type = <<"http-01">>, token=Tkn}, Key) ->
|
||||
KeyAuthz = key_authorization(Tkn, Key),
|
||||
%% save_key_authorization(Chal, Tkn, KeyAuthz, HttpDir);
|
||||
ets_put_key_authorization(Tkn, KeyAuthz),
|
||||
{ok, Chal#challenge.uri, KeyAuthz};
|
||||
solve_challenge1(Challenge, _Key) ->
|
||||
?ERROR_MSG("Unknown Challenge Type: ~p", [Challenge]),
|
||||
{error, unknown_challenge}.
|
||||
|
||||
|
||||
-spec ets_put_key_authorization(bitstring(), bitstring()) -> ok.
|
||||
ets_put_key_authorization(Tkn, KeyAuthz) ->
|
||||
Tab = ets_get_acme_table(),
|
||||
Key = [<<"acme-challenge">>, Tkn],
|
||||
ets:insert(Tab, {Key, KeyAuthz}),
|
||||
ok.
|
||||
|
||||
-spec ets_get_key_authorization([bitstring()]) -> bitstring().
|
||||
ets_get_key_authorization(Key) ->
|
||||
Tab = ets_get_acme_table(),
|
||||
case ets:lookup(Tab, Key) of
|
||||
[{Key, KeyAuthz}] ->
|
||||
ets:delete(Tab, Key),
|
||||
KeyAuthz;
|
||||
_ ->
|
||||
?ERROR_MSG("Unable to serve key authorization in: ~p", [Key]),
|
||||
<<"">>
|
||||
end.
|
||||
|
||||
-spec ets_get_acme_table() -> atom().
|
||||
ets_get_acme_table() ->
|
||||
case ets:info(acme) of
|
||||
undefined ->
|
||||
ets:new(acme, [named_table, public]);
|
||||
_ ->
|
||||
acme
|
||||
end.
|
||||
|
||||
%% Useful functions
|
||||
|
||||
is_challenge_type(DesiredType, #challenge{type = Type}) when DesiredType =:= Type ->
|
||||
true;
|
||||
is_challenge_type(_DesiredType, #challenge{type = _Type}) ->
|
||||
false.
|
||||
|
||||
-spec is_error({'error', _}) -> 'true';
|
||||
({'ok', _}) -> 'false'.
|
||||
is_error({error, _}) -> true;
|
||||
is_error(_) -> false.
|
||||
+25
-25
@@ -90,7 +90,7 @@ format_error({bad_module, Mod}, Ctx)
|
||||
when Ctx == [listen, module];
|
||||
Ctx == [listen, request_handlers] ->
|
||||
Mods = ejabberd_config:beams(all),
|
||||
format("~s: unknown ~s: ~s. Did you mean ~s?",
|
||||
format("~ts: unknown ~ts: ~ts. Did you mean ~ts?",
|
||||
[yconf:format_ctx(Ctx),
|
||||
format_module_type(Ctx),
|
||||
format_module(Mod),
|
||||
@@ -105,7 +105,7 @@ format_error({bad_module, Mod}, Ctx)
|
||||
_ -> false
|
||||
end
|
||||
end, ejabberd_config:beams(all)),
|
||||
format("~s: unknown ~s: ~s. Did you mean ~s?",
|
||||
format("~ts: unknown ~ts: ~ts. Did you mean ~ts?",
|
||||
[yconf:format_ctx(Ctx),
|
||||
format_module_type(Ctx),
|
||||
format_module(Mod),
|
||||
@@ -118,30 +118,30 @@ format_error({bad_export, {F, A}, Mod}, Ctx)
|
||||
Slogan = yconf:format_ctx(Ctx),
|
||||
case lists:member(Mod, ejabberd_config:beams(local)) of
|
||||
true ->
|
||||
format("~s: '~s' is not a ~s",
|
||||
format("~ts: '~ts' is not a ~ts",
|
||||
[Slogan, format_module(Mod), Type]);
|
||||
false ->
|
||||
case lists:member(Mod, ejabberd_config:beams(external)) of
|
||||
true ->
|
||||
format("~s: third-party ~s '~s' doesn't export "
|
||||
"function ~s/~B. If it's really a ~s, "
|
||||
format("~ts: third-party ~ts '~ts' doesn't export "
|
||||
"function ~ts/~B. If it's really a ~ts, "
|
||||
"consider to upgrade it",
|
||||
[Slogan, Type, format_module(Mod),F, A, Type]);
|
||||
false ->
|
||||
format("~s: '~s' doesn't match any known ~s",
|
||||
format("~ts: '~ts' doesn't match any known ~ts",
|
||||
[Slogan, format_module(Mod), Type])
|
||||
end
|
||||
end;
|
||||
format_error({unknown_option, [], _} = Why, Ctx) ->
|
||||
format("~s. There are no available options",
|
||||
format("~ts. There are no available options",
|
||||
[yconf:format_error(Why, Ctx)]);
|
||||
format_error({unknown_option, Known, Opt} = Why, Ctx) ->
|
||||
format("~s. Did you mean ~s? ~s",
|
||||
format("~ts. Did you mean ~ts? ~ts",
|
||||
[yconf:format_error(Why, Ctx),
|
||||
misc:best_match(Opt, Known),
|
||||
format_known("Available options", Known)]);
|
||||
format_error({bad_enum, Known, Bad} = Why, Ctx) ->
|
||||
format("~s. Did you mean ~s? ~s",
|
||||
format("~ts. Did you mean ~ts? ~ts",
|
||||
[yconf:format_error(Why, Ctx),
|
||||
misc:best_match(Bad, Known),
|
||||
format_known("Possible values", Known)]);
|
||||
@@ -152,43 +152,43 @@ format_error(Reason, Ctx) ->
|
||||
yconf:format_ctx(Ctx) ++ ": " ++ [string:to_lower(H)|T].
|
||||
|
||||
format_error({bad_db_type, _, Atom}) ->
|
||||
format("unsupported database: ~s", [Atom]);
|
||||
format("unsupported database: ~ts", [Atom]);
|
||||
format_error({bad_lang, Lang}) ->
|
||||
format("Invalid language tag: ~s", [Lang]);
|
||||
format("Invalid language tag: ~ts", [Lang]);
|
||||
format_error({bad_pem, Why, Path}) ->
|
||||
format("Failed to read PEM file '~s': ~s",
|
||||
format("Failed to read PEM file '~ts': ~ts",
|
||||
[Path, pkix:format_error(Why)]);
|
||||
format_error({bad_cert, Why, Path}) ->
|
||||
format_error({bad_pem, Why, Path});
|
||||
format_error({bad_jwt_key, Path}) ->
|
||||
format("No valid JWT key found in file: ~s", [Path]);
|
||||
format("No valid JWT key found in file: ~ts", [Path]);
|
||||
format_error({bad_jid, Bad}) ->
|
||||
format("Invalid XMPP address: ~s", [Bad]);
|
||||
format("Invalid XMPP address: ~ts", [Bad]);
|
||||
format_error({bad_user, Bad}) ->
|
||||
format("Invalid user part: ~s", [Bad]);
|
||||
format("Invalid user part: ~ts", [Bad]);
|
||||
format_error({bad_domain, Bad}) ->
|
||||
format("Invalid domain: ~s", [Bad]);
|
||||
format("Invalid domain: ~ts", [Bad]);
|
||||
format_error({bad_resource, Bad}) ->
|
||||
format("Invalid resource part: ~s", [Bad]);
|
||||
format("Invalid resource part: ~ts", [Bad]);
|
||||
format_error({bad_ldap_filter, Bad}) ->
|
||||
format("Invalid LDAP filter: ~s", [Bad]);
|
||||
format("Invalid LDAP filter: ~ts", [Bad]);
|
||||
format_error({bad_sip_uri, Bad}) ->
|
||||
format("Invalid SIP URI: ~s", [Bad]);
|
||||
format("Invalid SIP URI: ~ts", [Bad]);
|
||||
format_error({route_conflict, R}) ->
|
||||
format("Failed to reuse route '~s' because it's "
|
||||
format("Failed to reuse route '~ts' because it's "
|
||||
"already registered on a virtual host",
|
||||
[R]);
|
||||
format_error({listener_dup, AddrPort}) ->
|
||||
format("Overlapping listeners found at ~s",
|
||||
format("Overlapping listeners found at ~ts",
|
||||
[format_addr_port(AddrPort)]);
|
||||
format_error({listener_conflict, AddrPort1, AddrPort2}) ->
|
||||
format("Overlapping listeners found at ~s and ~s",
|
||||
format("Overlapping listeners found at ~ts and ~ts",
|
||||
[format_addr_port(AddrPort1),
|
||||
format_addr_port(AddrPort2)]);
|
||||
format_error({invalid_syntax, Reason}) ->
|
||||
format("~s", [Reason]);
|
||||
format("~ts", [Reason]);
|
||||
format_error({missing_module_dep, Mod, DepMod}) ->
|
||||
format("module ~s depends on module ~s, "
|
||||
format("module ~ts depends on module ~ts, "
|
||||
"which is not found in the config",
|
||||
[Mod, DepMod]);
|
||||
format_error(eimp_error) ->
|
||||
@@ -674,7 +674,7 @@ vcard_sound() ->
|
||||
{binval, undefined, base64()},
|
||||
{extval, undefined, binary()}]).
|
||||
|
||||
-spec vcard_key() -> yconv:validator().
|
||||
-spec vcard_key() -> yconf:validator().
|
||||
vcard_key() ->
|
||||
vcard_validator(
|
||||
vcard_key, undefined,
|
||||
|
||||
+6
-6
@@ -87,8 +87,8 @@ start_app([App|Apps], Type, StartFlag) ->
|
||||
case lists:member(DepApp, [App|Apps]) of
|
||||
true ->
|
||||
Reason = io_lib:format(
|
||||
"Failed to start Erlang application '~s': "
|
||||
"circular dependency with '~s' detected",
|
||||
"Failed to start Erlang application '~ts': "
|
||||
"circular dependency with '~ts' detected",
|
||||
[App, DepApp]),
|
||||
exit_or_halt(Reason, StartFlag);
|
||||
false ->
|
||||
@@ -96,7 +96,7 @@ start_app([App|Apps], Type, StartFlag) ->
|
||||
end;
|
||||
{error, Why} ->
|
||||
Reason = io_lib:format(
|
||||
"Failed to start Erlang application '~s': ~s. ~s",
|
||||
"Failed to start Erlang application '~ts': ~ts. ~ts",
|
||||
[App, format_error(Why), hint()]),
|
||||
exit_or_halt(Reason, StartFlag)
|
||||
end;
|
||||
@@ -112,8 +112,8 @@ check_app_modules(App, StartFlag) ->
|
||||
non_existing ->
|
||||
File = get_module_file(App, Mod),
|
||||
Reason = io_lib:format(
|
||||
"Couldn't find file ~s needed "
|
||||
"for Erlang application '~s'. ~s",
|
||||
"Couldn't find file ~ts needed "
|
||||
"for Erlang application '~ts'. ~ts",
|
||||
[File, App, hint()]),
|
||||
exit_or_halt(Reason, StartFlag);
|
||||
_ ->
|
||||
@@ -131,7 +131,7 @@ check_apps() ->
|
||||
Apps = [ejabberd |
|
||||
[App || {App, _, _} <- application:which_applications(),
|
||||
App /= ejabberd]],
|
||||
?DEBUG("Checking consistency of applications: ~s",
|
||||
?DEBUG("Checking consistency of applications: ~ts",
|
||||
[misc:join_atoms(Apps, <<", ">>)]),
|
||||
misc:peach(
|
||||
fun(App) ->
|
||||
|
||||
@@ -106,7 +106,7 @@ handle_call({can_access, Cmd, CallerInfo}, _From, State) ->
|
||||
case matches_definition(Def, Cmd, CallerModule, Tag, Host, CallerInfo) of
|
||||
true ->
|
||||
?DEBUG("Command '~p' execution allowed by rule "
|
||||
"'~s' (CallerInfo=~p)", [Cmd, Name, CallerInfo]),
|
||||
"'~ts' (CallerInfo=~p)", [Cmd, Name, CallerInfo]),
|
||||
allow;
|
||||
_ ->
|
||||
none
|
||||
|
||||
+603
-1104
File diff suppressed because it is too large
Load Diff
@@ -1,407 +0,0 @@
|
||||
-module(ejabberd_acme_comm).
|
||||
-export([%% Directory
|
||||
directory/1,
|
||||
%% Account
|
||||
new_account/4,
|
||||
update_account/4,
|
||||
get_account/3,
|
||||
delete_account/3,
|
||||
%% Authorization
|
||||
new_authz/4,
|
||||
get_authz/1,
|
||||
complete_challenge/4,
|
||||
%% Authorization polling
|
||||
get_authz_until_valid/1,
|
||||
%% Certificate
|
||||
new_cert/4,
|
||||
get_cert/1,
|
||||
revoke_cert/4,
|
||||
get_issuer_cert/1
|
||||
%% Not yet implemented
|
||||
%% key_roll_over/5
|
||||
%% delete_authz/3
|
||||
]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("xmpp.hrl").
|
||||
|
||||
-include("ejabberd_acme.hrl").
|
||||
-include_lib("public_key/include/public_key.hrl").
|
||||
|
||||
-define(REQUEST_TIMEOUT, 5000). % 5 seconds.
|
||||
-define(MAX_POLL_REQUESTS, 20).
|
||||
-define(POLL_WAIT_TIME, 500). % 500 ms.
|
||||
|
||||
%%%
|
||||
%%% This module contains functions that implement all necessary http
|
||||
%%% requests to the ACME Certificate Authority. Its purpose is to
|
||||
%%% facilitate the acme client implementation by separating the
|
||||
%%% handling/validating/parsing of all the needed http requests.
|
||||
%%%
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%
|
||||
%% Directory
|
||||
%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
-spec directory(url()) -> {ok, dirs(), nonce()} | {error, _}.
|
||||
directory(CAUrl) ->
|
||||
Url = CAUrl ++ "/directory",
|
||||
prepare_get_request(Url, fun get_dirs/1).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%
|
||||
%% Account Handling
|
||||
%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
-spec new_account(dirs(), jose_jwk:key(), proplist(), nonce()) ->
|
||||
{ok, {url(), proplist()}, nonce()} | {error, _}.
|
||||
new_account(Dirs, PrivateKey, Req, Nonce) ->
|
||||
#{"new-reg" := Url} = Dirs,
|
||||
EJson = {[{ <<"resource">>, <<"new-reg">>}] ++ Req},
|
||||
prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response_tos/1).
|
||||
|
||||
-spec update_account({url(), string()}, jose_jwk:key(), proplist(), nonce()) ->
|
||||
{ok, proplist(), nonce()} | {error, _}.
|
||||
update_account({CAUrl, AccId}, PrivateKey, Req, Nonce) ->
|
||||
Url = CAUrl ++ "/acme/reg/" ++ AccId,
|
||||
EJson = {[{ <<"resource">>, <<"reg">>}] ++ Req},
|
||||
prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response/1).
|
||||
|
||||
-spec get_account({url(), string()}, jose_jwk:key(), nonce()) ->
|
||||
{ok, {url(), proplist()}, nonce()} | {error, _}.
|
||||
get_account({CAUrl, AccId}, PrivateKey, Nonce) ->
|
||||
Url = CAUrl ++ "/acme/reg/" ++ AccId,
|
||||
EJson = {[{<<"resource">>, <<"reg">>}]},
|
||||
prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response_tos/1).
|
||||
|
||||
-spec delete_account({url(), string()}, jose_jwk:key(), nonce()) ->
|
||||
{ok, proplist(), nonce()} | {error, _}.
|
||||
delete_account({CAUrl, AccId}, PrivateKey, Nonce) ->
|
||||
Url = CAUrl ++ "/acme/reg/" ++ AccId,
|
||||
EJson =
|
||||
{[{<<"resource">>, <<"reg">>},
|
||||
{<<"status">>, <<"deactivated">>}]},
|
||||
prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response/1).
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%
|
||||
%% Authorization Handling
|
||||
%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
-spec new_authz(dirs(), jose_jwk:key(), proplist(), nonce()) ->
|
||||
{ok, {url(), proplist()}, nonce()} | {error, _}.
|
||||
new_authz(Dirs, PrivateKey, Req, Nonce) ->
|
||||
#{"new-authz" := Url} = Dirs,
|
||||
EJson = {[{<<"resource">>, <<"new-authz">>}] ++ Req},
|
||||
prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response_location/1).
|
||||
|
||||
-spec get_authz({url(), string()}) -> {ok, proplist(), nonce()} | {error, _}.
|
||||
get_authz({CAUrl, AuthzId}) ->
|
||||
Url = CAUrl ++ "/acme/authz/" ++ AuthzId,
|
||||
prepare_get_request(Url, fun get_response/1).
|
||||
|
||||
-spec complete_challenge({url(), string(), string()}, jose_jwk:key(), proplist(), nonce()) ->
|
||||
{ok, proplist(), nonce()} | {error, _}.
|
||||
complete_challenge({CAUrl, AuthzId, ChallId}, PrivateKey, Req, Nonce) ->
|
||||
Url = CAUrl ++ "/acme/challenge/" ++ AuthzId ++ "/" ++ ChallId,
|
||||
EJson = {[{<<"resource">>, <<"challenge">>}] ++ Req},
|
||||
prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response/1).
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%
|
||||
%% Certificate Handling
|
||||
%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
-spec new_cert(dirs(), jose_jwk:key(), proplist(), nonce()) ->
|
||||
{ok, {url(), list()}, nonce()} | {error, _}.
|
||||
new_cert(Dirs, PrivateKey, Req, Nonce) ->
|
||||
#{"new-cert" := Url} = Dirs,
|
||||
EJson = {[{<<"resource">>, <<"new-cert">>}] ++ Req},
|
||||
prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response_link_up/1,
|
||||
"application/pkix-cert").
|
||||
|
||||
-spec get_cert({url(), string()}) -> {ok, list(), nonce()} | {error, _}.
|
||||
get_cert({CAUrl, CertId}) ->
|
||||
Url = CAUrl ++ "/acme/cert/" ++ CertId,
|
||||
prepare_get_request(Url, fun get_response/1, "application/pkix-cert").
|
||||
|
||||
-spec revoke_cert(dirs(), jose_jwk:key(), proplist(), nonce()) ->
|
||||
{ok, _, nonce()} | {error, _}.
|
||||
revoke_cert(Dirs, PrivateKey, Req, Nonce) ->
|
||||
#{"revoke-cert" := Url} = Dirs,
|
||||
EJson = {[{<<"resource">>, <<"revoke-cert">>}] ++ Req},
|
||||
prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response/1,
|
||||
"application/pkix-cert").
|
||||
|
||||
-spec get_issuer_cert(url()) -> {ok, list(), nonce()} | {error, _}.
|
||||
get_issuer_cert(IssuerCertUrl) ->
|
||||
prepare_get_request(IssuerCertUrl, fun get_response/1, "application/pkix-cert").
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%
|
||||
%% Handle Response Functions
|
||||
%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
-spec get_dirs({ok, proplist(), proplist()}) -> {ok, map(), nonce()}.
|
||||
get_dirs({ok, Head, Return}) ->
|
||||
NewNonce = get_nonce(Head),
|
||||
StrDirectories = [{bitstring_to_list(X), bitstring_to_list(Y)} ||
|
||||
{X, Y} <- Return, is_bitstring(X) andalso is_bitstring(Y)],
|
||||
NewDirs = maps:from_list(StrDirectories),
|
||||
{ok, NewDirs, NewNonce}.
|
||||
|
||||
-spec get_response({ok, proplist(), proplist()}) -> {ok, proplist(), nonce()}.
|
||||
get_response({ok, Head, Return}) ->
|
||||
NewNonce = get_nonce(Head),
|
||||
{ok, Return, NewNonce}.
|
||||
|
||||
-spec get_response_tos({ok, proplist(), proplist()}) -> {ok, {url(), proplist()}, nonce()}.
|
||||
get_response_tos({ok, Head, Return}) ->
|
||||
TOSUrl = get_tos(Head),
|
||||
NewNonce = get_nonce(Head),
|
||||
{ok, {TOSUrl, Return}, NewNonce}.
|
||||
|
||||
-spec get_response_location({ok, proplist(), proplist()}) -> {ok, {url(), proplist()}, nonce()}.
|
||||
get_response_location({ok, Head, Return}) ->
|
||||
Location = get_location(Head),
|
||||
NewNonce = get_nonce(Head),
|
||||
{ok, {Location, Return}, NewNonce}.
|
||||
|
||||
-spec get_response_link_up({ok, proplist(), proplist()}) -> {ok, {url(), proplist()}, nonce()}.
|
||||
get_response_link_up({ok, Head, Return}) ->
|
||||
LinkUp = get_link_up(Head),
|
||||
NewNonce = get_nonce(Head),
|
||||
{ok, {LinkUp, Return}, NewNonce}.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%
|
||||
%% Authorization Polling
|
||||
%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
-spec get_authz_until_valid({url(), string()}) -> {ok, proplist(), nonce()} | {error, _}.
|
||||
get_authz_until_valid({CAUrl, AuthzId}) ->
|
||||
get_authz_until_valid({CAUrl, AuthzId}, ?MAX_POLL_REQUESTS).
|
||||
|
||||
-spec get_authz_until_valid({url(), string()}, non_neg_integer()) ->
|
||||
{ok, proplist(), nonce()} | {error, _}.
|
||||
get_authz_until_valid({_CAUrl, _AuthzId}, 0) ->
|
||||
?ERROR_MSG("Maximum request limit waiting for validation reached", []),
|
||||
{error, max_request_limit};
|
||||
get_authz_until_valid({CAUrl, AuthzId}, N) ->
|
||||
case get_authz({CAUrl, AuthzId}) of
|
||||
{ok, Resp, Nonce} ->
|
||||
case is_authz_valid(Resp) of
|
||||
true ->
|
||||
{ok, Resp, Nonce};
|
||||
false ->
|
||||
timer:sleep(?POLL_WAIT_TIME),
|
||||
get_authz_until_valid({CAUrl, AuthzId}, N-1)
|
||||
end;
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
-spec is_authz_valid(proplist()) -> boolean().
|
||||
is_authz_valid(Authz) ->
|
||||
case proplists:lookup(<<"status">>, Authz) of
|
||||
{<<"status">>, <<"valid">>} ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%
|
||||
%% Request Functions
|
||||
%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
%% TODO: Fix the duplicated code at the below 4 functions
|
||||
-spec make_post_request(url(), bitstring(), string()) ->
|
||||
{ok, proplist(), proplist()} | {error, _}.
|
||||
make_post_request(Url, ReqBody, ResponseType) ->
|
||||
Options = [],
|
||||
HttpOptions = [{timeout, ?REQUEST_TIMEOUT}],
|
||||
case httpc:request(post,
|
||||
{Url, [], "application/jose+json", ReqBody}, HttpOptions, Options) of
|
||||
{ok, {{_, Code, _}, Head, Body}} when Code >= 200, Code =< 299 ->
|
||||
decode_response(Head, Body, ResponseType);
|
||||
Error ->
|
||||
failed_http_request(Error, Url)
|
||||
end.
|
||||
|
||||
-spec make_get_request(url(), string()) ->
|
||||
{ok, proplist(), proplist()} | {error, _}.
|
||||
make_get_request(Url, ResponseType) ->
|
||||
Options = [],
|
||||
HttpOptions = [{timeout, ?REQUEST_TIMEOUT}],
|
||||
case httpc:request(get, {Url, []}, HttpOptions, Options) of
|
||||
{ok, {{_, Code, _}, Head, Body}} when Code >= 200, Code =< 299 ->
|
||||
decode_response(Head, Body, ResponseType);
|
||||
Error ->
|
||||
failed_http_request(Error, Url)
|
||||
end.
|
||||
|
||||
-spec prepare_post_request(url(), jose_jwk:key(), jiffy:json_value(),
|
||||
nonce(), handle_resp_fun()) -> {ok, _, nonce()} | {error, _}.
|
||||
prepare_post_request(Url, PrivateKey, EJson, Nonce, HandleRespFun) ->
|
||||
prepare_post_request(Url, PrivateKey, EJson, Nonce, HandleRespFun, "application/jose+json").
|
||||
|
||||
-spec prepare_post_request(url(), jose_jwk:key(), jiffy:json_value(),
|
||||
nonce(), handle_resp_fun(), string()) -> {ok, _, nonce()} | {error, _}.
|
||||
prepare_post_request(Url, PrivateKey, EJson, Nonce, HandleRespFun, ResponseType) ->
|
||||
case encode(EJson) of
|
||||
{ok, ReqBody} ->
|
||||
FinalBody = sign_encode_json_jose(PrivateKey, ReqBody, Nonce),
|
||||
case make_post_request(Url, FinalBody, ResponseType) of
|
||||
{ok, Head, Return} ->
|
||||
HandleRespFun({ok, Head, Return});
|
||||
Error ->
|
||||
Error
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Error: ~p when encoding: ~p", [Reason, EJson]),
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
-spec prepare_get_request(url(), handle_resp_fun()) ->
|
||||
{ok, _, nonce()} | {error, _}.
|
||||
prepare_get_request(Url, HandleRespFun) ->
|
||||
prepare_get_request(Url, HandleRespFun, "application/jose+json").
|
||||
|
||||
-spec prepare_get_request(url(), handle_resp_fun(), string()) ->
|
||||
{ok, _, nonce()} | {error, _}.
|
||||
prepare_get_request(Url, HandleRespFun, ResponseType) ->
|
||||
case make_get_request(Url, ResponseType) of
|
||||
{ok, Head, Return} ->
|
||||
HandleRespFun({ok, Head, Return});
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%
|
||||
%% Jose Json Functions
|
||||
%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
-spec sign_json_jose(jose_jwk:key(), bitstring(), nonce()) -> {_, jws()}.
|
||||
sign_json_jose(Key, Json, Nonce) ->
|
||||
PubKey = ejabberd_acme:to_public(Key),
|
||||
{_, BinaryPubKey} = jose_jwk:to_binary(PubKey),
|
||||
PubKeyJson = jiffy:decode(BinaryPubKey),
|
||||
%% TODO: Ensure this works for all cases
|
||||
AlgMap = jose_jwk:signer(Key),
|
||||
JwsMap =
|
||||
#{ <<"jwk">> => PubKeyJson,
|
||||
%% <<"b64">> => true,
|
||||
<<"nonce">> => list_to_bitstring(Nonce)
|
||||
},
|
||||
JwsObj0 = maps:merge(JwsMap, AlgMap),
|
||||
JwsObj = jose_jws:from(JwsObj0),
|
||||
jose_jws:sign(Key, Json, JwsObj).
|
||||
|
||||
-spec sign_encode_json_jose(jose_jwk:key(), bitstring(), nonce()) -> bitstring().
|
||||
sign_encode_json_jose(Key, Json, Nonce) ->
|
||||
{_, Signed} = sign_json_jose(Key, Json, Nonce),
|
||||
%% This depends on jose library, so we can consider it safe
|
||||
jiffy:encode(Signed).
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%
|
||||
%% Useful funs
|
||||
%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
-spec get_nonce(proplist()) -> nonce() | 'none'.
|
||||
get_nonce(Head) ->
|
||||
case proplists:lookup("replay-nonce", Head) of
|
||||
{"replay-nonce", Nonce} -> Nonce;
|
||||
none -> none
|
||||
end.
|
||||
|
||||
-spec get_location(proplist()) -> url() | 'none'.
|
||||
get_location(Head) ->
|
||||
case proplists:lookup("location", Head) of
|
||||
{"location", Location} -> Location;
|
||||
none -> none
|
||||
end.
|
||||
|
||||
-spec get_tos(proplist()) -> url() | 'none'.
|
||||
get_tos(Head) ->
|
||||
get_header_link(Head, "\"terms-of-service\"").
|
||||
|
||||
-spec get_link_up(proplist()) -> url() | 'none'.
|
||||
get_link_up(Head) ->
|
||||
get_header_link(Head, "rel=\"up\"").
|
||||
|
||||
%% TODO: Find a more reliable way to extract this
|
||||
-spec get_header_link(proplist(), string()) -> url() | 'none'.
|
||||
get_header_link(Head, Suffix) ->
|
||||
try
|
||||
[{_, Link}] = [{K, V} || {K, V} <- Head,
|
||||
K =:= "link" andalso
|
||||
lists:suffix(Suffix, V)],
|
||||
[Link1, _] = string:tokens(Link, ";"),
|
||||
Link2 = string:strip(Link1, left, $<),
|
||||
string:strip(Link2, right, $>)
|
||||
catch
|
||||
_:_ ->
|
||||
none
|
||||
end.
|
||||
|
||||
decode_response(Head, Body, "application/pkix-cert") ->
|
||||
{ok, Head, Body};
|
||||
decode_response(Head, Body, "application/jose+json") ->
|
||||
case decode(Body) of
|
||||
{ok, Return} ->
|
||||
{ok, Head, Return};
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Problem decoding: ~s", [Body]),
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
encode(EJson) ->
|
||||
try
|
||||
{ok, jiffy:encode(EJson)}
|
||||
catch
|
||||
_:Reason ->
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
decode(Json) ->
|
||||
try
|
||||
{Result} = jiffy:decode(Json),
|
||||
{ok, Result}
|
||||
catch
|
||||
_:Reason ->
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%
|
||||
%% Handle Failed HTTP Requests
|
||||
%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
-spec failed_http_request({ok, _} | {error, _}, url()) -> {error, _}.
|
||||
failed_http_request({ok, {{_, Code, Reason}, _Head, Body}}, Url) ->
|
||||
?ERROR_MSG("Unexpected status code from <~s>: ~B, Body: ~s",
|
||||
[Url, Code, Body]),
|
||||
throw({error, {unexpected_code, Code, Reason}});
|
||||
failed_http_request({error, Reason}, Url) ->
|
||||
?ERROR_MSG("Error making a request to <~s>: ~p",
|
||||
[Url, Reason]),
|
||||
throw({error, Reason}).
|
||||
@@ -405,7 +405,7 @@ status() ->
|
||||
false ->
|
||||
{ejabberd_not_running, "ejabberd is not running in that node."};
|
||||
{value, {_, _, Version}} ->
|
||||
{ok, io_lib:format("ejabberd ~s is running in that node", [Version])}
|
||||
{ok, io_lib:format("ejabberd ~ts is running in that node", [Version])}
|
||||
end,
|
||||
{Is_running, String1 ++ String2}.
|
||||
|
||||
@@ -449,7 +449,7 @@ stop_kindly(DelaySeconds, AnnouncementTextString) ->
|
||||
SecondsDiff =
|
||||
calendar:datetime_to_gregorian_seconds({date(), time()})
|
||||
- TimestampStart,
|
||||
io:format("[~p/~p ~ps] ~s... ",
|
||||
io:format("[~p/~p ~ps] ~ts... ",
|
||||
[NumberThis, NumberLast, SecondsDiff, Desc]),
|
||||
Result = (catch apply(Mod, Func, Args)),
|
||||
io:format("~p~n", [Result]),
|
||||
@@ -460,7 +460,7 @@ stop_kindly(DelaySeconds, AnnouncementTextString) ->
|
||||
ok.
|
||||
|
||||
send_service_message_all_mucs(Subject, AnnouncementText) ->
|
||||
Message = str:format("~s~n~s", [Subject, AnnouncementText]),
|
||||
Message = str:format("~ts~n~ts", [Subject, AnnouncementText]),
|
||||
lists:foreach(
|
||||
fun(ServerHost) ->
|
||||
MUCHosts = gen_mod:get_module_opt_hosts(ServerHost, mod_muc),
|
||||
@@ -504,12 +504,12 @@ register(User, Host, Password) ->
|
||||
true ->
|
||||
case ejabberd_auth:try_register(User, Host, Password) of
|
||||
ok ->
|
||||
{ok, io_lib:format("User ~s@~s successfully registered", [User, Host])};
|
||||
{ok, io_lib:format("User ~ts@~ts successfully registered", [User, Host])};
|
||||
{error, exists} ->
|
||||
Msg = io_lib:format("User ~s@~s already registered", [User, Host]),
|
||||
Msg = io_lib:format("User ~ts@~ts already registered", [User, Host]),
|
||||
{error, conflict, 10090, Msg};
|
||||
{error, Reason} ->
|
||||
String = io_lib:format("Can't register user ~s@~s at node ~p: ~s",
|
||||
String = io_lib:format("Can't register user ~ts@~ts at node ~p: ~ts",
|
||||
[User, Host, node(),
|
||||
mod_register:format_error(Reason)]),
|
||||
{error, cannot_register, 10001, String}
|
||||
|
||||
@@ -59,7 +59,7 @@ start(normal, _Args) ->
|
||||
ejabberd_hooks:run(ejabberd_started, []),
|
||||
ejabberd:check_apps(),
|
||||
{T2, _} = statistics(wall_clock),
|
||||
?INFO_MSG("ejabberd ~s is started in the node ~p in ~.2fs",
|
||||
?INFO_MSG("ejabberd ~ts is started in the node ~p in ~.2fs",
|
||||
[ejabberd_option:version(),
|
||||
node(), (T2-T1)/1000]),
|
||||
{ok, SupPid};
|
||||
@@ -68,7 +68,7 @@ start(normal, _Args) ->
|
||||
ejabberd:halt()
|
||||
end;
|
||||
Err ->
|
||||
?CRITICAL_MSG("Failed to start ejabberd application: ~s",
|
||||
?CRITICAL_MSG("Failed to start ejabberd application: ~ts",
|
||||
[ejabberd_config:format_error(Err)]),
|
||||
ejabberd:halt()
|
||||
end
|
||||
@@ -106,7 +106,7 @@ prep_stop(State) ->
|
||||
|
||||
%% All the processes were killed when this function is called
|
||||
stop(_State) ->
|
||||
?INFO_MSG("ejabberd ~s is stopped in the node ~p",
|
||||
?INFO_MSG("ejabberd ~ts is stopped in the node ~p",
|
||||
[ejabberd_option:version(), node()]),
|
||||
delete_pid_file().
|
||||
|
||||
@@ -139,11 +139,11 @@ write_pid_file() ->
|
||||
end.
|
||||
|
||||
write_pid_file(Pid, PidFilename) ->
|
||||
case file:write_file(PidFilename, io_lib:format("~s~n", [Pid])) of
|
||||
case file:write_file(PidFilename, io_lib:format("~ts~n", [Pid])) of
|
||||
ok ->
|
||||
ok;
|
||||
{error, Reason} = Err ->
|
||||
?CRITICAL_MSG("Cannot write PID file ~s: ~s",
|
||||
?CRITICAL_MSG("Cannot write PID file ~ts: ~ts",
|
||||
[PidFilename, file:format_error(Reason)]),
|
||||
throw({?MODULE, Err})
|
||||
end.
|
||||
|
||||
+25
-13
@@ -76,7 +76,7 @@
|
||||
{ets_cache:tag(), {ok, password()} | {error, db_failure | not_allowed}}.
|
||||
-callback remove_user(binary(), binary()) -> ok | {error, db_failure | not_allowed}.
|
||||
-callback user_exists(binary(), binary()) -> {ets_cache:tag(), boolean() | {error, db_failure}}.
|
||||
-callback check_password(binary(), binary(), binary(), binary()) -> {ets_cache:tag(), boolean()}.
|
||||
-callback check_password(binary(), binary(), binary(), binary()) -> {ets_cache:tag(), boolean() | {stop, boolean()}}.
|
||||
-callback try_register(binary(), binary(), password()) ->
|
||||
{ets_cache:tag(), {ok, password()} | {error, exists | db_failure | not_allowed}}.
|
||||
-callback get_users(binary(), opts()) -> [{binary(), binary()}].
|
||||
@@ -237,17 +237,20 @@ check_password_with_authmodule(User, AuthzId, Server, Password, Digest, DigestGe
|
||||
error ->
|
||||
false;
|
||||
LAuthzId ->
|
||||
lists:foldl(
|
||||
fun(Mod, false) ->
|
||||
case db_check_password(
|
||||
LUser, LAuthzId, LServer, Password,
|
||||
Digest, DigestGen, Mod) of
|
||||
true -> {true, Mod};
|
||||
false -> false
|
||||
end;
|
||||
(_, Acc) ->
|
||||
Acc
|
||||
end, false, auth_modules(LServer))
|
||||
untag_stop(
|
||||
lists:foldl(
|
||||
fun(Mod, false) ->
|
||||
case db_check_password(
|
||||
LUser, LAuthzId, LServer, Password,
|
||||
Digest, DigestGen, Mod) of
|
||||
true -> {true, Mod};
|
||||
false -> false;
|
||||
{stop, true} -> {stop, {true, Mod}};
|
||||
{stop, false} -> {stop, false}
|
||||
end;
|
||||
(_, Acc) ->
|
||||
Acc
|
||||
end, false, auth_modules(LServer)))
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
@@ -484,7 +487,11 @@ remove_user(User, Server, Password) ->
|
||||
<<"">>, undefined, Mod) of
|
||||
true ->
|
||||
db_remove_user(LUser, LServer, Mod);
|
||||
{stop, true} ->
|
||||
db_remove_user(LUser, LServer, Mod);
|
||||
false ->
|
||||
{error, not_allowed};
|
||||
{stop, false} ->
|
||||
{error, not_allowed}
|
||||
end
|
||||
end, {error, not_allowed}, auth_modules(Server)) of
|
||||
@@ -654,7 +661,9 @@ db_check_password(User, AuthzId, Server, ProvidedPassword,
|
||||
case Mod:check_password(
|
||||
User, AuthzId, Server, ProvidedPassword) of
|
||||
{CacheTag, true} -> {CacheTag, {ok, ProvidedPassword}};
|
||||
{CacheTag, false} -> {CacheTag, error}
|
||||
{CacheTag, {stop, true}} -> {CacheTag, {ok, ProvidedPassword}};
|
||||
{CacheTag, false} -> {CacheTag, error};
|
||||
{CacheTag, {stop, false}} -> {CacheTag, error}
|
||||
end
|
||||
end) of
|
||||
{ok, _} ->
|
||||
@@ -891,6 +900,9 @@ validate_credentials(User, Server, Password) ->
|
||||
end
|
||||
end.
|
||||
|
||||
untag_stop({stop, Val}) -> Val;
|
||||
untag_stop(Val) -> Val.
|
||||
|
||||
import_info() ->
|
||||
[{<<"users">>, 3}].
|
||||
|
||||
|
||||
@@ -101,5 +101,5 @@ check_password_extauth(User, _AuthzId, Server, Password) ->
|
||||
-spec failure(binary(), binary(), atom(), any()) -> {nocache, {error, db_failure}}.
|
||||
failure(User, Server, Fun, Reason) ->
|
||||
?ERROR_MSG("External authentication program failed when calling "
|
||||
"'~s' for ~s@~s: ~p", [Fun, User, Server, Reason]),
|
||||
"'~ts' for ~ts@~ts: ~p", [Fun, User, Server, Reason]),
|
||||
{nocache, {error, db_failure}}.
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
-behaviour(ejabberd_auth).
|
||||
|
||||
-export([start/1, stop/1, check_password/4,
|
||||
store_type/1, plain_password_required/1
|
||||
store_type/1, plain_password_required/1,
|
||||
user_exists/2, use_cache/1
|
||||
]).
|
||||
|
||||
-include("xmpp.hrl").
|
||||
@@ -42,7 +43,7 @@
|
||||
start(Host) ->
|
||||
case ejabberd_option:jwt_key(Host) of
|
||||
undefined ->
|
||||
?ERROR_MSG("Option jwt_key is not configured for ~s: "
|
||||
?ERROR_MSG("Option jwt_key is not configured for ~ts: "
|
||||
"JWT authentication won't work", [Host]);
|
||||
_ ->
|
||||
ok
|
||||
@@ -54,7 +55,7 @@ plain_password_required(_Host) -> true.
|
||||
|
||||
store_type(_Host) -> external.
|
||||
|
||||
-spec check_password(binary(), binary(), binary(), binary()) -> {ets_cache:tag(), boolean()}.
|
||||
-spec check_password(binary(), binary(), binary(), binary()) -> {ets_cache:tag(), boolean() | {stop, boolean()}}.
|
||||
check_password(User, AuthzId, Server, Token) ->
|
||||
%% MREMOND: Should we move the AuthzId check at a higher level in
|
||||
%% the call stack?
|
||||
@@ -63,10 +64,23 @@ check_password(User, AuthzId, Server, Token) ->
|
||||
true ->
|
||||
if Token == <<"">> -> {nocache, false};
|
||||
true ->
|
||||
{nocache, check_jwt_token(User, Server, Token)}
|
||||
Res = check_jwt_token(User, Server, Token),
|
||||
Rule = ejabberd_option:jwt_auth_only_rule(Server),
|
||||
case acl:match_rule(Server, Rule,
|
||||
jid:make(User, Server, <<"">>)) of
|
||||
deny ->
|
||||
{nocache, Res};
|
||||
allow ->
|
||||
{nocache, {stop, Res}}
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
user_exists(_User, _Host) -> {nocache, false}.
|
||||
|
||||
use_cache(_) ->
|
||||
false.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
@@ -251,7 +251,7 @@ transform(#passwd{us = {U, S}, password = Password} = P)
|
||||
scram ->
|
||||
case jid:resourceprep(Password) of
|
||||
error ->
|
||||
?ERROR_MSG("SASLprep failed for password of user ~s@~s",
|
||||
?ERROR_MSG("SASLprep failed for password of user ~ts@~ts",
|
||||
[U, S]),
|
||||
P;
|
||||
_ ->
|
||||
|
||||
@@ -293,7 +293,7 @@ convert_to_scram(Server) ->
|
||||
error ->
|
||||
?ERROR_MSG(
|
||||
"SASLprep failed for "
|
||||
"password of user ~s@~s",
|
||||
"password of user ~ts@~ts",
|
||||
[LUser, LServer]);
|
||||
_ ->
|
||||
Scram = ejabberd_auth:password_to_scram(Password),
|
||||
|
||||
@@ -518,7 +518,7 @@ handle_event({change_shaper, Shaper}, StateName,
|
||||
State) ->
|
||||
{next_state, StateName, State#state{shaper_state = Shaper}};
|
||||
handle_event(_Event, StateName, State) ->
|
||||
?ERROR_MSG("Unexpected event in '~s': ~p",
|
||||
?ERROR_MSG("Unexpected event in '~ts': ~p",
|
||||
[StateName, _Event]),
|
||||
{next_state, StateName, State}.
|
||||
|
||||
@@ -558,7 +558,7 @@ handle_sync_event(deactivate_socket, _From, StateName,
|
||||
{reply, ok, StateName,
|
||||
StateData#state{c2s_pid = undefined}};
|
||||
handle_sync_event(_Event, _From, StateName, State) ->
|
||||
?ERROR_MSG("Unexpected sync event in '~s': ~p",
|
||||
?ERROR_MSG("Unexpected sync event in '~ts': ~p",
|
||||
[StateName, _Event]),
|
||||
{reply, {error, badarg}, StateName, State}.
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
%% API
|
||||
-export([get_presence/1, set_presence/2, resend_presence/1, resend_presence/2,
|
||||
open_session/1, call/3, cast/2, send/2, close/1, close/2, stop/1,
|
||||
reply/2, copy_state/2, set_timeout/2, route/2,
|
||||
reply/2, copy_state/2, set_timeout/2, route/2, format_reason/2,
|
||||
host_up/1, host_down/1, send_ws_ping/1, bounce_message_queue/2]).
|
||||
|
||||
-include("xmpp.hrl").
|
||||
@@ -268,7 +268,7 @@ reject_unauthenticated_packet(State, _Pkt) ->
|
||||
process_auth_result(#{sasl_mech := Mech, auth_module := AuthModule,
|
||||
socket := Socket, ip := IP, lserver := LServer} = State,
|
||||
true, User) ->
|
||||
?INFO_MSG("(~s) Accepted c2s ~s authentication for ~s@~s by ~s backend from ~s",
|
||||
?INFO_MSG("(~ts) Accepted c2s ~ts authentication for ~ts@~ts by ~ts backend from ~ts",
|
||||
[xmpp_socket:pp(Socket), Mech, User, LServer,
|
||||
ejabberd_auth:backend_type(AuthModule),
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
|
||||
@@ -276,7 +276,7 @@ process_auth_result(#{sasl_mech := Mech, auth_module := AuthModule,
|
||||
process_auth_result(#{sasl_mech := Mech,
|
||||
socket := Socket, ip := IP, lserver := LServer} = State,
|
||||
{false, Reason}, User) ->
|
||||
?WARNING_MSG("(~s) Failed c2s ~s authentication ~sfrom ~s: ~s",
|
||||
?WARNING_MSG("(~ts) Failed c2s ~ts authentication ~tsfrom ~ts: ~ts",
|
||||
[xmpp_socket:pp(Socket), Mech,
|
||||
if User /= <<"">> -> ["for ", User, "@", LServer, " "];
|
||||
true -> ""
|
||||
@@ -291,7 +291,7 @@ process_terminated(#{sid := SID, socket := Socket,
|
||||
jid := JID, user := U, server := S, resource := R} = State,
|
||||
Reason) ->
|
||||
Status = format_reason(State, Reason),
|
||||
?INFO_MSG("(~s) Closing c2s session for ~s: ~s",
|
||||
?INFO_MSG("(~ts) Closing c2s session for ~ts: ~ts",
|
||||
[xmpp_socket:pp(Socket), jid:encode(JID), Status]),
|
||||
State1 = case maps:is_key(pres_last, State) of
|
||||
true ->
|
||||
@@ -309,7 +309,7 @@ process_terminated(#{sid := SID, socket := Socket,
|
||||
State1;
|
||||
process_terminated(#{socket := Socket,
|
||||
stop_reason := {tls, _}} = State, Reason) ->
|
||||
?WARNING_MSG("(~s) Failed to secure c2s connection: ~s",
|
||||
?WARNING_MSG("(~ts) Failed to secure c2s connection: ~ts",
|
||||
[xmpp_socket:pp(Socket), format_reason(State, Reason)]),
|
||||
State;
|
||||
process_terminated(State, _Reason) ->
|
||||
@@ -431,12 +431,12 @@ bind(R, #{user := U, server := S, access := Access, lang := Lang,
|
||||
sid => ejabberd_sm:make_sid()}),
|
||||
State2 = ejabberd_hooks:run_fold(
|
||||
c2s_session_opened, LServer, State1, []),
|
||||
?INFO_MSG("(~s) Opened c2s session for ~s",
|
||||
?INFO_MSG("(~ts) Opened c2s session for ~ts",
|
||||
[xmpp_socket:pp(Socket), jid:encode(JID)]),
|
||||
{ok, State2};
|
||||
deny ->
|
||||
ejabberd_hooks:run(forbidden_session_hook, LServer, [JID]),
|
||||
?WARNING_MSG("(~s) Forbidden c2s session for ~s",
|
||||
?WARNING_MSG("(~ts) Forbidden c2s session for ~ts",
|
||||
[xmpp_socket:pp(Socket), jid:encode(JID)]),
|
||||
Txt = ?T("Access denied by service policy"),
|
||||
{error, xmpp:err_not_allowed(Txt, Lang), State}
|
||||
@@ -894,7 +894,7 @@ bounce_message_queue({_, Pid} = SID, JID) ->
|
||||
SIDs = ejabberd_sm:get_session_sids(U, S, R),
|
||||
case lists:member(SID, SIDs) of
|
||||
true ->
|
||||
?WARNING_MSG("The session for ~s@~s/~s is supposed to "
|
||||
?WARNING_MSG("The session for ~ts@~ts/~ts is supposed to "
|
||||
"be unregistered, but session identifier ~p "
|
||||
"still presents in the 'session' table",
|
||||
[U, S, R, Pid]);
|
||||
|
||||
@@ -94,8 +94,8 @@ create_captcha(SID, From, To, Lang, Limiter, Args) ->
|
||||
Lang, [challenge]),
|
||||
X = #xdata{type = form, fields = Fs},
|
||||
Captcha = #xcaptcha{xdata = X},
|
||||
BodyString = {?T("Your subscription request and/or messages to ~s have been blocked. "
|
||||
"To unblock your subscription request, visit ~s"), [JID, get_url(Id)]},
|
||||
BodyString = {?T("Your subscription request and/or messages to ~ts have been blocked. "
|
||||
"To unblock your subscription request, visit ~ts"), [JID, get_url(Id)]},
|
||||
Body = xmpp:mk_text(BodyString, Lang),
|
||||
OOB = #oob_x{url = get_url(Id)},
|
||||
Hint = #hint{type = 'no-store'},
|
||||
@@ -197,7 +197,7 @@ process_reply(#xdata{} = X) ->
|
||||
captcha_not_found -> {error, not_found}
|
||||
end
|
||||
catch _:{captcha_form, Why} ->
|
||||
?WARNING_MSG("Malformed CAPTCHA form: ~s",
|
||||
?WARNING_MSG("Malformed CAPTCHA form: ~ts",
|
||||
[captcha_form:format_error(Why)]),
|
||||
{error, malformed}
|
||||
end;
|
||||
@@ -392,7 +392,7 @@ create_image(Limiter, Key) ->
|
||||
{error, image_error()}.
|
||||
do_create_image(Key) ->
|
||||
FileName = get_prog_name(),
|
||||
Cmd = lists:flatten(io_lib:format("~s ~s", [FileName, Key])),
|
||||
Cmd = lists:flatten(io_lib:format("~ts ~ts", [FileName, Key])),
|
||||
case cmd(Cmd) of
|
||||
{ok,
|
||||
<<137, $P, $N, $G, $\r, $\n, 26, $\n, _/binary>> =
|
||||
@@ -404,18 +404,18 @@ do_create_image(Key) ->
|
||||
when X == $7; X == $9 ->
|
||||
{ok, <<"image/gif">>, Key, Img};
|
||||
{error, enodata = Reason} ->
|
||||
?ERROR_MSG("Failed to process output from \"~s\". "
|
||||
?ERROR_MSG("Failed to process output from \"~ts\". "
|
||||
"Maybe ImageMagick's Convert program "
|
||||
"is not installed.",
|
||||
[Cmd]),
|
||||
{error, Reason};
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Failed to process an output from \"~s\": ~p",
|
||||
?ERROR_MSG("Failed to process an output from \"~ts\": ~p",
|
||||
[Cmd, Reason]),
|
||||
{error, Reason};
|
||||
_ ->
|
||||
Reason = malformed_image,
|
||||
?ERROR_MSG("Failed to process an output from \"~s\": ~p",
|
||||
?ERROR_MSG("Failed to process an output from \"~ts\": ~p",
|
||||
[Cmd, Reason]),
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
@@ -201,10 +201,10 @@ handle_cast(Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({node_up, Node}, State) ->
|
||||
?INFO_MSG("Node ~s has joined", [Node]),
|
||||
?INFO_MSG("Node ~ts has joined", [Node]),
|
||||
{noreply, State};
|
||||
handle_info({node_down, Node}, State) ->
|
||||
?INFO_MSG("Node ~s has left", [Node]),
|
||||
?INFO_MSG("Node ~ts has left", [Node]),
|
||||
{noreply, State};
|
||||
handle_info(Info, State) ->
|
||||
?WARNING_MSG("Unexpected info: ~p", [Info]),
|
||||
|
||||
@@ -337,14 +337,14 @@ gen_calls(#ejabberd_commands{args_example=Values, args=ArgsDesc,
|
||||
end.
|
||||
|
||||
format_type({list, {_, {tuple, Els}}}) ->
|
||||
io_lib:format("[~s]", [format_type({tuple, Els})]);
|
||||
io_lib:format("[~ts]", [format_type({tuple, Els})]);
|
||||
format_type({list, El}) ->
|
||||
io_lib:format("[~s]", [format_type(El)]);
|
||||
io_lib:format("[~ts]", [format_type(El)]);
|
||||
format_type({tuple, Els}) ->
|
||||
Args = [format_type(El) || El <- Els],
|
||||
io_lib:format("{~s}", [string:join(Args, ", ")]);
|
||||
io_lib:format("{~ts}", [string:join(Args, ", ")]);
|
||||
format_type({Name, Type}) ->
|
||||
io_lib:format("~s::~s", [Name, format_type(Type)]);
|
||||
io_lib:format("~ts::~ts", [Name, format_type(Type)]);
|
||||
format_type(binary) ->
|
||||
"string";
|
||||
format_type(atom) ->
|
||||
@@ -437,7 +437,7 @@ generate_html_output(File, RegExp, Languages) ->
|
||||
Langs = binary:split(Languages, <<",">>, [global]),
|
||||
Out = lists:map(fun(C) -> gen_doc(C, true, Langs) end, Cmds4),
|
||||
{ok, Fh} = file:open(File, [write]),
|
||||
io:format(Fh, "~s", [[html_pre(), Out, html_post()]]),
|
||||
io:format(Fh, "~ts", [[html_pre(), Out, html_post()]]),
|
||||
file:close(Fh),
|
||||
ok.
|
||||
|
||||
@@ -463,7 +463,7 @@ generate_md_output(File, RegExp, Languages) ->
|
||||
"// Autogenerated with 'ejabberdctl gen_markdown_doc_for_commands'\n---">>,
|
||||
Out = lists:map(fun(C) -> gen_doc(C, false, Langs) end, Cmds4),
|
||||
{ok, Fh} = file:open(File, [write]),
|
||||
io:format(Fh, "~s~s", [Header, Out]),
|
||||
io:format(Fh, "~ts~ts", [Header, Out]),
|
||||
file:close(Fh),
|
||||
ok.
|
||||
|
||||
|
||||
+18
-18
@@ -77,7 +77,7 @@ load() ->
|
||||
load(Path) ->
|
||||
ConfigFile = unicode:characters_to_binary(Path),
|
||||
UnixTime = erlang:monotonic_time(second),
|
||||
?INFO_MSG("Loading configuration from ~s", [ConfigFile]),
|
||||
?INFO_MSG("Loading configuration from ~ts", [ConfigFile]),
|
||||
_ = ets:new(ejabberd_options,
|
||||
[named_table, public, {read_concurrency, true}]),
|
||||
case load_file(ConfigFile) of
|
||||
@@ -92,7 +92,7 @@ load(Path) ->
|
||||
-spec reload() -> ok | error_return().
|
||||
reload() ->
|
||||
ConfigFile = path(),
|
||||
?INFO_MSG("Reloading configuration from ~s", [ConfigFile]),
|
||||
?INFO_MSG("Reloading configuration from ~ts", [ConfigFile]),
|
||||
OldHosts = get_myhosts(),
|
||||
case load_file(ConfigFile) of
|
||||
ok ->
|
||||
@@ -111,7 +111,7 @@ reload() ->
|
||||
delete_host_options(DelHosts),
|
||||
?INFO_MSG("Configuration reloaded successfully", []);
|
||||
Err ->
|
||||
?ERROR_MSG("Configuration reload aborted: ~s",
|
||||
?ERROR_MSG("Configuration reload aborted: ~ts",
|
||||
[format_error(Err)]),
|
||||
Err
|
||||
end.
|
||||
@@ -130,7 +130,7 @@ dump(Y, Output) ->
|
||||
Data = fast_yaml:encode(Y),
|
||||
case Output of
|
||||
stdout ->
|
||||
io:format("~s~n", [Data]);
|
||||
io:format("~ts~n", [Data]);
|
||||
FileName ->
|
||||
try
|
||||
ok = filelib:ensure_dir(FileName),
|
||||
@@ -158,10 +158,10 @@ get_option({O, Host} = Opt) ->
|
||||
catch ?EX_RULE(error, badarg, St) when Host /= global ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
Val = get_option({O, global}),
|
||||
?WARNING_MSG("Option '~s' is not defined for virtual host '~s'. "
|
||||
"This is a bug, please report it with the following "
|
||||
"stacktrace included:~n** ~s",
|
||||
[O, Host, misc:format_exception(2, error, badarg, StackTrace)]),
|
||||
?DEBUG("Option '~ts' is not defined for virtual host '~ts'. "
|
||||
"This is a bug, please report it with the following "
|
||||
"stacktrace included:~n** ~ts",
|
||||
[O, Host, misc:format_exception(2, error, badarg, StackTrace)]),
|
||||
Val
|
||||
end.
|
||||
|
||||
@@ -271,9 +271,9 @@ default_db(Opt, Host, Mod, Default) ->
|
||||
case code:ensure_loaded(DBMod) of
|
||||
{module, _} -> Type;
|
||||
{error, _} ->
|
||||
?WARNING_MSG("Module ~s doesn't support database '~s' "
|
||||
"defined in option '~s', using "
|
||||
"'~s' as fallback", [Mod, Type, Opt, Default]),
|
||||
?WARNING_MSG("Module ~ts doesn't support database '~ts' "
|
||||
"defined in option '~ts', using "
|
||||
"'~ts' as fallback", [Mod, Type, Opt, Default]),
|
||||
Default
|
||||
end.
|
||||
|
||||
@@ -355,15 +355,15 @@ format_error({error, Reason, Ctx}) ->
|
||||
format_error({error, {merge_conflict, Opt, Host}}) ->
|
||||
lists:flatten(
|
||||
io_lib:format(
|
||||
"Cannot merge value of option '~s' defined in append_host_config "
|
||||
"for virtual host ~s: only options of type list or map are allowed "
|
||||
"Cannot merge value of option '~ts' defined in append_host_config "
|
||||
"for virtual host ~ts: only options of type list or map are allowed "
|
||||
"in append_host_config. Hint: specify the option in host_config",
|
||||
[Opt, Host]));
|
||||
format_error({error, {old_config, Path, Reason}}) ->
|
||||
lists:flatten(
|
||||
io_lib:format(
|
||||
"Failed to read configuration from '~s': ~s~s",
|
||||
[unicode:characters_to_binary(Path),
|
||||
"Failed to read configuration from '~ts': ~ts~ts",
|
||||
[Path,
|
||||
case Reason of
|
||||
{_, _, _} -> "at line ";
|
||||
_ -> ""
|
||||
@@ -371,8 +371,8 @@ format_error({error, {old_config, Path, Reason}}) ->
|
||||
format_error({error, {write_file, Path, Reason}}) ->
|
||||
lists:flatten(
|
||||
io_lib:format(
|
||||
"Failed to write to '~s': ~s",
|
||||
[unicode:characters_to_binary(Path),
|
||||
"Failed to write to '~ts': ~ts",
|
||||
[Path,
|
||||
file:format_error(Reason)]));
|
||||
format_error({error, {exception, Class, Reason, St}}) ->
|
||||
lists:flatten(
|
||||
@@ -381,7 +381,7 @@ format_error({error, {exception, Class, Reason, St}}) ->
|
||||
"This is most likely due to faulty/incompatible validator in "
|
||||
"third-party code. If you are not running any third-party "
|
||||
"code, please report the bug with ejabberd configuration "
|
||||
"file attached and the following stacktrace included:~n** ~s",
|
||||
"file attached and the following stacktrace included:~n** ~ts",
|
||||
[misc:format_exception(2, Class, Reason, St)])).
|
||||
|
||||
%%%===================================================================
|
||||
|
||||
@@ -31,7 +31,7 @@ map_reduce(Y) ->
|
||||
Y2 = (validator())(Y1),
|
||||
Y3 = transform(Y2),
|
||||
if Y2 /= Y3 ->
|
||||
?DEBUG("Transformed configuration:~s~n",
|
||||
?DEBUG("Transformed configuration:~ts~n",
|
||||
[misc:format_val({yaml, Y3})]);
|
||||
true ->
|
||||
ok
|
||||
@@ -89,8 +89,8 @@ transform(global, listen, Listeners, Acc) ->
|
||||
transform(_Host, Opt, CertFile, Acc) when (Opt == domain_certfile) orelse
|
||||
(Opt == c2s_certfile) orelse
|
||||
(Opt == s2s_certfile) ->
|
||||
?WARNING_MSG("Option '~s' is deprecated and was automatically "
|
||||
"appended to 'certfiles' option. ~s",
|
||||
?WARNING_MSG("Option '~ts' is deprecated and was automatically "
|
||||
"appended to 'certfiles' option. ~ts",
|
||||
[Opt, adjust_hint()]),
|
||||
CertFiles = maps:get(certfiles, Acc, []),
|
||||
Acc1 = maps:put(certfiles, CertFiles ++ [CertFile], Acc),
|
||||
@@ -99,12 +99,31 @@ transform(_Host, certfiles, CertFiles1, Acc) ->
|
||||
CertFiles2 = maps:get(certfiles, Acc, []),
|
||||
Acc1 = maps:put(certfiles, CertFiles1 ++ CertFiles2, Acc),
|
||||
{true, Acc1};
|
||||
transform(_Host, acme, ACME, Acc) ->
|
||||
ACME1 = lists:map(
|
||||
fun({ca_url, URL} = Opt) ->
|
||||
case http_uri:parse(binary_to_list(URL)) of
|
||||
{ok, {_, _, "acme-v01.api.letsencrypt.org", _, _, _}} ->
|
||||
NewURL = ejabberd_acme:default_directory_url(),
|
||||
?WARNING_MSG("ACME directory URL ~ts defined in "
|
||||
"option acme->ca_url is deprecated "
|
||||
"and was automatically replaced "
|
||||
"with ~ts. ~ts",
|
||||
[URL, NewURL, adjust_hint()]),
|
||||
{ca_url, NewURL};
|
||||
_ ->
|
||||
Opt
|
||||
end;
|
||||
(Opt) ->
|
||||
Opt
|
||||
end, ACME),
|
||||
{{true, {acme, ACME1}}, Acc};
|
||||
transform(Host, s2s_use_starttls, required_trusted, Acc) ->
|
||||
?WARNING_MSG("The value 'required_trusted' of option "
|
||||
"'s2s_use_starttls' is deprecated and was "
|
||||
"automatically replaced with value 'required'. "
|
||||
"The module 'mod_s2s_dialback' has also "
|
||||
"been automatically removed from the configuration. ~s",
|
||||
"been automatically removed from the configuration. ~ts",
|
||||
[adjust_hint()]),
|
||||
Hosts = maps:get(remove_s2s_dialback, Acc, []),
|
||||
Acc1 = maps:put(remove_s2s_dialback, [Host|Hosts], Acc),
|
||||
@@ -276,7 +295,7 @@ replace_request_handlers(Opts) ->
|
||||
({http_poll, _}) ->
|
||||
?WARNING_MSG("Listening option 'http_poll' is "
|
||||
"ignored: HTTP Polling support was "
|
||||
"removed in ejabberd 15.04. ~s",
|
||||
"removed in ejabberd 15.04. ~ts",
|
||||
[adjust_hint()]),
|
||||
false;
|
||||
({request_handlers, _}) ->
|
||||
@@ -313,9 +332,9 @@ collect_listener_certfiles(Opts, Acc) ->
|
||||
Mod == ejabberd_s2s_in ->
|
||||
case lists:keyfind(certfile, 1, Opts) of
|
||||
{_, CertFile} ->
|
||||
?WARNING_MSG("Listening option 'certfile' of module ~s "
|
||||
?WARNING_MSG("Listening option 'certfile' of module ~ts "
|
||||
"is deprecated and was automatically "
|
||||
"appended to global 'certfiles' option. ~s",
|
||||
"appended to global 'certfiles' option. ~ts",
|
||||
[Mod, adjust_hint()]),
|
||||
CertFiles = maps:get(certfiles, Acc, []),
|
||||
{proplists:delete(certfile, Opts),
|
||||
@@ -439,10 +458,10 @@ transform_module(_Host, mod_pubsub, Opts, Acc) ->
|
||||
<<"public">>]) of
|
||||
true ->
|
||||
?WARNING_MSG(
|
||||
"Plugin '~s' of mod_pubsub is not "
|
||||
"Plugin '~ts' of mod_pubsub is not "
|
||||
"supported anymore and has been "
|
||||
"automatically removed from 'plugins' "
|
||||
"option. ~s",
|
||||
"option. ~ts",
|
||||
[Plugin, adjust_hint()]),
|
||||
false;
|
||||
false ->
|
||||
@@ -482,49 +501,49 @@ set_certfiles(Y, Acc) ->
|
||||
%%% Warnings
|
||||
%%%===================================================================
|
||||
warn_replaced_module(From, To) ->
|
||||
?WARNING_MSG("Module ~s is deprecated and was automatically "
|
||||
"replaced by ~s. ~s",
|
||||
?WARNING_MSG("Module ~ts is deprecated and was automatically "
|
||||
"replaced by ~ts. ~ts",
|
||||
[From, To, adjust_hint()]).
|
||||
|
||||
warn_replaced_module(From, To, Type) ->
|
||||
?WARNING_MSG("Module ~s is deprecated and was automatically "
|
||||
"replaced by ~s with db_type: ~s. ~s",
|
||||
?WARNING_MSG("Module ~ts is deprecated and was automatically "
|
||||
"replaced by ~ts with db_type: ~ts. ~ts",
|
||||
[From, To, Type, adjust_hint()]).
|
||||
|
||||
warn_removed_module(Mod) ->
|
||||
?WARNING_MSG("Module ~s is deprecated and was automatically "
|
||||
"removed from the configuration. ~s", [Mod, adjust_hint()]).
|
||||
?WARNING_MSG("Module ~ts is deprecated and was automatically "
|
||||
"removed from the configuration. ~ts", [Mod, adjust_hint()]).
|
||||
|
||||
warn_replaced_handler(Opt, {Path, Module}) ->
|
||||
?WARNING_MSG("Listening option '~s' is deprecated "
|
||||
?WARNING_MSG("Listening option '~ts' is deprecated "
|
||||
"and was automatically replaced by "
|
||||
"HTTP request handler: \"~s\" -> ~s. ~s",
|
||||
"HTTP request handler: \"~ts\" -> ~ts. ~ts",
|
||||
[Opt, Path, Module, adjust_hint()]).
|
||||
|
||||
warn_deprecated_option(OldOpt, NewOpt) ->
|
||||
?WARNING_MSG("Option '~s' is deprecated. Use option '~s' instead.",
|
||||
?WARNING_MSG("Option '~ts' is deprecated. Use option '~ts' instead.",
|
||||
[OldOpt, NewOpt]).
|
||||
|
||||
warn_replaced_option(OldOpt, NewOpt) ->
|
||||
?WARNING_MSG("Option '~s' is deprecated and was automatically "
|
||||
"replaced by '~s'. ~s",
|
||||
?WARNING_MSG("Option '~ts' is deprecated and was automatically "
|
||||
"replaced by '~ts'. ~ts",
|
||||
[OldOpt, NewOpt, adjust_hint()]).
|
||||
|
||||
warn_removed_option(Opt) ->
|
||||
?WARNING_MSG("Option '~s' is deprecated and has no effect anymore. "
|
||||
?WARNING_MSG("Option '~ts' is deprecated and has no effect anymore. "
|
||||
"Please remove it from the configuration.", [Opt]).
|
||||
|
||||
warn_removed_option(OldOpt, NewOpt) ->
|
||||
?WARNING_MSG("Option '~s' is deprecated and has no effect anymore. "
|
||||
"Use option '~s' instead.", [OldOpt, NewOpt]).
|
||||
?WARNING_MSG("Option '~ts' is deprecated and has no effect anymore. "
|
||||
"Use option '~ts' instead.", [OldOpt, NewOpt]).
|
||||
|
||||
warn_removed_module_option(Opt, Mod) ->
|
||||
?WARNING_MSG("Option '~s' of module ~s is deprecated "
|
||||
"and has no effect anymore. ~s",
|
||||
?WARNING_MSG("Option '~ts' of module ~ts is deprecated "
|
||||
"and has no effect anymore. ~ts",
|
||||
[Opt, Mod, adjust_hint()]).
|
||||
|
||||
warn_huge_timeout(Opt, T) when is_integer(T), T >= 1000 ->
|
||||
?WARNING_MSG("Value '~B' of option '~s' is too big, "
|
||||
?WARNING_MSG("Value '~B' of option '~ts' is too big, "
|
||||
"are you sure you have set seconds?",
|
||||
[T, Opt]);
|
||||
warn_huge_timeout(_, _) ->
|
||||
@@ -550,6 +569,10 @@ validator() ->
|
||||
default_db => econf:atom(),
|
||||
default_ram_db => econf:atom(),
|
||||
auth_method => econf:list_or_single(econf:atom()),
|
||||
acme => econf:options(
|
||||
#{ca_url => econf:binary(),
|
||||
'_' => econf:any()},
|
||||
[unique]),
|
||||
listen =>
|
||||
econf:list(
|
||||
econf:options(
|
||||
|
||||
+11
-11
@@ -173,11 +173,11 @@ process(["status"], _Version) ->
|
||||
false ->
|
||||
EjabberdLogPath = ejabberd_logger:get_log_path(),
|
||||
print("ejabberd is not running in that node~n"
|
||||
"Check for error messages: ~s~n"
|
||||
"Check for error messages: ~ts~n"
|
||||
"or other files in that directory.~n", [EjabberdLogPath]),
|
||||
?STATUS_ERROR;
|
||||
true ->
|
||||
print("ejabberd ~s is running in that node~n", [ejabberd_option:version()]),
|
||||
print("ejabberd ~ts is running in that node~n", [ejabberd_option:version()]),
|
||||
?STATUS_SUCCESS
|
||||
end;
|
||||
|
||||
@@ -252,7 +252,7 @@ process(Args, Version) ->
|
||||
case String of
|
||||
[] -> ok;
|
||||
_ ->
|
||||
io:format("~s~n", [String])
|
||||
io:format("~ts~n", [String])
|
||||
end,
|
||||
Code.
|
||||
|
||||
@@ -327,14 +327,14 @@ try_call_command(Args, Auth, AccessCommands, Version) ->
|
||||
KnownCommands = [Cmd || {Cmd, _, _} <- ejabberd_commands:list_commands(Version)],
|
||||
UnknownCommand = list_to_atom(hd(Args)),
|
||||
{io_lib:format(
|
||||
"Error: unknown command '~s'. Did you mean '~s'?",
|
||||
"Error: unknown command '~ts'. Did you mean '~ts'?",
|
||||
[hd(Args), misc:best_match(UnknownCommand, KnownCommands)]),
|
||||
?STATUS_ERROR};
|
||||
throw:Error ->
|
||||
{io_lib:format("~p", [Error]), ?STATUS_ERROR};
|
||||
?EX_RULE(A, Why, Stack) ->
|
||||
StackTrace = ?EX_STACK(Stack),
|
||||
{io_lib:format("Unhandled exception occurred executing the command:~n** ~s",
|
||||
{io_lib:format("Unhandled exception occurred executing the command:~n** ~ts",
|
||||
[misc:format_exception(2, A, Why, StackTrace)]),
|
||||
?STATUS_ERROR}
|
||||
end.
|
||||
@@ -363,7 +363,7 @@ call_command([CmdString | Args], Auth, _AccessCommands, Version) ->
|
||||
{L1, L2} when L1 < L2 -> {L2-L1, "less argument"};
|
||||
{L1, L2} when L1 > L2 -> {L1-L2, "more argument"}
|
||||
end,
|
||||
{io_lib:format("Error: the command ~p requires ~p ~s.",
|
||||
{io_lib:format("Error: the command ~p requires ~p ~ts.",
|
||||
[CmdString, NumCompa, TextCompa]),
|
||||
wrong_command_arguments}
|
||||
end.
|
||||
@@ -417,16 +417,16 @@ format_result(Int, {_Name, integer}) ->
|
||||
io_lib:format("~p", [Int]);
|
||||
|
||||
format_result([A|_]=String, {_Name, string}) when is_list(String) and is_integer(A) ->
|
||||
io_lib:format("~s", [String]);
|
||||
io_lib:format("~ts", [String]);
|
||||
|
||||
format_result(Binary, {_Name, string}) when is_binary(Binary) ->
|
||||
io_lib:format("~s", [binary_to_list(Binary)]);
|
||||
io_lib:format("~ts", [binary_to_list(Binary)]);
|
||||
|
||||
format_result(Atom, {_Name, string}) when is_atom(Atom) ->
|
||||
io_lib:format("~s", [atom_to_list(Atom)]);
|
||||
io_lib:format("~ts", [atom_to_list(Atom)]);
|
||||
|
||||
format_result(Integer, {_Name, string}) when is_integer(Integer) ->
|
||||
io_lib:format("~s", [integer_to_list(Integer)]);
|
||||
io_lib:format("~ts", [integer_to_list(Integer)]);
|
||||
|
||||
format_result(Other, {_Name, string}) ->
|
||||
io_lib:format("~p", [Other]);
|
||||
@@ -435,7 +435,7 @@ format_result(Code, {_Name, rescode}) ->
|
||||
make_status(Code);
|
||||
|
||||
format_result({Code, Text}, {_Name, restuple}) ->
|
||||
{io_lib:format("~s", [Text]), make_status(Code)};
|
||||
{io_lib:format("~ts", [Text]), make_status(Code)};
|
||||
|
||||
%% The result is a list of something: [something()]
|
||||
format_result([], {_Name, {list, _ElementsDef}}) ->
|
||||
|
||||
@@ -239,7 +239,7 @@ safe_apply(Hook, Module, Function, Args) ->
|
||||
Stack = ?EX_STACK(St),
|
||||
?ERROR_MSG("Hook ~p crashed when running ~p:~p/~p:~n" ++
|
||||
string:join(
|
||||
["** ~s"|
|
||||
["** ~ts"|
|
||||
["** Arg " ++ integer_to_list(I) ++ " = ~p"
|
||||
|| I <- lists:seq(1, length(Args))]],
|
||||
"~n"),
|
||||
|
||||
+31
-40
@@ -179,7 +179,7 @@ send_file(State, Fd, Size, FileName) ->
|
||||
end
|
||||
catch _:{case_clause, {error, Why}} ->
|
||||
if Why /= closed ->
|
||||
?WARNING_MSG("Failed to read ~s: ~s",
|
||||
?WARNING_MSG("Failed to read ~ts: ~ts",
|
||||
[FileName, file_format_error(Why)]),
|
||||
exit(normal);
|
||||
true ->
|
||||
@@ -280,37 +280,29 @@ process_header(State, Data) ->
|
||||
send_text(State1, Out),
|
||||
process_header(State, {ok, {http_error, <<>>}});
|
||||
{ok, http_eoh} ->
|
||||
?DEBUG("(~w) http query: ~w ~p~n",
|
||||
[State#state.socket, State#state.request_method,
|
||||
element(2, State#state.request_path)]),
|
||||
case ejabberd_router:is_my_route(State#state.request_host) of
|
||||
true ->
|
||||
{State3, Out} = process_request(State),
|
||||
send_text(State3, Out),
|
||||
case State3#state.request_keepalive of
|
||||
true ->
|
||||
#state{sockmod = SockMod, socket = Socket,
|
||||
trail = State3#state.trail,
|
||||
options = State#state.options,
|
||||
default_host = State#state.default_host,
|
||||
custom_headers = State#state.custom_headers,
|
||||
request_handlers = State#state.request_handlers,
|
||||
addr_re = State#state.addr_re};
|
||||
_ ->
|
||||
#state{end_of_request = true,
|
||||
trail = State3#state.trail,
|
||||
options = State#state.options,
|
||||
default_host = State#state.default_host,
|
||||
custom_headers = State#state.custom_headers,
|
||||
request_handlers = State#state.request_handlers,
|
||||
addr_re = State#state.addr_re}
|
||||
end;
|
||||
false ->
|
||||
Out = make_text_output(State, 400, State#state.custom_headers,
|
||||
<<"Host not served">>),
|
||||
send_text(State, Out),
|
||||
process_header(State, {ok, {http_error, <<>>}})
|
||||
end;
|
||||
?DEBUG("(~w) http query: ~w ~p~n",
|
||||
[State#state.socket, State#state.request_method,
|
||||
element(2, State#state.request_path)]),
|
||||
{State3, Out} = process_request(State),
|
||||
send_text(State3, Out),
|
||||
case State3#state.request_keepalive of
|
||||
true ->
|
||||
#state{sockmod = SockMod, socket = Socket,
|
||||
trail = State3#state.trail,
|
||||
options = State#state.options,
|
||||
default_host = State#state.default_host,
|
||||
custom_headers = State#state.custom_headers,
|
||||
request_handlers = State#state.request_handlers,
|
||||
addr_re = State#state.addr_re};
|
||||
_ ->
|
||||
#state{end_of_request = true,
|
||||
trail = State3#state.trail,
|
||||
options = State#state.options,
|
||||
default_host = State#state.default_host,
|
||||
custom_headers = State#state.custom_headers,
|
||||
request_handlers = State#state.request_handlers,
|
||||
addr_re = State#state.addr_re}
|
||||
end;
|
||||
_ ->
|
||||
#state{end_of_request = true,
|
||||
options = State#state.options,
|
||||
@@ -475,7 +467,7 @@ process_request(#state{request_method = Method,
|
||||
{error, _} = E -> throw(E)
|
||||
end,
|
||||
XFF = proplists:get_value('X-Forwarded-For', RequestHeaders, []),
|
||||
IP = analyze_ip_xff(IPHere, XFF, Host),
|
||||
IP = analyze_ip_xff(IPHere, XFF),
|
||||
Request = #request{method = Method,
|
||||
path = LPath,
|
||||
q = LQuery,
|
||||
@@ -526,12 +518,11 @@ make_bad_request(State) ->
|
||||
[{xmlcdata,
|
||||
<<"400 Bad Request">>}]}])).
|
||||
|
||||
analyze_ip_xff(IP, [], _Host) -> IP;
|
||||
analyze_ip_xff({IPLast, Port}, XFF, Host) ->
|
||||
analyze_ip_xff(IP, []) -> IP;
|
||||
analyze_ip_xff({IPLast, Port}, XFF) ->
|
||||
[ClientIP | ProxiesIPs] = str:tokens(XFF, <<", ">>) ++
|
||||
[misc:ip_to_list(IPLast)],
|
||||
ServerHost = ejabberd_router:host_of_route(Host),
|
||||
TrustedProxies = ejabberd_option:trusted_proxies(ServerHost),
|
||||
TrustedProxies = ejabberd_option:trusted_proxies(),
|
||||
IPClient = case is_ipchain_trusted(ProxiesIPs,
|
||||
TrustedProxies)
|
||||
of
|
||||
@@ -539,7 +530,7 @@ analyze_ip_xff({IPLast, Port}, XFF, Host) ->
|
||||
{ok, IPFirst} = inet_parse:address(
|
||||
binary_to_list(ClientIP)),
|
||||
?DEBUG("The IP ~w was replaced with ~w due to "
|
||||
"header X-Forwarded-For: ~s",
|
||||
"header X-Forwarded-For: ~ts",
|
||||
[IPLast, IPFirst, XFF]),
|
||||
IPFirst;
|
||||
false -> IPLast
|
||||
@@ -686,12 +677,12 @@ make_file_output(State, Status, Headers, FileName) ->
|
||||
none;
|
||||
{error, Why} ->
|
||||
Reason = file_format_error(Why),
|
||||
?ERROR_MSG("Failed to open ~s: ~s", [FileName, Reason]),
|
||||
?ERROR_MSG("Failed to open ~ts: ~ts", [FileName, Reason]),
|
||||
make_text_output(State, 404, Reason, [], <<>>)
|
||||
end;
|
||||
{error, Why} ->
|
||||
Reason = file_format_error(Why),
|
||||
?ERROR_MSG("Failed to read info of ~s: ~s", [FileName, Reason]),
|
||||
?ERROR_MSG("Failed to read info of ~ts: ~ts", [FileName, Reason]),
|
||||
make_text_output(State, 404, Reason, [], <<>>)
|
||||
end.
|
||||
|
||||
|
||||
+1
-1
@@ -176,7 +176,7 @@ callback(undefined, IQRes, Fun) ->
|
||||
try Fun(IQRes)
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to process iq response:~n~s~n** ~s",
|
||||
?ERROR_MSG("Failed to process iq response:~n~ts~n** ~ts",
|
||||
[xmpp:pp(IQRes),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
end;
|
||||
|
||||
@@ -115,7 +115,7 @@ init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) ->
|
||||
proc_lib:init_ack({ok, self()}),
|
||||
case application:ensure_started(ejabberd) of
|
||||
ok ->
|
||||
?INFO_MSG("Start accepting ~s connections at ~s for ~p",
|
||||
?INFO_MSG("Start accepting ~ts connections at ~ts for ~p",
|
||||
[format_transport(udp, Opts),
|
||||
format_endpoint({Port1, Addr, udp}), Module]),
|
||||
Opts1 = opts_to_list(Module, Opts),
|
||||
@@ -148,7 +148,7 @@ init({Port, _, tcp} = EndPoint, Module, Opts, SockOpts) ->
|
||||
Sup = start_module_sup(Module, Opts),
|
||||
Interval = maps:get(accept_interval, Opts),
|
||||
Proxy = maps:get(use_proxy_protocol, Opts),
|
||||
?INFO_MSG("Start accepting ~s connections at ~s for ~p",
|
||||
?INFO_MSG("Start accepting ~ts connections at ~ts for ~p",
|
||||
[format_transport(tcp, Opts),
|
||||
format_endpoint({Port1, Addr, tcp}), Module]),
|
||||
Opts1 = opts_to_list(Module, Opts),
|
||||
@@ -223,7 +223,7 @@ accept(ListenSocket, Module, State, Sup, Interval, Proxy, Arity) ->
|
||||
{ok, Socket} when Proxy ->
|
||||
case proxy_protocol:decode(gen_tcp, Socket, 10000) of
|
||||
{error, Err} ->
|
||||
?ERROR_MSG("(~w) Proxy protocol parsing failed: ~s",
|
||||
?ERROR_MSG("(~w) Proxy protocol parsing failed: ~ts",
|
||||
[ListenSocket, format_error(Err)]),
|
||||
gen_tcp:close(Socket);
|
||||
{{Addr, Port}, {PAddr, PPort}} = SP ->
|
||||
@@ -236,7 +236,7 @@ accept(ListenSocket, Module, State, Sup, Interval, Proxy, Arity) ->
|
||||
gen_tcp:close(Socket),
|
||||
none
|
||||
end,
|
||||
?INFO_MSG("(~p) Accepted proxied connection ~s -> ~s",
|
||||
?INFO_MSG("(~p) Accepted proxied connection ~ts -> ~ts",
|
||||
[Receiver,
|
||||
ejabberd_config:may_hide_data(
|
||||
format_endpoint({PPort, PAddr, tcp})),
|
||||
@@ -253,7 +253,7 @@ accept(ListenSocket, Module, State, Sup, Interval, Proxy, Arity) ->
|
||||
gen_tcp:close(Socket),
|
||||
none
|
||||
end,
|
||||
?INFO_MSG("(~p) Accepted connection ~s -> ~s",
|
||||
?INFO_MSG("(~p) Accepted connection ~ts -> ~ts",
|
||||
[Receiver,
|
||||
ejabberd_config:may_hide_data(
|
||||
format_endpoint({PPort, PAddr, tcp})),
|
||||
@@ -263,7 +263,7 @@ accept(ListenSocket, Module, State, Sup, Interval, Proxy, Arity) ->
|
||||
end,
|
||||
accept(ListenSocket, Module, State, Sup, NewInterval, Proxy, Arity);
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("(~w) Failed TCP accept: ~s",
|
||||
?ERROR_MSG("(~w) Failed TCP accept: ~ts",
|
||||
[ListenSocket, format_error(Reason)]),
|
||||
accept(ListenSocket, Module, State, Sup, NewInterval, Proxy, Arity)
|
||||
end.
|
||||
@@ -283,7 +283,7 @@ udp_recv(Socket, Module, State) ->
|
||||
udp_recv(Socket, Module, NewState)
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Unexpected UDP error: ~s", [format_error(Reason)]),
|
||||
?ERROR_MSG("Unexpected UDP error: ~ts", [format_error(Reason)]),
|
||||
throw({error, Reason})
|
||||
end.
|
||||
|
||||
@@ -376,7 +376,7 @@ stop_listeners() ->
|
||||
stop_listener({_, _, Transport} = EndPoint, Module, Opts) ->
|
||||
case supervisor:terminate_child(?MODULE, EndPoint) of
|
||||
ok ->
|
||||
?INFO_MSG("Stop accepting ~s connections at ~s for ~p",
|
||||
?INFO_MSG("Stop accepting ~ts connections at ~ts for ~p",
|
||||
[format_transport(Transport, Opts),
|
||||
format_endpoint(EndPoint), Module]),
|
||||
ets:delete(?MODULE, EndPoint),
|
||||
@@ -444,7 +444,7 @@ config_reloaded() ->
|
||||
|
||||
-spec report_socket_error(inet:posix(), endpoint(), module()) -> ok.
|
||||
report_socket_error(Reason, EndPoint, Module) ->
|
||||
?ERROR_MSG("Failed to open socket at ~s for ~s: ~s",
|
||||
?ERROR_MSG("Failed to open socket at ~ts for ~ts: ~ts",
|
||||
[format_endpoint(EndPoint), Module, format_error(Reason)]).
|
||||
|
||||
-spec format_error(inet:posix() | atom()) -> string().
|
||||
|
||||
@@ -71,7 +71,7 @@ start_link() ->
|
||||
|
||||
-spec route(stanza()) -> ok.
|
||||
route(Packet) ->
|
||||
?DEBUG("Local route:~n~s", [xmpp:pp(Packet)]),
|
||||
?DEBUG("Local route:~n~ts", [xmpp:pp(Packet)]),
|
||||
Type = xmpp:get_type(Packet),
|
||||
To = xmpp:get_to(Packet),
|
||||
if To#jid.luser /= <<"">> ->
|
||||
|
||||
@@ -71,7 +71,7 @@ get_integer_env(Name, Default) ->
|
||||
undefined ->
|
||||
Default;
|
||||
{ok, Junk} ->
|
||||
error_logger:error_msg("wrong value for ~s: ~p; "
|
||||
error_logger:error_msg("wrong value for ~ts: ~p; "
|
||||
"using ~p as a fallback~n",
|
||||
[Name, Junk, Default]),
|
||||
Default
|
||||
@@ -83,7 +83,7 @@ get_string_env(Name, Default) ->
|
||||
undefined ->
|
||||
Default;
|
||||
{ok, Junk} ->
|
||||
error_logger:error_msg("wrong value for ~s: ~p; "
|
||||
error_logger:error_msg("wrong value for ~ts: ~p; "
|
||||
"using ~p as a fallback~n",
|
||||
[Name, Junk, Default]),
|
||||
Default
|
||||
|
||||
+23
-17
@@ -81,7 +81,7 @@ init([]) ->
|
||||
Schema = read_schema_file(),
|
||||
{ok, #state{schema = Schema}};
|
||||
false ->
|
||||
?CRITICAL_MSG("Node name mismatch: I'm [~s], "
|
||||
?CRITICAL_MSG("Node name mismatch: I'm [~ts], "
|
||||
"the database is owned by ~p", [MyNode, DbNodes]),
|
||||
?CRITICAL_MSG("Either set ERLANG_NODE in ejabberdctl.cfg "
|
||||
"or change node name in Mnesia", []),
|
||||
@@ -139,7 +139,7 @@ do_create(Module, Name, TabDef, TabDefs) ->
|
||||
end.
|
||||
|
||||
reset(Name, TabDef) ->
|
||||
?INFO_MSG("Deleting Mnesia table '~s'", [Name]),
|
||||
?INFO_MSG("Deleting Mnesia table '~ts'", [Name]),
|
||||
mnesia_op(delete_table, [Name]),
|
||||
create(Name, TabDef).
|
||||
|
||||
@@ -170,7 +170,7 @@ change_table_copy_type(Name, TabDef) ->
|
||||
[] -> CurrType
|
||||
end,
|
||||
if NewType /= CurrType ->
|
||||
?INFO_MSG("Changing Mnesia table '~s' from ~s to ~s",
|
||||
?INFO_MSG("Changing Mnesia table '~ts' from ~ts to ~ts",
|
||||
[Name, CurrType, NewType]),
|
||||
mnesia_op(change_table_copy_type, [Name, node(), NewType]);
|
||||
true ->
|
||||
@@ -178,7 +178,7 @@ change_table_copy_type(Name, TabDef) ->
|
||||
end.
|
||||
|
||||
delete_indexes(Name, [Index|Indexes]) ->
|
||||
?INFO_MSG("Deleting index '~s' from Mnesia table '~s'", [Index, Name]),
|
||||
?INFO_MSG("Deleting index '~ts' from Mnesia table '~ts'", [Index, Name]),
|
||||
case mnesia_op(del_table_index, [Name, Index]) of
|
||||
{atomic, ok} ->
|
||||
delete_indexes(Name, Indexes);
|
||||
@@ -189,7 +189,7 @@ delete_indexes(_Name, []) ->
|
||||
{atomic, ok}.
|
||||
|
||||
add_indexes(Name, [Index|Indexes]) ->
|
||||
?INFO_MSG("Adding index '~s' to Mnesia table '~s'", [Index, Name]),
|
||||
?INFO_MSG("Adding index '~ts' to Mnesia table '~ts'", [Index, Name]),
|
||||
case mnesia_op(add_table_index, [Name, Index]) of
|
||||
{atomic, ok} ->
|
||||
add_indexes(Name, Indexes);
|
||||
@@ -207,7 +207,7 @@ schema(Name, Default, Schema) ->
|
||||
case lists:keyfind(Name, 1, Schema) of
|
||||
{_, Custom} ->
|
||||
TabDefs = merge(Custom, Default),
|
||||
?DEBUG("Using custom schema for table '~s': ~p",
|
||||
?DEBUG("Using custom schema for table '~ts': ~p",
|
||||
[Name, TabDefs]),
|
||||
TabDefs;
|
||||
false ->
|
||||
@@ -221,7 +221,7 @@ read_schema_file() ->
|
||||
{ok, Y} ->
|
||||
case econf:validate(validator(), lists:flatten(Y)) of
|
||||
{ok, []} ->
|
||||
?WARNING_MSG("Mnesia schema file ~s is empty", [File]),
|
||||
?WARNING_MSG("Mnesia schema file ~ts is empty", [File]),
|
||||
[];
|
||||
{ok, Config} ->
|
||||
lists:map(
|
||||
@@ -232,15 +232,15 @@ read_schema_file() ->
|
||||
end, Opts)}
|
||||
end, Config);
|
||||
{error, Reason, Ctx} ->
|
||||
?ERROR_MSG("Failed to read Mnesia schema from ~s: ~s",
|
||||
?ERROR_MSG("Failed to read Mnesia schema from ~ts: ~ts",
|
||||
[File, econf:format_error(Reason, Ctx)]),
|
||||
[]
|
||||
end;
|
||||
{error, enoent} ->
|
||||
?DEBUG("No custom Mnesia schema file found at ~s", [File]),
|
||||
?DEBUG("No custom Mnesia schema file found at ~ts", [File]),
|
||||
[];
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Failed to read Mnesia schema file ~s: ~s",
|
||||
?ERROR_MSG("Failed to read Mnesia schema file ~ts: ~ts",
|
||||
[File, fast_yaml:format_error(Reason)])
|
||||
end.
|
||||
|
||||
@@ -258,7 +258,13 @@ validator() ->
|
||||
[unique]).
|
||||
|
||||
create(Name, TabDef) ->
|
||||
?INFO_MSG("Creating Mnesia table '~s'", [Name]),
|
||||
Type = lists:foldl(
|
||||
fun({ram_copies, _}, _) -> " ram ";
|
||||
({disc_copies, _}, _) -> " disc ";
|
||||
({disc_only_copies, _}, _) -> " disc_only ";
|
||||
(_, Acc) -> Acc
|
||||
end, " ", TabDef),
|
||||
?INFO_MSG("Creating Mnesia~tstable '~ts'", [Type, Name]),
|
||||
case mnesia_op(create_table, [Name, TabDef]) of
|
||||
{atomic, ok} ->
|
||||
add_table_copy(Name);
|
||||
@@ -317,7 +323,7 @@ transform(Module, Name, NewAttrs) ->
|
||||
transform(Module, Name, Attrs, Attrs) ->
|
||||
case need_transform(Module, Name) of
|
||||
true ->
|
||||
?INFO_MSG("Transforming table '~s', this may take a while", [Name]),
|
||||
?INFO_MSG("Transforming table '~ts', this may take a while", [Name]),
|
||||
transform_table(Module, Name);
|
||||
false ->
|
||||
{atomic, ok}
|
||||
@@ -370,9 +376,9 @@ transform_fun(Module, Name) ->
|
||||
try Module:transform(Obj)
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to transform Mnesia table ~s:~n"
|
||||
?ERROR_MSG("Failed to transform Mnesia table ~ts:~n"
|
||||
"** Record: ~p~n"
|
||||
"** ~s",
|
||||
"** ~ts",
|
||||
[Name, Obj,
|
||||
misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
erlang:raise(Class, Reason, StackTrace)
|
||||
@@ -420,7 +426,7 @@ mnesia_op(Fun, Args) ->
|
||||
{atomic, ok} ->
|
||||
{atomic, ok};
|
||||
Other ->
|
||||
?ERROR_MSG("Failure on mnesia ~s ~p: ~p",
|
||||
?ERROR_MSG("Failure on mnesia ~ts ~p: ~p",
|
||||
[Fun, Args, Other]),
|
||||
Other
|
||||
end.
|
||||
@@ -448,8 +454,8 @@ dump_schema() ->
|
||||
end, mnesia:system_info(tables)),
|
||||
case file:write_file(File, [fast_yaml:encode(Schema), io_lib:nl()]) of
|
||||
ok ->
|
||||
io:format("Mnesia schema is written to ~s~n", [File]);
|
||||
io:format("Mnesia schema is written to ~ts~n", [File]);
|
||||
{error, Reason} ->
|
||||
io:format("Failed to write Mnesia schema to ~s: ~s",
|
||||
io:format("Failed to write Mnesia schema to ~ts: ~ts",
|
||||
[File, file:format_error(Reason)])
|
||||
end.
|
||||
|
||||
+19
-3
@@ -45,10 +45,11 @@
|
||||
check_token/2,
|
||||
scope_in_scope_list/2,
|
||||
process/2,
|
||||
config_reloaded/0]).
|
||||
config_reloaded/0,
|
||||
verify_resowner_scope/3]).
|
||||
|
||||
-export([get_commands_spec/0,
|
||||
oauth_issue_token/3, oauth_list_tokens/0, oauth_revoke_token/1]).
|
||||
oauth_issue_token/3, oauth_list_tokens/0, oauth_revoke_token/1]).
|
||||
|
||||
-include("xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -211,6 +212,21 @@ authenticate_user({User, Server}, Ctx) ->
|
||||
|
||||
authenticate_client(Client, Ctx) -> {ok, {Ctx, {client, Client}}}.
|
||||
|
||||
-spec verify_resowner_scope({user, binary(), binary()}, [binary()], any()) ->
|
||||
{ok, any(), [binary()]} | {error, any()}.
|
||||
verify_resowner_scope({user, _User, _Server}, Scope, Ctx) ->
|
||||
Cmds = [atom_to_binary(Name, utf8) || {Name, _, _} <- ejabberd_commands:list_commands()],
|
||||
AllowedScopes = [<<"ejabberd:user">>, <<"ejabberd:admin">>, <<"sasl_auth">>] ++ Cmds,
|
||||
case oauth2_priv_set:is_subset(oauth2_priv_set:new(Scope),
|
||||
oauth2_priv_set:new(AllowedScopes)) of
|
||||
true ->
|
||||
{ok, {Ctx, Scope}};
|
||||
false ->
|
||||
{error, badscope}
|
||||
end;
|
||||
verify_resowner_scope(_, _, _) ->
|
||||
{error, badscope}.
|
||||
|
||||
%% This is callback for oauth tokens generated through the command line. Only open and admin commands are
|
||||
%% made available.
|
||||
%verify_client_scope({client, ejabberd_ctl}, Scope, Ctx) ->
|
||||
@@ -398,7 +414,7 @@ process(_Handlers,
|
||||
[{<<"action">>, <<"authorization_token">>},
|
||||
{<<"method">>, <<"post">>}],
|
||||
[?LABEL(<<"username">>, [?CT(?T("User (jid)")), ?C(<<": ">>)]),
|
||||
?INPUTID(<<"text">>, <<"username">>, <<"">>),
|
||||
?INPUTID(<<"email">>, <<"username">>, <<"">>),
|
||||
?BR,
|
||||
?LABEL(<<"password">>, [?CT(?T("Password")), ?C(<<": ">>)]),
|
||||
?INPUTID(<<"password">>, <<"password">>, <<"">>),
|
||||
|
||||
@@ -523,7 +523,7 @@ transform_globals(Opts) ->
|
||||
transform_globals(Opt, Opts) when Opt == override_global;
|
||||
Opt == override_local;
|
||||
Opt == override_acls ->
|
||||
?WARNING_MSG("Option '~s' has no effect anymore", [Opt]),
|
||||
?WARNING_MSG("Option '~ts' has no effect anymore", [Opt]),
|
||||
Opts;
|
||||
transform_globals({node_start, _}, Opts) ->
|
||||
?WARNING_MSG("Option 'node_start' has no effect anymore", []),
|
||||
|
||||
+13
-8
@@ -50,6 +50,7 @@
|
||||
-export([host_config/0]).
|
||||
-export([hosts/0]).
|
||||
-export([include_config_file/0, include_config_file/1]).
|
||||
-export([jwt_auth_only_rule/0, jwt_auth_only_rule/1]).
|
||||
-export([jwt_key/0, jwt_key/1]).
|
||||
-export([language/0, language/1]).
|
||||
-export([ldap_backups/0, ldap_backups/1]).
|
||||
@@ -147,7 +148,7 @@
|
||||
-export([sql_start_interval/0, sql_start_interval/1]).
|
||||
-export([sql_type/0, sql_type/1]).
|
||||
-export([sql_username/0, sql_username/1]).
|
||||
-export([trusted_proxies/0, trusted_proxies/1]).
|
||||
-export([trusted_proxies/0]).
|
||||
-export([use_cache/0, use_cache/1]).
|
||||
-export([validate_stream/0]).
|
||||
-export([version/0]).
|
||||
@@ -169,7 +170,7 @@ acl() ->
|
||||
acl(Host) ->
|
||||
ejabberd_config:get_option({acl, Host}).
|
||||
|
||||
-spec acme() -> #{'ca_url'=>binary(), 'contact'=>binary()}.
|
||||
-spec acme() -> #{'auto'=>boolean(), 'ca_url'=>binary(), 'cert_type'=>'ec' | 'rsa', 'contact'=>[binary()]}.
|
||||
acme() ->
|
||||
ejabberd_config:get_option({acme, global}).
|
||||
|
||||
@@ -424,6 +425,13 @@ include_config_file() ->
|
||||
include_config_file(Host) ->
|
||||
ejabberd_config:get_option({include_config_file, Host}).
|
||||
|
||||
-spec jwt_auth_only_rule() -> atom().
|
||||
jwt_auth_only_rule() ->
|
||||
jwt_auth_only_rule(global).
|
||||
-spec jwt_auth_only_rule(global | binary()) -> atom().
|
||||
jwt_auth_only_rule(Host) ->
|
||||
ejabberd_config:get_option({jwt_auth_only_rule, Host}).
|
||||
|
||||
-spec jwt_key() -> jose_jwk:key() | 'undefined'.
|
||||
jwt_key() ->
|
||||
jwt_key(global).
|
||||
@@ -975,10 +983,10 @@ sql_start_interval() ->
|
||||
sql_start_interval(Host) ->
|
||||
ejabberd_config:get_option({sql_start_interval, Host}).
|
||||
|
||||
-spec sql_type() -> 'mssql' | 'mysql' | 'odbc' | 'pgsql' | 'sqlite' | 'undefined'.
|
||||
-spec sql_type() -> 'mssql' | 'mysql' | 'odbc' | 'pgsql' | 'sqlite'.
|
||||
sql_type() ->
|
||||
sql_type(global).
|
||||
-spec sql_type(global | binary()) -> 'mssql' | 'mysql' | 'odbc' | 'pgsql' | 'sqlite' | 'undefined'.
|
||||
-spec sql_type(global | binary()) -> 'mssql' | 'mysql' | 'odbc' | 'pgsql' | 'sqlite'.
|
||||
sql_type(Host) ->
|
||||
ejabberd_config:get_option({sql_type, Host}).
|
||||
|
||||
@@ -991,10 +999,7 @@ sql_username(Host) ->
|
||||
|
||||
-spec trusted_proxies() -> 'all' | [{inet:ip4_address() | inet:ip6_address(),byte()}].
|
||||
trusted_proxies() ->
|
||||
trusted_proxies(global).
|
||||
-spec trusted_proxies(global | binary()) -> 'all' | [{inet:ip4_address() | inet:ip6_address(),byte()}].
|
||||
trusted_proxies(Host) ->
|
||||
ejabberd_config:get_option({trusted_proxies, Host}).
|
||||
ejabberd_config:get_option({trusted_proxies, global}).
|
||||
|
||||
-spec use_cache() -> boolean().
|
||||
use_cache() ->
|
||||
|
||||
@@ -40,7 +40,9 @@ opt_type(acl) ->
|
||||
opt_type(acme) ->
|
||||
econf:options(
|
||||
#{ca_url => econf:url(),
|
||||
contact => econf:binary("^[a-zA-Z]+:[^:]+$")},
|
||||
contact => econf:list_or_single(econf:binary("^[a-zA-Z]+:[^:]+$")),
|
||||
auto => econf:bool(),
|
||||
cert_type => econf:enum([ec, rsa])},
|
||||
[unique, {return, map}]);
|
||||
opt_type(allow_contrib_modules) ->
|
||||
econf:bool();
|
||||
@@ -409,7 +411,9 @@ opt_type(jwt_key) ->
|
||||
{error, Reason} ->
|
||||
econf:fail({read_file, Reason, Path})
|
||||
end
|
||||
end).
|
||||
end);
|
||||
opt_type(jwt_auth_only_rule) ->
|
||||
econf:atom().
|
||||
|
||||
%% We only define the types of options that cannot be derived
|
||||
%% automatically by tools/opt_type.sh script
|
||||
@@ -599,7 +603,7 @@ options() ->
|
||||
fun(Host) -> ejabberd_config:default_ram_db(Host, ejabberd_sm) end},
|
||||
{sm_use_cache,
|
||||
fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end},
|
||||
{sql_type, undefined},
|
||||
{sql_type, odbc},
|
||||
{sql_connect_timeout, timer:seconds(5)},
|
||||
{sql_database, undefined},
|
||||
{sql_keepalive_interval, undefined},
|
||||
@@ -635,7 +639,8 @@ options() ->
|
||||
{websocket_origin, []},
|
||||
{websocket_ping_interval, timer:seconds(60)},
|
||||
{websocket_timeout, timer:minutes(5)},
|
||||
{jwt_key, undefined}].
|
||||
{jwt_key, undefined},
|
||||
{jwt_auth_only_rule, none}].
|
||||
|
||||
-spec globals() -> [atom()].
|
||||
globals() ->
|
||||
@@ -698,6 +703,7 @@ globals() ->
|
||||
sm_cache_life_time,
|
||||
sm_cache_missed,
|
||||
sm_cache_size,
|
||||
trusted_proxies,
|
||||
validate_stream,
|
||||
version,
|
||||
websocket_origin,
|
||||
|
||||
+12
-12
@@ -86,7 +86,7 @@ import_file(FileName, State) ->
|
||||
Res;
|
||||
{error, Reason} ->
|
||||
ErrTxt = file:format_error(Reason),
|
||||
?ERROR_MSG("Failed to open file '~s': ~s", [FileName, ErrTxt]),
|
||||
?ERROR_MSG("Failed to open file '~ts': ~ts", [FileName, ErrTxt]),
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
@@ -125,7 +125,7 @@ export_hosts(Hosts, Dir) ->
|
||||
end, ok, FilesAndHosts);
|
||||
{error, Reason} ->
|
||||
ErrTxt = file:format_error(Reason),
|
||||
?ERROR_MSG("Failed to open file '~s': ~s", [DFn, ErrTxt]),
|
||||
?ERROR_MSG("Failed to open file '~ts': ~ts", [DFn, ErrTxt]),
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
@@ -149,7 +149,7 @@ export_host(Dir, FnH, Host) ->
|
||||
end;
|
||||
{error, Reason} ->
|
||||
ErrTxt = file:format_error(Reason),
|
||||
?ERROR_MSG("Failed to open file '~s': ~s", [DFn, ErrTxt]),
|
||||
?ERROR_MSG("Failed to open file '~ts': ~ts", [DFn, ErrTxt]),
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
@@ -315,7 +315,7 @@ process_el({xmlstreamstart, <<"server-data">>, Attrs}, State) ->
|
||||
?NS_PIE ->
|
||||
{ok, State};
|
||||
NS ->
|
||||
stop("Unknown 'server-data' namespace = ~s", [NS])
|
||||
stop("Unknown 'server-data' namespace = ~ts", [NS])
|
||||
end;
|
||||
process_el({xmlstreamend, _}, State) ->
|
||||
{ok, State};
|
||||
@@ -344,10 +344,10 @@ process_el({xmlstreamelement, #xmlel{name = <<"host">>,
|
||||
true ->
|
||||
process_users(Els, State#state{server = S});
|
||||
false ->
|
||||
stop("Unknown host: ~s", [S])
|
||||
stop("Unknown host: ~ts", [S])
|
||||
end
|
||||
catch _:{bad_jid, _} ->
|
||||
stop("Invalid 'jid': ~s", [JIDS])
|
||||
stop("Invalid 'jid': ~ts", [JIDS])
|
||||
end;
|
||||
process_el({xmlstreamstart, <<"user">>, Attrs}, State = #state{server = S})
|
||||
when S /= <<"">> ->
|
||||
@@ -395,7 +395,7 @@ process_user(#xmlel{name = <<"user">>, attrs = Attrs, children = Els},
|
||||
|
||||
case jid:nodeprep(Name) of
|
||||
error ->
|
||||
stop("Invalid 'user': ~s", [Name]);
|
||||
stop("Invalid 'user': ~ts", [Name]);
|
||||
LUser ->
|
||||
case ejabberd_auth:try_register(LUser, LServer, Pass) of
|
||||
ok ->
|
||||
@@ -403,7 +403,7 @@ process_user(#xmlel{name = <<"user">>, attrs = Attrs, children = Els},
|
||||
{error, invalid_password} when (Password == <<>>) ->
|
||||
process_user_els(Els, State#state{user = LUser});
|
||||
{error, Err} ->
|
||||
stop("Failed to create user '~s': ~p", [Name, Err])
|
||||
stop("Failed to create user '~ts': ~p", [Name, Err])
|
||||
end
|
||||
end.
|
||||
|
||||
@@ -442,7 +442,7 @@ process_user_el(#xmlel{name = Name, attrs = Attrs, children = Els} = El,
|
||||
end
|
||||
catch _:{xmpp_codec, Why} ->
|
||||
ErrTxt = xmpp:format_error(Why),
|
||||
stop("failed to decode XML '~s': ~s",
|
||||
stop("failed to decode XML '~ts': ~ts",
|
||||
[fxml:element_to_binary(El), ErrTxt])
|
||||
end.
|
||||
|
||||
@@ -585,7 +585,7 @@ make_piefxis_xml_tail() ->
|
||||
|
||||
%% @spec () -> string()
|
||||
make_piefxis_server_head() ->
|
||||
io_lib:format("<server-data xmlns='~s' xmlns:xi='~s'>",
|
||||
io_lib:format("<server-data xmlns='~ts' xmlns:xi='~ts'>",
|
||||
[?NS_PIE, ?NS_XI]).
|
||||
|
||||
%% @spec () -> string()
|
||||
@@ -594,7 +594,7 @@ make_piefxis_server_tail() ->
|
||||
|
||||
%% @spec (Host::string()) -> string()
|
||||
make_piefxis_host_head(Host) ->
|
||||
io_lib:format("<host xmlns='~s' xmlns:xi='~s' jid='~s'>",
|
||||
io_lib:format("<host xmlns='~ts' xmlns:xi='~ts' jid='~ts'>",
|
||||
[?NS_PIE, ?NS_XI, Host]).
|
||||
|
||||
%% @spec () -> string()
|
||||
@@ -604,7 +604,7 @@ make_piefxis_host_tail() ->
|
||||
%% @spec (Fn::string()) -> string()
|
||||
make_xinclude(Fn) ->
|
||||
Base = filename:basename(Fn),
|
||||
io_lib:format("<xi:include href='~s'/>", [Base]).
|
||||
io_lib:format("<xi:include href='~ts'/>", [Base]).
|
||||
|
||||
print(Fd, String) ->
|
||||
file:write(Fd, String).
|
||||
|
||||
+99
-35
@@ -26,9 +26,12 @@
|
||||
%% API
|
||||
-export([start_link/0]).
|
||||
-export([certs_dir/0]).
|
||||
-export([add_certfile/1, try_certfile/1, get_certfile/0, get_certfile/1]).
|
||||
-export([add_certfile/1, del_certfile/1, commit/0]).
|
||||
-export([notify_expired/1]).
|
||||
-export([try_certfile/1, get_certfile/0, get_certfile/1]).
|
||||
-export([get_certfile_no_default/1]).
|
||||
%% Hooks
|
||||
-export([ejabberd_started/0, config_reloaded/0]).
|
||||
-export([ejabberd_started/0, config_reloaded/0, cert_expired/2]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3, format_status/2]).
|
||||
@@ -59,13 +62,21 @@ add_certfile(Path0) ->
|
||||
end
|
||||
end.
|
||||
|
||||
-spec del_certfile(file:filename_all()) -> ok.
|
||||
del_certfile(Path0) ->
|
||||
Path = prep_path(Path0),
|
||||
try gen_server:call(?MODULE, {del_certfile, Path}, ?CALL_TIMEOUT)
|
||||
catch exit:{noproc, _} ->
|
||||
pkix:del_file(Path)
|
||||
end.
|
||||
|
||||
-spec try_certfile(file:filename_all()) -> filename().
|
||||
try_certfile(Path0) ->
|
||||
Path = prep_path(Path0),
|
||||
case pkix:is_pem_file(Path) of
|
||||
true -> Path;
|
||||
{false, Reason} ->
|
||||
?ERROR_MSG("Failed to read PEM file ~s: ~s",
|
||||
?ERROR_MSG("Failed to read PEM file ~ts: ~ts",
|
||||
[Path, pkix:format_error(Reason)]),
|
||||
erlang:error(badarg)
|
||||
end.
|
||||
@@ -81,14 +92,14 @@ get_certfile(Domain) ->
|
||||
|
||||
-spec get_certfile_no_default(binary()) -> {ok, filename()} | error.
|
||||
get_certfile_no_default(Domain) ->
|
||||
case xmpp_idna:domain_utf8_to_ascii(Domain) of
|
||||
false ->
|
||||
error;
|
||||
try list_to_binary(idna:utf8_to_ascii(Domain)) of
|
||||
ASCIIDomain ->
|
||||
case pkix:get_certfile(ASCIIDomain) of
|
||||
error -> error;
|
||||
Ret -> {ok, select_certfile(Ret)}
|
||||
end
|
||||
catch _:_ ->
|
||||
error
|
||||
end.
|
||||
|
||||
-spec get_certfile() -> {ok, filename()} | error.
|
||||
@@ -103,6 +114,10 @@ certs_dir() ->
|
||||
MnesiaDir = mnesia:system_info(directory),
|
||||
filename:join(MnesiaDir, "certs").
|
||||
|
||||
-spec commit() -> ok.
|
||||
commit() ->
|
||||
gen_server:call(?MODULE, commit, ?CALL_TIMEOUT).
|
||||
|
||||
-spec ejabberd_started() -> ok.
|
||||
ejabberd_started() ->
|
||||
gen_server:call(?MODULE, ejabberd_started, ?CALL_TIMEOUT).
|
||||
@@ -111,21 +126,38 @@ ejabberd_started() ->
|
||||
config_reloaded() ->
|
||||
gen_server:call(?MODULE, config_reloaded, ?CALL_TIMEOUT).
|
||||
|
||||
-spec notify_expired(pkix:notify_event()) -> ok.
|
||||
notify_expired(Event) ->
|
||||
gen_server:cast(?MODULE, Event).
|
||||
|
||||
-spec cert_expired(_, pkix:cert_info()) -> ok.
|
||||
cert_expired(_Cert, #{domains := Domains,
|
||||
expiry := Expiry,
|
||||
files := [{Path, Line}|_]}) ->
|
||||
?WARNING_MSG("Certificate in ~ts (at line: ~B)~ts ~ts",
|
||||
[Path, Line,
|
||||
case Domains of
|
||||
[] -> "";
|
||||
_ -> " for " ++ misc:format_hosts_list(Domains)
|
||||
end,
|
||||
format_expiration_date(Expiry)]).
|
||||
|
||||
%%%===================================================================
|
||||
%%% gen_server callbacks
|
||||
%%%===================================================================
|
||||
-spec init([]) -> {ok, state()}.
|
||||
init([]) ->
|
||||
process_flag(trap_exit, true),
|
||||
ejabberd_hooks:add(cert_expired, ?MODULE, cert_expired, 50),
|
||||
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 100),
|
||||
ejabberd_hooks:add(ejabberd_started, ?MODULE, ejabberd_started, 30),
|
||||
case add_files() of
|
||||
{Files, []} ->
|
||||
{ok, #state{files = Files}};
|
||||
{_Files, []} ->
|
||||
{ok, #state{}};
|
||||
{Files, [_|_]} ->
|
||||
case ejabberd:is_loaded() of
|
||||
true ->
|
||||
{ok, #state{files = Files}};
|
||||
{ok, #state{}};
|
||||
false ->
|
||||
del_files(Files),
|
||||
stop_ejabberd()
|
||||
@@ -137,13 +169,15 @@ init([]) ->
|
||||
handle_call({add_certfile, Path}, _From, State) ->
|
||||
case add_file(Path) of
|
||||
ok ->
|
||||
Files = sets:add_element(Path, State#state.files),
|
||||
{reply, {ok, Path}, State#state{files = Files}};
|
||||
{reply, {ok, Path}, State};
|
||||
{error, _} = Err ->
|
||||
{reply, Err, State}
|
||||
end;
|
||||
handle_call({del_certfile, Path}, _From, State) ->
|
||||
pkix:del_file(Path),
|
||||
{reply, ok, State};
|
||||
handle_call(ejabberd_started, _From, State) ->
|
||||
case commit() of
|
||||
case do_commit() of
|
||||
{ok, []} ->
|
||||
check_domain_certfiles(),
|
||||
{reply, ok, State};
|
||||
@@ -151,22 +185,25 @@ handle_call(ejabberd_started, _From, State) ->
|
||||
stop_ejabberd()
|
||||
end;
|
||||
handle_call(config_reloaded, _From, State) ->
|
||||
Old = State#state.files,
|
||||
New = get_certfiles_from_config_options(),
|
||||
del_files(sets:subtract(Old, New)),
|
||||
_ = add_files(New),
|
||||
case commit() of
|
||||
Files = get_certfiles_from_config_options(),
|
||||
_ = add_files(Files),
|
||||
case do_commit() of
|
||||
{ok, _} ->
|
||||
check_domain_certfiles(),
|
||||
{reply, ok, State#state{files = New}};
|
||||
{reply, ok, State};
|
||||
error ->
|
||||
{reply, ok, State}
|
||||
end;
|
||||
handle_call(commit, From, State) ->
|
||||
handle_call(config_reloaded, From, State);
|
||||
handle_call(Request, _From, State) ->
|
||||
?WARNING_MSG("Unexpected call: ~p", [Request]),
|
||||
{noreply, State}.
|
||||
|
||||
-spec handle_cast(term(), state()) -> {noreply, state()}.
|
||||
handle_cast({cert_expired, Cert, CertInfo}, State) ->
|
||||
ejabberd_hooks:run(cert_expired, [Cert, CertInfo]),
|
||||
{noreply, State};
|
||||
handle_cast(Request, State) ->
|
||||
?WARNING_MSG("Unexpected cast: ~p", [Request]),
|
||||
{noreply, State}.
|
||||
@@ -179,6 +216,7 @@ handle_info(Info, State) ->
|
||||
-spec terminate(normal | shutdown | {shutdown, term()} | term(),
|
||||
state()) -> any().
|
||||
terminate(_Reason, State) ->
|
||||
ejabberd_hooks:delete(cert_expired, ?MODULE, cert_expired, 50),
|
||||
ejabberd_hooks:delete(ejabberd_started, ?MODULE, ejabberd_started, 30),
|
||||
ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 100),
|
||||
del_files(State#state.files).
|
||||
@@ -224,7 +262,7 @@ add_file(File) ->
|
||||
case pkix:add_file(File) of
|
||||
ok -> ok;
|
||||
{error, Reason} = Err ->
|
||||
?ERROR_MSG("Failed to read PEM file ~s: ~s",
|
||||
?ERROR_MSG("Failed to read PEM file ~ts: ~ts",
|
||||
[File, pkix:format_error(Reason)]),
|
||||
Err
|
||||
end.
|
||||
@@ -233,11 +271,16 @@ add_file(File) ->
|
||||
del_files(Files) ->
|
||||
lists:foreach(fun pkix:del_file/1, sets:to_list(Files)).
|
||||
|
||||
-spec commit() -> {ok, [{filename(), pkix:error_reason()}]} | error.
|
||||
commit() ->
|
||||
-spec do_commit() -> {ok, [{filename(), pkix:error_reason()}]} | error.
|
||||
do_commit() ->
|
||||
CAFile = ejabberd_option:ca_file(),
|
||||
?DEBUG("Using CA root certificates from: ~s", [CAFile]),
|
||||
Opts = [{cafile, CAFile}],
|
||||
?DEBUG("Using CA root certificates from: ~ts", [CAFile]),
|
||||
Opts = [{cafile, CAFile},
|
||||
{notify_before, [7*24*60*60, % 1 week
|
||||
24*60*60, % 1 day
|
||||
60*60, % 1 hour
|
||||
0]},
|
||||
{notify_fun, fun ?MODULE:notify_expired/1}],
|
||||
case pkix:commit(certs_dir(), Opts) of
|
||||
{ok, Errors, Warnings, CAError} ->
|
||||
log_errors(Errors),
|
||||
@@ -246,7 +289,7 @@ commit() ->
|
||||
fast_tls_add_certfiles(),
|
||||
{ok, Errors};
|
||||
{error, File, Reason} ->
|
||||
?CRITICAL_MSG("Failed to write to ~s: ~s",
|
||||
?CRITICAL_MSG("Failed to write to ~ts: ~ts",
|
||||
[File, file:format_error(Reason)]),
|
||||
error
|
||||
end.
|
||||
@@ -267,12 +310,7 @@ check_domain_certfiles(Hosts) ->
|
||||
case get_certfile_no_default(Host) of
|
||||
error ->
|
||||
?WARNING_MSG(
|
||||
"No certificate found matching '~s': strictly "
|
||||
"configured clients or servers will reject "
|
||||
"connections with this host; obtain "
|
||||
"a certificate for this (sub)domain from any "
|
||||
"trusted CA such as Let's Encrypt "
|
||||
"(www.letsencrypt.org)",
|
||||
"No certificate found matching ~ts",
|
||||
[Host]);
|
||||
_ ->
|
||||
ok
|
||||
@@ -301,7 +339,7 @@ prep_path(Path0) ->
|
||||
{ok, CWD} ->
|
||||
unicode:characters_to_binary(filename:join(CWD, Path0));
|
||||
{error, Reason} ->
|
||||
?WARNING_MSG("Failed to get current directory name: ~s",
|
||||
?WARNING_MSG("Failed to get current directory name: ~ts",
|
||||
[file:format_error(Reason)]),
|
||||
unicode:characters_to_binary(Path0)
|
||||
end;
|
||||
@@ -321,7 +359,7 @@ wildcard(Path) when is_binary(Path) ->
|
||||
wildcard(Path) ->
|
||||
case filelib:wildcard(Path) of
|
||||
[] ->
|
||||
?WARNING_MSG("Path ~s is empty, please make sure ejabberd has "
|
||||
?WARNING_MSG("Path ~ts is empty, please make sure ejabberd has "
|
||||
"sufficient rights to read it", [Path]),
|
||||
[];
|
||||
Files ->
|
||||
@@ -344,9 +382,9 @@ fast_tls_add_certfiles() ->
|
||||
fast_tls:clear_cache().
|
||||
|
||||
reason_to_fmt({invalid_cert, _, _}) ->
|
||||
"Invalid certificate in ~s: ~s";
|
||||
"Invalid certificate in ~ts: ~ts";
|
||||
reason_to_fmt(_) ->
|
||||
"Failed to read PEM file ~s: ~s".
|
||||
"Failed to read PEM file ~ts: ~ts".
|
||||
|
||||
-spec log_warnings([{filename(), pkix:error_reason()}]) -> ok.
|
||||
log_warnings(Warnings) ->
|
||||
@@ -366,8 +404,34 @@ log_errors(Errors) ->
|
||||
|
||||
-spec log_cafile_error({filename(), pkix:error_reason()} | undefined) -> ok.
|
||||
log_cafile_error({File, Reason}) ->
|
||||
?CRITICAL_MSG("Failed to read CA certitificates from ~s: ~s. "
|
||||
?CRITICAL_MSG("Failed to read CA certitificates from ~ts: ~ts. "
|
||||
"Try to change/set option 'ca_file'",
|
||||
[File, pkix:format_error(Reason)]);
|
||||
log_cafile_error(_) ->
|
||||
ok.
|
||||
|
||||
-spec time_before_expiration(calendar:datetime()) -> {non_neg_integer(), string()}.
|
||||
time_before_expiration(Expiry) ->
|
||||
T1 = calendar:datetime_to_gregorian_seconds(Expiry),
|
||||
T2 = calendar:datetime_to_gregorian_seconds(
|
||||
calendar:now_to_datetime(erlang:timestamp())),
|
||||
Secs = max(0, T1 - T2),
|
||||
if Secs == {0, ""};
|
||||
Secs >= 220752000 -> {round(Secs/220752000), "year"};
|
||||
Secs >= 2592000 -> {round(Secs/2592000), "month"};
|
||||
Secs >= 604800 -> {round(Secs/604800), "week"};
|
||||
Secs >= 86400 -> {round(Secs/86400), "day"};
|
||||
Secs >= 3600 -> {round(Secs/3600), "hour"};
|
||||
Secs >= 60 -> {round(Secs/60), "minute"};
|
||||
true -> {Secs, "second"}
|
||||
end.
|
||||
|
||||
-spec format_expiration_date(calendar:datetime()) -> string().
|
||||
format_expiration_date(DateTime) ->
|
||||
case time_before_expiration(DateTime) of
|
||||
{0, _} -> "is expired";
|
||||
{1, Unit} -> "will expire in a " ++ Unit;
|
||||
{Int, Unit} ->
|
||||
"will expire in " ++ integer_to_list(Int)
|
||||
++ " " ++ Unit ++ "s"
|
||||
end.
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_rdbms.erl
|
||||
%%% Author : Mickael Remond <mickael.remond@process-one.net>
|
||||
%%% Purpose : Manage the start of the database modules when needed
|
||||
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(ejabberd_rdbms).
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start_link/0, init/1, stop/0,
|
||||
config_reloaded/0, start_host/1, stop_host/1]).
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
init([]) ->
|
||||
file:delete(ejabberd_sql:freetds_config()),
|
||||
file:delete(ejabberd_sql:odbc_config()),
|
||||
file:delete(ejabberd_sql:odbcinst_config()),
|
||||
ejabberd_hooks:add(host_up, ?MODULE, start_host, 20),
|
||||
ejabberd_hooks:add(host_down, ?MODULE, stop_host, 90),
|
||||
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20),
|
||||
{ok, {{one_for_one, 10, 1}, get_specs()}}.
|
||||
|
||||
stop() ->
|
||||
ejabberd_hooks:delete(host_up, ?MODULE, start_host, 20),
|
||||
ejabberd_hooks:delete(host_down, ?MODULE, stop_host, 90),
|
||||
ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 20),
|
||||
ejabberd_sup:stop_child(?MODULE).
|
||||
|
||||
-spec get_specs() -> [supervisor:child_spec()].
|
||||
get_specs() ->
|
||||
lists:flatmap(
|
||||
fun(Host) ->
|
||||
case get_spec(Host) of
|
||||
{ok, Spec} -> [Spec];
|
||||
undefined -> []
|
||||
end
|
||||
end, ejabberd_option:hosts()).
|
||||
|
||||
-spec get_spec(binary()) -> {ok, supervisor:child_spec()} | undefined.
|
||||
get_spec(Host) ->
|
||||
case needs_sql(Host) of
|
||||
{true, App} ->
|
||||
ejabberd:start_app(App),
|
||||
SupName = gen_mod:get_module_proc(Host, ejabberd_sql_sup),
|
||||
{ok, {SupName, {ejabberd_sql_sup, start_link, [Host]},
|
||||
transient, infinity, supervisor, [ejabberd_sql_sup]}};
|
||||
false ->
|
||||
undefined
|
||||
end.
|
||||
|
||||
-spec config_reloaded() -> ok.
|
||||
config_reloaded() ->
|
||||
lists:foreach(fun reload_host/1, ejabberd_option:hosts()).
|
||||
|
||||
-spec start_host(binary()) -> ok.
|
||||
start_host(Host) ->
|
||||
case get_spec(Host) of
|
||||
{ok, Spec} ->
|
||||
case supervisor:start_child(?MODULE, Spec) of
|
||||
{ok, _PID} ->
|
||||
ok;
|
||||
{error, {already_started, _}} ->
|
||||
ok;
|
||||
{error, _} = Err ->
|
||||
erlang:error(Err)
|
||||
end;
|
||||
undefined ->
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec stop_host(binary()) -> ok | {error, atom()}.
|
||||
stop_host(Host) ->
|
||||
SupName = gen_mod:get_module_proc(Host, ejabberd_sql_sup),
|
||||
case supervisor:terminate_child(?MODULE, SupName) of
|
||||
ok -> supervisor:delete_child(?MODULE, SupName);
|
||||
Err -> Err
|
||||
end.
|
||||
|
||||
-spec reload_host(binary()) -> ok.
|
||||
reload_host(Host) ->
|
||||
case needs_sql(Host) of
|
||||
{true, _} -> ejabberd_sql_sup:reload(Host);
|
||||
false -> ok
|
||||
end.
|
||||
|
||||
%% Returns {true, App} if we have configured sql for the given host
|
||||
needs_sql(Host) ->
|
||||
LHost = jid:nameprep(Host),
|
||||
case ejabberd_option:sql_type(LHost) of
|
||||
mysql -> {true, p1_mysql};
|
||||
pgsql -> {true, p1_pgsql};
|
||||
sqlite -> {true, sqlite3};
|
||||
mssql -> {true, odbc};
|
||||
odbc -> {true, odbc};
|
||||
undefined -> false
|
||||
end.
|
||||
@@ -425,7 +425,7 @@ handle_info({subscribed, Channel, Pid}, State) ->
|
||||
case maps:is_key(Channel, State#state.subscriptions) of
|
||||
true -> eredis_sub:ack_message(Pid);
|
||||
false ->
|
||||
?WARNING_MSG("Got subscription ack for unknown channel ~s",
|
||||
?WARNING_MSG("Got subscription ack for unknown channel ~ts",
|
||||
[Channel])
|
||||
end;
|
||||
_ ->
|
||||
@@ -466,7 +466,7 @@ connect(#state{num = Num}) ->
|
||||
ConnTimeout = ejabberd_option:redis_connect_timeout(),
|
||||
try case do_connect(Num, Server, Port, Pass, DB, ConnTimeout) of
|
||||
{ok, Client} ->
|
||||
?DEBUG("Connection #~p established to Redis at ~s:~p",
|
||||
?DEBUG("Connection #~p established to Redis at ~ts:~p",
|
||||
[Num, Server, Port]),
|
||||
register(get_connection(Num), Client),
|
||||
{ok, Client};
|
||||
@@ -476,7 +476,7 @@ connect(#state{num = Num}) ->
|
||||
catch _:Reason ->
|
||||
Timeout = p1_rand:uniform(
|
||||
min(10, ejabberd_redis_sup:get_pool_size())),
|
||||
?ERROR_MSG("Redis connection #~p at ~s:~p has failed: ~p; "
|
||||
?ERROR_MSG("Redis connection #~p at ~ts:~p has failed: ~p; "
|
||||
"reconnecting in ~p seconds",
|
||||
[Num, Server, Port, Reason, Timeout]),
|
||||
erlang:send_after(timer:seconds(Timeout), self(), connect),
|
||||
@@ -542,7 +542,7 @@ gen_server_call(Proc, Msg) ->
|
||||
log_error(Cmd, Reason) ->
|
||||
?ERROR_MSG("Redis request has failed:~n"
|
||||
"** request = ~p~n"
|
||||
"** response = ~s",
|
||||
"** response = ~ts",
|
||||
[Cmd, format_error(Reason)]).
|
||||
|
||||
-spec get_rnd_id() -> pos_integer().
|
||||
|
||||
@@ -47,7 +47,7 @@ start() ->
|
||||
{ok, _} -> ok;
|
||||
{error, {already_started, _}} -> ok;
|
||||
{error, Why} = Err ->
|
||||
?ERROR_MSG("Failed to start ~s: ~p", [?MODULE, Why]),
|
||||
?ERROR_MSG("Failed to start ~ts: ~p", [?MODULE, Why]),
|
||||
Err
|
||||
end
|
||||
end.
|
||||
|
||||
@@ -92,7 +92,7 @@ route(Packet) ->
|
||||
try do_route(Packet)
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to route packet:~n~s~n** ~s",
|
||||
?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts",
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
end.
|
||||
@@ -103,7 +103,7 @@ route(#jid{} = From, #jid{} = To, #xmlel{} = El) ->
|
||||
Pkt -> route(From, To, Pkt)
|
||||
catch _:{xmpp_codec, Why} ->
|
||||
?ERROR_MSG("Failed to decode xml element ~p when "
|
||||
"routing from ~s to ~s: ~s",
|
||||
"routing from ~ts to ~ts: ~ts",
|
||||
[El, jid:encode(From), jid:encode(To),
|
||||
xmpp:format_error(Why)])
|
||||
end;
|
||||
@@ -171,12 +171,12 @@ register_route(Domain, ServerHost, LocalHint, Pid) ->
|
||||
case Mod:register_route(LDomain, LServerHost, LocalHint,
|
||||
get_component_number(LDomain), Pid) of
|
||||
ok ->
|
||||
?DEBUG("Route registered: ~s", [LDomain]),
|
||||
?DEBUG("Route registered: ~ts", [LDomain]),
|
||||
monitor_route(LDomain, Pid),
|
||||
ejabberd_hooks:run(route_registered, [LDomain]),
|
||||
delete_cache(Mod, LDomain);
|
||||
{error, Err} ->
|
||||
?ERROR_MSG("Failed to register route ~s: ~p",
|
||||
?ERROR_MSG("Failed to register route ~ts: ~p",
|
||||
[LDomain, Err])
|
||||
end
|
||||
end.
|
||||
@@ -201,12 +201,12 @@ unregister_route(Domain, Pid) ->
|
||||
case Mod:unregister_route(
|
||||
LDomain, get_component_number(LDomain), Pid) of
|
||||
ok ->
|
||||
?DEBUG("Route unregistered: ~s", [LDomain]),
|
||||
?DEBUG("Route unregistered: ~ts", [LDomain]),
|
||||
demonitor_route(LDomain, Pid),
|
||||
ejabberd_hooks:run(route_unregistered, [LDomain]),
|
||||
delete_cache(Mod, LDomain);
|
||||
{error, Err} ->
|
||||
?ERROR_MSG("Failed to unregister route ~s: ~p",
|
||||
?ERROR_MSG("Failed to unregister route ~ts: ~p",
|
||||
[LDomain, Err])
|
||||
end
|
||||
end.
|
||||
@@ -355,7 +355,7 @@ handle_info({route, Packet}, State) ->
|
||||
handle_info({'DOWN', MRef, _, Pid, Info}, State) ->
|
||||
MRefs = maps:filter(
|
||||
fun({Domain, P}, M) when P == Pid, M == MRef ->
|
||||
?DEBUG("Process ~p with route registered to ~s "
|
||||
?DEBUG("Process ~p with route registered to ~ts "
|
||||
"has terminated unexpectedly with reason: ~p",
|
||||
[P, Domain, Info]),
|
||||
try unregister_route(Domain, Pid)
|
||||
@@ -381,7 +381,7 @@ code_change(_OldVsn, State, _Extra) ->
|
||||
%%--------------------------------------------------------------------
|
||||
-spec do_route(stanza()) -> ok.
|
||||
do_route(OrigPacket) ->
|
||||
?DEBUG("Route:~n~s", [xmpp:pp(OrigPacket)]),
|
||||
?DEBUG("Route:~n~ts", [xmpp:pp(OrigPacket)]),
|
||||
case ejabberd_hooks:run_fold(filter_packet, OrigPacket, []) of
|
||||
drop ->
|
||||
ok;
|
||||
|
||||
@@ -211,7 +211,7 @@ code_change(_OldVsn, State, _Extra) ->
|
||||
%% Destinations = [#jid]
|
||||
-spec do_route(binary(), [jid()], stanza()) -> any().
|
||||
do_route(Domain, Destinations, Packet) ->
|
||||
?DEBUG("Route multicast:~n~s~nDomain: ~s~nDestinations: ~s~n",
|
||||
?DEBUG("Route multicast:~n~ts~nDomain: ~ts~nDestinations: ~ts~n",
|
||||
[xmpp:pp(Packet), Domain,
|
||||
str:join([jid:encode(To) || To <- Destinations], <<", ">>)]),
|
||||
%% Try to find an appropriate multicast service
|
||||
|
||||
@@ -125,8 +125,8 @@ row_to_route(Domain, {ServerHost, NodeS, PidS, LocalHintS} = Row) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to decode row from 'route' table:~n"
|
||||
"** Row = ~p~n"
|
||||
"** Domain = ~s~n"
|
||||
"** ~s",
|
||||
"** Domain = ~ts~n"
|
||||
"** ~ts",
|
||||
[Row, Domain,
|
||||
misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
[]
|
||||
|
||||
+14
-14
@@ -42,7 +42,7 @@
|
||||
list_temporarily_blocked_hosts/0,
|
||||
external_host_overloaded/1, is_temporarly_blocked/1,
|
||||
get_commands_spec/0, zlib_enabled/1, get_idle_timeout/1,
|
||||
tls_required/1, tls_enabled/1, tls_options/2,
|
||||
tls_required/1, tls_enabled/1, tls_options/3,
|
||||
host_up/1, host_down/1, queue_type/1]).
|
||||
|
||||
%% gen_server callbacks
|
||||
@@ -91,7 +91,7 @@ list_temporarily_blocked_hosts() ->
|
||||
|
||||
-spec external_host_overloaded(binary()) -> {aborted, any()} | {atomic, ok}.
|
||||
external_host_overloaded(Host) ->
|
||||
?INFO_MSG("Disabling s2s connections to ~s for ~p seconds",
|
||||
?INFO_MSG("Disabling s2s connections to ~ts for ~p seconds",
|
||||
[Host, ?S2S_OVERLOAD_BLOCK_PERIOD]),
|
||||
mnesia:transaction(fun () ->
|
||||
Time = erlang:monotonic_time(),
|
||||
@@ -122,7 +122,7 @@ remove_connection({From, To} = FromTo, Pid) ->
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, _} -> ok;
|
||||
{aborted, Reason} ->
|
||||
?ERROR_MSG("Failed to unregister s2s connection ~s -> ~s: "
|
||||
?ERROR_MSG("Failed to unregister s2s connection ~ts -> ~ts: "
|
||||
"Mnesia failure: ~p",
|
||||
[From, To, Reason])
|
||||
end;
|
||||
@@ -167,7 +167,7 @@ try_register({From, To} = FromTo) ->
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, Res} -> Res;
|
||||
{aborted, Reason} ->
|
||||
?ERROR_MSG("Failed to register s2s connection ~s -> ~s: "
|
||||
?ERROR_MSG("Failed to register s2s connection ~ts -> ~ts: "
|
||||
"Mnesia failure: ~p",
|
||||
[From, To, Reason]),
|
||||
false
|
||||
@@ -177,34 +177,34 @@ try_register({From, To} = FromTo) ->
|
||||
dirty_get_connections() ->
|
||||
mnesia:dirty_all_keys(s2s).
|
||||
|
||||
-spec tls_options(binary(), [proplists:property()]) -> [proplists:property()].
|
||||
tls_options(LServer, DefaultOpts) ->
|
||||
-spec tls_options(binary(), binary(), [proplists:property()]) -> [proplists:property()].
|
||||
tls_options(LServer, ServerHost, DefaultOpts) ->
|
||||
TLSOpts1 = case ejabberd_pkix:get_certfile(LServer) of
|
||||
error -> DefaultOpts;
|
||||
{ok, CertFile} ->
|
||||
lists:keystore(certfile, 1, DefaultOpts,
|
||||
{certfile, CertFile})
|
||||
end,
|
||||
TLSOpts2 = case ejabberd_option:s2s_ciphers(LServer) of
|
||||
TLSOpts2 = case ejabberd_option:s2s_ciphers(ServerHost) of
|
||||
undefined -> TLSOpts1;
|
||||
Ciphers -> lists:keystore(ciphers, 1, TLSOpts1,
|
||||
{ciphers, Ciphers})
|
||||
end,
|
||||
TLSOpts3 = case ejabberd_option:s2s_protocol_options(LServer) of
|
||||
TLSOpts3 = case ejabberd_option:s2s_protocol_options(ServerHost) of
|
||||
undefined -> TLSOpts2;
|
||||
ProtoOpts -> lists:keystore(protocol_options, 1, TLSOpts2,
|
||||
{protocol_options, ProtoOpts})
|
||||
end,
|
||||
TLSOpts4 = case ejabberd_option:s2s_dhfile(LServer) of
|
||||
TLSOpts4 = case ejabberd_option:s2s_dhfile(ServerHost) of
|
||||
undefined -> TLSOpts3;
|
||||
DHFile -> lists:keystore(dhfile, 1, TLSOpts3,
|
||||
{dhfile, DHFile})
|
||||
end,
|
||||
TLSOpts5 = case lists:keymember(cafile, 1, TLSOpts4) of
|
||||
true -> TLSOpts4;
|
||||
false -> [{cafile, get_cafile(LServer)}|TLSOpts4]
|
||||
false -> [{cafile, get_cafile(ServerHost)}|TLSOpts4]
|
||||
end,
|
||||
case ejabberd_option:s2s_tls_compression(LServer) of
|
||||
case ejabberd_option:s2s_tls_compression(ServerHost) of
|
||||
undefined -> TLSOpts5;
|
||||
false -> [compression_none | TLSOpts5];
|
||||
true -> lists:delete(compression_none, TLSOpts5)
|
||||
@@ -284,7 +284,7 @@ handle_info({route, Packet}, State) ->
|
||||
try route(Packet)
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to route packet:~n~s~n** ~s",
|
||||
?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts",
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
end,
|
||||
@@ -344,7 +344,7 @@ clean_table_from_bad_node(Node) ->
|
||||
|
||||
-spec route(stanza()) -> ok.
|
||||
route(Packet) ->
|
||||
?DEBUG("Local route:~n~s", [xmpp:pp(Packet)]),
|
||||
?DEBUG("Local route:~n~ts", [xmpp:pp(Packet)]),
|
||||
From = xmpp:get_from(Packet),
|
||||
To = xmpp:get_to(Packet),
|
||||
case start_connection(From, To) of
|
||||
@@ -477,7 +477,7 @@ new_connection(MyServer, Server, From, FromTo,
|
||||
end,
|
||||
[Pid1];
|
||||
{aborted, Reason} ->
|
||||
?ERROR_MSG("Failed to register s2s connection ~s -> ~s: "
|
||||
?ERROR_MSG("Failed to register s2s connection ~ts -> ~ts: "
|
||||
"Mnesia failure: ~p",
|
||||
[MyServer, Server, Reason]),
|
||||
ejabberd_s2s_out:stop(Pid),
|
||||
|
||||
@@ -128,15 +128,15 @@ process_closed(#{server := LServer} = State, Reason) ->
|
||||
#{ip := IP} ->
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP))
|
||||
end,
|
||||
?INFO_MSG("Closing inbound s2s connection ~s -> ~s: ~s",
|
||||
?INFO_MSG("Closing inbound s2s connection ~ts -> ~ts: ~ts",
|
||||
[RServer, LServer, xmpp_stream_out:format_error(Reason)]),
|
||||
stop(State).
|
||||
|
||||
%%%===================================================================
|
||||
%%% xmpp_stream_in callbacks
|
||||
%%%===================================================================
|
||||
tls_options(#{tls_options := TLSOpts, server_host := ServerHost}) ->
|
||||
ejabberd_s2s:tls_options(ServerHost, TLSOpts).
|
||||
tls_options(#{tls_options := TLSOpts, lserver := LServer, server_host := ServerHost}) ->
|
||||
ejabberd_s2s:tls_options(LServer, ServerHost, TLSOpts).
|
||||
|
||||
tls_required(#{server_host := ServerHost}) ->
|
||||
ejabberd_s2s:tls_required(ServerHost).
|
||||
@@ -178,7 +178,7 @@ handle_auth_success(RServer, Mech, _AuthModule,
|
||||
auth_domains := AuthDomains,
|
||||
server_host := ServerHost,
|
||||
lserver := LServer} = State) ->
|
||||
?INFO_MSG("(~s) Accepted inbound s2s ~s authentication ~s -> ~s (~s)",
|
||||
?INFO_MSG("(~ts) Accepted inbound s2s ~ts authentication ~ts -> ~ts (~ts)",
|
||||
[xmpp_socket:pp(Socket), Mech, RServer, LServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
|
||||
State1 = case ejabberd_s2s:allow_host(ServerHost, RServer) of
|
||||
@@ -195,7 +195,7 @@ handle_auth_failure(RServer, Mech, Reason,
|
||||
#{socket := Socket, ip := IP,
|
||||
server_host := ServerHost,
|
||||
lserver := LServer} = State) ->
|
||||
?WARNING_MSG("(~s) Failed inbound s2s ~s authentication ~s -> ~s (~s): ~s",
|
||||
?WARNING_MSG("(~ts) Failed inbound s2s ~ts authentication ~ts -> ~ts (~ts): ~ts",
|
||||
[xmpp_socket:pp(Socket), Mech, RServer, LServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)), Reason]),
|
||||
ejabberd_hooks:run_fold(s2s_in_auth_result,
|
||||
@@ -283,7 +283,7 @@ terminate(Reason, #{auth_domains := AuthDomains,
|
||||
socket := Socket} = State) ->
|
||||
case maps:get(stop_reason, State, undefined) of
|
||||
{tls, _} = Err ->
|
||||
?WARNING_MSG("(~s) Failed to secure inbound s2s connection: ~s",
|
||||
?WARNING_MSG("(~ts) Failed to secure inbound s2s connection: ~ts",
|
||||
[xmpp_socket:pp(Socket), xmpp_stream_in:format_error(Err)]);
|
||||
_ ->
|
||||
ok
|
||||
|
||||
@@ -135,7 +135,7 @@ host_down(Host) ->
|
||||
process_auth_result(#{server := LServer, remote_server := RServer} = State,
|
||||
{false, Reason}) ->
|
||||
Delay = get_delay(),
|
||||
?WARNING_MSG("Failed to establish outbound s2s connection ~s -> ~s: "
|
||||
?WARNING_MSG("Failed to establish outbound s2s connection ~ts -> ~ts: "
|
||||
"authentication failed; bouncing for ~p seconds",
|
||||
[LServer, RServer, Delay div 1000]),
|
||||
State1 = State#{on_route => bounce, stop_reason => Reason},
|
||||
@@ -148,13 +148,13 @@ process_auth_result(State, true) ->
|
||||
process_closed(#{server := LServer, remote_server := RServer,
|
||||
on_route := send} = State,
|
||||
Reason) ->
|
||||
?INFO_MSG("Closing outbound s2s connection ~s -> ~s: ~s",
|
||||
?INFO_MSG("Closing outbound s2s connection ~ts -> ~ts: ~ts",
|
||||
[LServer, RServer, format_error(Reason)]),
|
||||
stop(State);
|
||||
process_closed(#{server := LServer, remote_server := RServer} = State,
|
||||
Reason) ->
|
||||
Delay = get_delay(),
|
||||
?WARNING_MSG("Failed to establish outbound s2s connection ~s -> ~s: ~s; "
|
||||
?WARNING_MSG("Failed to establish outbound s2s connection ~ts -> ~ts: ~ts; "
|
||||
"bouncing for ~p seconds",
|
||||
[LServer, RServer, format_error(Reason), Delay div 1000]),
|
||||
State1 = State#{on_route => bounce},
|
||||
@@ -175,8 +175,8 @@ process_downgraded(State, _StreamStart) ->
|
||||
%%%===================================================================
|
||||
%%% xmpp_stream_out callbacks
|
||||
%%%===================================================================
|
||||
tls_options(#{server_host := ServerHost}) ->
|
||||
ejabberd_s2s:tls_options(ServerHost, []).
|
||||
tls_options(#{server := LServer, server_host := ServerHost}) ->
|
||||
ejabberd_s2s:tls_options(LServer, ServerHost, []).
|
||||
|
||||
tls_required(#{server_host := ServerHost}) ->
|
||||
ejabberd_s2s:tls_required(ServerHost).
|
||||
@@ -206,7 +206,7 @@ handle_auth_success(Mech, #{socket := Socket, ip := IP,
|
||||
remote_server := RServer,
|
||||
server_host := ServerHost,
|
||||
server := LServer} = State) ->
|
||||
?INFO_MSG("(~s) Accepted outbound s2s ~s authentication ~s -> ~s (~s)",
|
||||
?INFO_MSG("(~ts) Accepted outbound s2s ~ts authentication ~ts -> ~ts (~ts)",
|
||||
[xmpp_socket:pp(Socket), Mech, LServer, RServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
|
||||
ejabberd_hooks:run_fold(s2s_out_auth_result, ServerHost, State, [true]).
|
||||
@@ -216,7 +216,7 @@ handle_auth_failure(Mech, Reason,
|
||||
remote_server := RServer,
|
||||
server_host := ServerHost,
|
||||
server := LServer} = State) ->
|
||||
?WARNING_MSG("(~s) Failed outbound s2s ~s authentication ~s -> ~s (~s): ~s",
|
||||
?WARNING_MSG("(~ts) Failed outbound s2s ~ts authentication ~ts -> ~ts (~ts): ~ts",
|
||||
[xmpp_socket:pp(Socket), Mech, LServer, RServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)),
|
||||
xmpp_stream_out:format_error(Reason)]),
|
||||
@@ -270,7 +270,7 @@ init([#{server := LServer, remote_server := RServer} = State, Opts]) ->
|
||||
server_host => ServerHost,
|
||||
shaper => none},
|
||||
State2 = xmpp_stream_out:set_timeout(State1, Timeout),
|
||||
?INFO_MSG("Outbound s2s connection started: ~s -> ~s",
|
||||
?INFO_MSG("Outbound s2s connection started: ~ts -> ~ts",
|
||||
[LServer, RServer]),
|
||||
ejabberd_hooks:run_fold(s2s_out_init, ServerHost, {ok, State2}, [Opts]).
|
||||
|
||||
@@ -335,7 +335,7 @@ bounce_message_queue({LServer, RServer} = FromTo, State) ->
|
||||
Pids = ejabberd_s2s:get_connections_pids(FromTo),
|
||||
case lists:member(self(), Pids) of
|
||||
true ->
|
||||
?WARNING_MSG("Outgoing s2s connection ~s -> ~s is supposed "
|
||||
?WARNING_MSG("Outgoing s2s connection ~ts -> ~ts is supposed "
|
||||
"to be unregistered, but pid ~p still presents "
|
||||
"in 's2s' table", [LServer, RServer, self()]),
|
||||
State;
|
||||
|
||||
@@ -164,8 +164,8 @@ get_password_fun(#{remote_server := RemoteServer,
|
||||
{ok, Password} ->
|
||||
{Password, undefined};
|
||||
error ->
|
||||
?WARNING_MSG("(~s) Domain ~s is unconfigured for "
|
||||
"external component from ~s",
|
||||
?WARNING_MSG("(~ts) Domain ~ts is unconfigured for "
|
||||
"external component from ~ts",
|
||||
[xmpp_socket:pp(Socket), RemoteServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
|
||||
{false, undefined}
|
||||
@@ -176,8 +176,8 @@ handle_auth_success(_, Mech, _,
|
||||
#{remote_server := RemoteServer, host_opts := HostOpts,
|
||||
socket := Socket, ip := IP,
|
||||
global_routes := GlobalRoutes} = State) ->
|
||||
?INFO_MSG("(~s) Accepted external component ~s authentication "
|
||||
"for ~s from ~s",
|
||||
?INFO_MSG("(~ts) Accepted external component ~ts authentication "
|
||||
"for ~ts from ~ts",
|
||||
[xmpp_socket:pp(Socket), Mech, RemoteServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
|
||||
Routes = if GlobalRoutes ->
|
||||
@@ -195,8 +195,8 @@ handle_auth_success(_, Mech, _,
|
||||
handle_auth_failure(_, Mech, Reason,
|
||||
#{remote_server := RemoteServer,
|
||||
socket := Socket, ip := IP} = State) ->
|
||||
?WARNING_MSG("(~s) Failed external component ~s authentication "
|
||||
"for ~s from ~s: ~s",
|
||||
?WARNING_MSG("(~ts) Failed external component ~ts authentication "
|
||||
"for ~ts from ~ts: ~ts",
|
||||
[xmpp_socket:pp(Socket), Mech, RemoteServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP)),
|
||||
Reason]),
|
||||
@@ -272,7 +272,7 @@ listen_opt_type(shaper_rule) ->
|
||||
econf:and_then(
|
||||
econf:shaper(),
|
||||
fun(S) ->
|
||||
?WARNING_MSG("Listening option 'shaper_rule' of module ~s "
|
||||
?WARNING_MSG("Listening option 'shaper_rule' of module ~ts "
|
||||
"is renamed to 'shaper'. Please adjust your "
|
||||
"configuration", [?MODULE]),
|
||||
S
|
||||
|
||||
@@ -72,7 +72,7 @@ new(_) -> none.
|
||||
update(none, _Size) -> {none, 0};
|
||||
update(Shaper1, Size) ->
|
||||
Shaper2 = p1_shaper:update(Shaper1, Size),
|
||||
?DEBUG("Shaper update:~n~s =>~n~s",
|
||||
?DEBUG("Shaper update:~n~ts =>~n~ts",
|
||||
[p1_shaper:pp(Shaper1), p1_shaper:pp(Shaper2)]),
|
||||
Shaper2.
|
||||
|
||||
@@ -231,7 +231,7 @@ resolve_shapers(ShaperRule, Rules, Shapers) ->
|
||||
try {true, {maps:get(Name, Shapers), Rule}}
|
||||
catch _:{badkey, _} ->
|
||||
?WARNING_MSG(
|
||||
"Shaper rule '~s' refers to unknown shaper: ~s",
|
||||
"Shaper rule '~ts' refers to unknown shaper: ~ts",
|
||||
[ShaperRule, Name]),
|
||||
false
|
||||
end;
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
-include("logger.hrl").
|
||||
-export([accept/1, start/3, start_link/3, listen_options/0]).
|
||||
fail() ->
|
||||
?CRITICAL_MSG("Listening module ~s is not available: "
|
||||
?CRITICAL_MSG("Listening module ~ts is not available: "
|
||||
"ejabberd is not compiled with SIP support",
|
||||
[?MODULE]),
|
||||
erlang:error(sip_not_compiled).
|
||||
|
||||
+12
-12
@@ -129,9 +129,9 @@ route(To, Term) ->
|
||||
try do_route(To, Term), ok
|
||||
catch ?EX_RULE(E, R, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to route term to ~s:~n"
|
||||
?ERROR_MSG("Failed to route term to ~ts:~n"
|
||||
"** Term = ~p~n"
|
||||
"** ~s",
|
||||
"** ~ts",
|
||||
[jid:encode(To), Term,
|
||||
misc:format_exception(2, E, R, StackTrace)])
|
||||
end.
|
||||
@@ -141,7 +141,7 @@ route(Packet) ->
|
||||
#jid{lserver = LServer} = xmpp:get_to(Packet),
|
||||
case ejabberd_hooks:run_fold(sm_receive_packet, LServer, Packet, []) of
|
||||
drop ->
|
||||
?DEBUG("Hook dropped stanza:~n~s", [xmpp:pp(Packet)]);
|
||||
?DEBUG("Hook dropped stanza:~n~ts", [xmpp:pp(Packet)]);
|
||||
Packet1 ->
|
||||
do_route(Packet1),
|
||||
ok
|
||||
@@ -204,7 +204,7 @@ bounce_sm_packet({bounce, Packet} = Acc) ->
|
||||
ejabberd_router:route_error(Packet, Err),
|
||||
{stop, Acc};
|
||||
bounce_sm_packet({_, Packet} = Acc) ->
|
||||
?DEBUG("Dropping packet to unavailable resource:~n~s",
|
||||
?DEBUG("Dropping packet to unavailable resource:~n~ts",
|
||||
[xmpp:pp(Packet)]),
|
||||
Acc.
|
||||
|
||||
@@ -654,7 +654,7 @@ do_route(#jid{lresource = <<"">>} = To, Term) ->
|
||||
do_route(jid:replace_resource(To, R), Term)
|
||||
end, get_user_resources(To#jid.user, To#jid.server));
|
||||
do_route(To, Term) ->
|
||||
?DEBUG("Broadcasting ~p to ~s", [Term, jid:encode(To)]),
|
||||
?DEBUG("Broadcasting ~p to ~ts", [Term, jid:encode(To)]),
|
||||
{U, S, R} = jid:tolower(To),
|
||||
Mod = get_sm_backend(S),
|
||||
case get_sessions(Mod, U, S, R) of
|
||||
@@ -670,7 +670,7 @@ do_route(To, Term) ->
|
||||
-spec do_route(stanza()) -> any().
|
||||
do_route(#presence{to = To, type = T} = Packet)
|
||||
when T == subscribe; T == subscribed; T == unsubscribe; T == unsubscribed ->
|
||||
?DEBUG("Processing subscription:~n~s", [xmpp:pp(Packet)]),
|
||||
?DEBUG("Processing subscription:~n~ts", [xmpp:pp(Packet)]),
|
||||
#jid{luser = LUser, lserver = LServer} = To,
|
||||
case is_privacy_allow(Packet) andalso
|
||||
ejabberd_hooks:run_fold(
|
||||
@@ -683,7 +683,7 @@ do_route(#presence{to = To, type = T} = Packet)
|
||||
priority = Prio}) when is_integer(Prio) ->
|
||||
Pid = element(2, SID),
|
||||
Packet1 = Packet#presence{to = jid:replace_resource(To, R)},
|
||||
?DEBUG("Sending to process ~p:~n~s",
|
||||
?DEBUG("Sending to process ~p:~n~ts",
|
||||
[Pid, xmpp:pp(Packet1)]),
|
||||
ejabberd_c2s:route(Pid, {route, Packet1});
|
||||
(_) ->
|
||||
@@ -693,14 +693,14 @@ do_route(#presence{to = To, type = T} = Packet)
|
||||
ok
|
||||
end;
|
||||
do_route(#presence{to = #jid{lresource = <<"">>} = To} = Packet) ->
|
||||
?DEBUG("Processing presence to bare JID:~n~s", [xmpp:pp(Packet)]),
|
||||
?DEBUG("Processing presence to bare JID:~n~ts", [xmpp:pp(Packet)]),
|
||||
{LUser, LServer, _} = jid:tolower(To),
|
||||
lists:foreach(
|
||||
fun({_, R}) ->
|
||||
do_route(Packet#presence{to = jid:replace_resource(To, R)})
|
||||
end, get_user_present_resources(LUser, LServer));
|
||||
do_route(#message{to = #jid{lresource = <<"">>} = To, type = T} = Packet) ->
|
||||
?DEBUG("Processing message to bare JID:~n~s", [xmpp:pp(Packet)]),
|
||||
?DEBUG("Processing message to bare JID:~n~ts", [xmpp:pp(Packet)]),
|
||||
if T == chat; T == headline; T == normal ->
|
||||
route_message(Packet);
|
||||
true ->
|
||||
@@ -709,14 +709,14 @@ do_route(#message{to = #jid{lresource = <<"">>} = To, type = T} = Packet) ->
|
||||
end;
|
||||
do_route(#iq{to = #jid{lresource = <<"">>} = To, type = T} = Packet) ->
|
||||
if T == set; T == get ->
|
||||
?DEBUG("Processing IQ to bare JID:~n~s", [xmpp:pp(Packet)]),
|
||||
?DEBUG("Processing IQ to bare JID:~n~ts", [xmpp:pp(Packet)]),
|
||||
gen_iq_handler:handle(?MODULE, Packet);
|
||||
true ->
|
||||
ejabberd_hooks:run_fold(bounce_sm_packet,
|
||||
To#jid.lserver, {pass, Packet}, [])
|
||||
end;
|
||||
do_route(Packet) ->
|
||||
?DEBUG("Processing packet to full JID:~n~s", [xmpp:pp(Packet)]),
|
||||
?DEBUG("Processing packet to full JID:~n~ts", [xmpp:pp(Packet)]),
|
||||
To = xmpp:get_to(Packet),
|
||||
{LUser, LServer, LResource} = jid:tolower(To),
|
||||
Mod = get_sm_backend(LServer),
|
||||
@@ -738,7 +738,7 @@ do_route(Packet) ->
|
||||
Ss ->
|
||||
Session = lists:max(Ss),
|
||||
Pid = element(2, Session#session.sid),
|
||||
?DEBUG("Sending to process ~p:~n~s", [Pid, xmpp:pp(Packet)]),
|
||||
?DEBUG("Sending to process ~p:~n~ts", [Pid, xmpp:pp(Packet)]),
|
||||
ejabberd_c2s:route(Pid, {route, Packet})
|
||||
end.
|
||||
|
||||
|
||||
@@ -250,8 +250,8 @@ load_script() ->
|
||||
V when V >= ?MIN_REDIS_VERSION ->
|
||||
ejabberd_redis:script_load(Data);
|
||||
V ->
|
||||
?CRITICAL_MSG("Unsupported Redis version: ~s. "
|
||||
"The version must be ~s or above",
|
||||
?CRITICAL_MSG("Unsupported Redis version: ~ts. "
|
||||
"The version must be ~ts or above",
|
||||
[V, ?MIN_REDIS_VERSION]),
|
||||
{error, unsupported_redis_version}
|
||||
end;
|
||||
|
||||
+62
-41
@@ -141,8 +141,8 @@ sql_call(Host, Msg) ->
|
||||
Timeout = query_timeout(Host),
|
||||
case get(?STATE_KEY) of
|
||||
undefined ->
|
||||
Proc = get_worker(Host),
|
||||
sync_send_event(Proc, {sql_cmd, Msg, current_time() + Timeout},
|
||||
sync_send_event(Host,
|
||||
{sql_cmd, Msg, current_time() + Timeout},
|
||||
Timeout);
|
||||
_State ->
|
||||
nested_op(Msg)
|
||||
@@ -161,6 +161,14 @@ keep_alive(Host, Proc) ->
|
||||
sync_send_event(Proc, force_timeout, Timeout)
|
||||
end.
|
||||
|
||||
sync_send_event(Host, Msg, Timeout) when is_binary(Host) ->
|
||||
case ejabberd_sql_sup:start(Host) of
|
||||
ok ->
|
||||
Proc = get_worker(Host),
|
||||
sync_send_event(Proc, Msg, Timeout);
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end;
|
||||
sync_send_event(Proc, Msg, Timeout) ->
|
||||
try p1_fsm:sync_send_event(Proc, Msg, Timeout)
|
||||
catch _:{Reason, {p1_fsm, _, _}} ->
|
||||
@@ -256,14 +264,14 @@ decode_term(Bin) ->
|
||||
Term
|
||||
catch _:{badmatch, {error, {Line, Mod, Reason}, _}} ->
|
||||
?ERROR_MSG("Corrupted Erlang term in SQL database:~n"
|
||||
"** Scanner error: at line ~B: ~s~n"
|
||||
"** Term: ~s",
|
||||
"** Scanner error: at line ~B: ~ts~n"
|
||||
"** Term: ~ts",
|
||||
[Line, Mod:format_error(Reason), Bin]),
|
||||
erlang:error(badarg);
|
||||
_:{badmatch, {error, {Line, Mod, Reason}}} ->
|
||||
?ERROR_MSG("Corrupted Erlang term in SQL database:~n"
|
||||
"** Parser error: at line ~B: ~s~n"
|
||||
"** Term: ~s",
|
||||
"** Parser error: at line ~B: ~ts~n"
|
||||
"** Term: ~ts",
|
||||
[Line, Mod:format_error(Reason), Bin]),
|
||||
erlang:error(badarg)
|
||||
end.
|
||||
@@ -282,7 +290,7 @@ sqlite_file(Host) ->
|
||||
{ok, Cwd} ->
|
||||
filename:join([Cwd|Path]);
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Failed to get current directory: ~s",
|
||||
?ERROR_MSG("Failed to get current directory: ~ts",
|
||||
[file:format_error(Reason)]),
|
||||
filename:join(Path)
|
||||
end;
|
||||
@@ -374,7 +382,7 @@ connecting({sql_cmd, Command, Timestamp} = Req, From,
|
||||
State#state.pending_requests)
|
||||
catch error:full ->
|
||||
Err = <<"SQL request queue is overfilled">>,
|
||||
?ERROR_MSG("~s, bouncing all pending requests", [Err]),
|
||||
?ERROR_MSG("~ts, bouncing all pending requests", [Err]),
|
||||
Q = p1_queue:dropwhile(
|
||||
fun({sql_cmd, _, To, TS}) ->
|
||||
reply(To, {error, Err}, TS),
|
||||
@@ -489,7 +497,7 @@ inner_transaction(F) ->
|
||||
?TOP_LEVEL_TXN ->
|
||||
{backtrace, T} = process_info(self(), backtrace),
|
||||
?ERROR_MSG("Inner transaction called at outer txn "
|
||||
"level. Trace: ~s",
|
||||
"level. Trace: ~ts",
|
||||
[T]),
|
||||
erlang:exit(implementation_faulty);
|
||||
_N -> ok
|
||||
@@ -511,19 +519,19 @@ outer_transaction(F, NRestarts, _Reason) ->
|
||||
_N ->
|
||||
{backtrace, T} = process_info(self(), backtrace),
|
||||
?ERROR_MSG("Outer transaction called at inner txn "
|
||||
"level. Trace: ~s",
|
||||
"level. Trace: ~ts",
|
||||
[T]),
|
||||
erlang:exit(implementation_faulty)
|
||||
end,
|
||||
sql_query_internal([<<"begin;">>]),
|
||||
sql_begin(),
|
||||
put(?NESTING_KEY, PreviousNestingLevel + 1),
|
||||
try F() of
|
||||
Res ->
|
||||
sql_query_internal([<<"commit;">>]),
|
||||
sql_commit(),
|
||||
{atomic, Res}
|
||||
catch
|
||||
?EX_RULE(throw, {aborted, Reason}, _) when NRestarts > 0 ->
|
||||
sql_query_internal([<<"rollback;">>]),
|
||||
sql_rollback(),
|
||||
put(?NESTING_KEY, ?TOP_LEVEL_TXN),
|
||||
outer_transaction(F, NRestarts - 1, Reason);
|
||||
?EX_RULE(throw, {aborted, Reason}, Stack) when NRestarts =:= 0 ->
|
||||
@@ -534,10 +542,10 @@ outer_transaction(F, NRestarts, _Reason) ->
|
||||
"== ~p",
|
||||
[?MAX_TRANSACTION_RESTARTS, Reason,
|
||||
StackTrace, get(?STATE_KEY)]),
|
||||
sql_query_internal([<<"rollback;">>]),
|
||||
sql_rollback(),
|
||||
{aborted, Reason};
|
||||
?EX_RULE(exit, Reason, _) ->
|
||||
sql_query_internal([<<"rollback;">>]),
|
||||
sql_rollback(),
|
||||
{aborted, Reason}
|
||||
end.
|
||||
|
||||
@@ -608,7 +616,7 @@ sql_query_internal(#sql_query{} = Query) ->
|
||||
{error, <<"shutdown">>};
|
||||
?EX_RULE(Class, Reason, Stack) ->
|
||||
StackTrace = ?EX_STACK(Stack),
|
||||
?ERROR_MSG("Internal error while processing SQL query:~n** ~s",
|
||||
?ERROR_MSG("Internal error while processing SQL query:~n** ~ts",
|
||||
[misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
{error, <<"internal error">>}
|
||||
end,
|
||||
@@ -621,7 +629,7 @@ sql_query_internal(F) when is_function(F) ->
|
||||
end;
|
||||
sql_query_internal(Query) ->
|
||||
State = get(?STATE_KEY),
|
||||
?DEBUG("SQL: \"~s\"", [Query]),
|
||||
?DEBUG("SQL: \"~ts\"", [Query]),
|
||||
QueryTimeout = query_timeout(State#state.host),
|
||||
Res = case State#state.db_type of
|
||||
odbc ->
|
||||
@@ -736,7 +744,7 @@ pgsql_execute_sql_query(SQLQuery, State) ->
|
||||
pgsql:execute(State#state.db_ref, SQLQuery#sql_query.hash, Args),
|
||||
% {T, ExecuteRes} =
|
||||
% timer:tc(pgsql, execute, [State#state.db_ref, SQLQuery#sql_query.hash, Args]),
|
||||
% io:format("T ~s ~p~n", [SQLQuery#sql_query.hash, T]),
|
||||
% io:format("T ~ts ~p~n", [SQLQuery#sql_query.hash, T]),
|
||||
Res = pgsql_execute_to_odbc(ExecuteRes),
|
||||
sql_query_format_res(Res, SQLQuery).
|
||||
|
||||
@@ -751,7 +759,7 @@ sql_query_format_res({selected, _, Rows}, SQLQuery) ->
|
||||
?EX_RULE(Class, Reason, Stack) ->
|
||||
StackTrace = ?EX_STACK(Stack),
|
||||
?ERROR_MSG("Error while processing SQL query result:~n"
|
||||
"** Row: ~p~n** ~s",
|
||||
"** Row: ~p~n** ~ts",
|
||||
[Row,
|
||||
misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
[]
|
||||
@@ -764,6 +772,22 @@ sql_query_format_res(Res, _SQLQuery) ->
|
||||
sql_query_to_iolist(SQLQuery) ->
|
||||
generic_sql_query_format(SQLQuery).
|
||||
|
||||
sql_begin() ->
|
||||
sql_query_internal(
|
||||
[{mssql, [<<"begin transaction;">>]},
|
||||
{any, [<<"begin;">>]}]).
|
||||
|
||||
sql_commit() ->
|
||||
sql_query_internal(
|
||||
[{mssql, [<<"commit transaction;">>]},
|
||||
{any, [<<"commit;">>]}]).
|
||||
|
||||
sql_rollback() ->
|
||||
sql_query_internal(
|
||||
[{mssql, [<<"rollback transaction;">>]},
|
||||
{any, [<<"rollback;">>]}]).
|
||||
|
||||
|
||||
%% Generate the OTP callback return tuple depending on the driver result.
|
||||
abort_on_driver_error({error, <<"query timed out">>} = Reply, From, Timestamp) ->
|
||||
reply(From, Reply, Timestamp),
|
||||
@@ -993,10 +1017,7 @@ log(Level, Format, Args) ->
|
||||
end.
|
||||
|
||||
db_opts(Host) ->
|
||||
Type = case ejabberd_option:sql_type(Host) of
|
||||
undefined -> odbc;
|
||||
T -> T
|
||||
end,
|
||||
Type = ejabberd_option:sql_type(Host),
|
||||
Server = ejabberd_option:sql_server(Host),
|
||||
Timeout = ejabberd_option:sql_connect_timeout(Host),
|
||||
Transport = case ejabberd_option:sql_ssl(Host) of
|
||||
@@ -1032,7 +1053,7 @@ warn_if_ssl_unsupported(tcp, _) ->
|
||||
warn_if_ssl_unsupported(ssl, pgsql) ->
|
||||
ok;
|
||||
warn_if_ssl_unsupported(ssl, Type) ->
|
||||
?WARNING_MSG("SSL connection is not supported for ~s", [Type]).
|
||||
?WARNING_MSG("SSL connection is not supported for ~ts", [Type]).
|
||||
|
||||
get_ssl_opts(ssl, Host) ->
|
||||
Opts1 = case ejabberd_option:sql_ssl_certfile(Host) of
|
||||
@@ -1068,8 +1089,8 @@ init_mssql(Host) ->
|
||||
undefined -> <<"ejabberd">>;
|
||||
D -> D
|
||||
end,
|
||||
FreeTDS = io_lib:fwrite("[~s]~n"
|
||||
"\thost = ~s~n"
|
||||
FreeTDS = io_lib:fwrite("[~ts]~n"
|
||||
"\thost = ~ts~n"
|
||||
"\tport = ~p~n"
|
||||
"\tclient charset = UTF-8~n"
|
||||
"\ttds version = 7.1~n",
|
||||
@@ -1080,16 +1101,16 @@ init_mssql(Host) ->
|
||||
"Setup = libtdsS.so~n"
|
||||
"UsageCount = 1~n"
|
||||
"FileUsage = 1~n", []),
|
||||
ODBCINI = io_lib:fwrite("[~s]~n"
|
||||
ODBCINI = io_lib:fwrite("[~ts]~n"
|
||||
"Description = MS SQL~n"
|
||||
"Driver = freetds~n"
|
||||
"Servername = ~s~n"
|
||||
"Database = ~s~n"
|
||||
"Servername = ~ts~n"
|
||||
"Database = ~ts~n"
|
||||
"Port = ~p~n",
|
||||
[Host, Host, DB, Port]),
|
||||
?DEBUG("~s:~n~s", [freetds_config(), FreeTDS]),
|
||||
?DEBUG("~s:~n~s", [odbcinst_config(), ODBCINST]),
|
||||
?DEBUG("~s:~n~s", [odbc_config(), ODBCINI]),
|
||||
?DEBUG("~ts:~n~ts", [freetds_config(), FreeTDS]),
|
||||
?DEBUG("~ts:~n~ts", [odbcinst_config(), ODBCINST]),
|
||||
?DEBUG("~ts:~n~ts", [odbc_config(), ODBCINI]),
|
||||
case filelib:ensure_dir(freetds_config()) of
|
||||
ok ->
|
||||
try
|
||||
@@ -1101,12 +1122,12 @@ init_mssql(Host) ->
|
||||
os:putenv("FREETDSCONF", freetds_config()),
|
||||
ok
|
||||
catch error:{badmatch, {error, Reason} = Err} ->
|
||||
?ERROR_MSG("Failed to create temporary files in ~s: ~s",
|
||||
?ERROR_MSG("Failed to create temporary files in ~ts: ~ts",
|
||||
[tmp_dir(), file:format_error(Reason)]),
|
||||
Err
|
||||
end;
|
||||
{error, Reason} = Err ->
|
||||
?ERROR_MSG("Failed to create temporary directory ~s: ~s",
|
||||
?ERROR_MSG("Failed to create temporary directory ~ts: ~ts",
|
||||
[tmp_dir(), file:format_error(Reason)]),
|
||||
Err
|
||||
end.
|
||||
@@ -1147,22 +1168,22 @@ current_time() ->
|
||||
%% ***IMPORTANT*** This error format requires extended_errors turned on.
|
||||
extended_error({"08S01", _, Reason}) ->
|
||||
% TCP Provider: The specified network name is no longer available
|
||||
?DEBUG("ODBC Link Failure: ~s", [Reason]),
|
||||
?DEBUG("ODBC Link Failure: ~ts", [Reason]),
|
||||
<<"Communication link failure">>;
|
||||
extended_error({"08001", _, Reason}) ->
|
||||
% Login timeout expired
|
||||
?DEBUG("ODBC Connect Timeout: ~s", [Reason]),
|
||||
?DEBUG("ODBC Connect Timeout: ~ts", [Reason]),
|
||||
<<"SQL connection failed">>;
|
||||
extended_error({"IMC01", _, Reason}) ->
|
||||
% The connection is broken and recovery is not possible
|
||||
?DEBUG("ODBC Link Failure: ~s", [Reason]),
|
||||
?DEBUG("ODBC Link Failure: ~ts", [Reason]),
|
||||
<<"Communication link failure">>;
|
||||
extended_error({"IMC06", _, Reason}) ->
|
||||
% The connection is broken and recovery is not possible
|
||||
?DEBUG("ODBC Link Failure: ~s", [Reason]),
|
||||
?DEBUG("ODBC Link Failure: ~ts", [Reason]),
|
||||
<<"Communication link failure">>;
|
||||
extended_error({Code, _, Reason}) ->
|
||||
?DEBUG("ODBC Error ~s: ~s", [Code, Reason]),
|
||||
?DEBUG("ODBC Error ~ts: ~ts", [Code, Reason]),
|
||||
iolist_to_binary(Reason);
|
||||
extended_error(Error) ->
|
||||
Error.
|
||||
@@ -1171,14 +1192,14 @@ check_error({error, Why} = Err, _Query) when Why == killed ->
|
||||
Err;
|
||||
check_error({error, Why}, #sql_query{} = Query) ->
|
||||
Err = extended_error(Why),
|
||||
?ERROR_MSG("SQL query '~s' at ~p failed: ~p",
|
||||
?ERROR_MSG("SQL query '~ts' at ~p failed: ~p",
|
||||
[Query#sql_query.hash, Query#sql_query.loc, Err]),
|
||||
{error, Err};
|
||||
check_error({error, Why}, Query) ->
|
||||
Err = extended_error(Why),
|
||||
case catch iolist_to_binary(Query) of
|
||||
SQuery when is_binary(SQuery) ->
|
||||
?ERROR_MSG("SQL query '~s' failed: ~p", [SQuery, Err]);
|
||||
?ERROR_MSG("SQL query '~ts' failed: ~p", [SQuery, Err]);
|
||||
_ ->
|
||||
?ERROR_MSG("SQL query ~p failed: ~p", [Query, Err])
|
||||
end,
|
||||
|
||||
@@ -27,15 +27,67 @@
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start_link/1, init/1, reload/1, is_started/1]).
|
||||
-export([start/1, stop/1, stop/0]).
|
||||
-export([start_link/0, start_link/1]).
|
||||
-export([init/1, reload/1, config_reloaded/0, is_started/1]).
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
start(Host) ->
|
||||
case is_started(Host) of
|
||||
true -> ok;
|
||||
false ->
|
||||
App = case ejabberd_option:sql_type(Host) of
|
||||
mysql -> p1_mysql;
|
||||
pgsql -> p1_pgsql;
|
||||
sqlite -> sqlite3;
|
||||
_ -> odbc
|
||||
end,
|
||||
ejabberd:start_app(App),
|
||||
Spec = #{id => gen_mod:get_module_proc(Host, ?MODULE),
|
||||
start => {ejabberd_sql_sup, start_link, [Host]},
|
||||
restart => transient,
|
||||
shutdown => infinity,
|
||||
type => supervisor,
|
||||
modules => [?MODULE]},
|
||||
case supervisor:start_child(ejabberd_db_sup, Spec) of
|
||||
{ok, _} -> ok;
|
||||
{error, {already_started, _}} -> ok;
|
||||
{error, Why} = Err ->
|
||||
?ERROR_MSG("Failed to start ~ts: ~p", [?MODULE, Why]),
|
||||
Err
|
||||
end
|
||||
end.
|
||||
|
||||
stop(Host) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
||||
case supervisor:terminate_child(ejabberd_db_sup, Proc) of
|
||||
ok -> supervisor:delete_child(ejabberd_db_sup, Proc);
|
||||
Err -> Err
|
||||
end.
|
||||
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
start_link(Host) ->
|
||||
supervisor:start_link({local,
|
||||
gen_mod:get_module_proc(Host, ?MODULE)},
|
||||
?MODULE, [Host]).
|
||||
|
||||
stop() ->
|
||||
ejabberd_hooks:delete(host_up, ?MODULE, start, 20),
|
||||
ejabberd_hooks:delete(host_down, ?MODULE, stop, 90),
|
||||
ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 20).
|
||||
|
||||
init([]) ->
|
||||
file:delete(ejabberd_sql:freetds_config()),
|
||||
file:delete(ejabberd_sql:odbc_config()),
|
||||
file:delete(ejabberd_sql:odbcinst_config()),
|
||||
ejabberd_hooks:add(host_up, ?MODULE, start, 20),
|
||||
ejabberd_hooks:add(host_down, ?MODULE, stop, 90),
|
||||
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20),
|
||||
ignore;
|
||||
init([Host]) ->
|
||||
Type = ejabberd_option:sql_type(Host),
|
||||
PoolSize = get_pool_size(Type, Host),
|
||||
@@ -49,6 +101,10 @@ init([Host]) ->
|
||||
end,
|
||||
{ok, {{one_for_one, PoolSize * 10, 1}, child_specs(Host, PoolSize)}}.
|
||||
|
||||
-spec config_reloaded() -> ok.
|
||||
config_reloaded() ->
|
||||
lists:foreach(fun reload/1, ejabberd_option:hosts()).
|
||||
|
||||
-spec reload(binary()) -> ok.
|
||||
reload(Host) ->
|
||||
case is_started(Host) of
|
||||
@@ -139,7 +195,7 @@ create_sqlite_tables(DB) ->
|
||||
[ok = sqlite3:sql_exec(DB, Q) || Q <- Qs],
|
||||
ok = sqlite3:sql_exec(DB, "commit");
|
||||
{error, Reason} ->
|
||||
?WARNING_MSG("Failed to read SQLite schema file: ~s",
|
||||
?WARNING_MSG("Failed to read SQLite schema file: ~ts",
|
||||
[file:format_error(Reason)])
|
||||
end.
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
-include("logger.hrl").
|
||||
-export([accept/1, start/3, start_link/3, listen_options/0]).
|
||||
fail() ->
|
||||
?CRITICAL_MSG("Listening module ~s is not available: "
|
||||
?CRITICAL_MSG("Listening module ~ts is not available: "
|
||||
"ejabberd is not compiled with STUN/TURN support",
|
||||
[?MODULE]),
|
||||
erlang:error(stun_not_compiled).
|
||||
@@ -104,7 +104,7 @@ prepare_turn_opts(Opts, _UseTurn = true) ->
|
||||
"'auth_realm' is undefined and "
|
||||
"'auth_type' is set to 'user', "
|
||||
"most likely the TURN relay won't "
|
||||
"be working properly. Using ~s as "
|
||||
"be working properly. Using ~ts as "
|
||||
"a fallback", [ejabberd_config:get_myname()]);
|
||||
true ->
|
||||
ok
|
||||
|
||||
@@ -46,12 +46,11 @@ init([]) ->
|
||||
worker(ejabberd_admin),
|
||||
supervisor(ejabberd_listener),
|
||||
worker(ejabberd_pkix),
|
||||
worker(ejabberd_acme),
|
||||
worker(acl),
|
||||
worker(ejabberd_shaper),
|
||||
supervisor(ejabberd_db_sup),
|
||||
supervisor(ejabberd_backend_sup),
|
||||
supervisor(ejabberd_rdbms),
|
||||
supervisor(ejabberd_sql_sup),
|
||||
worker(ejabberd_iq),
|
||||
worker(ejabberd_router),
|
||||
worker(ejabberd_router_multicast),
|
||||
@@ -64,6 +63,7 @@ init([]) ->
|
||||
worker(ejabberd_captcha),
|
||||
worker(ext_mod),
|
||||
supervisor(ejabberd_gen_mod_sup, gen_mod),
|
||||
worker(ejabberd_acme),
|
||||
worker(ejabberd_auth),
|
||||
worker(ejabberd_oauth)]}}.
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ handle_event({set_alarm, {process_memory_high_watermark, Pid}}, State) ->
|
||||
case proc_stat(Pid, get_app_pids()) of
|
||||
#proc_stat{name = Name} = ProcStat ->
|
||||
error_logger:warning_msg(
|
||||
"Process ~p consumes more than 5% of OS memory (~s)~n",
|
||||
"Process ~p consumes more than 5% of OS memory (~ts)~n",
|
||||
[Name, format_proc(ProcStat)]),
|
||||
handle_overload(State),
|
||||
{ok, State};
|
||||
@@ -140,8 +140,8 @@ handle_overload(_State, Procs) ->
|
||||
error_logger:warning_msg(
|
||||
"The system is overloaded with ~b messages "
|
||||
"queued by ~b process(es) (~b%) "
|
||||
"from the following applications: ~s; "
|
||||
"the top processes are:~n~s~n",
|
||||
"from the following applications: ~ts; "
|
||||
"the top processes are:~n~ts~n",
|
||||
[TotalMsgs, ProcsNum,
|
||||
round(ProcsNum*100/length(Procs)),
|
||||
format_apps(Apps),
|
||||
@@ -246,13 +246,13 @@ format_proc(#proc_stat{qlen = Len, memory = Mem, initial_call = InitCall,
|
||||
current_function = CurrFun, ancestors = Ancs,
|
||||
application = App}) ->
|
||||
io_lib:format(
|
||||
"msgs = ~b, memory = ~b, initial_call = ~s, "
|
||||
"current_function = ~s, ancestors = ~w, application = ~w",
|
||||
"msgs = ~b, memory = ~b, initial_call = ~ts, "
|
||||
"current_function = ~ts, ancestors = ~w, application = ~w",
|
||||
[Len, Mem, format_mfa(InitCall), format_mfa(CurrFun), Ancs, App]).
|
||||
|
||||
-spec format_mfa(mfa()) -> iodata().
|
||||
format_mfa({M, F, A}) when is_atom(M), is_atom(F), is_integer(A) ->
|
||||
io_lib:format("~s:~s/~b", [M, F, A]);
|
||||
io_lib:format("~ts:~ts/~b", [M, F, A]);
|
||||
format_mfa(WTF) ->
|
||||
io_lib:format("~w", [WTF]).
|
||||
|
||||
|
||||
+55
-38
@@ -166,7 +166,7 @@ process([<<"doc">>, LocalFile], _Request) ->
|
||||
"documentation with the environment variable "
|
||||
"EJABBERD_DOC_PATH. Check the ejabberd "
|
||||
"Guide for more information.">>,
|
||||
?WARNING_MSG("Problem '~p' accessing the local Guide file ~s", [Error, Help]),
|
||||
?WARNING_MSG("Problem '~p' accessing the local Guide file ~ts", [Error, Help]),
|
||||
case Error of
|
||||
eacces -> {403, [], <<"Forbidden", Help/binary>>};
|
||||
enoent -> {307, [{<<"Location">>, <<"http://docs.ejabberd.im/admin/guide/configuration/">>}], <<"Not found", Help/binary>>};
|
||||
@@ -213,31 +213,36 @@ process(RPath,
|
||||
#request{auth = Auth, lang = Lang, host = HostHTTP,
|
||||
method = Method} =
|
||||
Request) ->
|
||||
case get_auth_admin(Auth, HostHTTP, RPath, Method) of
|
||||
{ok, {User, Server}} ->
|
||||
AJID = get_jid(Auth, HostHTTP, Method),
|
||||
process_admin(global,
|
||||
Request#request{path = RPath,
|
||||
us = {User, Server}},
|
||||
AJID);
|
||||
{unauthorized, <<"no-auth-provided">>} ->
|
||||
{401,
|
||||
[{<<"WWW-Authenticate">>,
|
||||
<<"basic realm=\"ejabberd\"">>}],
|
||||
ejabberd_web:make_xhtml([?XCT(<<"h1">>,
|
||||
?T("Unauthorized"))])};
|
||||
{unauthorized, Error} ->
|
||||
{BadUser, _BadPass} = Auth,
|
||||
{IPT, _Port} = Request#request.ip,
|
||||
IPS = ejabberd_config:may_hide_data(misc:ip_to_list(IPT)),
|
||||
?WARNING_MSG("Access of ~p from ~p failed with error: ~p",
|
||||
[BadUser, IPS, Error]),
|
||||
{401,
|
||||
[{<<"WWW-Authenticate">>,
|
||||
<<"basic realm=\"auth error, retry login "
|
||||
"to ejabberd\"">>}],
|
||||
ejabberd_web:make_xhtml([?XCT(<<"h1">>,
|
||||
?T("Unauthorized"))])}
|
||||
case ejabberd_router:is_my_host(HostHTTP) of
|
||||
true ->
|
||||
case get_auth_admin(Auth, HostHTTP, RPath, Method) of
|
||||
{ok, {User, Server}} ->
|
||||
AJID = get_jid(Auth, HostHTTP, Method),
|
||||
process_admin(global,
|
||||
Request#request{path = RPath,
|
||||
us = {User, Server}},
|
||||
AJID);
|
||||
{unauthorized, <<"no-auth-provided">>} ->
|
||||
{401,
|
||||
[{<<"WWW-Authenticate">>,
|
||||
<<"basic realm=\"ejabberd\"">>}],
|
||||
ejabberd_web:make_xhtml([?XCT(<<"h1">>,
|
||||
?T("Unauthorized"))])};
|
||||
{unauthorized, Error} ->
|
||||
{BadUser, _BadPass} = Auth,
|
||||
{IPT, _Port} = Request#request.ip,
|
||||
IPS = ejabberd_config:may_hide_data(misc:ip_to_list(IPT)),
|
||||
?WARNING_MSG("Access of ~p from ~p failed with error: ~p",
|
||||
[BadUser, IPS, Error]),
|
||||
{401,
|
||||
[{<<"WWW-Authenticate">>,
|
||||
<<"basic realm=\"auth error, retry login "
|
||||
"to ejabberd\"">>}],
|
||||
ejabberd_web:make_xhtml([?XCT(<<"h1">>,
|
||||
?T("Unauthorized"))])}
|
||||
end;
|
||||
false ->
|
||||
ejabberd_web:error(not_found)
|
||||
end.
|
||||
|
||||
get_auth_admin(Auth, HostHTTP, RPath, Method) ->
|
||||
@@ -260,6 +265,13 @@ get_auth_admin(Auth, HostHTTP, RPath, Method) ->
|
||||
|
||||
get_auth_account(HostOfRule, AccessRule, User, Server,
|
||||
Pass) ->
|
||||
case lists:member(Server, ejabberd_config:get_option(hosts)) of
|
||||
true -> get_auth_account2(HostOfRule, AccessRule, User, Server, Pass);
|
||||
false -> {unauthorized, <<"inexistent-host">>}
|
||||
end.
|
||||
|
||||
get_auth_account2(HostOfRule, AccessRule, User, Server,
|
||||
Pass) ->
|
||||
case ejabberd_auth:check_password(User, <<"">>, Server, Pass) of
|
||||
true ->
|
||||
case any_rules_allowed(HostOfRule, AccessRule,
|
||||
@@ -438,7 +450,7 @@ process_admin(_Host, #request{path = [<<"additions.js">>]}, _) ->
|
||||
process_admin(global, #request{path = [<<"vhosts">>], lang = Lang}, AJID) ->
|
||||
Res = list_vhosts(Lang, AJID),
|
||||
make_xhtml((?H1GL((translate:translate(Lang, ?T("Virtual Hosts"))),
|
||||
<<"virtualhosting">>, ?T("Virtual Hosting")))
|
||||
<<"virtual-hosting">>, ?T("Virtual Hosting")))
|
||||
++ Res,
|
||||
global, Lang, AJID);
|
||||
process_admin(Host, #request{path = [<<"users">>], q = Query,
|
||||
@@ -473,8 +485,8 @@ process_admin(Host, #request{path = [<<"last-activity">>],
|
||||
list_last_activity(Host, Lang, false, Month);
|
||||
_ -> list_last_activity(Host, Lang, true, Month)
|
||||
end,
|
||||
make_xhtml([?XCT(<<"h1">>, ?T("Users Last Activity"))]
|
||||
++
|
||||
PageH1 = ?H1GL(translate:translate(Lang, ?T("Users Last Activity")), <<"mod-last">>, <<"mod_last">>),
|
||||
make_xhtml(PageH1 ++
|
||||
[?XAE(<<"form">>,
|
||||
[{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
|
||||
[?CT(?T("Period: ")),
|
||||
@@ -504,8 +516,8 @@ process_admin(Host, #request{path = [<<"last-activity">>],
|
||||
Host, Lang, AJID);
|
||||
process_admin(Host, #request{path = [<<"stats">>], lang = Lang}, AJID) ->
|
||||
Res = get_stats(Host, Lang),
|
||||
make_xhtml([?XCT(<<"h1">>, ?T("Statistics"))] ++ Res,
|
||||
Host, Lang, AJID);
|
||||
PageH1 = ?H1GL(translate:translate(Lang, ?T("Statistics")), <<"mod-stats">>, <<"mod_stats">>),
|
||||
make_xhtml(PageH1 ++ Res, Host, Lang, AJID);
|
||||
process_admin(Host, #request{path = [<<"user">>, U],
|
||||
q = Query, lang = Lang}, AJID) ->
|
||||
case ejabberd_auth:user_exists(U, Host) of
|
||||
@@ -915,7 +927,7 @@ user_info(User, Server, Query, Lang) ->
|
||||
end;
|
||||
_ -> translate:translate(Lang, ?T("Online"))
|
||||
end,
|
||||
[?XC(<<"h1">>, (str:format(translate:translate(Lang, ?T("User ~s")),
|
||||
[?XC(<<"h1">>, (str:format(translate:translate(Lang, ?T("User ~ts")),
|
||||
[us_to_list(US)])))]
|
||||
++
|
||||
case Res of
|
||||
@@ -1280,9 +1292,7 @@ get_node(global, Node, [<<"backup">>], Query, Lang) ->
|
||||
[?CT(?T("Export data of users in a host to PIEFXIS "
|
||||
"files (XEP-0227):")),
|
||||
?C(<<" ">>),
|
||||
?INPUT(<<"text">>,
|
||||
<<"export_piefxis_host_dirhost">>,
|
||||
(ejabberd_config:get_myname()))]),
|
||||
make_select_host(Lang, <<"export_piefxis_host_dirhost">>)]),
|
||||
?XE(<<"td">>,
|
||||
[?INPUT(<<"text">>,
|
||||
<<"export_piefxis_host_dirpath">>,
|
||||
@@ -1296,9 +1306,7 @@ get_node(global, Node, [<<"backup">>], Query, Lang) ->
|
||||
[?CT(?T("Export all tables as SQL queries "
|
||||
"to a file:")),
|
||||
?C(<<" ">>),
|
||||
?INPUT(<<"text">>,
|
||||
<<"export_sql_filehost">>,
|
||||
(ejabberd_config:get_myname()))]),
|
||||
make_select_host(Lang, <<"export_sql_filehost">>)]),
|
||||
?XE(<<"td">>,
|
||||
[?INPUT(<<"text">>,
|
||||
<<"export_sql_filepath">>,
|
||||
@@ -1468,6 +1476,15 @@ node_parse_query(Node, Query) ->
|
||||
end
|
||||
end.
|
||||
|
||||
make_select_host(Lang, Name) ->
|
||||
?XAE(<<"select">>,
|
||||
[{<<"name">>, Name}],
|
||||
(lists:map(fun (Host) ->
|
||||
?XACT(<<"option">>,
|
||||
([{<<"value">>, Host}]), Host)
|
||||
end,
|
||||
ejabberd_config:get_option(hosts)))).
|
||||
|
||||
db_storage_select(ID, Opt, Lang) ->
|
||||
?XAE(<<"select">>,
|
||||
[{<<"name">>, <<"table", ID/binary>>}],
|
||||
|
||||
@@ -211,7 +211,7 @@ ws_loop(FrameInfo, Socket, WsHandleLoopPid, SocketMode, Shaper) ->
|
||||
?DEBUG("TCP connection was closed, exit", []),
|
||||
websocket_close(Socket, WsHandleLoopPid, SocketMode, 0);
|
||||
{tcp_error, Socket, Reason} ->
|
||||
?DEBUG("TCP connection error: ~s", [inet:format_error(Reason)]),
|
||||
?DEBUG("TCP connection error: ~ts", [inet:format_error(Reason)]),
|
||||
websocket_close(Socket, WsHandleLoopPid, SocketMode, 0);
|
||||
{'DOWN', Ref, process, WsHandleLoopPid, Reason} ->
|
||||
Code = case Reason of
|
||||
@@ -356,7 +356,7 @@ process_frame(#frame_info{unprocessed = none,
|
||||
8 -> % Close
|
||||
CloseCode = case Unmasked of
|
||||
<<Code:16/integer-big, Message/binary>> ->
|
||||
?DEBUG("WebSocket close op: ~p ~s",
|
||||
?DEBUG("WebSocket close op: ~p ~ts",
|
||||
[Code, Message]),
|
||||
Code;
|
||||
<<Code:16/integer-big>> ->
|
||||
|
||||
@@ -78,7 +78,7 @@ process(_, #request{method = 'POST', data = Data, opts = Opts, ip = {IP, _}}) ->
|
||||
El ->
|
||||
case fxmlrpc:decode(El) of
|
||||
{error, _} = Err ->
|
||||
?ERROR_MSG("XML-RPC request ~s failed with reason: ~p",
|
||||
?ERROR_MSG("XML-RPC request ~ts failed with reason: ~p",
|
||||
[Data, Err]),
|
||||
{400, [],
|
||||
#xmlel{name = <<"h1">>, attrs = [],
|
||||
|
||||
+5
-5
@@ -86,8 +86,8 @@ export(Server, Output, Module1) ->
|
||||
case export(LServer, Table, IO, ConvertFun) of
|
||||
{atomic, ok} -> ok;
|
||||
{aborted, {no_exists, _}} ->
|
||||
?WARNING_MSG("Ignoring export for module ~s: "
|
||||
"Mnesia table ~s doesn't exist (most likely "
|
||||
?WARNING_MSG("Ignoring export for module ~ts: "
|
||||
"Mnesia table ~ts doesn't exist (most likely "
|
||||
"because the module is unused)",
|
||||
[Module1, Table]);
|
||||
{aborted, Reason} ->
|
||||
@@ -133,9 +133,9 @@ import(Mod, Server, Dir, ToType) ->
|
||||
{error, enoent} ->
|
||||
ok;
|
||||
eof ->
|
||||
?INFO_MSG("It seems like SQL dump ~s is empty", [FileName]);
|
||||
?INFO_MSG("It seems like SQL dump ~ts is empty", [FileName]);
|
||||
Err ->
|
||||
?ERROR_MSG("Failed to open SQL dump ~s: ~s",
|
||||
?ERROR_MSG("Failed to open SQL dump ~ts: ~ts",
|
||||
[FileName, format_error(Err)])
|
||||
end
|
||||
end, import_info(Mod)),
|
||||
@@ -260,7 +260,7 @@ import_rows(LServer, FromType, ToType, Tab, Mod, Dump, FieldsNumber) ->
|
||||
eof ->
|
||||
ok;
|
||||
Err ->
|
||||
?ERROR_MSG("Failed to read row from SQL dump: ~s",
|
||||
?ERROR_MSG("Failed to read row from SQL dump: ~ts",
|
||||
[format_error(Err)])
|
||||
end.
|
||||
|
||||
|
||||
+4
-4
@@ -692,7 +692,7 @@ handle_info({Tag, _Socket, Data}, StateName, S)
|
||||
end;
|
||||
handle_info({Tag, _Socket}, Fsm_state, S)
|
||||
when Tag == tcp_closed; Tag == ssl_closed ->
|
||||
?WARNING_MSG("LDAP server closed the connection: ~s:~p~nIn "
|
||||
?WARNING_MSG("LDAP server closed the connection: ~ts:~p~nIn "
|
||||
"State: ~p",
|
||||
[S#eldap.host, S#eldap.port, Fsm_state]),
|
||||
{next_state, connecting, close_and_retry(S)};
|
||||
@@ -985,7 +985,7 @@ close_and_retry(S) ->
|
||||
close_and_retry(S, ?RETRY_TIMEOUT).
|
||||
|
||||
report_bind_failure(Host, Port, Reason) ->
|
||||
?WARNING_MSG("LDAP bind failed on ~s:~p~nReason: ~p",
|
||||
?WARNING_MSG("LDAP bind failed on ~ts:~p~nReason: ~p",
|
||||
[Host, Port, Reason]).
|
||||
|
||||
%%-----------------------------------------------------------------------
|
||||
@@ -1048,7 +1048,7 @@ connect_bind(S) ->
|
||||
[{packet, asn1}, {active, true}, {keepalive, true},
|
||||
{send_timeout, ?SEND_TIMEOUT}, binary]
|
||||
end,
|
||||
?DEBUG("Connecting to LDAP server at ~s:~p with options ~p",
|
||||
?DEBUG("Connecting to LDAP server at ~ts:~p with options ~p",
|
||||
[Host, S#eldap.port, Opts]),
|
||||
HostS = binary_to_list(Host),
|
||||
SockMod = case S#eldap.tls of
|
||||
@@ -1070,7 +1070,7 @@ connect_bind(S) ->
|
||||
{ok, connecting, NewS#eldap{host = Host}}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("LDAP connection to ~s:~b failed: ~s",
|
||||
?ERROR_MSG("LDAP connection to ~ts:~b failed: ~ts",
|
||||
[Host, S#eldap.port, format_error(SockMod, Reason)]),
|
||||
NewS = close_and_retry(S),
|
||||
{ok, connecting, NewS#eldap{host = Host}}
|
||||
|
||||
+2
-2
@@ -159,7 +159,7 @@ update() ->
|
||||
end, Contrib, modules_spec(sources_dir(), "*/*")),
|
||||
Repos = maps:fold(fun(Repo, _Mods, Acc) ->
|
||||
Update = add_sources(Repo),
|
||||
?INFO_MSG("Update packages from repo ~s: ~p", [Repo, Update]),
|
||||
?INFO_MSG("Update packages from repo ~ts: ~p", [Repo, Update]),
|
||||
case Update of
|
||||
ok -> Acc;
|
||||
Error -> [{repository, Repo, Error}|Acc]
|
||||
@@ -168,7 +168,7 @@ update() ->
|
||||
Res = lists:foldl(fun({Package, Spec}, Acc) ->
|
||||
Repo = proplists:get_value(url, Spec, ""),
|
||||
Update = add_sources(Package, Repo),
|
||||
?INFO_MSG("Update package ~s: ~p", [Package, Update]),
|
||||
?INFO_MSG("Update package ~ts: ~p", [Package, Update]),
|
||||
case Update of
|
||||
ok -> Acc;
|
||||
Error -> [{Package, Repo, Error}|Acc]
|
||||
|
||||
+3
-3
@@ -129,7 +129,7 @@ handle_call({cmd, Cmd, EndTime}, _From, State) ->
|
||||
{reply, decode_bool(N), State};
|
||||
{Port, Data} ->
|
||||
?ERROR_MSG("Received unexpected response from external "
|
||||
"authentication program '~s': ~p "
|
||||
"authentication program '~ts': ~p "
|
||||
"(port = ~p, pid = ~w)",
|
||||
[State#state.prog, Data, Port, State#state.os_pid]),
|
||||
{reply, {error, unexpected_response}, State};
|
||||
@@ -149,11 +149,11 @@ handle_info({'EXIT', Port, _Reason}, #state{port = Port,
|
||||
start_time = Time} = State) ->
|
||||
case curr_time() - Time of
|
||||
Diff when Diff < 1000 ->
|
||||
?ERROR_MSG("Failed to start external authentication program '~s'",
|
||||
?ERROR_MSG("Failed to start external authentication program '~ts'",
|
||||
[State#state.prog]),
|
||||
{stop, normal, State};
|
||||
_ ->
|
||||
?ERROR_MSG("External authentication program '~s' has terminated "
|
||||
?ERROR_MSG("External authentication program '~ts' has terminated "
|
||||
"unexpectedly (pid=~w), restarting via supervisor...",
|
||||
[State#state.prog, State#state.os_pid]),
|
||||
{stop, normal, State}
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@
|
||||
start(Host) ->
|
||||
case extauth:prog_name(Host) of
|
||||
undefined ->
|
||||
?ERROR_MSG("Option 'extauth_program' is not set for '~s'",
|
||||
?ERROR_MSG("Option 'extauth_program' is not set for '~ts'",
|
||||
[Host]),
|
||||
ignore;
|
||||
Prog ->
|
||||
|
||||
@@ -113,7 +113,7 @@ process_iq(_Host, Module, Function, IQ) ->
|
||||
ok
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to process iq:~n~s~n** ~s",
|
||||
?ERROR_MSG("Failed to process iq:~n~ts~n** ~ts",
|
||||
[xmpp:pp(IQ),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
Txt = ?T("Module failed to handle the query"),
|
||||
|
||||
+16
-35
@@ -56,7 +56,7 @@
|
||||
|
||||
-callback start(binary(), opts()) -> ok | {ok, pid()} | {error, term()}.
|
||||
-callback stop(binary()) -> any().
|
||||
-callback reload(binary(), opts(), opts()) -> ok | {ok, pid()}.
|
||||
-callback reload(binary(), opts(), opts()) -> ok | {ok, pid()} | {error, term()}.
|
||||
-callback mod_opt_type(atom()) -> econf:validator().
|
||||
-callback mod_options(binary()) -> [{atom(), term()} | atom()].
|
||||
-callback depends(binary(), opts()) -> [{module(), hard | soft}].
|
||||
@@ -121,7 +121,7 @@ stop_child(Proc) ->
|
||||
-spec start_modules() -> any().
|
||||
start_modules() ->
|
||||
Hosts = ejabberd_option:hosts(),
|
||||
?INFO_MSG("Loading modules for ~s", [format_hosts_list(Hosts)]),
|
||||
?INFO_MSG("Loading modules for ~ts", [misc:format_hosts_list(Hosts)]),
|
||||
lists:foreach(fun start_modules/1, Hosts).
|
||||
|
||||
-spec start_modules(binary()) -> ok.
|
||||
@@ -144,7 +144,7 @@ start_module(Host, Module) ->
|
||||
|
||||
-spec start_module(binary(), atom(), opts(), integer()) -> ok | {ok, pid()}.
|
||||
start_module(Host, Module, Opts, Order) ->
|
||||
?DEBUG("Loading ~s at ~s", [Module, Host]),
|
||||
?DEBUG("Loading ~ts at ~ts", [Module, Host]),
|
||||
store_options(Host, Module, Opts, Order),
|
||||
try case Module:start(Host, Opts) of
|
||||
ok -> ok;
|
||||
@@ -205,7 +205,7 @@ reload_modules(Host) ->
|
||||
reload_module(Host, Module, NewOpts, OldOpts, Order) ->
|
||||
case erlang:function_exported(Module, reload, 3) of
|
||||
true ->
|
||||
?DEBUG("Reloading ~s at ~s", [Module, Host]),
|
||||
?DEBUG("Reloading ~ts at ~ts", [Module, Host]),
|
||||
store_options(Host, Module, NewOpts, Order),
|
||||
try case Module:reload(Host, NewOpts, OldOpts) of
|
||||
ok -> ok;
|
||||
@@ -222,7 +222,7 @@ reload_module(Host, Module, NewOpts, OldOpts, Order) ->
|
||||
erlang:raise(Class, Reason, StackTrace)
|
||||
end;
|
||||
false ->
|
||||
?WARNING_MSG("Module ~s doesn't support reloading "
|
||||
?WARNING_MSG("Module ~ts doesn't support reloading "
|
||||
"and will be restarted", [Module]),
|
||||
stop_module(Host, Module),
|
||||
start_module(Host, Module, NewOpts, Order)
|
||||
@@ -284,14 +284,14 @@ stop_module(Host, Module) ->
|
||||
|
||||
-spec stop_module_keep_config(binary(), atom()) -> error | ok.
|
||||
stop_module_keep_config(Host, Module) ->
|
||||
?DEBUG("Stopping ~s at ~s", [Module, Host]),
|
||||
?DEBUG("Stopping ~ts at ~ts", [Module, Host]),
|
||||
try Module:stop(Host) of
|
||||
_ ->
|
||||
ets:delete(ejabberd_modules, {Module, Host}),
|
||||
ok
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to stop module ~s at ~s:~n** ~s",
|
||||
?ERROR_MSG("Failed to stop module ~ts at ~ts:~n** ~ts",
|
||||
[Module, Host,
|
||||
misc:format_exception(2, Class, Reason, StackTrace)]),
|
||||
error
|
||||
@@ -429,42 +429,23 @@ is_equal_opt(Opt, NewOpts, OldOpts) ->
|
||||
format_module_error(Module, Fun, Arity, Opts, Class, Reason, St) ->
|
||||
case {Class, Reason} of
|
||||
{error, {bad_return, Module, {error, _} = Err}} ->
|
||||
io_lib:format("Failed to ~s module ~s: ~s",
|
||||
io_lib:format("Failed to ~ts module ~ts: ~ts",
|
||||
[Fun, Module, misc:format_val(Err)]);
|
||||
{error, {bad_return, Module, Ret}} ->
|
||||
io_lib:format("Module ~s returned unexpected value from ~s/~B:~n"
|
||||
io_lib:format("Module ~ts returned unexpected value from ~ts/~B:~n"
|
||||
"** Error: ~p~n"
|
||||
"** Hint: this is either not an ejabberd module "
|
||||
"or it implements ejabberd API incorrectly",
|
||||
[Module, Fun, Arity, Ret]);
|
||||
_ ->
|
||||
io_lib:format("Internal error of module ~s has "
|
||||
"occurred during ~s:~n"
|
||||
io_lib:format("Internal error of module ~ts has "
|
||||
"occurred during ~ts:~n"
|
||||
"** Options: ~p~n"
|
||||
"** ~s",
|
||||
"** ~ts",
|
||||
[Module, Fun, Opts,
|
||||
misc:format_exception(2, Class, Reason, St)])
|
||||
end.
|
||||
|
||||
-spec format_hosts_list([binary()]) -> iolist().
|
||||
format_hosts_list([Host]) ->
|
||||
Host;
|
||||
format_hosts_list([H1, H2]) ->
|
||||
[H1, " and ", H2];
|
||||
format_hosts_list([H1, H2, H3]) ->
|
||||
[H1, ", ", H2, " and ", H3];
|
||||
format_hosts_list([H1, H2|Hs]) ->
|
||||
io_lib:format("~s, ~s and ~B more hosts",
|
||||
[H1, H2, length(Hs)]).
|
||||
|
||||
-spec format_cycle([atom()]) -> iolist().
|
||||
format_cycle([M1]) ->
|
||||
atom_to_list(M1);
|
||||
format_cycle([M1, M2]) ->
|
||||
[atom_to_list(M1), " and ", atom_to_list(M2)];
|
||||
format_cycle([M|Ms]) ->
|
||||
atom_to_list(M) ++ ", " ++ format_cycle(Ms).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Validation
|
||||
%%%===================================================================
|
||||
@@ -592,14 +573,14 @@ sort_modules(Host, ModOpts) ->
|
||||
|
||||
-spec warn_soft_dep_fail(module(), module()) -> ok.
|
||||
warn_soft_dep_fail(DepMod, Mod) ->
|
||||
?WARNING_MSG("Module ~s is recommended for module "
|
||||
"~s but is not found in the config",
|
||||
?WARNING_MSG("Module ~ts is recommended for module "
|
||||
"~ts but is not found in the config",
|
||||
[DepMod, Mod]).
|
||||
|
||||
-spec warn_cyclic_dep([module()]) -> ok.
|
||||
warn_cyclic_dep(Path) ->
|
||||
?WARNING_MSG("Cyclic dependency detected between modules ~s. "
|
||||
?WARNING_MSG("Cyclic dependency detected between modules ~ts. "
|
||||
"This is either a bug, or the modules are not "
|
||||
"supposed to work together in this configuration. "
|
||||
"The modules will still be loaded though",
|
||||
[format_cycle(Path)]).
|
||||
[misc:format_cycle(Path)]).
|
||||
|
||||
+6
-6
@@ -50,24 +50,24 @@ import_file(File) ->
|
||||
El when is_record(El, xmlel) ->
|
||||
case catch process_xdb(User, Server, El) of
|
||||
{'EXIT', Reason} ->
|
||||
?ERROR_MSG("Error while processing file \"~s\": "
|
||||
?ERROR_MSG("Error while processing file \"~ts\": "
|
||||
"~p~n",
|
||||
[File, Reason]),
|
||||
{error, Reason};
|
||||
_ -> ok
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Can't parse file \"~s\": ~p~n",
|
||||
?ERROR_MSG("Can't parse file \"~ts\": ~p~n",
|
||||
[File, Reason]),
|
||||
{error, Reason}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Can't read file \"~s\": ~p~n",
|
||||
?ERROR_MSG("Can't read file \"~ts\": ~p~n",
|
||||
[File, Reason]),
|
||||
{error, Reason}
|
||||
end;
|
||||
false ->
|
||||
?ERROR_MSG("Illegal user/server name in file \"~s\"~n",
|
||||
?ERROR_MSG("Illegal user/server name in file \"~ts\"~n",
|
||||
[File]),
|
||||
{error, <<"illegal user/server">>}
|
||||
end.
|
||||
@@ -144,7 +144,7 @@ xdb_data(User, Server, #xmlel{attrs = Attrs} = El) ->
|
||||
From,
|
||||
[{XMLNS, El#xmlel{attrs = NewAttrs}}]);
|
||||
_ ->
|
||||
?DEBUG("Unknown namespace \"~s\"~n", [XMLNS])
|
||||
?DEBUG("Unknown namespace \"~ts\"~n", [XMLNS])
|
||||
end,
|
||||
ok
|
||||
end.
|
||||
@@ -166,7 +166,7 @@ process_offline(Server, To, #xmlel{children = Els}) ->
|
||||
ok
|
||||
catch _:{xmpp_codec, Why} ->
|
||||
Txt = xmpp:format_error(Why),
|
||||
?ERROR_MSG("Failed to decode XML '~s': ~s",
|
||||
?ERROR_MSG("Failed to decode XML '~ts': ~ts",
|
||||
[fxml:element_to_binary(El), Txt])
|
||||
end
|
||||
end, Els).
|
||||
|
||||
+44
-6
@@ -40,7 +40,8 @@
|
||||
read_css/1, read_img/1, read_js/1, read_lua/1, try_url/1,
|
||||
intersection/2, format_val/1, cancel_timer/1, unique_timestamp/0,
|
||||
is_mucsub_message/1, best_match/2, pmap/2, peach/2, format_exception/4,
|
||||
parse_ip_mask/1, match_ip_mask/3]).
|
||||
parse_ip_mask/1, match_ip_mask/3, format_hosts_list/1, format_cycle/1,
|
||||
delete_dir/1]).
|
||||
|
||||
%% Deprecated functions
|
||||
-export([decode_base64/1, encode_base64/1]).
|
||||
@@ -314,7 +315,7 @@ try_read_file(Path) ->
|
||||
file:close(Fd),
|
||||
iolist_to_binary(Path);
|
||||
{error, Why} ->
|
||||
?ERROR_MSG("Failed to read ~s: ~s", [Path, file:format_error(Why)]),
|
||||
?ERROR_MSG("Failed to read ~ts: ~ts", [Path, file:format_error(Why)]),
|
||||
erlang:error(badarg)
|
||||
end.
|
||||
|
||||
@@ -329,15 +330,15 @@ try_url(URL0) ->
|
||||
end,
|
||||
case http_uri:parse(URL) of
|
||||
{ok, {Scheme, _, _, _, _, _}} when Scheme /= http, Scheme /= https ->
|
||||
?ERROR_MSG("Unsupported URI scheme: ~s", [URL]),
|
||||
?ERROR_MSG("Unsupported URI scheme: ~ts", [URL]),
|
||||
erlang:error(badarg);
|
||||
{ok, {_, _, Host, _, _, _}} when Host == ""; Host == <<"">> ->
|
||||
?ERROR_MSG("Invalid URL: ~s", [URL]),
|
||||
?ERROR_MSG("Invalid URL: ~ts", [URL]),
|
||||
erlang:error(badarg);
|
||||
{ok, _} ->
|
||||
iolist_to_binary(URL);
|
||||
{error, _} ->
|
||||
?ERROR_MSG("Invalid URL: ~s", [URL]),
|
||||
?ERROR_MSG("Invalid URL: ~ts", [URL]),
|
||||
erlang:error(badarg)
|
||||
end.
|
||||
|
||||
@@ -546,6 +547,43 @@ match_ip_mask({0, 0, 0, 0, 0, 16#FFFF, _, _} = IP,
|
||||
match_ip_mask(_, _, _) ->
|
||||
false.
|
||||
|
||||
-spec format_hosts_list([binary(), ...]) -> iolist().
|
||||
format_hosts_list([Host]) ->
|
||||
Host;
|
||||
format_hosts_list([H1, H2]) ->
|
||||
[H1, " and ", H2];
|
||||
format_hosts_list([H1, H2, H3]) ->
|
||||
[H1, ", ", H2, " and ", H3];
|
||||
format_hosts_list([H1, H2|Hs]) ->
|
||||
io_lib:format("~ts, ~ts and ~B more hosts",
|
||||
[H1, H2, length(Hs)]).
|
||||
|
||||
-spec format_cycle([atom(), ...]) -> iolist().
|
||||
format_cycle([M1]) ->
|
||||
atom_to_list(M1);
|
||||
format_cycle([M1, M2]) ->
|
||||
[atom_to_list(M1), " and ", atom_to_list(M2)];
|
||||
format_cycle([M|Ms]) ->
|
||||
atom_to_list(M) ++ ", " ++ format_cycle(Ms).
|
||||
|
||||
-spec delete_dir(file:filename_all()) -> ok | {error, file:posix()}.
|
||||
delete_dir(Dir) ->
|
||||
try
|
||||
{ok, Entries} = file:list_dir(Dir),
|
||||
lists:foreach(fun(Path) ->
|
||||
case filelib:is_dir(Path) of
|
||||
true ->
|
||||
ok = delete_dir(Path);
|
||||
false ->
|
||||
ok = file:delete(Path)
|
||||
end
|
||||
end, [filename:join(Dir, Entry) || Entry <- Entries]),
|
||||
ok = file:del_dir(Dir)
|
||||
catch
|
||||
_:{badmatch, {error, Error}} ->
|
||||
{error, Error}
|
||||
end.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
@@ -583,7 +621,7 @@ read_file(Path) ->
|
||||
{ok, Data} ->
|
||||
{ok, Data};
|
||||
{error, Why} = Err ->
|
||||
?ERROR_MSG("Failed to read file ~s: ~s",
|
||||
?ERROR_MSG("Failed to read file ~ts: ~ts",
|
||||
[Path, file:format_error(Why)]),
|
||||
Err
|
||||
end.
|
||||
|
||||
@@ -1504,13 +1504,13 @@ send_stanza(FromString, ToString, Stanza) ->
|
||||
Pkt = xmpp:decode(El, ?NS_CLIENT, CodecOpts),
|
||||
ejabberd_router:route(xmpp:set_from_to(Pkt, From, To))
|
||||
catch _:{xmpp_codec, Why} ->
|
||||
io:format("incorrect stanza: ~s~n", [xmpp:format_error(Why)]),
|
||||
io:format("incorrect stanza: ~ts~n", [xmpp:format_error(Why)]),
|
||||
{error, Why};
|
||||
_:{badmatch, {error, Why}} ->
|
||||
io:format("invalid xml: ~p~n", [Why]),
|
||||
{error, Why};
|
||||
_:{bad_jid, S} ->
|
||||
io:format("malformed JID: ~s~n", [S]),
|
||||
io:format("malformed JID: ~ts~n", [S]),
|
||||
{error, "JID malformed"}
|
||||
end.
|
||||
|
||||
@@ -1530,7 +1530,7 @@ send_stanza_c2s(Username, Host, Resource, Stanza) ->
|
||||
io:format("invalid xml: ~p~n", [Why]),
|
||||
Err;
|
||||
_:{xmpp_codec, Why} ->
|
||||
io:format("incorrect stanza: ~s~n", [xmpp:format_error(Why)]),
|
||||
io:format("incorrect stanza: ~ts~n", [xmpp:format_error(Why)]),
|
||||
{error, Why}
|
||||
end.
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ create_index(#state{dbtype = mysql} = State, Table, Index, Cols) ->
|
||||
SCols, ");"]).
|
||||
|
||||
sql_query(Host, Query) ->
|
||||
io:format("executing \"~s\" on ~s~n", [Query, Host]),
|
||||
io:format("executing \"~ts\" on ~ts~n", [Query, Host]),
|
||||
case ejabberd_sql:sql_query(Host, Query) of
|
||||
{error, Error} ->
|
||||
io:format("error: ~p~n", [Error]),
|
||||
|
||||
@@ -724,7 +724,7 @@ send_motd({#presence{type = available},
|
||||
ok
|
||||
end
|
||||
catch _:{xmpp_codec, Why} ->
|
||||
?ERROR_MSG("Failed to decode motd packet ~p: ~s",
|
||||
?ERROR_MSG("Failed to decode motd packet ~p: ~ts",
|
||||
[Packet, xmpp:format_error(Why)])
|
||||
end;
|
||||
_ ->
|
||||
@@ -808,7 +808,7 @@ get_stored_motd(LServer) ->
|
||||
#message{body = Body, subject = Subject} ->
|
||||
{xmpp:get_text(Subject), xmpp:get_text(Body)}
|
||||
catch _:{xmpp_codec, Why} ->
|
||||
?ERROR_MSG("Failed to decode motd packet ~p: ~s",
|
||||
?ERROR_MSG("Failed to decode motd packet ~p: ~ts",
|
||||
[Packet, xmpp:format_error(Why)])
|
||||
end;
|
||||
_ ->
|
||||
|
||||
@@ -155,6 +155,6 @@ parse_element(XML) ->
|
||||
{ok, El};
|
||||
_ ->
|
||||
?ERROR_MSG("Malformed XML element in SQL table "
|
||||
"'motd' for username='': ~s", [XML]),
|
||||
"'motd' for username='': ~ts", [XML]),
|
||||
{error, db_failure}
|
||||
end.
|
||||
|
||||
+18
-18
@@ -96,11 +96,11 @@ pubsub_publish_item(LServer, ?NS_AVATAR_METADATA,
|
||||
set_vcard_avatar(From, Photo, #{})
|
||||
end;
|
||||
_ ->
|
||||
?WARNING_MSG("Invalid avatar metadata of ~s@~s published "
|
||||
"with item id ~s",
|
||||
?WARNING_MSG("Invalid avatar metadata of ~ts@~ts published "
|
||||
"with item id ~ts",
|
||||
[LUser, LServer, ItemId])
|
||||
catch _:{xmpp_codec, Why} ->
|
||||
?WARNING_MSG("Failed to decode avatar metadata of ~s@~s: ~s",
|
||||
?WARNING_MSG("Failed to decode avatar metadata of ~ts@~ts: ~ts",
|
||||
[LUser, LServer, xmpp:format_error(Why)])
|
||||
end;
|
||||
pubsub_publish_item(_, _, _, _, _, _) ->
|
||||
@@ -210,26 +210,26 @@ get_avatar_data(JID, ItemID) ->
|
||||
{ok, Data};
|
||||
_ ->
|
||||
?WARNING_MSG("Invalid avatar data detected "
|
||||
"for ~s@~s with item id ~s",
|
||||
"for ~ts@~ts with item id ~ts",
|
||||
[LUser, LServer, ItemID]),
|
||||
{error, invalid_data}
|
||||
catch _:{xmpp_codec, Why} ->
|
||||
?WARNING_MSG("Failed to decode avatar data for "
|
||||
"~s@~s with item id ~s: ~s",
|
||||
"~ts@~ts with item id ~ts: ~ts",
|
||||
[LUser, LServer, ItemID,
|
||||
xmpp:format_error(Why)]),
|
||||
{error, invalid_data}
|
||||
end;
|
||||
#pubsub_item{payload = []} ->
|
||||
?WARNING_MSG("Empty avatar data detected "
|
||||
"for ~s@~s with item id ~s",
|
||||
"for ~ts@~ts with item id ~ts",
|
||||
[LUser, LServer, ItemID]),
|
||||
{error, invalid_data};
|
||||
{error, #stanza_error{reason = 'item-not-found'}} ->
|
||||
{error, notfound};
|
||||
{error, Reason} ->
|
||||
?WARNING_MSG("Failed to get item for ~s@~s at node ~s "
|
||||
"with item id ~s: ~p",
|
||||
?WARNING_MSG("Failed to get item for ~ts@~ts at node ~ts "
|
||||
"with item id ~ts: ~p",
|
||||
[LUser, LServer, ?NS_AVATAR_METADATA, ItemID, Reason]),
|
||||
{error, internal_error}
|
||||
end.
|
||||
@@ -248,12 +248,12 @@ get_avatar_meta(#iq{from = JID}) ->
|
||||
{ok, ItemID, Meta};
|
||||
_ ->
|
||||
?WARNING_MSG("Invalid metadata payload detected "
|
||||
"for ~s@~s with item id ~s",
|
||||
"for ~ts@~ts with item id ~ts",
|
||||
[LUser, LServer, ItemID]),
|
||||
{error, invalid_metadata}
|
||||
catch _:{xmpp_codec, Why} ->
|
||||
?WARNING_MSG("Failed to decode metadata for "
|
||||
"~s@~s with item id ~s: ~s",
|
||||
"~ts@~ts with item id ~ts: ~ts",
|
||||
[LUser, LServer, ItemID,
|
||||
xmpp:format_error(Why)]),
|
||||
{error, invalid_metadata}
|
||||
@@ -261,7 +261,7 @@ get_avatar_meta(#iq{from = JID}) ->
|
||||
{error, #stanza_error{reason = 'item-not-found'}} ->
|
||||
{error, notfound};
|
||||
{error, Reason} ->
|
||||
?WARNING_MSG("Failed to get items for ~s@~s at node ~s: ~p",
|
||||
?WARNING_MSG("Failed to get items for ~ts@~ts at node ~ts: ~p",
|
||||
[LUser, LServer, ?NS_AVATAR_METADATA, Reason]),
|
||||
{error, internal_error}
|
||||
end.
|
||||
@@ -305,16 +305,16 @@ publish_avatar(#iq{from = JID} = IQ, Meta, MimeType, Data, ItemID) ->
|
||||
{result, _} ->
|
||||
IQ;
|
||||
{error, StanzaErr} ->
|
||||
?ERROR_MSG("Failed to publish avatar metadata for ~s: ~p",
|
||||
?ERROR_MSG("Failed to publish avatar metadata for ~ts: ~p",
|
||||
[jid:encode(JID), StanzaErr]),
|
||||
{stop, StanzaErr}
|
||||
end;
|
||||
{error, #stanza_error{reason = 'not-acceptable'} = StanzaErr} ->
|
||||
?WARNING_MSG("Failed to publish avatar data for ~s: ~p",
|
||||
?WARNING_MSG("Failed to publish avatar data for ~ts: ~p",
|
||||
[jid:encode(JID), StanzaErr]),
|
||||
{stop, StanzaErr};
|
||||
{error, StanzaErr} ->
|
||||
?ERROR_MSG("Failed to publish avatar data for ~s: ~p",
|
||||
?ERROR_MSG("Failed to publish avatar data for ~ts: ~p",
|
||||
[jid:encode(JID), StanzaErr]),
|
||||
{stop, StanzaErr}
|
||||
end.
|
||||
@@ -346,7 +346,7 @@ convert_avatar(LUser, LServer, Data, Rules) ->
|
||||
if NewType == undefined ->
|
||||
pass;
|
||||
true ->
|
||||
?DEBUG("Converting avatar of ~s@~s: ~s -> ~s",
|
||||
?DEBUG("Converting avatar of ~ts@~ts: ~ts -> ~ts",
|
||||
[LUser, LServer, Type, NewType]),
|
||||
RateLimit = mod_avatar_opt:rate_limit(LServer),
|
||||
Opts = [{limit_by, {LUser, LServer}},
|
||||
@@ -356,7 +356,7 @@ convert_avatar(LUser, LServer, Data, Rules) ->
|
||||
{ok, encode_mime_type(NewType), NewData};
|
||||
{error, Reason} = Err ->
|
||||
?ERROR_MSG("Failed to convert avatar of "
|
||||
"~s@~s (~s -> ~s): ~s",
|
||||
"~ts@~ts (~ts -> ~ts): ~ts",
|
||||
[LUser, LServer, Type, NewType,
|
||||
eimp:format_error(Reason)]),
|
||||
Err
|
||||
@@ -393,11 +393,11 @@ get_vcard(#jid{luser = LUser, lserver = LServer}) ->
|
||||
#vcard_temp{} = VCard ->
|
||||
{ok, VCard};
|
||||
_ ->
|
||||
?ERROR_MSG("Invalid vCard of ~s@~s in the database",
|
||||
?ERROR_MSG("Invalid vCard of ~ts@~ts in the database",
|
||||
[LUser, LServer]),
|
||||
{error, invalid_vcard}
|
||||
catch _:{xmpp_codec, Why} ->
|
||||
?ERROR_MSG("Failed to decode vCard of ~s@~s: ~s",
|
||||
?ERROR_MSG("Failed to decode vCard of ~ts@~ts: ~ts",
|
||||
[LUser, LServer, xmpp:format_error(Why)]),
|
||||
{error, invalid_vcard}
|
||||
end.
|
||||
|
||||
@@ -116,7 +116,7 @@ filter_subscription(Acc, #presence{from = From, to = To, lang = Lang,
|
||||
case mod_block_strangers_opt:log(LServer) of
|
||||
true ->
|
||||
?INFO_MSG("Challenge subscription request "
|
||||
"from stranger ~s to ~s with "
|
||||
"from stranger ~ts to ~ts with "
|
||||
"CAPTCHA",
|
||||
[jid:encode(From), jid:encode(To)]);
|
||||
false ->
|
||||
@@ -164,7 +164,7 @@ check_message(#message{from = From, to = To, lang = Lang} = Msg) ->
|
||||
Log = mod_block_strangers_opt:log(LServer),
|
||||
if
|
||||
Log ->
|
||||
?INFO_MSG("~s message from stranger ~s to ~s",
|
||||
?INFO_MSG("~ts message from stranger ~ts to ~ts",
|
||||
[if Drop -> "Rejecting";
|
||||
true -> "Allow"
|
||||
end,
|
||||
|
||||
@@ -166,7 +166,7 @@ process_block(#iq{from = From} = IQ, LJIDs) ->
|
||||
broadcast_event(From, #block{items = Items}),
|
||||
xmpp:make_iq_result(IQ);
|
||||
{error, notfound} ->
|
||||
?ERROR_MSG("Failed to set default list '~s': "
|
||||
?ERROR_MSG("Failed to set default list '~ts': "
|
||||
"the list should exist, but not found",
|
||||
[Name]),
|
||||
err_db_failure(IQ);
|
||||
|
||||
+119
-26
@@ -33,12 +33,16 @@
|
||||
terminate/2, code_change/3, start_link/0]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
|
||||
-record(bosh, {sid = <<"">> :: binary() | '_',
|
||||
timestamp = erlang:timestamp() :: erlang:timestamp() | '_',
|
||||
pid = self() :: pid() | '$1'}).
|
||||
-define(CALL_TIMEOUT, timer:minutes(10)).
|
||||
|
||||
-record(state, {}).
|
||||
-record(bosh, {sid = <<"">> :: binary(),
|
||||
timestamp = erlang:timestamp() :: erlang:timestamp(),
|
||||
pid = self() :: pid()}).
|
||||
|
||||
-record(state, {nodes = #{} :: #{node() => {pid(), reference()}}}).
|
||||
-type state() :: #state{}.
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
@@ -49,6 +53,7 @@ init() ->
|
||||
transient, 5000, worker, [?MODULE]},
|
||||
case supervisor:start_child(ejabberd_backend_sup, Spec) of
|
||||
{ok, _Pid} -> ok;
|
||||
{error, {already_started, _}} -> ok;
|
||||
Err -> Err
|
||||
end.
|
||||
|
||||
@@ -59,28 +64,21 @@ start_link() ->
|
||||
use_cache() ->
|
||||
false.
|
||||
|
||||
-spec open_session(binary(), pid()) -> ok.
|
||||
open_session(SID, Pid) ->
|
||||
Session = #bosh{sid = SID, timestamp = erlang:timestamp(), pid = Pid},
|
||||
lists:foreach(
|
||||
fun(Node) when Node == node() ->
|
||||
gen_server:call(?MODULE, {write, Session});
|
||||
(Node) ->
|
||||
cluster_send({?MODULE, Node}, {write, Session})
|
||||
end, ejabberd_cluster:get_nodes()).
|
||||
gen_server:call(?MODULE, {write, Session}, ?CALL_TIMEOUT).
|
||||
|
||||
-spec close_session(binary()) -> ok.
|
||||
close_session(SID) ->
|
||||
case mnesia:dirty_read(bosh, SID) of
|
||||
[Session] ->
|
||||
lists:foreach(
|
||||
fun(Node) when Node == node() ->
|
||||
gen_server:call(?MODULE, {delete, Session});
|
||||
(Node) ->
|
||||
cluster_send({?MODULE, Node}, {delete, Session})
|
||||
end, ejabberd_cluster:get_nodes());
|
||||
gen_server:call(?MODULE, {delete, Session}, ?CALL_TIMEOUT);
|
||||
[] ->
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec find_session(binary()) -> {ok, pid()} | {error, notfound}.
|
||||
find_session(SID) ->
|
||||
case mnesia:dirty_read(bosh, SID) of
|
||||
[#bosh{pid = Pid}] ->
|
||||
@@ -92,30 +90,90 @@ find_session(SID) ->
|
||||
%%%===================================================================
|
||||
%%% gen_server callbacks
|
||||
%%%===================================================================
|
||||
-spec init([]) -> {ok, state()}.
|
||||
init([]) ->
|
||||
setup_database(),
|
||||
multicast({join, node(), self()}),
|
||||
mnesia:subscribe(system),
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call({write, Session}, _From, State) ->
|
||||
Res = write_session(Session),
|
||||
{reply, Res, State};
|
||||
handle_call({delete, Session}, _From, State) ->
|
||||
Res = delete_session(Session),
|
||||
{reply, Res, State};
|
||||
-spec handle_call(_, _, state()) -> {reply, ok, state()} | {noreply, state()}.
|
||||
handle_call({write, Session} = Msg, _From, State) ->
|
||||
write_session(Session),
|
||||
multicast(Msg),
|
||||
{reply, ok, State};
|
||||
handle_call({delete, Session} = Msg, _From, State) ->
|
||||
delete_session(Session),
|
||||
multicast(Msg),
|
||||
{reply, ok, State};
|
||||
handle_call(Request, From, State) ->
|
||||
?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]),
|
||||
{noreply, State}.
|
||||
|
||||
-spec handle_cast(_, state()) -> {noreply, state()}.
|
||||
handle_cast(Msg, State) ->
|
||||
?WARNING_MSG("Unexpected cast: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
-spec handle_info(_, state()) -> {noreply, state()}.
|
||||
handle_info({write, Session}, State) ->
|
||||
write_session(Session),
|
||||
{noreply, State};
|
||||
handle_info({delete, Session}, State) ->
|
||||
delete_session(Session),
|
||||
{noreply, State};
|
||||
handle_info({join, Node, Pid}, State) ->
|
||||
ejabberd_cluster:send(Pid, {joined, node(), self()}),
|
||||
case maps:find(Node, State#state.nodes) of
|
||||
{ok, {Pid, _}} ->
|
||||
ok;
|
||||
_ ->
|
||||
ejabberd_cluster:send(Pid, {join, node(), self()})
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_info({joined, Node, Pid}, State) ->
|
||||
case maps:find(Node, State#state.nodes) of
|
||||
{ok, {Pid, _}} ->
|
||||
{noreply, State};
|
||||
Ret ->
|
||||
MRef = erlang:monitor(process, {?MODULE, Node}),
|
||||
Nodes = maps:put(Node, {Pid, MRef}, State#state.nodes),
|
||||
case Ret of
|
||||
error -> ejabberd_cluster:send(Pid, {first, self()});
|
||||
_ -> ok
|
||||
end,
|
||||
{noreply, State#state{nodes = Nodes}}
|
||||
end;
|
||||
handle_info({first, From}, State) ->
|
||||
ejabberd_cluster:send(From, {replica, node(), first_session()}),
|
||||
{noreply, State};
|
||||
handle_info({next, From, Key}, State) ->
|
||||
ejabberd_cluster:send(From, {replica, node(), next_session(Key)}),
|
||||
{noreply, State};
|
||||
handle_info({replica, _From, '$end_of_table'}, State) ->
|
||||
{noreply, State};
|
||||
handle_info({replica, From, Session}, State) ->
|
||||
write_session(Session),
|
||||
ejabberd_cluster:send(From, {next, self(), Session#bosh.sid}),
|
||||
{noreply, State};
|
||||
handle_info({'DOWN', _, process, {?MODULE, _}, _Info}, State) ->
|
||||
{noreply, State};
|
||||
handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
|
||||
Sessions =
|
||||
ets:select(
|
||||
bosh,
|
||||
ets:fun2ms(
|
||||
fun(#bosh{pid = Pid} = S) when node(Pid) == Node ->
|
||||
S
|
||||
end)),
|
||||
lists:foreach(
|
||||
fun(S) ->
|
||||
mnesia:dirty_delete_object(S)
|
||||
end, Sessions),
|
||||
Nodes = maps:remove(Node, State#state.nodes),
|
||||
{noreply, State#state{nodes = Nodes}};
|
||||
handle_info({mnesia_system_event, _}, State) ->
|
||||
{noreply, State};
|
||||
handle_info(Info, State) ->
|
||||
?WARNING_MSG("Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
@@ -129,22 +187,24 @@ code_change(_OldVsn, State, _Extra) ->
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
-spec write_session(#bosh{}) -> ok.
|
||||
write_session(#bosh{pid = Pid1, sid = SID, timestamp = T1} = S1) ->
|
||||
case mnesia:dirty_read(bosh, SID) of
|
||||
[#bosh{pid = Pid2, timestamp = T2} = S2] ->
|
||||
if Pid1 == Pid2 ->
|
||||
mnesia:dirty_write(S1);
|
||||
T1 < T2 ->
|
||||
cluster_send(Pid2, replaced),
|
||||
ejabberd_cluster:send(Pid2, replaced),
|
||||
mnesia:dirty_write(S1);
|
||||
true ->
|
||||
cluster_send(Pid1, replaced),
|
||||
ejabberd_cluster:send(Pid1, replaced),
|
||||
mnesia:dirty_write(S2)
|
||||
end;
|
||||
[] ->
|
||||
mnesia:dirty_write(S1)
|
||||
end.
|
||||
|
||||
-spec delete_session(#bosh{}) -> ok.
|
||||
delete_session(#bosh{sid = SID, pid = Pid1}) ->
|
||||
case mnesia:dirty_read(bosh, SID) of
|
||||
[#bosh{pid = Pid2}] ->
|
||||
@@ -157,8 +217,14 @@ delete_session(#bosh{sid = SID, pid = Pid1}) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
cluster_send(NodePid, Msg) ->
|
||||
erlang:send(NodePid, Msg, [noconnect, nosuspend]).
|
||||
-spec multicast(_) -> ok.
|
||||
multicast(Msg) ->
|
||||
lists:foreach(
|
||||
fun(Node) when Node /= node() ->
|
||||
ejabberd_cluster:send({?MODULE, Node}, Msg);
|
||||
(_) ->
|
||||
ok
|
||||
end, ejabberd_cluster:get_nodes()).
|
||||
|
||||
setup_database() ->
|
||||
case catch mnesia:table_info(bosh, attributes) of
|
||||
@@ -170,3 +236,30 @@ setup_database() ->
|
||||
ejabberd_mnesia:create(?MODULE, bosh,
|
||||
[{ram_copies, [node()]}, {local_content, true},
|
||||
{attributes, record_info(fields, bosh)}]).
|
||||
|
||||
-spec first_session() -> #bosh{} | '$end_of_table'.
|
||||
first_session() ->
|
||||
case mnesia:dirty_first(bosh) of
|
||||
'$end_of_table' ->
|
||||
'$end_of_table';
|
||||
First ->
|
||||
read_session(First)
|
||||
end.
|
||||
|
||||
-spec next_session(binary()) -> #bosh{} | '$end_of_table'.
|
||||
next_session(Prev) ->
|
||||
case mnesia:dirty_next(bosh, Prev) of
|
||||
'$end_of_table' ->
|
||||
'$end_of_table';
|
||||
Next ->
|
||||
read_session(Next)
|
||||
end.
|
||||
|
||||
-spec read_session(binary()) -> #bosh{} | '$end_of_table'.
|
||||
read_session(Key) ->
|
||||
case mnesia:dirty_read(bosh, Key) of
|
||||
[#bosh{pid = Pid} = Session] when node(Pid) == node() ->
|
||||
Session;
|
||||
_ ->
|
||||
next_session(Key)
|
||||
end.
|
||||
|
||||
@@ -89,7 +89,7 @@ find_session(SID) ->
|
||||
try
|
||||
{ok, binary_to_term(Pid)}
|
||||
catch _:badarg ->
|
||||
?ERROR_MSG("Malformed data in redis (key = '~s'): ~p",
|
||||
?ERROR_MSG("Malformed data in redis (key = '~ts'): ~p",
|
||||
[SID, Pid]),
|
||||
{error, db_failure}
|
||||
end;
|
||||
|
||||
@@ -241,23 +241,23 @@ build_forward_packet(JID, #message{type = T} = Msg, Sender, Dest, Direction) ->
|
||||
|
||||
-spec enable(binary(), binary(), binary(), binary()) -> ok | {error, any()}.
|
||||
enable(Host, U, R, CC)->
|
||||
?DEBUG("Enabling carbons for ~s@~s/~s", [U, Host, R]),
|
||||
?DEBUG("Enabling carbons for ~ts@~ts/~ts", [U, Host, R]),
|
||||
case ejabberd_sm:set_user_info(U, Host, R, carboncopy, CC) of
|
||||
ok -> ok;
|
||||
{error, Reason} = Err ->
|
||||
?ERROR_MSG("Failed to enable carbons for ~s@~s/~s: ~p",
|
||||
?ERROR_MSG("Failed to enable carbons for ~ts@~ts/~ts: ~p",
|
||||
[U, Host, R, Reason]),
|
||||
Err
|
||||
end.
|
||||
|
||||
-spec disable(binary(), binary(), binary()) -> ok | {error, any()}.
|
||||
disable(Host, U, R)->
|
||||
?DEBUG("Disabling carbons for ~s@~s/~s", [U, Host, R]),
|
||||
?DEBUG("Disabling carbons for ~ts@~ts/~ts", [U, Host, R]),
|
||||
case ejabberd_sm:del_user_info(U, Host, R, carboncopy) of
|
||||
ok -> ok;
|
||||
{error, notfound} -> ok;
|
||||
{error, Reason} = Err ->
|
||||
?ERROR_MSG("Failed to disable carbons for ~s@~s/~s: ~p",
|
||||
?ERROR_MSG("Failed to disable carbons for ~ts@~ts/~ts: ~p",
|
||||
[U, Host, R, Reason]),
|
||||
Err
|
||||
end.
|
||||
|
||||
@@ -227,7 +227,7 @@ filter_presence({#presence{meta = #{csi_resend := true}}, _} = Acc) ->
|
||||
filter_presence({#presence{to = To, type = Type} = Pres,
|
||||
#{csi_state := inactive} = C2SState})
|
||||
when Type == available; Type == unavailable ->
|
||||
?DEBUG("Got availability presence stanza for ~s", [jid:encode(To)]),
|
||||
?DEBUG("Got availability presence stanza for ~ts", [jid:encode(To)]),
|
||||
enqueue_stanza(presence, Pres, C2SState);
|
||||
filter_presence(Acc) ->
|
||||
Acc.
|
||||
@@ -246,7 +246,7 @@ filter_chat_states({#message{from = From, to = To} = Msg,
|
||||
%% conversations across clients.
|
||||
Acc;
|
||||
_ ->
|
||||
?DEBUG("Got standalone chat state notification for ~s",
|
||||
?DEBUG("Got standalone chat state notification for ~ts",
|
||||
[jid:encode(To)]),
|
||||
enqueue_stanza(chatstate, Msg, C2SState)
|
||||
end;
|
||||
@@ -265,7 +265,7 @@ filter_pep({#message{to = To} = Msg,
|
||||
undefined ->
|
||||
Acc;
|
||||
Node ->
|
||||
?DEBUG("Got PEP notification for ~s", [jid:encode(To)]),
|
||||
?DEBUG("Got PEP notification for ~ts", [jid:encode(To)]),
|
||||
enqueue_stanza({pep, Node}, Msg, C2SState)
|
||||
end;
|
||||
filter_pep(Acc) ->
|
||||
@@ -277,7 +277,7 @@ filter_other({Stanza, #{jid := JID} = C2SState} = Acc) when ?is_stanza(Stanza) -
|
||||
#{csi_resend := true} ->
|
||||
Acc;
|
||||
_ ->
|
||||
?DEBUG("Won't add stanza for ~s to CSI queue", [jid:encode(JID)]),
|
||||
?DEBUG("Won't add stanza for ~ts to CSI queue", [jid:encode(JID)]),
|
||||
From = case xmpp:get_from(Stanza) of
|
||||
undefined -> JID;
|
||||
F -> F
|
||||
@@ -328,7 +328,7 @@ dequeue_sender(#jid{luser = U, lserver = S} = Sender,
|
||||
%% This may happen when the module is (re)loaded in runtime
|
||||
init_csi_state(C2SState);
|
||||
Q ->
|
||||
?DEBUG("Flushing packets of ~s@~s from CSI queue of ~s",
|
||||
?DEBUG("Flushing packets of ~ts@~ts from CSI queue of ~ts",
|
||||
[U, S, jid:encode(JID)]),
|
||||
{Elems, Q1} = queue_take(Sender, Q),
|
||||
C2SState1 = flush_stanzas(C2SState, Elems),
|
||||
@@ -337,7 +337,7 @@ dequeue_sender(#jid{luser = U, lserver = S} = Sender,
|
||||
|
||||
-spec flush_queue(c2s_state()) -> c2s_state().
|
||||
flush_queue(#{csi_queue := Q, jid := JID} = C2SState) ->
|
||||
?DEBUG("Flushing CSI queue of ~s", [jid:encode(JID)]),
|
||||
?DEBUG("Flushing CSI queue of ~ts", [jid:encode(JID)]),
|
||||
C2SState1 = flush_stanzas(C2SState, queue_to_list(Q)),
|
||||
C2SState1#{csi_queue => queue_new()}.
|
||||
|
||||
|
||||
+14
-14
@@ -663,7 +663,7 @@ get_outgoing_s2s(Host, Lang) ->
|
||||
Host == FH orelse str:suffix(DotHost, FH)],
|
||||
lists:map(
|
||||
fun (T) ->
|
||||
Name = str:format(tr(Lang, ?T("To ~s")),[T]),
|
||||
Name = str:format(tr(Lang, ?T("To ~ts")),[T]),
|
||||
#disco_item{jid = jid:make(Host),
|
||||
node = <<"outgoing s2s/", T/binary>>,
|
||||
name = Name}
|
||||
@@ -675,7 +675,7 @@ get_outgoing_s2s(Host, Lang, To) ->
|
||||
lists:map(
|
||||
fun ({F, _T}) ->
|
||||
Node = <<"outgoing s2s/", To/binary, "/", F/binary>>,
|
||||
Name = str:format(tr(Lang, ?T("From ~s")), [F]),
|
||||
Name = str:format(tr(Lang, ?T("From ~ts")), [F]),
|
||||
#disco_item{jid = jid:make(Host), node = Node, name = Name}
|
||||
end,
|
||||
lists:keysort(
|
||||
@@ -821,7 +821,7 @@ get_form(_Host, [<<"running nodes">>, ENode, <<"DB">>],
|
||||
case ejabberd_cluster:call(Node, mnesia, system_info, [tables]) of
|
||||
{badrpc, Reason} ->
|
||||
?ERROR_MSG("RPC call mnesia:system_info(tables) on node "
|
||||
"~s failed: ~p", [Node, Reason]),
|
||||
"~ts failed: ~p", [Node, Reason]),
|
||||
{error, xmpp:err_internal_server_error()};
|
||||
Tables ->
|
||||
STables = lists:sort(Tables),
|
||||
@@ -844,7 +844,7 @@ get_form(_Host, [<<"running nodes">>, ENode, <<"DB">>],
|
||||
fields = [?HFIELD()|Fs]}}
|
||||
catch _:{case_clause, {badrpc, Reason}} ->
|
||||
?ERROR_MSG("RPC call mnesia:table_info/2 "
|
||||
"on node ~s failed: ~p", [Node, Reason]),
|
||||
"on node ~ts failed: ~p", [Node, Reason]),
|
||||
{error, xmpp:err_internal_server_error()}
|
||||
end
|
||||
end
|
||||
@@ -1139,11 +1139,11 @@ set_form(_From, _Host,
|
||||
Node, mnesia, backup, [binary_to_list(String)],
|
||||
timer:minutes(10)) of
|
||||
{badrpc, Reason} ->
|
||||
?ERROR_MSG("RPC call mnesia:backup(~s) to node ~s "
|
||||
?ERROR_MSG("RPC call mnesia:backup(~ts) to node ~ts "
|
||||
"failed: ~p", [String, Node, Reason]),
|
||||
{error, xmpp:err_internal_server_error()};
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("RPC call mnesia:backup(~s) to node ~s "
|
||||
?ERROR_MSG("RPC call mnesia:backup(~ts) to node ~ts "
|
||||
"failed: ~p", [String, Node, Reason]),
|
||||
{error, xmpp:err_internal_server_error()};
|
||||
_ ->
|
||||
@@ -1172,12 +1172,12 @@ set_form(_From, _Host,
|
||||
Node, ejabberd_admin, restore,
|
||||
[String], timer:minutes(10)) of
|
||||
{badrpc, Reason} ->
|
||||
?ERROR_MSG("RPC call ejabberd_admin:restore(~s) to node "
|
||||
"~s failed: ~p", [String, Node, Reason]),
|
||||
?ERROR_MSG("RPC call ejabberd_admin:restore(~ts) to node "
|
||||
"~ts failed: ~p", [String, Node, Reason]),
|
||||
{error, xmpp:err_internal_server_error()};
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("RPC call ejabberd_admin:restore(~s) to node "
|
||||
"~s failed: ~p", [String, Node, Reason]),
|
||||
?ERROR_MSG("RPC call ejabberd_admin:restore(~ts) to node "
|
||||
"~ts failed: ~p", [String, Node, Reason]),
|
||||
{error, xmpp:err_internal_server_error()};
|
||||
_ ->
|
||||
{result, undefined}
|
||||
@@ -1205,12 +1205,12 @@ set_form(_From, _Host,
|
||||
Node, ejabberd_admin, dump_to_textfile,
|
||||
[String], timer:minutes(10)) of
|
||||
{badrpc, Reason} ->
|
||||
?ERROR_MSG("RPC call ejabberd_admin:dump_to_textfile(~s) "
|
||||
"to node ~s failed: ~p", [String, Node, Reason]),
|
||||
?ERROR_MSG("RPC call ejabberd_admin:dump_to_textfile(~ts) "
|
||||
"to node ~ts failed: ~p", [String, Node, Reason]),
|
||||
{error, xmpp:err_internal_server_error()};
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("RPC call ejabberd_admin:dump_to_textfile(~s) "
|
||||
"to node ~s failed: ~p", [String, Node, Reason]),
|
||||
?ERROR_MSG("RPC call ejabberd_admin:dump_to_textfile(~ts) "
|
||||
"to node ~ts failed: ~p", [String, Node, Reason]),
|
||||
{error, xmpp:err_internal_server_error()};
|
||||
_ ->
|
||||
{result, undefined}
|
||||
|
||||
@@ -168,7 +168,7 @@ handle_cast({component_connected, Host}, State) ->
|
||||
allow ->
|
||||
send_disco_queries(ServerHost, Host, NS);
|
||||
deny ->
|
||||
?DEBUG("Denied delegation for ~s on ~s", [Host, NS])
|
||||
?DEBUG("Denied delegation for ~ts on ~ts", [Host, NS])
|
||||
end
|
||||
end, NSAttrsAccessList),
|
||||
{noreply, State};
|
||||
@@ -177,8 +177,8 @@ handle_cast({component_disconnected, Host}, State) ->
|
||||
Delegations =
|
||||
maps:filter(
|
||||
fun({NS, Type}, {H, _}) when H == Host ->
|
||||
?INFO_MSG("Remove delegation of namespace '~s' "
|
||||
"from external component '~s'",
|
||||
?INFO_MSG("Remove delegation of namespace '~ts' "
|
||||
"from external component '~ts'",
|
||||
[NS, Host]),
|
||||
gen_iq_handler:remove_iq_handler(Type, ServerHost, NS),
|
||||
false;
|
||||
@@ -293,7 +293,7 @@ process_iq_result(#iq{from = From, to = To, id = ID, lang = Lang} = IQ,
|
||||
end
|
||||
catch _:_ ->
|
||||
?ERROR_MSG("Got iq-result with invalid delegated "
|
||||
"payload:~n~s", [xmpp:pp(ResIQ)]),
|
||||
"payload:~n~ts", [xmpp:pp(ResIQ)]),
|
||||
Txt = ?T("External component failure"),
|
||||
Err = xmpp:err_internal_server_error(Txt, Lang),
|
||||
ejabberd_router:route_error(IQ, Err)
|
||||
@@ -320,12 +320,12 @@ process_disco_info(ServerHost, Type, Host, NS, Info) ->
|
||||
gen_iq_handler:add_iq_handler(Type, ServerHost, NS, ?MODULE, Type),
|
||||
ejabberd_router:route(Msg),
|
||||
set_delegations(ServerHost, Delegations1),
|
||||
?INFO_MSG("Namespace '~s' is delegated to external component '~s'",
|
||||
?INFO_MSG("Namespace '~ts' is delegated to external component '~ts'",
|
||||
[NS, Host]);
|
||||
{ok, {AnotherHost, _}} ->
|
||||
?WARNING_MSG("Failed to delegate namespace '~s' to "
|
||||
"external component '~s' because it's already "
|
||||
"delegated to '~s'",
|
||||
?WARNING_MSG("Failed to delegate namespace '~ts' to "
|
||||
"external component '~ts' because it's already "
|
||||
"delegated to '~ts'",
|
||||
[NS, Host, AnotherHost])
|
||||
end.
|
||||
|
||||
|
||||
@@ -217,10 +217,10 @@ log_and_disconnect(#{ip := {Addr, _}, lang := Lang} = State, Attempts, UnbanTS)
|
||||
UnbanDate = format_date(
|
||||
calendar:now_to_universal_time(msec_to_now(UnbanTS))),
|
||||
Format = ?T("Too many (~p) failed authentications "
|
||||
"from this IP address (~s). The address "
|
||||
"will be unblocked at ~s UTC"),
|
||||
"from this IP address (~ts). The address "
|
||||
"will be unblocked at ~ts UTC"),
|
||||
Args = [Attempts, IP, UnbanDate],
|
||||
?WARNING_MSG("Connection attempt from blacklisted IP ~s: ~s",
|
||||
?WARNING_MSG("Connection attempt from blacklisted IP ~ts: ~ts",
|
||||
[IP, io_lib:fwrite(Format, Args)]),
|
||||
Err = xmpp:serr_policy_violation({Format, Args}, Lang),
|
||||
{stop, ejabberd_c2s:send(State, Err)}.
|
||||
|
||||
@@ -255,7 +255,7 @@ handle(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) ->
|
||||
?EX_RULE(Class, Error, Stack) ->
|
||||
StackTrace = ?EX_STACK(Stack),
|
||||
?ERROR_MSG("REST API Error: "
|
||||
"~s(~p) -> ~p:~p ~p",
|
||||
"~ts(~p) -> ~p:~p ~p",
|
||||
[Call, hide_sensitive_args(Args),
|
||||
Class, Error, StackTrace]),
|
||||
{500, <<"internal_error">>}
|
||||
@@ -286,7 +286,7 @@ get_elem_delete(Call, A, L, F) ->
|
||||
case proplists:get_all_values(A, L) of
|
||||
[Value] -> {Value, proplists:delete(A, L)};
|
||||
[_, _ | _] ->
|
||||
?INFO_MSG("Command ~s call rejected, it has duplicate attribute ~w",
|
||||
?INFO_MSG("Command ~ts call rejected, it has duplicate attribute ~w",
|
||||
[Call, A]),
|
||||
throw({invalid_parameter,
|
||||
io_lib:format("Request have duplicate argument: ~w", [A])});
|
||||
@@ -295,7 +295,7 @@ get_elem_delete(Call, A, L, F) ->
|
||||
{list, _} ->
|
||||
{[], L};
|
||||
_ ->
|
||||
?INFO_MSG("Command ~s call rejected, missing attribute ~w",
|
||||
?INFO_MSG("Command ~ts call rejected, missing attribute ~w",
|
||||
[Call, A]),
|
||||
throw({invalid_parameter,
|
||||
io_lib:format("Request have missing argument: ~w", [A])})
|
||||
@@ -318,7 +318,7 @@ format_args(Call, Args, ArgsFormat) ->
|
||||
[] -> R;
|
||||
L when is_list(L) ->
|
||||
ExtraArgs = [N || {N, _} <- L],
|
||||
?INFO_MSG("Command ~s call rejected, it has unknown arguments ~w",
|
||||
?INFO_MSG("Command ~ts call rejected, it has unknown arguments ~w",
|
||||
[Call, ExtraArgs]),
|
||||
throw({invalid_parameter,
|
||||
io_lib:format("Request have unknown arguments: ~w", [ExtraArgs])})
|
||||
@@ -506,9 +506,9 @@ json_error(HTTPCode, JSONCode, Message) ->
|
||||
|
||||
log(Call, Args, {Addr, Port}) ->
|
||||
AddrS = misc:ip_to_list({Addr, Port}),
|
||||
?INFO_MSG("API call ~s ~p from ~s:~p", [Call, hide_sensitive_args(Args), AddrS, Port]);
|
||||
?INFO_MSG("API call ~ts ~p from ~ts:~p", [Call, hide_sensitive_args(Args), AddrS, Port]);
|
||||
log(Call, Args, IP) ->
|
||||
?INFO_MSG("API call ~s ~p (~p)", [Call, hide_sensitive_args(Args), IP]).
|
||||
?INFO_MSG("API call ~ts ~p (~p)", [Call, hide_sensitive_args(Args), IP]).
|
||||
|
||||
hide_sensitive_args(Args=[_H|_T]) ->
|
||||
lists:map( fun({<<"password">>, Password}) -> {<<"password">>, ejabberd_config:may_hide_data(Password)};
|
||||
|
||||
@@ -137,7 +137,7 @@ initialize(Host, Opts) ->
|
||||
ContentTypes = build_list_content_types(
|
||||
mod_http_fileserver_opt:content_types(Opts),
|
||||
?DEFAULT_CONTENT_TYPES),
|
||||
?DEBUG("Known content types: ~s",
|
||||
?DEBUG("Known content types: ~ts",
|
||||
[str:join([[$*, K, " -> ", V] || {K, V} <- ContentTypes],
|
||||
<<", ">>)]),
|
||||
#state{host = Host,
|
||||
@@ -278,7 +278,7 @@ process(LocalPath, #request{host = Host, auth = Auth, headers = RHeaders} = Requ
|
||||
add_to_log(FileSize, Code, Request#request{host = VHost}),
|
||||
{Code, Headers, Contents}
|
||||
catch _:{Why, _} when Why == noproc; Why == invalid_domain; Why == unregistered_route ->
|
||||
?DEBUG("Received an HTTP request with Host: ~s, "
|
||||
?DEBUG("Received an HTTP request with Host: ~ts, "
|
||||
"but couldn't find the related "
|
||||
"ejabberd virtual host", [Host]),
|
||||
{FileSize1, Code1, Headers1, Contents1} = ?HTTP_ERR_HOST_UNKNOWN,
|
||||
@@ -342,7 +342,7 @@ serve_index(FileName, [Index | T], CH, DefaultContentType, ContentTypes) ->
|
||||
end.
|
||||
|
||||
serve_not_modified(FileInfo, FileName, CustomHeaders) ->
|
||||
?DEBUG("Delivering not modified: ~s", [FileName]),
|
||||
?DEBUG("Delivering not modified: ~ts", [FileName]),
|
||||
{0, 304,
|
||||
[{<<"Server">>, <<"ejabberd">>},
|
||||
{<<"Last-Modified">>, last_modified(FileInfo)}
|
||||
@@ -351,7 +351,7 @@ serve_not_modified(FileInfo, FileName, CustomHeaders) ->
|
||||
%% Assume the file exists if we got this far and attempt to read it in
|
||||
%% and serve it up.
|
||||
serve_file(FileInfo, FileName, CustomHeaders, DefaultContentType, ContentTypes) ->
|
||||
?DEBUG("Delivering: ~s", [FileName]),
|
||||
?DEBUG("Delivering: ~ts", [FileName]),
|
||||
ContentType = content_type(FileName, DefaultContentType,
|
||||
ContentTypes),
|
||||
{FileInfo#file_info.size, 200,
|
||||
@@ -414,7 +414,7 @@ add_to_log(File, FileSize, Code, Request) ->
|
||||
%% Missing time zone = (`+' | `-') 4*digit
|
||||
%% Missing protocol version: HTTP/1.1
|
||||
%% For reference: http://httpd.apache.org/docs/2.2/logs.html
|
||||
io:format(File, "~s - - [~p/~p/~p:~p:~p:~p] \"~s /~s~s\" ~p ~p ~p ~p~n",
|
||||
io:format(File, "~ts - - [~p/~p/~p:~p:~p:~p] \"~ts /~ts~ts\" ~p ~p ~p ~p~n",
|
||||
[IP, Day, Month, Year, Hour, Minute, Second, Request#request.method, Path, Query, Code,
|
||||
FileSize, Referer, UserAgent]).
|
||||
|
||||
|
||||
+155
-130
@@ -61,6 +61,7 @@
|
||||
%% gen_mod/supervisor callbacks.
|
||||
-export([start/2,
|
||||
stop/1,
|
||||
reload/3,
|
||||
depends/2,
|
||||
mod_opt_type/1,
|
||||
mod_options/1]).
|
||||
@@ -90,23 +91,23 @@
|
||||
-include("translate.hrl").
|
||||
|
||||
-record(state,
|
||||
{server_host :: binary(),
|
||||
hosts :: [binary()],
|
||||
name :: binary(),
|
||||
access :: atom(),
|
||||
max_size :: pos_integer() | infinity,
|
||||
secret_length :: pos_integer(),
|
||||
jid_in_url :: sha1 | node,
|
||||
{server_host = <<>> :: binary(),
|
||||
hosts = [] :: [binary()],
|
||||
name = <<>> :: binary(),
|
||||
access = none :: atom(),
|
||||
max_size = infinity :: pos_integer() | infinity,
|
||||
secret_length = 40 :: pos_integer(),
|
||||
jid_in_url = sha1 :: sha1 | node,
|
||||
file_mode :: integer() | undefined,
|
||||
dir_mode :: integer() | undefined,
|
||||
docroot :: binary(),
|
||||
put_url :: binary(),
|
||||
get_url :: binary(),
|
||||
docroot = <<>> :: binary(),
|
||||
put_url = <<>> :: binary(),
|
||||
get_url = <<>> :: binary(),
|
||||
service_url :: binary() | undefined,
|
||||
thumbnail :: boolean(),
|
||||
custom_headers :: [{binary(), binary()}],
|
||||
thumbnail = false :: boolean(),
|
||||
custom_headers = [] :: [{binary(), binary()}],
|
||||
slots = #{} :: slots(),
|
||||
external_secret :: binary()}).
|
||||
external_secret = <<>> :: binary()}).
|
||||
|
||||
-record(media_info,
|
||||
{path :: binary(),
|
||||
@@ -122,37 +123,37 @@
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_mod/supervisor callbacks.
|
||||
%%--------------------------------------------------------------------
|
||||
-spec start(binary(), gen_mod:opts()) -> {ok, pid()} | {error, already_started}.
|
||||
-spec start(binary(), gen_mod:opts()) -> {ok, pid()} | {error, term()}.
|
||||
start(ServerHost, Opts) ->
|
||||
case mod_http_upload_opt:rm_on_unregister(Opts) of
|
||||
true ->
|
||||
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE,
|
||||
remove_user, 50);
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
Proc = get_proc_name(ServerHost, ?MODULE),
|
||||
case whereis(Proc) of
|
||||
undefined ->
|
||||
gen_mod:start_child(?MODULE, ServerHost, Opts, Proc);
|
||||
_Pid ->
|
||||
case gen_mod:start_child(?MODULE, ServerHost, Opts, Proc) of
|
||||
{ok, _} = Ret -> Ret;
|
||||
{error, {already_started, _}} = Err ->
|
||||
?ERROR_MSG("Multiple virtual hosts can't use a single 'put_url' "
|
||||
"without the @HOST@ keyword", []),
|
||||
{error, already_started}
|
||||
Err;
|
||||
Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
-spec stop(binary()) -> ok | {error, any()}.
|
||||
stop(ServerHost) ->
|
||||
case mod_http_upload_opt:rm_on_unregister(ServerHost) of
|
||||
true ->
|
||||
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE,
|
||||
remove_user, 50);
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
Proc = get_proc_name(ServerHost, ?MODULE),
|
||||
gen_mod:stop_child(Proc).
|
||||
|
||||
-spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok | {ok, pid()} | {error, term()}.
|
||||
reload(ServerHost, NewOpts, OldOpts) ->
|
||||
NewURL = mod_http_upload_opt:put_url(NewOpts),
|
||||
OldURL = mod_http_upload_opt:put_url(OldOpts),
|
||||
OldProc = get_proc_name(ServerHost, ?MODULE, OldURL),
|
||||
NewProc = get_proc_name(ServerHost, ?MODULE, NewURL),
|
||||
if OldProc /= NewProc ->
|
||||
gen_mod:stop_child(OldProc),
|
||||
start(ServerHost, NewOpts);
|
||||
true ->
|
||||
gen_server:cast(NewProc, {reload, NewOpts, OldOpts})
|
||||
end.
|
||||
|
||||
-spec mod_opt_type(atom()) -> econf:validator().
|
||||
mod_opt_type(name) ->
|
||||
econf:binary();
|
||||
@@ -234,46 +235,15 @@ init([ServerHost|_]) ->
|
||||
process_flag(trap_exit, true),
|
||||
Opts = gen_mod:get_module_opts(ServerHost, ?MODULE),
|
||||
Hosts = gen_mod:get_opt_hosts(Opts),
|
||||
Name = mod_http_upload_opt:name(Opts),
|
||||
Access = mod_http_upload_opt:access(Opts),
|
||||
MaxSize = mod_http_upload_opt:max_size(Opts),
|
||||
SecretLength = mod_http_upload_opt:secret_length(Opts),
|
||||
JIDinURL = mod_http_upload_opt:jid_in_url(Opts),
|
||||
DocRoot = mod_http_upload_opt:docroot(Opts),
|
||||
FileMode = mod_http_upload_opt:file_mode(Opts),
|
||||
DirMode = mod_http_upload_opt:dir_mode(Opts),
|
||||
PutURL = mod_http_upload_opt:put_url(Opts),
|
||||
GetURL = case mod_http_upload_opt:get_url(Opts) of
|
||||
undefined -> PutURL;
|
||||
URL -> URL
|
||||
end,
|
||||
ServiceURL = mod_http_upload_opt:service_url(Opts),
|
||||
Thumbnail = mod_http_upload_opt:thumbnail(Opts),
|
||||
ExternalSecret = mod_http_upload_opt:external_secret(Opts),
|
||||
CustomHeaders = mod_http_upload_opt:custom_headers(Opts),
|
||||
DocRoot1 = expand_home(str:strip(DocRoot, right, $/)),
|
||||
DocRoot2 = expand_host(DocRoot1, ServerHost),
|
||||
case DirMode of
|
||||
undefined ->
|
||||
ok;
|
||||
Mode ->
|
||||
file:change_mode(DocRoot2, Mode)
|
||||
case mod_http_upload_opt:rm_on_unregister(Opts) of
|
||||
true ->
|
||||
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE,
|
||||
remove_user, 50);
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ejabberd_router:register_route(Host, ServerHost)
|
||||
end, Hosts),
|
||||
{ok, #state{server_host = ServerHost, hosts = Hosts, name = Name,
|
||||
access = Access, max_size = MaxSize,
|
||||
secret_length = SecretLength, jid_in_url = JIDinURL,
|
||||
file_mode = FileMode, dir_mode = DirMode,
|
||||
thumbnail = Thumbnail,
|
||||
docroot = DocRoot2,
|
||||
put_url = expand_host(str:strip(PutURL, right, $/), ServerHost),
|
||||
get_url = expand_host(str:strip(GetURL, right, $/), ServerHost),
|
||||
service_url = ServiceURL,
|
||||
external_secret = ExternalSecret,
|
||||
custom_headers = CustomHeaders}}.
|
||||
State = init_state(ServerHost, Hosts, Opts),
|
||||
{ok, State}.
|
||||
|
||||
-spec handle_call(_, {pid(), _}, state())
|
||||
-> {reply, {ok, pos_integer(), binary(),
|
||||
@@ -309,6 +279,24 @@ handle_call(Request, From, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
-spec handle_cast(_, state()) -> {noreply, state()}.
|
||||
handle_cast({reload, NewOpts, OldOpts},
|
||||
#state{server_host = ServerHost} = State) ->
|
||||
case {mod_http_upload_opt:rm_on_unregister(NewOpts),
|
||||
mod_http_upload_opt:rm_on_unregister(OldOpts)} of
|
||||
{true, false} ->
|
||||
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE,
|
||||
remove_user, 50);
|
||||
{false, true} ->
|
||||
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE,
|
||||
remove_user, 50);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
NewHosts = gen_mod:get_opt_hosts(NewOpts),
|
||||
OldHosts = gen_mod:get_opt_hosts(OldOpts),
|
||||
lists:foreach(fun ejabberd_router:unregister_route/1, OldHosts -- NewHosts),
|
||||
NewState = init_state(State#state{hosts = NewHosts -- OldHosts}, NewOpts),
|
||||
{noreply, NewState};
|
||||
handle_cast(Request, State) ->
|
||||
?WARNING_MSG("Unexpected cast: ~p", [Request]),
|
||||
{noreply, State}.
|
||||
@@ -346,12 +334,13 @@ handle_info(Info, State) ->
|
||||
|
||||
-spec terminate(normal | shutdown | {shutdown, _} | _, state()) -> ok.
|
||||
terminate(Reason, #state{server_host = ServerHost, hosts = Hosts}) ->
|
||||
?DEBUG("Stopping HTTP upload process for ~s: ~p", [ServerHost, Reason]),
|
||||
?DEBUG("Stopping HTTP upload process for ~ts: ~p", [ServerHost, Reason]),
|
||||
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50),
|
||||
lists:foreach(fun ejabberd_router:unregister_route/1, Hosts).
|
||||
|
||||
-spec code_change({down, _} | _, state(), _) -> {ok, state()}.
|
||||
code_change(_OldVsn, #state{server_host = ServerHost} = State, _Extra) ->
|
||||
?DEBUG("Updating HTTP upload process for ~s", [ServerHost]),
|
||||
?DEBUG("Updating HTTP upload process for ~ts", [ServerHost]),
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
@@ -364,7 +353,7 @@ process(LocalPath, #request{method = Method, host = Host, ip = IP})
|
||||
Method == 'PUT' orelse
|
||||
Method == 'GET' orelse
|
||||
Method == 'HEAD' ->
|
||||
?DEBUG("Rejecting ~s request from ~s for ~s: Too few path components",
|
||||
?DEBUG("Rejecting ~ts request from ~ts for ~ts: Too few path components",
|
||||
[Method, encode_addr(IP), Host]),
|
||||
http_response(404);
|
||||
process(_LocalPath, #request{method = 'PUT', host = Host, ip = IP,
|
||||
@@ -372,7 +361,7 @@ process(_LocalPath, #request{method = 'PUT', host = Host, ip = IP,
|
||||
{Proc, Slot} = parse_http_request(Request),
|
||||
try gen_server:call(Proc, {use_slot, Slot, Length}, ?CALL_TIMEOUT) of
|
||||
{ok, Path, FileMode, DirMode, GetPrefix, Thumbnail, CustomHeaders} ->
|
||||
?DEBUG("Storing file from ~s for ~s: ~s",
|
||||
?DEBUG("Storing file from ~ts for ~ts: ~ts",
|
||||
[encode_addr(IP), Host, Path]),
|
||||
case store_file(Path, Request, FileMode, DirMode,
|
||||
GetPrefix, Slot, Thumbnail) of
|
||||
@@ -381,30 +370,30 @@ process(_LocalPath, #request{method = 'PUT', host = Host, ip = IP,
|
||||
{ok, Headers, OutData} ->
|
||||
http_response(201, Headers ++ CustomHeaders, OutData);
|
||||
{error, closed} ->
|
||||
?DEBUG("Cannot store file ~s from ~s for ~s: connection closed",
|
||||
?DEBUG("Cannot store file ~ts from ~ts for ~ts: connection closed",
|
||||
[Path, encode_addr(IP), Host]),
|
||||
http_response(404);
|
||||
{error, Error} ->
|
||||
?ERROR_MSG("Cannot store file ~s from ~s for ~s: ~s",
|
||||
?ERROR_MSG("Cannot store file ~ts from ~ts for ~ts: ~ts",
|
||||
[Path, encode_addr(IP), Host, format_error(Error)]),
|
||||
http_response(500)
|
||||
end;
|
||||
{error, size_mismatch} ->
|
||||
?WARNING_MSG("Rejecting file ~s from ~s for ~s: Unexpected size (~B)",
|
||||
?WARNING_MSG("Rejecting file ~ts from ~ts for ~ts: Unexpected size (~B)",
|
||||
[lists:last(Slot), encode_addr(IP), Host, Length]),
|
||||
http_response(413);
|
||||
{error, invalid_slot} ->
|
||||
?WARNING_MSG("Rejecting file ~s from ~s for ~s: Invalid slot",
|
||||
?WARNING_MSG("Rejecting file ~ts from ~ts for ~ts: Invalid slot",
|
||||
[lists:last(Slot), encode_addr(IP), Host]),
|
||||
http_response(403)
|
||||
catch
|
||||
exit:{noproc, _} ->
|
||||
?WARNING_MSG("Cannot handle PUT request from ~s for ~s: "
|
||||
?WARNING_MSG("Cannot handle PUT request from ~ts for ~ts: "
|
||||
"Upload not configured for this host",
|
||||
[encode_addr(IP), Host]),
|
||||
http_response(404);
|
||||
_:Error ->
|
||||
?ERROR_MSG("Cannot handle PUT request from ~s for ~s: ~p",
|
||||
?ERROR_MSG("Cannot handle PUT request from ~ts for ~ts: ~p",
|
||||
[encode_addr(IP), Host, Error]),
|
||||
http_response(500)
|
||||
end;
|
||||
@@ -418,7 +407,7 @@ process(_LocalPath, #request{method = Method, host = Host, ip = IP} = Request)
|
||||
case file:open(Path, [read]) of
|
||||
{ok, Fd} ->
|
||||
file:close(Fd),
|
||||
?INFO_MSG("Serving ~s to ~s", [Path, encode_addr(IP)]),
|
||||
?INFO_MSG("Serving ~ts to ~ts", [Path, encode_addr(IP)]),
|
||||
ContentType = guess_content_type(FileName),
|
||||
Headers1 = case ContentType of
|
||||
<<"image/", _SubType/binary>> -> [];
|
||||
@@ -432,36 +421,36 @@ process(_LocalPath, #request{method = Method, host = Host, ip = IP} = Request)
|
||||
Headers3 = Headers2 ++ CustomHeaders,
|
||||
http_response(200, Headers3, {file, Path});
|
||||
{error, eacces} ->
|
||||
?WARNING_MSG("Cannot serve ~s to ~s: Permission denied",
|
||||
?WARNING_MSG("Cannot serve ~ts to ~ts: Permission denied",
|
||||
[Path, encode_addr(IP)]),
|
||||
http_response(403);
|
||||
{error, enoent} ->
|
||||
?WARNING_MSG("Cannot serve ~s to ~s: No such file",
|
||||
?WARNING_MSG("Cannot serve ~ts to ~ts: No such file",
|
||||
[Path, encode_addr(IP)]),
|
||||
http_response(404);
|
||||
{error, eisdir} ->
|
||||
?WARNING_MSG("Cannot serve ~s to ~s: Is a directory",
|
||||
?WARNING_MSG("Cannot serve ~ts to ~ts: Is a directory",
|
||||
[Path, encode_addr(IP)]),
|
||||
http_response(404);
|
||||
{error, Error} ->
|
||||
?WARNING_MSG("Cannot serve ~s to ~s: ~s",
|
||||
?WARNING_MSG("Cannot serve ~ts to ~ts: ~ts",
|
||||
[Path, encode_addr(IP), format_error(Error)]),
|
||||
http_response(500)
|
||||
end
|
||||
catch
|
||||
exit:{noproc, _} ->
|
||||
?WARNING_MSG("Cannot handle ~s request from ~s for ~s: "
|
||||
?WARNING_MSG("Cannot handle ~ts request from ~ts for ~ts: "
|
||||
"Upload not configured for this host",
|
||||
[Method, encode_addr(IP), Host]),
|
||||
http_response(404);
|
||||
_:Error ->
|
||||
?ERROR_MSG("Cannot handle ~s request from ~s for ~s: ~p",
|
||||
?ERROR_MSG("Cannot handle ~ts request from ~ts for ~ts: ~p",
|
||||
[Method, encode_addr(IP), Host, Error]),
|
||||
http_response(500)
|
||||
end;
|
||||
process(_LocalPath, #request{method = 'OPTIONS', host = Host,
|
||||
ip = IP} = Request) ->
|
||||
?DEBUG("Responding to OPTIONS request from ~s for ~s",
|
||||
?DEBUG("Responding to OPTIONS request from ~ts for ~ts",
|
||||
[encode_addr(IP), Host]),
|
||||
{Proc, _Slot} = parse_http_request(Request),
|
||||
try gen_server:call(Proc, get_conf, ?CALL_TIMEOUT) of
|
||||
@@ -470,26 +459,80 @@ process(_LocalPath, #request{method = 'OPTIONS', host = Host,
|
||||
http_response(200, [AllowHeader | CustomHeaders])
|
||||
catch
|
||||
exit:{noproc, _} ->
|
||||
?WARNING_MSG("Cannot handle OPTIONS request from ~s for ~s: "
|
||||
?WARNING_MSG("Cannot handle OPTIONS request from ~ts for ~ts: "
|
||||
"Upload not configured for this host",
|
||||
[encode_addr(IP), Host]),
|
||||
http_response(404);
|
||||
_:Error ->
|
||||
?ERROR_MSG("Cannot handle OPTIONS request from ~s for ~s: ~p",
|
||||
?ERROR_MSG("Cannot handle OPTIONS request from ~ts for ~ts: ~p",
|
||||
[encode_addr(IP), Host, Error]),
|
||||
http_response(500)
|
||||
end;
|
||||
process(_LocalPath, #request{method = Method, host = Host, ip = IP}) ->
|
||||
?DEBUG("Rejecting ~s request from ~s for ~s",
|
||||
?DEBUG("Rejecting ~ts request from ~ts for ~ts",
|
||||
[Method, encode_addr(IP), Host]),
|
||||
http_response(405, [{<<"Allow">>, <<"OPTIONS, HEAD, GET, PUT">>}]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% State initialization
|
||||
%%--------------------------------------------------------------------
|
||||
-spec init_state(binary(), [binary()], gen_mod:opts()) -> state().
|
||||
init_state(ServerHost, Hosts, Opts) ->
|
||||
init_state(#state{server_host = ServerHost, hosts = Hosts}, Opts).
|
||||
|
||||
-spec init_state(state(), gen_mod:opts()) -> state().
|
||||
init_state(#state{server_host = ServerHost, hosts = Hosts} = State, Opts) ->
|
||||
Name = mod_http_upload_opt:name(Opts),
|
||||
Access = mod_http_upload_opt:access(Opts),
|
||||
MaxSize = mod_http_upload_opt:max_size(Opts),
|
||||
SecretLength = mod_http_upload_opt:secret_length(Opts),
|
||||
JIDinURL = mod_http_upload_opt:jid_in_url(Opts),
|
||||
DocRoot = mod_http_upload_opt:docroot(Opts),
|
||||
FileMode = mod_http_upload_opt:file_mode(Opts),
|
||||
DirMode = mod_http_upload_opt:dir_mode(Opts),
|
||||
PutURL = mod_http_upload_opt:put_url(Opts),
|
||||
GetURL = case mod_http_upload_opt:get_url(Opts) of
|
||||
undefined -> PutURL;
|
||||
URL -> URL
|
||||
end,
|
||||
ServiceURL = mod_http_upload_opt:service_url(Opts),
|
||||
Thumbnail = mod_http_upload_opt:thumbnail(Opts),
|
||||
ExternalSecret = mod_http_upload_opt:external_secret(Opts),
|
||||
CustomHeaders = mod_http_upload_opt:custom_headers(Opts),
|
||||
DocRoot1 = expand_home(str:strip(DocRoot, right, $/)),
|
||||
DocRoot2 = expand_host(DocRoot1, ServerHost),
|
||||
case DirMode of
|
||||
undefined ->
|
||||
ok;
|
||||
Mode ->
|
||||
file:change_mode(DocRoot2, Mode)
|
||||
end,
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ejabberd_router:register_route(Host, ServerHost)
|
||||
end, Hosts),
|
||||
State#state{server_host = ServerHost, hosts = Hosts, name = Name,
|
||||
access = Access, max_size = MaxSize,
|
||||
secret_length = SecretLength, jid_in_url = JIDinURL,
|
||||
file_mode = FileMode, dir_mode = DirMode,
|
||||
thumbnail = Thumbnail,
|
||||
docroot = DocRoot2,
|
||||
put_url = expand_host(str:strip(PutURL, right, $/), ServerHost),
|
||||
get_url = expand_host(str:strip(GetURL, right, $/), ServerHost),
|
||||
service_url = ServiceURL,
|
||||
external_secret = ExternalSecret,
|
||||
custom_headers = CustomHeaders}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Exported utility functions.
|
||||
%%--------------------------------------------------------------------
|
||||
-spec get_proc_name(binary(), atom()) -> atom().
|
||||
get_proc_name(ServerHost, ModuleName) ->
|
||||
PutURL = mod_http_upload_opt:put_url(ServerHost),
|
||||
get_proc_name(ServerHost, ModuleName, PutURL).
|
||||
|
||||
-spec get_proc_name(binary(), atom(), binary()) -> atom().
|
||||
get_proc_name(ServerHost, ModuleName, PutURL) ->
|
||||
%% Once we depend on OTP >= 20.0, we can use binaries with http_uri.
|
||||
{ok, {_Scheme, _UserInfo, Host0, _Port, Path0, _Query}} =
|
||||
http_uri:parse(binary_to_list(expand_host(PutURL, ServerHost))),
|
||||
@@ -574,7 +617,7 @@ process_slot_request(#iq{lang = Lang, from = From} = IQ,
|
||||
xmpp:make_error(IQ, Error)
|
||||
end;
|
||||
deny ->
|
||||
?DEBUG("Denying HTTP upload slot request from ~s",
|
||||
?DEBUG("Denying HTTP upload slot request from ~ts",
|
||||
[jid:encode(From)]),
|
||||
Txt = ?T("Access denied by service policy"),
|
||||
xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang))
|
||||
@@ -588,7 +631,7 @@ create_slot(#state{service_url = undefined, max_size = MaxSize},
|
||||
when MaxSize /= infinity,
|
||||
Size > MaxSize ->
|
||||
Text = {?T("File larger than ~w bytes"), [MaxSize]},
|
||||
?WARNING_MSG("Rejecting file ~s from ~s (too large: ~B bytes)",
|
||||
?WARNING_MSG("Rejecting file ~ts from ~ts (too large: ~B bytes)",
|
||||
[File, jid:encode(JID), Size]),
|
||||
Error = xmpp:err_not_acceptable(Text, Lang),
|
||||
Els = xmpp:get_els(Error),
|
||||
@@ -609,7 +652,7 @@ create_slot(#state{service_url = undefined,
|
||||
allow ->
|
||||
RandStr = p1_rand:get_alphanum_string(SecretLength),
|
||||
FileStr = make_file_string(File),
|
||||
?INFO_MSG("Got HTTP upload slot for ~s (file: ~s, size: ~B)",
|
||||
?INFO_MSG("Got HTTP upload slot for ~ts (file: ~ts, size: ~B)",
|
||||
[jid:encode(JID), File, Size]),
|
||||
{ok, [UserStr, RandStr, FileStr]};
|
||||
deny ->
|
||||
@@ -635,33 +678,33 @@ create_slot(#state{service_url = ServiceURL},
|
||||
case binary:split(Body, <<$\n>>, [global, trim]) of
|
||||
[<<"http", _/binary>> = PutURL,
|
||||
<<"http", _/binary>> = GetURL] ->
|
||||
?INFO_MSG("Got HTTP upload slot for ~s (file: ~s, size: ~B)",
|
||||
?INFO_MSG("Got HTTP upload slot for ~ts (file: ~ts, size: ~B)",
|
||||
[jid:encode(JID), File, Size]),
|
||||
{ok, PutURL, GetURL};
|
||||
Lines ->
|
||||
?ERROR_MSG("Can't parse data received for ~s from <~s>: ~p",
|
||||
?ERROR_MSG("Can't parse data received for ~ts from <~ts>: ~p",
|
||||
[jid:encode(JID), ServiceURL, Lines]),
|
||||
Txt = ?T("Failed to parse HTTP response"),
|
||||
{error, xmpp:err_service_unavailable(Txt, Lang)}
|
||||
end;
|
||||
{ok, {402, _Body}} ->
|
||||
?WARNING_MSG("Got status code 402 for ~s from <~s>",
|
||||
?WARNING_MSG("Got status code 402 for ~ts from <~ts>",
|
||||
[jid:encode(JID), ServiceURL]),
|
||||
{error, xmpp:err_resource_constraint()};
|
||||
{ok, {403, _Body}} ->
|
||||
?WARNING_MSG("Got status code 403 for ~s from <~s>",
|
||||
?WARNING_MSG("Got status code 403 for ~ts from <~ts>",
|
||||
[jid:encode(JID), ServiceURL]),
|
||||
{error, xmpp:err_not_allowed()};
|
||||
{ok, {413, _Body}} ->
|
||||
?WARNING_MSG("Got status code 413 for ~s from <~s>",
|
||||
?WARNING_MSG("Got status code 413 for ~ts from <~ts>",
|
||||
[jid:encode(JID), ServiceURL]),
|
||||
{error, xmpp:err_not_acceptable()};
|
||||
{ok, {Code, _Body}} ->
|
||||
?ERROR_MSG("Unexpected status code for ~s from <~s>: ~B",
|
||||
?ERROR_MSG("Unexpected status code for ~ts from <~ts>: ~B",
|
||||
[jid:encode(JID), ServiceURL, Code]),
|
||||
{error, xmpp:err_service_unavailable()};
|
||||
{error, Reason} ->
|
||||
?ERROR_MSG("Error requesting upload slot for ~s from <~s>: ~p",
|
||||
?ERROR_MSG("Error requesting upload slot for ~ts from <~ts>: ~p",
|
||||
[jid:encode(JID), ServiceURL, Reason]),
|
||||
{error, xmpp:err_service_unavailable()}
|
||||
end.
|
||||
@@ -899,12 +942,12 @@ read_image(Path) ->
|
||||
width = proplists:get_value(width, Info),
|
||||
height = proplists:get_value(height, Info)}};
|
||||
{error, Why} ->
|
||||
?DEBUG("Cannot identify type of ~s: ~s",
|
||||
?DEBUG("Cannot identify type of ~ts: ~ts",
|
||||
[Path, eimp:format_error(Why)]),
|
||||
pass
|
||||
end;
|
||||
{error, Reason} ->
|
||||
?DEBUG("Failed to read file ~s: ~s",
|
||||
?DEBUG("Failed to read file ~ts: ~ts",
|
||||
[Path, format_error(Reason)]),
|
||||
pass
|
||||
end.
|
||||
@@ -912,7 +955,7 @@ read_image(Path) ->
|
||||
-spec convert(binary(), media_info()) -> {ok, media_info()} | pass.
|
||||
convert(InData, #media_info{path = Path, type = T, width = W, height = H} = Info) ->
|
||||
if W * H >= 25000000 ->
|
||||
?DEBUG("The image ~s is more than 25 Mpix", [Path]),
|
||||
?DEBUG("The image ~ts is more than 25 Mpix", [Path]),
|
||||
pass;
|
||||
W =< 300, H =< 300 ->
|
||||
{ok, Info};
|
||||
@@ -932,12 +975,12 @@ convert(InData, #media_info{path = Path, type = T, width = W, height = H} = Info
|
||||
ok ->
|
||||
{ok, OutInfo};
|
||||
{error, Why} ->
|
||||
?ERROR_MSG("Failed to write to ~s: ~s",
|
||||
?ERROR_MSG("Failed to write to ~ts: ~ts",
|
||||
[OutPath, format_error(Why)]),
|
||||
pass
|
||||
end;
|
||||
{error, Why} ->
|
||||
?ERROR_MSG("Failed to convert ~s to ~s: ~s",
|
||||
?ERROR_MSG("Failed to convert ~ts to ~ts: ~ts",
|
||||
[Path, OutPath, eimp:format_error(Why)]),
|
||||
pass
|
||||
end
|
||||
@@ -961,31 +1004,13 @@ remove_user(User, Server) ->
|
||||
DocRoot1 = expand_host(expand_home(DocRoot), ServerHost),
|
||||
UserStr = make_user_string(jid:make(User, Server), JIDinURL),
|
||||
UserDir = str:join([DocRoot1, UserStr], <<$/>>),
|
||||
case del_tree(UserDir) of
|
||||
case misc:delete_dir(UserDir) of
|
||||
ok ->
|
||||
?INFO_MSG("Removed HTTP upload directory of ~s@~s", [User, Server]);
|
||||
?INFO_MSG("Removed HTTP upload directory of ~ts@~ts", [User, Server]);
|
||||
{error, enoent} ->
|
||||
?DEBUG("Found no HTTP upload directory of ~s@~s", [User, Server]);
|
||||
?DEBUG("Found no HTTP upload directory of ~ts@~ts", [User, Server]);
|
||||
{error, Error} ->
|
||||
?ERROR_MSG("Cannot remove HTTP upload directory of ~s@~s: ~s",
|
||||
?ERROR_MSG("Cannot remove HTTP upload directory of ~ts@~ts: ~ts",
|
||||
[User, Server, format_error(Error)])
|
||||
end,
|
||||
ok.
|
||||
|
||||
-spec del_tree(file:filename_all()) -> ok | {error, file:posix()}.
|
||||
del_tree(Dir) ->
|
||||
try
|
||||
{ok, Entries} = file:list_dir(Dir),
|
||||
lists:foreach(fun(Path) ->
|
||||
case filelib:is_dir(Path) of
|
||||
true ->
|
||||
ok = del_tree(Path);
|
||||
false ->
|
||||
ok = file:delete(Path)
|
||||
end
|
||||
end, [filename:join(Dir, Entry) || Entry <- Entries]),
|
||||
ok = file:del_dir(Dir)
|
||||
catch
|
||||
_:{badmatch, {error, Error}} ->
|
||||
{error, Error}
|
||||
end.
|
||||
|
||||
@@ -155,24 +155,24 @@ handle_cast({handle_slot_request, #jid{user = U, server = S} = JID, Path, Size},
|
||||
end,
|
||||
NewSize = case {HardQuota, SoftQuota} of
|
||||
{0, 0} ->
|
||||
?DEBUG("No quota specified for ~s",
|
||||
?DEBUG("No quota specified for ~ts",
|
||||
[jid:encode(JID)]),
|
||||
undefined;
|
||||
{0, _} ->
|
||||
?WARNING_MSG("No hard quota specified for ~s",
|
||||
?WARNING_MSG("No hard quota specified for ~ts",
|
||||
[jid:encode(JID)]),
|
||||
enforce_quota(Path, Size, OldSize, SoftQuota, SoftQuota);
|
||||
{_, 0} ->
|
||||
?WARNING_MSG("No soft quota specified for ~s",
|
||||
?WARNING_MSG("No soft quota specified for ~ts",
|
||||
[jid:encode(JID)]),
|
||||
enforce_quota(Path, Size, OldSize, HardQuota, HardQuota);
|
||||
_ when SoftQuota > HardQuota ->
|
||||
?WARNING_MSG("Bad quota for ~s (soft: ~p, hard: ~p)",
|
||||
?WARNING_MSG("Bad quota for ~ts (soft: ~p, hard: ~p)",
|
||||
[jid:encode(JID),
|
||||
SoftQuota, HardQuota]),
|
||||
enforce_quota(Path, Size, OldSize, SoftQuota, SoftQuota);
|
||||
_ ->
|
||||
?DEBUG("Enforcing quota for ~s",
|
||||
?DEBUG("Enforcing quota for ~ts",
|
||||
[jid:encode(JID)]),
|
||||
enforce_quota(Path, Size, OldSize, SoftQuota, HardQuota)
|
||||
end,
|
||||
@@ -191,7 +191,7 @@ handle_info(sweep, #state{server_host = ServerHost,
|
||||
docroot = DocRoot,
|
||||
max_days = MaxDays} = State)
|
||||
when is_integer(MaxDays), MaxDays > 0 ->
|
||||
?DEBUG("Got 'sweep' message for ~s", [ServerHost]),
|
||||
?DEBUG("Got 'sweep' message for ~ts", [ServerHost]),
|
||||
case file:list_dir(DocRoot) of
|
||||
{ok, Entries} ->
|
||||
BackThen = secs_since_epoch() - (MaxDays * 86400),
|
||||
@@ -204,7 +204,7 @@ handle_info(sweep, #state{server_host = ServerHost,
|
||||
delete_old_files(UserDir, BackThen)
|
||||
end, UserDirs);
|
||||
{error, Error} ->
|
||||
?ERROR_MSG("Cannot open document root ~s: ~s",
|
||||
?ERROR_MSG("Cannot open document root ~ts: ~ts",
|
||||
[DocRoot, ?FORMAT(Error)])
|
||||
end,
|
||||
{noreply, State};
|
||||
@@ -214,14 +214,14 @@ handle_info(Info, State) ->
|
||||
|
||||
-spec terminate(normal | shutdown | {shutdown, _} | _, state()) -> ok.
|
||||
terminate(Reason, #state{server_host = ServerHost, timers = Timers}) ->
|
||||
?DEBUG("Stopping upload quota process for ~s: ~p", [ServerHost, Reason]),
|
||||
?DEBUG("Stopping upload quota process for ~ts: ~p", [ServerHost, Reason]),
|
||||
ejabberd_hooks:delete(http_upload_slot_request, ServerHost, ?MODULE,
|
||||
handle_slot_request, 50),
|
||||
lists:foreach(fun timer:cancel/1, Timers).
|
||||
|
||||
-spec code_change({down, _} | _, state(), _) -> {ok, state()}.
|
||||
code_change(_OldVsn, #state{server_host = ServerHost} = State, _Extra) ->
|
||||
?DEBUG("Updating upload quota process for ~s", [ServerHost]),
|
||||
?DEBUG("Updating upload quota process for ~ts", [ServerHost]),
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
@@ -295,20 +295,20 @@ gather_file_info(Dir) ->
|
||||
size = Size}} ->
|
||||
[{Path, Size, Time} | Acc];
|
||||
{ok, _Info} ->
|
||||
?DEBUG("Won't stat(2) non-regular file ~s",
|
||||
?DEBUG("Won't stat(2) non-regular file ~ts",
|
||||
[Path]),
|
||||
Acc;
|
||||
{error, Error} ->
|
||||
?ERROR_MSG("Cannot stat(2) ~s: ~s",
|
||||
?ERROR_MSG("Cannot stat(2) ~ts: ~ts",
|
||||
[Path, ?FORMAT(Error)]),
|
||||
Acc
|
||||
end
|
||||
end, [], Entries);
|
||||
{error, enoent} ->
|
||||
?DEBUG("Directory ~s doesn't exist", [Dir]),
|
||||
?DEBUG("Directory ~ts doesn't exist", [Dir]),
|
||||
[];
|
||||
{error, Error} ->
|
||||
?ERROR_MSG("Cannot open directory ~s: ~s", [Dir, ?FORMAT(Error)]),
|
||||
?ERROR_MSG("Cannot open directory ~ts: ~ts", [Dir, ?FORMAT(Error)]),
|
||||
[]
|
||||
end.
|
||||
|
||||
@@ -316,16 +316,16 @@ gather_file_info(Dir) ->
|
||||
del_file_and_dir(File) ->
|
||||
case file:delete(File) of
|
||||
ok ->
|
||||
?INFO_MSG("Removed ~s", [File]),
|
||||
?INFO_MSG("Removed ~ts", [File]),
|
||||
Dir = filename:dirname(File),
|
||||
case file:del_dir(Dir) of
|
||||
ok ->
|
||||
?DEBUG("Removed ~s", [Dir]);
|
||||
?DEBUG("Removed ~ts", [Dir]);
|
||||
{error, Error} ->
|
||||
?DEBUG("Cannot remove ~s: ~s", [Dir, ?FORMAT(Error)])
|
||||
?DEBUG("Cannot remove ~ts: ~ts", [Dir, ?FORMAT(Error)])
|
||||
end;
|
||||
{error, Error} ->
|
||||
?WARNING_MSG("Cannot remove ~s: ~s", [File, ?FORMAT(Error)])
|
||||
?WARNING_MSG("Cannot remove ~ts: ~ts", [File, ?FORMAT(Error)])
|
||||
end.
|
||||
|
||||
-spec secs_since_epoch() -> non_neg_integer().
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_jidprep.erl
|
||||
%%% Author : Holger Weiss <holger@zedat.fu-berlin.de>
|
||||
%%% Purpose : JID Prep (XEP-0328)
|
||||
%%% Created : 11 Sep 2019 by Holger Weiss <holger@zedat.fu-berlin.de>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2019 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(mod_jidprep).
|
||||
-author('holger@zedat.fu-berlin.de').
|
||||
-protocol({xep, 328, '0.1'}).
|
||||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
%% gen_mod callbacks.
|
||||
-export([start/2, stop/1, reload/3, mod_opt_type/1, mod_options/1, depends/2]).
|
||||
|
||||
%% ejabberd_hooks callbacks.
|
||||
-export([disco_local_features/5]).
|
||||
|
||||
%% gen_iq_handler callback.
|
||||
-export([process_iq/1]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("translate.hrl").
|
||||
-include("xmpp.hrl").
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_mod callbacks.
|
||||
%%--------------------------------------------------------------------
|
||||
-spec start(binary(), gen_mod:opts()) -> ok.
|
||||
start(Host, _Opts) ->
|
||||
register_iq_handlers(Host),
|
||||
register_hooks(Host).
|
||||
|
||||
-spec stop(binary()) -> ok.
|
||||
stop(Host) ->
|
||||
unregister_hooks(Host),
|
||||
unregister_iq_handlers(Host).
|
||||
|
||||
-spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok.
|
||||
reload(_Host, _NewOpts, _OldOpts) ->
|
||||
ok.
|
||||
|
||||
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
|
||||
depends(_Host, _Opts) ->
|
||||
[].
|
||||
|
||||
-spec mod_opt_type(atom()) -> econf:validator().
|
||||
mod_opt_type(access) ->
|
||||
econf:acl().
|
||||
|
||||
-spec mod_options(binary()) -> [{atom(), any()}].
|
||||
mod_options(_Host) ->
|
||||
[{access, local}].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Register/unregister hooks.
|
||||
%%--------------------------------------------------------------------
|
||||
-spec register_hooks(binary()) -> ok.
|
||||
register_hooks(Host) ->
|
||||
ejabberd_hooks:add(disco_local_features, Host, ?MODULE,
|
||||
disco_local_features, 50).
|
||||
|
||||
-spec unregister_hooks(binary()) -> ok.
|
||||
unregister_hooks(Host) ->
|
||||
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE,
|
||||
disco_local_features, 50).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Service discovery.
|
||||
%%--------------------------------------------------------------------
|
||||
-spec disco_local_features(mod_disco:features_acc(), jid(), jid(), binary(),
|
||||
binary()) -> mod_disco:features_acc().
|
||||
disco_local_features(empty, From, To, Node, Lang) ->
|
||||
disco_local_features({result, []}, From, To, Node, Lang);
|
||||
disco_local_features({result, OtherFeatures} = Acc, From,
|
||||
#jid{lserver = LServer}, <<"">>, _Lang) ->
|
||||
Access = mod_jidprep_opt:access(LServer),
|
||||
case acl:match_rule(LServer, Access, From) of
|
||||
allow ->
|
||||
{result, [?NS_JIDPREP_0 | OtherFeatures]};
|
||||
deny ->
|
||||
Acc
|
||||
end;
|
||||
disco_local_features(Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% IQ handlers.
|
||||
%%--------------------------------------------------------------------
|
||||
-spec register_iq_handlers(binary()) -> ok.
|
||||
register_iq_handlers(Host) ->
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
||||
?NS_JIDPREP_0, ?MODULE, process_iq).
|
||||
|
||||
-spec unregister_iq_handlers(binary()) -> ok.
|
||||
unregister_iq_handlers(Host) ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_JIDPREP_0).
|
||||
|
||||
-spec process_iq(iq()) -> iq().
|
||||
process_iq(#iq{type = set, lang = Lang} = IQ) ->
|
||||
Txt = ?T("Value 'set' of 'type' attribute is not allowed"),
|
||||
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
|
||||
process_iq(#iq{from = From, to = #jid{lserver = LServer}, lang = Lang,
|
||||
sub_els = [#jidprep{jid = #jid{luser = U,
|
||||
lserver = S,
|
||||
lresource = R} = JID}]} = IQ) ->
|
||||
Access = mod_jidprep_opt:access(LServer),
|
||||
case acl:match_rule(LServer, Access, From) of
|
||||
allow ->
|
||||
case jid:make(U, S, R) of
|
||||
#jid{} = Normalized ->
|
||||
?DEBUG("Normalized JID for ~ts: ~ts",
|
||||
[jid:encode(From), jid:encode(JID)]),
|
||||
xmpp:make_iq_result(IQ, #jidprep{jid = Normalized});
|
||||
error -> % Cannot happen.
|
||||
?DEBUG("Normalizing JID failed for ~ts: ~ts",
|
||||
[jid:encode(From), jid:encode(JID)]),
|
||||
Txt = ?T("JID normalization failed"),
|
||||
xmpp:make_error(IQ, xmpp:err_jid_malformed(Txt, Lang))
|
||||
end;
|
||||
deny ->
|
||||
?DEBUG("Won't return normalized JID to ~ts: ~ts",
|
||||
[jid:encode(From), jid:encode(JID)]),
|
||||
Txt = ?T("JID normalization denied by service policy"),
|
||||
xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang))
|
||||
end;
|
||||
process_iq(#iq{lang = Lang} = IQ) ->
|
||||
Txt = ?T("No module is handling this query"),
|
||||
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
|
||||
@@ -0,0 +1,13 @@
|
||||
%% Generated automatically
|
||||
%% DO NOT EDIT: run `make options` instead
|
||||
|
||||
-module(mod_jidprep_opt).
|
||||
|
||||
-export([access/1]).
|
||||
|
||||
-spec access(gen_mod:opts() | global | binary()) -> 'local' | acl:acl().
|
||||
access(Opts) when is_map(Opts) ->
|
||||
gen_mod:get_opt(access, Opts);
|
||||
access(Host) ->
|
||||
gen_mod:get_module_opt(Host, mod_jidprep, access).
|
||||
|
||||
+2
-2
@@ -95,7 +95,7 @@
|
||||
start(Host, Opts) ->
|
||||
case mod_mam_opt:db_type(Opts) of
|
||||
mnesia ->
|
||||
?WARNING_MSG("Mnesia backend for ~s is not recommended: "
|
||||
?WARNING_MSG("Mnesia backend for ~ts is not recommended: "
|
||||
"it's limited to 2GB and often gets corrupted "
|
||||
"when reaching this limit. SQL backend is "
|
||||
"recommended. Namely, for small servers SQLite "
|
||||
@@ -1233,7 +1233,7 @@ msg_to_el(#archive_msg{timestamp = TS, packet = El, nick = Nick,
|
||||
{ok, #forwarded{sub_els = [Pkt3], delay = Delay}}
|
||||
catch _:{xmpp_codec, Why} ->
|
||||
?ERROR_MSG("Failed to decode raw element ~p from message "
|
||||
"archive of user ~s: ~s",
|
||||
"archive of user ~ts: ~ts",
|
||||
[El, jid:encode(JidArchive), xmpp:format_error(Why)]),
|
||||
{error, invalid_xml}
|
||||
end.
|
||||
|
||||
@@ -127,7 +127,7 @@ delete_old_user_messages(User, TimeStamp, Type) ->
|
||||
{atomic, ok} ->
|
||||
delete_old_user_messages(NextRecord, TimeStamp, Type);
|
||||
{aborted, Err} ->
|
||||
?ERROR_MSG("Cannot delete old MAM messages: ~s", [Err]),
|
||||
?ERROR_MSG("Cannot delete old MAM messages: ~ts", [Err]),
|
||||
Err
|
||||
end.
|
||||
|
||||
@@ -138,7 +138,7 @@ store(Pkt, _, {LUser, LServer}, Type, Peer, Nick, _Dir, TS) ->
|
||||
case {mnesia:table_info(archive_msg, disc_only_copies),
|
||||
mnesia:table_info(archive_msg, memory)} of
|
||||
{[_|_], TableSize} when TableSize > ?TABLE_SIZE_LIMIT ->
|
||||
?ERROR_MSG("MAM archives too large, won't store message for ~s@~s",
|
||||
?ERROR_MSG("MAM archives too large, won't store message for ~ts@~ts",
|
||||
[LUser, LServer]),
|
||||
{error, overflow};
|
||||
_ ->
|
||||
@@ -158,7 +158,7 @@ store(Pkt, _, {LUser, LServer}, Type, Peer, Nick, _Dir, TS) ->
|
||||
{atomic, ok} ->
|
||||
ok;
|
||||
{aborted, Err} ->
|
||||
?ERROR_MSG("Cannot add message to MAM archive of ~s@~s: ~s",
|
||||
?ERROR_MSG("Cannot add message to MAM archive of ~ts@~ts: ~ts",
|
||||
[LUser, LServer, Err]),
|
||||
Err
|
||||
end
|
||||
|
||||
+5
-5
@@ -515,21 +515,21 @@ make_archive_el(User, TS, XML, Peer, Kind, Nick, MsgType, JidRequestor, JidArchi
|
||||
MsgType, JidRequestor, JidArchive)
|
||||
catch _:{bad_jid, _} ->
|
||||
?ERROR_MSG("Malformed 'peer' field with value "
|
||||
"'~s' detected for user ~s in table "
|
||||
"'~ts' detected for user ~ts in table "
|
||||
"'archive': invalid JID",
|
||||
[Peer, jid:encode(JidArchive)]),
|
||||
{error, invalid_jid}
|
||||
end
|
||||
catch _:_ ->
|
||||
?ERROR_MSG("Malformed 'timestamp' field with value '~s' "
|
||||
"detected for user ~s in table 'archive': "
|
||||
?ERROR_MSG("Malformed 'timestamp' field with value '~ts' "
|
||||
"detected for user ~ts in table 'archive': "
|
||||
"not an integer",
|
||||
[TS, jid:encode(JidArchive)]),
|
||||
{error, invalid_timestamp}
|
||||
end;
|
||||
{error, {_, Reason}} ->
|
||||
?ERROR_MSG("Malformed 'xml' field with value '~s' detected "
|
||||
"for user ~s in table 'archive': ~s",
|
||||
?ERROR_MSG("Malformed 'xml' field with value '~ts' detected "
|
||||
"for user ~ts in table 'archive': ~ts",
|
||||
[XML, jid:encode(JidArchive), Reason]),
|
||||
{error, invalid_xml}
|
||||
end.
|
||||
|
||||
+1
-1
@@ -173,7 +173,7 @@ get_socket(N) ->
|
||||
get_socket(N-1)
|
||||
end;
|
||||
{error, Reason} = Err ->
|
||||
?ERROR_MSG("Can not open udp socket to grapherl: ~s",
|
||||
?ERROR_MSG("Can not open udp socket to grapherl: ~ts",
|
||||
[inet:format_error(Reason)]),
|
||||
Err
|
||||
end;
|
||||
|
||||
+2
-2
@@ -110,7 +110,7 @@ route(#message{type = groupchat, id = ID, lang = Lang,
|
||||
process_mix_message(Msg)
|
||||
end;
|
||||
route(Pkt) ->
|
||||
?DEBUG("Dropping packet:~n~s", [xmpp:pp(Pkt)]).
|
||||
?DEBUG("Dropping packet:~n~ts", [xmpp:pp(Pkt)]).
|
||||
|
||||
-spec process_disco_info(iq()) -> iq().
|
||||
process_disco_info(#iq{type = set, lang = Lang} = IQ) ->
|
||||
@@ -281,7 +281,7 @@ handle_info({route, Packet}, State) ->
|
||||
try route(Packet)
|
||||
catch ?EX_RULE(Class, Reason, St) ->
|
||||
StackTrace = ?EX_STACK(St),
|
||||
?ERROR_MSG("Failed to route packet:~n~s~n** ~s",
|
||||
?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts",
|
||||
[xmpp:pp(Packet),
|
||||
misc:format_exception(2, Class, Reason, StackTrace)])
|
||||
end,
|
||||
|
||||
@@ -110,5 +110,5 @@ del_channels(User) ->
|
||||
%%%===================================================================
|
||||
-spec report_corrupted(#sql_query{}) -> ok.
|
||||
report_corrupted(SQL) ->
|
||||
?ERROR_MSG("Corrupted values returned by SQL request: ~s",
|
||||
?ERROR_MSG("Corrupted values returned by SQL request: ~ts",
|
||||
[SQL#sql_query.hash]).
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user