mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-22 16:20:52 +01:00
Use new configuration validator
This commit is contained in:
parent
d48c067681
commit
a02cff0e78
11
Makefile.in
11
Makefile.in
@ -119,6 +119,11 @@ update:
|
|||||||
xref: all
|
xref: all
|
||||||
$(REBAR) skip_deps=true xref
|
$(REBAR) skip_deps=true xref
|
||||||
|
|
||||||
|
hooks: all
|
||||||
|
tools/hook_deps.sh ebin
|
||||||
|
|
||||||
|
options: all
|
||||||
|
tools/opt_types.sh ebin
|
||||||
|
|
||||||
translations:
|
translations:
|
||||||
tools/prepare-tr.sh
|
tools/prepare-tr.sh
|
||||||
@ -335,8 +340,8 @@ dialyzer/erlang.plt:
|
|||||||
@mkdir -p dialyzer
|
@mkdir -p dialyzer
|
||||||
@dialyzer --build_plt --output_plt dialyzer/erlang.plt \
|
@dialyzer --build_plt --output_plt dialyzer/erlang.plt \
|
||||||
-o dialyzer/erlang.log --apps kernel stdlib sasl crypto \
|
-o dialyzer/erlang.log --apps kernel stdlib sasl crypto \
|
||||||
public_key ssl mnesia inets odbc tools compiler erts \
|
public_key ssl mnesia inets odbc compiler erts \
|
||||||
runtime_tools asn1 observer xmerl et gs wx syntax_tools; \
|
os_mon asn1 syntax_tools; \
|
||||||
status=$$? ; if [ $$status -ne 2 ]; then exit $$status; else exit 0; fi
|
status=$$? ; if [ $$status -ne 2 ]; then exit $$status; else exit 0; fi
|
||||||
|
|
||||||
dialyzer/deps.plt:
|
dialyzer/deps.plt:
|
||||||
@ -377,4 +382,4 @@ test:
|
|||||||
|
|
||||||
.PHONY: src edoc dialyzer Makefile TAGS clean clean-rel distclean rel \
|
.PHONY: src edoc dialyzer Makefile TAGS clean clean-rel distclean rel \
|
||||||
install uninstall uninstall-binary uninstall-all translations deps test \
|
install uninstall uninstall-binary uninstall-all translations deps test \
|
||||||
quicktest erlang_plt deps_plt ejabberd_plt
|
quicktest erlang_plt deps_plt ejabberd_plt xref hooks options
|
||||||
|
@ -12,22 +12,10 @@
|
|||||||
### ******* MAKE SURE YOU INDENT SECTIONS CORRECTLY *******
|
### ******* MAKE SURE YOU INDENT SECTIONS CORRECTLY *******
|
||||||
### *******************************************************
|
### *******************************************************
|
||||||
### Refer to http://en.wikipedia.org/wiki/YAML for the brief description.
|
### Refer to http://en.wikipedia.org/wiki/YAML for the brief description.
|
||||||
### However, ejabberd treats different literals as different types:
|
|
||||||
###
|
|
||||||
### - unquoted or single-quoted strings. They are called "atoms".
|
|
||||||
### Example: dog, 'Jupiter', '3.14159', YELLOW
|
|
||||||
###
|
|
||||||
### - numeric literals. Example: 3, -45.0, .0
|
|
||||||
###
|
|
||||||
### - quoted or folded strings.
|
|
||||||
### Examples of quoted string: "Lizzard", "orange".
|
|
||||||
### Example of folded string:
|
|
||||||
### > Art thou not Romeo,
|
|
||||||
### and a Montague?
|
|
||||||
###
|
###
|
||||||
|
|
||||||
hosts:
|
hosts:
|
||||||
- "localhost"
|
- localhost
|
||||||
|
|
||||||
loglevel: 4
|
loglevel: 4
|
||||||
log_rotate_size: 10485760
|
log_rotate_size: 10485760
|
||||||
@ -36,8 +24,8 @@ log_rotate_count: 1
|
|||||||
log_rate_limit: 100
|
log_rate_limit: 100
|
||||||
|
|
||||||
certfiles:
|
certfiles:
|
||||||
- "/etc/letsencrypt/live/localhost/fullchain.pem"
|
- /etc/letsencrypt/live/localhost/fullchain.pem
|
||||||
- "/etc/letsencrypt/live/localhost/privkey.pem"
|
- /etc/letsencrypt/live/localhost/privkey.pem
|
||||||
|
|
||||||
listen:
|
listen:
|
||||||
-
|
-
|
||||||
@ -84,25 +72,25 @@ acl:
|
|||||||
user_regexp: ""
|
user_regexp: ""
|
||||||
loopback:
|
loopback:
|
||||||
ip:
|
ip:
|
||||||
- "127.0.0.0/8"
|
- 127.0.0.0/8
|
||||||
- "::1/128"
|
- ::1/128
|
||||||
|
|
||||||
access_rules:
|
access_rules:
|
||||||
local:
|
local:
|
||||||
- allow: local
|
allow: local
|
||||||
c2s:
|
c2s:
|
||||||
- deny: blocked
|
deny: blocked
|
||||||
- allow
|
allow: all
|
||||||
announce:
|
announce:
|
||||||
- allow: admin
|
allow: admin
|
||||||
configure:
|
configure:
|
||||||
- allow: admin
|
allow: admin
|
||||||
muc_create:
|
muc_create:
|
||||||
- allow: local
|
allow: local
|
||||||
pubsub_createnode:
|
pubsub_createnode:
|
||||||
- allow: local
|
allow: local
|
||||||
trusted_network:
|
trusted_network:
|
||||||
- allow: loopback
|
allow: loopback
|
||||||
|
|
||||||
api_permissions:
|
api_permissions:
|
||||||
"console commands":
|
"console commands":
|
||||||
@ -112,26 +100,26 @@ api_permissions:
|
|||||||
what: "*"
|
what: "*"
|
||||||
"admin access":
|
"admin access":
|
||||||
who:
|
who:
|
||||||
- access:
|
access:
|
||||||
- allow:
|
allow:
|
||||||
- acl: loopback
|
acl: loopback
|
||||||
- acl: admin
|
acl: admin
|
||||||
- oauth:
|
oauth:
|
||||||
- scope: "ejabberd:admin"
|
scope: "ejabberd:admin"
|
||||||
- access:
|
access:
|
||||||
- allow:
|
allow:
|
||||||
- acl: loopback
|
acl: loopback
|
||||||
- acl: admin
|
acl: admin
|
||||||
what:
|
what:
|
||||||
- "*"
|
- "*"
|
||||||
- "!stop"
|
- "!stop"
|
||||||
- "!start"
|
- "!start"
|
||||||
"public commands":
|
"public commands":
|
||||||
who:
|
who:
|
||||||
- ip: "127.0.0.1/8"
|
ip: 127.0.0.1/8
|
||||||
what:
|
what:
|
||||||
- "status"
|
- status
|
||||||
- "connected_users_number"
|
- connected_users_number
|
||||||
|
|
||||||
shaper:
|
shaper:
|
||||||
normal: 1000
|
normal: 1000
|
||||||
@ -140,11 +128,11 @@ shaper:
|
|||||||
shaper_rules:
|
shaper_rules:
|
||||||
max_user_sessions: 10
|
max_user_sessions: 10
|
||||||
max_user_offline_messages:
|
max_user_offline_messages:
|
||||||
- 5000: admin
|
5000: admin
|
||||||
- 100
|
100: all
|
||||||
c2s_shaper:
|
c2s_shaper:
|
||||||
- none: admin
|
none: admin
|
||||||
- normal
|
normal: all
|
||||||
s2s_shaper: fast
|
s2s_shaper: fast
|
||||||
|
|
||||||
modules:
|
modules:
|
||||||
@ -163,7 +151,7 @@ modules:
|
|||||||
mod_fail2ban: {}
|
mod_fail2ban: {}
|
||||||
mod_http_api: {}
|
mod_http_api: {}
|
||||||
mod_http_upload:
|
mod_http_upload:
|
||||||
put_url: "https://@HOST@:5443/upload"
|
put_url: https://@HOST@:5443/upload
|
||||||
mod_last: {}
|
mod_last: {}
|
||||||
mod_mam:
|
mod_mam:
|
||||||
## Mnesia is limited to 2GB, better to use an SQL backend
|
## Mnesia is limited to 2GB, better to use an SQL backend
|
||||||
@ -196,11 +184,11 @@ modules:
|
|||||||
mod_pubsub:
|
mod_pubsub:
|
||||||
access_createnode: pubsub_createnode
|
access_createnode: pubsub_createnode
|
||||||
plugins:
|
plugins:
|
||||||
- "flat"
|
- flat
|
||||||
- "pep"
|
- pep
|
||||||
force_node_config:
|
force_node_config:
|
||||||
## Avoid buggy clients to make their bookmarks public
|
## Avoid buggy clients to make their bookmarks public
|
||||||
"storage:bookmarks":
|
storage:bookmarks:
|
||||||
access_model: whitelist
|
access_model: whitelist
|
||||||
mod_push: {}
|
mod_push: {}
|
||||||
mod_push_keepalive: {}
|
mod_push_keepalive: {}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
path = [] :: [binary()],
|
path = [] :: [binary()],
|
||||||
q = [] :: [{binary() | nokey, binary()}],
|
q = [] :: [{binary() | nokey, binary()}],
|
||||||
us = {<<>>, <<>>} :: {binary(), binary()},
|
us = {<<>>, <<>>} :: {binary(), binary()},
|
||||||
auth :: {binary(), binary()} | {oauth, binary(), []} | undefined,
|
auth :: {binary(), binary()} | {oauth, binary(), []} | undefined | invalid,
|
||||||
lang = <<"">> :: binary(),
|
lang = <<"">> :: binary(),
|
||||||
data = <<"">> :: binary(),
|
data = <<"">> :: binary(),
|
||||||
ip :: {inet:ip_address(), inet:port_number()},
|
ip :: {inet:ip_address(), inet:port_number()},
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
-type local_hint() :: integer() | {apply, atom(), atom()}.
|
-type local_hint() :: integer() | {apply, atom(), atom()}.
|
||||||
|
|
||||||
-record(route, {domain :: binary() | '_',
|
-record(route, {domain :: binary(),
|
||||||
server_host :: binary() | '_',
|
server_host :: binary(),
|
||||||
pid :: undefined | pid(),
|
pid :: undefined | pid(),
|
||||||
local_hint :: local_hint() | undefined | '_'}).
|
local_hint :: local_hint() | undefined}).
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
-type info() :: [{conn, atom()} | {ip, ip()} | {node, atom()}
|
-type info() :: [{conn, atom()} | {ip, ip()} | {node, atom()}
|
||||||
| {oor, boolean()} | {auth_module, atom()}
|
| {oor, boolean()} | {auth_module, atom()}
|
||||||
| {num_stanzas_in, non_neg_integer()}
|
| {num_stanzas_in, non_neg_integer()}
|
||||||
| offline].
|
| {atom(), term()}].
|
||||||
-type prio() :: undefined | integer().
|
-type prio() :: undefined | integer().
|
||||||
|
|
||||||
-endif.
|
-endif.
|
||||||
|
@ -17,19 +17,5 @@
|
|||||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
%%%
|
%%%
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
-compile([{parse_transform, ejabberd_sql_pt}]).
|
||||||
-define(SQL_MARK, sql__mark_).
|
-include("ejabberd_sql.hrl").
|
||||||
-define(SQL(SQL), ?SQL_MARK(SQL)).
|
|
||||||
|
|
||||||
-define(SQL_UPSERT_MARK, sql_upsert__mark_).
|
|
||||||
-define(SQL_UPSERT(Host, Table, Fields),
|
|
||||||
ejabberd_sql:sql_query(Host, ?SQL_UPSERT_MARK(Table, Fields))).
|
|
||||||
-define(SQL_UPSERT_T(Table, Fields),
|
|
||||||
ejabberd_sql:sql_query_t(?SQL_UPSERT_MARK(Table, Fields))).
|
|
||||||
|
|
||||||
-define(SQL_INSERT_MARK, sql_insert__mark_).
|
|
||||||
-define(SQL_INSERT(Table, Fields), ?SQL_INSERT_MARK(Table, Fields)).
|
|
||||||
|
|
||||||
-record(sql_query, {hash, format_query, format_res, args, loc}).
|
|
||||||
|
|
||||||
-record(sql_escape, {string, integer, boolean, in_array_string}).
|
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
attributes = [] :: [{binary(), [binary()]}]}).
|
attributes = [] :: [{binary(), [binary()]}]}).
|
||||||
|
|
||||||
-type tlsopts() :: [{encrypt, tls | starttls | none} |
|
-type tlsopts() :: [{encrypt, tls | starttls | none} |
|
||||||
|
{tls_certfile, binary() | undefined} |
|
||||||
{tls_cacertfile, binary() | undefined} |
|
{tls_cacertfile, binary() | undefined} |
|
||||||
{tls_depth, non_neg_integer() | undefined} |
|
{tls_depth, non_neg_integer() | undefined} |
|
||||||
{tls_verify, hard | soft | false}].
|
{tls_verify, hard | soft | false}].
|
||||||
@ -61,3 +62,18 @@
|
|||||||
-type eldap_config() :: #eldap_config{}.
|
-type eldap_config() :: #eldap_config{}.
|
||||||
-type eldap_search() :: #eldap_search{}.
|
-type eldap_search() :: #eldap_search{}.
|
||||||
-type eldap_entry() :: #eldap_entry{}.
|
-type eldap_entry() :: #eldap_entry{}.
|
||||||
|
|
||||||
|
-define(eldap_config(M, H),
|
||||||
|
#eldap_config{
|
||||||
|
servers = M:ldap_servers(H),
|
||||||
|
backups = M:ldap_backups(H),
|
||||||
|
tls_options = [{encrypt, M:ldap_encrypt(H)},
|
||||||
|
{tls_verify, M:ldap_tls_verify(H)},
|
||||||
|
{tls_certfile, M:ldap_tls_certfile(H)},
|
||||||
|
{tls_cacertfile, M:ldap_tls_cacertfile(H)},
|
||||||
|
{tls_depth, M:ldap_tls_depth(H)}],
|
||||||
|
port = M:ldap_port(H),
|
||||||
|
dn = M:ldap_rootdn(H),
|
||||||
|
password = M:ldap_password(H),
|
||||||
|
base = M:ldap_base(H),
|
||||||
|
deref_aliases = M:ldap_deref_aliases(H)}).
|
||||||
|
@ -22,19 +22,19 @@
|
|||||||
-compile([{parse_transform, lager_transform}]).
|
-compile([{parse_transform, lager_transform}]).
|
||||||
|
|
||||||
-define(DEBUG(Format, Args),
|
-define(DEBUG(Format, Args),
|
||||||
lager:debug(Format, Args)).
|
lager:debug(Format, Args), ok).
|
||||||
|
|
||||||
-define(INFO_MSG(Format, Args),
|
-define(INFO_MSG(Format, Args),
|
||||||
lager:info(Format, Args)).
|
lager:info(Format, Args), ok).
|
||||||
|
|
||||||
-define(WARNING_MSG(Format, Args),
|
-define(WARNING_MSG(Format, Args),
|
||||||
lager:warning(Format, Args)).
|
lager:warning(Format, Args), ok).
|
||||||
|
|
||||||
-define(ERROR_MSG(Format, Args),
|
-define(ERROR_MSG(Format, Args),
|
||||||
lager:error(Format, Args)).
|
lager:error(Format, Args), ok).
|
||||||
|
|
||||||
-define(CRITICAL_MSG(Format, Args),
|
-define(CRITICAL_MSG(Format, Args),
|
||||||
lager:critical(Format, Args)).
|
lager:critical(Format, Args), ok).
|
||||||
|
|
||||||
%% Use only when trying to troubleshoot test problem with ExUnit
|
%% Use only when trying to troubleshoot test problem with ExUnit
|
||||||
-define(EXUNIT_LOG(Format, Args),
|
-define(EXUNIT_LOG(Format, Args),
|
||||||
|
@ -19,12 +19,12 @@
|
|||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
-record(archive_msg,
|
-record(archive_msg,
|
||||||
{us = {<<"">>, <<"">>} :: {binary(), binary()} | '$2',
|
{us = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||||
id = <<>> :: binary() | '_',
|
id = <<>> :: binary(),
|
||||||
timestamp = erlang:timestamp() :: erlang:timestamp() | '_' | '$1',
|
timestamp = erlang:timestamp() :: erlang:timestamp(),
|
||||||
peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3' | undefined,
|
peer = {<<"">>, <<"">>, <<"">>} :: ljid() | undefined,
|
||||||
bare_peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3',
|
bare_peer = {<<"">>, <<"">>, <<"">>} :: ljid(),
|
||||||
packet = #xmlel{} :: xmlel() | message() | '_',
|
packet = #xmlel{} :: xmlel() | message(),
|
||||||
nick = <<"">> :: binary(),
|
nick = <<"">> :: binary(),
|
||||||
type = chat :: chat | groupchat}).
|
type = chat :: chat | groupchat}).
|
||||||
|
|
||||||
|
@ -24,11 +24,13 @@
|
|||||||
|
|
||||||
-record(lqueue,
|
-record(lqueue,
|
||||||
{
|
{
|
||||||
queue :: p1_queue:queue(),
|
queue = p1_queue:new() :: p1_queue:queue(),
|
||||||
max = 0 :: integer()
|
max = 0 :: integer()
|
||||||
}).
|
}).
|
||||||
|
|
||||||
-type lqueue() :: #lqueue{}.
|
-type lqueue() :: #lqueue{}.
|
||||||
|
-type lqueue_elem() :: {binary(), message(), boolean(),
|
||||||
|
erlang:timestamp(), non_neg_integer()}.
|
||||||
|
|
||||||
-record(config,
|
-record(config,
|
||||||
{
|
{
|
||||||
@ -63,7 +65,7 @@
|
|||||||
captcha_whitelist = (?SETS):empty() :: gb_sets:set(),
|
captcha_whitelist = (?SETS):empty() :: gb_sets:set(),
|
||||||
mam = false :: boolean(),
|
mam = false :: boolean(),
|
||||||
pubsub = <<"">> :: binary(),
|
pubsub = <<"">> :: binary(),
|
||||||
lang = ejabberd_config:get_mylang() :: binary()
|
lang = ejabberd_option:language() :: binary()
|
||||||
}).
|
}).
|
||||||
|
|
||||||
-type config() :: #config{}.
|
-type config() :: #config{}.
|
||||||
@ -89,8 +91,8 @@
|
|||||||
{
|
{
|
||||||
message_time = 0 :: integer(),
|
message_time = 0 :: integer(),
|
||||||
presence_time = 0 :: integer(),
|
presence_time = 0 :: integer(),
|
||||||
message_shaper = none :: shaper:shaper(),
|
message_shaper = none :: ejabberd_shaper:shaper(),
|
||||||
presence_shaper = none :: shaper:shaper(),
|
presence_shaper = none :: ejabberd_shaper:shaper(),
|
||||||
message :: message() | undefined,
|
message :: message() | undefined,
|
||||||
presence :: {binary(), presence()} | undefined
|
presence :: {binary(), presence()} | undefined
|
||||||
}).
|
}).
|
||||||
@ -110,11 +112,11 @@
|
|||||||
robots = #{} :: map(),
|
robots = #{} :: map(),
|
||||||
nicks = #{} :: map(),
|
nicks = #{} :: map(),
|
||||||
affiliations = #{} :: map(),
|
affiliations = #{} :: map(),
|
||||||
history :: lqueue(),
|
history = #lqueue{} :: lqueue(),
|
||||||
subject = [] :: [text()],
|
subject = [] :: [text()],
|
||||||
subject_author = <<"">> :: binary(),
|
subject_author = <<"">> :: binary(),
|
||||||
just_created = erlang:system_time(microsecond) :: true | integer(),
|
just_created = erlang:system_time(microsecond) :: true | integer(),
|
||||||
activity = treap:empty() :: treap:treap(),
|
activity = treap:empty() :: treap:treap(),
|
||||||
room_shaper = none :: shaper:shaper(),
|
room_shaper = none :: ejabberd_shaper:shaper(),
|
||||||
room_queue :: p1_queue:queue() | undefined
|
room_queue :: p1_queue:queue() | undefined
|
||||||
}).
|
}).
|
||||||
|
@ -26,11 +26,12 @@
|
|||||||
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.36"}}},
|
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.36"}}},
|
||||||
{xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.3.4"}}},
|
{xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.3.4"}}},
|
||||||
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.19"}}},
|
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.19"}}},
|
||||||
|
{yconf, ".*", {git, "https://github.com/processone/yconf", "master"}},
|
||||||
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
|
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
|
||||||
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.5"}}},
|
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.5"}}},
|
||||||
{pkix, ".*", {git, "https://github.com/processone/pkix", {tag, "1.0.2"}}},
|
{pkix, ".*", {git, "https://github.com/processone/pkix", {tag, "1.0.2"}}},
|
||||||
{jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.8.4"}}},
|
{jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.8.4"}}},
|
||||||
{eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.11"}}},
|
{eimp, ".*", {git, "https://github.com/processone/eimp", "master"}},
|
||||||
{mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.3"}}},
|
{mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.3"}}},
|
||||||
{if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.28"}}}},
|
{if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.28"}}}},
|
||||||
{if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.29"}}}},
|
{if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.29"}}}},
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
-module('ELDAPv3').
|
-module('ELDAPv3').
|
||||||
-compile(nowarn_unused_vars).
|
-compile(nowarn_unused_vars).
|
||||||
|
-dialyzer(no_match).
|
||||||
-include("ELDAPv3.hrl").
|
-include("ELDAPv3.hrl").
|
||||||
-asn1_info([{vsn,'2.0.1'},
|
-asn1_info([{vsn,'2.0.1'},
|
||||||
{module,'ELDAPv3'},
|
{module,'ELDAPv3'},
|
||||||
|
994
src/acl.erl
994
src/acl.erl
File diff suppressed because it is too large
Load Diff
529
src/econf.erl
Normal file
529
src/econf.erl
Normal file
@ -0,0 +1,529 @@
|
|||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% File : econf.erl
|
||||||
|
%%% Purpose : Validator for ejabberd configuration options
|
||||||
|
%%%
|
||||||
|
%%%
|
||||||
|
%%% 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(econf).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([parse/3, validate/2, fail/1, format_error/2, replace_macros/1]).
|
||||||
|
%% Simple types
|
||||||
|
-export([pos_int/0, pos_int/1, non_neg_int/0, non_neg_int/1]).
|
||||||
|
-export([int/0, int/2, number/1, octal/0]).
|
||||||
|
-export([binary/0, binary/1]).
|
||||||
|
-export([string/0, string/1]).
|
||||||
|
-export([enum/1, bool/0, atom/0, any/0]).
|
||||||
|
%% Complex types
|
||||||
|
-export([url/0, url/1]).
|
||||||
|
-export([file/0, file/1]).
|
||||||
|
-export([directory/0, directory/1]).
|
||||||
|
-export([ip/0, ipv4/0, ipv6/0, ip_mask/0, port/0]).
|
||||||
|
-export([re/0, glob/0]).
|
||||||
|
-export([path/0, binary_sep/1]).
|
||||||
|
-export([beam/0, beam/1]).
|
||||||
|
-export([timeout/1, timeout/2]).
|
||||||
|
%% Composite types
|
||||||
|
-export([list/1, list/2]).
|
||||||
|
-export([list_or_single/1, list_or_single/2]).
|
||||||
|
-export([map/2, map/3]).
|
||||||
|
-export([either/2, and_then/2, non_empty/1]).
|
||||||
|
-export([options/1, options/2]).
|
||||||
|
%% Custom types
|
||||||
|
-export([acl/0, shaper/0, url_or_file/0, lang/0]).
|
||||||
|
-export([pem/0, queue_type/0]).
|
||||||
|
-export([jid/0, user/0, domain/0, resource/0]).
|
||||||
|
-export([db_type/1, ldap_filter/0, well_known/2]).
|
||||||
|
-ifdef(SIP).
|
||||||
|
-export([sip_uri/0]).
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
-type error_reason() :: term().
|
||||||
|
-type error_return() :: {error, error_reason(), yconf:ctx()}.
|
||||||
|
-type validator() :: yconf:validator().
|
||||||
|
-type validator(T) :: yconf:validator(T).
|
||||||
|
-type validators() :: yconf:validators().
|
||||||
|
-export_type([validator/0, validator/1, validators/0]).
|
||||||
|
-export_type([error_reason/0, error_return/0]).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
parse(File, Validators, Options) ->
|
||||||
|
try yconf:parse(File, Validators, Options)
|
||||||
|
catch _:{?MODULE, Reason, Ctx} ->
|
||||||
|
{error, Reason, Ctx}
|
||||||
|
end.
|
||||||
|
|
||||||
|
validate(Validator, Y) ->
|
||||||
|
try yconf:validate(Validator, Y)
|
||||||
|
catch _:{?MODULE, Reason, Ctx} ->
|
||||||
|
{error, Reason, Ctx}
|
||||||
|
end.
|
||||||
|
|
||||||
|
replace_macros(Y) ->
|
||||||
|
yconf:replace_macros(Y).
|
||||||
|
|
||||||
|
-spec fail(error_reason()) -> no_return().
|
||||||
|
fail(Reason) ->
|
||||||
|
yconf:fail(?MODULE, Reason).
|
||||||
|
|
||||||
|
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?",
|
||||||
|
[yconf:format_ctx(Ctx),
|
||||||
|
format_module_type(Ctx), Mod,
|
||||||
|
misc:best_match(Mod, Mods)]);
|
||||||
|
format_error({bad_module, Mod}, Ctx)
|
||||||
|
when Ctx == [modules] ->
|
||||||
|
Mods = lists:filter(
|
||||||
|
fun(M) ->
|
||||||
|
case atom_to_list(M) of
|
||||||
|
"mod_" ++ _ -> true;
|
||||||
|
_ -> false
|
||||||
|
end
|
||||||
|
end, ejabberd_config:beams(all)),
|
||||||
|
format("~s: unknown ~s: ~s. Did you mean ~s?",
|
||||||
|
[yconf:format_ctx(Ctx),
|
||||||
|
format_module_type(Ctx), Mod,
|
||||||
|
misc:best_match(Mod, Mods)]);
|
||||||
|
format_error({bad_export, {F, A}, Mod}, Ctx)
|
||||||
|
when Ctx == [listen, module];
|
||||||
|
Ctx == [listen, request_handlers];
|
||||||
|
Ctx == [modules] ->
|
||||||
|
Type = format_module_type(Ctx),
|
||||||
|
Slogan = yconf:format_ctx(Ctx),
|
||||||
|
case lists:member(Mod, ejabberd_config:beams(local)) of
|
||||||
|
true ->
|
||||||
|
format("~s: '~s' is not a ~s", [Slogan, 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, "
|
||||||
|
"consider to upgrade it",
|
||||||
|
[Slogan, Type, Mod, F, A, Type]);
|
||||||
|
false ->
|
||||||
|
format("~s: '~s' doesn't match any known ~s",
|
||||||
|
[Slogan, Mod, Type])
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
format_error({unknown_option, [], _} = Why, Ctx) ->
|
||||||
|
format("~s. 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",
|
||||||
|
[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",
|
||||||
|
[yconf:format_error(Why, Ctx),
|
||||||
|
misc:best_match(Bad, Known),
|
||||||
|
format_known("Possible values", Known)]);
|
||||||
|
format_error({bad_yaml, _, _} = Why, _) ->
|
||||||
|
format_error(Why);
|
||||||
|
format_error(Reason, Ctx) ->
|
||||||
|
[H|T] = format_error(Reason),
|
||||||
|
yconf:format_ctx(Ctx) ++ ": " ++ [string:to_lower(H)|T].
|
||||||
|
|
||||||
|
format_error({bad_db_type, _, Atom}) ->
|
||||||
|
format("unsupported database: ~s", [Atom]);
|
||||||
|
format_error({bad_lang, Lang}) ->
|
||||||
|
format("Invalid language tag: ~s", [Lang]);
|
||||||
|
format_error({bad_pem, Why, Path}) ->
|
||||||
|
format("Failed to read PEM file '~s': ~s",
|
||||||
|
[Path, pkix:format_error(Why)]);
|
||||||
|
format_error({bad_cert, Why, Path}) ->
|
||||||
|
format_error({bad_pem, Why, Path});
|
||||||
|
format_error({bad_jid, Bad}) ->
|
||||||
|
format("Invalid XMPP address: ~s", [Bad]);
|
||||||
|
format_error({bad_user, Bad}) ->
|
||||||
|
format("Invalid user part: ~s", [Bad]);
|
||||||
|
format_error({bad_domain, Bad}) ->
|
||||||
|
format("Invalid domain: ~s", [Bad]);
|
||||||
|
format_error({bad_resource, Bad}) ->
|
||||||
|
format("Invalid resource part: ~s", [Bad]);
|
||||||
|
format_error({bad_ldap_filter, Bad}) ->
|
||||||
|
format("Invalid LDAP filter: ~s", [Bad]);
|
||||||
|
format_error({bad_sip_uri, Bad}) ->
|
||||||
|
format("Invalid SIP URI: ~s", [Bad]);
|
||||||
|
format_error({route_conflict, R}) ->
|
||||||
|
format("Failed to reuse route '~s' because it's "
|
||||||
|
"already registered on a virtual host",
|
||||||
|
[R]);
|
||||||
|
format_error({listener_dup, AddrPort}) ->
|
||||||
|
format("Overlapping listeners found at ~s",
|
||||||
|
[format_addr_port(AddrPort)]);
|
||||||
|
format_error({listener_conflict, AddrPort1, AddrPort2}) ->
|
||||||
|
format("Overlapping listeners found at ~s and ~s",
|
||||||
|
[format_addr_port(AddrPort1),
|
||||||
|
format_addr_port(AddrPort2)]);
|
||||||
|
format_error({invalid_syntax, Reason}) ->
|
||||||
|
format("~s", [Reason]);
|
||||||
|
format_error({missing_module_dep, Mod, DepMod}) ->
|
||||||
|
format("module ~s depends on module ~s, "
|
||||||
|
"which is not found in the config",
|
||||||
|
[Mod, DepMod]);
|
||||||
|
format_error(eimp_error) ->
|
||||||
|
format("ejabberd is built without image converter support", []);
|
||||||
|
format_error({mqtt_codec, Reason}) ->
|
||||||
|
mqtt_codec:format_error(Reason);
|
||||||
|
format_error(Reason) ->
|
||||||
|
yconf:format_error(Reason).
|
||||||
|
|
||||||
|
format_module_type([listen, module]) ->
|
||||||
|
"listening module";
|
||||||
|
format_module_type([listen, request_handlers]) ->
|
||||||
|
"HTTP request handler";
|
||||||
|
format_module_type([modules]) ->
|
||||||
|
"ejabberd module".
|
||||||
|
|
||||||
|
format_known(_, Known) when length(Known) > 20 ->
|
||||||
|
"";
|
||||||
|
format_known(Prefix, Known) ->
|
||||||
|
[Prefix, " are: ", format_join(Known)].
|
||||||
|
|
||||||
|
format_join([]) ->
|
||||||
|
"(empty)";
|
||||||
|
format_join([H|_] = L) when is_atom(H) ->
|
||||||
|
format_join([atom_to_binary(A, utf8) || A <- L]);
|
||||||
|
format_join(L) ->
|
||||||
|
str:join(lists:sort(L), <<", ">>).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Validators from yconf
|
||||||
|
%%%===================================================================
|
||||||
|
pos_int() ->
|
||||||
|
yconf:pos_int().
|
||||||
|
|
||||||
|
pos_int(Inf) ->
|
||||||
|
yconf:pos_int(Inf).
|
||||||
|
|
||||||
|
non_neg_int() ->
|
||||||
|
yconf:non_neg_int().
|
||||||
|
|
||||||
|
non_neg_int(Inf) ->
|
||||||
|
yconf:non_neg_int(Inf).
|
||||||
|
|
||||||
|
int() ->
|
||||||
|
yconf:int().
|
||||||
|
|
||||||
|
int(Min, Max) ->
|
||||||
|
yconf:int(Min, Max).
|
||||||
|
|
||||||
|
number(Min) ->
|
||||||
|
yconf:number(Min).
|
||||||
|
|
||||||
|
octal() ->
|
||||||
|
yconf:octal().
|
||||||
|
|
||||||
|
binary() ->
|
||||||
|
yconf:binary().
|
||||||
|
|
||||||
|
binary(Re) ->
|
||||||
|
yconf:binary(Re).
|
||||||
|
|
||||||
|
enum(L) ->
|
||||||
|
yconf:enum(L).
|
||||||
|
|
||||||
|
bool() ->
|
||||||
|
yconf:bool().
|
||||||
|
|
||||||
|
atom() ->
|
||||||
|
yconf:atom().
|
||||||
|
|
||||||
|
string() ->
|
||||||
|
yconf:string().
|
||||||
|
|
||||||
|
string(Re) ->
|
||||||
|
yconf:string(Re).
|
||||||
|
|
||||||
|
any() ->
|
||||||
|
yconf:any().
|
||||||
|
|
||||||
|
url() ->
|
||||||
|
yconf:url().
|
||||||
|
|
||||||
|
url(Schemes) ->
|
||||||
|
yconf:url(Schemes).
|
||||||
|
|
||||||
|
file() ->
|
||||||
|
yconf:file().
|
||||||
|
|
||||||
|
file(Type) ->
|
||||||
|
yconf:file(Type).
|
||||||
|
|
||||||
|
directory() ->
|
||||||
|
yconf:directory().
|
||||||
|
|
||||||
|
directory(Type) ->
|
||||||
|
yconf:directory(Type).
|
||||||
|
|
||||||
|
ip() ->
|
||||||
|
yconf:ip().
|
||||||
|
|
||||||
|
ipv4() ->
|
||||||
|
yconf:ipv4().
|
||||||
|
|
||||||
|
ipv6() ->
|
||||||
|
yconf:ipv6().
|
||||||
|
|
||||||
|
ip_mask() ->
|
||||||
|
yconf:ip_mask().
|
||||||
|
|
||||||
|
port() ->
|
||||||
|
yconf:port().
|
||||||
|
|
||||||
|
re() ->
|
||||||
|
yconf:re().
|
||||||
|
|
||||||
|
glob() ->
|
||||||
|
yconf:glob().
|
||||||
|
|
||||||
|
path() ->
|
||||||
|
yconf:path().
|
||||||
|
|
||||||
|
binary_sep(Sep) ->
|
||||||
|
yconf:binary_sep(Sep).
|
||||||
|
|
||||||
|
beam() ->
|
||||||
|
yconf:beam().
|
||||||
|
|
||||||
|
beam(Exports) ->
|
||||||
|
yconf:beam(Exports).
|
||||||
|
|
||||||
|
timeout(Units) ->
|
||||||
|
yconf:timeout(Units).
|
||||||
|
|
||||||
|
timeout(Units, Inf) ->
|
||||||
|
yconf:timeout(Units, Inf).
|
||||||
|
|
||||||
|
non_empty(F) ->
|
||||||
|
yconf:non_empty(F).
|
||||||
|
|
||||||
|
list(F) ->
|
||||||
|
yconf:list(F).
|
||||||
|
|
||||||
|
list(F, Opts) ->
|
||||||
|
yconf:list(F, Opts).
|
||||||
|
|
||||||
|
list_or_single(F) ->
|
||||||
|
yconf:list_or_single(F).
|
||||||
|
|
||||||
|
list_or_single(F, Opts) ->
|
||||||
|
yconf:list_or_single(F, Opts).
|
||||||
|
|
||||||
|
map(F1, F2) ->
|
||||||
|
yconf:map(F1, F2).
|
||||||
|
|
||||||
|
map(F1, F2, Opts) ->
|
||||||
|
yconf:map(F1, F2, Opts).
|
||||||
|
|
||||||
|
either(F1, F2) ->
|
||||||
|
yconf:either(F1, F2).
|
||||||
|
|
||||||
|
and_then(F1, F2) ->
|
||||||
|
yconf:and_then(F1, F2).
|
||||||
|
|
||||||
|
options(V) ->
|
||||||
|
yconf:options(V).
|
||||||
|
|
||||||
|
options(V, O) ->
|
||||||
|
yconf:options(V, O).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Custom validators
|
||||||
|
%%%===================================================================
|
||||||
|
acl() ->
|
||||||
|
either(
|
||||||
|
atom(),
|
||||||
|
acl:access_rules_validator()).
|
||||||
|
|
||||||
|
shaper() ->
|
||||||
|
either(
|
||||||
|
atom(),
|
||||||
|
ejabberd_shaper:shaper_rules_validator()).
|
||||||
|
|
||||||
|
-spec url_or_file() -> yconf:validator({file | url, binary()}).
|
||||||
|
url_or_file() ->
|
||||||
|
either(
|
||||||
|
and_then(url(), fun(URL) -> {url, URL} end),
|
||||||
|
and_then(file(), fun(File) -> {file, File} end)).
|
||||||
|
|
||||||
|
-spec lang() -> yconf:validator(binary()).
|
||||||
|
lang() ->
|
||||||
|
and_then(
|
||||||
|
binary(),
|
||||||
|
fun(Lang) ->
|
||||||
|
try xmpp_lang:check(Lang)
|
||||||
|
catch _:_ -> fail({bad_lang, Lang})
|
||||||
|
end
|
||||||
|
end).
|
||||||
|
|
||||||
|
-spec pem() -> yconf:validator(binary()).
|
||||||
|
pem() ->
|
||||||
|
and_then(
|
||||||
|
path(),
|
||||||
|
fun(Path) ->
|
||||||
|
case pkix:is_pem_file(Path) of
|
||||||
|
true -> Path;
|
||||||
|
{false, Reason} ->
|
||||||
|
fail({bad_pem, Reason, Path})
|
||||||
|
end
|
||||||
|
end).
|
||||||
|
|
||||||
|
-spec jid() -> yconf:validator(jid:jid()).
|
||||||
|
jid() ->
|
||||||
|
and_then(
|
||||||
|
binary(),
|
||||||
|
fun(Val) ->
|
||||||
|
try jid:decode(Val)
|
||||||
|
catch _:{bad_jid, _} = Reason -> fail(Reason)
|
||||||
|
end
|
||||||
|
end).
|
||||||
|
|
||||||
|
-spec user() -> yconf:validator(binary()).
|
||||||
|
user() ->
|
||||||
|
and_then(
|
||||||
|
binary(),
|
||||||
|
fun(Val) ->
|
||||||
|
case jid:nodeprep(Val) of
|
||||||
|
error -> fail({bad_user, Val});
|
||||||
|
U -> U
|
||||||
|
end
|
||||||
|
end).
|
||||||
|
|
||||||
|
-spec domain() -> yconf:validator(binary()).
|
||||||
|
domain() ->
|
||||||
|
and_then(
|
||||||
|
non_empty(binary()),
|
||||||
|
fun(Val) ->
|
||||||
|
try jid:tolower(jid:decode(Val)) of
|
||||||
|
{<<"">>, Domain, <<"">>} -> Domain;
|
||||||
|
_ -> fail({bad_domain, Val})
|
||||||
|
catch _:{bad_jid, _} ->
|
||||||
|
fail({bad_domain, Val})
|
||||||
|
end
|
||||||
|
end).
|
||||||
|
|
||||||
|
-spec resource() -> yconf:validator(binary()).
|
||||||
|
resource() ->
|
||||||
|
and_then(
|
||||||
|
binary(),
|
||||||
|
fun(Val) ->
|
||||||
|
case jid:resourceprep(Val) of
|
||||||
|
error -> fail({bad_resource, Val});
|
||||||
|
R -> R
|
||||||
|
end
|
||||||
|
end).
|
||||||
|
|
||||||
|
-spec db_type(module()) -> yconf:validator(atom()).
|
||||||
|
db_type(M) ->
|
||||||
|
and_then(
|
||||||
|
atom(),
|
||||||
|
fun(T) ->
|
||||||
|
case code:ensure_loaded(db_module(M, T)) of
|
||||||
|
{module, _} -> T;
|
||||||
|
{error, _} -> fail({bad_db_type, M, T})
|
||||||
|
end
|
||||||
|
end).
|
||||||
|
|
||||||
|
-spec queue_type() -> yconf:validator(ram | file).
|
||||||
|
queue_type() ->
|
||||||
|
enum([ram, file]).
|
||||||
|
|
||||||
|
-spec ldap_filter() -> yconf:validator(binary()).
|
||||||
|
ldap_filter() ->
|
||||||
|
and_then(
|
||||||
|
binary(),
|
||||||
|
fun(Val) ->
|
||||||
|
case eldap_filter:parse(Val) of
|
||||||
|
{ok, _} -> Val;
|
||||||
|
_ -> fail({bad_ldap_filter, Val})
|
||||||
|
end
|
||||||
|
end).
|
||||||
|
|
||||||
|
well_known(queue_type, _) ->
|
||||||
|
queue_type();
|
||||||
|
well_known(db_type, M) ->
|
||||||
|
db_type(M);
|
||||||
|
well_known(ram_db_type, M) ->
|
||||||
|
db_type(M);
|
||||||
|
well_known(cache_life_time, _) ->
|
||||||
|
pos_int(infinity);
|
||||||
|
well_known(cache_size, _) ->
|
||||||
|
pos_int(infinity);
|
||||||
|
well_known(use_cache, _) ->
|
||||||
|
bool();
|
||||||
|
well_known(cache_missed, _) ->
|
||||||
|
bool();
|
||||||
|
well_known(host, _) ->
|
||||||
|
host();
|
||||||
|
well_known(hosts, _) ->
|
||||||
|
list(host(), [unique]).
|
||||||
|
|
||||||
|
-ifdef(SIP).
|
||||||
|
sip_uri() ->
|
||||||
|
and_then(
|
||||||
|
binary(),
|
||||||
|
fun(Val) ->
|
||||||
|
case esip:decode_uri(Val) of
|
||||||
|
error -> fail({bad_sip_uri, Val});
|
||||||
|
URI -> URI
|
||||||
|
end
|
||||||
|
end).
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
-spec host() -> yconf:validator(binary()).
|
||||||
|
host() ->
|
||||||
|
fun(Domain) ->
|
||||||
|
Host = ejabberd_config:get_myname(),
|
||||||
|
Hosts = ejabberd_config:get_option(hosts),
|
||||||
|
Domain1 = (binary())(Domain),
|
||||||
|
Domain2 = misc:expand_keyword(<<"@HOST@">>, Domain1, Host),
|
||||||
|
Domain3 = (domain())(Domain2),
|
||||||
|
case lists:member(Domain3, Hosts) of
|
||||||
|
true -> fail({route_conflict, Domain3});
|
||||||
|
false -> Domain3
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
-spec db_module(module(), atom()) -> module().
|
||||||
|
db_module(M, Type) ->
|
||||||
|
try list_to_atom(atom_to_list(M) ++ "_" ++ atom_to_list(Type))
|
||||||
|
catch _:system_limit ->
|
||||||
|
fail({bad_length, 255})
|
||||||
|
end.
|
||||||
|
|
||||||
|
format_addr_port({IP, Port}) ->
|
||||||
|
IPStr = case tuple_size(IP) of
|
||||||
|
4 -> inet:ntoa(IP);
|
||||||
|
8 -> "[" ++ inet:ntoa(IP) ++ "]"
|
||||||
|
end,
|
||||||
|
IPStr ++ ":" ++ integer_to_list(Port).
|
||||||
|
|
||||||
|
-spec format(iolist(), list()) -> string().
|
||||||
|
format(Fmt, Args) ->
|
||||||
|
lists:flatten(io_lib:format(Fmt, Args)).
|
@ -38,7 +38,7 @@
|
|||||||
-protocol({xep, 270, '1.0'}).
|
-protocol({xep, 270, '1.0'}).
|
||||||
|
|
||||||
-export([start/0, stop/0, halt/0, start_app/1, start_app/2,
|
-export([start/0, stop/0, halt/0, start_app/1, start_app/2,
|
||||||
get_pid_file/0, check_app/1, module_name/1, is_loaded/0]).
|
get_pid_file/0, check_apps/0, module_name/1, is_loaded/0]).
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
@ -49,8 +49,8 @@ stop() ->
|
|||||||
application:stop(ejabberd).
|
application:stop(ejabberd).
|
||||||
|
|
||||||
halt() ->
|
halt() ->
|
||||||
application:stop(lager),
|
_ = application:stop(lager),
|
||||||
application:stop(sasl),
|
_ = application:stop(sasl),
|
||||||
erlang:halt(1, [{flush, true}]).
|
erlang:halt(1, [{flush, true}]).
|
||||||
|
|
||||||
%% @spec () -> false | string()
|
%% @spec () -> false | string()
|
||||||
@ -71,21 +71,15 @@ start_app(App, Type) ->
|
|||||||
StartFlag = not is_loaded(),
|
StartFlag = not is_loaded(),
|
||||||
start_app(App, Type, StartFlag).
|
start_app(App, Type, StartFlag).
|
||||||
|
|
||||||
check_app(App) ->
|
|
||||||
StartFlag = not is_loaded(),
|
|
||||||
spawn(fun() -> check_app_modules(App, StartFlag) end),
|
|
||||||
ok.
|
|
||||||
|
|
||||||
is_loaded() ->
|
is_loaded() ->
|
||||||
Apps = application:which_applications(),
|
Apps = application:which_applications(),
|
||||||
lists:keymember(ejabberd, 1, Apps).
|
lists:keymember(ejabberd, 1, Apps).
|
||||||
|
|
||||||
start_app(App, Type, StartFlag) when not is_list(App) ->
|
start_app(App, Type, StartFlag) when is_atom(App) ->
|
||||||
start_app([App], Type, StartFlag);
|
start_app([App], Type, StartFlag);
|
||||||
start_app([App|Apps], Type, StartFlag) ->
|
start_app([App|Apps], Type, StartFlag) ->
|
||||||
case application:start(App,Type) of
|
case application:start(App,Type) of
|
||||||
ok ->
|
ok ->
|
||||||
spawn(fun() -> check_app_modules(App, StartFlag) end),
|
|
||||||
start_app(Apps, Type, StartFlag);
|
start_app(Apps, Type, StartFlag);
|
||||||
{error, {already_started, _}} ->
|
{error, {already_started, _}} ->
|
||||||
start_app(Apps, Type, StartFlag);
|
start_app(Apps, Type, StartFlag);
|
||||||
@ -93,23 +87,23 @@ start_app([App|Apps], Type, StartFlag) ->
|
|||||||
case lists:member(DepApp, [App|Apps]) of
|
case lists:member(DepApp, [App|Apps]) of
|
||||||
true ->
|
true ->
|
||||||
Reason = io_lib:format(
|
Reason = io_lib:format(
|
||||||
"failed to start application '~p': "
|
"Failed to start Erlang application '~s': "
|
||||||
"circular dependency on '~p' detected",
|
"circular dependency with '~s' detected",
|
||||||
[App, DepApp]),
|
[App, DepApp]),
|
||||||
exit_or_halt(Reason, StartFlag);
|
exit_or_halt(Reason, StartFlag);
|
||||||
false ->
|
false ->
|
||||||
start_app([DepApp,App|Apps], Type, StartFlag)
|
start_app([DepApp,App|Apps], Type, StartFlag)
|
||||||
end;
|
end;
|
||||||
Err ->
|
{error, Why} ->
|
||||||
Reason = io_lib:format("failed to start application '~p': ~p",
|
Reason = io_lib:format(
|
||||||
[App, Err]),
|
"Failed to start Erlang application '~s': ~s. ~s",
|
||||||
|
[App, format_error(Why), hint()]),
|
||||||
exit_or_halt(Reason, StartFlag)
|
exit_or_halt(Reason, StartFlag)
|
||||||
end;
|
end;
|
||||||
start_app([], _Type, _StartFlag) ->
|
start_app([], _Type, _StartFlag) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
check_app_modules(App, StartFlag) ->
|
check_app_modules(App, StartFlag) ->
|
||||||
sleep(5000),
|
|
||||||
case application:get_key(App, modules) of
|
case application:get_key(App, modules) of
|
||||||
{ok, Mods} ->
|
{ok, Mods} ->
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
@ -118,12 +112,12 @@ check_app_modules(App, StartFlag) ->
|
|||||||
non_existing ->
|
non_existing ->
|
||||||
File = get_module_file(App, Mod),
|
File = get_module_file(App, Mod),
|
||||||
Reason = io_lib:format(
|
Reason = io_lib:format(
|
||||||
"couldn't find module ~s "
|
"Couldn't find file ~s needed "
|
||||||
"needed for application '~p'",
|
"for Erlang application '~s'. ~s",
|
||||||
[File, App]),
|
[File, App, hint()]),
|
||||||
exit_or_halt(Reason, StartFlag);
|
exit_or_halt(Reason, StartFlag);
|
||||||
_ ->
|
_ ->
|
||||||
sleep(10)
|
ok
|
||||||
end
|
end
|
||||||
end, Mods);
|
end, Mods);
|
||||||
_ ->
|
_ ->
|
||||||
@ -131,6 +125,23 @@ check_app_modules(App, StartFlag) ->
|
|||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
check_apps() ->
|
||||||
|
spawn(
|
||||||
|
fun() ->
|
||||||
|
Apps = [ejabberd |
|
||||||
|
[App || {App, _, _} <- application:which_applications(),
|
||||||
|
App /= ejabberd]],
|
||||||
|
?DEBUG("Checking consistency of applications: ~s",
|
||||||
|
[misc:join_atoms(Apps, <<", ">>)]),
|
||||||
|
misc:peach(
|
||||||
|
fun(App) ->
|
||||||
|
check_app_modules(App, true)
|
||||||
|
end, Apps),
|
||||||
|
?DEBUG("All applications are intact", []),
|
||||||
|
lists:foreach(fun erlang:garbage_collect/1, processes())
|
||||||
|
end).
|
||||||
|
|
||||||
|
-spec exit_or_halt(iodata(), boolean()) -> no_return().
|
||||||
exit_or_halt(Reason, StartFlag) ->
|
exit_or_halt(Reason, StartFlag) ->
|
||||||
?CRITICAL_MSG(Reason, []),
|
?CRITICAL_MSG(Reason, []),
|
||||||
if StartFlag ->
|
if StartFlag ->
|
||||||
@ -140,9 +151,6 @@ exit_or_halt(Reason, StartFlag) ->
|
|||||||
erlang:error(application_start_failed)
|
erlang:error(application_start_failed)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
sleep(N) ->
|
|
||||||
timer:sleep(p1_rand:uniform(N)).
|
|
||||||
|
|
||||||
get_module_file(App, Mod) ->
|
get_module_file(App, Mod) ->
|
||||||
BaseName = atom_to_list(Mod),
|
BaseName = atom_to_list(Mod),
|
||||||
case code:lib_dir(App, ebin) of
|
case code:lib_dir(App, ebin) of
|
||||||
@ -177,3 +185,12 @@ erlang_name(Atom) when is_atom(Atom) ->
|
|||||||
misc:atom_to_binary(Atom);
|
misc:atom_to_binary(Atom);
|
||||||
erlang_name(Bin) when is_binary(Bin) ->
|
erlang_name(Bin) when is_binary(Bin) ->
|
||||||
Bin.
|
Bin.
|
||||||
|
|
||||||
|
format_error({Reason, File}) when is_list(Reason), is_list(File) ->
|
||||||
|
Reason ++ ": " ++ File;
|
||||||
|
format_error(Term) ->
|
||||||
|
io_lib:format("~p", [Term]).
|
||||||
|
|
||||||
|
hint() ->
|
||||||
|
"This usually means that ejabberd or Erlang "
|
||||||
|
"was compiled/installed incorrectly.".
|
||||||
|
@ -29,17 +29,13 @@
|
|||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([start_link/0,
|
-export([start_link/0,
|
||||||
parse_api_permissions/1,
|
|
||||||
can_access/2,
|
can_access/2,
|
||||||
invalidate/0,
|
invalidate/0,
|
||||||
opt_type/1,
|
validator/0,
|
||||||
show_current_definitions/0,
|
show_current_definitions/0]).
|
||||||
register_permission_addon/2,
|
|
||||||
unregister_permission_addon/1]).
|
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([init/1,
|
-export([init/1,
|
||||||
@ -51,16 +47,29 @@
|
|||||||
|
|
||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
-record(state, {
|
-record(state,
|
||||||
definitions = none,
|
{definitions = none :: none | [definition()]}).
|
||||||
fragments_generators = []
|
|
||||||
}).
|
-type state() :: #state{}.
|
||||||
|
-type rule() :: {access, acl:access()} |
|
||||||
|
{acl, all | none | acl:acl_rule()}.
|
||||||
|
-type what() :: all | none | [atom() | {tag, atom()}].
|
||||||
|
-type who() :: rule() | {oauth, {[binary()], [rule()]}}.
|
||||||
|
-type from() :: atom().
|
||||||
|
-type permission() :: {binary(), {[from()], [who()], {what(), what()}}}.
|
||||||
|
-type definition() :: {binary(), {[from()], [who()], [atom()] | all}}.
|
||||||
|
-type caller_info() :: #{caller_module => module(),
|
||||||
|
caller_host => global | binary(),
|
||||||
|
tag => binary() | none,
|
||||||
|
extra_permissions => [definition()],
|
||||||
|
atom() => term()}.
|
||||||
|
|
||||||
|
-export_type([permission/0]).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% API
|
%%% API
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
-spec can_access(atom(), caller_info()) -> allow | deny.
|
||||||
-spec can_access(atom(), map()) -> allow | deny.
|
|
||||||
can_access(Cmd, CallerInfo) ->
|
can_access(Cmd, CallerInfo) ->
|
||||||
gen_server:call(?MODULE, {can_access, Cmd, CallerInfo}).
|
gen_server:call(?MODULE, {can_access, Cmd, CallerInfo}).
|
||||||
|
|
||||||
@ -68,65 +77,24 @@ can_access(Cmd, CallerInfo) ->
|
|||||||
invalidate() ->
|
invalidate() ->
|
||||||
gen_server:cast(?MODULE, invalidate).
|
gen_server:cast(?MODULE, invalidate).
|
||||||
|
|
||||||
-spec register_permission_addon(atom(), fun()) -> ok.
|
-spec show_current_definitions() -> [definition()].
|
||||||
register_permission_addon(Name, Fun) ->
|
|
||||||
gen_server:call(?MODULE, {register_config_fragment_generator, Name, Fun}).
|
|
||||||
|
|
||||||
-spec unregister_permission_addon(atom()) -> ok.
|
|
||||||
unregister_permission_addon(Name) ->
|
|
||||||
gen_server:call(?MODULE, {unregister_config_fragment_generator, Name}).
|
|
||||||
|
|
||||||
-spec show_current_definitions() -> any().
|
|
||||||
show_current_definitions() ->
|
show_current_definitions() ->
|
||||||
gen_server:call(?MODULE, show_current_definitions).
|
gen_server:call(?MODULE, show_current_definitions).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% @doc
|
|
||||||
%% Starts the server
|
|
||||||
%%
|
|
||||||
%% @end
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
-spec start_link() -> {ok, Pid :: pid()} | ignore | {error, Reason :: term()}.
|
|
||||||
start_link() ->
|
start_link() ->
|
||||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% gen_server callbacks
|
%%% gen_server callbacks
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
-spec init([]) -> {ok, state()}.
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% @private
|
|
||||||
%% @doc
|
|
||||||
%% Initializes the server
|
|
||||||
%%
|
|
||||||
%% @spec init(Args) -> {ok, State} |
|
|
||||||
%% {ok, State, Timeout} |
|
|
||||||
%% ignore |
|
|
||||||
%% {stop, Reason}
|
|
||||||
%% @end
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
-spec init(Args :: term()) ->
|
|
||||||
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
|
|
||||||
{stop, Reason :: term()} | ignore.
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
ejabberd_hooks:add(config_reloaded, ?MODULE, invalidate, 90),
|
ejabberd_hooks:add(config_reloaded, ?MODULE, invalidate, 90),
|
||||||
{ok, #state{}}.
|
{ok, #state{}}.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
-spec handle_call({can_access, atom(), caller_info()} |
|
||||||
%% @private
|
show_current_definitions | term(),
|
||||||
%% @doc
|
term(), state()) -> {reply, term(), state()}.
|
||||||
%% Handling call messages
|
|
||||||
%%
|
|
||||||
%% @end
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
-spec handle_call(Request :: term(), From :: {pid(), Tag :: term()},
|
|
||||||
State :: #state{}) ->
|
|
||||||
{reply, Reply :: term(), NewState :: #state{}} |
|
|
||||||
{reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
|
|
||||||
{noreply, NewState :: #state{}} |
|
|
||||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
|
||||||
{stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
|
|
||||||
{stop, Reason :: term(), NewState :: #state{}}.
|
|
||||||
handle_call({can_access, Cmd, CallerInfo}, _From, State) ->
|
handle_call({can_access, Cmd, CallerInfo}, _From, State) ->
|
||||||
CallerModule = maps:get(caller_module, CallerInfo, none),
|
CallerModule = maps:get(caller_module, CallerInfo, none),
|
||||||
Host = maps:get(caller_host, CallerInfo, global),
|
Host = maps:get(caller_host, CallerInfo, global),
|
||||||
@ -137,7 +105,8 @@ handle_call({can_access, Cmd, CallerInfo}, _From, State) ->
|
|||||||
fun({Name, _} = Def, none) ->
|
fun({Name, _} = Def, none) ->
|
||||||
case matches_definition(Def, Cmd, CallerModule, Tag, Host, CallerInfo) of
|
case matches_definition(Def, Cmd, CallerModule, Tag, Host, CallerInfo) of
|
||||||
true ->
|
true ->
|
||||||
?DEBUG("Command '~p' execution allowed by rule '~s' (CallerInfo=~p)", [Cmd, Name, CallerInfo]),
|
?DEBUG("Command '~p' execution allowed by rule "
|
||||||
|
"'~s' (CallerInfo=~p)", [Cmd, Name, CallerInfo]),
|
||||||
allow;
|
allow;
|
||||||
_ ->
|
_ ->
|
||||||
none
|
none
|
||||||
@ -148,109 +117,46 @@ handle_call({can_access, Cmd, CallerInfo}, _From, State) ->
|
|||||||
Res2 = case Res of
|
Res2 = case Res of
|
||||||
allow -> allow;
|
allow -> allow;
|
||||||
_ ->
|
_ ->
|
||||||
?DEBUG("Command '~p' execution denied (CallerInfo=~p)", [Cmd, CallerInfo]),
|
?DEBUG("Command '~p' execution denied "
|
||||||
|
"(CallerInfo=~p)", [Cmd, CallerInfo]),
|
||||||
deny
|
deny
|
||||||
end,
|
end,
|
||||||
{reply, Res2, State2};
|
{reply, Res2, State2};
|
||||||
handle_call(show_current_definitions, _From, State) ->
|
handle_call(show_current_definitions, _From, State) ->
|
||||||
{State2, Defs} = get_definitions(State),
|
{State2, Defs} = get_definitions(State),
|
||||||
{reply, Defs, State2};
|
{reply, Defs, State2};
|
||||||
handle_call({register_config_fragment_generator, Name, Fun}, _From, #state{fragments_generators = Gens} = State) ->
|
|
||||||
NGens = lists:keystore(Name, 1, Gens, {Name, Fun}),
|
|
||||||
{reply, ok, State#state{fragments_generators = NGens}};
|
|
||||||
handle_call({unregister_config_fragment_generator, Name}, _From, #state{fragments_generators = Gens} = State) ->
|
|
||||||
NGens = lists:keydelete(Name, 1, Gens),
|
|
||||||
{reply, ok, State#state{fragments_generators = NGens}};
|
|
||||||
handle_call(_Request, _From, State) ->
|
handle_call(_Request, _From, State) ->
|
||||||
{reply, ok, State}.
|
{reply, ok, State}.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
-spec handle_cast(invalidate | term(), state()) -> {noreply, state()}.
|
||||||
%% @private
|
|
||||||
%% @doc
|
|
||||||
%% Handling cast messages
|
|
||||||
%%
|
|
||||||
%% @end
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
-spec handle_cast(Request :: term(), State :: #state{}) ->
|
|
||||||
{noreply, NewState :: #state{}} |
|
|
||||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
|
||||||
{stop, Reason :: term(), NewState :: #state{}}.
|
|
||||||
handle_cast(invalidate, State) ->
|
handle_cast(invalidate, State) ->
|
||||||
{noreply, State#state{definitions = none}};
|
{noreply, State#state{definitions = none}};
|
||||||
handle_cast(_Request, State) ->
|
handle_cast(_Request, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% @private
|
|
||||||
%% @doc
|
|
||||||
%% Handling all non call/cast messages
|
|
||||||
%%
|
|
||||||
%% @spec handle_info(Info, State) -> {noreply, State} |
|
|
||||||
%% {noreply, State, Timeout} |
|
|
||||||
%% {stop, Reason, State}
|
|
||||||
%% @end
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
-spec handle_info(Info :: timeout() | term(), State :: #state{}) ->
|
|
||||||
{noreply, NewState :: #state{}} |
|
|
||||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
|
||||||
{stop, Reason :: term(), NewState :: #state{}}.
|
|
||||||
handle_info(_Info, State) ->
|
handle_info(_Info, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% @private
|
|
||||||
%% @doc
|
|
||||||
%% This function is called by a gen_server when it is about to
|
|
||||||
%% terminate. It should be the opposite of Module:init/1 and do any
|
|
||||||
%% necessary cleaning up. When it returns, the gen_server terminates
|
|
||||||
%% with Reason. The return value is ignored.
|
|
||||||
%%
|
|
||||||
%% @spec terminate(Reason, State) -> void()
|
|
||||||
%% @end
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
-spec terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
|
|
||||||
State :: #state{}) -> term().
|
|
||||||
terminate(_Reason, _State) ->
|
terminate(_Reason, _State) ->
|
||||||
ejabberd_hooks:delete(config_reloaded, ?MODULE, invalidate, 90).
|
ejabberd_hooks:delete(config_reloaded, ?MODULE, invalidate, 90).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% @private
|
|
||||||
%% @doc
|
|
||||||
%% Convert process state when code is changed
|
|
||||||
%%
|
|
||||||
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
|
|
||||||
%% @end
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
-spec code_change(OldVsn :: term() | {down, term()}, State :: #state{},
|
|
||||||
Extra :: term()) ->
|
|
||||||
{ok, NewState :: #state{}} | {error, Reason :: term()}.
|
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
-spec get_definitions(state()) -> {state(), [definition()]}.
|
||||||
-spec get_definitions(#state{}) -> {#state{}, any()}.
|
|
||||||
get_definitions(#state{definitions = Defs} = State) when Defs /= none ->
|
get_definitions(#state{definitions = Defs} = State) when Defs /= none ->
|
||||||
{State, Defs};
|
{State, Defs};
|
||||||
get_definitions(#state{definitions = none, fragments_generators = Gens} = State) ->
|
get_definitions(#state{definitions = none} = State) ->
|
||||||
DefaultOptions = [{<<"admin access">>,
|
ApiPerms = ejabberd_option:api_permissions(),
|
||||||
{[],
|
|
||||||
[{acl,{acl,admin}},
|
|
||||||
{oauth,[<<"ejabberd:admin">>],[{acl,{acl,admin}}]}],
|
|
||||||
{all, [start, stop]}}}],
|
|
||||||
ApiPerms = ejabberd_config:get_option(api_permissions, DefaultOptions),
|
|
||||||
AllCommands = ejabberd_commands:get_commands_definition(),
|
AllCommands = ejabberd_commands:get_commands_definition(),
|
||||||
Frags = lists:foldl(
|
|
||||||
fun({_Name, Generator}, Acc) ->
|
|
||||||
Acc ++ Generator()
|
|
||||||
end, [], Gens),
|
|
||||||
NDefs0 = lists:map(
|
NDefs0 = lists:map(
|
||||||
fun({Name, {From, Who, {Add, Del}}}) ->
|
fun({Name, {From, Who, {Add, Del}}}) ->
|
||||||
Cmds = filter_commands_with_permissions(AllCommands, Add, Del),
|
Cmds = filter_commands_with_permissions(AllCommands, Add, Del),
|
||||||
{Name, {From, Who, Cmds}}
|
{Name, {From, Who, Cmds}}
|
||||||
end, ApiPerms ++ Frags),
|
end, ApiPerms),
|
||||||
NDefs = case lists:keyfind(<<"console commands">>, 1, NDefs0) of
|
NDefs = case lists:keyfind(<<"console commands">>, 1, NDefs0) of
|
||||||
false ->
|
false ->
|
||||||
[{<<"console commands">>,
|
[{<<"console commands">>,
|
||||||
@ -262,6 +168,8 @@ get_definitions(#state{definitions = none, fragments_generators = Gens} = State)
|
|||||||
end,
|
end,
|
||||||
{State#state{definitions = NDefs}, NDefs}.
|
{State#state{definitions = NDefs}, NDefs}.
|
||||||
|
|
||||||
|
-spec matches_definition(definition(), atom(), module(),
|
||||||
|
atom(), global | binary(), caller_info()) -> boolean().
|
||||||
matches_definition({_Name, {From, Who, What}}, Cmd, Module, Tag, Host, CallerInfo) ->
|
matches_definition({_Name, {From, Who, What}}, Cmd, Module, Tag, Host, CallerInfo) ->
|
||||||
case What == all orelse lists:member(Cmd, What) of
|
case What == all orelse lists:member(Cmd, What) of
|
||||||
true ->
|
true ->
|
||||||
@ -272,17 +180,21 @@ matches_definition({_Name, {From, Who, What}}, Cmd, Module, Tag, Host, CallerInf
|
|||||||
Scope = maps:get(oauth_scope, CallerInfo, none),
|
Scope = maps:get(oauth_scope, CallerInfo, none),
|
||||||
lists:any(
|
lists:any(
|
||||||
fun({access, Access}) when Scope == none ->
|
fun({access, Access}) when Scope == none ->
|
||||||
acl:access_matches(Access, CallerInfo, Host) == allow;
|
acl:match_rule(Host, Access, CallerInfo) == allow;
|
||||||
|
({acl, Name} = Acl) when Scope == none, is_atom(Name) ->
|
||||||
|
acl:match_acl(Host, Acl, CallerInfo);
|
||||||
({acl, Acl}) when Scope == none ->
|
({acl, Acl}) when Scope == none ->
|
||||||
acl:acl_rule_matches(Acl, CallerInfo, Host);
|
acl:match_acl(Host, Acl, CallerInfo);
|
||||||
({oauth, Scopes, List}) when Scope /= none ->
|
({oauth, {Scopes, List}}) when Scope /= none ->
|
||||||
case ejabberd_oauth:scope_in_scope_list(Scope, Scopes) of
|
case ejabberd_oauth:scope_in_scope_list(Scope, Scopes) of
|
||||||
true ->
|
true ->
|
||||||
lists:any(
|
lists:any(
|
||||||
fun({access, Access}) ->
|
fun({access, Access}) ->
|
||||||
acl:access_matches(Access, CallerInfo, Host) == allow;
|
acl:match_rule(Host, Access, CallerInfo) == allow;
|
||||||
|
({acl, Name} = Acl) when is_atom(Name) ->
|
||||||
|
acl:match_acl(Host, Acl, CallerInfo);
|
||||||
({acl, Acl}) ->
|
({acl, Acl}) ->
|
||||||
acl:acl_rule_matches(Acl, CallerInfo, Host)
|
acl:match_acl(Host, Acl, CallerInfo)
|
||||||
end, List);
|
end, List);
|
||||||
_ ->
|
_ ->
|
||||||
false
|
false
|
||||||
@ -297,12 +209,15 @@ matches_definition({_Name, {From, Who, What}}, Cmd, Module, Tag, Host, CallerInf
|
|||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec filter_commands_with_permissions([#ejabberd_commands{}], what(), what()) -> [atom()].
|
||||||
filter_commands_with_permissions(AllCommands, Add, Del) ->
|
filter_commands_with_permissions(AllCommands, Add, Del) ->
|
||||||
CommandsAdd = filter_commands_with_patterns(AllCommands, Add, []),
|
CommandsAdd = filter_commands_with_patterns(AllCommands, Add, []),
|
||||||
CommandsDel = filter_commands_with_patterns(CommandsAdd, Del, []),
|
CommandsDel = filter_commands_with_patterns(CommandsAdd, Del, []),
|
||||||
lists:map(fun(#ejabberd_commands{name = N}) -> N end,
|
lists:map(fun(#ejabberd_commands{name = N}) -> N end,
|
||||||
CommandsAdd -- CommandsDel).
|
CommandsAdd -- CommandsDel).
|
||||||
|
|
||||||
|
-spec filter_commands_with_patterns([#ejabberd_commands{}], what(),
|
||||||
|
[#ejabberd_commands{}]) -> [#ejabberd_commands{}].
|
||||||
filter_commands_with_patterns([], _Patterns, Acc) ->
|
filter_commands_with_patterns([], _Patterns, Acc) ->
|
||||||
Acc;
|
Acc;
|
||||||
filter_commands_with_patterns([C | CRest], Patterns, Acc) ->
|
filter_commands_with_patterns([C | CRest], Patterns, Acc) ->
|
||||||
@ -313,6 +228,7 @@ filter_commands_with_patterns([C | CRest], Patterns, Acc) ->
|
|||||||
filter_commands_with_patterns(CRest, Patterns, Acc)
|
filter_commands_with_patterns(CRest, Patterns, Acc)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec command_matches_patterns(#ejabberd_commands{}, what()) -> boolean().
|
||||||
command_matches_patterns(_, all) ->
|
command_matches_patterns(_, all) ->
|
||||||
true;
|
true;
|
||||||
command_matches_patterns(_, none) ->
|
command_matches_patterns(_, none) ->
|
||||||
@ -332,115 +248,16 @@ command_matches_patterns(C, [_ | Tail]) ->
|
|||||||
command_matches_patterns(C, Tail).
|
command_matches_patterns(C, Tail).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Options parsing code
|
%%% Validators
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
-spec parse_what([binary()]) -> {what(), what()}.
|
||||||
parse_api_permissions(Data) when is_list(Data) ->
|
parse_what(Defs) ->
|
||||||
[parse_api_permission(Name, Args) || {Name, Args} <- Data].
|
{A, D} =
|
||||||
|
lists:foldl(
|
||||||
parse_api_permission(Name, Args0) ->
|
|
||||||
Args = lists:flatten(Args0),
|
|
||||||
{From, Who, What} = case key_split(Args, [{from, []}, {who, none}, {what, []}]) of
|
|
||||||
{error, Msg} ->
|
|
||||||
report_error(<<"~s inside api_permission '~s' section">>, [Msg, Name]);
|
|
||||||
Val -> Val
|
|
||||||
end,
|
|
||||||
{Name, {parse_from(Name, From), parse_who(Name, Who, oauth), parse_what(Name, What)}}.
|
|
||||||
|
|
||||||
parse_from(_Name, Module) when is_atom(Module) ->
|
|
||||||
[Module];
|
|
||||||
parse_from(Name, Modules) when is_list(Modules) ->
|
|
||||||
lists:map(
|
|
||||||
fun(Module) when is_atom(Module) ->
|
|
||||||
Module;
|
|
||||||
([{tag, Tag}]) when is_binary(Tag) ->
|
|
||||||
{tag, Tag};
|
|
||||||
(Val) ->
|
|
||||||
report_error(<<"Invalid value '~p' used inside 'from' section for api_permission '~s'">>,
|
|
||||||
[Val, Name])
|
|
||||||
end, Modules);
|
|
||||||
parse_from(Name, Val) ->
|
|
||||||
report_error(<<"Invalid value '~p' used inside 'from' section for api_permission '~s'">>,
|
|
||||||
[Val, Name]).
|
|
||||||
|
|
||||||
parse_who(Name, Atom, ParseOauth) when is_atom(Atom) ->
|
|
||||||
parse_who(Name, [Atom], ParseOauth);
|
|
||||||
parse_who(Name, Defs, ParseOauth) when is_list(Defs) ->
|
|
||||||
lists:map(
|
|
||||||
fun([Val]) ->
|
|
||||||
[NVal] = parse_who(Name, [Val], ParseOauth),
|
|
||||||
NVal;
|
|
||||||
({access, Val}) ->
|
|
||||||
try acl:access_rules_validator(Val) of
|
|
||||||
Rule ->
|
|
||||||
{access, Rule}
|
|
||||||
catch
|
|
||||||
throw:{invalid_syntax, Msg} ->
|
|
||||||
report_error(<<"Invalid access rule: '~s' used inside 'who' section for api_permission '~s'">>,
|
|
||||||
[Msg, Name]);
|
|
||||||
error:_ ->
|
|
||||||
report_error(<<"Invalid access rule '~p' used inside 'who' section for api_permission '~s'">>,
|
|
||||||
[Val, Name])
|
|
||||||
end;
|
|
||||||
({oauth, OauthList}) when is_list(OauthList) ->
|
|
||||||
case ParseOauth of
|
|
||||||
oauth ->
|
|
||||||
Nested = parse_who(Name, lists:flatten(OauthList), scope),
|
|
||||||
{Scopes, Rest} = lists:partition(
|
|
||||||
fun({scope, _}) -> true;
|
|
||||||
(_) -> false
|
|
||||||
end, Nested),
|
|
||||||
case Scopes of
|
|
||||||
[] ->
|
|
||||||
report_error(<<"Oauth rule must contain at least one scope rule in 'who' section for api_permission '~s'">>,
|
|
||||||
[Name]);
|
|
||||||
_ ->
|
|
||||||
{oauth, lists:foldl(fun({scope, S}, A) -> S ++ A end, [], Scopes), Rest}
|
|
||||||
end;
|
|
||||||
scope ->
|
|
||||||
report_error(<<"Oauth rule can't be embedded inside other oauth rule in 'who' section for api_permission '~s'">>,
|
|
||||||
[Name])
|
|
||||||
end;
|
|
||||||
({scope, ScopeList}) ->
|
|
||||||
case ParseOauth of
|
|
||||||
oauth ->
|
|
||||||
report_error(<<"Scope can be included only inside oauth rule in 'who' section for api_permission '~s'">>,
|
|
||||||
[Name]);
|
|
||||||
scope ->
|
|
||||||
ScopeList2 = case ScopeList of
|
|
||||||
V when is_binary(V) -> [V];
|
|
||||||
V2 when is_list(V2) -> V2;
|
|
||||||
V3 ->
|
|
||||||
report_error(<<"Invalid value for scope '~p' in 'who' section for api_permission '~s'">>,
|
|
||||||
[V3, Name])
|
|
||||||
end,
|
|
||||||
{scope, ScopeList2}
|
|
||||||
end;
|
|
||||||
(Atom) when is_atom(Atom) ->
|
|
||||||
{acl, {acl, Atom}};
|
|
||||||
(Other) ->
|
|
||||||
try acl:normalize_spec(Other) of
|
|
||||||
Rule2 ->
|
|
||||||
{acl, Rule2}
|
|
||||||
catch
|
|
||||||
_:_ ->
|
|
||||||
report_error(<<"Invalid value '~p' used inside 'who' section for api_permission '~s'">>,
|
|
||||||
[Other, Name])
|
|
||||||
end
|
|
||||||
end, Defs);
|
|
||||||
parse_who(Name, Val, _ParseOauth) ->
|
|
||||||
report_error(<<"Invalid value '~p' used inside 'who' section for api_permission '~s'">>,
|
|
||||||
[Val, Name]).
|
|
||||||
|
|
||||||
parse_what(Name, Binary) when is_binary(Binary) ->
|
|
||||||
parse_what(Name, [Binary]);
|
|
||||||
parse_what(Name, Defs) when is_list(Defs) ->
|
|
||||||
{A, D} = lists:foldl(
|
|
||||||
fun(Def, {Add, Del}) ->
|
fun(Def, {Add, Del}) ->
|
||||||
case parse_single_what(Def) of
|
case parse_single_what(Def) of
|
||||||
{error, Err} ->
|
{error, Err} ->
|
||||||
report_error(<<"~s used in value '~p' in 'what' section for api_permission '~s'">>,
|
econf:fail({invalid_syntax, [Err, ": ", Def]});
|
||||||
[Err, Def, Name]);
|
|
||||||
all ->
|
all ->
|
||||||
{case Add of none -> none; _ -> all end, Del};
|
{case Add of none -> none; _ -> all end, Del};
|
||||||
{neg, all} ->
|
{neg, all} ->
|
||||||
@ -458,11 +275,9 @@ parse_what(Name, Defs) when is_list(Defs) ->
|
|||||||
{A2, none};
|
{A2, none};
|
||||||
V ->
|
V ->
|
||||||
V
|
V
|
||||||
end;
|
end.
|
||||||
parse_what(Name, Val) ->
|
|
||||||
report_error(<<"Invalid value '~p' used inside 'what' section for api_permission '~s'">>,
|
|
||||||
[Val, Name]).
|
|
||||||
|
|
||||||
|
-spec parse_single_what(binary()) -> atom() | {neg, atom()} | {tag, atom()} | {error, string()}.
|
||||||
parse_single_what(<<"*">>) ->
|
parse_single_what(<<"*">>) ->
|
||||||
all;
|
all;
|
||||||
parse_single_what(<<"!*">>) ->
|
parse_single_what(<<"!*">>) ->
|
||||||
@ -470,7 +285,7 @@ parse_single_what(<<"!*">>) ->
|
|||||||
parse_single_what(<<"!", Rest/binary>>) ->
|
parse_single_what(<<"!", Rest/binary>>) ->
|
||||||
case parse_single_what(Rest) of
|
case parse_single_what(Rest) of
|
||||||
{neg, _} ->
|
{neg, _} ->
|
||||||
{error, <<"Double negation">>};
|
{error, "double negation"};
|
||||||
{error, _} = Err ->
|
{error, _} = Err ->
|
||||||
Err;
|
Err;
|
||||||
V ->
|
V ->
|
||||||
@ -485,71 +300,78 @@ parse_single_what(<<"[tag:", Rest/binary>>) ->
|
|||||||
V when is_atom(V) ->
|
V when is_atom(V) ->
|
||||||
{tag, V};
|
{tag, V};
|
||||||
_ ->
|
_ ->
|
||||||
{error, <<"Invalid tag">>}
|
{error, "invalid tag"}
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
{error, <<"Invalid tag">>}
|
{error, "invalid tag"}
|
||||||
end;
|
end;
|
||||||
parse_single_what(Binary) when is_binary(Binary) ->
|
parse_single_what(B) ->
|
||||||
case is_valid_command_name(Binary) of
|
case re:run(B, "^[a-z0-9_\\-]*$") of
|
||||||
true ->
|
nomatch -> {error, "invalid command"};
|
||||||
binary_to_atom(Binary, latin1);
|
_ -> binary_to_atom(B, latin1)
|
||||||
_ ->
|
|
||||||
{error, <<"Invalid value">>}
|
|
||||||
end;
|
|
||||||
parse_single_what(Atom) when is_atom(Atom) ->
|
|
||||||
parse_single_what(atom_to_binary(Atom, latin1));
|
|
||||||
parse_single_what(_) ->
|
|
||||||
{error, <<"Invalid value">>}.
|
|
||||||
|
|
||||||
is_valid_command_name(<<>>) ->
|
|
||||||
false;
|
|
||||||
is_valid_command_name(Val) ->
|
|
||||||
is_valid_command_name2(Val).
|
|
||||||
|
|
||||||
is_valid_command_name2(<<>>) ->
|
|
||||||
true;
|
|
||||||
is_valid_command_name2(<<K:8, Rest/binary>>) when (K >= $a andalso K =< $z)
|
|
||||||
orelse (K >= $0 andalso K =< $9)
|
|
||||||
orelse K == $_ orelse K == $- ->
|
|
||||||
is_valid_command_name2(Rest);
|
|
||||||
is_valid_command_name2(_) ->
|
|
||||||
false.
|
|
||||||
|
|
||||||
key_split(Args, Fields) ->
|
|
||||||
{_, Order1, Results1, Required1} = lists:foldl(
|
|
||||||
fun({Field, Default}, {Idx, Order, Results, Required}) ->
|
|
||||||
{Idx + 1, maps:put(Field, Idx, Order), [Default | Results], Required};
|
|
||||||
(Field, {Idx, Order, Results, Required}) ->
|
|
||||||
{Idx + 1, maps:put(Field, Idx, Order), [none | Results], maps:put(Field, 1, Required)}
|
|
||||||
end, {1, #{}, [], #{}}, Fields),
|
|
||||||
key_split(Args, list_to_tuple(Results1), Order1, Required1, #{}).
|
|
||||||
|
|
||||||
key_split([], _Results, _Order, Required, _Duplicates) when map_size(Required) > 0 ->
|
|
||||||
parse_error(<<"Missing fields '~s">>, [str:join(maps:keys(Required), <<", ">>)]);
|
|
||||||
key_split([], Results, _Order, _Required, _Duplicates) ->
|
|
||||||
Results;
|
|
||||||
key_split([{Arg, Value} | Rest], Results, Order, Required, Duplicates) ->
|
|
||||||
case maps:find(Arg, Order) of
|
|
||||||
{ok, Idx} ->
|
|
||||||
case maps:is_key(Arg, Duplicates) of
|
|
||||||
false ->
|
|
||||||
Results2 = setelement(Idx, Results, Value),
|
|
||||||
key_split(Rest, Results2, Order, maps:remove(Arg, Required), maps:put(Arg, 1, Duplicates));
|
|
||||||
true ->
|
|
||||||
parse_error(<<"Duplicate field '~s'">>, [Arg])
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
parse_error(<<"Unknown field '~s'">>, [Arg])
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
report_error(Format, Args) ->
|
validator(Map, Opts) ->
|
||||||
throw({invalid_syntax, (str:format(Format, Args))}).
|
econf:and_then(
|
||||||
|
fun(L) when is_list(L) ->
|
||||||
|
lists:map(
|
||||||
|
fun({K, V}) -> {(econf:atom())(K), V};
|
||||||
|
(A) -> {acl, (econf:atom())(A)}
|
||||||
|
end, lists:flatten(L));
|
||||||
|
(A) ->
|
||||||
|
[{acl, (econf:atom())(A)}]
|
||||||
|
end,
|
||||||
|
econf:and_then(
|
||||||
|
econf:options(maps:merge(acl:validators(), Map), Opts),
|
||||||
|
fun(Rules) ->
|
||||||
|
lists:flatmap(
|
||||||
|
fun({Type, Rs}) when is_list(Rs) ->
|
||||||
|
case maps:is_key(Type, acl:validators()) of
|
||||||
|
true -> [{acl, {Type, R}} || R <- Rs];
|
||||||
|
false -> [{Type, Rs}]
|
||||||
|
end;
|
||||||
|
(Other) ->
|
||||||
|
[Other]
|
||||||
|
end, Rules)
|
||||||
|
end)).
|
||||||
|
|
||||||
parse_error(Format, Args) ->
|
validator(from) ->
|
||||||
{error, (str:format(Format, Args))}.
|
fun(L) when is_list(L) ->
|
||||||
|
lists:map(
|
||||||
|
fun({K, V}) -> {(econf:enum([tag]))(K), (econf:binary())(V)};
|
||||||
|
(A) -> (econf:enum([ejabberd_xmlrpc, mod_http_api, ejabberd_ctl]))(A)
|
||||||
|
end, lists:flatten(L));
|
||||||
|
(A) ->
|
||||||
|
[(econf:enum([ejabberd_xmlrpc, mod_http_api, ejabberd_ctl]))(A)]
|
||||||
|
end;
|
||||||
|
validator(what) ->
|
||||||
|
econf:and_then(
|
||||||
|
econf:list_or_single(econf:non_empty(econf:binary())),
|
||||||
|
fun parse_what/1);
|
||||||
|
validator(who) ->
|
||||||
|
validator(#{access => econf:acl(), oauth => validator(oauth)}, []);
|
||||||
|
validator(oauth) ->
|
||||||
|
econf:and_then(
|
||||||
|
validator(#{access => econf:acl(),
|
||||||
|
scope => econf:non_empty(
|
||||||
|
econf:list_or_single(econf:binary()))},
|
||||||
|
[{required, [scope]}]),
|
||||||
|
fun(Os) ->
|
||||||
|
{[Scopes], Rest} = proplists:split(Os, [scope]),
|
||||||
|
{lists:flatten([S || {_, S} <- Scopes]), Rest}
|
||||||
|
end).
|
||||||
|
|
||||||
opt_type(api_permissions) ->
|
validator() ->
|
||||||
fun parse_api_permissions/1;
|
econf:map(
|
||||||
opt_type(_) ->
|
econf:binary(),
|
||||||
[api_permissions].
|
econf:and_then(
|
||||||
|
econf:options(
|
||||||
|
#{from => validator(from),
|
||||||
|
what => validator(what),
|
||||||
|
who => validator(who)}),
|
||||||
|
fun(Os) ->
|
||||||
|
{proplists:get_value(from, Os, []),
|
||||||
|
proplists:get_value(who, Os, none),
|
||||||
|
proplists:get_value(what, Os, [])}
|
||||||
|
end),
|
||||||
|
[unique]).
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
-module (ejabberd_acme).
|
-module (ejabberd_acme).
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
%% ejabberdctl commands
|
%% ejabberdctl commands
|
||||||
-export([get_commands_spec/0,
|
-export([get_commands_spec/0,
|
||||||
@ -18,7 +17,7 @@
|
|||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
terminate/2, code_change/3]).
|
terminate/2, code_change/3]).
|
||||||
-export([start_link/0, opt_type/1, register_certfiles/0]).
|
-export([start_link/0, register_certfiles/0]).
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("xmpp.hrl").
|
-include("xmpp.hrl").
|
||||||
@ -188,7 +187,7 @@ get_certificates1(CAUrl, DomainString, PrivateKey) ->
|
|||||||
Hosts = [list_to_bitstring(D) || D <- Domains],
|
Hosts = [list_to_bitstring(D) || D <- Domains],
|
||||||
get_certificates2(CAUrl, PrivateKey, Hosts).
|
get_certificates2(CAUrl, PrivateKey, Hosts).
|
||||||
|
|
||||||
-spec get_certificates2(url(), jose_jwk:key(), [bitstring()]) -> string().
|
-spec get_certificates2(url(), jose_jwk:key(), [binary()]) -> string().
|
||||||
get_certificates2(CAUrl, PrivateKey, Hosts) ->
|
get_certificates2(CAUrl, PrivateKey, Hosts) ->
|
||||||
%% Get a certificate for each host
|
%% Get a certificate for each host
|
||||||
PemCertKeys = [get_certificate(CAUrl, Host, PrivateKey) || Host <- Hosts],
|
PemCertKeys = [get_certificate(CAUrl, Host, PrivateKey) || Host <- Hosts],
|
||||||
@ -199,8 +198,8 @@ get_certificates2(CAUrl, PrivateKey, Hosts) ->
|
|||||||
%% Format the result to send back to ejabberdctl
|
%% Format the result to send back to ejabberdctl
|
||||||
format_get_certificates_result(SavedCerts).
|
format_get_certificates_result(SavedCerts).
|
||||||
|
|
||||||
-spec format_get_certificates_result([{'ok', bitstring(), _} |
|
-spec format_get_certificates_result([{'ok', binary(), _} |
|
||||||
{'error', bitstring(), _}]) ->
|
{'error', binary(), _}]) ->
|
||||||
string().
|
string().
|
||||||
format_get_certificates_result(Certs) ->
|
format_get_certificates_result(Certs) ->
|
||||||
Cond = lists:all(fun(Cert) ->
|
Cond = lists:all(fun(Cert) ->
|
||||||
@ -217,21 +216,21 @@ format_get_certificates_result(Certs) ->
|
|||||||
lists:flatten(Result)
|
lists:flatten(Result)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec format_get_certificate({'ok', bitstring(), _} |
|
-spec format_get_certificate({'ok', binary(), _} |
|
||||||
{'error', bitstring(), _}) ->
|
{'error', binary(), _}) ->
|
||||||
string().
|
string().
|
||||||
format_get_certificate({ok, Domain, saved}) ->
|
format_get_certificate({ok, Domain, saved}) ->
|
||||||
io_lib:format(" Certificate for domain: \"~s\" acquired and saved", [Domain]);
|
io_lib:format(" Certificate for domain: \"~s\" acquired and saved", [Domain]);
|
||||||
format_get_certificate({ok, Domain, not_found}) ->
|
format_get_certificate({error, Domain, not_found}) ->
|
||||||
io_lib:format(" Certificate for domain: \"~s\" not found, so it was not renewed", [Domain]);
|
io_lib:format(" Certificate for domain: \"~s\" not found, so it was not renewed", [Domain]);
|
||||||
format_get_certificate({ok, Domain, no_expire}) ->
|
format_get_certificate({ok, Domain, no_expire}) ->
|
||||||
io_lib:format(" Certificate for domain: \"~s\" is not close to expiring", [Domain]);
|
io_lib:format(" Certificate for domain: \"~s\" is not close to expiring", [Domain]);
|
||||||
format_get_certificate({error, Domain, Reason}) ->
|
format_get_certificate({error, Domain, Reason}) ->
|
||||||
io_lib:format(" Error for domain: \"~s\", with reason: \'~s\'", [Domain, Reason]).
|
io_lib:format(" Error for domain: \"~s\", with reason: \'~s\'", [Domain, Reason]).
|
||||||
|
|
||||||
-spec get_certificate(url(), bitstring(), jose_jwk:key()) ->
|
-spec get_certificate(url(), binary(), jose_jwk:key()) ->
|
||||||
{'ok', bitstring(), pem()} |
|
{'ok', binary(), pem()} |
|
||||||
{'error', bitstring(), _}.
|
{'error', binary(), _}.
|
||||||
get_certificate(CAUrl, DomainName, PrivateKey) ->
|
get_certificate(CAUrl, DomainName, PrivateKey) ->
|
||||||
try
|
try
|
||||||
AllSubDomains = find_all_sub_domains(DomainName),
|
AllSubDomains = find_all_sub_domains(DomainName),
|
||||||
@ -266,7 +265,7 @@ create_save_new_account(CAUrl) ->
|
|||||||
|
|
||||||
%% TODO:
|
%% TODO:
|
||||||
%% Find a way to ask the user if he accepts the TOS
|
%% Find a way to ask the user if he accepts the TOS
|
||||||
-spec create_new_account(url(), bitstring(), jose_jwk:key()) -> {'ok', string()} |
|
-spec create_new_account(url(), binary(), jose_jwk:key()) -> {'ok', string()} |
|
||||||
no_return().
|
no_return().
|
||||||
create_new_account(CAUrl, Contact, PrivateKey) ->
|
create_new_account(CAUrl, Contact, PrivateKey) ->
|
||||||
try
|
try
|
||||||
@ -287,7 +286,7 @@ create_new_account(CAUrl, Contact, PrivateKey) ->
|
|||||||
throw({error,create_new_account})
|
throw({error,create_new_account})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec create_new_authorization(url(), bitstring(), jose_jwk:key()) ->
|
-spec create_new_authorization(url(), binary(), jose_jwk:key()) ->
|
||||||
{'ok', proplist()} | no_return().
|
{'ok', proplist()} | no_return().
|
||||||
create_new_authorization(CAUrl, DomainName, PrivateKey) ->
|
create_new_authorization(CAUrl, DomainName, PrivateKey) ->
|
||||||
acme_challenge:register_hooks(DomainName),
|
acme_challenge:register_hooks(DomainName),
|
||||||
@ -320,12 +319,12 @@ create_new_authorization(CAUrl, DomainName, PrivateKey) ->
|
|||||||
acme_challenge:unregister_hooks(DomainName)
|
acme_challenge:unregister_hooks(DomainName)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec create_new_certificate(url(), {bitstring(), [bitstring()]}, jose_jwk:key()) ->
|
-spec create_new_certificate(url(), {binary(), [binary()]}, jose_jwk:key()) ->
|
||||||
{ok, bitstring(), pem()}.
|
{ok, binary(), pem()}.
|
||||||
create_new_certificate(CAUrl, {DomainName, AllSubDomains}, PrivateKey) ->
|
create_new_certificate(CAUrl, {DomainName, AllSubDomains}, PrivateKey) ->
|
||||||
try
|
try
|
||||||
{ok, Dirs, Nonce0} = ejabberd_acme_comm:directory(CAUrl),
|
{ok, Dirs, Nonce0} = ejabberd_acme_comm:directory(CAUrl),
|
||||||
CSRSubject = [{commonName, bitstring_to_list(DomainName)}],
|
CSRSubject = [{?'id-at-commonName', bitstring_to_list(DomainName)}],
|
||||||
SANs = [{dNSName, SAN} || SAN <- AllSubDomains],
|
SANs = [{dNSName, SAN} || SAN <- AllSubDomains],
|
||||||
{CSR, CSRKey} = make_csr(CSRSubject, SANs),
|
{CSR, CSRKey} = make_csr(CSRSubject, SANs),
|
||||||
{NotBefore, NotAfter} = not_before_not_after(),
|
{NotBefore, NotAfter} = not_before_not_after(),
|
||||||
@ -404,9 +403,9 @@ renew_certificates0(CAUrl) ->
|
|||||||
%% Format the result to send back to ejabberdctl
|
%% Format the result to send back to ejabberdctl
|
||||||
format_get_certificates_result(SavedCerts).
|
format_get_certificates_result(SavedCerts).
|
||||||
|
|
||||||
-spec renew_certificate(url(), {bitstring(), data_cert()}, jose_jwk:key()) ->
|
-spec renew_certificate(url(), {binary(), data_cert()}, jose_jwk:key()) ->
|
||||||
{'ok', bitstring(), _} |
|
{'ok', binary(), _} |
|
||||||
{'error', bitstring(), _}.
|
{'error', binary(), _}.
|
||||||
renew_certificate(CAUrl, {DomainName, _} = Cert, PrivateKey) ->
|
renew_certificate(CAUrl, {DomainName, _} = Cert, PrivateKey) ->
|
||||||
case cert_to_expire(Cert) of
|
case cert_to_expire(Cert) of
|
||||||
true ->
|
true ->
|
||||||
@ -416,7 +415,7 @@ renew_certificate(CAUrl, {DomainName, _} = Cert, PrivateKey) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
-spec cert_to_expire({bitstring(), data_cert()}) -> boolean().
|
-spec cert_to_expire({binary(), data_cert()}) -> boolean().
|
||||||
cert_to_expire({_DomainName, #data_cert{pem = Pem}}) ->
|
cert_to_expire({_DomainName, #data_cert{pem = Pem}}) ->
|
||||||
Certificate = pem_to_certificate(Pem),
|
Certificate = pem_to_certificate(Pem),
|
||||||
Validity = get_utc_validity(Certificate),
|
Validity = get_utc_validity(Certificate),
|
||||||
@ -494,7 +493,7 @@ format_certificate(DataCert, Verbose) ->
|
|||||||
fail_format_certificate(DomainName)
|
fail_format_certificate(DomainName)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec format_certificate_plain(bitstring(), [string()], {expired | ok, string()}, string())
|
-spec format_certificate_plain(binary(), [string()], {expired | ok, string()}, string())
|
||||||
-> string().
|
-> string().
|
||||||
format_certificate_plain(DomainName, SANs, NotAfter, Path) ->
|
format_certificate_plain(DomainName, SANs, NotAfter, Path) ->
|
||||||
Result = lists:flatten(io_lib:format(
|
Result = lists:flatten(io_lib:format(
|
||||||
@ -507,7 +506,7 @@ format_certificate_plain(DomainName, SANs, NotAfter, Path) ->
|
|||||||
format_validity(NotAfter), Path])),
|
format_validity(NotAfter), Path])),
|
||||||
Result.
|
Result.
|
||||||
|
|
||||||
-spec format_certificate_verbose(bitstring(), [string()], {expired | ok, string()}, bitstring())
|
-spec format_certificate_verbose(binary(), [string()], {expired | ok, string()}, binary())
|
||||||
-> string().
|
-> string().
|
||||||
format_certificate_verbose(DomainName, SANs, NotAfter, PemCert) ->
|
format_certificate_verbose(DomainName, SANs, NotAfter, PemCert) ->
|
||||||
Result = lists:flatten(io_lib:format(
|
Result = lists:flatten(io_lib:format(
|
||||||
@ -526,7 +525,7 @@ format_validity({expired, NotAfter}) ->
|
|||||||
format_validity({ok, NotAfter}) ->
|
format_validity({ok, NotAfter}) ->
|
||||||
io_lib:format("Valid until: ~s UTC", [NotAfter]).
|
io_lib:format("Valid until: ~s UTC", [NotAfter]).
|
||||||
|
|
||||||
-spec fail_format_certificate(bitstring()) -> string().
|
-spec fail_format_certificate(binary()) -> string().
|
||||||
fail_format_certificate(DomainName) ->
|
fail_format_certificate(DomainName) ->
|
||||||
Result = lists:flatten(io_lib:format(
|
Result = lists:flatten(io_lib:format(
|
||||||
" Domain: ~s~n"
|
" Domain: ~s~n"
|
||||||
@ -542,7 +541,7 @@ get_commonName(#'Certificate'{tbsCertificate = TbsCertificate}) ->
|
|||||||
|
|
||||||
%% TODO: Not the best way to find the commonName
|
%% TODO: Not the best way to find the commonName
|
||||||
ShallowSubjectList = [Attribute || [Attribute] <- SubjectList],
|
ShallowSubjectList = [Attribute || [Attribute] <- SubjectList],
|
||||||
{_, _, CommonName} = lists:keyfind(attribute_oid(commonName), 2, ShallowSubjectList),
|
{_, _, CommonName} = lists:keyfind(?'id-at-commonName', 2, ShallowSubjectList),
|
||||||
|
|
||||||
%% TODO: Remove the length-encoding from the commonName before returning it
|
%% TODO: Remove the length-encoding from the commonName before returning it
|
||||||
CommonName.
|
CommonName.
|
||||||
@ -574,7 +573,7 @@ get_subjectAltNames(#'Certificate'{tbsCertificate = TbsCertificate}) ->
|
|||||||
} = TbsCertificate,
|
} = TbsCertificate,
|
||||||
|
|
||||||
EncodedSANs = [Val || #'Extension'{extnID = Oid, extnValue = Val} <- Exts,
|
EncodedSANs = [Val || #'Extension'{extnID = Oid, extnValue = Val} <- Exts,
|
||||||
Oid =:= attribute_oid(subjectAltName)],
|
Oid == ?'id-ce-subjectAltName'],
|
||||||
|
|
||||||
lists:flatmap(
|
lists:flatmap(
|
||||||
fun(EncSAN) ->
|
fun(EncSAN) ->
|
||||||
@ -624,7 +623,7 @@ revoke_certificate0(CAUrl, DomainOrFile) ->
|
|||||||
ParsedCert = parse_revoke_cert_argument(DomainOrFile),
|
ParsedCert = parse_revoke_cert_argument(DomainOrFile),
|
||||||
revoke_certificate1(CAUrl, ParsedCert).
|
revoke_certificate1(CAUrl, ParsedCert).
|
||||||
|
|
||||||
-spec revoke_certificate1(url(), {domain, bitstring()} | {file, file:filename()}) ->
|
-spec revoke_certificate1(url(), {domain, binary()} | {file, file:filename()}) ->
|
||||||
{ok, deleted}.
|
{ok, deleted}.
|
||||||
revoke_certificate1(CAUrl, {domain, Domain}) ->
|
revoke_certificate1(CAUrl, {domain, Domain}) ->
|
||||||
case domain_certificate_exists(Domain) of
|
case domain_certificate_exists(Domain) of
|
||||||
@ -657,13 +656,13 @@ revoke_certificate2(CAUrl, PemEncodedCert) ->
|
|||||||
{ok, [], _Nonce1} = ejabberd_acme_comm:revoke_cert(Dirs, CertPrivateKey, Req, Nonce),
|
{ok, [], _Nonce1} = ejabberd_acme_comm:revoke_cert(Dirs, CertPrivateKey, Req, Nonce),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
-spec parse_revoke_cert_argument(string()) -> {domain, bitstring()} | {file, file:filename()}.
|
-spec parse_revoke_cert_argument(string()) -> {domain, binary()} | {file, file:filename()}.
|
||||||
parse_revoke_cert_argument([$f, $i, $l, $e, $:|File]) ->
|
parse_revoke_cert_argument([$f, $i, $l, $e, $:|File]) ->
|
||||||
{file, File};
|
{file, File};
|
||||||
parse_revoke_cert_argument([$d, $o, $m, $a, $i, $n, $: | Domain]) ->
|
parse_revoke_cert_argument([$d, $o, $m, $a, $i, $n, $: | Domain]) ->
|
||||||
{domain, list_to_bitstring(Domain)}.
|
{domain, list_to_bitstring(Domain)}.
|
||||||
|
|
||||||
-spec prepare_certificate_revoke(pem()) -> {bitstring(), jose_jwk:key()}.
|
-spec prepare_certificate_revoke(pem()) -> {binary(), jose_jwk:key()}.
|
||||||
prepare_certificate_revoke(PemEncodedCert) ->
|
prepare_certificate_revoke(PemEncodedCert) ->
|
||||||
PemList = public_key:pem_decode(PemEncodedCert),
|
PemList = public_key:pem_decode(PemEncodedCert),
|
||||||
PemCertEnc = lists:keyfind('Certificate', 1, PemList),
|
PemCertEnc = lists:keyfind('Certificate', 1, PemList),
|
||||||
@ -674,7 +673,7 @@ prepare_certificate_revoke(PemEncodedCert) ->
|
|||||||
{ok, Key} = find_private_key_in_pem(PemEncodedCert),
|
{ok, Key} = find_private_key_in_pem(PemEncodedCert),
|
||||||
{Base64Cert, Key}.
|
{Base64Cert, Key}.
|
||||||
|
|
||||||
-spec domain_certificate_exists(bitstring()) -> {bitstring(), data_cert()} | false.
|
-spec domain_certificate_exists(binary()) -> {binary(), data_cert()} | false.
|
||||||
domain_certificate_exists(Domain) ->
|
domain_certificate_exists(Domain) ->
|
||||||
Certs = read_certificates_persistent(),
|
Certs = read_certificates_persistent(),
|
||||||
lists:keyfind(Domain, 1, Certs).
|
lists:keyfind(Domain, 1, Certs).
|
||||||
@ -688,7 +687,7 @@ domain_certificate_exists(Domain) ->
|
|||||||
%% For now we accept only generating a key of
|
%% For now we accept only generating a key of
|
||||||
%% specific type for signing the csr
|
%% specific type for signing the csr
|
||||||
|
|
||||||
-spec make_csr(proplist(), [{dNSName, bitstring()}])
|
-spec make_csr(proplist(), [{dNSName, binary()}])
|
||||||
-> {binary(), jose_jwk:key()}.
|
-> {binary(), jose_jwk:key()}.
|
||||||
make_csr(Attributes, SANs) ->
|
make_csr(Attributes, SANs) ->
|
||||||
Key = generate_key(),
|
Key = generate_key(),
|
||||||
@ -698,7 +697,7 @@ make_csr(Attributes, SANs) ->
|
|||||||
SubPKInfoAlgo = subject_pk_info_algo(KeyPub),
|
SubPKInfoAlgo = subject_pk_info_algo(KeyPub),
|
||||||
{ok, RawBinPubKey} = raw_binary_public_key(KeyPub),
|
{ok, RawBinPubKey} = raw_binary_public_key(KeyPub),
|
||||||
SubPKInfo = subject_pk_info(SubPKInfoAlgo, RawBinPubKey),
|
SubPKInfo = subject_pk_info(SubPKInfoAlgo, RawBinPubKey),
|
||||||
{ok, Subject} = attributes_from_list(Attributes),
|
Subject = attributes_from_list(Attributes),
|
||||||
ExtensionRequest = extension_request(SANs),
|
ExtensionRequest = extension_request(SANs),
|
||||||
CRI = certificate_request_info(SubPKInfo, Subject, ExtensionRequest),
|
CRI = certificate_request_info(SubPKInfo, Subject, ExtensionRequest),
|
||||||
{ok, EncodedCRI} = der_encode(
|
{ok, EncodedCRI} = der_encode(
|
||||||
@ -737,7 +736,7 @@ subject_pk_info(Algo, RawBinPubKey) ->
|
|||||||
|
|
||||||
extension(SANs) ->
|
extension(SANs) ->
|
||||||
#'Extension'{
|
#'Extension'{
|
||||||
extnID = attribute_oid(subjectAltName),
|
extnID = ?'id-ce-subjectAltName',
|
||||||
critical = false,
|
critical = false,
|
||||||
extnValue = public_key:der_encode('SubjectAltName', SANs)}.
|
extnValue = public_key:der_encode('SubjectAltName', SANs)}.
|
||||||
|
|
||||||
@ -791,45 +790,12 @@ der_encode(Type, Term) ->
|
|||||||
{error, der_encode}
|
{error, der_encode}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%
|
|
||||||
%% Attributes Parser
|
|
||||||
%%
|
|
||||||
|
|
||||||
attributes_from_list(Attrs) ->
|
attributes_from_list(Attrs) ->
|
||||||
ParsedAttrs = [attribute_parser_fun(Attr) || Attr <- Attrs],
|
{rdnSequence,
|
||||||
case lists:any(fun is_error/1, ParsedAttrs) of
|
[[#'AttributeTypeAndValue'{
|
||||||
true ->
|
type = AttrName,
|
||||||
{error, bad_attributes};
|
|
||||||
false ->
|
|
||||||
{ok, {rdnSequence, [[PAttr] || PAttr <- ParsedAttrs]}}
|
|
||||||
end.
|
|
||||||
|
|
||||||
attribute_parser_fun({AttrName, AttrVal}) ->
|
|
||||||
try
|
|
||||||
#'AttributeTypeAndValue'{
|
|
||||||
type = attribute_oid(AttrName),
|
|
||||||
%% TODO: Check if every attribute should be encoded as
|
|
||||||
%% common name. Actually it doesn't matter in
|
|
||||||
%% practice. Only in theory in order to have cleaner code.
|
|
||||||
value = public_key:der_encode('X520CommonName', {printableString, AttrVal})
|
value = public_key:der_encode('X520CommonName', {printableString, AttrVal})
|
||||||
%% value = length_bitstring(list_to_bitstring(AttrVal))
|
}] || {AttrName, AttrVal} <- Attrs]}.
|
||||||
}
|
|
||||||
catch
|
|
||||||
_:_ ->
|
|
||||||
?ERROR_MSG("Bad attribute: ~p~n", [{AttrName, AttrVal}]),
|
|
||||||
{error, bad_attributes}
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec attribute_oid(atom()) -> tuple() | no_return().
|
|
||||||
attribute_oid(commonName) -> ?'id-at-commonName';
|
|
||||||
attribute_oid(countryName) -> ?'id-at-countryName';
|
|
||||||
attribute_oid(stateOrProvinceName) -> ?'id-at-stateOrProvinceName';
|
|
||||||
attribute_oid(localityName) -> ?'id-at-localityName';
|
|
||||||
attribute_oid(organizationName) -> ?'id-at-organizationName';
|
|
||||||
attribute_oid(subjectAltName) -> ?'id-ce-subjectAltName';
|
|
||||||
attribute_oid(_) -> error(bad_attributes).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
%%
|
%%
|
||||||
@ -939,7 +905,7 @@ private_key_types() ->
|
|||||||
'DSAPrivateKey',
|
'DSAPrivateKey',
|
||||||
'ECPrivateKey'].
|
'ECPrivateKey'].
|
||||||
|
|
||||||
-spec find_all_sub_domains(bitstring()) -> [bitstring()].
|
-spec find_all_sub_domains(binary()) -> [binary()].
|
||||||
find_all_sub_domains(DomainName) ->
|
find_all_sub_domains(DomainName) ->
|
||||||
AllRoutes = ejabberd_router:get_all_routes(),
|
AllRoutes = ejabberd_router:get_all_routes(),
|
||||||
DomainLen = size(DomainName),
|
DomainLen = size(DomainName),
|
||||||
@ -1094,8 +1060,8 @@ remove_certificate_persistent(DataCert) ->
|
|||||||
NewData = data_remove_certificate(Data, DataCert),
|
NewData = data_remove_certificate(Data, DataCert),
|
||||||
ok = write_persistent(NewData).
|
ok = write_persistent(NewData).
|
||||||
|
|
||||||
-spec save_certificate({ok, bitstring(), binary()} | {error, _, _}) ->
|
-spec save_certificate({ok, binary(), binary()} | {error, _, _}) ->
|
||||||
{ok, bitstring(), saved} | {error, bitstring(), _}.
|
{ok, binary(), saved} | {error, binary(), _}.
|
||||||
save_certificate({error, _, _} = Error) ->
|
save_certificate({error, _, _} = Error) ->
|
||||||
Error;
|
Error;
|
||||||
save_certificate({ok, DomainName, Cert}) ->
|
save_certificate({ok, DomainName, Cert}) ->
|
||||||
@ -1123,8 +1089,8 @@ save_certificate({ok, DomainName, Cert}) ->
|
|||||||
{error, DomainName, saving}
|
{error, DomainName, saving}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec save_renewed_certificate({ok, bitstring(), _} | {error, _, _}) ->
|
-spec save_renewed_certificate({ok, binary(), _} | {error, _, _}) ->
|
||||||
{ok, bitstring(), _} | {error, bitstring(), _}.
|
{ok, binary(), _} | {error, binary(), _}.
|
||||||
save_renewed_certificate({error, _, _} = Error) ->
|
save_renewed_certificate({error, _, _} = Error) ->
|
||||||
Error;
|
Error;
|
||||||
save_renewed_certificate({ok, _, no_expire} = Cert) ->
|
save_renewed_certificate({ok, _, no_expire} = Cert) ->
|
||||||
@ -1141,7 +1107,7 @@ register_certfiles() ->
|
|||||||
ejabberd_pkix:add_certfile(Path)
|
ejabberd_pkix:add_certfile(Path)
|
||||||
end, Paths).
|
end, Paths).
|
||||||
|
|
||||||
-spec write_cert(file:filename(), binary(), bitstring()) -> {ok, bitstring(), saved}.
|
-spec write_cert(file:filename(), binary(), binary()) -> ok.
|
||||||
write_cert(CertificateFile, Cert, DomainName) ->
|
write_cert(CertificateFile, Cert, DomainName) ->
|
||||||
case file:write_file(CertificateFile, Cert) of
|
case file:write_file(CertificateFile, Cert) of
|
||||||
ok ->
|
ok ->
|
||||||
@ -1150,59 +1116,34 @@ write_cert(CertificateFile, Cert, DomainName) ->
|
|||||||
{error, Why} ->
|
{error, Why} ->
|
||||||
?WARNING_MSG("Failed to change mode of file ~s: ~s",
|
?WARNING_MSG("Failed to change mode of file ~s: ~s",
|
||||||
[CertificateFile, file:format_error(Why)])
|
[CertificateFile, file:format_error(Why)])
|
||||||
end,
|
end;
|
||||||
{ok, DomainName, saved};
|
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
?ERROR_MSG("Error: ~p saving certificate at file: ~p",
|
?ERROR_MSG("Error: ~p saving certificate at file: ~p",
|
||||||
[Reason, CertificateFile]),
|
[Reason, CertificateFile]),
|
||||||
throw({error, DomainName, saving})
|
throw({error, DomainName, saving})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get_config_acme() -> acme_config().
|
-spec get_config_contact() -> binary().
|
||||||
get_config_acme() ->
|
|
||||||
case ejabberd_config:get_option(acme, undefined) of
|
|
||||||
undefined ->
|
|
||||||
?WARNING_MSG("No acme configuration has been specified", []),
|
|
||||||
%% throw({error, configuration});
|
|
||||||
[];
|
|
||||||
Acme ->
|
|
||||||
Acme
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec get_config_contact() -> bitstring().
|
|
||||||
get_config_contact() ->
|
get_config_contact() ->
|
||||||
Acme = get_config_acme(),
|
Acme = ejabberd_option:acme(),
|
||||||
case lists:keyfind(contact, 1, Acme) of
|
try maps:get(contact, Acme)
|
||||||
{contact, Contact} ->
|
catch _:{badkey, _} ->
|
||||||
Contact;
|
|
||||||
false ->
|
|
||||||
?WARNING_MSG("No contact has been specified in configuration", []),
|
?WARNING_MSG("No contact has been specified in configuration", []),
|
||||||
?DEFAULT_CONFIG_CONTACT
|
?DEFAULT_CONFIG_CONTACT
|
||||||
%% throw({error, configuration_contact})
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get_config_ca_url() -> url().
|
-spec get_config_ca_url() -> url().
|
||||||
get_config_ca_url() ->
|
get_config_ca_url() ->
|
||||||
Acme = get_config_acme(),
|
Acme = ejabberd_option:acme(),
|
||||||
case lists:keyfind(ca_url, 1, Acme) of
|
try maps:get(ca_url, Acme)
|
||||||
{ca_url, CAUrl} ->
|
catch _:{badkey, _} ->
|
||||||
CAUrl;
|
|
||||||
false ->
|
|
||||||
?ERROR_MSG("No CA url has been specified in configuration", []),
|
?ERROR_MSG("No CA url has been specified in configuration", []),
|
||||||
?DEFAULT_CONFIG_CA_URL
|
?DEFAULT_CONFIG_CA_URL
|
||||||
%% throw({error, configuration_ca_url})
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec get_config_hosts() -> [binary()].
|
||||||
-spec get_config_hosts() -> [bitstring()].
|
|
||||||
get_config_hosts() ->
|
get_config_hosts() ->
|
||||||
case ejabberd_config:get_option(hosts, undefined) of
|
ejabberd_option:hosts().
|
||||||
undefined ->
|
|
||||||
?ERROR_MSG("No hosts have been specified in configuration", []),
|
|
||||||
throw({error, configuration_hosts});
|
|
||||||
Hosts ->
|
|
||||||
Hosts
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec acme_certs_dir() -> file:filename().
|
-spec acme_certs_dir() -> file:filename().
|
||||||
acme_certs_dir() ->
|
acme_certs_dir() ->
|
||||||
@ -1210,23 +1151,3 @@ acme_certs_dir() ->
|
|||||||
|
|
||||||
generate_key() ->
|
generate_key() ->
|
||||||
jose_jwk:generate_key({ec, secp256r1}).
|
jose_jwk:generate_key({ec, secp256r1}).
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
%%
|
|
||||||
%% Option Parsing Code
|
|
||||||
%%
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(acme) ->
|
|
||||||
fun(L) ->
|
|
||||||
lists:map(
|
|
||||||
fun({ca_url, URL}) ->
|
|
||||||
{ca_url, misc:try_url(URL)};
|
|
||||||
({contact, Contact}) ->
|
|
||||||
[<<_, _/binary>>, <<_, _/binary>>] =
|
|
||||||
binary:split(Contact, <<":">>),
|
|
||||||
{contact, Contact}
|
|
||||||
end, L)
|
|
||||||
end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[acme].
|
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
stop_kindly/2, send_service_message_all_mucs/2,
|
stop_kindly/2, send_service_message_all_mucs/2,
|
||||||
registered_vhosts/0,
|
registered_vhosts/0,
|
||||||
reload_config/0,
|
reload_config/0,
|
||||||
|
dump_config/1,
|
||||||
|
convert_to_yaml/2,
|
||||||
%% Cluster
|
%% Cluster
|
||||||
join_cluster/1, leave_cluster/1, list_cluster/0,
|
join_cluster/1, leave_cluster/1, list_cluster/0,
|
||||||
%% Erlang
|
%% Erlang
|
||||||
@ -152,7 +154,7 @@ get_commands_spec() ->
|
|||||||
result_desc = "The type of logger module used",
|
result_desc = "The type of logger module used",
|
||||||
result_example = lager,
|
result_example = lager,
|
||||||
args = [{loglevel, integer}],
|
args = [{loglevel, integer}],
|
||||||
result = {logger, atom}},
|
result = {res, rescode}},
|
||||||
|
|
||||||
#ejabberd_commands{name = update_list, tags = [server],
|
#ejabberd_commands{name = update_list, tags = [server],
|
||||||
desc = "List modified modules that can be updated",
|
desc = "List modified modules that can be updated",
|
||||||
@ -285,11 +287,18 @@ get_commands_spec() ->
|
|||||||
|
|
||||||
#ejabberd_commands{name = convert_to_yaml, tags = [config],
|
#ejabberd_commands{name = convert_to_yaml, tags = [config],
|
||||||
desc = "Convert the input file from Erlang to YAML format",
|
desc = "Convert the input file from Erlang to YAML format",
|
||||||
module = ejabberd_config, function = convert_to_yaml,
|
module = ?MODULE, function = convert_to_yaml,
|
||||||
args_desc = ["Full path to the original configuration file", "And full path to final file"],
|
args_desc = ["Full path to the original configuration file", "And full path to final file"],
|
||||||
args_example = ["/etc/ejabberd/ejabberd.cfg", "/etc/ejabberd/ejabberd.yml"],
|
args_example = ["/etc/ejabberd/ejabberd.cfg", "/etc/ejabberd/ejabberd.yml"],
|
||||||
args = [{in, string}, {out, string}],
|
args = [{in, string}, {out, string}],
|
||||||
result = {res, rescode}},
|
result = {res, rescode}},
|
||||||
|
#ejabberd_commands{name = dump_config, tags = [config],
|
||||||
|
desc = "Dump configuration in YAML format as seen by ejabberd",
|
||||||
|
module = ?MODULE, function = dump_config,
|
||||||
|
args_desc = ["Full path to output file"],
|
||||||
|
args_example = ["/tmp/ejabberd.yml"],
|
||||||
|
args = [{out, string}],
|
||||||
|
result = {res, rescode}},
|
||||||
|
|
||||||
#ejabberd_commands{name = delete_expired_messages, tags = [purge],
|
#ejabberd_commands{name = delete_expired_messages, tags = [purge],
|
||||||
desc = "Delete expired offline messages from database",
|
desc = "Delete expired offline messages from database",
|
||||||
@ -407,9 +416,7 @@ rotate_log() ->
|
|||||||
ejabberd_logger:rotate_log().
|
ejabberd_logger:rotate_log().
|
||||||
|
|
||||||
set_loglevel(LogLevel) ->
|
set_loglevel(LogLevel) ->
|
||||||
{module, Module} = ejabberd_logger:set(LogLevel),
|
ejabberd_logger:set(LogLevel).
|
||||||
Module.
|
|
||||||
|
|
||||||
|
|
||||||
%%%
|
%%%
|
||||||
%%% Stop Kindly
|
%%% Stop Kindly
|
||||||
@ -454,11 +461,13 @@ send_service_message_all_mucs(Subject, AnnouncementText) ->
|
|||||||
Message = str:format("~s~n~s", [Subject, AnnouncementText]),
|
Message = str:format("~s~n~s", [Subject, AnnouncementText]),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(ServerHost) ->
|
fun(ServerHost) ->
|
||||||
MUCHost = gen_mod:get_module_opt_host(
|
MUCHosts = gen_mod:get_module_opt_hosts(ServerHost, mod_muc),
|
||||||
ServerHost, mod_muc, <<"conference.@HOST@">>),
|
lists:foreach(
|
||||||
|
fun(MUCHost) ->
|
||||||
mod_muc:broadcast_service_message(ServerHost, MUCHost, Message)
|
mod_muc:broadcast_service_message(ServerHost, MUCHost, Message)
|
||||||
|
end, MUCHosts)
|
||||||
end,
|
end,
|
||||||
ejabberd_config:get_myhosts()).
|
ejabberd_option:hosts()).
|
||||||
|
|
||||||
%%%
|
%%%
|
||||||
%%% ejabberd_update
|
%%% ejabberd_update
|
||||||
@ -512,10 +521,31 @@ registered_users(Host) ->
|
|||||||
lists:map(fun({U, _S}) -> U end, SUsers).
|
lists:map(fun({U, _S}) -> U end, SUsers).
|
||||||
|
|
||||||
registered_vhosts() ->
|
registered_vhosts() ->
|
||||||
ejabberd_config:get_myhosts().
|
ejabberd_option:hosts().
|
||||||
|
|
||||||
reload_config() ->
|
reload_config() ->
|
||||||
ejabberd_config:reload_file().
|
case ejabberd_config:reload() of
|
||||||
|
ok -> {ok, ""};
|
||||||
|
Err ->
|
||||||
|
Reason = ejabberd_config:format_error(Err),
|
||||||
|
{invalid_config, Reason}
|
||||||
|
end.
|
||||||
|
|
||||||
|
dump_config(Path) ->
|
||||||
|
case ejabberd_config:dump(Path) of
|
||||||
|
ok -> {ok, ""};
|
||||||
|
Err ->
|
||||||
|
Reason = ejabberd_config:format_error(Err),
|
||||||
|
{invalid_file, Reason}
|
||||||
|
end.
|
||||||
|
|
||||||
|
convert_to_yaml(In, Out) ->
|
||||||
|
case ejabberd_config:convert_to_yaml(In, Out) of
|
||||||
|
ok -> {ok, ""};
|
||||||
|
Err ->
|
||||||
|
Reason = ejabberd_config:format_error(Err),
|
||||||
|
{invalid_config, Reason}
|
||||||
|
end.
|
||||||
|
|
||||||
%%%
|
%%%
|
||||||
%%% Cluster management
|
%%% Cluster management
|
||||||
@ -562,13 +592,13 @@ delete_expired_messages() ->
|
|||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(Host) ->
|
fun(Host) ->
|
||||||
{atomic, ok} = mod_offline:remove_expired_messages(Host)
|
{atomic, ok} = mod_offline:remove_expired_messages(Host)
|
||||||
end, ejabberd_config:get_myhosts()).
|
end, ejabberd_option:hosts()).
|
||||||
|
|
||||||
delete_old_messages(Days) ->
|
delete_old_messages(Days) ->
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(Host) ->
|
fun(Host) ->
|
||||||
{atomic, _} = mod_offline:remove_old_messages(Days, Host)
|
{atomic, _} = mod_offline:remove_old_messages(Days, Host)
|
||||||
end, ejabberd_config:get_myhosts()).
|
end, ejabberd_option:hosts()).
|
||||||
|
|
||||||
%%%
|
%%%
|
||||||
%%% Mnesia management
|
%%% Mnesia management
|
||||||
@ -602,10 +632,6 @@ restore_mnesia(Path) ->
|
|||||||
case ejabberd_admin:restore(Path) of
|
case ejabberd_admin:restore(Path) of
|
||||||
{atomic, _} ->
|
{atomic, _} ->
|
||||||
{ok, ""};
|
{ok, ""};
|
||||||
{error, Reason} ->
|
|
||||||
String = io_lib:format("Can't restore backup from ~p at node ~p: ~p",
|
|
||||||
[filename:absname(Path), node(), Reason]),
|
|
||||||
{cannot_restore, String};
|
|
||||||
{aborted,{no_exists,Table}} ->
|
{aborted,{no_exists,Table}} ->
|
||||||
String = io_lib:format("Can't restore backup from ~p at node ~p: Table ~p does not exist.",
|
String = io_lib:format("Can't restore backup from ~p at node ~p: Table ~p does not exist.",
|
||||||
[filename:absname(Path), node(), Table]),
|
[filename:absname(Path), node(), Table]),
|
||||||
|
@ -32,20 +32,21 @@
|
|||||||
-export([start/2, prep_stop/1, stop/1]).
|
-export([start/2, prep_stop/1, stop/1]).
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
-include("ejabberd_stacktrace.hrl").
|
||||||
|
|
||||||
%%%
|
%%%
|
||||||
%%% Application API
|
%%% Application API
|
||||||
%%%
|
%%%
|
||||||
|
|
||||||
start(normal, _Args) ->
|
start(normal, _Args) ->
|
||||||
|
try
|
||||||
{T1, _} = statistics(wall_clock),
|
{T1, _} = statistics(wall_clock),
|
||||||
ejabberd_logger:start(),
|
ejabberd_logger:start(),
|
||||||
write_pid_file(),
|
write_pid_file(),
|
||||||
start_included_apps(),
|
start_included_apps(),
|
||||||
start_elixir_application(),
|
start_elixir_application(),
|
||||||
ejabberd:check_app(ejabberd),
|
|
||||||
setup_if_elixir_conf_used(),
|
setup_if_elixir_conf_used(),
|
||||||
case ejabberd_config:start() of
|
case ejabberd_config:load() of
|
||||||
ok ->
|
ok ->
|
||||||
ejabberd_mnesia:start(),
|
ejabberd_mnesia:start(),
|
||||||
file_queue_init(),
|
file_queue_init(),
|
||||||
@ -56,18 +57,23 @@ start(normal, _Args) ->
|
|||||||
register_elixir_config_hooks(),
|
register_elixir_config_hooks(),
|
||||||
ejabberd_cluster:wait_for_sync(infinity),
|
ejabberd_cluster:wait_for_sync(infinity),
|
||||||
ejabberd_hooks:run(ejabberd_started, []),
|
ejabberd_hooks:run(ejabberd_started, []),
|
||||||
|
ejabberd:check_apps(),
|
||||||
{T2, _} = statistics(wall_clock),
|
{T2, _} = statistics(wall_clock),
|
||||||
?INFO_MSG("ejabberd ~s is started in the node ~p in ~.2fs",
|
?INFO_MSG("ejabberd ~s is started in the node ~p in ~.2fs",
|
||||||
[ejabberd_config:get_version(),
|
[ejabberd_option:version(),
|
||||||
node(), (T2-T1)/1000]),
|
node(), (T2-T1)/1000]),
|
||||||
lists:foreach(fun erlang:garbage_collect/1, processes()),
|
|
||||||
{ok, SupPid};
|
{ok, SupPid};
|
||||||
Err ->
|
Err ->
|
||||||
?CRITICAL_MSG("Failed to start ejabberd application: ~p", [Err]),
|
?CRITICAL_MSG("Failed to start ejabberd application: ~p", [Err]),
|
||||||
ejabberd:halt()
|
ejabberd:halt()
|
||||||
end;
|
end;
|
||||||
{error, Reason} ->
|
Err ->
|
||||||
?CRITICAL_MSG("Failed to start ejabberd application: ~p", [Reason]),
|
?CRITICAL_MSG("Failed to start ejabberd application: ~s",
|
||||||
|
[ejabberd_config:format_error(Err)]),
|
||||||
|
ejabberd:halt()
|
||||||
|
end
|
||||||
|
catch throw:{?MODULE, Error} ->
|
||||||
|
?DEBUG("Failed to start ejabberd application: ~p", [Error]),
|
||||||
ejabberd:halt()
|
ejabberd:halt()
|
||||||
end;
|
end;
|
||||||
start(_, _) ->
|
start(_, _) ->
|
||||||
@ -92,18 +98,15 @@ start_included_apps() ->
|
|||||||
prep_stop(State) ->
|
prep_stop(State) ->
|
||||||
ejabberd_hooks:run(ejabberd_stopping, []),
|
ejabberd_hooks:run(ejabberd_stopping, []),
|
||||||
ejabberd_listener:stop_listeners(),
|
ejabberd_listener:stop_listeners(),
|
||||||
ejabberd_sm:stop(),
|
_ = ejabberd_sm:stop(),
|
||||||
gen_mod:stop_modules(),
|
gen_mod:stop_modules(),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
%% All the processes were killed when this function is called
|
%% All the processes were killed when this function is called
|
||||||
stop(_State) ->
|
stop(_State) ->
|
||||||
?INFO_MSG("ejabberd ~s is stopped in the node ~p",
|
?INFO_MSG("ejabberd ~s is stopped in the node ~p",
|
||||||
[ejabberd_config:get_version(), node()]),
|
[ejabberd_option:version(), node()]),
|
||||||
delete_pid_file(),
|
delete_pid_file().
|
||||||
%%ejabberd_debug:stop(),
|
|
||||||
ok.
|
|
||||||
|
|
||||||
|
|
||||||
%%%
|
%%%
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
@ -134,13 +137,13 @@ write_pid_file() ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
write_pid_file(Pid, PidFilename) ->
|
write_pid_file(Pid, PidFilename) ->
|
||||||
case file:open(PidFilename, [write]) of
|
case file:write_file(PidFilename, io_lib:format("~s~n", [Pid])) of
|
||||||
{ok, Fd} ->
|
ok ->
|
||||||
io:format(Fd, "~s~n", [Pid]),
|
ok;
|
||||||
file:close(Fd);
|
{error, Reason} = Err ->
|
||||||
{error, Reason} ->
|
?CRITICAL_MSG("Cannot write PID file ~s: ~s",
|
||||||
?ERROR_MSG("Cannot write PID file ~s~nReason: ~p", [PidFilename, Reason]),
|
[PidFilename, file:format_error(Reason)]),
|
||||||
throw({cannot_write_pid_file, PidFilename, Reason})
|
throw({?MODULE, Err})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
delete_pid_file() ->
|
delete_pid_file() ->
|
||||||
@ -152,34 +155,42 @@ delete_pid_file() ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
file_queue_init() ->
|
file_queue_init() ->
|
||||||
QueueDir = case ejabberd_config:queue_dir() of
|
QueueDir = case ejabberd_option:queue_dir() of
|
||||||
undefined ->
|
undefined ->
|
||||||
MnesiaDir = mnesia:system_info(directory),
|
MnesiaDir = mnesia:system_info(directory),
|
||||||
filename:join(MnesiaDir, "queue");
|
filename:join(MnesiaDir, "queue");
|
||||||
Path ->
|
Path ->
|
||||||
Path
|
Path
|
||||||
end,
|
end,
|
||||||
p1_queue:start(QueueDir).
|
case p1_queue:start(QueueDir) of
|
||||||
|
ok -> ok;
|
||||||
|
Err -> throw({?MODULE, Err})
|
||||||
|
end.
|
||||||
|
|
||||||
|
-ifdef(ELIXIR_ENABLED).
|
||||||
|
is_using_elixir_config() ->
|
||||||
|
Config = ejabberd_config:path(),
|
||||||
|
'Elixir.Ejabberd.ConfigUtil':is_elixir_config(Config).
|
||||||
|
|
||||||
setup_if_elixir_conf_used() ->
|
setup_if_elixir_conf_used() ->
|
||||||
case ejabberd_config:is_using_elixir_config() of
|
case is_using_elixir_config() of
|
||||||
true -> 'Elixir.Ejabberd.Config.Store':start_link();
|
true -> 'Elixir.Ejabberd.Config.Store':start_link();
|
||||||
false -> ok
|
false -> ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
register_elixir_config_hooks() ->
|
register_elixir_config_hooks() ->
|
||||||
case ejabberd_config:is_using_elixir_config() of
|
case is_using_elixir_config() of
|
||||||
true -> 'Elixir.Ejabberd.Config':start_hooks();
|
true -> 'Elixir.Ejabberd.Config':start_hooks();
|
||||||
false -> ok
|
false -> ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
start_elixir_application() ->
|
start_elixir_application() ->
|
||||||
case ejabberd_config:is_elixir_enabled() of
|
|
||||||
true ->
|
|
||||||
case application:ensure_started(elixir) of
|
case application:ensure_started(elixir) of
|
||||||
ok -> ok;
|
ok -> ok;
|
||||||
{error, _Msg} -> ?ERROR_MSG("Elixir application not started.", [])
|
{error, _Msg} -> ?ERROR_MSG("Elixir application not started.", [])
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
ok
|
|
||||||
end.
|
end.
|
||||||
|
-else.
|
||||||
|
setup_if_elixir_conf_used() -> ok.
|
||||||
|
register_elixir_config_hooks() -> ok.
|
||||||
|
start_elixir_application() -> ok.
|
||||||
|
-endif.
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
-module(ejabberd_auth).
|
-module(ejabberd_auth).
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
@ -47,7 +46,7 @@
|
|||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
terminate/2, code_change/3]).
|
terminate/2, code_change/3]).
|
||||||
|
|
||||||
-export([auth_modules/1, opt_type/1]).
|
-export([auth_modules/1]).
|
||||||
|
|
||||||
-include("scram.hrl").
|
-include("scram.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
@ -107,7 +106,7 @@ init([]) ->
|
|||||||
fun(Host, Acc) ->
|
fun(Host, Acc) ->
|
||||||
Modules = auth_modules(Host),
|
Modules = auth_modules(Host),
|
||||||
maps:put(Host, Modules, Acc)
|
maps:put(Host, Modules, Acc)
|
||||||
end, #{}, ejabberd_config:get_myhosts()),
|
end, #{}, ejabberd_option:hosts()),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun({Host, Modules}) ->
|
fun({Host, Modules}) ->
|
||||||
start(Host, Modules)
|
start(Host, Modules)
|
||||||
@ -141,7 +140,7 @@ handle_cast(config_reloaded, #state{host_modules = HostModules} = State) ->
|
|||||||
stop(Host, OldModules -- NewModules),
|
stop(Host, OldModules -- NewModules),
|
||||||
reload(Host, misc:intersection(OldModules, NewModules)),
|
reload(Host, misc:intersection(OldModules, NewModules)),
|
||||||
maps:put(Host, NewModules, Acc)
|
maps:put(Host, NewModules, Acc)
|
||||||
end, HostModules, ejabberd_config:get_myhosts()),
|
end, HostModules, ejabberd_option:hosts()),
|
||||||
init_cache(NewHostModules),
|
init_cache(NewHostModules),
|
||||||
{noreply, State#state{host_modules = NewHostModules}};
|
{noreply, State#state{host_modules = NewHostModules}};
|
||||||
handle_cast(Msg, State) ->
|
handle_cast(Msg, State) ->
|
||||||
@ -530,7 +529,7 @@ backend_type(Mod) ->
|
|||||||
|
|
||||||
-spec password_format(binary() | global) -> plain | scram.
|
-spec password_format(binary() | global) -> plain | scram.
|
||||||
password_format(LServer) ->
|
password_format(LServer) ->
|
||||||
ejabberd_config:get_option({auth_password_format, LServer}, plain).
|
ejabberd_option:auth_password_format(LServer).
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% Backend calls
|
%%% Backend calls
|
||||||
@ -767,15 +766,9 @@ init_cache(HostModules) ->
|
|||||||
|
|
||||||
-spec cache_opts() -> [proplists:property()].
|
-spec cache_opts() -> [proplists:property()].
|
||||||
cache_opts() ->
|
cache_opts() ->
|
||||||
MaxSize = ejabberd_config:get_option(
|
MaxSize = ejabberd_option:auth_cache_size(),
|
||||||
auth_cache_size,
|
CacheMissed = ejabberd_option:auth_cache_missed(),
|
||||||
ejabberd_config:cache_size(global)),
|
LifeTime = case ejabberd_option:auth_cache_life_time() of
|
||||||
CacheMissed = ejabberd_config:get_option(
|
|
||||||
auth_cache_missed,
|
|
||||||
ejabberd_config:cache_missed(global)),
|
|
||||||
LifeTime = case ejabberd_config:get_option(
|
|
||||||
auth_cache_life_time,
|
|
||||||
ejabberd_config:cache_life_time(global)) of
|
|
||||||
infinity -> infinity;
|
infinity -> infinity;
|
||||||
I -> timer:seconds(I)
|
I -> timer:seconds(I)
|
||||||
end,
|
end,
|
||||||
@ -803,9 +796,7 @@ use_cache(Mod, LServer) ->
|
|||||||
case erlang:function_exported(Mod, use_cache, 1) of
|
case erlang:function_exported(Mod, use_cache, 1) of
|
||||||
true -> Mod:use_cache(LServer);
|
true -> Mod:use_cache(LServer);
|
||||||
false ->
|
false ->
|
||||||
ejabberd_config:get_option(
|
ejabberd_option:auth_use_cache(LServer)
|
||||||
{auth_use_cache, LServer},
|
|
||||||
ejabberd_config:use_cache(LServer))
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec cache_nodes(module(), binary()) -> [node()].
|
-spec cache_nodes(module(), binary()) -> [node()].
|
||||||
@ -827,13 +818,12 @@ auth_modules() ->
|
|||||||
lists:flatmap(
|
lists:flatmap(
|
||||||
fun(Host) ->
|
fun(Host) ->
|
||||||
[{Host, Mod} || Mod <- auth_modules(Host)]
|
[{Host, Mod} || Mod <- auth_modules(Host)]
|
||||||
end, ejabberd_config:get_myhosts()).
|
end, ejabberd_option:hosts()).
|
||||||
|
|
||||||
-spec auth_modules(binary()) -> [module()].
|
-spec auth_modules(binary()) -> [module()].
|
||||||
auth_modules(Server) ->
|
auth_modules(Server) ->
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
Default = ejabberd_config:default_db(LServer, ?MODULE),
|
Methods = ejabberd_option:auth_method(LServer),
|
||||||
Methods = ejabberd_config:get_option({auth_method, LServer}, [Default]),
|
|
||||||
[ejabberd:module_name([<<"ejabberd">>, <<"auth">>,
|
[ejabberd:module_name([<<"ejabberd">>, <<"auth">>,
|
||||||
misc:atom_to_binary(M)])
|
misc:atom_to_binary(M)])
|
||||||
|| M <- Methods].
|
|| M <- Methods].
|
||||||
@ -911,31 +901,3 @@ import(Server, {sql, _}, riak, <<"users">>, Fields) ->
|
|||||||
ejabberd_auth_riak:import(Server, Fields);
|
ejabberd_auth_riak:import(Server, Fields);
|
||||||
import(_LServer, {sql, _}, sql, <<"users">>, _) ->
|
import(_LServer, {sql, _}, sql, <<"users">>, _) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(auth_method) ->
|
|
||||||
fun (V) when is_list(V) ->
|
|
||||||
lists:map(fun(M) -> ejabberd_config:v_db(?MODULE, M) end, V);
|
|
||||||
(V) -> [ejabberd_config:v_db(?MODULE, V)]
|
|
||||||
end;
|
|
||||||
opt_type(auth_password_format) ->
|
|
||||||
fun (plain) -> plain;
|
|
||||||
(scram) -> scram
|
|
||||||
end;
|
|
||||||
opt_type(auth_use_cache) ->
|
|
||||||
fun(B) when is_boolean(B) -> B end;
|
|
||||||
opt_type(auth_cache_missed) ->
|
|
||||||
fun(B) when is_boolean(B) -> B end;
|
|
||||||
opt_type(auth_cache_life_time) ->
|
|
||||||
fun(I) when is_integer(I), I>0 -> I;
|
|
||||||
(unlimited) -> infinity;
|
|
||||||
(infinity) -> infinity
|
|
||||||
end;
|
|
||||||
opt_type(auth_cache_size) ->
|
|
||||||
fun(I) when is_integer(I), I>0 -> I;
|
|
||||||
(unlimited) -> infinity;
|
|
||||||
(infinity) -> infinity
|
|
||||||
end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[auth_method, auth_password_format, auth_use_cache,
|
|
||||||
auth_cache_missed, auth_cache_life_time, auth_cache_size].
|
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
-module(ejabberd_auth_anonymous).
|
-module(ejabberd_auth_anonymous).
|
||||||
|
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
-behaviour(ejabberd_auth).
|
-behaviour(ejabberd_auth).
|
||||||
-author('mickael.remond@process-one.net').
|
-author('mickael.remond@process-one.net').
|
||||||
|
|
||||||
@ -43,7 +42,7 @@
|
|||||||
|
|
||||||
-export([login/2, check_password/4, user_exists/2,
|
-export([login/2, check_password/4, user_exists/2,
|
||||||
get_users/2, count_users/2, store_type/1,
|
get_users/2, count_users/2, store_type/1,
|
||||||
plain_password_required/1, opt_type/1]).
|
plain_password_required/1]).
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("jid.hrl").
|
-include("jid.hrl").
|
||||||
@ -98,12 +97,12 @@ is_login_anonymous_enabled(Host) ->
|
|||||||
%% Return the anonymous protocol to use: sasl_anon|login_anon|both
|
%% Return the anonymous protocol to use: sasl_anon|login_anon|both
|
||||||
%% defaults to login_anon
|
%% defaults to login_anon
|
||||||
anonymous_protocol(Host) ->
|
anonymous_protocol(Host) ->
|
||||||
ejabberd_config:get_option({anonymous_protocol, Host}, sasl_anon).
|
ejabberd_option:anonymous_protocol(Host).
|
||||||
|
|
||||||
%% Return true if multiple connections have been allowed in the config file
|
%% Return true if multiple connections have been allowed in the config file
|
||||||
%% defaults to false
|
%% defaults to false
|
||||||
allow_multiple_connections(Host) ->
|
allow_multiple_connections(Host) ->
|
||||||
ejabberd_config:get_option({allow_multiple_connections, Host}, false).
|
ejabberd_option:allow_multiple_connections(Host).
|
||||||
|
|
||||||
anonymous_user_exist(User, Server) ->
|
anonymous_user_exist(User, Server) ->
|
||||||
lists:any(
|
lists:any(
|
||||||
@ -188,14 +187,3 @@ plain_password_required(_) ->
|
|||||||
|
|
||||||
store_type(_) ->
|
store_type(_) ->
|
||||||
external.
|
external.
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(allow_multiple_connections) ->
|
|
||||||
fun (V) when is_boolean(V) -> V end;
|
|
||||||
opt_type(anonymous_protocol) ->
|
|
||||||
fun (sasl_anon) -> sasl_anon;
|
|
||||||
(login_anon) -> login_anon;
|
|
||||||
(both) -> both
|
|
||||||
end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[allow_multiple_connections, anonymous_protocol].
|
|
||||||
|
@ -25,15 +25,13 @@
|
|||||||
|
|
||||||
-module(ejabberd_auth_external).
|
-module(ejabberd_auth_external).
|
||||||
|
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-behaviour(ejabberd_auth).
|
-behaviour(ejabberd_auth).
|
||||||
|
|
||||||
-export([start/1, stop/1, reload/1, set_password/3, check_password/4,
|
-export([start/1, stop/1, reload/1, set_password/3, check_password/4,
|
||||||
try_register/3, user_exists/2, remove_user/2,
|
try_register/3, user_exists/2, remove_user/2,
|
||||||
store_type/1, plain_password_required/1, opt_type/1]).
|
store_type/1, plain_password_required/1]).
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
@ -91,7 +89,7 @@ check_password_extauth(User, _AuthzId, Server, Password) ->
|
|||||||
case extauth:check_password(User, Server, Password) of
|
case extauth:check_password(User, Server, Password) of
|
||||||
Res when is_boolean(Res) -> Res;
|
Res when is_boolean(Res) -> Res;
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
failure(User, Server, check_password, Reason),
|
_ = failure(User, Server, check_password, Reason),
|
||||||
false
|
false
|
||||||
end;
|
end;
|
||||||
true ->
|
true ->
|
||||||
@ -103,26 +101,3 @@ failure(User, Server, Fun, Reason) ->
|
|||||||
?ERROR_MSG("External authentication program failed when calling "
|
?ERROR_MSG("External authentication program failed when calling "
|
||||||
"'~s' for ~s@~s: ~p", [Fun, User, Server, Reason]),
|
"'~s' for ~s@~s: ~p", [Fun, User, Server, Reason]),
|
||||||
{error, db_failure}.
|
{error, db_failure}.
|
||||||
|
|
||||||
opt_type(extauth_cache) ->
|
|
||||||
?WARNING_MSG("option 'extauth_cache' is deprecated and has no effect, "
|
|
||||||
"use authentication or global cache configuration "
|
|
||||||
"options: auth_use_cache, auth_cache_life_time, "
|
|
||||||
"use_cache, cache_life_time, and so on", []),
|
|
||||||
fun (false) -> false;
|
|
||||||
(I) when is_integer(I), I >= 0 -> I
|
|
||||||
end;
|
|
||||||
opt_type(extauth_instances) ->
|
|
||||||
?WARNING_MSG("option 'extauth_instances' is deprecated and has no effect, "
|
|
||||||
"use 'extauth_pool_size'", []),
|
|
||||||
fun (V) when is_integer(V), V > 0 -> V end;
|
|
||||||
opt_type(extauth_program) ->
|
|
||||||
fun (V) -> binary_to_list(iolist_to_binary(V)) end;
|
|
||||||
opt_type(extauth_pool_name) ->
|
|
||||||
fun (V) -> iolist_to_binary(V) end;
|
|
||||||
opt_type(extauth_pool_size) ->
|
|
||||||
fun(I) when is_integer(I), I>0 -> I end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[extauth_program, extauth_pool_size, extauth_pool_name,
|
|
||||||
%% Deprecated:
|
|
||||||
extauth_cache, extauth_instances].
|
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
|
|
||||||
-module(ejabberd_auth_ldap).
|
-module(ejabberd_auth_ldap).
|
||||||
|
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
@ -39,8 +37,7 @@
|
|||||||
-export([start/1, stop/1, start_link/1, set_password/3,
|
-export([start/1, stop/1, start_link/1, set_password/3,
|
||||||
check_password/4, user_exists/2,
|
check_password/4, user_exists/2,
|
||||||
get_users/2, count_users/2,
|
get_users/2, count_users/2,
|
||||||
store_type/1, plain_password_required/1,
|
store_type/1, plain_password_required/1]).
|
||||||
opt_type/1]).
|
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
@ -60,7 +57,6 @@
|
|||||||
uids = [] :: [{binary()} | {binary(), binary()}],
|
uids = [] :: [{binary()} | {binary(), binary()}],
|
||||||
ufilter = <<"">> :: binary(),
|
ufilter = <<"">> :: binary(),
|
||||||
sfilter = <<"">> :: binary(),
|
sfilter = <<"">> :: binary(),
|
||||||
lfilter :: {any(), any()} | undefined,
|
|
||||||
deref_aliases = never :: never | searching | finding | always,
|
deref_aliases = never :: never | searching | finding | always,
|
||||||
dn_filter :: binary() | undefined,
|
dn_filter :: binary() | undefined,
|
||||||
dn_filter_attrs = [] :: [binary()]}).
|
dn_filter_attrs = [] :: [binary()]}).
|
||||||
@ -85,8 +81,10 @@ start(Host) ->
|
|||||||
|
|
||||||
stop(Host) ->
|
stop(Host) ->
|
||||||
Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
||||||
supervisor:terminate_child(ejabberd_backend_sup, Proc),
|
case supervisor:terminate_child(ejabberd_backend_sup, Proc) of
|
||||||
supervisor:delete_child(ejabberd_backend_sup, Proc).
|
ok -> supervisor:delete_child(ejabberd_backend_sup, Proc);
|
||||||
|
Err -> Err
|
||||||
|
end.
|
||||||
|
|
||||||
start_link(Host) ->
|
start_link(Host) ->
|
||||||
Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
||||||
@ -246,19 +244,12 @@ find_user_dn(User, State) ->
|
|||||||
[#eldap_entry{attributes = Attrs,
|
[#eldap_entry{attributes = Attrs,
|
||||||
object_name = DN}
|
object_name = DN}
|
||||||
| _]} ->
|
| _]} ->
|
||||||
dn_filter(DN, Attrs, State);
|
is_valid_dn(DN, Attrs, State);
|
||||||
_ -> false
|
_ -> false
|
||||||
end;
|
end;
|
||||||
_ -> false
|
_ -> false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% apply the dn filter and the local filter:
|
|
||||||
dn_filter(DN, Attrs, State) ->
|
|
||||||
case check_local_filter(Attrs, State) of
|
|
||||||
false -> false;
|
|
||||||
true -> is_valid_dn(DN, Attrs, State)
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% Check that the DN is valid, based on the dn filter
|
%% Check that the DN is valid, based on the dn filter
|
||||||
is_valid_dn(DN, _, #state{dn_filter = undefined}) -> DN;
|
is_valid_dn(DN, _, #state{dn_filter = undefined}) -> DN;
|
||||||
is_valid_dn(DN, Attrs, State) ->
|
is_valid_dn(DN, Attrs, State) ->
|
||||||
@ -294,30 +285,6 @@ is_valid_dn(DN, Attrs, State) ->
|
|||||||
_ -> false
|
_ -> false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% The local filter is used to check an attribute in ejabberd
|
|
||||||
%% and not in LDAP to limit the load on the LDAP directory.
|
|
||||||
%% A local rule can be either:
|
|
||||||
%% {equal, {"accountStatus",["active"]}}
|
|
||||||
%% {notequal, {"accountStatus",["disabled"]}}
|
|
||||||
%% {ldap_local_filter, {notequal, {"accountStatus",["disabled"]}}}
|
|
||||||
check_local_filter(_Attrs,
|
|
||||||
#state{lfilter = undefined}) ->
|
|
||||||
true;
|
|
||||||
check_local_filter(Attrs,
|
|
||||||
#state{lfilter = LocalFilter}) ->
|
|
||||||
{Operation, FilterMatch} = LocalFilter,
|
|
||||||
local_filter(Operation, Attrs, FilterMatch).
|
|
||||||
|
|
||||||
local_filter(equal, Attrs, FilterMatch) ->
|
|
||||||
{Attr, Value} = FilterMatch,
|
|
||||||
case lists:keysearch(Attr, 1, Attrs) of
|
|
||||||
false -> false;
|
|
||||||
{value, {Attr, Value}} -> true;
|
|
||||||
_ -> false
|
|
||||||
end;
|
|
||||||
local_filter(notequal, Attrs, FilterMatch) ->
|
|
||||||
not local_filter(equal, Attrs, FilterMatch).
|
|
||||||
|
|
||||||
result_attrs(#state{uids = UIDs,
|
result_attrs(#state{uids = UIDs,
|
||||||
dn_filter_attrs = DNFilterAttrs}) ->
|
dn_filter_attrs = DNFilterAttrs}) ->
|
||||||
lists:foldl(fun ({UID}, Acc) -> [UID | Acc];
|
lists:foldl(fun ({UID}, Acc) -> [UID | Acc];
|
||||||
@ -329,25 +296,21 @@ result_attrs(#state{uids = UIDs,
|
|||||||
%%% Auxiliary functions
|
%%% Auxiliary functions
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
parse_options(Host) ->
|
parse_options(Host) ->
|
||||||
Cfg = eldap_utils:get_config(Host, []),
|
Cfg = ?eldap_config(ejabberd_option, Host),
|
||||||
Eldap_ID = misc:atom_to_binary(gen_mod:get_module_proc(Host, ?MODULE)),
|
Eldap_ID = misc:atom_to_binary(gen_mod:get_module_proc(Host, ?MODULE)),
|
||||||
Bind_Eldap_ID = misc:atom_to_binary(
|
Bind_Eldap_ID = misc:atom_to_binary(
|
||||||
gen_mod:get_module_proc(Host, bind_ejabberd_auth_ldap)),
|
gen_mod:get_module_proc(Host, bind_ejabberd_auth_ldap)),
|
||||||
UIDsTemp = ejabberd_config:get_option(
|
UIDsTemp = ejabberd_option:ldap_uids(Host),
|
||||||
{ldap_uids, Host}, [{<<"uid">>, <<"%u">>}]),
|
|
||||||
UIDs = eldap_utils:uids_domain_subst(Host, UIDsTemp),
|
UIDs = eldap_utils:uids_domain_subst(Host, UIDsTemp),
|
||||||
SubFilter = eldap_utils:generate_subfilter(UIDs),
|
SubFilter = eldap_utils:generate_subfilter(UIDs),
|
||||||
UserFilter = case ejabberd_config:get_option({ldap_filter, Host}, <<"">>) of
|
UserFilter = case ejabberd_option:ldap_filter(Host) of
|
||||||
<<"">> ->
|
<<"">> ->
|
||||||
SubFilter;
|
SubFilter;
|
||||||
F ->
|
F ->
|
||||||
<<"(&", SubFilter/binary, F/binary, ")">>
|
<<"(&", SubFilter/binary, F/binary, ")">>
|
||||||
end,
|
end,
|
||||||
SearchFilter = eldap_filter:do_sub(UserFilter,
|
SearchFilter = eldap_filter:do_sub(UserFilter, [{<<"%u">>, <<"*">>}]),
|
||||||
[{<<"%u">>, <<"*">>}]),
|
{DNFilter, DNFilterAttrs} = ejabberd_option:ldap_dn_filter(Host),
|
||||||
{DNFilter, DNFilterAttrs} =
|
|
||||||
ejabberd_config:get_option({ldap_dn_filter, Host}, {undefined, []}),
|
|
||||||
LocalFilter = ejabberd_config:get_option({ldap_local_filter, Host}),
|
|
||||||
#state{host = Host, eldap_id = Eldap_ID,
|
#state{host = Host, eldap_id = Eldap_ID,
|
||||||
bind_eldap_id = Bind_Eldap_ID,
|
bind_eldap_id = Bind_Eldap_ID,
|
||||||
servers = Cfg#eldap_config.servers,
|
servers = Cfg#eldap_config.servers,
|
||||||
@ -359,19 +322,5 @@ parse_options(Host) ->
|
|||||||
base = Cfg#eldap_config.base,
|
base = Cfg#eldap_config.base,
|
||||||
deref_aliases = Cfg#eldap_config.deref_aliases,
|
deref_aliases = Cfg#eldap_config.deref_aliases,
|
||||||
uids = UIDs, ufilter = UserFilter,
|
uids = UIDs, ufilter = UserFilter,
|
||||||
sfilter = SearchFilter, lfilter = LocalFilter,
|
sfilter = SearchFilter,
|
||||||
dn_filter = DNFilter, dn_filter_attrs = DNFilterAttrs}.
|
dn_filter = DNFilter, dn_filter_attrs = DNFilterAttrs}.
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(ldap_dn_filter) ->
|
|
||||||
fun ([{DNF, DNFA}]) ->
|
|
||||||
NewDNFA = case DNFA of
|
|
||||||
undefined -> [];
|
|
||||||
_ -> [iolist_to_binary(A) || A <- DNFA]
|
|
||||||
end,
|
|
||||||
NewDNF = eldap_utils:check_filter(DNF),
|
|
||||||
{NewDNF, NewDNFA}
|
|
||||||
end;
|
|
||||||
opt_type(ldap_local_filter) -> fun (V) -> V end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[ldap_dn_filter, ldap_local_filter].
|
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
|
|
||||||
-module(ejabberd_auth_mnesia).
|
-module(ejabberd_auth_mnesia).
|
||||||
|
|
||||||
-compile([{parse_transform, ejabberd_sql_pt}]).
|
|
||||||
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-behaviour(ejabberd_auth).
|
-behaviour(ejabberd_auth).
|
||||||
@ -77,9 +75,7 @@ update_reg_users_counter_table(Server) ->
|
|||||||
use_cache(Host) ->
|
use_cache(Host) ->
|
||||||
case mnesia:table_info(passwd, storage_type) of
|
case mnesia:table_info(passwd, storage_type) of
|
||||||
disc_only_copies ->
|
disc_only_copies ->
|
||||||
ejabberd_config:get_option(
|
ejabberd_option:auth_use_cache(Host);
|
||||||
{auth_use_cache, Host},
|
|
||||||
ejabberd_config:use_cache(Host));
|
|
||||||
_ ->
|
_ ->
|
||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
@ -207,7 +203,7 @@ remove_user(User, Server) ->
|
|||||||
|
|
||||||
need_transform(#reg_users_counter{}) ->
|
need_transform(#reg_users_counter{}) ->
|
||||||
false;
|
false;
|
||||||
need_transform(#passwd{us = {U, S}, password = Pass}) ->
|
need_transform({passwd, {U, S}, Pass}) ->
|
||||||
if is_binary(Pass) ->
|
if is_binary(Pass) ->
|
||||||
case store_type(S) of
|
case store_type(S) of
|
||||||
scram ->
|
scram ->
|
||||||
@ -234,7 +230,7 @@ need_transform(#passwd{us = {U, S}, password = Pass}) ->
|
|||||||
true
|
true
|
||||||
end.
|
end.
|
||||||
|
|
||||||
transform(#passwd{us = {U, S}, password = Pass} = R)
|
transform({passwd, {U, S}, Pass})
|
||||||
when is_list(U) orelse is_list(S) orelse is_list(Pass) ->
|
when is_list(U) orelse is_list(S) orelse is_list(Pass) ->
|
||||||
NewUS = {iolist_to_binary(U), iolist_to_binary(S)},
|
NewUS = {iolist_to_binary(U), iolist_to_binary(S)},
|
||||||
NewPass = case Pass of
|
NewPass = case Pass of
|
||||||
@ -248,7 +244,7 @@ transform(#passwd{us = {U, S}, password = Pass} = R)
|
|||||||
_ ->
|
_ ->
|
||||||
iolist_to_binary(Pass)
|
iolist_to_binary(Pass)
|
||||||
end,
|
end,
|
||||||
transform(R#passwd{us = NewUS, password = NewPass});
|
transform(#passwd{us = NewUS, password = NewPass});
|
||||||
transform(#passwd{us = {U, S}, password = Password} = P)
|
transform(#passwd{us = {U, S}, password = Password} = P)
|
||||||
when is_binary(Password) ->
|
when is_binary(Password) ->
|
||||||
case store_type(S) of
|
case store_type(S) of
|
||||||
|
@ -24,15 +24,12 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(ejabberd_auth_pam).
|
-module(ejabberd_auth_pam).
|
||||||
|
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-author('xram@jabber.ru').
|
-author('xram@jabber.ru').
|
||||||
|
|
||||||
-behaviour(ejabberd_auth).
|
-behaviour(ejabberd_auth).
|
||||||
|
|
||||||
-export([start/1, stop/1, check_password/4,
|
-export([start/1, stop/1, check_password/4,
|
||||||
user_exists/2, store_type/1, plain_password_required/1,
|
user_exists/2, store_type/1, plain_password_required/1]).
|
||||||
opt_type/1]).
|
|
||||||
|
|
||||||
start(_Host) ->
|
start(_Host) ->
|
||||||
ejabberd:start_app(epam).
|
ejabberd:start_app(epam).
|
||||||
@ -77,15 +74,7 @@ store_type(_) -> external.
|
|||||||
%% Internal functions
|
%% Internal functions
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
get_pam_service(Host) ->
|
get_pam_service(Host) ->
|
||||||
ejabberd_config:get_option({pam_service, Host}, <<"ejabberd">>).
|
ejabberd_option:pam_service(Host).
|
||||||
|
|
||||||
get_pam_userinfotype(Host) ->
|
get_pam_userinfotype(Host) ->
|
||||||
ejabberd_config:get_option({pam_userinfotype, Host}, username).
|
ejabberd_option:pam_userinfotype(Host).
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(pam_service) -> fun iolist_to_binary/1;
|
|
||||||
opt_type(pam_userinfotype) ->
|
|
||||||
fun (username) -> username;
|
|
||||||
(jid) -> jid
|
|
||||||
end;
|
|
||||||
opt_type(_) -> [pam_service, pam_userinfotype].
|
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
|
|
||||||
-module(ejabberd_auth_riak).
|
-module(ejabberd_auth_riak).
|
||||||
|
|
||||||
-compile([{parse_transform, ejabberd_sql_pt}]).
|
|
||||||
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-behaviour(ejabberd_auth).
|
-behaviour(ejabberd_auth).
|
||||||
|
@ -25,17 +25,15 @@
|
|||||||
|
|
||||||
-module(ejabberd_auth_sql).
|
-module(ejabberd_auth_sql).
|
||||||
|
|
||||||
-compile([{parse_transform, ejabberd_sql_pt}]).
|
|
||||||
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-behaviour(ejabberd_auth).
|
-behaviour(ejabberd_auth).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-export([start/1, stop/1, set_password/3, try_register/3,
|
-export([start/1, stop/1, set_password/3, try_register/3,
|
||||||
get_users/2, count_users/2, get_password/2,
|
get_users/2, count_users/2, get_password/2,
|
||||||
remove_user/2, store_type/1, plain_password_required/1,
|
remove_user/2, store_type/1, plain_password_required/1,
|
||||||
convert_to_scram/1, opt_type/1, export/1, which_users_exists/2]).
|
convert_to_scram/1, export/1, which_users_exists/2]).
|
||||||
|
|
||||||
-include("scram.hrl").
|
-include("scram.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
@ -221,8 +219,7 @@ users_number(LServer) ->
|
|||||||
LServer,
|
LServer,
|
||||||
fun(pgsql, _) ->
|
fun(pgsql, _) ->
|
||||||
case
|
case
|
||||||
ejabberd_config:get_option(
|
ejabberd_option:pgsql_users_number_estimate(LServer) of
|
||||||
{pgsql_users_number_estimate, LServer}, false) of
|
|
||||||
true ->
|
true ->
|
||||||
ejabberd_sql:sql_query_t(
|
ejabberd_sql:sql_query_t(
|
||||||
?SQL("select @(reltuples :: bigint)d from pg_class"
|
?SQL("select @(reltuples :: bigint)d from pg_class"
|
||||||
@ -349,8 +346,3 @@ export(_Server) ->
|
|||||||
(_Host, _R) ->
|
(_Host, _R) ->
|
||||||
[]
|
[]
|
||||||
end}].
|
end}].
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(pgsql_users_number_estimate) ->
|
|
||||||
fun (V) when is_boolean(V) -> V end;
|
|
||||||
opt_type(_) -> [pgsql_users_number_estimate].
|
|
||||||
|
@ -91,8 +91,8 @@
|
|||||||
xmpp_ver = <<"">> :: binary(),
|
xmpp_ver = <<"">> :: binary(),
|
||||||
inactivity_timer :: reference() | undefined,
|
inactivity_timer :: reference() | undefined,
|
||||||
wait_timer :: reference() | undefined,
|
wait_timer :: reference() | undefined,
|
||||||
wait_timeout = ?DEFAULT_WAIT :: timeout(),
|
wait_timeout = ?DEFAULT_WAIT :: pos_integer(),
|
||||||
inactivity_timeout :: timeout(),
|
inactivity_timeout :: pos_integer(),
|
||||||
prev_rid = 0 :: non_neg_integer(),
|
prev_rid = 0 :: non_neg_integer(),
|
||||||
prev_key = <<"">> :: binary(),
|
prev_key = <<"">> :: binary(),
|
||||||
prev_poll :: erlang:timestamp() | undefined,
|
prev_poll :: erlang:timestamp() | undefined,
|
||||||
@ -274,8 +274,7 @@ init([#body{attrs = Attrs}, IP, SID]) ->
|
|||||||
Socket = make_socket(self(), IP),
|
Socket = make_socket(self(), IP),
|
||||||
XMPPVer = get_attr('xmpp:version', Attrs),
|
XMPPVer = get_attr('xmpp:version', Attrs),
|
||||||
XMPPDomain = get_attr(to, Attrs),
|
XMPPDomain = get_attr(to, Attrs),
|
||||||
{InBuf, Opts} = case gen_mod:get_module_opt(
|
{InBuf, Opts} = case mod_bosh_opt:prebind(XMPPDomain) of
|
||||||
XMPPDomain, mod_bosh, prebind) of
|
|
||||||
true ->
|
true ->
|
||||||
JID = make_random_jid(XMPPDomain),
|
JID = make_random_jid(XMPPDomain),
|
||||||
{buf_new(XMPPDomain), [{jid, JID} | Opts2]};
|
{buf_new(XMPPDomain), [{jid, JID} | Opts2]};
|
||||||
@ -287,9 +286,8 @@ init([#body{attrs = Attrs}, IP, SID]) ->
|
|||||||
case ejabberd_c2s:start(?MODULE, Socket, [{receiver, self()}|Opts]) of
|
case ejabberd_c2s:start(?MODULE, Socket, [{receiver, self()}|Opts]) of
|
||||||
{ok, C2SPid} ->
|
{ok, C2SPid} ->
|
||||||
ejabberd_c2s:accept(C2SPid),
|
ejabberd_c2s:accept(C2SPid),
|
||||||
Inactivity = gen_mod:get_module_opt(XMPPDomain,
|
Inactivity = mod_bosh_opt:max_inactivity(XMPPDomain),
|
||||||
mod_bosh, max_inactivity),
|
MaxConcat = mod_bosh_opt:max_concat(XMPPDomain),
|
||||||
MaxConcat = gen_mod:get_module_opt(XMPPDomain, mod_bosh, max_concat),
|
|
||||||
ShapedReceivers = buf_new(XMPPDomain, ?MAX_SHAPED_REQUESTS_QUEUE_LEN),
|
ShapedReceivers = buf_new(XMPPDomain, ?MAX_SHAPED_REQUESTS_QUEUE_LEN),
|
||||||
State = #state{host = XMPPDomain, sid = SID, ip = IP,
|
State = #state{host = XMPPDomain, sid = SID, ip = IP,
|
||||||
xmpp_ver = XMPPVer, el_ibuf = InBuf,
|
xmpp_ver = XMPPVer, el_ibuf = InBuf,
|
||||||
@ -298,8 +296,12 @@ init([#body{attrs = Attrs}, IP, SID]) ->
|
|||||||
shaped_receivers = ShapedReceivers,
|
shaped_receivers = ShapedReceivers,
|
||||||
shaper_state = ShaperState},
|
shaper_state = ShaperState},
|
||||||
NewState = restart_inactivity_timer(State),
|
NewState = restart_inactivity_timer(State),
|
||||||
mod_bosh:open_session(SID, self()),
|
case mod_bosh:open_session(SID, self()) of
|
||||||
|
ok ->
|
||||||
{ok, wait_for_session, NewState};
|
{ok, wait_for_session, NewState};
|
||||||
|
{error, Reason} ->
|
||||||
|
{stop, Reason}
|
||||||
|
end;
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
{stop, Reason};
|
{stop, Reason};
|
||||||
ignore ->
|
ignore ->
|
||||||
@ -328,8 +330,7 @@ wait_for_session(#body{attrs = Attrs} = Req, From,
|
|||||||
Wait == 0, Hold == 0 -> erlang:timestamp();
|
Wait == 0, Hold == 0 -> erlang:timestamp();
|
||||||
true -> undefined
|
true -> undefined
|
||||||
end,
|
end,
|
||||||
MaxPause = gen_mod:get_module_opt(State#state.host,
|
MaxPause = mod_bosh_opt:max_pause(State#state.host),
|
||||||
mod_bosh, max_pause),
|
|
||||||
Resp = #body{attrs =
|
Resp = #body{attrs =
|
||||||
[{sid, State#state.sid}, {wait, Wait},
|
[{sid, State#state.sid}, {wait, Wait},
|
||||||
{ver, ?BOSH_VERSION}, {polling, ?DEFAULT_POLLING},
|
{ver, ?BOSH_VERSION}, {polling, ?DEFAULT_POLLING},
|
||||||
@ -887,7 +888,9 @@ decode_body(Data, Size, Type) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
decode(Data, xml) ->
|
decode(Data, xml) ->
|
||||||
fxml_stream:parse_element(Data).
|
fxml_stream:parse_element(Data);
|
||||||
|
decode(Data, json) ->
|
||||||
|
Data.
|
||||||
|
|
||||||
attrs_to_body_attrs(Attrs) ->
|
attrs_to_body_attrs(Attrs) ->
|
||||||
lists:foldl(fun (_, {error, Reason}) -> {error, Reason};
|
lists:foldl(fun (_, {error, Reason}) -> {error, Reason};
|
||||||
@ -991,8 +994,7 @@ buf_new(Host) ->
|
|||||||
buf_new(Host, unlimited).
|
buf_new(Host, unlimited).
|
||||||
|
|
||||||
buf_new(Host, Limit) ->
|
buf_new(Host, Limit) ->
|
||||||
QueueType = gen_mod:get_module_opt(
|
QueueType = mod_bosh_opt:queue_type(Host),
|
||||||
Host, mod_bosh, queue_type),
|
|
||||||
p1_queue:new(QueueType, Limit).
|
p1_queue:new(QueueType, Limit).
|
||||||
|
|
||||||
buf_in(Xs, Buf) ->
|
buf_in(Xs, Buf) ->
|
||||||
|
@ -21,15 +21,13 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(ejabberd_c2s).
|
-module(ejabberd_c2s).
|
||||||
-behaviour(xmpp_stream_in).
|
-behaviour(xmpp_stream_in).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
-behaviour(ejabberd_listener).
|
-behaviour(ejabberd_listener).
|
||||||
|
-dialyzer([{no_fail_call, [stop/1, process_closed/2]},
|
||||||
|
{no_return, process_closed/2}]).
|
||||||
-protocol({rfc, 6121}).
|
-protocol({rfc, 6121}).
|
||||||
|
|
||||||
%% ejabberd_listener callbacks
|
%% ejabberd_listener callbacks
|
||||||
-export([start/3, start_link/3, accept/1, listen_opt_type/1, listen_options/0]).
|
-export([start/3, start_link/3, accept/1, listen_opt_type/1, listen_options/0]).
|
||||||
%% ejabberd_config callbacks
|
|
||||||
-export([opt_type/1, transform_listen_option/2]).
|
|
||||||
%% xmpp_stream_in callbacks
|
%% xmpp_stream_in callbacks
|
||||||
-export([init/1, handle_call/3, handle_cast/2,
|
-export([init/1, handle_call/3, handle_cast/2,
|
||||||
handle_info/2, terminate/2, code_change/3]).
|
handle_info/2, terminate/2, code_change/3]).
|
||||||
@ -109,8 +107,7 @@ resend_presence(Pid, To) ->
|
|||||||
close(Ref) ->
|
close(Ref) ->
|
||||||
xmpp_stream_in:close(Ref).
|
xmpp_stream_in:close(Ref).
|
||||||
|
|
||||||
-spec close(pid(), atom()) -> ok;
|
-spec close(pid(), atom()) -> ok.
|
||||||
(state(), atom()) -> state().
|
|
||||||
close(Ref, Reason) ->
|
close(Ref, Reason) ->
|
||||||
xmpp_stream_in:close(Ref, Reason).
|
xmpp_stream_in:close(Ref, Reason).
|
||||||
|
|
||||||
@ -319,37 +316,34 @@ tls_options(#{lserver := LServer, tls_options := DefaultOpts,
|
|||||||
TLSOpts1 = case {Encrypted, proplists:get_value(certfile, DefaultOpts)} of
|
TLSOpts1 = case {Encrypted, proplists:get_value(certfile, DefaultOpts)} of
|
||||||
{true, CertFile} when CertFile /= undefined -> DefaultOpts;
|
{true, CertFile} when CertFile /= undefined -> DefaultOpts;
|
||||||
{_, _} ->
|
{_, _} ->
|
||||||
case get_certfile(LServer) of
|
case ejabberd_pkix:get_certfile(LServer) of
|
||||||
undefined -> DefaultOpts;
|
error -> DefaultOpts;
|
||||||
CertFile -> lists:keystore(certfile, 1, DefaultOpts,
|
{ok, CertFile} ->
|
||||||
|
lists:keystore(certfile, 1, DefaultOpts,
|
||||||
{certfile, CertFile})
|
{certfile, CertFile})
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
TLSOpts2 = case ejabberd_config:get_option(
|
TLSOpts2 = case ejabberd_option:c2s_ciphers(LServer) of
|
||||||
{c2s_ciphers, LServer}) of
|
|
||||||
undefined -> TLSOpts1;
|
undefined -> TLSOpts1;
|
||||||
Ciphers -> lists:keystore(ciphers, 1, TLSOpts1,
|
Ciphers -> lists:keystore(ciphers, 1, TLSOpts1,
|
||||||
{ciphers, Ciphers})
|
{ciphers, Ciphers})
|
||||||
end,
|
end,
|
||||||
TLSOpts3 = case ejabberd_config:get_option(
|
TLSOpts3 = case ejabberd_option:c2s_protocol_options(LServer) of
|
||||||
{c2s_protocol_options, LServer}) of
|
|
||||||
undefined -> TLSOpts2;
|
undefined -> TLSOpts2;
|
||||||
ProtoOpts -> lists:keystore(protocol_options, 1, TLSOpts2,
|
ProtoOpts -> lists:keystore(protocol_options, 1, TLSOpts2,
|
||||||
{protocol_options, ProtoOpts})
|
{protocol_options, ProtoOpts})
|
||||||
end,
|
end,
|
||||||
TLSOpts4 = case ejabberd_config:get_option(
|
TLSOpts4 = case ejabberd_option:c2s_dhfile(LServer) of
|
||||||
{c2s_dhfile, LServer}) of
|
|
||||||
undefined -> TLSOpts3;
|
undefined -> TLSOpts3;
|
||||||
DHFile -> lists:keystore(dhfile, 1, TLSOpts3,
|
DHFile -> lists:keystore(dhfile, 1, TLSOpts3,
|
||||||
{dhfile, DHFile})
|
{dhfile, DHFile})
|
||||||
end,
|
end,
|
||||||
TLSOpts5 = case ejabberd_config:get_option(
|
TLSOpts5 = case ejabberd_option:c2s_cafile(LServer) of
|
||||||
{c2s_cafile, LServer}) of
|
|
||||||
undefined -> TLSOpts4;
|
undefined -> TLSOpts4;
|
||||||
CAFile -> lists:keystore(cafile, 1, TLSOpts4,
|
CAFile -> lists:keystore(cafile, 1, TLSOpts4,
|
||||||
{cafile, CAFile})
|
{cafile, CAFile})
|
||||||
end,
|
end,
|
||||||
case ejabberd_config:get_option({c2s_tls_compression, LServer}) of
|
case ejabberd_option:c2s_tls_compression(LServer) of
|
||||||
undefined -> TLSOpts5;
|
undefined -> TLSOpts5;
|
||||||
false -> [compression_none | TLSOpts5];
|
false -> [compression_none | TLSOpts5];
|
||||||
true -> lists:delete(compression_none, TLSOpts5)
|
true -> lists:delete(compression_none, TLSOpts5)
|
||||||
@ -376,7 +370,7 @@ authenticated_stream_features(#{lserver := LServer}) ->
|
|||||||
|
|
||||||
sasl_mechanisms(Mechs, #{lserver := LServer} = State) ->
|
sasl_mechanisms(Mechs, #{lserver := LServer} = State) ->
|
||||||
Type = ejabberd_auth:store_type(LServer),
|
Type = ejabberd_auth:store_type(LServer),
|
||||||
Mechs1 = ejabberd_config:get_option({disable_sasl_mechanisms, LServer}, []),
|
Mechs1 = ejabberd_option:disable_sasl_mechanisms(LServer),
|
||||||
%% I re-created it from cyrsasl ets magic, but I think it's wrong
|
%% I re-created it from cyrsasl ets magic, but I think it's wrong
|
||||||
%% TODO: need to check before 18.09 release
|
%% TODO: need to check before 18.09 release
|
||||||
lists:filter(
|
lists:filter(
|
||||||
@ -423,9 +417,8 @@ bind(R, #{user := U, server := S, access := Access, lang := Lang,
|
|||||||
{error, xmpp:err_conflict(), State};
|
{error, xmpp:err_conflict(), State};
|
||||||
{accept_resource, Resource} ->
|
{accept_resource, Resource} ->
|
||||||
JID = jid:make(U, S, Resource),
|
JID = jid:make(U, S, Resource),
|
||||||
case acl:access_matches(Access,
|
case acl:match_rule(LServer, Access,
|
||||||
#{usr => jid:split(JID), ip => IP},
|
#{usr => jid:split(JID), ip => IP}) of
|
||||||
LServer) of
|
|
||||||
allow ->
|
allow ->
|
||||||
State1 = open_session(State#{resource => Resource,
|
State1 = open_session(State#{resource => Resource,
|
||||||
sid => ejabberd_sm:make_sid()}),
|
sid => ejabberd_sm:make_sid()}),
|
||||||
@ -538,14 +531,14 @@ init([State, Opts]) ->
|
|||||||
TLSRequired = proplists:get_bool(starttls_required, Opts),
|
TLSRequired = proplists:get_bool(starttls_required, Opts),
|
||||||
TLSVerify = proplists:get_bool(tls_verify, Opts),
|
TLSVerify = proplists:get_bool(tls_verify, Opts),
|
||||||
Zlib = proplists:get_bool(zlib, Opts),
|
Zlib = proplists:get_bool(zlib, Opts),
|
||||||
Timeout = ejabberd_config:negotiation_timeout(),
|
Timeout = ejabberd_option:negotiation_timeout(),
|
||||||
State1 = State#{tls_options => TLSOpts2,
|
State1 = State#{tls_options => TLSOpts2,
|
||||||
tls_required => TLSRequired,
|
tls_required => TLSRequired,
|
||||||
tls_enabled => TLSEnabled,
|
tls_enabled => TLSEnabled,
|
||||||
tls_verify => TLSVerify,
|
tls_verify => TLSVerify,
|
||||||
pres_a => ?SETS:new(),
|
pres_a => ?SETS:new(),
|
||||||
zlib => Zlib,
|
zlib => Zlib,
|
||||||
lang => ejabberd_config:get_mylang(),
|
lang => ejabberd_option:language(),
|
||||||
server => ejabberd_config:get_myname(),
|
server => ejabberd_config:get_myname(),
|
||||||
lserver => ejabberd_config:get_myname(),
|
lserver => ejabberd_config:get_myname(),
|
||||||
access => Access,
|
access => Access,
|
||||||
@ -668,36 +661,39 @@ route_probe_reply(_, _) ->
|
|||||||
|
|
||||||
-spec process_presence_out(state(), presence()) -> state().
|
-spec process_presence_out(state(), presence()) -> state().
|
||||||
process_presence_out(#{lserver := LServer, jid := JID,
|
process_presence_out(#{lserver := LServer, jid := JID,
|
||||||
lang := Lang, pres_a := PresA} = State,
|
lang := Lang, pres_a := PresA} = State0,
|
||||||
#presence{from = From, to = To, type = Type} = Pres) ->
|
#presence{from = From, to = To, type = Type} = Pres) ->
|
||||||
|
State1 =
|
||||||
if Type == subscribe; Type == subscribed;
|
if Type == subscribe; Type == subscribed;
|
||||||
Type == unsubscribe; Type == unsubscribed ->
|
Type == unsubscribe; Type == unsubscribed ->
|
||||||
Access = gen_mod:get_module_opt(LServer, mod_roster, access),
|
Access = mod_roster_opt:access(LServer),
|
||||||
MyBareJID = jid:remove_resource(JID),
|
MyBareJID = jid:remove_resource(JID),
|
||||||
case acl:match_rule(LServer, Access, MyBareJID) of
|
case acl:match_rule(LServer, Access, MyBareJID) of
|
||||||
deny ->
|
deny ->
|
||||||
AccessErrTxt = <<"Access denied by service policy">>,
|
AccessErrTxt = <<"Access denied by service policy">>,
|
||||||
AccessErr = xmpp:err_forbidden(AccessErrTxt, Lang),
|
AccessErr = xmpp:err_forbidden(AccessErrTxt, Lang),
|
||||||
send_error(State, Pres, AccessErr);
|
send_error(State0, Pres, AccessErr);
|
||||||
allow ->
|
allow ->
|
||||||
ejabberd_hooks:run(roster_out_subscription, LServer, [Pres])
|
ejabberd_hooks:run(roster_out_subscription, LServer, [Pres]),
|
||||||
|
State0
|
||||||
end;
|
end;
|
||||||
true -> ok
|
true ->
|
||||||
|
State0
|
||||||
end,
|
end,
|
||||||
case privacy_check_packet(State, Pres, out) of
|
case privacy_check_packet(State1, Pres, out) of
|
||||||
deny ->
|
deny ->
|
||||||
PrivErrTxt = <<"Your active privacy list has denied "
|
PrivErrTxt = <<"Your active privacy list has denied "
|
||||||
"the routing of this stanza.">>,
|
"the routing of this stanza.">>,
|
||||||
PrivErr = xmpp:err_not_acceptable(PrivErrTxt, Lang),
|
PrivErr = xmpp:err_not_acceptable(PrivErrTxt, Lang),
|
||||||
send_error(State, Pres, PrivErr);
|
send_error(State1, Pres, PrivErr);
|
||||||
allow when Type == subscribe; Type == subscribed;
|
allow when Type == subscribe; Type == subscribed;
|
||||||
Type == unsubscribe; Type == unsubscribed ->
|
Type == unsubscribe; Type == unsubscribed ->
|
||||||
BareFrom = jid:remove_resource(From),
|
BareFrom = jid:remove_resource(From),
|
||||||
ejabberd_router:route(xmpp:set_from_to(Pres, BareFrom, To)),
|
ejabberd_router:route(xmpp:set_from_to(Pres, BareFrom, To)),
|
||||||
State;
|
State1;
|
||||||
allow when Type == error; Type == probe ->
|
allow when Type == error; Type == probe ->
|
||||||
ejabberd_router:route(Pres),
|
ejabberd_router:route(Pres),
|
||||||
State;
|
State1;
|
||||||
allow ->
|
allow ->
|
||||||
ejabberd_router:route(Pres),
|
ejabberd_router:route(Pres),
|
||||||
LTo = jid:tolower(To),
|
LTo = jid:tolower(To),
|
||||||
@ -710,12 +706,12 @@ process_presence_out(#{lserver := LServer, jid := JID,
|
|||||||
available -> ?SETS:add_element(LTo, PresA);
|
available -> ?SETS:add_element(LTo, PresA);
|
||||||
unavailable -> ?SETS:del_element(LTo, PresA)
|
unavailable -> ?SETS:del_element(LTo, PresA)
|
||||||
end,
|
end,
|
||||||
State#{pres_a => A};
|
State1#{pres_a => A};
|
||||||
true ->
|
true ->
|
||||||
State
|
State1
|
||||||
end;
|
end;
|
||||||
true ->
|
true ->
|
||||||
State
|
State1
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
@ -724,7 +720,7 @@ process_self_presence(#{lserver := LServer, sid := SID,
|
|||||||
user := U, server := S, resource := R} = State,
|
user := U, server := S, resource := R} = State,
|
||||||
#presence{type = unavailable} = Pres) ->
|
#presence{type = unavailable} = Pres) ->
|
||||||
Status = xmpp:get_text(Pres#presence.status),
|
Status = xmpp:get_text(Pres#presence.status),
|
||||||
ejabberd_sm:unset_presence(SID, U, S, R, Status),
|
_ = ejabberd_sm:unset_presence(SID, U, S, R, Status),
|
||||||
{Pres1, State1} = ejabberd_hooks:run_fold(
|
{Pres1, State1} = ejabberd_hooks:run_fold(
|
||||||
c2s_self_presence, LServer, {Pres, State}, []),
|
c2s_self_presence, LServer, {Pres, State}, []),
|
||||||
State2 = broadcast_presence_unavailable(State1, Pres1),
|
State2 = broadcast_presence_unavailable(State1, Pres1),
|
||||||
@ -732,7 +728,7 @@ process_self_presence(#{lserver := LServer, sid := SID,
|
|||||||
process_self_presence(#{lserver := LServer} = State,
|
process_self_presence(#{lserver := LServer} = State,
|
||||||
#presence{type = available} = Pres) ->
|
#presence{type = available} = Pres) ->
|
||||||
PreviousPres = maps:get(pres_last, State, undefined),
|
PreviousPres = maps:get(pres_last, State, undefined),
|
||||||
update_priority(State, Pres),
|
_ = update_priority(State, Pres),
|
||||||
{Pres1, State1} = ejabberd_hooks:run_fold(
|
{Pres1, State1} = ejabberd_hooks:run_fold(
|
||||||
c2s_self_presence, LServer, {Pres, State}, []),
|
c2s_self_presence, LServer, {Pres, State}, []),
|
||||||
State2 = State1#{pres_last => Pres1,
|
State2 = State1#{pres_last => Pres1,
|
||||||
@ -867,8 +863,7 @@ get_subscription(#jid{luser = LUser, lserver = LServer}, JID) ->
|
|||||||
resource_conflict_action(U, S, R) ->
|
resource_conflict_action(U, S, R) ->
|
||||||
OptionRaw = case ejabberd_sm:is_existing_resource(U, S, R) of
|
OptionRaw = case ejabberd_sm:is_existing_resource(U, S, R) of
|
||||||
true ->
|
true ->
|
||||||
ejabberd_config:get_option(
|
ejabberd_option:resource_conflict(S);
|
||||||
{resource_conflict, S}, acceptnew);
|
|
||||||
false ->
|
false ->
|
||||||
acceptnew
|
acceptnew
|
||||||
end,
|
end,
|
||||||
@ -934,9 +929,8 @@ fix_from_to(Pkt, _State) ->
|
|||||||
change_shaper(#{shaper := ShaperName, ip := IP, lserver := LServer,
|
change_shaper(#{shaper := ShaperName, ip := IP, lserver := LServer,
|
||||||
user := U, server := S, resource := R} = State) ->
|
user := U, server := S, resource := R} = State) ->
|
||||||
JID = jid:make(U, S, R),
|
JID = jid:make(U, S, R),
|
||||||
Shaper = acl:access_matches(ShaperName,
|
Shaper = ejabberd_shaper:match(LServer, ShaperName,
|
||||||
#{usr => jid:split(JID), ip => IP},
|
#{usr => jid:split(JID), ip => IP}),
|
||||||
LServer),
|
|
||||||
xmpp_stream_in:change_shaper(State, ejabberd_shaper:new(Shaper)).
|
xmpp_stream_in:change_shaper(State, ejabberd_shaper:new(Shaper)).
|
||||||
|
|
||||||
-spec format_reason(state(), term()) -> binary().
|
-spec format_reason(state(), term()) -> binary().
|
||||||
@ -951,84 +945,24 @@ format_reason(_, {shutdown, _}) ->
|
|||||||
format_reason(_, _) ->
|
format_reason(_, _) ->
|
||||||
<<"internal server error">>.
|
<<"internal server error">>.
|
||||||
|
|
||||||
-spec get_certfile(binary()) -> file:filename_all() | undefined.
|
listen_opt_type(starttls) ->
|
||||||
get_certfile(LServer) ->
|
econf:bool();
|
||||||
case ejabberd_pkix:get_certfile(LServer) of
|
listen_opt_type(starttls_required) ->
|
||||||
{ok, CertFile} ->
|
econf:bool();
|
||||||
CertFile;
|
listen_opt_type(tls_verify) ->
|
||||||
error ->
|
econf:bool();
|
||||||
ejabberd_config:get_option(
|
|
||||||
{domain_certfile, LServer},
|
|
||||||
ejabberd_config:get_option({c2s_certfile, LServer}))
|
|
||||||
end.
|
|
||||||
|
|
||||||
transform_listen_option(Opt, Opts) ->
|
|
||||||
[Opt|Opts].
|
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(c2s_ciphers) -> fun iolist_to_binary/1;
|
|
||||||
opt_type(c2s_dhfile) -> fun misc:try_read_file/1;
|
|
||||||
opt_type(c2s_cafile) -> fun misc:try_read_file/1;
|
|
||||||
opt_type(c2s_protocol_options) ->
|
|
||||||
fun (Options) -> str:join(Options, <<"|">>) end;
|
|
||||||
opt_type(c2s_tls_compression) ->
|
|
||||||
fun (true) -> true;
|
|
||||||
(false) -> false
|
|
||||||
end;
|
|
||||||
opt_type(resource_conflict) ->
|
|
||||||
fun (setresource) -> setresource;
|
|
||||||
(closeold) -> closeold;
|
|
||||||
(closenew) -> closenew;
|
|
||||||
(acceptnew) -> acceptnew
|
|
||||||
end;
|
|
||||||
opt_type(disable_sasl_mechanisms) ->
|
|
||||||
fun (V) when is_list(V) ->
|
|
||||||
lists:map(fun (M) -> str:to_upper(M) end, V);
|
|
||||||
(V) -> [str:to_upper(V)]
|
|
||||||
end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[c2s_ciphers, c2s_cafile, c2s_dhfile,
|
|
||||||
c2s_protocol_options, c2s_tls_compression, resource_conflict,
|
|
||||||
disable_sasl_mechanisms].
|
|
||||||
|
|
||||||
listen_opt_type(certfile = Opt) ->
|
|
||||||
fun(S) ->
|
|
||||||
?WARNING_MSG("Listening option '~s' for ~s is deprecated, use "
|
|
||||||
"'certfiles' global option instead", [Opt, ?MODULE]),
|
|
||||||
{ok, File} = ejabberd_pkix:add_certfile(S),
|
|
||||||
File
|
|
||||||
end;
|
|
||||||
listen_opt_type(starttls) -> fun(B) when is_boolean(B) -> B end;
|
|
||||||
listen_opt_type(starttls_required) -> fun(B) when is_boolean(B) -> B end;
|
|
||||||
listen_opt_type(tls_verify) -> fun(B) when is_boolean(B) -> B end;
|
|
||||||
listen_opt_type(zlib) ->
|
listen_opt_type(zlib) ->
|
||||||
fun(true) ->
|
econf:and_then(
|
||||||
|
econf:bool(),
|
||||||
|
fun(false) -> false;
|
||||||
|
(true) ->
|
||||||
ejabberd:start_app(ezlib),
|
ejabberd:start_app(ezlib),
|
||||||
true;
|
true
|
||||||
(false) ->
|
end).
|
||||||
false
|
|
||||||
end;
|
|
||||||
listen_opt_type(stream_management) ->
|
|
||||||
fun(B) when is_boolean(B) ->
|
|
||||||
?ERROR_MSG("Listening option 'stream_management' is ignored: "
|
|
||||||
"use mod_stream_mgmt module", []),
|
|
||||||
B
|
|
||||||
end;
|
|
||||||
listen_opt_type(O) ->
|
|
||||||
MgmtOpts = mod_stream_mgmt:mod_options(ejabberd_config:get_myname()),
|
|
||||||
case lists:keymember(O, 1, MgmtOpts) of
|
|
||||||
true ->
|
|
||||||
fun(V) ->
|
|
||||||
?ERROR_MSG("Listening option '~s' is ignored: use '~s' "
|
|
||||||
"option from mod_stream_mgmt module", [O, O]),
|
|
||||||
(mod_stream_mgmt:mod_opt_type(O))(V)
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
listen_options() ->
|
listen_options() ->
|
||||||
[{access, all},
|
[{access, all},
|
||||||
{shaper, none},
|
{shaper, none},
|
||||||
{certfile, undefined},
|
|
||||||
{ciphers, undefined},
|
{ciphers, undefined},
|
||||||
{dhfile, undefined},
|
{dhfile, undefined},
|
||||||
{cafile, undefined},
|
{cafile, undefined},
|
||||||
@ -1040,5 +974,4 @@ listen_options() ->
|
|||||||
{tls_verify, false},
|
{tls_verify, false},
|
||||||
{zlib, false},
|
{zlib, false},
|
||||||
{max_stanza_size, infinity},
|
{max_stanza_size, infinity},
|
||||||
{max_fsm_queue, 5000}|
|
{max_fsm_queue, 5000}].
|
||||||
mod_stream_mgmt:mod_options(ejabberd_config:get_myname())].
|
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
%% Get first c2s configuration limitations to apply it to other c2s
|
%% Get first c2s configuration limitations to apply it to other c2s
|
||||||
%% connectors.
|
%% connectors.
|
||||||
get_c2s_limits() ->
|
get_c2s_limits() ->
|
||||||
C2SFirstListen = ejabberd_config:get_option(listen, []),
|
C2SFirstListen = ejabberd_option:listen(),
|
||||||
case lists:keysearch(ejabberd_c2s, 2, C2SFirstListen) of
|
case lists:keysearch(ejabberd_c2s, 2, C2SFirstListen) of
|
||||||
false -> [];
|
false -> [];
|
||||||
{value, {_Port, ejabberd_c2s, Opts}} ->
|
{value, {_Port, ejabberd_c2s, Opts}} ->
|
||||||
@ -41,23 +41,12 @@ get_c2s_limits() ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
%% Only get access, shaper and max_stanza_size values
|
%% Only get access, shaper and max_stanza_size values
|
||||||
|
|
||||||
select_opts_values(Opts) ->
|
select_opts_values(Opts) ->
|
||||||
select_opts_values(Opts, []).
|
maps:fold(
|
||||||
|
fun(Opt, Val, Acc) when Opt == access;
|
||||||
select_opts_values([], SelectedValues) ->
|
Opt == shaper;
|
||||||
SelectedValues;
|
Opt == max_stanza_size ->
|
||||||
select_opts_values([{access, Value} | Opts],
|
[{Opt, Val}|Acc];
|
||||||
SelectedValues) ->
|
(_, _, Acc) ->
|
||||||
select_opts_values(Opts,
|
Acc
|
||||||
[{access, Value} | SelectedValues]);
|
end, [], Opts).
|
||||||
select_opts_values([{shaper, Value} | Opts],
|
|
||||||
SelectedValues) ->
|
|
||||||
select_opts_values(Opts,
|
|
||||||
[{shaper, Value} | SelectedValues]);
|
|
||||||
select_opts_values([{max_stanza_size, Value} | Opts],
|
|
||||||
SelectedValues) ->
|
|
||||||
select_opts_values(Opts,
|
|
||||||
[{max_stanza_size, Value} | SelectedValues]);
|
|
||||||
select_opts_values([_Opt | Opts], SelectedValues) ->
|
|
||||||
select_opts_values(Opts, SelectedValues).
|
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
|
|
||||||
-module(ejabberd_captcha).
|
-module(ejabberd_captcha).
|
||||||
|
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-protocol({xep, 158, '1.0'}).
|
-protocol({xep, 158, '1.0'}).
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
@ -41,7 +39,7 @@
|
|||||||
-export([create_captcha/6, build_captcha_html/2,
|
-export([create_captcha/6, build_captcha_html/2,
|
||||||
check_captcha/2, process_reply/1, process/2,
|
check_captcha/2, process_reply/1, process/2,
|
||||||
is_feature_available/0, create_captcha_x/5,
|
is_feature_available/0, create_captcha_x/5,
|
||||||
opt_type/1, host_up/1, host_down/1,
|
host_up/1, host_down/1,
|
||||||
config_reloaded/0, process_iq/1]).
|
config_reloaded/0, process_iq/1]).
|
||||||
|
|
||||||
-include("xmpp.hrl").
|
-include("xmpp.hrl").
|
||||||
@ -52,6 +50,7 @@
|
|||||||
-define(LIMIT_PERIOD, 60*1000*1000).
|
-define(LIMIT_PERIOD, 60*1000*1000).
|
||||||
|
|
||||||
-type image_error() :: efbig | enodata | limit | malformed_image | timeout.
|
-type image_error() :: efbig | enodata | limit | malformed_image | timeout.
|
||||||
|
-type priority() :: neg_integer().
|
||||||
|
|
||||||
-record(state, {limits = treap:empty() :: treap:treap(),
|
-record(state, {limits = treap:empty() :: treap:treap(),
|
||||||
enabled = false :: boolean()}).
|
enabled = false :: boolean()}).
|
||||||
@ -66,11 +65,11 @@ start_link() ->
|
|||||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [],
|
gen_server:start_link({local, ?MODULE}, ?MODULE, [],
|
||||||
[]).
|
[]).
|
||||||
|
|
||||||
-spec captcha_text(undefined | binary()) -> binary().
|
-spec captcha_text(binary()) -> binary().
|
||||||
captcha_text(Lang) ->
|
captcha_text(Lang) ->
|
||||||
translate:translate(Lang, <<"Enter the text you see">>).
|
translate:translate(Lang, <<"Enter the text you see">>).
|
||||||
|
|
||||||
-spec mk_ocr_field(binary() | undefined, binary(), binary()) -> xdata_field().
|
-spec mk_ocr_field(binary(), binary(), binary()) -> xdata_field().
|
||||||
mk_ocr_field(Lang, CID, Type) ->
|
mk_ocr_field(Lang, CID, Type) ->
|
||||||
URI = #media_uri{type = Type, uri = <<"cid:", CID/binary>>},
|
URI = #media_uri{type = Type, uri = <<"cid:", CID/binary>>},
|
||||||
#xdata_field{var = <<"ocr">>,
|
#xdata_field{var = <<"ocr">>,
|
||||||
@ -79,13 +78,13 @@ mk_ocr_field(Lang, CID, Type) ->
|
|||||||
required = true,
|
required = true,
|
||||||
sub_els = [#media{uri = [URI]}]}.
|
sub_els = [#media{uri = [URI]}]}.
|
||||||
|
|
||||||
|
-spec mk_field(_, binary(), binary()) -> xdata_field().
|
||||||
mk_field(Type, Var, Value) ->
|
mk_field(Type, Var, Value) ->
|
||||||
#xdata_field{type = Type, var = Var, values = [Value]}.
|
#xdata_field{type = Type, var = Var, values = [Value]}.
|
||||||
|
|
||||||
-spec create_captcha(binary(), jid(), jid(),
|
-spec create_captcha(binary(), jid(), jid(),
|
||||||
binary(), any(), any()) -> {error, image_error()} |
|
binary(), any(), any()) -> {error, image_error()} |
|
||||||
{ok, binary(), [text()], [xmlel()]}.
|
{ok, binary(), [text()], [xmpp_element()]}.
|
||||||
|
|
||||||
create_captcha(SID, From, To, Lang, Limiter, Args) ->
|
create_captcha(SID, From, To, Lang, Limiter, Args) ->
|
||||||
case create_image(Limiter) of
|
case create_image(Limiter) of
|
||||||
{ok, Type, Key, Image} ->
|
{ok, Type, Key, Image} ->
|
||||||
@ -116,8 +115,7 @@ create_captcha(SID, From, To, Lang, Limiter, Args) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
-spec create_captcha_x(binary(), jid(), binary(), any(), xdata()) ->
|
-spec create_captcha_x(binary(), jid(), binary(), any(), xdata()) ->
|
||||||
{ok, xdata()} | {error, image_error()}.
|
{ok, [xmpp_element()]} | {error, image_error()}.
|
||||||
|
|
||||||
create_captcha_x(SID, To, Lang, Limiter, #xdata{fields = Fs} = X) ->
|
create_captcha_x(SID, To, Lang, Limiter, #xdata{fields = Fs} = X) ->
|
||||||
case create_image(Limiter) of
|
case create_image(Limiter) of
|
||||||
{ok, Type, Key, Image} ->
|
{ok, Type, Key, Image} ->
|
||||||
@ -151,7 +149,7 @@ create_captcha_x(SID, To, Lang, Limiter, #xdata{fields = Fs} = X) ->
|
|||||||
|
|
||||||
-spec build_captcha_html(binary(), binary()) -> captcha_not_found |
|
-spec build_captcha_html(binary(), binary()) -> captcha_not_found |
|
||||||
{xmlel(),
|
{xmlel(),
|
||||||
{xmlel(), xmlel(),
|
{xmlel(), cdata(),
|
||||||
xmlel(), xmlel()}}.
|
xmlel(), xmlel()}}.
|
||||||
|
|
||||||
build_captcha_html(Id, Lang) ->
|
build_captcha_html(Id, Lang) ->
|
||||||
@ -161,7 +159,7 @@ build_captcha_html(Id, Lang) ->
|
|||||||
attrs =
|
attrs =
|
||||||
[{<<"src">>, get_url(<<Id/binary, "/image">>)}],
|
[{<<"src">>, get_url(<<Id/binary, "/image">>)}],
|
||||||
children = []},
|
children = []},
|
||||||
TextEl = {xmlcdata, captcha_text(Lang)},
|
Text = {xmlcdata, captcha_text(Lang)},
|
||||||
IdEl = #xmlel{name = <<"input">>,
|
IdEl = #xmlel{name = <<"input">>,
|
||||||
attrs =
|
attrs =
|
||||||
[{<<"type">>, <<"hidden">>}, {<<"name">>, <<"id">>},
|
[{<<"type">>, <<"hidden">>}, {<<"name">>, <<"id">>},
|
||||||
@ -181,7 +179,7 @@ build_captcha_html(Id, Lang) ->
|
|||||||
[ImgEl,
|
[ImgEl,
|
||||||
#xmlel{name = <<"br">>, attrs = [],
|
#xmlel{name = <<"br">>, attrs = [],
|
||||||
children = []},
|
children = []},
|
||||||
TextEl,
|
Text,
|
||||||
#xmlel{name = <<"br">>, attrs = [],
|
#xmlel{name = <<"br">>, attrs = [],
|
||||||
children = []},
|
children = []},
|
||||||
IdEl, KeyEl,
|
IdEl, KeyEl,
|
||||||
@ -193,7 +191,7 @@ build_captcha_html(Id, Lang) ->
|
|||||||
{<<"name">>, <<"enter">>},
|
{<<"name">>, <<"enter">>},
|
||||||
{<<"value">>, <<"OK">>}],
|
{<<"value">>, <<"OK">>}],
|
||||||
children = []}]},
|
children = []}]},
|
||||||
{FormEl, {ImgEl, TextEl, IdEl, KeyEl}};
|
{FormEl, {ImgEl, Text, IdEl, KeyEl}};
|
||||||
_ -> captcha_not_found
|
_ -> captcha_not_found
|
||||||
end.
|
end.
|
||||||
|
|
||||||
@ -216,6 +214,7 @@ process_reply(#xcaptcha{xdata = #xdata{} = X}) ->
|
|||||||
process_reply(_) ->
|
process_reply(_) ->
|
||||||
{error, malformed}.
|
{error, malformed}.
|
||||||
|
|
||||||
|
-spec process_iq(iq()) -> iq().
|
||||||
process_iq(#iq{type = set, lang = Lang, sub_els = [#xcaptcha{} = El]} = IQ) ->
|
process_iq(#iq{type = set, lang = Lang, sub_els = [#xcaptcha{} = El]} = IQ) ->
|
||||||
case process_reply(El) of
|
case process_reply(El) of
|
||||||
ok ->
|
ok ->
|
||||||
@ -238,7 +237,7 @@ process(_Handlers,
|
|||||||
#request{method = 'GET', lang = Lang,
|
#request{method = 'GET', lang = Lang,
|
||||||
path = [_, Id]}) ->
|
path = [_, Id]}) ->
|
||||||
case build_captcha_html(Id, Lang) of
|
case build_captcha_html(Id, Lang) of
|
||||||
{FormEl, _} when is_tuple(FormEl) ->
|
{FormEl, _} ->
|
||||||
Form = #xmlel{name = <<"div">>,
|
Form = #xmlel{name = <<"div">>,
|
||||||
attrs = [{<<"align">>, <<"center">>}],
|
attrs = [{<<"align">>, <<"center">>}],
|
||||||
children = [FormEl]},
|
children = [FormEl]},
|
||||||
@ -292,8 +291,8 @@ config_reloaded() ->
|
|||||||
gen_server:call(?MODULE, config_reloaded, timer:minutes(1)).
|
gen_server:call(?MODULE, config_reloaded, timer:minutes(1)).
|
||||||
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
mnesia:delete_table(captcha),
|
_ = mnesia:delete_table(captcha),
|
||||||
ets:new(captcha, [named_table, public, {keypos, #captcha.id}]),
|
_ = ets:new(captcha, [named_table, public, {keypos, #captcha.id}]),
|
||||||
case check_captcha_setup() of
|
case check_captcha_setup() of
|
||||||
true ->
|
true ->
|
||||||
register_handlers(),
|
register_handlers(),
|
||||||
@ -364,27 +363,36 @@ terminate(_Reason, #state{enabled = Enabled}) ->
|
|||||||
register_handlers() ->
|
register_handlers() ->
|
||||||
ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
|
ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
|
||||||
ejabberd_hooks:add(host_down, ?MODULE, host_down, 50),
|
ejabberd_hooks:add(host_down, ?MODULE, host_down, 50),
|
||||||
lists:foreach(fun host_up/1, ejabberd_config:get_myhosts()).
|
lists:foreach(fun host_up/1, ejabberd_option:hosts()).
|
||||||
|
|
||||||
unregister_handlers() ->
|
unregister_handlers() ->
|
||||||
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50),
|
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50),
|
||||||
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 50),
|
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 50),
|
||||||
lists:foreach(fun host_down/1, ejabberd_config:get_myhosts()).
|
lists:foreach(fun host_down/1, ejabberd_option:hosts()).
|
||||||
|
|
||||||
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
code_change(_OldVsn, State, _Extra) -> {ok, State}.
|
||||||
|
|
||||||
create_image() -> create_image(undefined).
|
-spec create_image() -> {ok, binary(), binary(), binary()} |
|
||||||
|
{error, image_error()}.
|
||||||
|
create_image() ->
|
||||||
|
create_image(undefined).
|
||||||
|
|
||||||
|
-spec create_image(term()) -> {ok, binary(), binary(), binary()} |
|
||||||
|
{error, image_error()}.
|
||||||
create_image(Limiter) ->
|
create_image(Limiter) ->
|
||||||
Key = str:substr(p1_rand:get_string(), 1, 6),
|
Key = str:substr(p1_rand:get_string(), 1, 6),
|
||||||
create_image(Limiter, Key).
|
create_image(Limiter, Key).
|
||||||
|
|
||||||
|
-spec create_image(term(), binary()) -> {ok, binary(), binary(), binary()} |
|
||||||
|
{error, image_error()}.
|
||||||
create_image(Limiter, Key) ->
|
create_image(Limiter, Key) ->
|
||||||
case is_limited(Limiter) of
|
case is_limited(Limiter) of
|
||||||
true -> {error, limit};
|
true -> {error, limit};
|
||||||
false -> do_create_image(Key)
|
false -> do_create_image(Key)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec do_create_image(binary()) -> {ok, binary(), binary(), binary()} |
|
||||||
|
{error, image_error()}.
|
||||||
do_create_image(Key) ->
|
do_create_image(Key) ->
|
||||||
FileName = get_prog_name(),
|
FileName = get_prog_name(),
|
||||||
Cmd = lists:flatten(io_lib:format("~s ~s", [FileName, Key])),
|
Cmd = lists:flatten(io_lib:format("~s ~s", [FileName, Key])),
|
||||||
@ -416,7 +424,7 @@ do_create_image(Key) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
get_prog_name() ->
|
get_prog_name() ->
|
||||||
case ejabberd_config:get_option(captcha_cmd) of
|
case ejabberd_option:captcha_cmd() of
|
||||||
undefined ->
|
undefined ->
|
||||||
?DEBUG("The option captcha_cmd is not configured, "
|
?DEBUG("The option captcha_cmd is not configured, "
|
||||||
"but some module wants to use the CAPTCHA "
|
"but some module wants to use the CAPTCHA "
|
||||||
@ -427,8 +435,9 @@ get_prog_name() ->
|
|||||||
FileName
|
FileName
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec get_url(binary()) -> binary().
|
||||||
get_url(Str) ->
|
get_url(Str) ->
|
||||||
CaptchaHost = ejabberd_config:get_option(captcha_host, <<"">>),
|
CaptchaHost = ejabberd_option:captcha_host(),
|
||||||
case str:tokens(CaptchaHost, <<":">>) of
|
case str:tokens(CaptchaHost, <<":">>) of
|
||||||
[Host] ->
|
[Host] ->
|
||||||
<<"http://", Host/binary, "/captcha/", Str/binary>>;
|
<<"http://", Host/binary, "/captcha/", Str/binary>>;
|
||||||
@ -453,7 +462,7 @@ get_transfer_protocol(PortString) ->
|
|||||||
get_captcha_transfer_protocol(PortListeners).
|
get_captcha_transfer_protocol(PortListeners).
|
||||||
|
|
||||||
get_port_listeners(PortNumber) ->
|
get_port_listeners(PortNumber) ->
|
||||||
AllListeners = ejabberd_config:get_option(listen, []),
|
AllListeners = ejabberd_option:listen(),
|
||||||
lists:filter(
|
lists:filter(
|
||||||
fun({{Port, _IP, _Transport}, _Module, _Opts}) ->
|
fun({{Port, _IP, _Transport}, _Module, _Opts}) ->
|
||||||
Port == PortNumber
|
Port == PortNumber
|
||||||
@ -465,21 +474,26 @@ get_captcha_transfer_protocol([]) ->
|
|||||||
"'captcha' option. Change the port number "
|
"'captcha' option. Change the port number "
|
||||||
"or specify http:// in that option.">>);
|
"or specify http:// in that option.">>);
|
||||||
get_captcha_transfer_protocol([{_, ejabberd_http, Opts} | Listeners]) ->
|
get_captcha_transfer_protocol([{_, ejabberd_http, Opts} | Listeners]) ->
|
||||||
case proplists:get_bool(captcha, Opts) of
|
Handlers = maps:get(request_handlers, Opts, []),
|
||||||
|
case lists:any(
|
||||||
|
fun({_, ?MODULE}) -> true;
|
||||||
|
({_, _}) -> false
|
||||||
|
end, Handlers) of
|
||||||
true ->
|
true ->
|
||||||
case proplists:get_bool(tls, Opts) of
|
case maps:get(tls, Opts) of
|
||||||
true -> https;
|
true -> https;
|
||||||
false -> http
|
false -> http
|
||||||
end;
|
end;
|
||||||
false -> get_captcha_transfer_protocol(Listeners)
|
false ->
|
||||||
|
get_captcha_transfer_protocol(Listeners)
|
||||||
end;
|
end;
|
||||||
get_captcha_transfer_protocol([_ | Listeners]) ->
|
get_captcha_transfer_protocol([_ | Listeners]) ->
|
||||||
get_captcha_transfer_protocol(Listeners).
|
get_captcha_transfer_protocol(Listeners).
|
||||||
|
|
||||||
is_limited(undefined) -> false;
|
is_limited(undefined) -> false;
|
||||||
is_limited(Limiter) ->
|
is_limited(Limiter) ->
|
||||||
case ejabberd_config:get_option(captcha_limit) of
|
case ejabberd_option:captcha_limit() of
|
||||||
undefined -> false;
|
infinity -> false;
|
||||||
Int ->
|
Int ->
|
||||||
case catch gen_server:call(?MODULE,
|
case catch gen_server:call(?MODULE,
|
||||||
{is_limited, Limiter, Int}, 5000)
|
{is_limited, Limiter, Int}, 5000)
|
||||||
@ -494,12 +508,14 @@ is_limited(Limiter) ->
|
|||||||
|
|
||||||
-define(MAX_FILE_SIZE, 64 * 1024).
|
-define(MAX_FILE_SIZE, 64 * 1024).
|
||||||
|
|
||||||
|
-spec cmd(string()) -> {ok, binary()} | {error, image_error()}.
|
||||||
cmd(Cmd) ->
|
cmd(Cmd) ->
|
||||||
Port = open_port({spawn, Cmd}, [stream, eof, binary]),
|
Port = open_port({spawn, Cmd}, [stream, eof, binary]),
|
||||||
TRef = erlang:start_timer(?CMD_TIMEOUT, self(),
|
TRef = erlang:start_timer(?CMD_TIMEOUT, self(),
|
||||||
timeout),
|
timeout),
|
||||||
recv_data(Port, TRef, <<>>).
|
recv_data(Port, TRef, <<>>).
|
||||||
|
|
||||||
|
-spec recv_data(port(), reference(), binary()) -> {ok, binary()} | {error, image_error()}.
|
||||||
recv_data(Port, TRef, Buf) ->
|
recv_data(Port, TRef, Buf) ->
|
||||||
receive
|
receive
|
||||||
{Port, {data, Bytes}} ->
|
{Port, {data, Bytes}} ->
|
||||||
@ -516,6 +532,8 @@ recv_data(Port, TRef, Buf) ->
|
|||||||
return(Port, TRef, {error, timeout})
|
return(Port, TRef, {error, timeout})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec return(port(), reference(), {ok, binary()} | {error, image_error()}) ->
|
||||||
|
{ok, binary()} | {error, image_error()}.
|
||||||
return(Port, TRef, Result) ->
|
return(Port, TRef, Result) ->
|
||||||
misc:cancel_timer(TRef),
|
misc:cancel_timer(TRef),
|
||||||
catch port_close(Port),
|
catch port_close(Port),
|
||||||
@ -543,10 +561,11 @@ check_captcha_setup() ->
|
|||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec lookup_captcha(binary()) -> {ok, #captcha{}} | {error, enoent}.
|
||||||
lookup_captcha(Id) ->
|
lookup_captcha(Id) ->
|
||||||
case ets:lookup(captcha, Id) of
|
case ets:lookup(captcha, Id) of
|
||||||
[C] -> {ok, C};
|
[C] -> {ok, C};
|
||||||
_ -> {error, enoent}
|
[] -> {error, enoent}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec check_captcha(binary(), binary()) -> captcha_not_found |
|
-spec check_captcha(binary(), binary()) -> captcha_not_found |
|
||||||
@ -554,8 +573,8 @@ lookup_captcha(Id) ->
|
|||||||
captcha_non_valid.
|
captcha_non_valid.
|
||||||
|
|
||||||
check_captcha(Id, ProvidedKey) ->
|
check_captcha(Id, ProvidedKey) ->
|
||||||
case ets:lookup(captcha, Id) of
|
case lookup_captcha(Id) of
|
||||||
[#captcha{pid = Pid, args = Args, key = ValidKey, tref = Tref}] ->
|
{ok, #captcha{pid = Pid, args = Args, key = ValidKey, tref = Tref}} ->
|
||||||
ets:delete(captcha, Id),
|
ets:delete(captcha, Id),
|
||||||
misc:cancel_timer(Tref),
|
misc:cancel_timer(Tref),
|
||||||
if ValidKey == ProvidedKey ->
|
if ValidKey == ProvidedKey ->
|
||||||
@ -565,10 +584,11 @@ check_captcha(Id, ProvidedKey) ->
|
|||||||
callback(captcha_failed, Pid, Args),
|
callback(captcha_failed, Pid, Args),
|
||||||
captcha_non_valid
|
captcha_non_valid
|
||||||
end;
|
end;
|
||||||
_ ->
|
{error, _} ->
|
||||||
captcha_not_found
|
captcha_not_found
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec clean_treap(treap:treap(), priority()) -> treap:treap().
|
||||||
clean_treap(Treap, CleanPriority) ->
|
clean_treap(Treap, CleanPriority) ->
|
||||||
case treap:is_empty(Treap) of
|
case treap:is_empty(Treap) of
|
||||||
true -> Treap;
|
true -> Treap;
|
||||||
@ -588,16 +608,6 @@ callback(Result, Pid, Args) when is_pid(Pid) ->
|
|||||||
callback(_, _, _) ->
|
callback(_, _, _) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
-spec now_priority() -> priority().
|
||||||
now_priority() ->
|
now_priority() ->
|
||||||
-erlang:system_time(microsecond).
|
-erlang:system_time(microsecond).
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(captcha_cmd) ->
|
|
||||||
fun (FileName) ->
|
|
||||||
F = iolist_to_binary(FileName), if F /= <<"">> -> F end
|
|
||||||
end;
|
|
||||||
opt_type(captcha_host) -> fun iolist_to_binary/1;
|
|
||||||
opt_type(captcha_limit) ->
|
|
||||||
fun (I) when is_integer(I), I > 0 -> I end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[captcha_cmd, captcha_host, captcha_limit].
|
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
%%%
|
%%%
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(ejabberd_cluster).
|
-module(ejabberd_cluster).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
@ -33,7 +32,6 @@
|
|||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
terminate/2, code_change/3]).
|
terminate/2, code_change/3]).
|
||||||
-export([opt_type/1]).
|
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
@ -154,9 +152,9 @@ subscribe(Proc) ->
|
|||||||
%%% gen_server API
|
%%% gen_server API
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
init([]) ->
|
init([]) ->
|
||||||
Ticktime = ejabberd_config:get_option(net_ticktime, 60),
|
Ticktime = ejabberd_option:net_ticktime(),
|
||||||
Nodes = ejabberd_config:get_option(cluster_nodes, []),
|
Nodes = ejabberd_option:cluster_nodes(),
|
||||||
net_kernel:set_net_ticktime(Ticktime),
|
_ = net_kernel:set_net_ticktime(Ticktime),
|
||||||
lists:foreach(fun(Node) ->
|
lists:foreach(fun(Node) ->
|
||||||
net_kernel:connect_node(Node)
|
net_kernel:connect_node(Node)
|
||||||
end, Nodes),
|
end, Nodes),
|
||||||
@ -195,19 +193,8 @@ code_change(_OldVsn, State, _Extra) ->
|
|||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
get_mod() ->
|
get_mod() ->
|
||||||
Backend = ejabberd_config:get_option(cluster_backend, mnesia),
|
Backend = ejabberd_option:cluster_backend(),
|
||||||
list_to_atom("ejabberd_cluster_" ++ atom_to_list(Backend)).
|
list_to_atom("ejabberd_cluster_" ++ atom_to_list(Backend)).
|
||||||
|
|
||||||
rpc_timeout() ->
|
rpc_timeout() ->
|
||||||
timer:seconds(ejabberd_config:get_option(rpc_timeout, 5)).
|
ejabberd_option:rpc_timeout().
|
||||||
|
|
||||||
opt_type(net_ticktime) ->
|
|
||||||
fun (P) when is_integer(P), P > 0 -> P end;
|
|
||||||
opt_type(cluster_nodes) ->
|
|
||||||
fun (Ns) -> true = lists:all(fun is_atom/1, Ns), Ns end;
|
|
||||||
opt_type(rpc_timeout) ->
|
|
||||||
fun (T) when is_integer(T), T > 0 -> T end;
|
|
||||||
opt_type(cluster_backend) ->
|
|
||||||
fun (T) -> ejabberd_config:v_db(?MODULE, T) end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[rpc_timeout, cluster_backend, cluster_nodes, net_ticktime].
|
|
||||||
|
@ -211,7 +211,6 @@
|
|||||||
-author('badlop@process-one.net').
|
-author('badlop@process-one.net').
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-define(DEFAULT_VERSION, 1000000).
|
-define(DEFAULT_VERSION, 1000000).
|
||||||
|
|
||||||
@ -225,11 +224,8 @@
|
|||||||
get_command_definition/2,
|
get_command_definition/2,
|
||||||
get_tags_commands/0,
|
get_tags_commands/0,
|
||||||
get_tags_commands/1,
|
get_tags_commands/1,
|
||||||
get_exposed_commands/0,
|
|
||||||
register_commands/1,
|
register_commands/1,
|
||||||
unregister_commands/1,
|
unregister_commands/1,
|
||||||
expose_commands/1,
|
|
||||||
opt_type/1,
|
|
||||||
get_commands_spec/0,
|
get_commands_spec/0,
|
||||||
get_commands_definition/0,
|
get_commands_definition/0,
|
||||||
get_commands_definition/1,
|
get_commands_definition/1,
|
||||||
@ -245,6 +241,8 @@
|
|||||||
|
|
||||||
-define(POLICY_ACCESS, '$policy').
|
-define(POLICY_ACCESS, '$policy').
|
||||||
|
|
||||||
|
-type auth() :: {binary(), binary(), binary() | {oauth, binary()}, boolean()} | map().
|
||||||
|
|
||||||
-record(state, {}).
|
-record(state, {}).
|
||||||
|
|
||||||
get_commands_spec() ->
|
get_commands_spec() ->
|
||||||
@ -292,7 +290,6 @@ init([]) ->
|
|||||||
{attributes, record_info(fields, ejabberd_commands)},
|
{attributes, record_info(fields, ejabberd_commands)},
|
||||||
{type, bag}]),
|
{type, bag}]),
|
||||||
register_commands(get_commands_spec()),
|
register_commands(get_commands_spec()),
|
||||||
ejabberd_access_permissions:register_permission_addon(?MODULE, fun permission_addon/0),
|
|
||||||
{ok, #state{}}.
|
{ok, #state{}}.
|
||||||
|
|
||||||
handle_call(_Request, _From, State) ->
|
handle_call(_Request, _From, State) ->
|
||||||
@ -338,29 +335,7 @@ unregister_commands(Commands) ->
|
|||||||
mnesia:dirty_delete_object(Command)
|
mnesia:dirty_delete_object(Command)
|
||||||
end,
|
end,
|
||||||
Commands),
|
Commands),
|
||||||
ejabberd_access_permissions:invalidate(),
|
ejabberd_access_permissions:invalidate().
|
||||||
ok.
|
|
||||||
|
|
||||||
%% @doc Expose command through ejabberd ReST API.
|
|
||||||
%% Pass a list of command names or policy to expose.
|
|
||||||
-spec expose_commands([ejabberd_commands()|atom()|open|user|admin|restricted]) -> ok | {error, atom()}.
|
|
||||||
|
|
||||||
expose_commands(Commands) ->
|
|
||||||
Names = lists:map(fun(#ejabberd_commands{name = Name}) ->
|
|
||||||
Name;
|
|
||||||
(Name) when is_atom(Name) ->
|
|
||||||
Name
|
|
||||||
end,
|
|
||||||
Commands),
|
|
||||||
|
|
||||||
case ejabberd_config:add_option(commands, [{add_commands, Names}]) of
|
|
||||||
ok ->
|
|
||||||
ok;
|
|
||||||
{aborted, Reason} ->
|
|
||||||
{error, Reason};
|
|
||||||
{atomic, Result} ->
|
|
||||||
Result
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec list_commands() -> [{atom(), [aterm()], string()}].
|
-spec list_commands() -> [{atom(), [aterm()], string()}].
|
||||||
|
|
||||||
@ -378,20 +353,6 @@ list_commands(Version) ->
|
|||||||
args = Args,
|
args = Args,
|
||||||
desc = Desc} <- Commands].
|
desc = Desc} <- Commands].
|
||||||
|
|
||||||
|
|
||||||
-spec list_commands_policy(integer()) ->
|
|
||||||
[{atom(), [aterm()], string(), atom()}].
|
|
||||||
|
|
||||||
%% @doc Get a list of all the available commands, arguments,
|
|
||||||
%% description, and policy in a given API version.
|
|
||||||
list_commands_policy(Version) ->
|
|
||||||
Commands = get_commands_definition(Version),
|
|
||||||
[{Name, Args, Desc, Policy} ||
|
|
||||||
#ejabberd_commands{name = Name,
|
|
||||||
args = Args,
|
|
||||||
desc = Desc,
|
|
||||||
policy = Policy} <- Commands].
|
|
||||||
|
|
||||||
-spec get_command_format(atom()) -> {[aterm()], rterm()}.
|
-spec get_command_format(atom()) -> {[aterm()], rterm()}.
|
||||||
|
|
||||||
%% @doc Get the format of arguments and result of a command.
|
%% @doc Get the format of arguments and result of a command.
|
||||||
@ -402,12 +363,7 @@ get_command_format(Name, Version) when is_integer(Version) ->
|
|||||||
get_command_format(Name, Auth) ->
|
get_command_format(Name, Auth) ->
|
||||||
get_command_format(Name, Auth, ?DEFAULT_VERSION).
|
get_command_format(Name, Auth, ?DEFAULT_VERSION).
|
||||||
|
|
||||||
-spec get_command_format(atom(),
|
-spec get_command_format(atom(), noauth | admin | auth(), integer()) -> {[aterm()], rterm()}.
|
||||||
{binary(), binary(), binary(), boolean()} |
|
|
||||||
noauth | admin,
|
|
||||||
integer()) ->
|
|
||||||
{[aterm()], rterm()}.
|
|
||||||
|
|
||||||
get_command_format(Name, Auth, Version) ->
|
get_command_format(Name, Auth, Version) ->
|
||||||
Admin = is_admin(Name, Auth, #{}),
|
Admin = is_admin(Name, Auth, #{}),
|
||||||
#ejabberd_commands{args = Args,
|
#ejabberd_commands{args = Args,
|
||||||
@ -422,12 +378,6 @@ get_command_format(Name, Auth, Version) ->
|
|||||||
{Args, Result}
|
{Args, Result}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% The oauth scopes for a command are the command name itself,
|
|
||||||
%% also might include either 'ejabberd:user' or 'ejabberd:admin'
|
|
||||||
cmd_scope(#ejabberd_commands{policy = Policy, name = Name}) ->
|
|
||||||
[erlang:atom_to_binary(Name,utf8)] ++ [<<"ejabberd:user">> || Policy == user] ++ [<<"ejabberd:admin">> || Policy == admin].
|
|
||||||
|
|
||||||
|
|
||||||
-spec get_command_definition(atom()) -> ejabberd_commands().
|
-spec get_command_definition(atom()) -> ejabberd_commands().
|
||||||
|
|
||||||
%% @doc Get the definition record of a command.
|
%% @doc Get the definition record of a command.
|
||||||
@ -533,95 +483,12 @@ get_tags_commands(Version) ->
|
|||||||
%% -----------------------------
|
%% -----------------------------
|
||||||
%% Access verification
|
%% Access verification
|
||||||
%% -----------------------------
|
%% -----------------------------
|
||||||
|
-spec is_admin(atom(), admin | noauth | auth(), map()) -> boolean().
|
||||||
-spec check_auth(ejabberd_commands(), noauth) -> noauth_provided;
|
|
||||||
(ejabberd_commands(),
|
|
||||||
{binary(), binary(), binary(), boolean()}) ->
|
|
||||||
{ok, binary(), binary()}.
|
|
||||||
|
|
||||||
check_auth(_Command, noauth) ->
|
|
||||||
no_auth_provided;
|
|
||||||
check_auth(Command, {User, Server, {oauth, Token}, _}) ->
|
|
||||||
ScopeList = cmd_scope(Command),
|
|
||||||
case ejabberd_oauth:check_token(User, Server, ScopeList, Token) of
|
|
||||||
true ->
|
|
||||||
{ok, User, Server};
|
|
||||||
_ ->
|
|
||||||
throw({error, invalid_account_data})
|
|
||||||
end;
|
|
||||||
check_auth(_Command, {User, Server, Password, _}) when is_binary(Password) ->
|
|
||||||
%% Check the account exists and password is valid
|
|
||||||
case ejabberd_auth:check_password(User, <<"">>, Server, Password) of
|
|
||||||
true -> {ok, User, Server};
|
|
||||||
_ -> throw({error, invalid_account_data})
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_exposed_commands() ->
|
|
||||||
get_exposed_commands(?DEFAULT_VERSION).
|
|
||||||
get_exposed_commands(Version) ->
|
|
||||||
Opts0 = ejabberd_config:get_option(commands, []),
|
|
||||||
Opts = lists:map(fun(V) when is_tuple(V) -> [V]; (V) -> V end, Opts0),
|
|
||||||
CommandsList = list_commands_policy(Version),
|
|
||||||
OpenCmds = [N || {N, _, _, open} <- CommandsList],
|
|
||||||
RestrictedCmds = [N || {N, _, _, restricted} <- CommandsList],
|
|
||||||
AdminCmds = [N || {N, _, _, admin} <- CommandsList],
|
|
||||||
UserCmds = [N || {N, _, _, user} <- CommandsList],
|
|
||||||
Cmds =
|
|
||||||
lists:foldl(
|
|
||||||
fun([{add_commands, L}], Acc) ->
|
|
||||||
Cmds = expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds),
|
|
||||||
lists:usort(Cmds ++ Acc);
|
|
||||||
([{remove_commands, L}], Acc) ->
|
|
||||||
Cmds = expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds),
|
|
||||||
Acc -- Cmds;
|
|
||||||
(_, Acc) -> Acc
|
|
||||||
end, [], Opts),
|
|
||||||
Cmds.
|
|
||||||
|
|
||||||
%% This is used to allow mixing command policy (like open, user, admin, restricted), with command entry
|
|
||||||
expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds) when is_atom(L) ->
|
|
||||||
expand_commands([L], OpenCmds, UserCmds, AdminCmds, RestrictedCmds);
|
|
||||||
expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds) when is_list(L) ->
|
|
||||||
lists:foldl(fun(open, Acc) -> OpenCmds ++ Acc;
|
|
||||||
(user, Acc) -> UserCmds ++ Acc;
|
|
||||||
(admin, Acc) -> AdminCmds ++ Acc;
|
|
||||||
(restricted, Acc) -> RestrictedCmds ++ Acc;
|
|
||||||
(Command, Acc) when is_atom(Command) ->
|
|
||||||
[Command|Acc]
|
|
||||||
end, [], L).
|
|
||||||
|
|
||||||
is_admin(_Name, admin, _Extra) ->
|
is_admin(_Name, admin, _Extra) ->
|
||||||
true;
|
true;
|
||||||
is_admin(_Name, {_User, _Server, _, false}, _Extra) ->
|
is_admin(_Name, {_User, _Server, _, false}, _Extra) ->
|
||||||
false;
|
false;
|
||||||
is_admin(_Name, Map, _extra) when is_map(Map) ->
|
is_admin(_Name, Map, _extra) when is_map(Map) ->
|
||||||
true;
|
true;
|
||||||
is_admin(Name, Auth, Extra) ->
|
is_admin(_Name, _Auth, _Extra) ->
|
||||||
{ACLInfo, Server} = case Auth of
|
false.
|
||||||
{U, S, _, _} ->
|
|
||||||
{Extra#{usr=>jid:split(jid:make(U, S))}, S};
|
|
||||||
_ ->
|
|
||||||
{Extra, global}
|
|
||||||
end,
|
|
||||||
AdminAccess = ejabberd_config:get_option(commands_admin_access, none),
|
|
||||||
case acl:access_matches(AdminAccess, ACLInfo, Server) of
|
|
||||||
allow ->
|
|
||||||
case catch check_auth(get_command_definition(Name), Auth) of
|
|
||||||
{ok, _, _} -> true;
|
|
||||||
no_auth_provided -> true;
|
|
||||||
_ -> false
|
|
||||||
end;
|
|
||||||
deny -> false
|
|
||||||
end.
|
|
||||||
|
|
||||||
permission_addon() ->
|
|
||||||
[{<<"'commands' option compatibility shim">>,
|
|
||||||
{[],
|
|
||||||
[{access, ejabberd_config:get_option(commands_admin_access, none)}],
|
|
||||||
{get_exposed_commands(), []}}}].
|
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(commands_admin_access) -> fun acl:access_rules_validator/1;
|
|
||||||
opt_type(commands) ->
|
|
||||||
fun(V) when is_list(V) -> V end;
|
|
||||||
opt_type(_) -> [commands, commands_admin_access].
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
517
src/ejabberd_config_transformer.erl
Normal file
517
src/ejabberd_config_transformer.erl
Normal file
@ -0,0 +1,517 @@
|
|||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% 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_config_transformer).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([map_reduce/1]).
|
||||||
|
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
map_reduce(Y) ->
|
||||||
|
F = fun(Y1) ->
|
||||||
|
Y2 = (validator())(Y1),
|
||||||
|
Y3 = transform(Y2),
|
||||||
|
if Y2 /= Y3 ->
|
||||||
|
?DEBUG("Transformed configuration:~s~n",
|
||||||
|
[misc:format_val({yaml, Y3})]);
|
||||||
|
true ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
Y3
|
||||||
|
end,
|
||||||
|
econf:validate(F, Y).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Transformer
|
||||||
|
%%%===================================================================
|
||||||
|
transform(Y) ->
|
||||||
|
{Y1, Acc1} = transform(global, Y, #{}),
|
||||||
|
{Y2, Acc2} = update(Y1, Acc1),
|
||||||
|
filter(global, Y2, Acc2).
|
||||||
|
|
||||||
|
transform(Host, Y, Acc) ->
|
||||||
|
filtermapfoldr(
|
||||||
|
fun({Opt, HostOpts}, Acc1) when (Opt == host_config orelse
|
||||||
|
Opt == append_host_config)
|
||||||
|
andalso Host == global ->
|
||||||
|
case filtermapfoldr(
|
||||||
|
fun({Host1, Opts}, Acc2) ->
|
||||||
|
case transform(Host1, Opts, Acc2) of
|
||||||
|
{[], Acc3} ->
|
||||||
|
{false, Acc3};
|
||||||
|
{Opts1, Acc3} ->
|
||||||
|
{{true, {Host1, Opts1}}, Acc3}
|
||||||
|
end
|
||||||
|
end, Acc1, HostOpts) of
|
||||||
|
{[], Acc4} ->
|
||||||
|
{false, Acc4};
|
||||||
|
{HostOpts1, Acc4} ->
|
||||||
|
{{true, {Opt, HostOpts1}}, Acc4}
|
||||||
|
end;
|
||||||
|
({Opt, Val}, Acc1) ->
|
||||||
|
transform(Host, Opt, Val, Acc1)
|
||||||
|
end, Acc, Y).
|
||||||
|
|
||||||
|
transform(Host, modules, ModOpts, Acc) ->
|
||||||
|
{ModOpts1, Acc2} =
|
||||||
|
lists:mapfoldr(
|
||||||
|
fun({Mod, Opts}, Acc1) ->
|
||||||
|
Opts1 = transform_module_options(Opts),
|
||||||
|
transform_module(Host, Mod, Opts1, Acc1)
|
||||||
|
end, Acc, ModOpts),
|
||||||
|
{{true, {modules, ModOpts1}}, Acc2};
|
||||||
|
transform(global, listen, Listeners, Acc) ->
|
||||||
|
{Listeners1, Acc2} =
|
||||||
|
lists:mapfoldr(
|
||||||
|
fun(Opts, Acc1) ->
|
||||||
|
transform_listener(Opts, Acc1)
|
||||||
|
end, Acc, Listeners),
|
||||||
|
{{true, {listen, Listeners1}}, Acc2};
|
||||||
|
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",
|
||||||
|
[Opt, adjust_hint()]),
|
||||||
|
CertFiles = maps:get(certfiles, Acc, []),
|
||||||
|
Acc1 = maps:put(certfiles, CertFiles ++ [CertFile], Acc),
|
||||||
|
{false, Acc1};
|
||||||
|
transform(_Host, certfiles, CertFiles1, Acc) ->
|
||||||
|
CertFiles2 = maps:get(certfiles, Acc, []),
|
||||||
|
Acc1 = maps:put(certfiles, CertFiles1 ++ CertFiles2, Acc),
|
||||||
|
{true, Acc1};
|
||||||
|
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",
|
||||||
|
[adjust_hint()]),
|
||||||
|
Hosts = maps:get(remove_s2s_dialback, Acc, []),
|
||||||
|
Acc1 = maps:put(remove_s2s_dialback, [Host|Hosts], Acc),
|
||||||
|
{{true, {s2s_use_starttls, required}}, Acc1};
|
||||||
|
transform(_Host, _Opt, _Val, Acc) ->
|
||||||
|
{true, Acc}.
|
||||||
|
|
||||||
|
update(Y, Acc) ->
|
||||||
|
set_certfiles(Y, Acc).
|
||||||
|
|
||||||
|
filter(Host, Y, Acc) ->
|
||||||
|
lists:filtermap(
|
||||||
|
fun({Opt, HostOpts}) when (Opt == host_config orelse
|
||||||
|
Opt == append_host_config)
|
||||||
|
andalso Host == global ->
|
||||||
|
HostOpts1 = lists:map(
|
||||||
|
fun({Host1, Opts1}) ->
|
||||||
|
{Host1, filter(Host1, Opts1, Acc)}
|
||||||
|
end, HostOpts),
|
||||||
|
{true, {Opt, HostOpts1}};
|
||||||
|
({Opt, Val}) ->
|
||||||
|
filter(Host, Opt, Val, Acc)
|
||||||
|
end, Y).
|
||||||
|
|
||||||
|
filter(_Host, ca_path, _, _) ->
|
||||||
|
warn_removed_option(ca_path, ca_file),
|
||||||
|
false;
|
||||||
|
filter(_Host, iqdisc, _, _) ->
|
||||||
|
warn_removed_option(iqdisc),
|
||||||
|
false;
|
||||||
|
filter(_Host, access, _, _) ->
|
||||||
|
warn_removed_option(access, access_rules),
|
||||||
|
false;
|
||||||
|
filter(_Host, commands, _, _) ->
|
||||||
|
warn_removed_option(commands, api_permissions),
|
||||||
|
false;
|
||||||
|
filter(_Host, ejabberdctl_access_commands, _, _) ->
|
||||||
|
warn_removed_option(ejabberdctl_access_commands, api_permissions),
|
||||||
|
false;
|
||||||
|
filter(_Host, commands_admin_access, _, _) ->
|
||||||
|
warn_removed_option(commands_admin_access, api_permissions),
|
||||||
|
false;
|
||||||
|
filter(_Host, ldap_group_cache_size, _, _) ->
|
||||||
|
warn_removed_option(ldap_group_cache_size, cache_size),
|
||||||
|
false;
|
||||||
|
filter(_Host, ldap_user_cache_size, _, _) ->
|
||||||
|
warn_removed_option(ldap_user_cache_size, cache_size),
|
||||||
|
false;
|
||||||
|
filter(_Host, ldap_group_cache_validity, _, _) ->
|
||||||
|
warn_removed_option(ldap_group_cache_validity, cache_life_time),
|
||||||
|
false;
|
||||||
|
filter(_Host, ldap_user_cache_validity, _, _) ->
|
||||||
|
warn_removed_option(ldap_user_cache_validity, cache_life_time),
|
||||||
|
false;
|
||||||
|
filter(_Host, ldap_local_filter, _, _) ->
|
||||||
|
warn_removed_option(ldap_local_filter),
|
||||||
|
false;
|
||||||
|
filter(_Host, deref_aliases, Val, _) ->
|
||||||
|
warn_replaced_option(deref_aliases, ldap_deref_aliases),
|
||||||
|
{true, {ldap_deref_aliases, Val}};
|
||||||
|
filter(_Host, default_db, internal, _) ->
|
||||||
|
{true, {default_db, mnesia}};
|
||||||
|
filter(_Host, default_db, odbc, _) ->
|
||||||
|
{true, {default_db, sql}};
|
||||||
|
filter(_Host, auth_method, Ms, _) ->
|
||||||
|
Ms1 = lists:map(
|
||||||
|
fun(internal) -> mnesia;
|
||||||
|
(odbc) -> sql;
|
||||||
|
(M) -> M
|
||||||
|
end, Ms),
|
||||||
|
{true, {auth_method, Ms1}};
|
||||||
|
filter(_Host, default_ram_db, internal, _) ->
|
||||||
|
{true, {default_ram_db, mnesia}};
|
||||||
|
filter(_Host, default_ram_db, odbc, _) ->
|
||||||
|
{true, {default_ram_db, sql}};
|
||||||
|
filter(_Host, extauth_cache, _, _) ->
|
||||||
|
?WARNING_MSG("Option 'extauth_cache' is deprecated "
|
||||||
|
"and has no effect, use authentication "
|
||||||
|
"or global cache configuration options: "
|
||||||
|
"auth_use_cache, auth_cache_life_time, "
|
||||||
|
"use_cache, cache_life_time, and so on", []),
|
||||||
|
false;
|
||||||
|
filter(_Host, extauth_instances, Val, _) ->
|
||||||
|
warn_replaced_option(extauth_instances, extauth_pool_size),
|
||||||
|
{true, {extauth_pool_size, Val}};
|
||||||
|
filter(_Host, Opt, Val, _) when Opt == outgoing_s2s_timeout;
|
||||||
|
Opt == s2s_dns_timeout ->
|
||||||
|
warn_huge_timeout(Opt, Val),
|
||||||
|
true;
|
||||||
|
filter(Host, modules, ModOpts, #{remove_s2s_dialback := Hosts}) ->
|
||||||
|
ModOpts1 = case lists:member(Host, Hosts) of
|
||||||
|
true ->
|
||||||
|
lists:filter(
|
||||||
|
fun({mod_s2s_dialback, _}) -> false;
|
||||||
|
(_) -> true
|
||||||
|
end, ModOpts);
|
||||||
|
false ->
|
||||||
|
ModOpts
|
||||||
|
end,
|
||||||
|
{true, {modules, ModOpts1}};
|
||||||
|
filter(_, _, _, _) ->
|
||||||
|
true.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Listener transformers
|
||||||
|
%%%===================================================================
|
||||||
|
transform_listener(Opts, Acc) ->
|
||||||
|
Opts1 = transform_request_handlers(Opts),
|
||||||
|
Opts2 = remove_inet_options(Opts1),
|
||||||
|
collect_listener_certfiles(Opts2, Acc).
|
||||||
|
|
||||||
|
transform_request_handlers(Opts) ->
|
||||||
|
case lists:keyfind(module, 1, Opts) of
|
||||||
|
{_, ejabberd_http} ->
|
||||||
|
replace_request_handlers(Opts);
|
||||||
|
_ ->
|
||||||
|
Opts
|
||||||
|
end.
|
||||||
|
|
||||||
|
replace_request_handlers(Opts) ->
|
||||||
|
Handlers = proplists:get_value(request_handlers, Opts, []),
|
||||||
|
Handlers1 =
|
||||||
|
lists:foldl(
|
||||||
|
fun({captcha, true}, Acc) ->
|
||||||
|
Handler = {<<"/captcha">>, ejabberd_captcha},
|
||||||
|
warn_replaced_handler(captcha, Handler),
|
||||||
|
[Handler|Acc];
|
||||||
|
({register, true}, Acc) ->
|
||||||
|
Handler = {<<"/register">>, mod_register_web},
|
||||||
|
warn_replaced_handler(register, Handler),
|
||||||
|
[Handler|Acc];
|
||||||
|
({web_admin, true}, Acc) ->
|
||||||
|
Handler = {<<"/admin">>, ejabberd_web_admin},
|
||||||
|
warn_replaced_handler(web_admin, Handler),
|
||||||
|
[Handler|Acc];
|
||||||
|
({http_bind, true}, Acc) ->
|
||||||
|
Handler = {<<"/bosh">>, mod_bosh},
|
||||||
|
warn_replaced_handler(http_bind, Handler),
|
||||||
|
[Handler|Acc];
|
||||||
|
({xmlrpc, true}, Acc) ->
|
||||||
|
Handler = {<<"/">>, ejabberd_xmlrpc},
|
||||||
|
warn_replaced_handler(xmlrpc, Handler),
|
||||||
|
Acc ++ [Handler];
|
||||||
|
(_, Acc) ->
|
||||||
|
Acc
|
||||||
|
end, Handlers, Opts),
|
||||||
|
Handlers2 = lists:map(
|
||||||
|
fun({Path, mod_http_bind}) ->
|
||||||
|
warn_replaced_module(mod_http_bind, mod_bosh),
|
||||||
|
{Path, mod_bosh};
|
||||||
|
(PathMod) ->
|
||||||
|
PathMod
|
||||||
|
end, Handlers1),
|
||||||
|
lists:filtermap(
|
||||||
|
fun({captcha, _}) -> false;
|
||||||
|
({register, _}) -> false;
|
||||||
|
({web_admin, _}) -> false;
|
||||||
|
({http_bind, _}) -> false;
|
||||||
|
({xmlrpc, _}) -> false;
|
||||||
|
({http_poll, _}) ->
|
||||||
|
?WARNING_MSG("Listening option 'http_poll' is "
|
||||||
|
"ignored: HTTP Polling support was "
|
||||||
|
"removed in ejabberd 15.04. ~s",
|
||||||
|
[adjust_hint()]),
|
||||||
|
false;
|
||||||
|
({request_handlers, _}) ->
|
||||||
|
{true, {request_handlers, Handlers2}};
|
||||||
|
(_) -> true
|
||||||
|
end, Opts).
|
||||||
|
|
||||||
|
remove_inet_options(Opts) ->
|
||||||
|
lists:filter(
|
||||||
|
fun({Opt, _}) when Opt == inet; Opt == inet6 ->
|
||||||
|
warn_removed_option(Opt, ip),
|
||||||
|
false;
|
||||||
|
(_) ->
|
||||||
|
true
|
||||||
|
end, Opts).
|
||||||
|
|
||||||
|
collect_listener_certfiles(Opts, Acc) ->
|
||||||
|
Mod = proplists:get_value(module, Opts),
|
||||||
|
if Mod == ejabberd_http;
|
||||||
|
Mod == ejabberd_c2s;
|
||||||
|
Mod == ejabberd_s2s_in ->
|
||||||
|
case lists:keyfind(certfile, 1, Opts) of
|
||||||
|
{_, CertFile} ->
|
||||||
|
?WARNING_MSG("Listening option 'certfile' of module ~s "
|
||||||
|
"is deprecated and was automatically "
|
||||||
|
"appended to global 'certfiles' option. ~s",
|
||||||
|
[Mod, adjust_hint()]),
|
||||||
|
CertFiles = maps:get(certfiles, Acc, []),
|
||||||
|
{proplists:delete(certfile, Opts),
|
||||||
|
maps:put(certfiles, [CertFile|CertFiles], Acc)};
|
||||||
|
false ->
|
||||||
|
{Opts, Acc}
|
||||||
|
end;
|
||||||
|
true ->
|
||||||
|
{Opts, Acc}
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Module transformers
|
||||||
|
%%% NOTE: transform_module_options/1 is called before transform_module/4
|
||||||
|
%%%===================================================================
|
||||||
|
transform_module_options(Opts) ->
|
||||||
|
lists:filtermap(
|
||||||
|
fun({Opt, internal}) when Opt == db_type;
|
||||||
|
Opt == ram_db_type ->
|
||||||
|
{true, {Opt, mnesia}};
|
||||||
|
({Opt, odbc}) when Opt == db_type;
|
||||||
|
Opt == ram_db_type ->
|
||||||
|
{true, {Opt, sql}};
|
||||||
|
({deref_aliases, Val}) ->
|
||||||
|
warn_replaced_option(deref_aliases, ldap_deref_aliases),
|
||||||
|
{true, {ldap_deref_aliases, Val}};
|
||||||
|
({ldap_group_cache_size, _}) ->
|
||||||
|
warn_removed_option(ldap_group_cache_size, cache_size),
|
||||||
|
false;
|
||||||
|
({ldap_user_cache_size, _}) ->
|
||||||
|
warn_removed_option(ldap_user_cache_size, cache_size),
|
||||||
|
false;
|
||||||
|
({ldap_group_cache_validity, _}) ->
|
||||||
|
warn_removed_option(ldap_group_cache_validity, cache_life_time),
|
||||||
|
false;
|
||||||
|
({ldap_user_cache_validity, _}) ->
|
||||||
|
warn_removed_option(ldap_user_cache_validity, cache_life_time),
|
||||||
|
false;
|
||||||
|
({iqdisc, _}) ->
|
||||||
|
warn_removed_option(iqdisc),
|
||||||
|
false;
|
||||||
|
(_) ->
|
||||||
|
true
|
||||||
|
end, Opts).
|
||||||
|
|
||||||
|
transform_module(_Host, mod_http_bind, Opts, Acc) ->
|
||||||
|
warn_replaced_module(mod_http_bind, mod_bosh),
|
||||||
|
{{mod_bosh, Opts}, Acc};
|
||||||
|
transform_module(_Host, mod_vcard_xupdate_odbc, Opts, Acc) ->
|
||||||
|
warn_replaced_module(mod_vcard_xupdate_odbc, mod_vcard_xupdate),
|
||||||
|
{{mod_vcard_xupdate, Opts}, Acc};
|
||||||
|
transform_module(_Host, mod_vcard_ldap, Opts, Acc) ->
|
||||||
|
warn_replaced_module(mod_vcard_ldap, mod_vcard, ldap),
|
||||||
|
{{mod_vcard, [{db_type, ldap}|Opts]}, Acc};
|
||||||
|
transform_module(_Host, M, Opts, Acc) when (M == mod_announce_odbc orelse
|
||||||
|
M == mod_blocking_odbc orelse
|
||||||
|
M == mod_caps_odbc orelse
|
||||||
|
M == mod_last_odbc orelse
|
||||||
|
M == mod_muc_odbc orelse
|
||||||
|
M == mod_offline_odbc orelse
|
||||||
|
M == mod_privacy_odbc orelse
|
||||||
|
M == mod_private_odbc orelse
|
||||||
|
M == mod_pubsub_odbc orelse
|
||||||
|
M == mod_roster_odbc orelse
|
||||||
|
M == mod_shared_roster_odbc orelse
|
||||||
|
M == mod_vcard_odbc) ->
|
||||||
|
M1 = strip_odbc_suffix(M),
|
||||||
|
warn_replaced_module(M, M1, sql),
|
||||||
|
{{M1, [{db_type, sql}|Opts]}, Acc};
|
||||||
|
transform_module(_Host, mod_blocking, Opts, Acc) ->
|
||||||
|
Opts1 = lists:filter(
|
||||||
|
fun({db_type, _}) ->
|
||||||
|
warn_removed_module_option(db_type, mod_blocking),
|
||||||
|
false;
|
||||||
|
(_) ->
|
||||||
|
true
|
||||||
|
end, Opts),
|
||||||
|
{{mod_blocking, Opts1}, Acc};
|
||||||
|
transform_module(_Host, mod_carboncopy, Opts, Acc) ->
|
||||||
|
Opts1 = lists:filter(
|
||||||
|
fun({Opt, _}) when Opt == ram_db_type;
|
||||||
|
Opt == use_cache;
|
||||||
|
Opt == cache_size;
|
||||||
|
Opt == cache_missed;
|
||||||
|
Opt == cache_life_time ->
|
||||||
|
warn_removed_module_option(Opt, mod_carboncopy),
|
||||||
|
false;
|
||||||
|
(_) ->
|
||||||
|
true
|
||||||
|
end, Opts),
|
||||||
|
{{mod_carboncopy, Opts1}, Acc};
|
||||||
|
transform_module(_Host, mod_http_api, Opts, Acc) ->
|
||||||
|
Opts1 = lists:filter(
|
||||||
|
fun({admin_ip_access, _}) ->
|
||||||
|
warn_removed_option(admin_ip_access, api_permissions),
|
||||||
|
false;
|
||||||
|
(_) ->
|
||||||
|
true
|
||||||
|
end, Opts),
|
||||||
|
{{mod_http_api, Opts1}, Acc};
|
||||||
|
transform_module(_Host, Mod, Opts, Acc) ->
|
||||||
|
{{Mod, Opts}, Acc}.
|
||||||
|
|
||||||
|
strip_odbc_suffix(M) ->
|
||||||
|
[_|T] = lists:reverse(string:tokens(atom_to_list(M), "_")),
|
||||||
|
list_to_atom(string:join(lists:reverse(T), "_")).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Aux
|
||||||
|
%%%===================================================================
|
||||||
|
filtermapfoldr(Fun, Init, List) ->
|
||||||
|
lists:foldr(
|
||||||
|
fun(X, {Ret, Acc}) ->
|
||||||
|
case Fun(X, Acc) of
|
||||||
|
{true, Acc1} -> {[X|Ret], Acc1};
|
||||||
|
{{true, X1}, Acc1} -> {[X1|Ret], Acc1};
|
||||||
|
{false, Acc1} -> {Ret, Acc1}
|
||||||
|
end
|
||||||
|
end, {[], Init}, List).
|
||||||
|
|
||||||
|
set_certfiles(Y, #{certfiles := CertFiles} = Acc) ->
|
||||||
|
{lists:keystore(certfiles, 1, Y, {certfiles, CertFiles}), Acc};
|
||||||
|
set_certfiles(Y, Acc) ->
|
||||||
|
{Y, Acc}.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Warnings
|
||||||
|
%%%===================================================================
|
||||||
|
warn_replaced_module(From, To) ->
|
||||||
|
?WARNING_MSG("Module ~s is deprecated and was automatically "
|
||||||
|
"replaced by ~s. ~s",
|
||||||
|
[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",
|
||||||
|
[From, To, Type, adjust_hint()]).
|
||||||
|
|
||||||
|
warn_replaced_handler(Opt, {Path, Module}) ->
|
||||||
|
?WARNING_MSG("Listening option '~s' is deprecated "
|
||||||
|
"and was automatically replaced by "
|
||||||
|
"HTTP request handler: \"~s\" -> ~s. ~s",
|
||||||
|
[Opt, Path, Module, adjust_hint()]).
|
||||||
|
|
||||||
|
warn_replaced_option(OldOpt, NewOpt) ->
|
||||||
|
?WARNING_MSG("Option '~s' is deprecated and was automatically "
|
||||||
|
"replaced by '~s'. ~s",
|
||||||
|
[OldOpt, NewOpt, adjust_hint()]).
|
||||||
|
|
||||||
|
warn_removed_option(Opt) ->
|
||||||
|
?WARNING_MSG("Option '~s' 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]).
|
||||||
|
|
||||||
|
warn_removed_module_option(Opt, Mod) ->
|
||||||
|
?WARNING_MSG("Option '~s' of module ~s is deprecated "
|
||||||
|
"and has no effect anymore. ~s",
|
||||||
|
[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, "
|
||||||
|
"are you sure you have set seconds?",
|
||||||
|
[T, Opt]);
|
||||||
|
warn_huge_timeout(_, _) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
adjust_hint() ->
|
||||||
|
"Please adjust your configuration accordingly. "
|
||||||
|
"Hint: use `ejabberdctl dump-config` command to view current "
|
||||||
|
"configuration as it is seen by ejabberd.".
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Very raw validator: just to make sure we get properly typed terms
|
||||||
|
%%% Expand it if you need to transform more options, but don't
|
||||||
|
%%% abuse complex types: simple and composite types are preferred
|
||||||
|
%%%===================================================================
|
||||||
|
validator() ->
|
||||||
|
Validators =
|
||||||
|
#{s2s_use_starttls => econf:atom(),
|
||||||
|
certfiles => econf:list(econf:any()),
|
||||||
|
c2s_certfile => econf:binary(),
|
||||||
|
s2s_certfile => econf:binary(),
|
||||||
|
domain_certfile => econf:binary(),
|
||||||
|
default_db => econf:atom(),
|
||||||
|
default_ram_db => econf:atom(),
|
||||||
|
auth_method => econf:list_or_single(econf:atom()),
|
||||||
|
listen =>
|
||||||
|
econf:list(
|
||||||
|
econf:options(
|
||||||
|
#{captcha => econf:bool(),
|
||||||
|
register => econf:bool(),
|
||||||
|
web_admin => econf:bool(),
|
||||||
|
http_bind => econf:bool(),
|
||||||
|
http_poll => econf:bool(),
|
||||||
|
xmlrpc => econf:bool(),
|
||||||
|
module => econf:atom(),
|
||||||
|
certfile => econf:binary(),
|
||||||
|
request_handlers =>
|
||||||
|
econf:map(econf:binary(), econf:atom()),
|
||||||
|
'_' => econf:any()},
|
||||||
|
[])),
|
||||||
|
modules =>
|
||||||
|
econf:options(
|
||||||
|
#{'_' =>
|
||||||
|
econf:options(
|
||||||
|
#{db_type => econf:atom(),
|
||||||
|
'_' => econf:any()},
|
||||||
|
[])},
|
||||||
|
[]),
|
||||||
|
'_' => econf:any()},
|
||||||
|
econf:options(
|
||||||
|
Validators#{host_config =>
|
||||||
|
econf:map(econf:binary(),
|
||||||
|
econf:options(Validators, [])),
|
||||||
|
append_host_config =>
|
||||||
|
econf:map(econf:binary(),
|
||||||
|
econf:options(Validators, []))},
|
||||||
|
[]).
|
@ -45,13 +45,11 @@
|
|||||||
|
|
||||||
-module(ejabberd_ctl).
|
-module(ejabberd_ctl).
|
||||||
|
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-export([start/0, start_link/0, process/1, process2/2,
|
-export([start/0, start_link/0, process/1, process2/2,
|
||||||
register_commands/3, unregister_commands/3,
|
register_commands/3, unregister_commands/3]).
|
||||||
opt_type/1]).
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
terminate/2, code_change/3]).
|
terminate/2, code_change/3]).
|
||||||
@ -177,7 +175,7 @@ process(["status"], _Version) ->
|
|||||||
"or other files in that directory.~n", [EjabberdLogPath]),
|
"or other files in that directory.~n", [EjabberdLogPath]),
|
||||||
?STATUS_ERROR;
|
?STATUS_ERROR;
|
||||||
true ->
|
true ->
|
||||||
print("ejabberd ~s is running in that node~n", [ejabberd_config:get_version()]),
|
print("ejabberd ~s is running in that node~n", [ejabberd_option:version()]),
|
||||||
?STATUS_SUCCESS
|
?STATUS_SUCCESS
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -248,8 +246,7 @@ process(["--version", Arg | Args], _) ->
|
|||||||
process(Args, Version);
|
process(Args, Version);
|
||||||
|
|
||||||
process(Args, Version) ->
|
process(Args, Version) ->
|
||||||
AccessCommands = get_accesscommands(),
|
{String, Code} = process2(Args, [], Version),
|
||||||
{String, Code} = process2(Args, AccessCommands, Version),
|
|
||||||
case String of
|
case String of
|
||||||
[] -> ok;
|
[] -> ok;
|
||||||
_ ->
|
_ ->
|
||||||
@ -291,9 +288,6 @@ process2(Args, AccessCommands, Auth, Version) ->
|
|||||||
{"Erroneous result: " ++ io_lib:format("~p", [Other]), ?STATUS_ERROR}
|
{"Erroneous result: " ++ io_lib:format("~p", [Other]), ?STATUS_ERROR}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_accesscommands() ->
|
|
||||||
ejabberd_config:get_option(ejabberdctl_access_commands, []).
|
|
||||||
|
|
||||||
%%-----------------------------
|
%%-----------------------------
|
||||||
%% Command calling
|
%% Command calling
|
||||||
%%-----------------------------
|
%%-----------------------------
|
||||||
@ -322,8 +316,8 @@ try_run_ctp(Args, Auth, AccessCommands, Version) ->
|
|||||||
%% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()}
|
%% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()}
|
||||||
try_call_command(Args, Auth, AccessCommands, Version) ->
|
try_call_command(Args, Auth, AccessCommands, Version) ->
|
||||||
try call_command(Args, Auth, AccessCommands, Version) of
|
try call_command(Args, Auth, AccessCommands, Version) of
|
||||||
{error, wrong_command_arguments} ->
|
{Reason, wrong_command_arguments} ->
|
||||||
{"Error: wrong arguments", ?STATUS_ERROR};
|
{Reason, ?STATUS_ERROR};
|
||||||
Res ->
|
Res ->
|
||||||
Res
|
Res
|
||||||
catch
|
catch
|
||||||
@ -346,10 +340,7 @@ call_command([CmdString | Args], Auth, _AccessCommands, Version) ->
|
|||||||
CmdStringU = ejabberd_regexp:greplace(
|
CmdStringU = ejabberd_regexp:greplace(
|
||||||
list_to_binary(CmdString), <<"-">>, <<"_">>),
|
list_to_binary(CmdString), <<"-">>, <<"_">>),
|
||||||
Command = list_to_atom(binary_to_list(CmdStringU)),
|
Command = list_to_atom(binary_to_list(CmdStringU)),
|
||||||
case ejabberd_commands:get_command_format(Command, Auth, Version) of
|
{ArgsFormat, ResultFormat} = ejabberd_commands:get_command_format(Command, Auth, Version),
|
||||||
{error, command_unknown} ->
|
|
||||||
throw({error, unknown_command});
|
|
||||||
{ArgsFormat, ResultFormat} ->
|
|
||||||
case (catch format_args(Args, ArgsFormat)) of
|
case (catch format_args(Args, ArgsFormat)) of
|
||||||
ArgsFormatted when is_list(ArgsFormatted) ->
|
ArgsFormatted when is_list(ArgsFormatted) ->
|
||||||
CI = case Auth of
|
CI = case Auth of
|
||||||
@ -371,7 +362,6 @@ call_command([CmdString | Args], Auth, _AccessCommands, Version) ->
|
|||||||
{io_lib:format("Error: the command ~p requires ~p ~s.",
|
{io_lib:format("Error: the command ~p requires ~p ~s.",
|
||||||
[CmdString, NumCompa, TextCompa]),
|
[CmdString, NumCompa, TextCompa]),
|
||||||
wrong_command_arguments}
|
wrong_command_arguments}
|
||||||
end
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
@ -735,11 +725,12 @@ print_usage_help(MaxC, ShCode) ->
|
|||||||
"Those commands can be identified because the description starts with: *"],
|
"Those commands can be identified because the description starts with: *"],
|
||||||
ArgsDef = [],
|
ArgsDef = [],
|
||||||
C = #ejabberd_commands{
|
C = #ejabberd_commands{
|
||||||
|
name = help,
|
||||||
desc = "Show help of ejabberd commands",
|
desc = "Show help of ejabberd commands",
|
||||||
longdesc = lists:flatten(LongDesc),
|
longdesc = lists:flatten(LongDesc),
|
||||||
args = ArgsDef,
|
args = ArgsDef,
|
||||||
result = {help, string}},
|
result = {help, string}},
|
||||||
print_usage_command("help", C, MaxC, ShCode).
|
print_usage_command2("help", C, MaxC, ShCode).
|
||||||
|
|
||||||
|
|
||||||
%%-----------------------------
|
%%-----------------------------
|
||||||
@ -792,12 +783,8 @@ filter_commands_regexp(All, Glob) ->
|
|||||||
%% @spec (Cmd::string(), MaxC::integer(), ShCode::boolean()) -> ok
|
%% @spec (Cmd::string(), MaxC::integer(), ShCode::boolean()) -> ok
|
||||||
print_usage_command(Cmd, MaxC, ShCode, Version) ->
|
print_usage_command(Cmd, MaxC, ShCode, Version) ->
|
||||||
Name = list_to_atom(Cmd),
|
Name = list_to_atom(Cmd),
|
||||||
case ejabberd_commands:get_command_definition(Name, Version) of
|
C = ejabberd_commands:get_command_definition(Name, Version),
|
||||||
command_not_found ->
|
print_usage_command2(Cmd, C, MaxC, ShCode).
|
||||||
io:format("Error: command ~p not known.~n", [Cmd]);
|
|
||||||
C ->
|
|
||||||
print_usage_command2(Cmd, C, MaxC, ShCode)
|
|
||||||
end.
|
|
||||||
|
|
||||||
print_usage_command2(Cmd, C, MaxC, ShCode) ->
|
print_usage_command2(Cmd, C, MaxC, ShCode) ->
|
||||||
#ejabberd_commands{
|
#ejabberd_commands{
|
||||||
@ -881,9 +868,3 @@ print(Format, Args) ->
|
|||||||
%% Struct(Integer res) create_account(Struct(String user, String server, String password))
|
%% Struct(Integer res) create_account(Struct(String user, String server, String password))
|
||||||
%%format_usage_xmlrpc(ArgsDef, ResultDef) ->
|
%%format_usage_xmlrpc(ArgsDef, ResultDef) ->
|
||||||
%% ["aaaa bbb ccc"].
|
%% ["aaaa bbb ccc"].
|
||||||
|
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(ejabberdctl_access_commands) ->
|
|
||||||
fun (V) when is_list(V) -> V end;
|
|
||||||
opt_type(_) -> [ejabberdctl_access_commands].
|
|
||||||
|
46
src/ejabberd_db_sup.erl
Normal file
46
src/ejabberd_db_sup.erl
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% Created : 13 June 2019 by Evgeny Khramtsov <ekhramtsov@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_db_sup).
|
||||||
|
|
||||||
|
-behaviour(supervisor).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([start_link/0]).
|
||||||
|
|
||||||
|
%% Supervisor callbacks
|
||||||
|
-export([init/1]).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API functions
|
||||||
|
%%%===================================================================
|
||||||
|
start_link() ->
|
||||||
|
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Supervisor callbacks
|
||||||
|
%%%===================================================================
|
||||||
|
init([]) ->
|
||||||
|
{ok, {{one_for_one, 10, 1}, []}}.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
@ -151,11 +151,13 @@ run(Hook, Args) ->
|
|||||||
-spec run(atom(), binary() | global, list()) -> ok.
|
-spec run(atom(), binary() | global, list()) -> ok.
|
||||||
|
|
||||||
run(Hook, Host, Args) ->
|
run(Hook, Host, Args) ->
|
||||||
case ets:lookup(hooks, {Hook, Host}) of
|
try ets:lookup(hooks, {Hook, Host}) of
|
||||||
[{_, Ls}] ->
|
[{_, Ls}] ->
|
||||||
run1(Ls, Hook, Args);
|
run1(Ls, Hook, Args);
|
||||||
[] ->
|
[] ->
|
||||||
ok
|
ok
|
||||||
|
catch _:badarg ->
|
||||||
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec run_fold(atom(), any(), list()) -> any().
|
-spec run_fold(atom(), any(), list()) -> any().
|
||||||
@ -171,11 +173,13 @@ run_fold(Hook, Val, Args) ->
|
|||||||
-spec run_fold(atom(), binary() | global, any(), list()) -> any().
|
-spec run_fold(atom(), binary() | global, any(), list()) -> any().
|
||||||
|
|
||||||
run_fold(Hook, Host, Val, Args) ->
|
run_fold(Hook, Host, Val, Args) ->
|
||||||
case ets:lookup(hooks, {Hook, Host}) of
|
try ets:lookup(hooks, {Hook, Host}) of
|
||||||
[{_, Ls}] ->
|
[{_, Ls}] ->
|
||||||
run_fold1(Ls, Hook, Val, Args);
|
run_fold1(Ls, Hook, Val, Args);
|
||||||
[] ->
|
[] ->
|
||||||
Val
|
Val
|
||||||
|
catch _:badarg ->
|
||||||
|
Val
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
@ -190,7 +194,7 @@ run_fold(Hook, Host, Val, Args) ->
|
|||||||
%% {stop, Reason}
|
%% {stop, Reason}
|
||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
init([]) ->
|
init([]) ->
|
||||||
ets:new(hooks, [named_table, {read_concurrency, true}]),
|
_ = ets:new(hooks, [named_table, {read_concurrency, true}]),
|
||||||
{ok, #state{}}.
|
{ok, #state{}}.
|
||||||
|
|
||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
@ -381,13 +385,14 @@ safe_apply(Hook, Module, Function, Args) ->
|
|||||||
apply(Module, Function, Args)
|
apply(Module, Function, Args)
|
||||||
end
|
end
|
||||||
catch ?EX_RULE(E, R, St) when E /= exit; R /= normal ->
|
catch ?EX_RULE(E, R, St) when E /= exit; R /= normal ->
|
||||||
|
Stack = ?EX_STACK(St),
|
||||||
?ERROR_MSG("Hook ~p crashed when running ~p:~p/~p:~n" ++
|
?ERROR_MSG("Hook ~p crashed when running ~p:~p/~p:~n" ++
|
||||||
string:join(
|
string:join(
|
||||||
["** Reason = ~p"|
|
["** ~s"|
|
||||||
["** Arg " ++ integer_to_list(I) ++ " = ~p"
|
["** Arg " ++ integer_to_list(I) ++ " = ~p"
|
||||||
|| I <- lists:seq(1, length(Args))]],
|
|| I <- lists:seq(1, length(Args))]],
|
||||||
"~n"),
|
"~n"),
|
||||||
[Hook, Module, Function, length(Args),
|
[Hook, Module, Function, length(Args),
|
||||||
{E, R, ?EX_STACK(St)}|Args]),
|
misc:format_exception(2, E, R, Stack)|Args]),
|
||||||
'EXIT'
|
'EXIT'
|
||||||
end.
|
end.
|
||||||
|
@ -25,17 +25,15 @@
|
|||||||
|
|
||||||
-module(ejabberd_http).
|
-module(ejabberd_http).
|
||||||
-behaviour(ejabberd_listener).
|
-behaviour(ejabberd_listener).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
%% External exports
|
%% External exports
|
||||||
-export([start/3, start_link/3,
|
-export([start/3, start_link/3,
|
||||||
accept/1, receive_headers/1, recv_file/2,
|
accept/1, receive_headers/1, recv_file/2,
|
||||||
transform_listen_option/2, listen_opt_type/1,
|
listen_opt_type/1, listen_options/0]).
|
||||||
listen_options/0]).
|
|
||||||
|
|
||||||
-export([init/3, opt_type/1]).
|
-export([init/3]).
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("xmpp.hrl").
|
-include("xmpp.hrl").
|
||||||
@ -112,9 +110,10 @@ init(SockMod, Socket, Opts) ->
|
|||||||
false -> [compression_none | TLSOpts1];
|
false -> [compression_none | TLSOpts1];
|
||||||
true -> TLSOpts1
|
true -> TLSOpts1
|
||||||
end,
|
end,
|
||||||
TLSOpts3 = case get_certfile(Opts) of
|
TLSOpts3 = case ejabberd_pkix:get_certfile(
|
||||||
undefined -> TLSOpts2;
|
ejabberd_config:get_myname()) of
|
||||||
CertFile -> [{certfile, CertFile}|TLSOpts2]
|
error -> TLSOpts2;
|
||||||
|
{ok, CertFile} -> [{certfile, CertFile}|TLSOpts2]
|
||||||
end,
|
end,
|
||||||
TLSOpts = [verify_none | TLSOpts3],
|
TLSOpts = [verify_none | TLSOpts3],
|
||||||
{SockMod1, Socket1} = if TLSEnabled ->
|
{SockMod1, Socket1} = if TLSEnabled ->
|
||||||
@ -124,30 +123,8 @@ init(SockMod, Socket, Opts) ->
|
|||||||
{fast_tls, TLSSocket};
|
{fast_tls, TLSSocket};
|
||||||
true -> {SockMod, Socket}
|
true -> {SockMod, Socket}
|
||||||
end,
|
end,
|
||||||
Captcha = case proplists:get_bool(captcha, Opts) of
|
|
||||||
true -> [{[<<"captcha">>], ejabberd_captcha}];
|
|
||||||
false -> []
|
|
||||||
end,
|
|
||||||
Register = case proplists:get_bool(register, Opts) of
|
|
||||||
true -> [{[<<"register">>], mod_register_web}];
|
|
||||||
false -> []
|
|
||||||
end,
|
|
||||||
Admin = case proplists:get_bool(web_admin, Opts) of
|
|
||||||
true -> [{[<<"admin">>], ejabberd_web_admin}];
|
|
||||||
false -> []
|
|
||||||
end,
|
|
||||||
Bind = case proplists:get_bool(http_bind, Opts) of
|
|
||||||
true -> [{[<<"http-bind">>], mod_bosh}];
|
|
||||||
false -> []
|
|
||||||
end,
|
|
||||||
XMLRPC = case proplists:get_bool(xmlrpc, Opts) of
|
|
||||||
true -> [{[], ejabberd_xmlrpc}];
|
|
||||||
false -> []
|
|
||||||
end,
|
|
||||||
SockPeer = proplists:get_value(sock_peer_name, Opts, none),
|
SockPeer = proplists:get_value(sock_peer_name, Opts, none),
|
||||||
DefinedHandlers = proplists:get_value(request_handlers, Opts, []),
|
RequestHandlers = proplists:get_value(request_handlers, Opts, []),
|
||||||
RequestHandlers = DefinedHandlers ++ Captcha ++ Register ++
|
|
||||||
Admin ++ Bind ++ XMLRPC,
|
|
||||||
?DEBUG("S: ~p~n", [RequestHandlers]),
|
?DEBUG("S: ~p~n", [RequestHandlers]),
|
||||||
|
|
||||||
DefaultHost = proplists:get_value(default_host, Opts),
|
DefaultHost = proplists:get_value(default_host, Opts),
|
||||||
@ -557,7 +534,7 @@ analyze_ip_xff(IP, [], _Host) -> IP;
|
|||||||
analyze_ip_xff({IPLast, Port}, XFF, Host) ->
|
analyze_ip_xff({IPLast, Port}, XFF, Host) ->
|
||||||
[ClientIP | ProxiesIPs] = str:tokens(XFF, <<", ">>) ++
|
[ClientIP | ProxiesIPs] = str:tokens(XFF, <<", ">>) ++
|
||||||
[misc:ip_to_list(IPLast)],
|
[misc:ip_to_list(IPLast)],
|
||||||
TrustedProxies = ejabberd_config:get_option({trusted_proxies, Host}, []),
|
TrustedProxies = ejabberd_option:trusted_proxies(Host),
|
||||||
IPClient = case is_ipchain_trusted(ProxiesIPs,
|
IPClient = case is_ipchain_trusted(ProxiesIPs,
|
||||||
TrustedProxies)
|
TrustedProxies)
|
||||||
of
|
of
|
||||||
@ -581,7 +558,7 @@ is_ipchain_trusted(UserIPs, Masks) ->
|
|||||||
{ok, IP2} ->
|
{ok, IP2} ->
|
||||||
lists:any(
|
lists:any(
|
||||||
fun({Mask, MaskLen}) ->
|
fun({Mask, MaskLen}) ->
|
||||||
acl:ip_matches_mask(IP2, Mask, MaskLen)
|
misc:match_ip_mask(IP2, Mask, MaskLen)
|
||||||
end, Masks);
|
end, Masks);
|
||||||
_ ->
|
_ ->
|
||||||
false
|
false
|
||||||
@ -803,7 +780,7 @@ rest_dir(N, Path, <<_H, T/binary>>) -> rest_dir(N, Path, T).
|
|||||||
expand_custom_headers(Headers) ->
|
expand_custom_headers(Headers) ->
|
||||||
lists:map(fun({K, V}) ->
|
lists:map(fun({K, V}) ->
|
||||||
{K, misc:expand_keyword(<<"@VERSION@">>, V,
|
{K, misc:expand_keyword(<<"@VERSION@">>, V,
|
||||||
ejabberd_config:get_version())}
|
ejabberd_option:version())}
|
||||||
end, Headers).
|
end, Headers).
|
||||||
|
|
||||||
code_to_phrase(100) -> <<"Continue">>;
|
code_to_phrase(100) -> <<"Continue">>;
|
||||||
@ -851,7 +828,7 @@ code_to_phrase(503) -> <<"Service Unavailable">>;
|
|||||||
code_to_phrase(504) -> <<"Gateway Timeout">>;
|
code_to_phrase(504) -> <<"Gateway Timeout">>;
|
||||||
code_to_phrase(505) -> <<"HTTP Version Not Supported">>.
|
code_to_phrase(505) -> <<"HTTP Version Not Supported">>.
|
||||||
|
|
||||||
-spec parse_auth(binary()) -> {binary(), binary()} | {oauth, binary(), []} | undefined.
|
-spec parse_auth(binary()) -> {binary(), binary()} | {oauth, binary(), []} | invalid.
|
||||||
parse_auth(<<"Basic ", Auth64/binary>>) ->
|
parse_auth(<<"Basic ", Auth64/binary>>) ->
|
||||||
try base64:decode(Auth64) of
|
try base64:decode(Auth64) of
|
||||||
Auth ->
|
Auth ->
|
||||||
@ -927,150 +904,32 @@ normalize_path([_Parent, <<"..">>|Path], Norm) ->
|
|||||||
normalize_path([Part | Path], Norm) ->
|
normalize_path([Part | Path], Norm) ->
|
||||||
normalize_path(Path, [Part|Norm]).
|
normalize_path(Path, [Part|Norm]).
|
||||||
|
|
||||||
-spec get_certfile([proplists:property()]) -> binary() | undefined.
|
|
||||||
get_certfile(Opts) ->
|
|
||||||
case lists:keyfind(certfile, 1, Opts) of
|
|
||||||
{_, CertFile} ->
|
|
||||||
CertFile;
|
|
||||||
false ->
|
|
||||||
case ejabberd_pkix:get_certfile(ejabberd_config:get_myname()) of
|
|
||||||
{ok, CertFile} ->
|
|
||||||
CertFile;
|
|
||||||
error ->
|
|
||||||
ejabberd_config:get_option({domain_certfile, ejabberd_config:get_myname()})
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
transform_listen_option(captcha, Opts) ->
|
|
||||||
[{captcha, true}|Opts];
|
|
||||||
transform_listen_option(register, Opts) ->
|
|
||||||
[{register, true}|Opts];
|
|
||||||
transform_listen_option(web_admin, Opts) ->
|
|
||||||
[{web_admin, true}|Opts];
|
|
||||||
transform_listen_option(http_bind, Opts) ->
|
|
||||||
[{http_bind, true}|Opts];
|
|
||||||
transform_listen_option(http_poll, Opts) ->
|
|
||||||
Opts;
|
|
||||||
transform_listen_option({request_handlers, Hs}, Opts) ->
|
|
||||||
Hs1 = lists:map(
|
|
||||||
fun({PList, Mod}) when is_list(PList) ->
|
|
||||||
Path = iolist_to_binary([[$/, P] || P <- PList]),
|
|
||||||
{Path, Mod};
|
|
||||||
(Opt) ->
|
|
||||||
Opt
|
|
||||||
end, Hs),
|
|
||||||
[{request_handlers, Hs1} | Opts];
|
|
||||||
transform_listen_option(Opt, Opts) ->
|
|
||||||
[Opt|Opts].
|
|
||||||
|
|
||||||
prepare_request_module(mod_http_bind) ->
|
|
||||||
mod_bosh;
|
|
||||||
prepare_request_module(Mod) when is_atom(Mod) ->
|
|
||||||
case code:ensure_loaded(Mod) of
|
|
||||||
{module, Mod} ->
|
|
||||||
Mod;
|
|
||||||
Err ->
|
|
||||||
?ERROR_MSG(
|
|
||||||
"Failed to load request handler ~s, "
|
|
||||||
"did you mean ~s? Hint: "
|
|
||||||
"make sure there is no typo and file ~s.beam "
|
|
||||||
"exists inside either ~s or ~s directory",
|
|
||||||
[Mod,
|
|
||||||
misc:best_match(Mod, ejabberd_config:get_modules()),
|
|
||||||
Mod,
|
|
||||||
filename:dirname(code:which(?MODULE)),
|
|
||||||
ext_mod:modules_dir()]),
|
|
||||||
erlang:error(Err)
|
|
||||||
end.
|
|
||||||
|
|
||||||
emit_option_replacement(Option, Path, Handler) ->
|
|
||||||
?WARNING_MSG(
|
|
||||||
"Listening option '~s' is deprecated, enable it via request handlers, e.g.:~n"
|
|
||||||
"listen:~n"
|
|
||||||
" ...~n"
|
|
||||||
" -~n"
|
|
||||||
" module: ~s~n"
|
|
||||||
" request_handlers:~n"
|
|
||||||
" ...~n"
|
|
||||||
" \"~s\": ~s~n",
|
|
||||||
[Option, ?MODULE, Path, Handler]).
|
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(trusted_proxies) ->
|
|
||||||
fun (all) -> all;
|
|
||||||
(TPs) -> lists:filtermap(
|
|
||||||
fun(TP) ->
|
|
||||||
case acl:parse_ip_netmask(iolist_to_binary(TP)) of
|
|
||||||
{ok, Ip, Mask} -> {true, {Ip, Mask}};
|
|
||||||
_ -> false
|
|
||||||
end
|
|
||||||
end, TPs)
|
|
||||||
end;
|
|
||||||
opt_type(_) -> [trusted_proxies].
|
|
||||||
|
|
||||||
listen_opt_type(certfile = Opt) ->
|
|
||||||
fun(S) ->
|
|
||||||
?WARNING_MSG("Listening option '~s' for ~s is deprecated, use "
|
|
||||||
"'certfiles' global option instead", [Opt, ?MODULE]),
|
|
||||||
{ok, File} = ejabberd_pkix:add_certfile(S),
|
|
||||||
File
|
|
||||||
end;
|
|
||||||
listen_opt_type(captcha) ->
|
|
||||||
fun(B) when is_boolean(B) ->
|
|
||||||
emit_option_replacement(captcha, "/captcha", ejabberd_captcha),
|
|
||||||
B
|
|
||||||
end;
|
|
||||||
listen_opt_type(register) ->
|
|
||||||
fun(B) when is_boolean(B) ->
|
|
||||||
emit_option_replacement(register, "/register", mod_register_web),
|
|
||||||
B
|
|
||||||
end;
|
|
||||||
listen_opt_type(web_admin) ->
|
|
||||||
fun(B) when is_boolean(B) ->
|
|
||||||
emit_option_replacement(web_admin, "/admin", ejabberd_web_admin),
|
|
||||||
B
|
|
||||||
end;
|
|
||||||
listen_opt_type(http_bind) ->
|
|
||||||
fun(B) when is_boolean(B) ->
|
|
||||||
emit_option_replacement(http_bind, "/bosh", mod_bosh),
|
|
||||||
B
|
|
||||||
end;
|
|
||||||
listen_opt_type(xmlrpc) ->
|
|
||||||
fun(B) when is_boolean(B) ->
|
|
||||||
emit_option_replacement(xmlrpc, "/", ejabberd_xmlrpc),
|
|
||||||
B
|
|
||||||
end;
|
|
||||||
listen_opt_type(tag) ->
|
listen_opt_type(tag) ->
|
||||||
fun(B) when is_binary(B) -> B end;
|
econf:binary();
|
||||||
listen_opt_type(request_handlers) ->
|
listen_opt_type(request_handlers) ->
|
||||||
fun(Hs) ->
|
econf:and_then(
|
||||||
Hs1 = lists:map(fun
|
econf:map(
|
||||||
({Mod, Path}) when is_atom(Mod) -> {Path, Mod};
|
econf:binary(),
|
||||||
({Path, Mod}) -> {Path, Mod}
|
econf:beam([[{socket_handoff, 3}, {process, 2}]])),
|
||||||
end, Hs),
|
fun(L) ->
|
||||||
Hs2 = [{str:tokens(
|
[{str:tokens(Path, <<"/">>), Mod} || {Path, Mod} <- L]
|
||||||
iolist_to_binary(Path), <<"/">>),
|
end);
|
||||||
Mod} || {Path, Mod} <- Hs1],
|
|
||||||
[{Path, prepare_request_module(Mod)} || {Path, Mod} <- Hs2]
|
|
||||||
end;
|
|
||||||
listen_opt_type(default_host) ->
|
listen_opt_type(default_host) ->
|
||||||
fun iolist_to_binary/1;
|
econf:domain();
|
||||||
listen_opt_type(custom_headers) ->
|
listen_opt_type(custom_headers) ->
|
||||||
fun expand_custom_headers/1.
|
econf:and_then(
|
||||||
|
econf:map(
|
||||||
|
econf:binary(),
|
||||||
|
econf:binary()),
|
||||||
|
fun expand_custom_headers/1).
|
||||||
|
|
||||||
listen_options() ->
|
listen_options() ->
|
||||||
[{certfile, undefined},
|
[{ciphers, undefined},
|
||||||
{ciphers, undefined},
|
|
||||||
{dhfile, undefined},
|
{dhfile, undefined},
|
||||||
{cafile, undefined},
|
{cafile, undefined},
|
||||||
{protocol_options, undefined},
|
{protocol_options, undefined},
|
||||||
{tls, false},
|
{tls, false},
|
||||||
{tls_compression, false},
|
{tls_compression, false},
|
||||||
{captcha, false},
|
|
||||||
{register, false},
|
|
||||||
{web_admin, false},
|
|
||||||
{http_bind, false},
|
|
||||||
{xmlrpc, false},
|
|
||||||
{request_handlers, []},
|
{request_handlers, []},
|
||||||
{tag, <<>>},
|
{tag, <<>>},
|
||||||
{default_host, undefined},
|
{default_host, undefined},
|
||||||
|
@ -40,15 +40,12 @@
|
|||||||
|
|
||||||
-include("ejabberd_http.hrl").
|
-include("ejabberd_http.hrl").
|
||||||
|
|
||||||
-define(PING_INTERVAL, 60).
|
|
||||||
-define(WEBSOCKET_TIMEOUT, 300).
|
|
||||||
|
|
||||||
-record(state,
|
-record(state,
|
||||||
{socket :: ws_socket(),
|
{socket :: ws_socket(),
|
||||||
ping_interval = ?PING_INTERVAL :: non_neg_integer(),
|
ping_interval :: non_neg_integer(),
|
||||||
ping_timer = make_ref() :: reference(),
|
ping_timer = make_ref() :: reference(),
|
||||||
pong_expected = false :: boolean(),
|
pong_expected = false :: boolean(),
|
||||||
timeout = ?WEBSOCKET_TIMEOUT :: non_neg_integer(),
|
timeout :: non_neg_integer(),
|
||||||
timer = make_ref() :: reference(),
|
timer = make_ref() :: reference(),
|
||||||
input = [] :: list(),
|
input = [] :: list(),
|
||||||
active = false :: boolean(),
|
active = false :: boolean(),
|
||||||
@ -133,12 +130,8 @@ init([{#ws{ip = IP, http_opts = HOpts}, _} = WS]) ->
|
|||||||
(_) -> false
|
(_) -> false
|
||||||
end, HOpts),
|
end, HOpts),
|
||||||
Opts = ejabberd_c2s_config:get_c2s_limits() ++ SOpts,
|
Opts = ejabberd_c2s_config:get_c2s_limits() ++ SOpts,
|
||||||
PingInterval = ejabberd_config:get_option(
|
PingInterval = ejabberd_option:websocket_ping_interval(),
|
||||||
{websocket_ping_interval, ejabberd_config:get_myname()},
|
WSTimeout = ejabberd_option:websocket_timeout(),
|
||||||
?PING_INTERVAL) * 1000,
|
|
||||||
WSTimeout = ejabberd_config:get_option(
|
|
||||||
{websocket_timeout, ejabberd_config:get_myname()},
|
|
||||||
?WEBSOCKET_TIMEOUT) * 1000,
|
|
||||||
Socket = {http_ws, self(), IP},
|
Socket = {http_ws, self(), IP},
|
||||||
?DEBUG("Client connected through websocket ~p",
|
?DEBUG("Client connected through websocket ~p",
|
||||||
[Socket]),
|
[Socket]),
|
||||||
@ -201,15 +194,15 @@ handle_sync_event({send_xml, Packet}, _From, StateName,
|
|||||||
case Packet2 of
|
case Packet2 of
|
||||||
{xmlstreamstart, Name, Attrs3} ->
|
{xmlstreamstart, Name, Attrs3} ->
|
||||||
B = fxml:element_to_binary(#xmlel{name = Name, attrs = Attrs3}),
|
B = fxml:element_to_binary(#xmlel{name = Name, attrs = Attrs3}),
|
||||||
WsPid ! {text, <<(binary:part(B, 0, byte_size(B)-2))/binary, ">">>};
|
route_text(WsPid, <<(binary:part(B, 0, byte_size(B)-2))/binary, ">">>);
|
||||||
{xmlstreamend, Name} ->
|
{xmlstreamend, Name} ->
|
||||||
WsPid ! {text, <<"</", Name/binary, ">">>};
|
route_text(WsPid, <<"</", Name/binary, ">">>);
|
||||||
{xmlstreamelement, El} ->
|
{xmlstreamelement, El} ->
|
||||||
WsPid ! {text, fxml:element_to_binary(El)};
|
route_text(WsPid, fxml:element_to_binary(El));
|
||||||
{xmlstreamraw, Bin} ->
|
{xmlstreamraw, Bin} ->
|
||||||
WsPid ! {text, Bin};
|
route_text(WsPid, Bin);
|
||||||
{xmlstreamcdata, Bin2} ->
|
{xmlstreamcdata, Bin2} ->
|
||||||
WsPid ! {text, Bin2};
|
route_text(WsPid, Bin2);
|
||||||
skip ->
|
skip ->
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
@ -224,7 +217,7 @@ handle_sync_event(close, _From, StateName, #state{ws = {_, WsPid}, rfc_compilant
|
|||||||
when StateName /= stream_end_sent ->
|
when StateName /= stream_end_sent ->
|
||||||
Close = #xmlel{name = <<"close">>,
|
Close = #xmlel{name = <<"close">>,
|
||||||
attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>}]},
|
attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>}]},
|
||||||
WsPid ! {text, fxml:element_to_binary(Close)},
|
route_text(WsPid, fxml:element_to_binary(Close)),
|
||||||
{stop, normal, StateData};
|
{stop, normal, StateData};
|
||||||
handle_sync_event(close, _From, _StateName, StateData) ->
|
handle_sync_event(close, _From, _StateName, StateData) ->
|
||||||
{stop, normal, StateData}.
|
{stop, normal, StateData}.
|
||||||
@ -366,3 +359,8 @@ parsed_items(List) ->
|
|||||||
after 0 ->
|
after 0 ->
|
||||||
lists:reverse(List)
|
lists:reverse(List)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec route_text(pid(), binary()) -> ok.
|
||||||
|
route_text(Pid, Data) ->
|
||||||
|
Pid ! {text, Data},
|
||||||
|
ok.
|
||||||
|
@ -70,7 +70,7 @@ dispatch(_) ->
|
|||||||
%%% gen_server callbacks
|
%%% gen_server callbacks
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
init([]) ->
|
init([]) ->
|
||||||
ets:new(?MODULE, [named_table, ordered_set, public]),
|
_ = ets:new(?MODULE, [named_table, ordered_set, public]),
|
||||||
{ok, #state{}}.
|
{ok, #state{}}.
|
||||||
|
|
||||||
handle_call(Request, From, State) ->
|
handle_call(Request, From, State) ->
|
||||||
@ -166,7 +166,7 @@ decode_id(_) ->
|
|||||||
|
|
||||||
-spec calc_checksum(binary()) -> binary().
|
-spec calc_checksum(binary()) -> binary().
|
||||||
calc_checksum(Data) ->
|
calc_checksum(Data) ->
|
||||||
Key = ejabberd_config:get_option(shared_key),
|
Key = ejabberd_config:get_shared_key(),
|
||||||
base64:encode(crypto:hash(sha, <<Data/binary, Key/binary>>)).
|
base64:encode(crypto:hash(sha, <<Data/binary, Key/binary>>)).
|
||||||
|
|
||||||
-spec callback(atom() | pid(), #iq{} | timeout, term()) -> any().
|
-spec callback(atom() | pid(), #iq{} | timeout, term()) -> any().
|
||||||
|
@ -25,36 +25,40 @@
|
|||||||
|
|
||||||
-module(ejabberd_listener).
|
-module(ejabberd_listener).
|
||||||
-behaviour(supervisor).
|
-behaviour(supervisor).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
-author('ekhramtsov@process-one.net').
|
-author('ekhramtsov@process-one.net').
|
||||||
|
|
||||||
-export([start_link/0, init/1, start/3, init/3,
|
-export([start_link/0, init/1, start/3, init/3,
|
||||||
start_listeners/0, start_listener/3, stop_listeners/0,
|
start_listeners/0, start_listener/3, stop_listeners/0,
|
||||||
stop_listener/2, add_listener/3, delete_listener/2,
|
add_listener/3, delete_listener/2,
|
||||||
transform_options/1, validate_cfg/1, opt_type/1,
|
config_reloaded/0]).
|
||||||
config_reloaded/0, get_certfiles/0]).
|
-export([listen_options/0, listen_opt_type/1, validator/0]).
|
||||||
%% Legacy API
|
-export([tls_listeners/0]).
|
||||||
-export([parse_listener_portip/2]).
|
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
-type transport() :: tcp | udp.
|
-type transport() :: tcp | udp.
|
||||||
-type endpoint() :: {inet:port_number(), inet:ip_address(), transport()}.
|
-type endpoint() :: {inet:port_number(), inet:ip_address(), transport()}.
|
||||||
-type listen_opts() :: [proplists:property()].
|
-type list_opts() :: [{atom(), term()}].
|
||||||
-type listener() :: {endpoint(), module(), listen_opts()}.
|
-type opts() :: #{atom() => term()}.
|
||||||
|
-type listener() :: {endpoint(), module(), opts()}.
|
||||||
-type sockmod() :: gen_tcp.
|
-type sockmod() :: gen_tcp.
|
||||||
-type socket() :: inet:socket().
|
-type socket() :: inet:socket().
|
||||||
|
-type state() :: term().
|
||||||
|
|
||||||
-callback start(sockmod(), socket(), listen_opts()) ->
|
-export_type([listener/0]).
|
||||||
|
|
||||||
|
-callback start(sockmod(), socket(), state()) ->
|
||||||
{ok, pid()} | {error, any()} | ignore.
|
{ok, pid()} | {error, any()} | ignore.
|
||||||
-callback start_link(sockmod(), socket(), listen_opts()) ->
|
-callback start_link(sockmod(), socket(), state()) ->
|
||||||
{ok, pid()} | {error, any()} | ignore.
|
{ok, pid()} | {error, any()} | ignore.
|
||||||
-callback accept(pid()) -> any().
|
-callback accept(pid()) -> any().
|
||||||
-callback listen_opt_type(atom()) -> fun((term()) -> term()).
|
-callback listen_opt_type(atom()) -> econf:validator().
|
||||||
-callback listen_options() -> listen_opts().
|
-callback listen_options() -> [{atom(), term()} | atom()].
|
||||||
|
-callback tcp_init(socket(), list_opts()) -> state().
|
||||||
|
-callback udp_init(socket(), list_opts()) -> state().
|
||||||
|
|
||||||
-optional_callbacks([listen_opt_type/1]).
|
-optional_callbacks([listen_opt_type/1, tcp_init/2, udp_init/2]).
|
||||||
|
|
||||||
-define(TCP_SEND_TIMEOUT, 15000).
|
-define(TCP_SEND_TIMEOUT, 15000).
|
||||||
|
|
||||||
@ -62,9 +66,9 @@ start_link() ->
|
|||||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||||
|
|
||||||
init(_) ->
|
init(_) ->
|
||||||
ets:new(?MODULE, [named_table, public]),
|
_ = ets:new(?MODULE, [named_table, public]),
|
||||||
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50),
|
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50),
|
||||||
Listeners = ejabberd_config:get_option(listen, []),
|
Listeners = ejabberd_option:listen(),
|
||||||
{ok, {{one_for_one, 10, 1}, listeners_childspec(Listeners)}}.
|
{ok, {{one_for_one, 10, 1}, listeners_childspec(Listeners)}}.
|
||||||
|
|
||||||
-spec listeners_childspec([listener()]) -> [supervisor:child_spec()].
|
-spec listeners_childspec([listener()]) -> [supervisor:child_spec()].
|
||||||
@ -79,22 +83,22 @@ listeners_childspec(Listeners) ->
|
|||||||
|
|
||||||
-spec start_listeners() -> ok.
|
-spec start_listeners() -> ok.
|
||||||
start_listeners() ->
|
start_listeners() ->
|
||||||
Listeners = ejabberd_config:get_option(listen, []),
|
Listeners = ejabberd_option:listen(),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(Spec) ->
|
fun(Spec) ->
|
||||||
supervisor:start_child(?MODULE, Spec)
|
supervisor:start_child(?MODULE, Spec)
|
||||||
end, listeners_childspec(Listeners)).
|
end, listeners_childspec(Listeners)).
|
||||||
|
|
||||||
-spec start(endpoint(), module(), listen_opts()) -> term().
|
-spec start(endpoint(), module(), opts()) -> term().
|
||||||
start(EndPoint, Module, Opts) ->
|
start(EndPoint, Module, Opts) ->
|
||||||
proc_lib:start_link(?MODULE, init, [EndPoint, Module, Opts]).
|
proc_lib:start_link(?MODULE, init, [EndPoint, Module, Opts]).
|
||||||
|
|
||||||
-spec init(endpoint(), module(), listen_opts()) -> ok.
|
-spec init(endpoint(), module(), opts()) -> ok.
|
||||||
init(EndPoint, Module, AllOpts) ->
|
init({_, _, Transport} = EndPoint, Module, AllOpts) ->
|
||||||
{ModuleOpts, SockOpts} = split_opts(AllOpts),
|
{ModuleOpts, SockOpts} = split_opts(Transport, AllOpts),
|
||||||
init(EndPoint, Module, ModuleOpts, SockOpts).
|
init(EndPoint, Module, ModuleOpts, SockOpts).
|
||||||
|
|
||||||
-spec init(endpoint(), module(), listen_opts(), [gen_tcp:option()]) -> ok.
|
-spec init(endpoint(), module(), opts(), [gen_tcp:option()]) -> ok.
|
||||||
init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) ->
|
init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) ->
|
||||||
case gen_udp:open(Port, [binary,
|
case gen_udp:open(Port, [binary,
|
||||||
{active, false},
|
{active, false},
|
||||||
@ -104,22 +108,21 @@ init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) ->
|
|||||||
case inet:sockname(Socket) of
|
case inet:sockname(Socket) of
|
||||||
{ok, {Addr, Port1}} ->
|
{ok, {Addr, Port1}} ->
|
||||||
proc_lib:init_ack({ok, self()}),
|
proc_lib:init_ack({ok, self()}),
|
||||||
application:ensure_started(ejabberd),
|
case application:ensure_started(ejabberd) of
|
||||||
?INFO_MSG("Start accepting UDP connections at ~s for ~p",
|
ok ->
|
||||||
[format_endpoint({Port1, Addr, udp}), Module]),
|
?INFO_MSG("Start accepting ~s connections at ~s for ~p",
|
||||||
|
[format_transport(udp, Opts),
|
||||||
|
format_endpoint({Port1, Addr, udp}), Module]),
|
||||||
|
Opts1 = opts_to_list(Module, Opts),
|
||||||
case erlang:function_exported(Module, udp_init, 2) of
|
case erlang:function_exported(Module, udp_init, 2) of
|
||||||
false ->
|
false ->
|
||||||
udp_recv(Socket, Module, Opts);
|
udp_recv(Socket, Module, Opts1);
|
||||||
true ->
|
true ->
|
||||||
case catch Module:udp_init(Socket, Opts) of
|
State = Module:udp_init(Socket, Opts1),
|
||||||
{'EXIT', _} = Err ->
|
udp_recv(Socket, Module, State)
|
||||||
?ERROR_MSG("failed to process callback function "
|
end;
|
||||||
"~p:~s(~p, ~p): ~p",
|
{error, _} ->
|
||||||
[Module, udp_init, Socket, Opts, Err]),
|
ok
|
||||||
udp_recv(Socket, Module, Opts);
|
|
||||||
NewOpts ->
|
|
||||||
udp_recv(Socket, Module, NewOpts)
|
|
||||||
end
|
|
||||||
end;
|
end;
|
||||||
{error, Reason} = Err ->
|
{error, Reason} = Err ->
|
||||||
report_socket_error(Reason, EndPoint, Module),
|
report_socket_error(Reason, EndPoint, Module),
|
||||||
@ -135,27 +138,28 @@ init({Port, _, tcp} = EndPoint, Module, Opts, SockOpts) ->
|
|||||||
case inet:sockname(ListenSocket) of
|
case inet:sockname(ListenSocket) of
|
||||||
{ok, {Addr, Port1}} ->
|
{ok, {Addr, Port1}} ->
|
||||||
proc_lib:init_ack({ok, self()}),
|
proc_lib:init_ack({ok, self()}),
|
||||||
application:ensure_started(ejabberd),
|
case application:ensure_started(ejabberd) of
|
||||||
|
ok ->
|
||||||
Sup = start_module_sup(Module, Opts),
|
Sup = start_module_sup(Module, Opts),
|
||||||
?INFO_MSG("Start accepting TCP connections at ~s for ~p",
|
Interval = maps:get(accept_interval, Opts),
|
||||||
[format_endpoint({Port1, Addr, tcp}), Module]),
|
Proxy = maps:get(use_proxy_protocol, Opts),
|
||||||
|
?INFO_MSG("Start accepting ~s connections at ~s for ~p",
|
||||||
|
[format_transport(tcp, Opts),
|
||||||
|
format_endpoint({Port1, Addr, tcp}), Module]),
|
||||||
|
Opts1 = opts_to_list(Module, Opts),
|
||||||
case erlang:function_exported(Module, tcp_init, 2) of
|
case erlang:function_exported(Module, tcp_init, 2) of
|
||||||
false ->
|
false ->
|
||||||
accept(ListenSocket, Module, Opts, Sup);
|
accept(ListenSocket, Module, Opts1, Sup, Interval, Proxy);
|
||||||
true ->
|
true ->
|
||||||
case catch Module:tcp_init(ListenSocket, Opts) of
|
State = Module:tcp_init(ListenSocket, Opts1),
|
||||||
{'EXIT', _} = Err ->
|
accept(ListenSocket, Module, State, Sup, Interval, Proxy)
|
||||||
?ERROR_MSG("failed to process callback function "
|
end;
|
||||||
"~p:~s(~p, ~p): ~p",
|
{error, _} ->
|
||||||
[Module, tcp_init, ListenSocket, Opts, Err]),
|
ok
|
||||||
accept(ListenSocket, Module, Opts, Sup);
|
|
||||||
NewOpts ->
|
|
||||||
accept(ListenSocket, Module, NewOpts, Sup)
|
|
||||||
end
|
|
||||||
end;
|
end;
|
||||||
{error, Reason} = Err ->
|
{error, Reason} = Err ->
|
||||||
report_socket_error(Reason, EndPoint, Module),
|
report_socket_error(Reason, EndPoint, Module),
|
||||||
Err
|
proc_lib:init_ack(Err)
|
||||||
end;
|
end;
|
||||||
{error, Reason} = Err ->
|
{error, Reason} = Err ->
|
||||||
report_socket_error(Reason, EndPoint, Module),
|
report_socket_error(Reason, EndPoint, Module),
|
||||||
@ -181,113 +185,115 @@ listen_tcp(Port, SockOpts) ->
|
|||||||
Err
|
Err
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec split_opts(listen_opts()) -> {listen_opts(), [gen_tcp:option()]}.
|
-spec split_opts(transport(), opts()) -> {opts(), [gen_tcp:option()]}.
|
||||||
split_opts(Opts) ->
|
split_opts(Transport, Opts) ->
|
||||||
lists:foldl(
|
maps:fold(
|
||||||
fun(Opt, {ModOpts, SockOpts} = Acc) ->
|
fun(Opt, Val, {ModOpts, SockOpts}) ->
|
||||||
case Opt of
|
case OptVal = {Opt, Val} of
|
||||||
{ip, _} -> {ModOpts, [Opt|SockOpts]};
|
{ip, _} ->
|
||||||
{backlog, _} -> {ModOpts, [Opt|SockOpts]};
|
{ModOpts, [OptVal|SockOpts]};
|
||||||
{inet, true} -> {ModOpts, [inet|SockOpts]};
|
{backlog, _} when Transport == tcp ->
|
||||||
{inet6, true} -> {ModOpts, [int6|SockOpts]};
|
{ModOpts, [OptVal|SockOpts]};
|
||||||
{inet, false} -> Acc;
|
{backlog, _} ->
|
||||||
{inet6, false} -> Acc;
|
{ModOpts, SockOpts};
|
||||||
_ -> {[Opt|ModOpts], SockOpts}
|
_ ->
|
||||||
|
{ModOpts#{Opt => Val}, SockOpts}
|
||||||
end
|
end
|
||||||
end, {[], []}, Opts).
|
end, {#{}, []}, Opts).
|
||||||
|
|
||||||
-spec accept(inet:socket(), module(), listen_opts(), atom()) -> no_return().
|
-spec accept(inet:socket(), module(), state(), atom(),
|
||||||
accept(ListenSocket, Module, Opts, Sup) ->
|
non_neg_integer(), boolean()) -> no_return().
|
||||||
Interval = proplists:get_value(accept_interval, Opts, 0),
|
accept(ListenSocket, Module, State, Sup, Interval, Proxy) ->
|
||||||
Arity = case erlang:function_exported(Module, start, 3) of
|
Arity = case erlang:function_exported(Module, start, 3) of
|
||||||
true -> 3;
|
true -> 3;
|
||||||
false -> 2
|
false -> 2
|
||||||
end,
|
end,
|
||||||
accept(ListenSocket, Module, Opts, Sup, Interval, Arity).
|
accept(ListenSocket, Module, State, Sup, Interval, Proxy, Arity).
|
||||||
|
|
||||||
-spec accept(inet:socket(), module(), listen_opts(), atom(),
|
-spec accept(inet:socket(), module(), state(), atom(),
|
||||||
non_neg_integer(), 2|3) -> no_return().
|
non_neg_integer(), boolean(), 2|3) -> no_return().
|
||||||
accept(ListenSocket, Module, Opts, Sup, Interval, Arity) ->
|
accept(ListenSocket, Module, State, Sup, Interval, Proxy, Arity) ->
|
||||||
NewInterval = check_rate_limit(Interval),
|
NewInterval = apply_rate_limit(Interval),
|
||||||
case gen_tcp:accept(ListenSocket) of
|
case gen_tcp:accept(ListenSocket) of
|
||||||
{ok, Socket} ->
|
{ok, Socket} when Proxy ->
|
||||||
case proplists:get_value(use_proxy_protocol, Opts, false) of
|
|
||||||
true ->
|
|
||||||
case proxy_protocol:decode(gen_tcp, Socket, 10000) of
|
case proxy_protocol:decode(gen_tcp, Socket, 10000) of
|
||||||
{error, Err} ->
|
{error, Err} ->
|
||||||
?ERROR_MSG("(~w) Proxy protocol parsing failed: ~s",
|
?ERROR_MSG("(~w) Proxy protocol parsing failed: ~s",
|
||||||
[ListenSocket, inet:format_error(Err)]),
|
[ListenSocket, format_error(Err)]),
|
||||||
gen_tcp:close(Socket);
|
gen_tcp:close(Socket);
|
||||||
{{Addr, Port}, {PAddr, PPort}} = SP ->
|
{{Addr, Port}, {PAddr, PPort}} = SP ->
|
||||||
Opts2 = [{sock_peer_name, SP} | Opts],
|
%% THIS IS WRONG
|
||||||
Receiver = case start_connection(Module, Arity, Socket, Opts2, Sup) of
|
State2 = [{sock_peer_name, SP} | State],
|
||||||
|
Receiver = case start_connection(Module, Arity, Socket, State2, Sup) of
|
||||||
{ok, RecvPid} ->
|
{ok, RecvPid} ->
|
||||||
RecvPid;
|
RecvPid;
|
||||||
_ ->
|
_ ->
|
||||||
gen_tcp:close(Socket),
|
gen_tcp:close(Socket),
|
||||||
none
|
none
|
||||||
end,
|
end,
|
||||||
?INFO_MSG("(~p) Accepted proxied connection ~s:~p -> ~s:~p",
|
?INFO_MSG("(~p) Accepted proxied connection ~s -> ~s",
|
||||||
[Receiver,
|
[Receiver,
|
||||||
ejabberd_config:may_hide_data(inet_parse:ntoa(PAddr)),
|
ejabberd_config:may_hide_data(
|
||||||
PPort, inet_parse:ntoa(Addr), Port])
|
format_endpoint({PPort, PAddr, tcp})),
|
||||||
end;
|
format_endpoint({Port, Addr, tcp})])
|
||||||
_ ->
|
end,
|
||||||
|
accept(ListenSocket, Module, State, Sup, NewInterval, Proxy, Arity);
|
||||||
|
{ok, Socket} ->
|
||||||
case {inet:sockname(Socket), inet:peername(Socket)} of
|
case {inet:sockname(Socket), inet:peername(Socket)} of
|
||||||
{{ok, {Addr, Port}}, {ok, {PAddr, PPort}}} ->
|
{{ok, {Addr, Port}}, {ok, {PAddr, PPort}}} ->
|
||||||
Receiver = case start_connection(Module, Arity, Socket, Opts, Sup) of
|
Receiver = case start_connection(Module, Arity, Socket, State, Sup) of
|
||||||
{ok, RecvPid} ->
|
{ok, RecvPid} ->
|
||||||
RecvPid;
|
RecvPid;
|
||||||
_ ->
|
_ ->
|
||||||
gen_tcp:close(Socket),
|
gen_tcp:close(Socket),
|
||||||
none
|
none
|
||||||
end,
|
end,
|
||||||
?INFO_MSG("(~p) Accepted connection ~s:~p -> ~s:~p",
|
?INFO_MSG("(~p) Accepted connection ~s -> ~s",
|
||||||
[Receiver,
|
[Receiver,
|
||||||
ejabberd_config:may_hide_data(inet_parse:ntoa(PAddr)),
|
ejabberd_config:may_hide_data(
|
||||||
PPort, inet_parse:ntoa(Addr), Port]);
|
format_endpoint({PPort, PAddr, tcp})),
|
||||||
|
format_endpoint({Port, Addr, tcp})]);
|
||||||
_ ->
|
_ ->
|
||||||
gen_tcp:close(Socket)
|
gen_tcp:close(Socket)
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
accept(ListenSocket, Module, Opts, Sup, NewInterval, Arity);
|
accept(ListenSocket, Module, State, Sup, NewInterval, Proxy, Arity);
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
?ERROR_MSG("(~w) Failed TCP accept: ~s",
|
?ERROR_MSG("(~w) Failed TCP accept: ~s",
|
||||||
[ListenSocket, inet:format_error(Reason)]),
|
[ListenSocket, format_error(Reason)]),
|
||||||
accept(ListenSocket, Module, Opts, Sup, NewInterval, Arity)
|
accept(ListenSocket, Module, State, Sup, NewInterval, Proxy, Arity)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec udp_recv(inet:socket(), module(), listen_opts()) -> no_return().
|
-spec udp_recv(inet:socket(), module(), state()) -> no_return().
|
||||||
udp_recv(Socket, Module, Opts) ->
|
udp_recv(Socket, Module, State) ->
|
||||||
case gen_udp:recv(Socket, 0) of
|
case gen_udp:recv(Socket, 0) of
|
||||||
{ok, {Addr, Port, Packet}} ->
|
{ok, {Addr, Port, Packet}} ->
|
||||||
case catch Module:udp_recv(Socket, Addr, Port, Packet, Opts) of
|
case catch Module:udp_recv(Socket, Addr, Port, Packet, State) of
|
||||||
{'EXIT', Reason} ->
|
{'EXIT', Reason} ->
|
||||||
?ERROR_MSG("failed to process UDP packet:~n"
|
?ERROR_MSG("Failed to process UDP packet:~n"
|
||||||
"** Source: {~p, ~p}~n"
|
"** Source: {~p, ~p}~n"
|
||||||
"** Reason: ~p~n** Packet: ~p",
|
"** Reason: ~p~n** Packet: ~p",
|
||||||
[Addr, Port, Reason, Packet]),
|
[Addr, Port, Reason, Packet]),
|
||||||
udp_recv(Socket, Module, Opts);
|
udp_recv(Socket, Module, State);
|
||||||
NewOpts ->
|
NewState ->
|
||||||
udp_recv(Socket, Module, NewOpts)
|
udp_recv(Socket, Module, NewState)
|
||||||
end;
|
end;
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
?ERROR_MSG("unexpected UDP error: ~s", [format_error(Reason)]),
|
?ERROR_MSG("Unexpected UDP error: ~s", [format_error(Reason)]),
|
||||||
throw({error, Reason})
|
throw({error, Reason})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec start_connection(module(), 2|3, inet:socket(), listen_opts(), atom()) ->
|
-spec start_connection(module(), 2|3, inet:socket(), state(), atom()) ->
|
||||||
{ok, pid()} | {error, any()} | ignore.
|
{ok, pid()} | {error, any()} | ignore.
|
||||||
start_connection(Module, Arity, Socket, Opts, Sup) ->
|
start_connection(Module, Arity, Socket, State, Sup) ->
|
||||||
Res = case Sup of
|
Res = case Sup of
|
||||||
undefined when Arity == 3 ->
|
undefined when Arity == 3 ->
|
||||||
Module:start(gen_tcp, Socket, Opts);
|
Module:start(gen_tcp, Socket, State);
|
||||||
undefined ->
|
undefined ->
|
||||||
Module:start({gen_tcp, Socket}, Opts);
|
Module:start({gen_tcp, Socket}, State);
|
||||||
_ when Arity == 3 ->
|
_ when Arity == 3 ->
|
||||||
supervisor:start_child(Sup, [gen_tcp, Socket, Opts]);
|
supervisor:start_child(Sup, [gen_tcp, Socket, State]);
|
||||||
_ ->
|
_ ->
|
||||||
supervisor:start_child(Sup, [{gen_tcp, Socket}, Opts])
|
supervisor:start_child(Sup, [{gen_tcp, Socket}, State])
|
||||||
end,
|
end,
|
||||||
case Res of
|
case Res of
|
||||||
{ok, Pid} ->
|
{ok, Pid} ->
|
||||||
@ -303,7 +309,7 @@ start_connection(Module, Arity, Socket, Opts, Sup) ->
|
|||||||
Err
|
Err
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec start_listener(endpoint(), module(), listen_opts()) ->
|
-spec start_listener(endpoint(), module(), opts()) ->
|
||||||
{ok, pid()} | {error, any()}.
|
{ok, pid()} | {error, any()}.
|
||||||
start_listener(EndPoint, Module, Opts) ->
|
start_listener(EndPoint, Module, Opts) ->
|
||||||
%% It is only required to start the supervisor in some cases.
|
%% It is only required to start the supervisor in some cases.
|
||||||
@ -323,9 +329,9 @@ start_listener(EndPoint, Module, Opts) ->
|
|||||||
{error, Error}
|
{error, Error}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec start_module_sup(module(), [proplists:property()]) -> atom().
|
-spec start_module_sup(module(), opts()) -> atom().
|
||||||
start_module_sup(Module, Opts) ->
|
start_module_sup(Module, Opts) ->
|
||||||
case proplists:get_value(supervisor, Opts, true) of
|
case maps:get(supervisor, Opts) of
|
||||||
true ->
|
true ->
|
||||||
Proc = list_to_atom(atom_to_list(Module) ++ "_sup"),
|
Proc = list_to_atom(atom_to_list(Module) ++ "_sup"),
|
||||||
ChildSpec = {Proc, {ejabberd_tmp_sup, start_link, [Proc, Module]},
|
ChildSpec = {Proc, {ejabberd_tmp_sup, start_link, [Proc, Module]},
|
||||||
@ -333,13 +339,15 @@ start_module_sup(Module, Opts) ->
|
|||||||
infinity,
|
infinity,
|
||||||
supervisor,
|
supervisor,
|
||||||
[ejabberd_tmp_sup]},
|
[ejabberd_tmp_sup]},
|
||||||
supervisor:start_child(ejabberd_sup, ChildSpec),
|
case supervisor:start_child(ejabberd_sup, ChildSpec) of
|
||||||
Proc;
|
{ok, _} -> Proc;
|
||||||
|
_ -> undefined
|
||||||
|
end;
|
||||||
false ->
|
false ->
|
||||||
undefined
|
undefined
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec start_listener_sup(endpoint(), module(), listen_opts()) ->
|
-spec start_listener_sup(endpoint(), module(), opts()) ->
|
||||||
{ok, pid()} | {error, any()}.
|
{ok, pid()} | {error, any()}.
|
||||||
start_listener_sup(EndPoint, Module, Opts) ->
|
start_listener_sup(EndPoint, Module, Opts) ->
|
||||||
ChildSpec = {EndPoint,
|
ChildSpec = {EndPoint,
|
||||||
@ -352,19 +360,19 @@ start_listener_sup(EndPoint, Module, Opts) ->
|
|||||||
|
|
||||||
-spec stop_listeners() -> ok.
|
-spec stop_listeners() -> ok.
|
||||||
stop_listeners() ->
|
stop_listeners() ->
|
||||||
Ports = ejabberd_config:get_option(listen, []),
|
Ports = ejabberd_option:listen(),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun({PortIpNetp, Module, _Opts}) ->
|
fun({PortIpNetp, Module, _Opts}) ->
|
||||||
delete_listener(PortIpNetp, Module)
|
delete_listener(PortIpNetp, Module)
|
||||||
end,
|
end,
|
||||||
Ports).
|
Ports).
|
||||||
|
|
||||||
-spec stop_listener(endpoint(), module()) -> ok | {error, any()}.
|
-spec stop_listener(endpoint(), module(), opts()) -> ok | {error, any()}.
|
||||||
stop_listener({_, _, Transport} = EndPoint, Module) ->
|
stop_listener({_, _, Transport} = EndPoint, Module, Opts) ->
|
||||||
case supervisor:terminate_child(?MODULE, EndPoint) of
|
case supervisor:terminate_child(?MODULE, EndPoint) of
|
||||||
ok ->
|
ok ->
|
||||||
?INFO_MSG("Stop accepting ~s connections at ~s for ~p",
|
?INFO_MSG("Stop accepting ~s connections at ~s for ~p",
|
||||||
[case Transport of udp -> "UDP"; tcp -> "TCP" end,
|
[format_transport(Transport, Opts),
|
||||||
format_endpoint(EndPoint), Module]),
|
format_endpoint(EndPoint), Module]),
|
||||||
ets:delete(?MODULE, EndPoint),
|
ets:delete(?MODULE, EndPoint),
|
||||||
supervisor:delete_child(?MODULE, EndPoint);
|
supervisor:delete_child(?MODULE, EndPoint);
|
||||||
@ -372,9 +380,10 @@ stop_listener({_, _, Transport} = EndPoint, Module) ->
|
|||||||
Err
|
Err
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec add_listener(endpoint(), module(), listen_opts()) -> ok | {error, any()}.
|
-spec add_listener(endpoint(), module(), opts()) -> ok | {error, any()}.
|
||||||
add_listener(EndPoint, Module, Opts) ->
|
add_listener(EndPoint, Module, Opts) ->
|
||||||
case start_listener(EndPoint, Module, Opts) of
|
Opts1 = apply_defaults(Module, Opts),
|
||||||
|
case start_listener(EndPoint, Module, Opts1) of
|
||||||
{ok, _Pid} ->
|
{ok, _Pid} ->
|
||||||
ok;
|
ok;
|
||||||
{error, {already_started, _Pid}} ->
|
{error, {already_started, _Pid}} ->
|
||||||
@ -385,17 +394,30 @@ add_listener(EndPoint, Module, Opts) ->
|
|||||||
|
|
||||||
-spec delete_listener(endpoint(), module()) -> ok | {error, any()}.
|
-spec delete_listener(endpoint(), module()) -> ok | {error, any()}.
|
||||||
delete_listener(EndPoint, Module) ->
|
delete_listener(EndPoint, Module) ->
|
||||||
stop_listener(EndPoint, Module).
|
try ets:lookup_element(?MODULE, EndPoint, 3) of
|
||||||
|
Opts -> stop_listener(EndPoint, Module, Opts)
|
||||||
|
catch _:badarg ->
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec tls_listeners() -> [module()].
|
||||||
|
tls_listeners() ->
|
||||||
|
lists:usort(
|
||||||
|
lists:filtermap(
|
||||||
|
fun({_, Module, #{tls := true}}) -> {true, Module};
|
||||||
|
({_, Module, #{starttls := true}}) -> {true, Module};
|
||||||
|
(_) -> false
|
||||||
|
end, ets:tab2list(?MODULE))).
|
||||||
|
|
||||||
-spec config_reloaded() -> ok.
|
-spec config_reloaded() -> ok.
|
||||||
config_reloaded() ->
|
config_reloaded() ->
|
||||||
New = ejabberd_config:get_option(listen, []),
|
New = ejabberd_option:listen(),
|
||||||
Old = ets:tab2list(?MODULE),
|
Old = ets:tab2list(?MODULE),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun({EndPoint, Module, _Opts}) ->
|
fun({EndPoint, Module, Opts}) ->
|
||||||
case lists:keyfind(EndPoint, 1, New) of
|
case lists:keyfind(EndPoint, 1, New) of
|
||||||
false ->
|
false ->
|
||||||
stop_listener(EndPoint, Module);
|
stop_listener(EndPoint, Module, Opts);
|
||||||
_ ->
|
_ ->
|
||||||
ok
|
ok
|
||||||
end
|
end
|
||||||
@ -405,8 +427,8 @@ config_reloaded() ->
|
|||||||
case lists:keyfind(EndPoint, 1, Old) of
|
case lists:keyfind(EndPoint, 1, Old) of
|
||||||
{_, Module, Opts} ->
|
{_, Module, Opts} ->
|
||||||
ok;
|
ok;
|
||||||
{_, OldModule, _} ->
|
{_, OldModule, OldOpts} ->
|
||||||
stop_listener(EndPoint, OldModule),
|
_ = stop_listener(EndPoint, OldModule, OldOpts),
|
||||||
ets:insert(?MODULE, {EndPoint, Module, Opts}),
|
ets:insert(?MODULE, {EndPoint, Module, Opts}),
|
||||||
start_listener(EndPoint, Module, Opts);
|
start_listener(EndPoint, Module, Opts);
|
||||||
false ->
|
false ->
|
||||||
@ -415,22 +437,12 @@ config_reloaded() ->
|
|||||||
end
|
end
|
||||||
end, New).
|
end, New).
|
||||||
|
|
||||||
-spec get_certfiles() -> [binary()].
|
|
||||||
get_certfiles() ->
|
|
||||||
lists:filtermap(
|
|
||||||
fun({_, _, Opts}) ->
|
|
||||||
case proplists:get_value(certfile, Opts) of
|
|
||||||
undefined -> false;
|
|
||||||
Cert -> {true, Cert}
|
|
||||||
end
|
|
||||||
end, ets:tab2list(?MODULE)).
|
|
||||||
|
|
||||||
-spec report_socket_error(inet:posix(), endpoint(), module()) -> ok.
|
-spec report_socket_error(inet:posix(), endpoint(), module()) -> ok.
|
||||||
report_socket_error(Reason, EndPoint, Module) ->
|
report_socket_error(Reason, EndPoint, Module) ->
|
||||||
?ERROR_MSG("Failed to open socket at ~s for ~s: ~s",
|
?ERROR_MSG("Failed to open socket at ~s for ~s: ~s",
|
||||||
[format_endpoint(EndPoint), Module, format_error(Reason)]).
|
[format_endpoint(EndPoint), Module, format_error(Reason)]).
|
||||||
|
|
||||||
-spec format_error(inet:posix()) -> string().
|
-spec format_error(inet:posix() | atom()) -> string().
|
||||||
format_error(Reason) ->
|
format_error(Reason) ->
|
||||||
case inet:format_error(Reason) of
|
case inet:format_error(Reason) of
|
||||||
"unknown POSIX error" ->
|
"unknown POSIX error" ->
|
||||||
@ -447,8 +459,17 @@ format_endpoint({Port, IP, _Transport}) ->
|
|||||||
end,
|
end,
|
||||||
IPStr ++ ":" ++ integer_to_list(Port).
|
IPStr ++ ":" ++ integer_to_list(Port).
|
||||||
|
|
||||||
-spec check_rate_limit(non_neg_integer()) -> non_neg_integer().
|
-spec format_transport(transport(), opts()) -> string().
|
||||||
check_rate_limit(Interval) ->
|
format_transport(Transport, Opts) ->
|
||||||
|
case maps:get(tls, Opts, false) of
|
||||||
|
true when Transport == tcp -> "TLS";
|
||||||
|
true when Transport == udp -> "DTLS";
|
||||||
|
false when Transport == tcp -> "TCP";
|
||||||
|
false when Transport == udp -> "UDP"
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec apply_rate_limit(non_neg_integer()) -> non_neg_integer().
|
||||||
|
apply_rate_limit(Interval) ->
|
||||||
NewInterval = receive
|
NewInterval = receive
|
||||||
{rate_limit, AcceptInterval} ->
|
{rate_limit, AcceptInterval} ->
|
||||||
AcceptInterval
|
AcceptInterval
|
||||||
@ -473,171 +494,71 @@ check_rate_limit(Interval) ->
|
|||||||
end,
|
end,
|
||||||
NewInterval.
|
NewInterval.
|
||||||
|
|
||||||
transform_option({{Port, IP, Transport}, Mod, Opts}) ->
|
-spec validator() -> econf:validator().
|
||||||
IPStr = if is_tuple(IP) ->
|
validator() ->
|
||||||
list_to_binary(inet_parse:ntoa(IP));
|
econf:and_then(
|
||||||
|
econf:list(
|
||||||
|
econf:and_then(
|
||||||
|
econf:options(
|
||||||
|
#{module => listen_opt_type(module),
|
||||||
|
transport => listen_opt_type(transport),
|
||||||
|
'_' => econf:any()},
|
||||||
|
[{required, [module]}]),
|
||||||
|
fun(Opts) ->
|
||||||
|
M = proplists:get_value(module, Opts),
|
||||||
|
T = proplists:get_value(transport, Opts, tcp),
|
||||||
|
(validator(M, T))(Opts)
|
||||||
|
end)),
|
||||||
|
fun prepare_opts/1).
|
||||||
|
|
||||||
|
-spec validator(module(), transport()) -> econf:validator().
|
||||||
|
validator(M, T) ->
|
||||||
|
Options = listen_options() ++ M:listen_options(),
|
||||||
|
Required = lists:usort([Opt || Opt <- Options, is_atom(Opt)]),
|
||||||
|
Disallowed = if T == udp ->
|
||||||
|
[backlog, use_proxy_protocol, accept_interval];
|
||||||
true ->
|
true ->
|
||||||
IP
|
[]
|
||||||
end,
|
end,
|
||||||
Opts1 = lists:map(
|
Validator = maps:from_list(
|
||||||
fun({ip, IPT}) when is_tuple(IPT) ->
|
lists:map(
|
||||||
{ip, list_to_binary(inet_parse:ntoa(IP))};
|
fun(Opt) ->
|
||||||
(ssl) -> {tls, true};
|
try {Opt, M:listen_opt_type(Opt)}
|
||||||
(A) when is_atom(A) -> {A, true};
|
catch _:_ when M /= ?MODULE ->
|
||||||
(Opt) -> Opt
|
{Opt, listen_opt_type(Opt)}
|
||||||
end, Opts),
|
|
||||||
Opts2 = lists:foldl(
|
|
||||||
fun(Opt, Acc) ->
|
|
||||||
try
|
|
||||||
Mod:transform_listen_option(Opt, Acc)
|
|
||||||
catch error:undef ->
|
|
||||||
[Opt|Acc]
|
|
||||||
end
|
end
|
||||||
end, [], Opts1),
|
end, proplists:get_keys(Options))),
|
||||||
TransportOpt = if Transport == tcp -> [];
|
econf:options(
|
||||||
true -> [{transport, Transport}]
|
Validator,
|
||||||
end,
|
[{required, Required}, {disallowed, Disallowed},
|
||||||
IPOpt = if IPStr == <<"0.0.0.0">> -> [];
|
{return, map}, unique]).
|
||||||
true -> [{ip, IPStr}]
|
|
||||||
end,
|
|
||||||
IPOpt ++ TransportOpt ++ [{port, Port}, {module, Mod} | Opts2];
|
|
||||||
transform_option({{Port, Transport}, Mod, Opts})
|
|
||||||
when Transport == tcp orelse Transport == udp ->
|
|
||||||
transform_option({{Port, all_zero_ip(Opts), Transport}, Mod, Opts});
|
|
||||||
transform_option({{Port, IP}, Mod, Opts}) ->
|
|
||||||
transform_option({{Port, IP, tcp}, Mod, Opts});
|
|
||||||
transform_option({Port, Mod, Opts}) ->
|
|
||||||
transform_option({{Port, all_zero_ip(Opts), tcp}, Mod, Opts});
|
|
||||||
transform_option(Opt) ->
|
|
||||||
Opt.
|
|
||||||
|
|
||||||
transform_options(Opts) ->
|
-spec prepare_opts([opts()]) -> [listener()].
|
||||||
lists:foldl(fun transform_options/2, [], Opts).
|
prepare_opts(Listeners) ->
|
||||||
|
check_overlapping_listeners(
|
||||||
transform_options({listen, LOpts}, Opts) ->
|
lists:map(
|
||||||
[{listen, lists:map(fun transform_option/1, LOpts)} | Opts];
|
fun(Opts1) ->
|
||||||
transform_options(Opt, Opts) ->
|
{Opts2, Opts3} = partition(
|
||||||
[Opt|Opts].
|
|
||||||
|
|
||||||
-spec validate_cfg(list()) -> [listener()].
|
|
||||||
validate_cfg(Listeners) ->
|
|
||||||
Listeners1 = lists:map(fun validate_opts/1, Listeners),
|
|
||||||
Listeners2 = lists:keysort(1, Listeners1),
|
|
||||||
check_overlapping_listeners(Listeners2).
|
|
||||||
|
|
||||||
-spec validate_module(module()) -> ok.
|
|
||||||
validate_module(Mod) ->
|
|
||||||
case code:ensure_loaded(Mod) of
|
|
||||||
{module, Mod} ->
|
|
||||||
lists:foreach(
|
|
||||||
fun({Fun, Arities}) ->
|
|
||||||
case lists:any(
|
|
||||||
fun(Arity) ->
|
|
||||||
erlang:function_exported(Mod, Fun, Arity)
|
|
||||||
end, Arities) of
|
|
||||||
true -> ok;
|
|
||||||
false ->
|
|
||||||
?ERROR_MSG("Failed to load listening module ~s, "
|
|
||||||
"because it doesn't export ~s/~B callback. "
|
|
||||||
"The module is either not a listening module "
|
|
||||||
"or it is a third-party module which "
|
|
||||||
"requires update",
|
|
||||||
[Mod, Fun, hd(Arities)]),
|
|
||||||
erlang:error(badarg)
|
|
||||||
end
|
|
||||||
end, [{start, [3,2]}, {start_link, [3,2]},
|
|
||||||
{accept, [1]}, {listen_options, [0]}]);
|
|
||||||
_ ->
|
|
||||||
?ERROR_MSG("Failed to load unknown listening module ~s: "
|
|
||||||
"make sure there is no typo and ~s.beam "
|
|
||||||
"exists inside either ~s or ~s directory",
|
|
||||||
[Mod, Mod,
|
|
||||||
filename:dirname(code:which(?MODULE)),
|
|
||||||
ext_mod:modules_dir()]),
|
|
||||||
erlang:error(badarg)
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec validate_opts(listen_opts()) -> listener().
|
|
||||||
validate_opts(Opts) ->
|
|
||||||
case lists:keyfind(module, 1, Opts) of
|
|
||||||
{_, Mod} ->
|
|
||||||
validate_module(Mod),
|
|
||||||
Opts1 = validate_opts(Mod, Opts),
|
|
||||||
{Opts2, Opts3} = lists:partition(
|
|
||||||
fun({port, _}) -> true;
|
fun({port, _}) -> true;
|
||||||
({transport, _}) -> true;
|
({transport, _}) -> true;
|
||||||
({module, _}) -> true;
|
({module, _}) -> true;
|
||||||
(_) -> false
|
(_) -> false
|
||||||
end, Opts1),
|
end, Opts1),
|
||||||
Port = proplists:get_value(port, Opts2),
|
Mod = maps:get(module, Opts2),
|
||||||
Transport = proplists:get_value(transport, Opts2, tcp),
|
Port = maps:get(port, Opts2),
|
||||||
IP = proplists:get_value(ip, Opts3, all_zero_ip(Opts3)),
|
Transport = maps:get(transport, Opts2, tcp),
|
||||||
{{Port, IP, Transport}, Mod, Opts3};
|
IP = maps:get(ip, Opts3, {0,0,0,0}),
|
||||||
false ->
|
Opts4 = apply_defaults(Mod, Opts3),
|
||||||
?ERROR_MSG("Missing required listening option: module", []),
|
{{Port, IP, Transport}, Mod, Opts4}
|
||||||
erlang:error(badarg)
|
end, Listeners)).
|
||||||
end.
|
|
||||||
|
|
||||||
-spec validate_opts(module(), listen_opts()) -> listen_opts().
|
|
||||||
validate_opts(Mod, Opts) ->
|
|
||||||
Defaults = listen_options() ++ Mod:listen_options(),
|
|
||||||
{Opts1, Defaults1} =
|
|
||||||
lists:mapfoldl(
|
|
||||||
fun({Opt, Val} = OptVal, Defs) ->
|
|
||||||
case proplists:is_defined(Opt, Defaults) of
|
|
||||||
true ->
|
|
||||||
NewOptVal = case lists:member(OptVal, Defaults) of
|
|
||||||
true -> [];
|
|
||||||
false -> [validate_module_opt(Mod, Opt, Val)]
|
|
||||||
end,
|
|
||||||
{NewOptVal, proplists:delete(Opt, Defs)};
|
|
||||||
false ->
|
|
||||||
?ERROR_MSG("Unknown listening option '~s' of "
|
|
||||||
"module ~s; available options are: ~s",
|
|
||||||
[Opt, Mod,
|
|
||||||
misc:join_atoms(
|
|
||||||
proplists:get_keys(Defaults),
|
|
||||||
<<", ">>)]),
|
|
||||||
erlang:error(badarg)
|
|
||||||
end
|
|
||||||
end, Defaults, Opts),
|
|
||||||
case lists:filter(fun is_atom/1, Defaults1) of
|
|
||||||
[] ->
|
|
||||||
lists:flatten(Opts1);
|
|
||||||
MissingRequiredOpts ->
|
|
||||||
?ERROR_MSG("Missing required listening option(s): ~s",
|
|
||||||
[misc:join_atoms(MissingRequiredOpts, <<", ">>)]),
|
|
||||||
erlang:error(badarg)
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec validate_module_opt(module(), atom(), any()) -> {atom(), any()}.
|
|
||||||
validate_module_opt(Module, Opt, Val) ->
|
|
||||||
VFun = try Module:listen_opt_type(Opt)
|
|
||||||
catch _:_ -> listen_opt_type(Opt)
|
|
||||||
end,
|
|
||||||
try {Opt, VFun(Val)}
|
|
||||||
catch _:R when R /= undef ->
|
|
||||||
?ERROR_MSG("Invalid value of listening option ~s: ~s",
|
|
||||||
[Opt, misc:format_val({yaml, Val})]),
|
|
||||||
erlang:error(badarg)
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec all_zero_ip(listen_opts()) -> inet:ip_address().
|
|
||||||
all_zero_ip(Opts) ->
|
|
||||||
case proplists:get_bool(inet6, Opts) of
|
|
||||||
true -> {0,0,0,0,0,0,0,0};
|
|
||||||
false -> {0,0,0,0}
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec check_overlapping_listeners([listener()]) -> [listener()].
|
-spec check_overlapping_listeners([listener()]) -> [listener()].
|
||||||
check_overlapping_listeners(Listeners) ->
|
check_overlapping_listeners(Listeners) ->
|
||||||
lists:foldl(
|
_ = lists:foldl(
|
||||||
fun({{Port, IP, Transport} = Key, _, _}, Acc) ->
|
fun({{Port, IP, Transport} = Key, _, _}, Acc) ->
|
||||||
case lists:member(Key, Acc) of
|
case lists:member(Key, Acc) of
|
||||||
true ->
|
true ->
|
||||||
?ERROR_MSG("Overlapping listeners found at ~s",
|
econf:fail({listener_dup, {IP, Port}});
|
||||||
[format_endpoint(Key)]),
|
|
||||||
erlang:error(badarg);
|
|
||||||
false ->
|
false ->
|
||||||
ZeroIP = case size(IP) of
|
ZeroIP = case size(IP) of
|
||||||
8 -> {0,0,0,0,0,0,0,0};
|
8 -> {0,0,0,0,0,0,0,0};
|
||||||
@ -646,10 +567,8 @@ check_overlapping_listeners(Listeners) ->
|
|||||||
Key1 = {Port, ZeroIP, Transport},
|
Key1 = {Port, ZeroIP, Transport},
|
||||||
case lists:member(Key1, Acc) of
|
case lists:member(Key1, Acc) of
|
||||||
true ->
|
true ->
|
||||||
?ERROR_MSG(
|
econf:fail({listener_conflict,
|
||||||
"Overlapping listeners found at ~s and ~s",
|
{IP, Port}, {ZeroIP, Port}});
|
||||||
[format_endpoint(Key), format_endpoint(Key1)]),
|
|
||||||
erlang:error(badarg);
|
|
||||||
false ->
|
false ->
|
||||||
[Key|Acc]
|
[Key|Acc]
|
||||||
end
|
end
|
||||||
@ -657,134 +576,89 @@ check_overlapping_listeners(Listeners) ->
|
|||||||
end, [], Listeners),
|
end, [], Listeners),
|
||||||
Listeners.
|
Listeners.
|
||||||
|
|
||||||
|
-spec apply_defaults(module(), opts()) -> opts().
|
||||||
|
apply_defaults(Mod, Opts) ->
|
||||||
|
lists:foldl(
|
||||||
|
fun({Opt, Default}, M) ->
|
||||||
|
case maps:is_key(Opt, M) of
|
||||||
|
true -> M;
|
||||||
|
false -> M#{Opt => Default}
|
||||||
|
end;
|
||||||
|
(_, M) ->
|
||||||
|
M
|
||||||
|
end, Opts, Mod:listen_options() ++ listen_options()).
|
||||||
|
|
||||||
|
%% Convert options to list with removing defaults
|
||||||
|
-spec opts_to_list(module(), opts()) -> list_opts().
|
||||||
|
opts_to_list(Mod, Opts) ->
|
||||||
|
Defaults = Mod:listen_options() ++ listen_options(),
|
||||||
|
maps:fold(
|
||||||
|
fun(Opt, Val, Acc) ->
|
||||||
|
case proplists:get_value(Opt, Defaults) of
|
||||||
|
Val -> Acc;
|
||||||
|
_ -> [{Opt, Val}|Acc]
|
||||||
|
end
|
||||||
|
end, [], Opts).
|
||||||
|
|
||||||
|
-spec partition(fun(({atom(), term()}) -> boolean()), opts()) -> {opts(), opts()}.
|
||||||
|
partition(Fun, Opts) ->
|
||||||
|
maps:fold(
|
||||||
|
fun(Opt, Val, {True, False}) ->
|
||||||
|
case Fun({Opt, Val}) of
|
||||||
|
true -> {True#{Opt => Val}, False};
|
||||||
|
false -> {True, False#{Opt => Val}}
|
||||||
|
end
|
||||||
|
end, {#{}, #{}}, Opts).
|
||||||
|
|
||||||
|
-spec listen_opt_type(atom()) -> econf:validator().
|
||||||
listen_opt_type(port) ->
|
listen_opt_type(port) ->
|
||||||
fun(I) when is_integer(I), I>0, I<65536 -> I end;
|
econf:int(0, 65535);
|
||||||
listen_opt_type(module) ->
|
listen_opt_type(module) ->
|
||||||
fun(A) when is_atom(A) -> A end;
|
econf:beam([[{start, 3}, {start, 2}],
|
||||||
|
[{start_link, 3}, {start_link, 2}],
|
||||||
|
{accept, 1}, {listen_options, 0}]);
|
||||||
listen_opt_type(ip) ->
|
listen_opt_type(ip) ->
|
||||||
fun(S) ->
|
econf:ip();
|
||||||
{ok, Addr} = inet_parse:address(binary_to_list(S)),
|
|
||||||
Addr
|
|
||||||
end;
|
|
||||||
listen_opt_type(transport) ->
|
listen_opt_type(transport) ->
|
||||||
fun(tcp) -> tcp;
|
econf:enum([tcp, udp]);
|
||||||
(udp) -> udp
|
|
||||||
end;
|
|
||||||
listen_opt_type(accept_interval) ->
|
listen_opt_type(accept_interval) ->
|
||||||
fun(I) when is_integer(I), I>=0 -> I end;
|
econf:non_neg_int();
|
||||||
listen_opt_type(backlog) ->
|
listen_opt_type(backlog) ->
|
||||||
fun(I) when is_integer(I), I>=0 -> I end;
|
econf:non_neg_int();
|
||||||
listen_opt_type(inet) ->
|
|
||||||
fun(B) when is_boolean(B) -> B end;
|
|
||||||
listen_opt_type(inet6) ->
|
|
||||||
fun(B) when is_boolean(B) -> B end;
|
|
||||||
listen_opt_type(supervisor) ->
|
listen_opt_type(supervisor) ->
|
||||||
fun(B) when is_boolean(B) -> B end;
|
econf:bool();
|
||||||
|
listen_opt_type(ciphers) ->
|
||||||
|
econf:binary();
|
||||||
|
listen_opt_type(dhfile) ->
|
||||||
|
econf:file();
|
||||||
|
listen_opt_type(cafile) ->
|
||||||
|
econf:pem();
|
||||||
listen_opt_type(certfile) ->
|
listen_opt_type(certfile) ->
|
||||||
fun(S) ->
|
econf:pem();
|
||||||
{ok, File} = ejabberd_pkix:add_certfile(S),
|
|
||||||
File
|
|
||||||
end;
|
|
||||||
listen_opt_type(ciphers) -> fun iolist_to_binary/1;
|
|
||||||
listen_opt_type(dhfile) -> fun misc:try_read_file/1;
|
|
||||||
listen_opt_type(cafile) -> fun ejabberd_pkix:try_certfile/1;
|
|
||||||
listen_opt_type(protocol_options) ->
|
listen_opt_type(protocol_options) ->
|
||||||
fun (Options) -> str:join(Options, <<"|">>) end;
|
econf:and_then(
|
||||||
|
econf:list(econf:binary()),
|
||||||
|
fun(Options) -> str:join(Options, <<"|">>) end);
|
||||||
listen_opt_type(tls_compression) ->
|
listen_opt_type(tls_compression) ->
|
||||||
fun(B) when is_boolean(B) -> B end;
|
econf:bool();
|
||||||
listen_opt_type(tls) ->
|
listen_opt_type(tls) ->
|
||||||
fun(B) when is_boolean(B) -> B end;
|
econf:bool();
|
||||||
listen_opt_type(max_stanza_size) ->
|
listen_opt_type(max_stanza_size) ->
|
||||||
fun(I) when is_integer(I), I>0 -> I;
|
econf:pos_int(infinity);
|
||||||
(unlimited) -> infinity;
|
|
||||||
(infinity) -> infinity
|
|
||||||
end;
|
|
||||||
listen_opt_type(max_fsm_queue) ->
|
listen_opt_type(max_fsm_queue) ->
|
||||||
fun(I) when is_integer(I), I>0 -> I end;
|
econf:pos_int();
|
||||||
listen_opt_type(shaper) ->
|
listen_opt_type(shaper) ->
|
||||||
fun acl:shaper_rules_validator/1;
|
econf:shaper();
|
||||||
listen_opt_type(access) ->
|
listen_opt_type(access) ->
|
||||||
fun acl:access_rules_validator/1;
|
econf:acl();
|
||||||
listen_opt_type(use_proxy_protocol) ->
|
listen_opt_type(use_proxy_protocol) ->
|
||||||
fun(B) when is_boolean(B) -> B end.
|
econf:bool().
|
||||||
|
|
||||||
listen_options() ->
|
listen_options() ->
|
||||||
[module, port,
|
[module, port,
|
||||||
{transport, tcp},
|
{transport, tcp},
|
||||||
{ip, <<"0.0.0.0">>},
|
{ip, {0,0,0,0}},
|
||||||
{inet, true},
|
|
||||||
{inet6, false},
|
|
||||||
{accept_interval, 0},
|
{accept_interval, 0},
|
||||||
{backlog, 5},
|
{backlog, 5},
|
||||||
{use_proxy_protocol, false},
|
{use_proxy_protocol, false},
|
||||||
{supervisor, true}].
|
{supervisor, true}].
|
||||||
|
|
||||||
opt_type(listen) -> fun validate_cfg/1;
|
|
||||||
opt_type(_) -> [listen].
|
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
%%% Some legacy code used by ejabberd_web_admin only
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
parse_listener_portip(PortIP, Opts) ->
|
|
||||||
{IPOpt, Opts2} = strip_ip_option(Opts),
|
|
||||||
{IPVOpt, OptsClean} = case proplists:get_bool(inet6, Opts2) of
|
|
||||||
true -> {inet6, proplists:delete(inet6, Opts2)};
|
|
||||||
false -> {inet, Opts2}
|
|
||||||
end,
|
|
||||||
{Port, IPT, Proto} =
|
|
||||||
case add_proto(PortIP, Opts) of
|
|
||||||
{P, Prot} ->
|
|
||||||
T = get_ip_tuple(IPOpt, IPVOpt),
|
|
||||||
{P, T, Prot};
|
|
||||||
{P, T, Prot} when is_integer(P) and is_tuple(T) ->
|
|
||||||
{P, T, Prot};
|
|
||||||
{P, S, Prot} when is_integer(P) and is_binary(S) ->
|
|
||||||
{ok, T} = inet_parse:address(binary_to_list(S)),
|
|
||||||
{P, T, Prot}
|
|
||||||
end,
|
|
||||||
IPV = case tuple_size(IPT) of
|
|
||||||
4 -> inet;
|
|
||||||
8 -> inet6
|
|
||||||
end,
|
|
||||||
{Port, IPT, IPV, Proto, OptsClean}.
|
|
||||||
|
|
||||||
add_proto(Port, Opts) when is_integer(Port) ->
|
|
||||||
{Port, get_proto(Opts)};
|
|
||||||
add_proto({Port, Proto}, _Opts) when is_atom(Proto) ->
|
|
||||||
{Port, normalize_proto(Proto)};
|
|
||||||
add_proto({Port, Addr}, Opts) ->
|
|
||||||
{Port, Addr, get_proto(Opts)};
|
|
||||||
add_proto({Port, Addr, Proto}, _Opts) ->
|
|
||||||
{Port, Addr, normalize_proto(Proto)}.
|
|
||||||
|
|
||||||
strip_ip_option(Opts) ->
|
|
||||||
{IPL, OptsNoIP} = lists:partition(
|
|
||||||
fun({ip, _}) -> true;
|
|
||||||
(_) -> false
|
|
||||||
end,
|
|
||||||
Opts),
|
|
||||||
case IPL of
|
|
||||||
%% Only the first ip option is considered
|
|
||||||
[{ip, T1} | _] ->
|
|
||||||
{T1, OptsNoIP};
|
|
||||||
[] ->
|
|
||||||
{no_ip_option, OptsNoIP}
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_ip_tuple(no_ip_option, inet) ->
|
|
||||||
{0, 0, 0, 0};
|
|
||||||
get_ip_tuple(no_ip_option, inet6) ->
|
|
||||||
{0, 0, 0, 0, 0, 0, 0, 0};
|
|
||||||
get_ip_tuple(IPOpt, _IPVOpt) ->
|
|
||||||
IPOpt.
|
|
||||||
|
|
||||||
get_proto(Opts) ->
|
|
||||||
case proplists:get_value(proto, Opts) of
|
|
||||||
undefined ->
|
|
||||||
tcp;
|
|
||||||
Proto ->
|
|
||||||
normalize_proto(Proto)
|
|
||||||
end.
|
|
||||||
|
|
||||||
normalize_proto(udp) -> udp;
|
|
||||||
normalize_proto(_) -> tcp.
|
|
||||||
|
@ -106,7 +106,7 @@ get_features(Host) ->
|
|||||||
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
process_flag(trap_exit, true),
|
process_flag(trap_exit, true),
|
||||||
lists:foreach(fun host_up/1, ejabberd_config:get_myhosts()),
|
lists:foreach(fun host_up/1, ejabberd_option:hosts()),
|
||||||
ejabberd_hooks:add(host_up, ?MODULE, host_up, 10),
|
ejabberd_hooks:add(host_up, ?MODULE, host_up, 10),
|
||||||
ejabberd_hooks:add(host_down, ?MODULE, host_down, 100),
|
ejabberd_hooks:add(host_down, ?MODULE, host_down, 100),
|
||||||
gen_iq_handler:start(?MODULE),
|
gen_iq_handler:start(?MODULE),
|
||||||
@ -126,7 +126,7 @@ handle_info(Info, State) ->
|
|||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
terminate(_Reason, _State) ->
|
terminate(_Reason, _State) ->
|
||||||
lists:foreach(fun host_down/1, ejabberd_config:get_myhosts()),
|
lists:foreach(fun host_down/1, ejabberd_option:hosts()),
|
||||||
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 10),
|
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 10),
|
||||||
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 100),
|
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 100),
|
||||||
ok.
|
ok.
|
||||||
|
@ -24,21 +24,21 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(ejabberd_logger).
|
-module(ejabberd_logger).
|
||||||
|
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([start/0, restart/0, reopen_log/0, rotate_log/0, get/0, set/1,
|
-export([start/0, restart/0, reopen_log/0, rotate_log/0, get/0, set/1,
|
||||||
get_log_path/0, opt_type/1]).
|
get_log_path/0]).
|
||||||
|
|
||||||
|
|
||||||
-type loglevel() :: 0 | 1 | 2 | 3 | 4 | 5.
|
-type loglevel() :: 0 | 1 | 2 | 3 | 4 | 5.
|
||||||
|
-type lager_level() :: none | emergency | alert | critical |
|
||||||
|
error | warning | notice | info | debug.
|
||||||
|
|
||||||
-spec start() -> ok.
|
-spec start() -> ok.
|
||||||
-spec get_log_path() -> string().
|
-spec get_log_path() -> string().
|
||||||
-spec reopen_log() -> ok.
|
-spec reopen_log() -> ok.
|
||||||
-spec rotate_log() -> ok.
|
-spec rotate_log() -> ok.
|
||||||
-spec get() -> {loglevel(), atom(), string()}.
|
-spec get() -> {loglevel(), atom(), string()}.
|
||||||
-spec set(loglevel() | {loglevel(), list()}) -> {module, module()}.
|
-spec set(loglevel()) -> ok.
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% API
|
%%% API
|
||||||
@ -64,17 +64,6 @@ get_log_path() ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
opt_type(log_rotate_date) ->
|
|
||||||
fun(S) -> binary_to_list(iolist_to_binary(S)) end;
|
|
||||||
opt_type(log_rotate_size) ->
|
|
||||||
fun(I) when is_integer(I), I >= 0 -> I end;
|
|
||||||
opt_type(log_rotate_count) ->
|
|
||||||
fun(I) when is_integer(I), I >= 0 -> I end;
|
|
||||||
opt_type(log_rate_limit) ->
|
|
||||||
fun(I) when is_integer(I), I >= 0 -> I end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[log_rotate_date, log_rotate_size, log_rotate_count, log_rate_limit].
|
|
||||||
|
|
||||||
get_integer_env(Name, Default) ->
|
get_integer_env(Name, Default) ->
|
||||||
case application:get_env(ejabberd, Name) of
|
case application:get_env(ejabberd, Name) of
|
||||||
{ok, I} when is_integer(I), I>=0 ->
|
{ok, I} when is_integer(I), I>=0 ->
|
||||||
@ -130,7 +119,7 @@ do_start_for_logger(Level) ->
|
|||||||
ejabberd:start_app(lager),
|
ejabberd:start_app(lager),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%% Start lager
|
-spec do_start(atom()) -> ok.
|
||||||
do_start(Level) ->
|
do_start(Level) ->
|
||||||
application:load(sasl),
|
application:load(sasl),
|
||||||
application:set_env(sasl, sasl_error_logger, false),
|
application:set_env(sasl, sasl_error_logger, false),
|
||||||
@ -162,11 +151,10 @@ do_start(Level) ->
|
|||||||
ejabberd:start_app(lager),
|
ejabberd:start_app(lager),
|
||||||
lists:foreach(fun(Handler) ->
|
lists:foreach(fun(Handler) ->
|
||||||
lager:set_loghwm(Handler, LogRateLimit)
|
lager:set_loghwm(Handler, LogRateLimit)
|
||||||
end, gen_event:which_handlers(lager_event)),
|
end, gen_event:which_handlers(lager_event)).
|
||||||
ok.
|
|
||||||
|
|
||||||
restart() ->
|
restart() ->
|
||||||
Level = ejabberd_config:get_option(loglevel, 4),
|
Level = ejabberd_option:loglevel(),
|
||||||
application:stop(lager),
|
application:stop(lager),
|
||||||
start(Level).
|
start(Level).
|
||||||
|
|
||||||
@ -199,7 +187,6 @@ get() ->
|
|||||||
debug -> {5, debug, "Debug"}
|
debug -> {5, debug, "Debug"}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @spec (loglevel() | {loglevel(), list()}) -> {module, module()}
|
|
||||||
set(LogLevel) when is_integer(LogLevel) ->
|
set(LogLevel) when is_integer(LogLevel) ->
|
||||||
LagerLogLevel = get_lager_loglevel(LogLevel),
|
LagerLogLevel = get_lager_loglevel(LogLevel),
|
||||||
case get_lager_loglevel() of
|
case get_lager_loglevel() of
|
||||||
@ -216,16 +203,12 @@ set(LogLevel) when is_integer(LogLevel) ->
|
|||||||
lager:set_loglevel(H, LagerLogLevel);
|
lager:set_loglevel(H, LagerLogLevel);
|
||||||
(_) ->
|
(_) ->
|
||||||
ok
|
ok
|
||||||
end, gen_event:which_handlers(lager_event))
|
end, get_lager_handlers())
|
||||||
end,
|
end,
|
||||||
case LogLevel of
|
case LogLevel of
|
||||||
5 -> xmpp:set_config([{debug, true}]);
|
5 -> xmpp:set_config([{debug, true}]);
|
||||||
_ -> xmpp:set_config([{debug, false}])
|
_ -> xmpp:set_config([{debug, false}])
|
||||||
end,
|
end.
|
||||||
{module, lager};
|
|
||||||
set({_LogLevel, _}) ->
|
|
||||||
error_logger:error_msg("custom loglevels are not supported for 'lager'"),
|
|
||||||
{module, lager}.
|
|
||||||
|
|
||||||
get_lager_loglevel() ->
|
get_lager_loglevel() ->
|
||||||
Handlers = get_lager_handlers(),
|
Handlers = get_lager_handlers(),
|
||||||
@ -238,6 +221,7 @@ get_lager_loglevel() ->
|
|||||||
end,
|
end,
|
||||||
none, Handlers).
|
none, Handlers).
|
||||||
|
|
||||||
|
-spec get_lager_loglevel(loglevel()) -> lager_level().
|
||||||
get_lager_loglevel(LogLevel) ->
|
get_lager_loglevel(LogLevel) ->
|
||||||
case LogLevel of
|
case LogLevel of
|
||||||
0 -> none;
|
0 -> none;
|
||||||
@ -245,8 +229,7 @@ get_lager_loglevel(LogLevel) ->
|
|||||||
2 -> error;
|
2 -> error;
|
||||||
3 -> warning;
|
3 -> warning;
|
||||||
4 -> info;
|
4 -> info;
|
||||||
5 -> debug;
|
5 -> debug
|
||||||
E -> erlang:error({wrong_loglevel, E})
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_lager_handlers() ->
|
get_lager_handlers() ->
|
||||||
@ -257,6 +240,7 @@ get_lager_handlers() ->
|
|||||||
Result
|
Result
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec get_lager_version() -> string().
|
||||||
get_lager_version() ->
|
get_lager_version() ->
|
||||||
Apps = application:loaded_applications(),
|
Apps = application:loaded_applications(),
|
||||||
case lists:keyfind(lager, 1, Apps) of
|
case lists:keyfind(lager, 1, Apps) of
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
-module(ejabberd_oauth).
|
-module(ejabberd_oauth).
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([init/1, handle_call/3, handle_cast/2,
|
-export([init/1, handle_call/3, handle_cast/2,
|
||||||
@ -38,7 +37,6 @@
|
|||||||
verify_redirection_uri/3,
|
verify_redirection_uri/3,
|
||||||
authenticate_user/2,
|
authenticate_user/2,
|
||||||
authenticate_client/2,
|
authenticate_client/2,
|
||||||
verify_resowner_scope/3,
|
|
||||||
associate_access_code/3,
|
associate_access_code/3,
|
||||||
associate_access_token/3,
|
associate_access_token/3,
|
||||||
associate_refresh_token/3,
|
associate_refresh_token/3,
|
||||||
@ -47,8 +45,7 @@
|
|||||||
check_token/2,
|
check_token/2,
|
||||||
scope_in_scope_list/2,
|
scope_in_scope_list/2,
|
||||||
process/2,
|
process/2,
|
||||||
config_reloaded/0,
|
config_reloaded/0]).
|
||||||
opt_type/1]).
|
|
||||||
|
|
||||||
-export([get_commands_spec/0,
|
-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]).
|
||||||
@ -73,8 +70,6 @@
|
|||||||
%% * Using the command line and oauth_issue_token command, the token is generated in behalf of ejabberd' sysadmin
|
%% * Using the command line and oauth_issue_token command, the token is generated in behalf of ejabberd' sysadmin
|
||||||
%% (as it has access to ejabberd command line).
|
%% (as it has access to ejabberd command line).
|
||||||
|
|
||||||
-define(EXPIRE, 4294967).
|
|
||||||
|
|
||||||
get_commands_spec() ->
|
get_commands_spec() ->
|
||||||
[
|
[
|
||||||
#ejabberd_commands{name = oauth_issue_token, tags = [oauth],
|
#ejabberd_commands{name = oauth_issue_token, tags = [oauth],
|
||||||
@ -189,9 +184,7 @@ authenticate_user({User, Server}, Ctx) ->
|
|||||||
case jid:make(User, Server) of
|
case jid:make(User, Server) of
|
||||||
#jid{} = JID ->
|
#jid{} = JID ->
|
||||||
Access =
|
Access =
|
||||||
ejabberd_config:get_option(
|
ejabberd_option:oauth_access(JID#jid.lserver),
|
||||||
{oauth_access, JID#jid.lserver},
|
|
||||||
none),
|
|
||||||
case acl:match_rule(JID#jid.lserver, Access, JID) of
|
case acl:match_rule(JID#jid.lserver, Access, JID) of
|
||||||
allow ->
|
allow ->
|
||||||
case Ctx of
|
case Ctx of
|
||||||
@ -214,21 +207,6 @@ authenticate_user({User, Server}, Ctx) ->
|
|||||||
|
|
||||||
authenticate_client(Client, Ctx) -> {ok, {Ctx, {client, Client}}}.
|
authenticate_client(Client, Ctx) -> {ok, {Ctx, {client, Client}}}.
|
||||||
|
|
||||||
verify_resowner_scope({user, _User, _Server}, Scope, Ctx) ->
|
|
||||||
Cmds = ejabberd_commands:get_exposed_commands(),
|
|
||||||
Cmds1 = ['ejabberd:user', 'ejabberd:admin', sasl_auth | Cmds],
|
|
||||||
RegisteredScope = [atom_to_binary(C, utf8) || C <- Cmds1],
|
|
||||||
case oauth2_priv_set:is_subset(oauth2_priv_set:new(Scope),
|
|
||||||
oauth2_priv_set:new(RegisteredScope)) 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
|
%% This is callback for oauth tokens generated through the command line. Only open and admin commands are
|
||||||
%% made available.
|
%% made available.
|
||||||
%verify_client_scope({client, ejabberd_ctl}, Scope, Ctx) ->
|
%verify_client_scope({client, ejabberd_ctl}, Scope, Ctx) ->
|
||||||
@ -286,6 +264,8 @@ scope_in_scope_list(Scope, ScopeList) ->
|
|||||||
oauth2_priv_set:is_member(Scope2, TokenScopeSet) end,
|
oauth2_priv_set:is_member(Scope2, TokenScopeSet) end,
|
||||||
ScopeList).
|
ScopeList).
|
||||||
|
|
||||||
|
-spec check_token(binary()) -> {ok, {binary(), binary()}, [binary()]} |
|
||||||
|
{false, expired | not_found}.
|
||||||
check_token(Token) ->
|
check_token(Token) ->
|
||||||
case lookup(Token) of
|
case lookup(Token) of
|
||||||
{ok, #oauth_token{us = US,
|
{ok, #oauth_token{us = US,
|
||||||
@ -380,29 +360,20 @@ init_cache(DBMod) ->
|
|||||||
use_cache(DBMod) ->
|
use_cache(DBMod) ->
|
||||||
case erlang:function_exported(DBMod, use_cache, 0) of
|
case erlang:function_exported(DBMod, use_cache, 0) of
|
||||||
true -> DBMod:use_cache();
|
true -> DBMod:use_cache();
|
||||||
false ->
|
false -> ejabberd_option:oauth_use_cache()
|
||||||
ejabberd_config:get_option(
|
|
||||||
oauth_use_cache,
|
|
||||||
ejabberd_config:use_cache(global))
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
cache_opts() ->
|
cache_opts() ->
|
||||||
MaxSize = ejabberd_config:get_option(
|
MaxSize = ejabberd_option:oauth_cache_size(),
|
||||||
oauth_cache_size,
|
CacheMissed = ejabberd_option:oauth_cache_missed(),
|
||||||
ejabberd_config:cache_size(global)),
|
LifeTime = case ejabberd_option:oauth_cache_life_time() of
|
||||||
CacheMissed = ejabberd_config:get_option(
|
|
||||||
oauth_cache_missed,
|
|
||||||
ejabberd_config:cache_missed(global)),
|
|
||||||
LifeTime = case ejabberd_config:get_option(
|
|
||||||
oauth_cache_life_time,
|
|
||||||
ejabberd_config:cache_life_time(global)) of
|
|
||||||
infinity -> infinity;
|
infinity -> infinity;
|
||||||
I -> timer:seconds(I)
|
I -> timer:seconds(I)
|
||||||
end,
|
end,
|
||||||
[{max_size, MaxSize}, {life_time, LifeTime}, {cache_missed, CacheMissed}].
|
[{max_size, MaxSize}, {life_time, LifeTime}, {cache_missed, CacheMissed}].
|
||||||
|
|
||||||
expire() ->
|
expire() ->
|
||||||
ejabberd_config:get_option(oauth_expire, ?EXPIRE).
|
ejabberd_option:oauth_expire().
|
||||||
|
|
||||||
-define(DIV(Class, Els),
|
-define(DIV(Class, Els),
|
||||||
?XAE(<<"div">>, [{<<"class">>, Class}], Els)).
|
?XAE(<<"div">>, [{<<"class">>, Class}], Els)).
|
||||||
@ -596,9 +567,7 @@ process(_Handlers, _Request) ->
|
|||||||
-spec get_db_backend() -> module().
|
-spec get_db_backend() -> module().
|
||||||
|
|
||||||
get_db_backend() ->
|
get_db_backend() ->
|
||||||
DBType = ejabberd_config:get_option(
|
DBType = ejabberd_option:oauth_db_type(),
|
||||||
oauth_db_type,
|
|
||||||
ejabberd_config:default_db(?MODULE)),
|
|
||||||
list_to_atom("ejabberd_oauth_" ++ atom_to_list(DBType)).
|
list_to_atom("ejabberd_oauth_" ++ atom_to_list(DBType)).
|
||||||
|
|
||||||
|
|
||||||
@ -645,21 +614,3 @@ logo() ->
|
|||||||
{error, _} ->
|
{error, _} ->
|
||||||
<<>>
|
<<>>
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(oauth_expire) ->
|
|
||||||
fun(I) when is_integer(I), I >= 0 -> I end;
|
|
||||||
opt_type(oauth_access) ->
|
|
||||||
fun acl:access_rules_validator/1;
|
|
||||||
opt_type(oauth_db_type) ->
|
|
||||||
fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
|
||||||
opt_type(O) when O == oauth_cache_life_time; O == oauth_cache_size ->
|
|
||||||
fun (I) when is_integer(I), I > 0 -> I;
|
|
||||||
(infinity) -> infinity
|
|
||||||
end;
|
|
||||||
opt_type(O) when O == oauth_use_cache; O == oauth_cache_missed ->
|
|
||||||
fun (B) when is_boolean(B) -> B end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[oauth_expire, oauth_access, oauth_db_type,
|
|
||||||
oauth_cache_life_time, oauth_cache_size, oauth_use_cache,
|
|
||||||
oauth_cache_missed].
|
|
||||||
|
@ -45,9 +45,7 @@ init() ->
|
|||||||
use_cache() ->
|
use_cache() ->
|
||||||
case mnesia:table_info(oauth_token, storage_type) of
|
case mnesia:table_info(oauth_token, storage_type) of
|
||||||
disc_only_copies ->
|
disc_only_copies ->
|
||||||
ejabberd_config:get_option(
|
ejabberd_option:oauth_use_cache();
|
||||||
oauth_use_cache,
|
|
||||||
ejabberd_config:use_cache(global));
|
|
||||||
_ ->
|
_ ->
|
||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
@ -73,4 +71,3 @@ clean(TS) ->
|
|||||||
lists:foreach(fun mnesia:delete_object/1, Ts)
|
lists:foreach(fun mnesia:delete_object/1, Ts)
|
||||||
end,
|
end,
|
||||||
mnesia:async_dirty(F).
|
mnesia:async_dirty(F).
|
||||||
|
|
||||||
|
@ -26,13 +26,11 @@
|
|||||||
|
|
||||||
-module(ejabberd_oauth_rest).
|
-module(ejabberd_oauth_rest).
|
||||||
-behaviour(ejabberd_oauth).
|
-behaviour(ejabberd_oauth).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-export([init/0,
|
-export([init/0,
|
||||||
store/1,
|
store/1,
|
||||||
lookup/1,
|
lookup/1,
|
||||||
clean/1,
|
clean/1]).
|
||||||
opt_type/1]).
|
|
||||||
|
|
||||||
-include("ejabberd_oauth.hrl").
|
-include("ejabberd_oauth.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
@ -88,11 +86,5 @@ clean(_TS) ->
|
|||||||
ok.
|
ok.
|
||||||
|
|
||||||
path(Path) ->
|
path(Path) ->
|
||||||
Base = ejabberd_config:get_option(ext_api_path_oauth, <<"/oauth">>),
|
Base = ejabberd_option:ext_api_path_oauth(),
|
||||||
<<Base/binary, "/", Path/binary>>.
|
<<Base/binary, "/", Path/binary>>.
|
||||||
|
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(ext_api_path_oauth) ->
|
|
||||||
fun (X) -> iolist_to_binary(X) end;
|
|
||||||
opt_type(_) -> [ext_api_path_oauth].
|
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
-module(ejabberd_oauth_sql).
|
-module(ejabberd_oauth_sql).
|
||||||
-behaviour(ejabberd_oauth).
|
-behaviour(ejabberd_oauth).
|
||||||
-compile([{parse_transform, ejabberd_sql_pt}]).
|
|
||||||
|
|
||||||
-export([init/0,
|
-export([init/0,
|
||||||
store/1,
|
store/1,
|
||||||
|
655
src/ejabberd_old_config.erl
Normal file
655
src/ejabberd_old_config.erl
Normal file
@ -0,0 +1,655 @@
|
|||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% Purpose: Transform old-style Erlang config to YAML config
|
||||||
|
%%%
|
||||||
|
%%% 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_old_config).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([read_file/1]).
|
||||||
|
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
read_file(File) ->
|
||||||
|
case consult(File) of
|
||||||
|
{ok, Terms1} ->
|
||||||
|
?INFO_MSG("Converting from old configuration format", []),
|
||||||
|
Terms2 = strings_to_binary(Terms1),
|
||||||
|
Terms3 = transform(Terms2),
|
||||||
|
Terms4 = transform_certfiles(Terms3),
|
||||||
|
Terms5 = transform_host_config(Terms4),
|
||||||
|
{ok, collect_options(Terms5)};
|
||||||
|
{error, Reason} ->
|
||||||
|
{error, {old_config, File, Reason}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
collect_options(Opts) ->
|
||||||
|
{D, InvalidOpts} =
|
||||||
|
lists:foldl(
|
||||||
|
fun({K, V}, {D, Os}) when is_list(V) ->
|
||||||
|
{orddict:append_list(K, V, D), Os};
|
||||||
|
({K, V}, {D, Os}) ->
|
||||||
|
{orddict:store(K, V, D), Os};
|
||||||
|
(Opt, {D, Os}) ->
|
||||||
|
{D, [Opt|Os]}
|
||||||
|
end, {orddict:new(), []}, Opts),
|
||||||
|
InvalidOpts ++ orddict:to_list(D).
|
||||||
|
|
||||||
|
transform(Opts) ->
|
||||||
|
Opts1 = transform_register(Opts),
|
||||||
|
Opts2 = transform_s2s(Opts1),
|
||||||
|
Opts3 = transform_listeners(Opts2),
|
||||||
|
Opts4 = transform_sql(Opts3),
|
||||||
|
Opts5 = transform_riak(Opts4),
|
||||||
|
Opts6 = transform_shaper(Opts5),
|
||||||
|
Opts7 = transform_s2s_out(Opts6),
|
||||||
|
Opts8 = transform_acl(Opts7),
|
||||||
|
Opts9 = transform_modules(Opts8),
|
||||||
|
Opts10 = transform_globals(Opts9),
|
||||||
|
collect_options(Opts10).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% mod_register
|
||||||
|
%%%===================================================================
|
||||||
|
transform_register(Opts) ->
|
||||||
|
try
|
||||||
|
{value, {modules, ModOpts}, Opts1} = lists:keytake(modules, 1, Opts),
|
||||||
|
{value, {?MODULE, RegOpts}, ModOpts1} = lists:keytake(?MODULE, 1, ModOpts),
|
||||||
|
{value, {ip_access, L}, RegOpts1} = lists:keytake(ip_access, 1, RegOpts),
|
||||||
|
true = is_list(L),
|
||||||
|
?WARNING_MSG("Old 'ip_access' format detected. "
|
||||||
|
"The old format is still supported "
|
||||||
|
"but it is better to fix your config: "
|
||||||
|
"use access rules instead.", []),
|
||||||
|
ACLs = lists:flatmap(
|
||||||
|
fun({Action, S}) ->
|
||||||
|
ACLName = misc:binary_to_atom(
|
||||||
|
iolist_to_binary(
|
||||||
|
["ip_", S])),
|
||||||
|
[{Action, ACLName},
|
||||||
|
{acl, ACLName, {ip, S}}]
|
||||||
|
end, L),
|
||||||
|
Access = {access, mod_register_networks,
|
||||||
|
[{Action, ACLName} || {Action, ACLName} <- ACLs]},
|
||||||
|
[ACL || {acl, _, _} = ACL <- ACLs] ++
|
||||||
|
[Access,
|
||||||
|
{modules,
|
||||||
|
[{mod_register,
|
||||||
|
[{ip_access, mod_register_networks}|RegOpts1]}
|
||||||
|
| ModOpts1]}|Opts1]
|
||||||
|
catch error:{badmatch, false} ->
|
||||||
|
Opts
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% ejabberd_s2s
|
||||||
|
%%%===================================================================
|
||||||
|
transform_s2s(Opts) ->
|
||||||
|
lists:foldl(fun transform_s2s/2, [], Opts).
|
||||||
|
|
||||||
|
transform_s2s({{s2s_host, Host}, Action}, Opts) ->
|
||||||
|
?WARNING_MSG("Option 's2s_host' is deprecated.", []),
|
||||||
|
ACLName = misc:binary_to_atom(
|
||||||
|
iolist_to_binary(["s2s_access_", Host])),
|
||||||
|
[{acl, ACLName, {server, Host}},
|
||||||
|
{access, s2s, [{Action, ACLName}]},
|
||||||
|
{s2s_access, s2s} |
|
||||||
|
Opts];
|
||||||
|
transform_s2s({s2s_default_policy, Action}, Opts) ->
|
||||||
|
?WARNING_MSG("Option 's2s_default_policy' is deprecated. "
|
||||||
|
"The option is still supported but it is better to "
|
||||||
|
"fix your config: "
|
||||||
|
"use 's2s_access' with an access rule.", []),
|
||||||
|
[{access, s2s, [{Action, all}]},
|
||||||
|
{s2s_access, s2s} |
|
||||||
|
Opts];
|
||||||
|
transform_s2s(Opt, Opts) ->
|
||||||
|
[Opt|Opts].
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% ejabberd_s2s_out
|
||||||
|
%%%===================================================================
|
||||||
|
transform_s2s_out(Opts) ->
|
||||||
|
lists:foldl(fun transform_s2s_out/2, [], Opts).
|
||||||
|
|
||||||
|
transform_s2s_out({outgoing_s2s_options, Families, Timeout}, Opts) ->
|
||||||
|
?WARNING_MSG("Option 'outgoing_s2s_options' is deprecated. "
|
||||||
|
"The option is still supported "
|
||||||
|
"but it is better to fix your config: "
|
||||||
|
"use 'outgoing_s2s_timeout' and "
|
||||||
|
"'outgoing_s2s_families' instead.", []),
|
||||||
|
[{outgoing_s2s_families, Families},
|
||||||
|
{outgoing_s2s_timeout, Timeout}
|
||||||
|
| Opts];
|
||||||
|
transform_s2s_out({s2s_dns_options, S2SDNSOpts}, AllOpts) ->
|
||||||
|
?WARNING_MSG("Option 's2s_dns_options' is deprecated. "
|
||||||
|
"The option is still supported "
|
||||||
|
"but it is better to fix your config: "
|
||||||
|
"use 's2s_dns_timeout' and "
|
||||||
|
"'s2s_dns_retries' instead", []),
|
||||||
|
lists:foldr(
|
||||||
|
fun({timeout, T}, AccOpts) ->
|
||||||
|
[{s2s_dns_timeout, T}|AccOpts];
|
||||||
|
({retries, R}, AccOpts) ->
|
||||||
|
[{s2s_dns_retries, R}|AccOpts];
|
||||||
|
(_, AccOpts) ->
|
||||||
|
AccOpts
|
||||||
|
end, AllOpts, S2SDNSOpts);
|
||||||
|
transform_s2s_out(Opt, Opts) ->
|
||||||
|
[Opt|Opts].
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% ejabberd_listener
|
||||||
|
%%%===================================================================
|
||||||
|
transform_listeners(Opts) ->
|
||||||
|
lists:foldl(fun transform_listeners/2, [], Opts).
|
||||||
|
|
||||||
|
transform_listeners({listen, LOpts}, Opts) ->
|
||||||
|
[{listen, lists:map(fun transform_listener/1, LOpts)} | Opts];
|
||||||
|
transform_listeners(Opt, Opts) ->
|
||||||
|
[Opt|Opts].
|
||||||
|
|
||||||
|
transform_listener({{Port, IP, Transport}, Mod, Opts}) ->
|
||||||
|
IPStr = if is_tuple(IP) ->
|
||||||
|
list_to_binary(inet_parse:ntoa(IP));
|
||||||
|
true ->
|
||||||
|
IP
|
||||||
|
end,
|
||||||
|
Opts1 = lists:map(
|
||||||
|
fun({ip, IPT}) when is_tuple(IPT) ->
|
||||||
|
{ip, list_to_binary(inet_parse:ntoa(IP))};
|
||||||
|
(ssl) -> {tls, true};
|
||||||
|
(A) when is_atom(A) -> {A, true};
|
||||||
|
(Opt) -> Opt
|
||||||
|
end, Opts),
|
||||||
|
Opts2 = lists:foldl(
|
||||||
|
fun(Opt, Acc) ->
|
||||||
|
transform_listen_option(Mod, Opt, Acc)
|
||||||
|
end, [], Opts1),
|
||||||
|
TransportOpt = if Transport == tcp -> [];
|
||||||
|
true -> [{transport, Transport}]
|
||||||
|
end,
|
||||||
|
IPOpt = if IPStr == <<"0.0.0.0">> -> [];
|
||||||
|
true -> [{ip, IPStr}]
|
||||||
|
end,
|
||||||
|
IPOpt ++ TransportOpt ++ [{port, Port}, {module, Mod} | Opts2];
|
||||||
|
transform_listener({{Port, Transport}, Mod, Opts})
|
||||||
|
when Transport == tcp orelse Transport == udp ->
|
||||||
|
transform_listener({{Port, all_zero_ip(Opts), Transport}, Mod, Opts});
|
||||||
|
transform_listener({{Port, IP}, Mod, Opts}) ->
|
||||||
|
transform_listener({{Port, IP, tcp}, Mod, Opts});
|
||||||
|
transform_listener({Port, Mod, Opts}) ->
|
||||||
|
transform_listener({{Port, all_zero_ip(Opts), tcp}, Mod, Opts});
|
||||||
|
transform_listener(Opt) ->
|
||||||
|
Opt.
|
||||||
|
|
||||||
|
transform_listen_option(ejabberd_http, captcha, Opts) ->
|
||||||
|
[{captcha, true}|Opts];
|
||||||
|
transform_listen_option(ejabberd_http, register, Opts) ->
|
||||||
|
[{register, true}|Opts];
|
||||||
|
transform_listen_option(ejabberd_http, web_admin, Opts) ->
|
||||||
|
[{web_admin, true}|Opts];
|
||||||
|
transform_listen_option(ejabberd_http, http_bind, Opts) ->
|
||||||
|
[{http_bind, true}|Opts];
|
||||||
|
transform_listen_option(ejabberd_http, http_poll, Opts) ->
|
||||||
|
[{http_poll, true}|Opts];
|
||||||
|
transform_listen_option(ejabberd_http, {request_handlers, Hs}, Opts) ->
|
||||||
|
Hs1 = lists:map(
|
||||||
|
fun({PList, Mod}) when is_list(PList) ->
|
||||||
|
Path = iolist_to_binary([[$/, P] || P <- PList]),
|
||||||
|
{Path, Mod};
|
||||||
|
(Opt) ->
|
||||||
|
Opt
|
||||||
|
end, Hs),
|
||||||
|
[{request_handlers, Hs1} | Opts];
|
||||||
|
transform_listen_option(ejabberd_service, {hosts, Hosts, O}, Opts) ->
|
||||||
|
case lists:keyfind(hosts, 1, Opts) of
|
||||||
|
{_, PrevHostOpts} ->
|
||||||
|
NewHostOpts =
|
||||||
|
lists:foldl(
|
||||||
|
fun(H, Acc) ->
|
||||||
|
dict:append_list(H, O, Acc)
|
||||||
|
end, dict:from_list(PrevHostOpts), Hosts),
|
||||||
|
[{hosts, dict:to_list(NewHostOpts)}|
|
||||||
|
lists:keydelete(hosts, 1, Opts)];
|
||||||
|
_ ->
|
||||||
|
[{hosts, [{H, O} || H <- Hosts]}|Opts]
|
||||||
|
end;
|
||||||
|
transform_listen_option(ejabberd_service, {host, Host, Os}, Opts) ->
|
||||||
|
transform_listen_option(ejabberd_service, {hosts, [Host], Os}, Opts);
|
||||||
|
transform_listen_option(ejabberd_xmlrpc, {access_commands, ACOpts}, Opts) ->
|
||||||
|
NewACOpts = lists:map(
|
||||||
|
fun({AName, ACmds, AOpts}) ->
|
||||||
|
{AName, [{commands, ACmds}, {options, AOpts}]};
|
||||||
|
(Opt) ->
|
||||||
|
Opt
|
||||||
|
end, ACOpts),
|
||||||
|
[{access_commands, NewACOpts}|Opts];
|
||||||
|
transform_listen_option(_, Opt, Opts) ->
|
||||||
|
[Opt|Opts].
|
||||||
|
|
||||||
|
-spec all_zero_ip([proplists:property()]) -> inet:ip_address().
|
||||||
|
all_zero_ip(Opts) ->
|
||||||
|
case proplists:get_bool(inet6, Opts) of
|
||||||
|
true -> {0,0,0,0,0,0,0,0};
|
||||||
|
false -> {0,0,0,0}
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% ejabberd_shaper
|
||||||
|
%%%===================================================================
|
||||||
|
transform_shaper(Opts) ->
|
||||||
|
lists:foldl(fun transform_shaper/2, [], Opts).
|
||||||
|
|
||||||
|
transform_shaper({shaper, Name, {maxrate, N}}, Opts) ->
|
||||||
|
[{shaper, [{Name, N}]} | Opts];
|
||||||
|
transform_shaper({shaper, Name, none}, Opts) ->
|
||||||
|
[{shaper, [{Name, none}]} | Opts];
|
||||||
|
transform_shaper({shaper, List}, Opts) when is_list(List) ->
|
||||||
|
R = lists:map(
|
||||||
|
fun({Name, Args}) when is_list(Args) ->
|
||||||
|
MaxRate = proplists:get_value(rate, Args, 1000),
|
||||||
|
BurstSize = proplists:get_value(burst_size, Args, MaxRate),
|
||||||
|
{Name, MaxRate, BurstSize};
|
||||||
|
({Name, Val}) ->
|
||||||
|
{Name, Val, Val}
|
||||||
|
end, List),
|
||||||
|
[{shaper, R} | Opts];
|
||||||
|
transform_shaper(Opt, Opts) ->
|
||||||
|
[Opt | Opts].
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% acl
|
||||||
|
%%%===================================================================
|
||||||
|
transform_acl(Opts) ->
|
||||||
|
Opts1 = lists:foldl(fun transform_acl/2, [], Opts),
|
||||||
|
{ACLOpts, Opts2} = lists:mapfoldl(
|
||||||
|
fun({acl, Os}, Acc) ->
|
||||||
|
{Os, Acc};
|
||||||
|
(O, Acc) ->
|
||||||
|
{[], [O|Acc]}
|
||||||
|
end, [], Opts1),
|
||||||
|
{AccessOpts, Opts3} = lists:mapfoldl(
|
||||||
|
fun({access, Os}, Acc) ->
|
||||||
|
{Os, Acc};
|
||||||
|
(O, Acc) ->
|
||||||
|
{[], [O|Acc]}
|
||||||
|
end, [], Opts2),
|
||||||
|
{NewAccessOpts, Opts4} = lists:mapfoldl(
|
||||||
|
fun({access_rules, Os}, Acc) ->
|
||||||
|
{Os, Acc};
|
||||||
|
(O, Acc) ->
|
||||||
|
{[], [O|Acc]}
|
||||||
|
end, [], Opts3),
|
||||||
|
{ShaperOpts, Opts5} = lists:mapfoldl(
|
||||||
|
fun({shaper_rules, Os}, Acc) ->
|
||||||
|
{Os, Acc};
|
||||||
|
(O, Acc) ->
|
||||||
|
{[], [O|Acc]}
|
||||||
|
end, [], Opts4),
|
||||||
|
ACLOpts1 = collect_options(lists:flatten(ACLOpts)),
|
||||||
|
AccessOpts1 = case collect_options(lists:flatten(AccessOpts)) of
|
||||||
|
[] -> [];
|
||||||
|
L1 -> [{access, L1}]
|
||||||
|
end,
|
||||||
|
ACLOpts2 = case lists:map(
|
||||||
|
fun({ACLName, Os}) ->
|
||||||
|
{ACLName, collect_options(Os)}
|
||||||
|
end, ACLOpts1) of
|
||||||
|
[] -> [];
|
||||||
|
L2 -> [{acl, L2}]
|
||||||
|
end,
|
||||||
|
NewAccessOpts1 = case lists:map(
|
||||||
|
fun({NAName, Os}) ->
|
||||||
|
{NAName, transform_access_rules_config(Os)}
|
||||||
|
end, lists:flatten(NewAccessOpts)) of
|
||||||
|
[] -> [];
|
||||||
|
L3 -> [{access_rules, L3}]
|
||||||
|
end,
|
||||||
|
ShaperOpts1 = case lists:map(
|
||||||
|
fun({SName, Ss}) ->
|
||||||
|
{SName, transform_access_rules_config(Ss)}
|
||||||
|
end, lists:flatten(ShaperOpts)) of
|
||||||
|
[] -> [];
|
||||||
|
L4 -> [{shaper_rules, L4}]
|
||||||
|
end,
|
||||||
|
ACLOpts2 ++ AccessOpts1 ++ NewAccessOpts1 ++ ShaperOpts1 ++ Opts5.
|
||||||
|
|
||||||
|
transform_acl({acl, Name, Type}, Opts) ->
|
||||||
|
T = case Type of
|
||||||
|
all -> all;
|
||||||
|
none -> none;
|
||||||
|
{user, U} -> {user, [b(U)]};
|
||||||
|
{user, U, S} -> {user, [[{b(U), b(S)}]]};
|
||||||
|
{shared_group, G} -> {shared_group, [b(G)]};
|
||||||
|
{shared_group, G, H} -> {shared_group, [[{b(G), b(H)}]]};
|
||||||
|
{user_regexp, UR} -> {user_regexp, [b(UR)]};
|
||||||
|
{user_regexp, UR, S} -> {user_regexp, [[{b(UR), b(S)}]]};
|
||||||
|
{node_regexp, UR, SR} -> {node_regexp, [[{b(UR), b(SR)}]]};
|
||||||
|
{user_glob, UR} -> {user_glob, [b(UR)]};
|
||||||
|
{user_glob, UR, S} -> {user_glob, [[{b(UR), b(S)}]]};
|
||||||
|
{node_glob, UR, SR} -> {node_glob, [[{b(UR), b(SR)}]]};
|
||||||
|
{server, S} -> {server, [b(S)]};
|
||||||
|
{resource, R} -> {resource, [b(R)]};
|
||||||
|
{server_regexp, SR} -> {server_regexp, [b(SR)]};
|
||||||
|
{server_glob, S} -> {server_glob, [b(S)]};
|
||||||
|
{ip, S} -> {ip, [b(S)]};
|
||||||
|
{resource_glob, R} -> {resource_glob, [b(R)]};
|
||||||
|
{resource_regexp, R} -> {resource_regexp, [b(R)]}
|
||||||
|
end,
|
||||||
|
[{acl, [{Name, [T]}]}|Opts];
|
||||||
|
transform_acl({access, Name, Rules}, Opts) ->
|
||||||
|
NewRules = [{ACL, Action} || {Action, ACL} <- Rules],
|
||||||
|
[{access, [{Name, NewRules}]}|Opts];
|
||||||
|
transform_acl(Opt, Opts) ->
|
||||||
|
[Opt|Opts].
|
||||||
|
|
||||||
|
transform_access_rules_config(Config) when is_list(Config) ->
|
||||||
|
lists:map(fun transform_access_rules_config2/1, lists:flatten(Config));
|
||||||
|
transform_access_rules_config(Config) ->
|
||||||
|
transform_access_rules_config([Config]).
|
||||||
|
|
||||||
|
transform_access_rules_config2(Type) when is_integer(Type); is_atom(Type) ->
|
||||||
|
{Type, [all]};
|
||||||
|
transform_access_rules_config2({Type, ACL}) when is_atom(ACL) ->
|
||||||
|
{Type, [{acl, ACL}]};
|
||||||
|
transform_access_rules_config2({Res, Rules}) when is_list(Rules) ->
|
||||||
|
T = lists:map(fun({Type, Args}) when is_list(Args) ->
|
||||||
|
{Type, hd(lists:flatten(Args))};
|
||||||
|
(V) ->
|
||||||
|
V
|
||||||
|
end, lists:flatten(Rules)),
|
||||||
|
{Res, T};
|
||||||
|
transform_access_rules_config2({Res, Rule}) ->
|
||||||
|
{Res, [Rule]}.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% SQL
|
||||||
|
%%%===================================================================
|
||||||
|
-define(PGSQL_PORT, 5432).
|
||||||
|
-define(MYSQL_PORT, 3306).
|
||||||
|
|
||||||
|
transform_sql(Opts) ->
|
||||||
|
lists:foldl(fun transform_sql/2, [], Opts).
|
||||||
|
|
||||||
|
transform_sql({odbc_server, {Type, Server, Port, DB, User, Pass}}, Opts) ->
|
||||||
|
[{sql_type, Type},
|
||||||
|
{sql_server, Server},
|
||||||
|
{sql_port, Port},
|
||||||
|
{sql_database, DB},
|
||||||
|
{sql_username, User},
|
||||||
|
{sql_password, Pass}|Opts];
|
||||||
|
transform_sql({odbc_server, {mysql, Server, DB, User, Pass}}, Opts) ->
|
||||||
|
transform_sql({odbc_server, {mysql, Server, ?MYSQL_PORT, DB, User, Pass}}, Opts);
|
||||||
|
transform_sql({odbc_server, {pgsql, Server, DB, User, Pass}}, Opts) ->
|
||||||
|
transform_sql({odbc_server, {pgsql, Server, ?PGSQL_PORT, DB, User, Pass}}, Opts);
|
||||||
|
transform_sql({odbc_server, {sqlite, DB}}, Opts) ->
|
||||||
|
[{sql_type, sqlite},
|
||||||
|
{sql_database, DB}|Opts];
|
||||||
|
transform_sql({odbc_pool_size, N}, Opts) ->
|
||||||
|
[{sql_pool_size, N}|Opts];
|
||||||
|
transform_sql(Opt, Opts) ->
|
||||||
|
[Opt|Opts].
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Riak
|
||||||
|
%%%===================================================================
|
||||||
|
transform_riak(Opts) ->
|
||||||
|
lists:foldl(fun transform_riak/2, [], Opts).
|
||||||
|
|
||||||
|
transform_riak({riak_server, {S, P}}, Opts) ->
|
||||||
|
[{riak_server, S}, {riak_port, P}|Opts];
|
||||||
|
transform_riak(Opt, Opts) ->
|
||||||
|
[Opt|Opts].
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% modules
|
||||||
|
%%%===================================================================
|
||||||
|
transform_modules(Opts) ->
|
||||||
|
lists:foldl(fun transform_modules/2, [], Opts).
|
||||||
|
|
||||||
|
transform_modules({modules, ModOpts}, Opts) ->
|
||||||
|
[{modules, lists:map(
|
||||||
|
fun({Mod, Opts1}) ->
|
||||||
|
{Mod, transform_module(Mod, Opts1)};
|
||||||
|
(Other) ->
|
||||||
|
Other
|
||||||
|
end, ModOpts)}|Opts];
|
||||||
|
transform_modules(Opt, Opts) ->
|
||||||
|
[Opt|Opts].
|
||||||
|
|
||||||
|
transform_module(mod_disco, Opts) ->
|
||||||
|
lists:map(
|
||||||
|
fun({server_info, Infos}) ->
|
||||||
|
NewInfos = lists:map(
|
||||||
|
fun({Modules, Name, URLs}) ->
|
||||||
|
[[{modules, Modules},
|
||||||
|
{name, Name},
|
||||||
|
{urls, URLs}]];
|
||||||
|
(Opt) ->
|
||||||
|
Opt
|
||||||
|
end, Infos),
|
||||||
|
{server_info, NewInfos};
|
||||||
|
(Opt) ->
|
||||||
|
Opt
|
||||||
|
end, Opts);
|
||||||
|
transform_module(mod_muc_log, Opts) ->
|
||||||
|
lists:map(
|
||||||
|
fun({top_link, {S1, S2}}) ->
|
||||||
|
{top_link, [{S1, S2}]};
|
||||||
|
(Opt) ->
|
||||||
|
Opt
|
||||||
|
end, Opts);
|
||||||
|
transform_module(mod_proxy65, Opts) ->
|
||||||
|
lists:map(
|
||||||
|
fun({ip, IP}) when is_tuple(IP) ->
|
||||||
|
{ip, misc:ip_to_list(IP)};
|
||||||
|
({hostname, IP}) when is_tuple(IP) ->
|
||||||
|
{hostname, misc:ip_to_list(IP)};
|
||||||
|
(Opt) ->
|
||||||
|
Opt
|
||||||
|
end, Opts);
|
||||||
|
transform_module(mod_register, Opts) ->
|
||||||
|
lists:flatmap(
|
||||||
|
fun({welcome_message, {Subj, Body}}) ->
|
||||||
|
[{welcome_message, [{subject, Subj}, {body, Body}]}];
|
||||||
|
(Opt) ->
|
||||||
|
[Opt]
|
||||||
|
end, Opts);
|
||||||
|
transform_module(_Mod, Opts) ->
|
||||||
|
Opts.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Host config
|
||||||
|
%%%===================================================================
|
||||||
|
transform_host_config(Opts) ->
|
||||||
|
Opts1 = lists:foldl(fun transform_host_config/2, [], Opts),
|
||||||
|
{HOpts, Opts2} = lists:mapfoldl(
|
||||||
|
fun({host_config, O}, Os) ->
|
||||||
|
{[O], Os};
|
||||||
|
(O, Os) ->
|
||||||
|
{[], [O|Os]}
|
||||||
|
end, [], Opts1),
|
||||||
|
{AHOpts, Opts3} = lists:mapfoldl(
|
||||||
|
fun({append_host_config, O}, Os) ->
|
||||||
|
{[O], Os};
|
||||||
|
(O, Os) ->
|
||||||
|
{[], [O|Os]}
|
||||||
|
end, [], Opts2),
|
||||||
|
HOpts1 = case collect_options(lists:flatten(HOpts)) of
|
||||||
|
[] ->
|
||||||
|
[];
|
||||||
|
HOs ->
|
||||||
|
[{host_config,
|
||||||
|
[{H, transform(O)} || {H, O} <- HOs]}]
|
||||||
|
end,
|
||||||
|
AHOpts1 = case collect_options(lists:flatten(AHOpts)) of
|
||||||
|
[] ->
|
||||||
|
[];
|
||||||
|
AHOs ->
|
||||||
|
[{append_host_config,
|
||||||
|
[{H, transform(O)} || {H, O} <- AHOs]}]
|
||||||
|
end,
|
||||||
|
HOpts1 ++ AHOpts1 ++ Opts3.
|
||||||
|
|
||||||
|
transform_host_config({host_config, Host, HOpts}, Opts) ->
|
||||||
|
{AddOpts, HOpts1} =
|
||||||
|
lists:mapfoldl(
|
||||||
|
fun({{add, Opt}, Val}, Os) ->
|
||||||
|
{[{Opt, Val}], Os};
|
||||||
|
(O, Os) ->
|
||||||
|
{[], [O|Os]}
|
||||||
|
end, [], HOpts),
|
||||||
|
[{append_host_config, [{Host, lists:flatten(AddOpts)}]},
|
||||||
|
{host_config, [{Host, HOpts1}]}|Opts];
|
||||||
|
transform_host_config(Opt, Opts) ->
|
||||||
|
[Opt|Opts].
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Top-level options
|
||||||
|
%%%===================================================================
|
||||||
|
transform_globals(Opts) ->
|
||||||
|
lists:foldl(fun transform_globals/2, [], Opts).
|
||||||
|
|
||||||
|
transform_globals(Opt, Opts) when Opt == override_global;
|
||||||
|
Opt == override_local;
|
||||||
|
Opt == override_acls ->
|
||||||
|
?WARNING_MSG("Option '~s' has no effect anymore", [Opt]),
|
||||||
|
Opts;
|
||||||
|
transform_globals({node_start, _}, Opts) ->
|
||||||
|
?WARNING_MSG("Option 'node_start' has no effect anymore", []),
|
||||||
|
Opts;
|
||||||
|
transform_globals({iqdisc, {queues, N}}, Opts) ->
|
||||||
|
[{iqdisc, N}|Opts];
|
||||||
|
transform_globals({define_macro, Macro, Val}, Opts) ->
|
||||||
|
[{define_macro, [{Macro, Val}]}|Opts];
|
||||||
|
transform_globals(Opt, Opts) ->
|
||||||
|
[Opt|Opts].
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Certfiles
|
||||||
|
%%%===================================================================
|
||||||
|
transform_certfiles(Opts) ->
|
||||||
|
lists:foldl(fun transform_certfiles/2, [], Opts).
|
||||||
|
|
||||||
|
transform_certfiles({domain_certfile, Domain, CertFile}, Opts) ->
|
||||||
|
[{host_config, [{Domain, [{domain_certfile, CertFile}]}]}|Opts];
|
||||||
|
transform_certfiles(Opt, Opts) ->
|
||||||
|
[Opt|Opts].
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Consult file
|
||||||
|
%%%===================================================================
|
||||||
|
consult(File) ->
|
||||||
|
case file:consult(File) of
|
||||||
|
{ok, Terms} ->
|
||||||
|
include_config_files(Terms);
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
|
include_config_files(Terms) ->
|
||||||
|
include_config_files(Terms, []).
|
||||||
|
|
||||||
|
include_config_files([], Res) ->
|
||||||
|
{ok, Res};
|
||||||
|
include_config_files([{include_config_file, Filename} | Terms], Res) ->
|
||||||
|
include_config_files([{include_config_file, Filename, []} | Terms], Res);
|
||||||
|
include_config_files([{include_config_file, Filename, Options} | Terms], Res) ->
|
||||||
|
case consult(Filename) of
|
||||||
|
{ok, Included_terms} ->
|
||||||
|
Disallow = proplists:get_value(disallow, Options, []),
|
||||||
|
Included_terms2 = delete_disallowed(Disallow, Included_terms),
|
||||||
|
Allow_only = proplists:get_value(allow_only, Options, all),
|
||||||
|
Included_terms3 = keep_only_allowed(Allow_only, Included_terms2),
|
||||||
|
include_config_files(Terms, Res ++ Included_terms3);
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
|
end;
|
||||||
|
include_config_files([Term | Terms], Res) ->
|
||||||
|
include_config_files(Terms, Res ++ [Term]).
|
||||||
|
|
||||||
|
delete_disallowed(Disallowed, Terms) ->
|
||||||
|
lists:foldl(
|
||||||
|
fun(Dis, Ldis) ->
|
||||||
|
delete_disallowed2(Dis, Ldis)
|
||||||
|
end,
|
||||||
|
Terms,
|
||||||
|
Disallowed).
|
||||||
|
|
||||||
|
delete_disallowed2(Disallowed, [H|T]) ->
|
||||||
|
case element(1, H) of
|
||||||
|
Disallowed ->
|
||||||
|
delete_disallowed2(Disallowed, T);
|
||||||
|
_ ->
|
||||||
|
[H|delete_disallowed2(Disallowed, T)]
|
||||||
|
end;
|
||||||
|
delete_disallowed2(_, []) ->
|
||||||
|
[].
|
||||||
|
|
||||||
|
keep_only_allowed(all, Terms) ->
|
||||||
|
Terms;
|
||||||
|
keep_only_allowed(Allowed, Terms) ->
|
||||||
|
{As, _NAs} = lists:partition(
|
||||||
|
fun(Term) ->
|
||||||
|
lists:member(element(1, Term), Allowed)
|
||||||
|
end, Terms),
|
||||||
|
As.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Aux functions
|
||||||
|
%%%===================================================================
|
||||||
|
strings_to_binary([]) ->
|
||||||
|
[];
|
||||||
|
strings_to_binary(L) when is_list(L) ->
|
||||||
|
case is_string(L) of
|
||||||
|
true ->
|
||||||
|
list_to_binary(L);
|
||||||
|
false ->
|
||||||
|
strings_to_binary1(L)
|
||||||
|
end;
|
||||||
|
strings_to_binary({A, B, C, D}) when
|
||||||
|
is_integer(A), is_integer(B), is_integer(C), is_integer(D) ->
|
||||||
|
{A, B, C ,D};
|
||||||
|
strings_to_binary(T) when is_tuple(T) ->
|
||||||
|
list_to_tuple(strings_to_binary1(tuple_to_list(T)));
|
||||||
|
strings_to_binary(X) ->
|
||||||
|
X.
|
||||||
|
|
||||||
|
strings_to_binary1([El|L]) ->
|
||||||
|
[strings_to_binary(El)|strings_to_binary1(L)];
|
||||||
|
strings_to_binary1([]) ->
|
||||||
|
[];
|
||||||
|
strings_to_binary1(T) ->
|
||||||
|
T.
|
||||||
|
|
||||||
|
is_string([C|T]) when (C >= 0) and (C =< 255) ->
|
||||||
|
is_string(T);
|
||||||
|
is_string([]) ->
|
||||||
|
true;
|
||||||
|
is_string(_) ->
|
||||||
|
false.
|
||||||
|
|
||||||
|
b(S) ->
|
||||||
|
iolist_to_binary(S).
|
1058
src/ejabberd_option.erl
Normal file
1058
src/ejabberd_option.erl
Normal file
File diff suppressed because it is too large
Load Diff
757
src/ejabberd_options.erl
Normal file
757
src/ejabberd_options.erl
Normal file
@ -0,0 +1,757 @@
|
|||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% 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_options).
|
||||||
|
-behaviour(ejabberd_config).
|
||||||
|
|
||||||
|
-export([opt_type/1, options/0, globals/0]).
|
||||||
|
|
||||||
|
-ifdef(NEW_SQL_SCHEMA).
|
||||||
|
-define(USE_NEW_SQL_SCHEMA_DEFAULT, true).
|
||||||
|
-else.
|
||||||
|
-define(USE_NEW_SQL_SCHEMA_DEFAULT, false).
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
-include_lib("kernel/include/inet.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
-spec opt_type(atom()) -> econf:validator().
|
||||||
|
opt_type(access_rules) ->
|
||||||
|
acl:validator(access_rules);
|
||||||
|
opt_type(acl) ->
|
||||||
|
acl:validator(acl);
|
||||||
|
opt_type(acme) ->
|
||||||
|
econf:options(
|
||||||
|
#{ca_url => econf:url(),
|
||||||
|
contact => econf:binary("^[a-zA-Z]+:[^:]+$")},
|
||||||
|
[unique, {return, map}]);
|
||||||
|
opt_type(allow_contrib_modules) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(allow_multiple_connections) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(anonymous_protocol) ->
|
||||||
|
econf:enum([sasl_anon, login_anon, both]);
|
||||||
|
opt_type(api_permissions) ->
|
||||||
|
ejabberd_access_permissions:validator();
|
||||||
|
opt_type(append_host_config) ->
|
||||||
|
econf:map(
|
||||||
|
econf:and_then(
|
||||||
|
econf:domain(),
|
||||||
|
econf:enum(ejabberd_option:hosts())),
|
||||||
|
validator());
|
||||||
|
opt_type(auth_cache_life_time) ->
|
||||||
|
econf:pos_int(infinity);
|
||||||
|
opt_type(auth_cache_missed) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(auth_cache_size) ->
|
||||||
|
econf:pos_int(infinity);
|
||||||
|
opt_type(auth_method) ->
|
||||||
|
econf:list_or_single(econf:db_type(ejabberd_auth));
|
||||||
|
opt_type(auth_password_format) ->
|
||||||
|
econf:enum([plain, scram]);
|
||||||
|
opt_type(auth_use_cache) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(c2s_cafile) ->
|
||||||
|
econf:file();
|
||||||
|
opt_type(c2s_ciphers) ->
|
||||||
|
econf:binary();
|
||||||
|
opt_type(c2s_dhfile) ->
|
||||||
|
econf:file();
|
||||||
|
opt_type(c2s_protocol_options) ->
|
||||||
|
econf:and_then(
|
||||||
|
econf:list(econf:binary(), [unique]),
|
||||||
|
fun concat_tls_protocol_options/1);
|
||||||
|
opt_type(c2s_tls_compression) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(ca_file) ->
|
||||||
|
econf:pem();
|
||||||
|
opt_type(cache_life_time) ->
|
||||||
|
econf:pos_int(infinity);
|
||||||
|
opt_type(cache_missed) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(cache_size) ->
|
||||||
|
econf:pos_int(infinity);
|
||||||
|
opt_type(captcha_cmd) ->
|
||||||
|
econf:file();
|
||||||
|
opt_type(captcha_host) ->
|
||||||
|
econf:binary();
|
||||||
|
opt_type(captcha_limit) ->
|
||||||
|
econf:pos_int(infinity);
|
||||||
|
opt_type(certfiles) ->
|
||||||
|
econf:list(econf:binary());
|
||||||
|
opt_type(cluster_backend) ->
|
||||||
|
econf:db_type(ejabberd_cluster);
|
||||||
|
opt_type(cluster_nodes) ->
|
||||||
|
econf:list(econf:atom(), [unique]);
|
||||||
|
opt_type(default_db) ->
|
||||||
|
econf:enum([mnesia, riak, sql]);
|
||||||
|
opt_type(default_ram_db) ->
|
||||||
|
econf:enum([mnesia, riak, sql, redis]);
|
||||||
|
opt_type(define_macro) ->
|
||||||
|
econf:any();
|
||||||
|
opt_type(disable_sasl_mechanisms) ->
|
||||||
|
econf:list_or_single(
|
||||||
|
econf:and_then(
|
||||||
|
econf:binary(),
|
||||||
|
fun str:to_upper/1));
|
||||||
|
opt_type(domain_balancing) ->
|
||||||
|
econf:map(
|
||||||
|
econf:domain(),
|
||||||
|
econf:options(
|
||||||
|
#{component_number => econf:int(2, 1000),
|
||||||
|
type => econf:enum([random, source, destination,
|
||||||
|
bare_source, bare_destination])},
|
||||||
|
[{required, [component_number]}, {return, map}, unique]),
|
||||||
|
[{return, map}]);
|
||||||
|
opt_type(ext_api_path_oauth) ->
|
||||||
|
econf:binary();
|
||||||
|
opt_type(ext_api_http_pool_size) ->
|
||||||
|
econf:pos_int();
|
||||||
|
opt_type(ext_api_url) ->
|
||||||
|
econf:url();
|
||||||
|
opt_type(ext_api_headers) ->
|
||||||
|
econf:binary();
|
||||||
|
opt_type(extauth_pool_name) ->
|
||||||
|
econf:binary();
|
||||||
|
opt_type(extauth_pool_size) ->
|
||||||
|
econf:pos_int();
|
||||||
|
opt_type(extauth_program) ->
|
||||||
|
econf:string();
|
||||||
|
opt_type(fqdn) ->
|
||||||
|
econf:list_or_single(econf:domain());
|
||||||
|
opt_type(hide_sensitive_log_data) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(host_config) ->
|
||||||
|
econf:map(
|
||||||
|
econf:and_then(
|
||||||
|
econf:domain(),
|
||||||
|
econf:enum(ejabberd_option:hosts())),
|
||||||
|
validator());
|
||||||
|
opt_type(hosts) ->
|
||||||
|
econf:non_empty(econf:list(econf:domain(), [unique]));
|
||||||
|
opt_type(include_config_file) ->
|
||||||
|
econf:any();
|
||||||
|
opt_type(language) ->
|
||||||
|
econf:lang();
|
||||||
|
opt_type(ldap_backups) ->
|
||||||
|
econf:list(econf:domain(), [unique]);
|
||||||
|
opt_type(ldap_base) ->
|
||||||
|
econf:binary();
|
||||||
|
opt_type(ldap_deref_aliases) ->
|
||||||
|
econf:enum([never, searching, finding, always]);
|
||||||
|
opt_type(ldap_dn_filter) ->
|
||||||
|
econf:and_then(
|
||||||
|
econf:non_empty(
|
||||||
|
econf:map(
|
||||||
|
econf:ldap_filter(),
|
||||||
|
econf:list(econf:binary()))),
|
||||||
|
fun hd/1);
|
||||||
|
opt_type(ldap_encrypt) ->
|
||||||
|
econf:enum([tls, starttls, none]);
|
||||||
|
opt_type(ldap_filter) ->
|
||||||
|
econf:ldap_filter();
|
||||||
|
opt_type(ldap_password) ->
|
||||||
|
econf:binary();
|
||||||
|
opt_type(ldap_port) ->
|
||||||
|
econf:port();
|
||||||
|
opt_type(ldap_rootdn) ->
|
||||||
|
econf:binary();
|
||||||
|
opt_type(ldap_servers) ->
|
||||||
|
econf:list(econf:domain(), [unique]);
|
||||||
|
opt_type(ldap_tls_cacertfile) ->
|
||||||
|
econf:pem();
|
||||||
|
opt_type(ldap_tls_certfile) ->
|
||||||
|
econf:pem();
|
||||||
|
opt_type(ldap_tls_depth) ->
|
||||||
|
econf:non_neg_int();
|
||||||
|
opt_type(ldap_tls_verify) ->
|
||||||
|
econf:enum([hard, soft, false]);
|
||||||
|
opt_type(ldap_uids) ->
|
||||||
|
econf:either(
|
||||||
|
econf:list(
|
||||||
|
econf:and_then(
|
||||||
|
econf:binary(),
|
||||||
|
fun(U) -> {U, <<"%u">>} end)),
|
||||||
|
econf:map(econf:binary(), econf:binary(), [unique]));
|
||||||
|
opt_type(listen) ->
|
||||||
|
ejabberd_listener:validator();
|
||||||
|
opt_type(log_rate_limit) ->
|
||||||
|
econf:non_neg_int();
|
||||||
|
opt_type(log_rotate_count) ->
|
||||||
|
econf:non_neg_int();
|
||||||
|
opt_type(log_rotate_date) ->
|
||||||
|
econf:string("^(\\$((D(([0-9])|(1[0-9])|(2[0-3])))|"
|
||||||
|
"(((W[0-6])|(M(([1-2][0-9])|(3[0-1])|([1-9]))))"
|
||||||
|
"(D(([0-9])|(1[0-9])|(2[0-3])))?)))?$");
|
||||||
|
opt_type(log_rotate_size) ->
|
||||||
|
econf:non_neg_int();
|
||||||
|
opt_type(loglevel) ->
|
||||||
|
econf:int(0, 5);
|
||||||
|
opt_type(max_fsm_queue) ->
|
||||||
|
econf:pos_int();
|
||||||
|
opt_type(modules) ->
|
||||||
|
econf:map(econf:atom(), econf:any());
|
||||||
|
opt_type(negotiation_timeout) ->
|
||||||
|
econf:timeout(second);
|
||||||
|
opt_type(net_ticktime) ->
|
||||||
|
econf:pos_int();
|
||||||
|
opt_type(new_sql_schema) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(oauth_access) ->
|
||||||
|
econf:acl();
|
||||||
|
opt_type(oauth_cache_life_time) ->
|
||||||
|
econf:pos_int(infinity);
|
||||||
|
opt_type(oauth_cache_missed) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(oauth_cache_size) ->
|
||||||
|
econf:pos_int(infinity);
|
||||||
|
opt_type(oauth_db_type) ->
|
||||||
|
econf:db_type(ejabberd_oauth);
|
||||||
|
opt_type(oauth_expire) ->
|
||||||
|
econf:non_neg_int();
|
||||||
|
opt_type(oauth_use_cache) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(oom_killer) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(oom_queue) ->
|
||||||
|
econf:pos_int();
|
||||||
|
opt_type(oom_watermark) ->
|
||||||
|
econf:int(1, 99);
|
||||||
|
opt_type(outgoing_s2s_families) ->
|
||||||
|
econf:and_then(
|
||||||
|
econf:non_empty(
|
||||||
|
econf:list(econf:enum([ipv4, ipv6]), [unique])),
|
||||||
|
fun(L) ->
|
||||||
|
lists:map(
|
||||||
|
fun(ipv4) -> inet;
|
||||||
|
(ipv6) -> inet6
|
||||||
|
end, L)
|
||||||
|
end);
|
||||||
|
opt_type(outgoing_s2s_port) ->
|
||||||
|
econf:port();
|
||||||
|
opt_type(outgoing_s2s_timeout) ->
|
||||||
|
econf:timeout(second, infinity);
|
||||||
|
opt_type(pam_service) ->
|
||||||
|
econf:binary();
|
||||||
|
opt_type(pam_userinfotype) ->
|
||||||
|
econf:enum([username, jid]);
|
||||||
|
opt_type(pgsql_users_number_estimate) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(queue_dir) ->
|
||||||
|
econf:directory(write);
|
||||||
|
opt_type(queue_type) ->
|
||||||
|
econf:enum([ram, file]);
|
||||||
|
opt_type(redis_connect_timeout) ->
|
||||||
|
econf:timeout(second);
|
||||||
|
opt_type(redis_db) ->
|
||||||
|
econf:non_neg_int();
|
||||||
|
opt_type(redis_password) ->
|
||||||
|
econf:string();
|
||||||
|
opt_type(redis_pool_size) ->
|
||||||
|
econf:pos_int();
|
||||||
|
opt_type(redis_port) ->
|
||||||
|
econf:port();
|
||||||
|
opt_type(redis_queue_type) ->
|
||||||
|
econf:enum([ram, file]);
|
||||||
|
opt_type(redis_server) ->
|
||||||
|
econf:string();
|
||||||
|
opt_type(registration_timeout) ->
|
||||||
|
econf:pos_int(infinity);
|
||||||
|
opt_type(resource_conflict) ->
|
||||||
|
econf:enum([setresource, closeold, closenew, acceptnew]);
|
||||||
|
opt_type(riak_cacertfile) ->
|
||||||
|
econf:and_then(econf:pem(), econf:string());
|
||||||
|
opt_type(riak_password) ->
|
||||||
|
econf:string();
|
||||||
|
opt_type(riak_pool_size) ->
|
||||||
|
econf:pos_int();
|
||||||
|
opt_type(riak_port) ->
|
||||||
|
econf:port();
|
||||||
|
opt_type(riak_server) ->
|
||||||
|
econf:string();
|
||||||
|
opt_type(riak_start_interval) ->
|
||||||
|
econf:timeout(second);
|
||||||
|
opt_type(riak_username) ->
|
||||||
|
econf:string();
|
||||||
|
opt_type(route_subdomains) ->
|
||||||
|
econf:enum([s2s, local]);
|
||||||
|
opt_type(router_cache_life_time) ->
|
||||||
|
econf:pos_int(infinity);
|
||||||
|
opt_type(router_cache_missed) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(router_cache_size) ->
|
||||||
|
econf:pos_int(infinity);
|
||||||
|
opt_type(router_db_type) ->
|
||||||
|
econf:db_type(ejabberd_router);
|
||||||
|
opt_type(router_use_cache) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(rpc_timeout) ->
|
||||||
|
econf:timeout(second);
|
||||||
|
opt_type(s2s_access) ->
|
||||||
|
econf:acl();
|
||||||
|
opt_type(s2s_cafile) ->
|
||||||
|
econf:pem();
|
||||||
|
opt_type(s2s_ciphers) ->
|
||||||
|
econf:binary();
|
||||||
|
opt_type(s2s_dhfile) ->
|
||||||
|
econf:file();
|
||||||
|
opt_type(s2s_dns_retries) ->
|
||||||
|
econf:non_neg_int();
|
||||||
|
opt_type(s2s_dns_timeout) ->
|
||||||
|
econf:timeout(second, infinity);
|
||||||
|
opt_type(s2s_max_retry_delay) ->
|
||||||
|
econf:pos_int();
|
||||||
|
opt_type(s2s_protocol_options) ->
|
||||||
|
econf:and_then(
|
||||||
|
econf:list(econf:binary(), [unique]),
|
||||||
|
fun concat_tls_protocol_options/1);
|
||||||
|
opt_type(s2s_queue_type) ->
|
||||||
|
econf:enum([ram, file]);
|
||||||
|
opt_type(s2s_timeout) ->
|
||||||
|
econf:timeout(second, infinity);
|
||||||
|
opt_type(s2s_tls_compression) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(s2s_use_starttls) ->
|
||||||
|
econf:either(
|
||||||
|
econf:bool(),
|
||||||
|
econf:enum([optional, required]));
|
||||||
|
opt_type(s2s_zlib) ->
|
||||||
|
econf:and_then(
|
||||||
|
econf:bool(),
|
||||||
|
fun(false) -> false;
|
||||||
|
(true) ->
|
||||||
|
ejabberd:start_app(ezlib),
|
||||||
|
true
|
||||||
|
end);
|
||||||
|
opt_type(shaper) ->
|
||||||
|
ejabberd_shaper:validator(shaper);
|
||||||
|
opt_type(shaper_rules) ->
|
||||||
|
ejabberd_shaper:validator(shaper_rules);
|
||||||
|
opt_type(sm_cache_life_time) ->
|
||||||
|
econf:pos_int(infinity);
|
||||||
|
opt_type(sm_cache_missed) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(sm_cache_size) ->
|
||||||
|
econf:pos_int(infinity);
|
||||||
|
opt_type(sm_db_type) ->
|
||||||
|
econf:db_type(ejabberd_sm);
|
||||||
|
opt_type(sm_use_cache) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(sql_connect_timeout) ->
|
||||||
|
econf:timeout(second);
|
||||||
|
opt_type(sql_database) ->
|
||||||
|
econf:binary();
|
||||||
|
opt_type(sql_keepalive_interval) ->
|
||||||
|
econf:timeout(second);
|
||||||
|
opt_type(sql_password) ->
|
||||||
|
econf:binary();
|
||||||
|
opt_type(sql_pool_size) ->
|
||||||
|
econf:pos_int();
|
||||||
|
opt_type(sql_port) ->
|
||||||
|
econf:port();
|
||||||
|
opt_type(sql_query_timeout) ->
|
||||||
|
econf:timeout(second);
|
||||||
|
opt_type(sql_queue_type) ->
|
||||||
|
econf:enum([ram, file]);
|
||||||
|
opt_type(sql_server) ->
|
||||||
|
econf:binary();
|
||||||
|
opt_type(sql_ssl) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(sql_ssl_cafile) ->
|
||||||
|
econf:pem();
|
||||||
|
opt_type(sql_ssl_certfile) ->
|
||||||
|
econf:pem();
|
||||||
|
opt_type(sql_ssl_verify) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(sql_start_interval) ->
|
||||||
|
econf:timeout(second);
|
||||||
|
opt_type(sql_type) ->
|
||||||
|
econf:enum([mysql, pgsql, sqlite, mssql, odbc]);
|
||||||
|
opt_type(sql_username) ->
|
||||||
|
econf:binary();
|
||||||
|
opt_type(trusted_proxies) ->
|
||||||
|
econf:either(all, econf:list(econf:ip_mask()));
|
||||||
|
opt_type(use_cache) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(validate_stream) ->
|
||||||
|
econf:bool();
|
||||||
|
opt_type(version) ->
|
||||||
|
econf:binary();
|
||||||
|
opt_type(websocket_origin) ->
|
||||||
|
econf:list(
|
||||||
|
econf:and_then(
|
||||||
|
econf:and_then(
|
||||||
|
econf:binary_sep("\\s+"),
|
||||||
|
econf:list(econf:url(), [unique])),
|
||||||
|
fun(L) -> str:join(L, <<" ">>) end),
|
||||||
|
[unique]);
|
||||||
|
opt_type(websocket_ping_interval) ->
|
||||||
|
econf:timeout(second);
|
||||||
|
opt_type(websocket_timeout) ->
|
||||||
|
econf:timeout(second).
|
||||||
|
|
||||||
|
%% We only define the types of options that cannot be derived
|
||||||
|
%% automatically by tools/opt_type.sh script
|
||||||
|
-spec options() -> [{s2s_protocol_options, undefined | binary()} |
|
||||||
|
{c2s_protocol_options, undefined | binary()} |
|
||||||
|
{websocket_origin, [binary()]} |
|
||||||
|
{disable_sasl_mechanisms, [binary()]} |
|
||||||
|
{s2s_zlib, boolean()} |
|
||||||
|
{listen, [ejabberd_listener:listener()]} |
|
||||||
|
{modules, [{module(), gen_mod:opts(), integer()}]} |
|
||||||
|
{ldap_uids, [{binary(), binary()}]} |
|
||||||
|
{ldap_dn_filter, {binary(), [binary()]}} |
|
||||||
|
{outgoing_s2s_families, [inet | inet6, ...]} |
|
||||||
|
{acl, [{atom(), [acl:acl_rule()]}]} |
|
||||||
|
{access_rules, [{atom(), acl:access()}]} |
|
||||||
|
{shaper, #{atom() => ejabberd_shaper:shaper_rate()}} |
|
||||||
|
{shaper_rules, [{atom(), [ejabberd_shaper:shaper_rule()]}]} |
|
||||||
|
{api_permissions, [ejabberd_access_permissions:permission()]} |
|
||||||
|
{append_host_config, [{binary(), any()}]} |
|
||||||
|
{host_config, [{binary(), any()}]} |
|
||||||
|
{define_macro, any()} |
|
||||||
|
{include_config_file, any()} |
|
||||||
|
{atom(), any()}].
|
||||||
|
options() ->
|
||||||
|
[%% Top-priority options
|
||||||
|
hosts,
|
||||||
|
{loglevel, 4},
|
||||||
|
{cache_life_time, 3600},
|
||||||
|
{cache_missed, true},
|
||||||
|
{cache_size, 1000},
|
||||||
|
{use_cache, true},
|
||||||
|
{default_db, mnesia},
|
||||||
|
{default_ram_db, mnesia},
|
||||||
|
{queue_type, ram},
|
||||||
|
%% Other options
|
||||||
|
{acl, []},
|
||||||
|
{access_rules, []},
|
||||||
|
{acme, #{}},
|
||||||
|
{allow_contrib_modules, true},
|
||||||
|
{allow_multiple_connections, false},
|
||||||
|
{anonymous_protocol, sasl_anon},
|
||||||
|
{api_permissions,
|
||||||
|
[{<<"admin access">>,
|
||||||
|
{[],
|
||||||
|
[{acl, admin},
|
||||||
|
{oauth, {[<<"ejabberd:admin">>], [{acl, admin}]}}],
|
||||||
|
{all, [start, stop]}}}]},
|
||||||
|
{append_host_config, []},
|
||||||
|
{auth_cache_life_time,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end},
|
||||||
|
{auth_cache_missed,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({cache_missed, Host}) end},
|
||||||
|
{auth_cache_size,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({cache_size, Host}) end},
|
||||||
|
{auth_method,
|
||||||
|
fun(Host) -> [ejabberd_config:default_db(Host, ejabberd_auth)] end},
|
||||||
|
{auth_password_format, plain},
|
||||||
|
{auth_use_cache,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end},
|
||||||
|
{c2s_cafile, undefined},
|
||||||
|
{c2s_ciphers, undefined},
|
||||||
|
{c2s_dhfile, undefined},
|
||||||
|
{c2s_protocol_options, undefined},
|
||||||
|
{c2s_tls_compression, undefined},
|
||||||
|
{ca_file, iolist_to_binary(pkix:get_cafile())},
|
||||||
|
{captcha_cmd, undefined},
|
||||||
|
{captcha_host, <<"">>},
|
||||||
|
{captcha_limit, infinity},
|
||||||
|
{certfiles, undefined},
|
||||||
|
{cluster_backend, mnesia},
|
||||||
|
{cluster_nodes, []},
|
||||||
|
{define_macro, []},
|
||||||
|
{disable_sasl_mechanisms, []},
|
||||||
|
{domain_balancing, #{}},
|
||||||
|
{ext_api_headers, <<>>},
|
||||||
|
{ext_api_http_pool_size, 100},
|
||||||
|
{ext_api_path_oauth, <<"/oauth">>},
|
||||||
|
{ext_api_url, <<"http://localhost/api">>},
|
||||||
|
{extauth_pool_name, undefined},
|
||||||
|
{extauth_pool_size, undefined},
|
||||||
|
{extauth_program, undefined},
|
||||||
|
{fqdn, fun fqdn/1},
|
||||||
|
{hide_sensitive_log_data, false},
|
||||||
|
{host_config, []},
|
||||||
|
{include_config_file, []},
|
||||||
|
{language, <<"en">>},
|
||||||
|
{ldap_backups, []},
|
||||||
|
{ldap_base, <<"">>},
|
||||||
|
{ldap_deref_aliases, never},
|
||||||
|
{ldap_dn_filter, {undefined, []}},
|
||||||
|
{ldap_encrypt, none},
|
||||||
|
{ldap_filter, <<"">>},
|
||||||
|
{ldap_password, <<"">>},
|
||||||
|
{ldap_port,
|
||||||
|
fun(Host) ->
|
||||||
|
case ejabberd_option:ldap_encrypt(Host) of
|
||||||
|
tls -> 636;
|
||||||
|
_ -> 389
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
{ldap_rootdn, <<"">>},
|
||||||
|
{ldap_servers, [<<"localhost">>]},
|
||||||
|
{ldap_tls_cacertfile, undefined},
|
||||||
|
{ldap_tls_certfile, undefined},
|
||||||
|
{ldap_tls_depth, undefined},
|
||||||
|
{ldap_tls_verify, false},
|
||||||
|
{ldap_uids, [{<<"uid">>, <<"%u">>}]},
|
||||||
|
{listen, []},
|
||||||
|
{log_rate_limit, undefined},
|
||||||
|
{log_rotate_count, undefined},
|
||||||
|
{log_rotate_date, undefined},
|
||||||
|
{log_rotate_size, undefined},
|
||||||
|
{max_fsm_queue, undefined},
|
||||||
|
{modules, []},
|
||||||
|
{negotiation_timeout, timer:seconds(30)},
|
||||||
|
{net_ticktime, 60},
|
||||||
|
{new_sql_schema, ?USE_NEW_SQL_SCHEMA_DEFAULT},
|
||||||
|
{oauth_access, none},
|
||||||
|
{oauth_cache_life_time,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end},
|
||||||
|
{oauth_cache_missed,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({cache_missed, Host}) end},
|
||||||
|
{oauth_cache_size,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({cache_size, Host}) end},
|
||||||
|
{oauth_db_type,
|
||||||
|
fun(Host) -> ejabberd_config:default_db(Host, ejabberd_oauth) end},
|
||||||
|
{oauth_expire, 4294967},
|
||||||
|
{oauth_use_cache,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end},
|
||||||
|
{oom_killer, true},
|
||||||
|
{oom_queue, 10000},
|
||||||
|
{oom_watermark, 80},
|
||||||
|
{outgoing_s2s_families, [inet, inet6]},
|
||||||
|
{outgoing_s2s_port, 5269},
|
||||||
|
{outgoing_s2s_timeout, timer:seconds(10)},
|
||||||
|
{pam_service, <<"ejabberd">>},
|
||||||
|
{pam_userinfotype, username},
|
||||||
|
{pgsql_users_number_estimate, false},
|
||||||
|
{queue_dir, undefined},
|
||||||
|
{redis_connect_timeout, timer:seconds(1)},
|
||||||
|
{redis_db, 0},
|
||||||
|
{redis_password, ""},
|
||||||
|
{redis_pool_size, 10},
|
||||||
|
{redis_port, 6379},
|
||||||
|
{redis_queue_type,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({queue_type, Host}) end},
|
||||||
|
{redis_server, "localhost"},
|
||||||
|
{registration_timeout, 600},
|
||||||
|
{resource_conflict, acceptnew},
|
||||||
|
{riak_cacertfile, nil},
|
||||||
|
{riak_password, nil},
|
||||||
|
{riak_pool_size, 10},
|
||||||
|
{riak_port, 8087},
|
||||||
|
{riak_server, "127.0.0.1"},
|
||||||
|
{riak_start_interval, timer:seconds(30)},
|
||||||
|
{riak_username, nil},
|
||||||
|
{route_subdomains, local},
|
||||||
|
{router_cache_life_time,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end},
|
||||||
|
{router_cache_missed,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({cache_missed, Host}) end},
|
||||||
|
{router_cache_size,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({cache_size, Host}) end},
|
||||||
|
{router_db_type,
|
||||||
|
fun(Host) -> ejabberd_config:default_ram_db(Host, ejabberd_router) end},
|
||||||
|
{router_use_cache,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end},
|
||||||
|
{rpc_timeout, timer:seconds(5)},
|
||||||
|
{s2s_access, all},
|
||||||
|
{s2s_cafile, undefined},
|
||||||
|
{s2s_ciphers, undefined},
|
||||||
|
{s2s_dhfile, undefined},
|
||||||
|
{s2s_dns_retries, 2},
|
||||||
|
{s2s_dns_timeout, timer:seconds(10)},
|
||||||
|
{s2s_max_retry_delay, 300},
|
||||||
|
{s2s_protocol_options, undefined},
|
||||||
|
{s2s_queue_type,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({queue_type, Host}) end},
|
||||||
|
{s2s_timeout, timer:minutes(10)},
|
||||||
|
{s2s_tls_compression, undefined},
|
||||||
|
{s2s_use_starttls, false},
|
||||||
|
{s2s_zlib, false},
|
||||||
|
{shaper, #{}},
|
||||||
|
{shaper_rules, []},
|
||||||
|
{sm_cache_life_time,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end},
|
||||||
|
{sm_cache_missed,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({cache_missed, Host}) end},
|
||||||
|
{sm_cache_size,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({cache_size, Host}) end},
|
||||||
|
{sm_db_type,
|
||||||
|
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_connect_timeout, timer:seconds(5)},
|
||||||
|
{sql_database, undefined},
|
||||||
|
{sql_keepalive_interval, undefined},
|
||||||
|
{sql_password, <<"">>},
|
||||||
|
{sql_pool_size,
|
||||||
|
fun(Host) ->
|
||||||
|
case ejabberd_config:get_option({sql_type, Host}) of
|
||||||
|
sqlite -> 1;
|
||||||
|
_ -> 10
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
{sql_port,
|
||||||
|
fun(Host) ->
|
||||||
|
case ejabberd_config:get_option({sql_type, Host}) of
|
||||||
|
mssql -> 1433;
|
||||||
|
mysql -> 3306;
|
||||||
|
pgsql -> 5432;
|
||||||
|
_ -> undefined
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
{sql_query_timeout, timer:seconds(60)},
|
||||||
|
{sql_queue_type,
|
||||||
|
fun(Host) -> ejabberd_config:get_option({queue_type, Host}) end},
|
||||||
|
{sql_server, <<"localhost">>},
|
||||||
|
{sql_ssl, false},
|
||||||
|
{sql_ssl_cafile, undefined},
|
||||||
|
{sql_ssl_certfile, undefined},
|
||||||
|
{sql_ssl_verify, false},
|
||||||
|
{sql_start_interval, timer:seconds(30)},
|
||||||
|
{sql_username, <<"ejabberd">>},
|
||||||
|
{trusted_proxies, []},
|
||||||
|
{validate_stream, false},
|
||||||
|
{version, fun version/1},
|
||||||
|
{websocket_origin, []},
|
||||||
|
{websocket_ping_interval, timer:seconds(60)},
|
||||||
|
{websocket_timeout, timer:minutes(5)}].
|
||||||
|
|
||||||
|
-spec globals() -> [atom()].
|
||||||
|
globals() ->
|
||||||
|
[acme,
|
||||||
|
allow_contrib_modules,
|
||||||
|
api_permissions,
|
||||||
|
append_host_config,
|
||||||
|
auth_cache_life_time,
|
||||||
|
auth_cache_missed,
|
||||||
|
auth_cache_size,
|
||||||
|
ca_file,
|
||||||
|
captcha_cmd,
|
||||||
|
captcha_host,
|
||||||
|
captcha_limit,
|
||||||
|
certfiles,
|
||||||
|
cluster_backend,
|
||||||
|
cluster_nodes,
|
||||||
|
domain_balancing,
|
||||||
|
ext_api_path_oauth,
|
||||||
|
fqdn,
|
||||||
|
hosts,
|
||||||
|
host_config,
|
||||||
|
listen,
|
||||||
|
loglevel,
|
||||||
|
log_rate_limit,
|
||||||
|
log_rotate_count,
|
||||||
|
log_rotate_date,
|
||||||
|
log_rotate_size,
|
||||||
|
negotiation_timeout,
|
||||||
|
net_ticktime,
|
||||||
|
new_sql_schema,
|
||||||
|
node_start,
|
||||||
|
oauth_cache_life_time,
|
||||||
|
oauth_cache_missed,
|
||||||
|
oauth_cache_size,
|
||||||
|
oauth_db_type,
|
||||||
|
oauth_expire,
|
||||||
|
oauth_use_cache,
|
||||||
|
oom_killer,
|
||||||
|
oom_queue,
|
||||||
|
oom_watermark,
|
||||||
|
queue_dir,
|
||||||
|
redis_connect_timeout,
|
||||||
|
redis_db,
|
||||||
|
redis_password,
|
||||||
|
redis_pool_size,
|
||||||
|
redis_port,
|
||||||
|
redis_queue_type,
|
||||||
|
redis_server,
|
||||||
|
registration_timeout,
|
||||||
|
riak_cacertfile,
|
||||||
|
riak_password,
|
||||||
|
riak_pool_size,
|
||||||
|
riak_port,
|
||||||
|
riak_server,
|
||||||
|
riak_start_interval,
|
||||||
|
riak_username,
|
||||||
|
router_cache_life_time,
|
||||||
|
router_cache_missed,
|
||||||
|
router_cache_size,
|
||||||
|
router_db_type,
|
||||||
|
router_use_cache,
|
||||||
|
rpc_timeout,
|
||||||
|
s2s_max_retry_delay,
|
||||||
|
shaper,
|
||||||
|
sm_cache_life_time,
|
||||||
|
sm_cache_missed,
|
||||||
|
sm_cache_size,
|
||||||
|
version,
|
||||||
|
websocket_origin,
|
||||||
|
websocket_ping_interval,
|
||||||
|
websocket_timeout].
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
-spec validator() -> econf:validator().
|
||||||
|
validator() ->
|
||||||
|
Disallowed = ejabberd_config:globals(),
|
||||||
|
{Validators, Required} = ejabberd_config:validators(Disallowed),
|
||||||
|
econf:options(
|
||||||
|
Validators,
|
||||||
|
[{disallowed, Required ++ Disallowed}, unique]).
|
||||||
|
|
||||||
|
-spec fqdn(global | binary()) -> [binary()].
|
||||||
|
fqdn(global) ->
|
||||||
|
{ok, Hostname} = inet:gethostname(),
|
||||||
|
case inet:gethostbyname(Hostname) of
|
||||||
|
{ok, #hostent{h_name = FQDN}} ->
|
||||||
|
case jid:nameprep(iolist_to_binary(FQDN)) of
|
||||||
|
error -> [];
|
||||||
|
Domain -> [Domain]
|
||||||
|
end;
|
||||||
|
{error, _} ->
|
||||||
|
[]
|
||||||
|
end;
|
||||||
|
fqdn(_) ->
|
||||||
|
ejabberd_config:get_option(fqdn).
|
||||||
|
|
||||||
|
-spec version(global | binary()) -> binary().
|
||||||
|
version(global) ->
|
||||||
|
case application:get_env(ejabberd, custom_vsn) of
|
||||||
|
{ok, Vsn0} when is_list(Vsn0) ->
|
||||||
|
list_to_binary(Vsn0);
|
||||||
|
{ok, Vsn1} when is_binary(Vsn1) ->
|
||||||
|
Vsn1;
|
||||||
|
_ ->
|
||||||
|
case application:get_key(ejabberd, vsn) of
|
||||||
|
undefined -> <<"">>;
|
||||||
|
{ok, Vsn} -> list_to_binary(Vsn)
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
version(_) ->
|
||||||
|
ejabberd_config:get_option(version).
|
||||||
|
|
||||||
|
-spec concat_tls_protocol_options([binary()]) -> binary().
|
||||||
|
concat_tls_protocol_options(Opts) ->
|
||||||
|
str:join(Opts, <<"|">>).
|
@ -92,7 +92,7 @@ import_file(FileName, State) ->
|
|||||||
|
|
||||||
-spec export_server(binary()) -> any().
|
-spec export_server(binary()) -> any().
|
||||||
export_server(Dir) ->
|
export_server(Dir) ->
|
||||||
export_hosts(ejabberd_config:get_myhosts(), Dir).
|
export_hosts(ejabberd_option:hosts(), Dir).
|
||||||
|
|
||||||
-spec export_host(binary(), binary()) -> any().
|
-spec export_host(binary(), binary()) -> any().
|
||||||
export_host(Dir, Host) ->
|
export_host(Dir, Host) ->
|
||||||
|
@ -22,11 +22,10 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(ejabberd_pkix).
|
-module(ejabberd_pkix).
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([start_link/0, opt_type/1]).
|
-export([start_link/0]).
|
||||||
-export([certs_dir/0, ca_file/0]).
|
-export([certs_dir/0]).
|
||||||
-export([add_certfile/1, try_certfile/1, get_certfile/0, get_certfile/1]).
|
-export([add_certfile/1, try_certfile/1, get_certfile/0, get_certfile/1]).
|
||||||
%% Hooks
|
%% Hooks
|
||||||
-export([ejabberd_started/0, config_reloaded/0]).
|
-export([ejabberd_started/0, config_reloaded/0]).
|
||||||
@ -99,11 +98,7 @@ get_certfile() ->
|
|||||||
Ret -> {ok, select_certfile(Ret)}
|
Ret -> {ok, select_certfile(Ret)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec ca_file() -> filename() | undefined.
|
-spec certs_dir() -> file:filename_all().
|
||||||
ca_file() ->
|
|
||||||
ejabberd_config:get_option(ca_file).
|
|
||||||
|
|
||||||
-spec certs_dir() -> file:dirname_all().
|
|
||||||
certs_dir() ->
|
certs_dir() ->
|
||||||
MnesiaDir = mnesia:system_info(directory),
|
MnesiaDir = mnesia:system_info(directory),
|
||||||
filename:join(MnesiaDir, "certs").
|
filename:join(MnesiaDir, "certs").
|
||||||
@ -116,24 +111,6 @@ ejabberd_started() ->
|
|||||||
config_reloaded() ->
|
config_reloaded() ->
|
||||||
gen_server:call(?MODULE, config_reloaded, ?CALL_TIMEOUT).
|
gen_server:call(?MODULE, config_reloaded, ?CALL_TIMEOUT).
|
||||||
|
|
||||||
opt_type(ca_path) ->
|
|
||||||
fun(_) ->
|
|
||||||
?WARNING_MSG("Option 'ca_path' has no effect anymore, "
|
|
||||||
"use 'ca_file' instead", []),
|
|
||||||
undefined
|
|
||||||
end;
|
|
||||||
opt_type(ca_file) ->
|
|
||||||
fun try_certfile/1;
|
|
||||||
opt_type(certfiles) ->
|
|
||||||
fun(Paths) -> [iolist_to_binary(Path) || Path <- Paths] end;
|
|
||||||
opt_type(O) when O == c2s_certfile; O == s2s_certfile; O == domain_certfile ->
|
|
||||||
fun(Path) ->
|
|
||||||
?WARNING_MSG("Option '~s' is deprecated, use 'certfiles' instead", [O]),
|
|
||||||
prep_path(Path)
|
|
||||||
end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[ca_path, ca_file, certfiles, c2s_certfile, s2s_certfile, domain_certfile].
|
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% gen_server callbacks
|
%%% gen_server callbacks
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
@ -177,7 +154,7 @@ handle_call(config_reloaded, _From, State) ->
|
|||||||
Old = State#state.files,
|
Old = State#state.files,
|
||||||
New = get_certfiles_from_config_options(),
|
New = get_certfiles_from_config_options(),
|
||||||
del_files(sets:subtract(Old, New)),
|
del_files(sets:subtract(Old, New)),
|
||||||
add_files(New),
|
_ = add_files(New),
|
||||||
case commit() of
|
case commit() of
|
||||||
{ok, _} ->
|
{ok, _} ->
|
||||||
check_domain_certfiles(),
|
check_domain_certfiles(),
|
||||||
@ -258,10 +235,9 @@ del_files(Files) ->
|
|||||||
|
|
||||||
-spec commit() -> {ok, [{filename(), pkix:error_reason()}]} | error.
|
-spec commit() -> {ok, [{filename(), pkix:error_reason()}]} | error.
|
||||||
commit() ->
|
commit() ->
|
||||||
Opts = case ca_file() of
|
CAFile = ejabberd_option:ca_file(),
|
||||||
undefined -> [];
|
?DEBUG("Using CA root certificates from: ~s", [CAFile]),
|
||||||
CAFile -> [{cafile, CAFile}]
|
Opts = [{cafile, CAFile}],
|
||||||
end,
|
|
||||||
case pkix:commit(certs_dir(), Opts) of
|
case pkix:commit(certs_dir(), Opts) of
|
||||||
{ok, Errors, Warnings, CAError} ->
|
{ok, Errors, Warnings, CAError} ->
|
||||||
log_errors(Errors),
|
log_errors(Errors),
|
||||||
@ -277,17 +253,21 @@ commit() ->
|
|||||||
|
|
||||||
-spec check_domain_certfiles() -> ok.
|
-spec check_domain_certfiles() -> ok.
|
||||||
check_domain_certfiles() ->
|
check_domain_certfiles() ->
|
||||||
Hosts = ejabberd_config:get_myhosts(),
|
Hosts = ejabberd_option:hosts(),
|
||||||
Routes = ejabberd_router:get_all_routes(),
|
Routes = ejabberd_router:get_all_routes(),
|
||||||
check_domain_certfiles(Hosts ++ Routes).
|
check_domain_certfiles(Hosts ++ Routes).
|
||||||
|
|
||||||
-spec check_domain_certfiles([binary()]) -> ok.
|
-spec check_domain_certfiles([binary()]) -> ok.
|
||||||
check_domain_certfiles(Hosts) ->
|
check_domain_certfiles(Hosts) ->
|
||||||
|
case ejabberd_listener:tls_listeners() of
|
||||||
|
[] -> ok;
|
||||||
|
_ ->
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(Host) ->
|
fun(Host) ->
|
||||||
case get_certfile_no_default(Host) of
|
case get_certfile_no_default(Host) of
|
||||||
error ->
|
error ->
|
||||||
?WARNING_MSG("No certificate found matching '~s': strictly "
|
?WARNING_MSG(
|
||||||
|
"No certificate found matching '~s': strictly "
|
||||||
"configured clients or servers will reject "
|
"configured clients or servers will reject "
|
||||||
"connections with this host; obtain "
|
"connections with this host; obtain "
|
||||||
"a certificate for this (sub)domain from any "
|
"a certificate for this (sub)domain from any "
|
||||||
@ -297,15 +277,12 @@ check_domain_certfiles(Hosts) ->
|
|||||||
_ ->
|
_ ->
|
||||||
ok
|
ok
|
||||||
end
|
end
|
||||||
end, Hosts).
|
end, Hosts)
|
||||||
|
end.
|
||||||
|
|
||||||
-spec deprecated_options() -> [atom()].
|
-spec get_certfiles_from_config_options() -> sets:set(filename()).
|
||||||
deprecated_options() ->
|
get_certfiles_from_config_options() ->
|
||||||
[c2s_certfile, s2s_certfile, domain_certfile].
|
case ejabberd_option:certfiles() of
|
||||||
|
|
||||||
-spec global_certfiles() -> sets:set(filename()).
|
|
||||||
global_certfiles() ->
|
|
||||||
case ejabberd_config:get_option(certfiles) of
|
|
||||||
undefined ->
|
undefined ->
|
||||||
sets:new();
|
sets:new();
|
||||||
Paths ->
|
Paths ->
|
||||||
@ -316,25 +293,6 @@ global_certfiles() ->
|
|||||||
end, sets:new(), Paths)
|
end, sets:new(), Paths)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec local_certfiles() -> sets:set(filename()).
|
|
||||||
local_certfiles() ->
|
|
||||||
Opts = [{Opt, Host} || Opt <- deprecated_options(),
|
|
||||||
Host <- ejabberd_config:get_myhosts()],
|
|
||||||
lists:foldl(
|
|
||||||
fun(OptHost, Acc) ->
|
|
||||||
case ejabberd_config:get_option(OptHost) of
|
|
||||||
undefined -> Acc;
|
|
||||||
Path -> sets:add_element(Path, Acc)
|
|
||||||
end
|
|
||||||
end, sets:new(), Opts).
|
|
||||||
|
|
||||||
-spec get_certfiles_from_config_options() -> sets:set(filename()).
|
|
||||||
get_certfiles_from_config_options() ->
|
|
||||||
Global = global_certfiles(),
|
|
||||||
Local = local_certfiles(),
|
|
||||||
Listen = sets:from_list(ejabberd_listener:get_certfiles()),
|
|
||||||
sets:union([Global, Local, Listen]).
|
|
||||||
|
|
||||||
-spec prep_path(file:filename_all()) -> filename().
|
-spec prep_path(file:filename_all()) -> filename().
|
||||||
prep_path(Path0) ->
|
prep_path(Path0) ->
|
||||||
case filename:pathtype(Path0) of
|
case filename:pathtype(Path0) of
|
||||||
|
@ -26,11 +26,10 @@
|
|||||||
-module(ejabberd_rdbms).
|
-module(ejabberd_rdbms).
|
||||||
|
|
||||||
-behaviour(supervisor).
|
-behaviour(supervisor).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-export([start_link/0, init/1, opt_type/1,
|
-export([start_link/0, init/1,
|
||||||
config_reloaded/0, start_host/1, stop_host/1]).
|
config_reloaded/0, start_host/1, stop_host/1]).
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
@ -55,7 +54,7 @@ get_specs() ->
|
|||||||
{ok, Spec} -> [Spec];
|
{ok, Spec} -> [Spec];
|
||||||
undefined -> []
|
undefined -> []
|
||||||
end
|
end
|
||||||
end, ejabberd_config:get_myhosts()).
|
end, ejabberd_option:hosts()).
|
||||||
|
|
||||||
-spec get_spec(binary()) -> {ok, supervisor:child_spec()} | undefined.
|
-spec get_spec(binary()) -> {ok, supervisor:child_spec()} | undefined.
|
||||||
get_spec(Host) ->
|
get_spec(Host) ->
|
||||||
@ -71,7 +70,7 @@ get_spec(Host) ->
|
|||||||
|
|
||||||
-spec config_reloaded() -> ok.
|
-spec config_reloaded() -> ok.
|
||||||
config_reloaded() ->
|
config_reloaded() ->
|
||||||
lists:foreach(fun reload_host/1, ejabberd_config:get_myhosts()).
|
lists:foreach(fun reload_host/1, ejabberd_option:hosts()).
|
||||||
|
|
||||||
-spec start_host(binary()) -> ok.
|
-spec start_host(binary()) -> ok.
|
||||||
start_host(Host) ->
|
start_host(Host) ->
|
||||||
@ -89,12 +88,13 @@ start_host(Host) ->
|
|||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec stop_host(binary()) -> ok.
|
-spec stop_host(binary()) -> ok | {error, atom()}.
|
||||||
stop_host(Host) ->
|
stop_host(Host) ->
|
||||||
SupName = gen_mod:get_module_proc(Host, ejabberd_sql_sup),
|
SupName = gen_mod:get_module_proc(Host, ejabberd_sql_sup),
|
||||||
supervisor:terminate_child(?MODULE, SupName),
|
case supervisor:terminate_child(?MODULE, SupName) of
|
||||||
supervisor:delete_child(?MODULE, SupName),
|
ok -> supervisor:delete_child(?MODULE, SupName);
|
||||||
ok.
|
Err -> Err
|
||||||
|
end.
|
||||||
|
|
||||||
-spec reload_host(binary()) -> ok.
|
-spec reload_host(binary()) -> ok.
|
||||||
reload_host(Host) ->
|
reload_host(Host) ->
|
||||||
@ -106,7 +106,7 @@ reload_host(Host) ->
|
|||||||
%% Returns {true, App} if we have configured sql for the given host
|
%% Returns {true, App} if we have configured sql for the given host
|
||||||
needs_sql(Host) ->
|
needs_sql(Host) ->
|
||||||
LHost = jid:nameprep(Host),
|
LHost = jid:nameprep(Host),
|
||||||
case ejabberd_config:get_option({sql_type, LHost}, undefined) of
|
case ejabberd_option:sql_type(LHost) of
|
||||||
mysql -> {true, p1_mysql};
|
mysql -> {true, p1_mysql};
|
||||||
pgsql -> {true, p1_pgsql};
|
pgsql -> {true, p1_pgsql};
|
||||||
sqlite -> {true, sqlite3};
|
sqlite -> {true, sqlite3};
|
||||||
@ -114,13 +114,3 @@ needs_sql(Host) ->
|
|||||||
odbc -> {true, odbc};
|
odbc -> {true, odbc};
|
||||||
undefined -> false
|
undefined -> false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(sql_type) ->
|
|
||||||
fun (mysql) -> mysql;
|
|
||||||
(pgsql) -> pgsql;
|
|
||||||
(sqlite) -> sqlite;
|
|
||||||
(mssql) -> mssql;
|
|
||||||
(odbc) -> odbc
|
|
||||||
end;
|
|
||||||
opt_type(_) -> [sql_type].
|
|
||||||
|
@ -59,8 +59,8 @@
|
|||||||
|
|
||||||
-type error_reason() :: binary() | timeout | disconnected | overloaded.
|
-type error_reason() :: binary() | timeout | disconnected | overloaded.
|
||||||
-type redis_error() :: {error, error_reason()}.
|
-type redis_error() :: {error, error_reason()}.
|
||||||
-type redis_reply() :: binary() | [binary()].
|
-type redis_reply() :: undefined | binary() | [binary()].
|
||||||
-type redis_command() :: [binary()].
|
-type redis_command() :: [iodata() | integer()].
|
||||||
-type redis_pipeline() :: [redis_command()].
|
-type redis_pipeline() :: [redis_command()].
|
||||||
-type redis_info() :: server | clients | memory | persistence |
|
-type redis_info() :: server | clients | memory | persistence |
|
||||||
stats | replication | cpu | commandstats |
|
stats | replication | cpu | commandstats |
|
||||||
@ -89,19 +89,18 @@ get_connection(I) ->
|
|||||||
q(Command) ->
|
q(Command) ->
|
||||||
call(get_rnd_id(), {q, Command}, ?MAX_RETRIES).
|
call(get_rnd_id(), {q, Command}, ?MAX_RETRIES).
|
||||||
|
|
||||||
-spec qp(redis_pipeline()) -> {ok, [redis_reply()]} | redis_error().
|
-spec qp(redis_pipeline()) -> [{ok, redis_reply()} | redis_error()] | redis_error().
|
||||||
qp(Pipeline) ->
|
qp(Pipeline) ->
|
||||||
call(get_rnd_id(), {qp, Pipeline}, ?MAX_RETRIES).
|
call(get_rnd_id(), {qp, Pipeline}, ?MAX_RETRIES).
|
||||||
|
|
||||||
-spec multi(fun(() -> any())) -> {ok, [redis_reply()]} | redis_error().
|
-spec multi(fun(() -> any())) -> {ok, redis_reply()} | redis_error().
|
||||||
multi(F) ->
|
multi(F) ->
|
||||||
case erlang:get(?TR_STACK) of
|
case erlang:get(?TR_STACK) of
|
||||||
undefined ->
|
undefined ->
|
||||||
erlang:put(?TR_STACK, []),
|
erlang:put(?TR_STACK, []),
|
||||||
try F() of
|
try F() of
|
||||||
_ ->
|
_ ->
|
||||||
Stack = erlang:get(?TR_STACK),
|
Stack = erlang:erase(?TR_STACK),
|
||||||
erlang:erase(?TR_STACK),
|
|
||||||
Command = [["MULTI"]|lists:reverse([["EXEC"]|Stack])],
|
Command = [["MULTI"]|lists:reverse([["EXEC"]|Stack])],
|
||||||
case qp(Command) of
|
case qp(Command) of
|
||||||
{error, _} = Err -> Err;
|
{error, _} = Err -> Err;
|
||||||
@ -298,7 +297,7 @@ hkeys(Key) ->
|
|||||||
|
|
||||||
-spec subscribe([binary()]) -> ok | redis_error().
|
-spec subscribe([binary()]) -> ok | redis_error().
|
||||||
subscribe(Channels) ->
|
subscribe(Channels) ->
|
||||||
try ?GEN_SERVER:call(get_proc(1), {subscribe, self(), Channels}, ?CALL_TIMEOUT)
|
try gen_server_call(get_proc(1), {subscribe, self(), Channels})
|
||||||
catch exit:{Why, {?GEN_SERVER, call, _}} ->
|
catch exit:{Why, {?GEN_SERVER, call, _}} ->
|
||||||
Reason = case Why of
|
Reason = case Why of
|
||||||
timeout -> timeout;
|
timeout -> timeout;
|
||||||
@ -329,7 +328,7 @@ script_load(Data) ->
|
|||||||
erlang:error(transaction_unsupported)
|
erlang:error(transaction_unsupported)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec evalsha(binary(), [iodata()], [iodata()]) -> {ok, binary()} | redis_error().
|
-spec evalsha(binary(), [iodata()], [iodata() | integer()]) -> {ok, binary()} | redis_error().
|
||||||
evalsha(SHA, Keys, Args) ->
|
evalsha(SHA, Keys, Args) ->
|
||||||
case erlang:get(?TR_STACK) of
|
case erlang:get(?TR_STACK) of
|
||||||
undefined ->
|
undefined ->
|
||||||
@ -458,13 +457,11 @@ code_change(_OldVsn, State, _Extra) ->
|
|||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
-spec connect(state()) -> {ok, pid()} | {error, any()}.
|
-spec connect(state()) -> {ok, pid()} | {error, any()}.
|
||||||
connect(#state{num = Num}) ->
|
connect(#state{num = Num}) ->
|
||||||
Server = ejabberd_config:get_option(redis_server, "localhost"),
|
Server = ejabberd_option:redis_server(),
|
||||||
Port = ejabberd_config:get_option(redis_port, 6379),
|
Port = ejabberd_option:redis_port(),
|
||||||
DB = ejabberd_config:get_option(redis_db, 0),
|
DB = ejabberd_option:redis_db(),
|
||||||
Pass = ejabberd_config:get_option(redis_password, ""),
|
Pass = ejabberd_option:redis_password(),
|
||||||
ConnTimeout = timer:seconds(
|
ConnTimeout = ejabberd_option:redis_connect_timeout(),
|
||||||
ejabberd_config:get_option(
|
|
||||||
redis_connect_timeout, 1)),
|
|
||||||
try case do_connect(Num, Server, Port, Pass, DB, ConnTimeout) of
|
try case do_connect(Num, Server, Port, Pass, DB, ConnTimeout) of
|
||||||
{ok, Client} ->
|
{ok, Client} ->
|
||||||
?DEBUG("Connection #~p established to Redis at ~s:~p",
|
?DEBUG("Connection #~p established to Redis at ~s:~p",
|
||||||
@ -498,7 +495,7 @@ do_connect(_, Server, Port, Pass, DB, ConnTimeout) ->
|
|||||||
-spec call(pos_integer(), {q, redis_command()}, integer()) ->
|
-spec call(pos_integer(), {q, redis_command()}, integer()) ->
|
||||||
{ok, redis_reply()} | redis_error();
|
{ok, redis_reply()} | redis_error();
|
||||||
(pos_integer(), {qp, redis_pipeline()}, integer()) ->
|
(pos_integer(), {qp, redis_pipeline()}, integer()) ->
|
||||||
{ok, [redis_reply()]} | redis_error().
|
[{ok, redis_reply()} | redis_error()] | redis_error().
|
||||||
call(I, {F, Cmd}, Retries) ->
|
call(I, {F, Cmd}, Retries) ->
|
||||||
?DEBUG("redis query: ~p", [Cmd]),
|
?DEBUG("redis query: ~p", [Cmd]),
|
||||||
Conn = get_connection(I),
|
Conn = get_connection(I),
|
||||||
@ -513,7 +510,7 @@ call(I, {F, Cmd}, Retries) ->
|
|||||||
end,
|
end,
|
||||||
case Res of
|
case Res of
|
||||||
{error, disconnected} when Retries > 0 ->
|
{error, disconnected} when Retries > 0 ->
|
||||||
try ?GEN_SERVER:call(get_proc(I), connect, ?CALL_TIMEOUT) of
|
try gen_server_call(get_proc(I), connect) of
|
||||||
ok -> call(I, {F, Cmd}, Retries-1);
|
ok -> call(I, {F, Cmd}, Retries-1);
|
||||||
{error, _} = Err -> Err
|
{error, _} = Err -> Err
|
||||||
catch exit:{Why, {?GEN_SERVER, call, _}} ->
|
catch exit:{Why, {?GEN_SERVER, call, _}} ->
|
||||||
@ -531,6 +528,14 @@ call(I, {F, Cmd}, Retries) ->
|
|||||||
Res
|
Res
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
gen_server_call(Proc, Msg) ->
|
||||||
|
case ejabberd_redis_sup:start() of
|
||||||
|
ok ->
|
||||||
|
?GEN_SERVER:call(Proc, Msg, ?CALL_TIMEOUT);
|
||||||
|
{error, _} ->
|
||||||
|
{error, disconnected}
|
||||||
|
end.
|
||||||
|
|
||||||
-spec log_error(redis_command() | redis_pipeline(), atom() | binary()) -> ok.
|
-spec log_error(redis_command() | redis_pipeline(), atom() | binary()) -> ok.
|
||||||
log_error(Cmd, Reason) ->
|
log_error(Cmd, Reason) ->
|
||||||
?ERROR_MSG("Redis request has failed:~n"
|
?ERROR_MSG("Redis request has failed:~n"
|
||||||
@ -542,8 +547,8 @@ log_error(Cmd, Reason) ->
|
|||||||
get_rnd_id() ->
|
get_rnd_id() ->
|
||||||
p1_rand:round_robin(ejabberd_redis_sup:get_pool_size() - 1) + 2.
|
p1_rand:round_robin(ejabberd_redis_sup:get_pool_size() - 1) + 2.
|
||||||
|
|
||||||
-spec get_result([{error, atom() | binary()} | {ok, iodata()}]) ->
|
-spec get_result([{ok, redis_reply()} | redis_error()]) ->
|
||||||
{ok, [redis_reply()]} | {error, binary()}.
|
{ok, redis_reply()} | redis_error().
|
||||||
get_result([{error, _} = Err|_]) ->
|
get_result([{error, _} = Err|_]) ->
|
||||||
Err;
|
Err;
|
||||||
get_result([{ok, _} = OK]) ->
|
get_result([{ok, _} = OK]) ->
|
||||||
@ -584,9 +589,7 @@ fsm_limit_opts() ->
|
|||||||
ejabberd_config:fsm_limit_opts([]).
|
ejabberd_config:fsm_limit_opts([]).
|
||||||
|
|
||||||
get_queue_type() ->
|
get_queue_type() ->
|
||||||
ejabberd_config:get_option(
|
ejabberd_option:redis_queue_type().
|
||||||
redis_queue_type,
|
|
||||||
ejabberd_config:default_queue_type(global)).
|
|
||||||
|
|
||||||
-spec flush_queue(p1_queue:queue()) -> p1_queue:queue().
|
-spec flush_queue(p1_queue:queue()) -> p1_queue:queue().
|
||||||
flush_queue(Q) ->
|
flush_queue(Q) ->
|
||||||
|
@ -23,41 +23,41 @@
|
|||||||
-module(ejabberd_redis_sup).
|
-module(ejabberd_redis_sup).
|
||||||
|
|
||||||
-behaviour(supervisor).
|
-behaviour(supervisor).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([start_link/0, get_pool_size/0,
|
-export([start/0, start_link/0]).
|
||||||
host_up/1, config_reloaded/0, opt_type/1]).
|
-export([get_pool_size/0, config_reloaded/0]).
|
||||||
|
|
||||||
%% Supervisor callbacks
|
%% Supervisor callbacks
|
||||||
-export([init/1]).
|
-export([init/1]).
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
-define(DEFAULT_POOL_SIZE, 10).
|
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% API functions
|
%%% API functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
start() ->
|
||||||
|
case is_started() of
|
||||||
|
true -> ok;
|
||||||
|
false ->
|
||||||
|
ejabberd:start_app(eredis),
|
||||||
|
Spec = {?MODULE, {?MODULE, start_link, []},
|
||||||
|
permanent, infinity, supervisor, [?MODULE]},
|
||||||
|
case supervisor:start_child(ejabberd_db_sup, Spec) of
|
||||||
|
{ok, _} -> ok;
|
||||||
|
{error, {already_started, _}} -> ok;
|
||||||
|
{error, Why} = Err ->
|
||||||
|
?ERROR_MSG("Failed to start ~s: ~p", [?MODULE, Why]),
|
||||||
|
Err
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
start_link() ->
|
start_link() ->
|
||||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||||
|
|
||||||
host_up(Host) ->
|
|
||||||
case is_redis_configured(Host) of
|
|
||||||
true ->
|
|
||||||
ejabberd:start_app(eredis),
|
|
||||||
lists:foreach(
|
|
||||||
fun(Spec) ->
|
|
||||||
supervisor:start_child(?MODULE, Spec)
|
|
||||||
end, get_specs());
|
|
||||||
false ->
|
|
||||||
ok
|
|
||||||
end.
|
|
||||||
|
|
||||||
config_reloaded() ->
|
config_reloaded() ->
|
||||||
case is_redis_configured() of
|
case is_started() of
|
||||||
true ->
|
true ->
|
||||||
ejabberd:start_app(eredis),
|
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(Spec) ->
|
fun(Spec) ->
|
||||||
supervisor:start_child(?MODULE, Spec)
|
supervisor:start_child(?MODULE, Spec)
|
||||||
@ -65,17 +65,15 @@ config_reloaded() ->
|
|||||||
PoolSize = get_pool_size(),
|
PoolSize = get_pool_size(),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun({Id, _, _, _}) when Id > PoolSize ->
|
fun({Id, _, _, _}) when Id > PoolSize ->
|
||||||
supervisor:terminate_child(?MODULE, Id),
|
case supervisor:terminate_child(?MODULE, Id) of
|
||||||
supervisor:delete_child(?MODULE, Id);
|
ok -> supervisor:delete_child(?MODULE, Id);
|
||||||
|
_ -> ok
|
||||||
|
end;
|
||||||
(_) ->
|
(_) ->
|
||||||
ok
|
ok
|
||||||
end, supervisor:which_children(?MODULE));
|
end, supervisor:which_children(?MODULE));
|
||||||
false ->
|
false ->
|
||||||
lists:foreach(
|
ok
|
||||||
fun({Id, _, _, _}) ->
|
|
||||||
supervisor:terminate_child(?MODULE, Id),
|
|
||||||
supervisor:delete_child(?MODULE, Id)
|
|
||||||
end, supervisor:which_children(?MODULE))
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
@ -83,36 +81,11 @@ config_reloaded() ->
|
|||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
init([]) ->
|
init([]) ->
|
||||||
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20),
|
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20),
|
||||||
ejabberd_hooks:add(host_up, ?MODULE, host_up, 20),
|
{ok, {{one_for_one, 500, 1}, get_specs()}}.
|
||||||
Specs = case is_redis_configured() of
|
|
||||||
true ->
|
|
||||||
ejabberd:start_app(eredis),
|
|
||||||
get_specs();
|
|
||||||
false ->
|
|
||||||
[]
|
|
||||||
end,
|
|
||||||
{ok, {{one_for_one, 500, 1}, Specs}}.
|
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
is_redis_configured() ->
|
|
||||||
lists:any(fun is_redis_configured/1, ejabberd_config:get_myhosts()).
|
|
||||||
|
|
||||||
is_redis_configured(Host) ->
|
|
||||||
ServerConfigured = ejabberd_config:has_option({redis_server, Host}),
|
|
||||||
PortConfigured = ejabberd_config:has_option({redis_port, Host}),
|
|
||||||
DBConfigured = ejabberd_config:has_option({redis_db, Host}),
|
|
||||||
PassConfigured = ejabberd_config:has_option({redis_password, Host}),
|
|
||||||
PoolSize = ejabberd_config:has_option({redis_pool_size, Host}),
|
|
||||||
ConnTimeoutConfigured = ejabberd_config:has_option(
|
|
||||||
{redis_connect_timeout, Host}),
|
|
||||||
SMConfigured = ejabberd_config:get_option({sm_db_type, Host}) == redis,
|
|
||||||
RouterConfigured = ejabberd_config:get_option({router_db_type, Host}) == redis,
|
|
||||||
ServerConfigured or PortConfigured or DBConfigured or PassConfigured or
|
|
||||||
PoolSize or ConnTimeoutConfigured or
|
|
||||||
SMConfigured or RouterConfigured.
|
|
||||||
|
|
||||||
get_specs() ->
|
get_specs() ->
|
||||||
lists:map(
|
lists:map(
|
||||||
fun(I) ->
|
fun(I) ->
|
||||||
@ -121,24 +94,7 @@ get_specs() ->
|
|||||||
end, lists:seq(1, get_pool_size())).
|
end, lists:seq(1, get_pool_size())).
|
||||||
|
|
||||||
get_pool_size() ->
|
get_pool_size() ->
|
||||||
ejabberd_config:get_option(redis_pool_size, ?DEFAULT_POOL_SIZE) + 1.
|
ejabberd_option:redis_pool_size() + 1.
|
||||||
|
|
||||||
iolist_to_list(IOList) ->
|
is_started() ->
|
||||||
binary_to_list(iolist_to_binary(IOList)).
|
whereis(?MODULE) /= undefined.
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(redis_connect_timeout) ->
|
|
||||||
fun (I) when is_integer(I), I > 0 -> I end;
|
|
||||||
opt_type(redis_db) ->
|
|
||||||
fun (I) when is_integer(I), I >= 0 -> I end;
|
|
||||||
opt_type(redis_password) -> fun iolist_to_list/1;
|
|
||||||
opt_type(redis_port) ->
|
|
||||||
fun (P) when is_integer(P), P > 0, P < 65536 -> P end;
|
|
||||||
opt_type(redis_server) -> fun iolist_to_list/1;
|
|
||||||
opt_type(redis_pool_size) ->
|
|
||||||
fun (I) when is_integer(I), I > 0 -> I end;
|
|
||||||
opt_type(redis_queue_type) ->
|
|
||||||
fun(ram) -> ram; (file) -> file end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[redis_connect_timeout, redis_db, redis_password,
|
|
||||||
redis_port, redis_pool_size, redis_server, redis_queue_type].
|
|
||||||
|
@ -125,13 +125,12 @@ sh_to_awk_3(<<"]", Sh/binary>>, false) ->
|
|||||||
sh_to_awk_3(<<C:8, Sh/binary>>, UpArrow) ->
|
sh_to_awk_3(<<C:8, Sh/binary>>, UpArrow) ->
|
||||||
[C|sh_to_awk_3(Sh, UpArrow)];
|
[C|sh_to_awk_3(Sh, UpArrow)];
|
||||||
sh_to_awk_3(<<>>, true) ->
|
sh_to_awk_3(<<>>, true) ->
|
||||||
[$^|sh_to_awk_1([])];
|
[$^|sh_to_awk_1(<<>>)];
|
||||||
sh_to_awk_3(<<>>, false) ->
|
sh_to_awk_3(<<>>, false) ->
|
||||||
sh_to_awk_1([]).
|
sh_to_awk_1(<<>>).
|
||||||
|
|
||||||
%% -type sh_special_char(char()) -> bool().
|
|
||||||
%% Test if a character is a special character.
|
%% Test if a character is a special character.
|
||||||
|
-spec sh_special_char(char()) -> boolean().
|
||||||
sh_special_char($|) -> true;
|
sh_special_char($|) -> true;
|
||||||
sh_special_char($*) -> true;
|
sh_special_char($*) -> true;
|
||||||
sh_special_char($+) -> true;
|
sh_special_char($+) -> true;
|
||||||
@ -146,4 +145,3 @@ sh_special_char($[) -> true;
|
|||||||
sh_special_char($]) -> true;
|
sh_special_char($]) -> true;
|
||||||
sh_special_char($") -> true;
|
sh_special_char($") -> true;
|
||||||
sh_special_char(_C) -> false.
|
sh_special_char(_C) -> false.
|
||||||
|
|
||||||
|
@ -520,8 +520,13 @@ make_invalid_object(Val) ->
|
|||||||
(str:format("Invalid object: ~p", [Val])).
|
(str:format("Invalid object: ~p", [Val])).
|
||||||
|
|
||||||
get_random_pid() ->
|
get_random_pid() ->
|
||||||
|
case ejabberd_riak_sup:start() of
|
||||||
|
ok ->
|
||||||
PoolPid = ejabberd_riak_sup:get_random_pid(),
|
PoolPid = ejabberd_riak_sup:get_random_pid(),
|
||||||
get_riak_pid(PoolPid).
|
get_riak_pid(PoolPid);
|
||||||
|
{error, _} = Err ->
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
get_riak_pid(PoolPid) ->
|
get_riak_pid(PoolPid) ->
|
||||||
case catch gen_server:call(PoolPid, get_pid) of
|
case catch gen_server:call(PoolPid, get_pid) of
|
||||||
|
@ -26,88 +26,64 @@
|
|||||||
-module(ejabberd_riak_sup).
|
-module(ejabberd_riak_sup).
|
||||||
|
|
||||||
-behaviour(supervisor).
|
-behaviour(supervisor).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-export([start_link/0, init/1, get_pids/0,
|
-export([start/0, start_link/0, init/1, get_pids/0,
|
||||||
transform_options/1, get_random_pid/0,
|
get_random_pid/0, config_reloaded/0]).
|
||||||
host_up/1, config_reloaded/0, opt_type/1]).
|
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
-define(DEFAULT_POOL_SIZE, 10).
|
|
||||||
-define(DEFAULT_RIAK_START_INTERVAL, 30). % 30 seconds
|
|
||||||
-define(DEFAULT_RIAK_HOST, "127.0.0.1").
|
|
||||||
-define(DEFAULT_RIAK_PORT, 8087).
|
|
||||||
|
|
||||||
% time to wait for the supervisor to start its child before returning
|
% time to wait for the supervisor to start its child before returning
|
||||||
% a timeout error to the request
|
% a timeout error to the request
|
||||||
-define(CONNECT_TIMEOUT, 500). % milliseconds
|
-define(CONNECT_TIMEOUT, 500). % milliseconds
|
||||||
|
|
||||||
host_up(Host) ->
|
start() ->
|
||||||
case is_riak_configured(Host) of
|
case is_started() of
|
||||||
true ->
|
true -> ok;
|
||||||
ejabberd:start_app(riakc),
|
|
||||||
lists:foreach(
|
|
||||||
fun(Spec) ->
|
|
||||||
supervisor:start_child(?MODULE, Spec)
|
|
||||||
end, get_specs());
|
|
||||||
false ->
|
false ->
|
||||||
ok
|
ejabberd:start_app(riakc),
|
||||||
|
Spec = {?MODULE, {?MODULE, start_link, []},
|
||||||
|
permanent, infinity, supervisor, [?MODULE]},
|
||||||
|
case supervisor:start_child(ejabberd_db_sup, Spec) of
|
||||||
|
{ok, _} -> ok;
|
||||||
|
{error, {already_started, _}} -> ok;
|
||||||
|
{error, Why} = Err ->
|
||||||
|
?ERROR_MSG("Failed to start ~s: ~p",
|
||||||
|
[?MODULE, Why]),
|
||||||
|
Err
|
||||||
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
config_reloaded() ->
|
config_reloaded() ->
|
||||||
case is_riak_configured() of
|
case is_started() of
|
||||||
true ->
|
true ->
|
||||||
ejabberd:start_app(riakc),
|
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(Spec) ->
|
fun(Spec) ->
|
||||||
supervisor:start_child(?MODULE, Spec)
|
supervisor:start_child(?MODULE, Spec)
|
||||||
end, get_specs());
|
end, get_specs()),
|
||||||
false ->
|
PoolSize = get_pool_size(),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun({Id, _, _, _}) ->
|
fun({Id, _, _, _}) when Id > PoolSize ->
|
||||||
supervisor:terminate_child(?MODULE, Id),
|
case supervisor:terminate_child(?MODULE, Id) of
|
||||||
supervisor:delete_child(?MODULE, Id)
|
ok -> supervisor:delete_child(?MODULE, Id);
|
||||||
end, supervisor:which_children(?MODULE))
|
_ -> ok
|
||||||
|
end;
|
||||||
|
(_) ->
|
||||||
|
ok
|
||||||
|
end, supervisor:which_children(?MODULE));
|
||||||
|
false ->
|
||||||
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
is_riak_configured() ->
|
|
||||||
lists:any(fun is_riak_configured/1, ejabberd_config:get_myhosts()).
|
|
||||||
|
|
||||||
is_riak_configured(Host) ->
|
|
||||||
ServerConfigured = ejabberd_config:has_option({riak_server, Host}),
|
|
||||||
PortConfigured = ejabberd_config:has_option({riak_port, Host}),
|
|
||||||
StartIntervalConfigured = ejabberd_config:has_option({riak_start_interval, Host}),
|
|
||||||
PoolConfigured = ejabberd_config:has_option({riak_pool_size, Host}),
|
|
||||||
CacertConfigured = ejabberd_config:has_option({riak_cacertfile, Host}),
|
|
||||||
UserConfigured = ejabberd_config:has_option({riak_username, Host}),
|
|
||||||
PassConfigured = ejabberd_config:has_option({riak_password, Host}),
|
|
||||||
AuthConfigured = lists:member(
|
|
||||||
ejabberd_auth_riak,
|
|
||||||
ejabberd_auth:auth_modules(Host)),
|
|
||||||
SMConfigured = ejabberd_config:get_option({sm_db_type, Host}) == riak,
|
|
||||||
RouterConfigured = ejabberd_config:get_option({router_db_type, Host}) == riak,
|
|
||||||
ServerConfigured or PortConfigured or StartIntervalConfigured
|
|
||||||
or PoolConfigured or CacertConfigured
|
|
||||||
or UserConfigured or PassConfigured
|
|
||||||
or SMConfigured or RouterConfigured
|
|
||||||
or AuthConfigured.
|
|
||||||
|
|
||||||
start_link() ->
|
start_link() ->
|
||||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||||
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20),
|
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20),
|
||||||
ejabberd_hooks:add(host_up, ?MODULE, host_up, 20),
|
{ok, {{one_for_one, 500, 1}, get_specs()}}.
|
||||||
Specs = case is_riak_configured() of
|
|
||||||
true ->
|
is_started() ->
|
||||||
ejabberd:start_app(riakc),
|
whereis(?MODULE) /= undefined.
|
||||||
get_specs();
|
|
||||||
false ->
|
|
||||||
[]
|
|
||||||
end,
|
|
||||||
{ok, {{one_for_one, 500, 1}, Specs}}.
|
|
||||||
|
|
||||||
-spec get_specs() -> [supervisor:child_spec()].
|
-spec get_specs() -> [supervisor:child_spec()].
|
||||||
get_specs() ->
|
get_specs() ->
|
||||||
@ -133,30 +109,30 @@ get_specs() ->
|
|||||||
fun(I) ->
|
fun(I) ->
|
||||||
{ejabberd_riak:get_proc(I),
|
{ejabberd_riak:get_proc(I),
|
||||||
{ejabberd_riak, start_link,
|
{ejabberd_riak, start_link,
|
||||||
[I, Server, Port, StartInterval*1000, Options]},
|
[I, Server, Port, StartInterval, Options]},
|
||||||
transient, 2000, worker, [?MODULE]}
|
transient, 2000, worker, [?MODULE]}
|
||||||
end, lists:seq(1, PoolSize)).
|
end, lists:seq(1, PoolSize)).
|
||||||
|
|
||||||
get_start_interval() ->
|
get_start_interval() ->
|
||||||
ejabberd_config:get_option(riak_start_interval, ?DEFAULT_RIAK_START_INTERVAL).
|
ejabberd_option:riak_start_interval().
|
||||||
|
|
||||||
get_pool_size() ->
|
get_pool_size() ->
|
||||||
ejabberd_config:get_option(riak_pool_size, ?DEFAULT_POOL_SIZE).
|
ejabberd_option:riak_pool_size().
|
||||||
|
|
||||||
get_riak_server() ->
|
get_riak_server() ->
|
||||||
ejabberd_config:get_option(riak_server, ?DEFAULT_RIAK_HOST).
|
ejabberd_option:riak_server().
|
||||||
|
|
||||||
get_riak_cacertfile() ->
|
get_riak_cacertfile() ->
|
||||||
ejabberd_config:get_option(riak_cacertfile, nil).
|
ejabberd_option:riak_cacertfile().
|
||||||
|
|
||||||
get_riak_username() ->
|
get_riak_username() ->
|
||||||
ejabberd_config:get_option(riak_username, nil).
|
ejabberd_option:riak_username().
|
||||||
|
|
||||||
get_riak_password() ->
|
get_riak_password() ->
|
||||||
ejabberd_config:get_option(riak_password, nil).
|
ejabberd_option:riak_password().
|
||||||
|
|
||||||
get_riak_port() ->
|
get_riak_port() ->
|
||||||
ejabberd_config:get_option(riak_port, ?DEFAULT_RIAK_PORT).
|
ejabberd_option:riak_port().
|
||||||
|
|
||||||
get_pids() ->
|
get_pids() ->
|
||||||
[ejabberd_riak:get_proc(I) || I <- lists:seq(1, get_pool_size())].
|
[ejabberd_riak:get_proc(I) || I <- lists:seq(1, get_pool_size())].
|
||||||
@ -164,30 +140,3 @@ get_pids() ->
|
|||||||
get_random_pid() ->
|
get_random_pid() ->
|
||||||
I = p1_rand:round_robin(get_pool_size()) + 1,
|
I = p1_rand:round_robin(get_pool_size()) + 1,
|
||||||
ejabberd_riak:get_proc(I).
|
ejabberd_riak:get_proc(I).
|
||||||
|
|
||||||
transform_options(Opts) ->
|
|
||||||
lists:foldl(fun transform_options/2, [], Opts).
|
|
||||||
|
|
||||||
transform_options({riak_server, {S, P}}, Opts) ->
|
|
||||||
[{riak_server, S}, {riak_port, P}|Opts];
|
|
||||||
transform_options(Opt, Opts) ->
|
|
||||||
[Opt|Opts].
|
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(riak_pool_size) ->
|
|
||||||
fun (N) when is_integer(N), N >= 1 -> N end;
|
|
||||||
opt_type(riak_port) ->
|
|
||||||
fun(P) when is_integer(P), P > 0, P < 65536 -> P end;
|
|
||||||
opt_type(riak_server) ->
|
|
||||||
fun(S) -> binary_to_list(iolist_to_binary(S)) end;
|
|
||||||
opt_type(riak_start_interval) ->
|
|
||||||
fun (N) when is_integer(N), N >= 1 -> N end;
|
|
||||||
opt_type(riak_cacertfile) ->
|
|
||||||
fun(S) -> binary_to_list(iolist_to_binary(S)) end;
|
|
||||||
opt_type(riak_username) ->
|
|
||||||
fun(S) -> binary_to_list(iolist_to_binary(S)) end;
|
|
||||||
opt_type(riak_password) ->
|
|
||||||
fun(S) -> binary_to_list(iolist_to_binary(S)) end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[riak_pool_size, riak_port, riak_server,
|
|
||||||
riak_start_interval, riak_cacertfile, riak_username, riak_password].
|
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
|
|
||||||
-module(ejabberd_router).
|
-module(ejabberd_router).
|
||||||
|
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-ifndef(GEN_SERVER).
|
-ifndef(GEN_SERVER).
|
||||||
@ -59,7 +57,7 @@
|
|||||||
-export([start_link/0]).
|
-export([start_link/0]).
|
||||||
|
|
||||||
-export([init/1, handle_call/3, handle_cast/2,
|
-export([init/1, handle_call/3, handle_cast/2,
|
||||||
handle_info/2, terminate/2, code_change/3, opt_type/1]).
|
handle_info/2, terminate/2, code_change/3]).
|
||||||
|
|
||||||
%% Deprecated functions
|
%% Deprecated functions
|
||||||
-export([route/3, route_error/4]).
|
-export([route/3, route_error/4]).
|
||||||
@ -388,10 +386,9 @@ do_route(_Pkt, _Route) ->
|
|||||||
|
|
||||||
-spec balancing_route(jid(), jid(), stanza(), [#route{}]) -> any().
|
-spec balancing_route(jid(), jid(), stanza(), [#route{}]) -> any().
|
||||||
balancing_route(From, To, Packet, Rs) ->
|
balancing_route(From, To, Packet, Rs) ->
|
||||||
LDstDomain = To#jid.lserver,
|
case get_domain_balancing(From, To, To#jid.lserver) of
|
||||||
Value = get_domain_balancing(From, To, LDstDomain),
|
|
||||||
case get_component_number(LDstDomain) of
|
|
||||||
undefined ->
|
undefined ->
|
||||||
|
Value = erlang:system_time(),
|
||||||
case [R || R <- Rs, node(R#route.pid) == node()] of
|
case [R || R <- Rs, node(R#route.pid) == node()] of
|
||||||
[] ->
|
[] ->
|
||||||
R = lists:nth(erlang:phash(Value, length(Rs)), Rs),
|
R = lists:nth(erlang:phash(Value, length(Rs)), Rs),
|
||||||
@ -400,7 +397,7 @@ balancing_route(From, To, Packet, Rs) ->
|
|||||||
R = lists:nth(erlang:phash(Value, length(LRs)), LRs),
|
R = lists:nth(erlang:phash(Value, length(LRs)), LRs),
|
||||||
do_route(Packet, R)
|
do_route(Packet, R)
|
||||||
end;
|
end;
|
||||||
_ ->
|
Value ->
|
||||||
SRs = lists:ukeysort(#route.local_hint, Rs),
|
SRs = lists:ukeysort(#route.local_hint, Rs),
|
||||||
R = lists:nth(erlang:phash(Value, length(SRs)), SRs),
|
R = lists:nth(erlang:phash(Value, length(SRs)), SRs),
|
||||||
do_route(Packet, R)
|
do_route(Packet, R)
|
||||||
@ -408,24 +405,30 @@ balancing_route(From, To, Packet, Rs) ->
|
|||||||
|
|
||||||
-spec get_component_number(binary()) -> pos_integer() | undefined.
|
-spec get_component_number(binary()) -> pos_integer() | undefined.
|
||||||
get_component_number(LDomain) ->
|
get_component_number(LDomain) ->
|
||||||
ejabberd_config:get_option({domain_balancing_component_number, LDomain}).
|
M = ejabberd_option:domain_balancing(),
|
||||||
|
case maps:get(LDomain, M, undefined) of
|
||||||
|
undefined -> undefined;
|
||||||
|
Opts -> maps:get(component_number, Opts)
|
||||||
|
end.
|
||||||
|
|
||||||
-spec get_domain_balancing(jid(), jid(), binary()) -> any().
|
-spec get_domain_balancing(jid(), jid(), binary()) -> integer() | ljid() | undefined.
|
||||||
get_domain_balancing(From, To, LDomain) ->
|
get_domain_balancing(From, To, LDomain) ->
|
||||||
case ejabberd_config:get_option({domain_balancing, LDomain}) of
|
M = ejabberd_option:domain_balancing(),
|
||||||
undefined -> erlang:system_time();
|
case maps:get(LDomain, M, undefined) of
|
||||||
|
undefined -> undefined;
|
||||||
|
Opts ->
|
||||||
|
case maps:get(type, Opts, random) of
|
||||||
random -> erlang:system_time();
|
random -> erlang:system_time();
|
||||||
source -> jid:tolower(From);
|
source -> jid:tolower(From);
|
||||||
destination -> jid:tolower(To);
|
destination -> jid:tolower(To);
|
||||||
bare_source -> jid:remove_resource(jid:tolower(From));
|
bare_source -> jid:remove_resource(jid:tolower(From));
|
||||||
bare_destination -> jid:remove_resource(jid:tolower(To))
|
bare_destination -> jid:remove_resource(jid:tolower(To))
|
||||||
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get_backend() -> module().
|
-spec get_backend() -> module().
|
||||||
get_backend() ->
|
get_backend() ->
|
||||||
DBType = ejabberd_config:get_option(
|
DBType = ejabberd_option:router_db_type(),
|
||||||
router_db_type,
|
|
||||||
ejabberd_config:default_ram_db(?MODULE)),
|
|
||||||
list_to_atom("ejabberd_router_" ++ atom_to_list(DBType)).
|
list_to_atom("ejabberd_router_" ++ atom_to_list(DBType)).
|
||||||
|
|
||||||
-spec cache_nodes(module()) -> [node()].
|
-spec cache_nodes(module()) -> [node()].
|
||||||
@ -439,10 +442,7 @@ cache_nodes(Mod) ->
|
|||||||
use_cache(Mod) ->
|
use_cache(Mod) ->
|
||||||
case erlang:function_exported(Mod, use_cache, 0) of
|
case erlang:function_exported(Mod, use_cache, 0) of
|
||||||
true -> Mod:use_cache();
|
true -> Mod:use_cache();
|
||||||
false ->
|
false -> ejabberd_option:router_use_cache()
|
||||||
ejabberd_config:get_option(
|
|
||||||
router_use_cache,
|
|
||||||
ejabberd_config:use_cache(global))
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec delete_cache(module(), binary()) -> ok.
|
-spec delete_cache(module(), binary()) -> ok.
|
||||||
@ -466,15 +466,9 @@ init_cache(Mod) ->
|
|||||||
|
|
||||||
-spec cache_opts() -> [proplists:property()].
|
-spec cache_opts() -> [proplists:property()].
|
||||||
cache_opts() ->
|
cache_opts() ->
|
||||||
MaxSize = ejabberd_config:get_option(
|
MaxSize = ejabberd_option:router_cache_size(),
|
||||||
router_cache_size,
|
CacheMissed = ejabberd_option:router_cache_missed(),
|
||||||
ejabberd_config:cache_size(global)),
|
LifeTime = case ejabberd_option:router_cache_life_time() of
|
||||||
CacheMissed = ejabberd_config:get_option(
|
|
||||||
router_cache_missed,
|
|
||||||
ejabberd_config:cache_missed(global)),
|
|
||||||
LifeTime = case ejabberd_config:get_option(
|
|
||||||
router_cache_life_time,
|
|
||||||
ejabberd_config:cache_life_time(global)) of
|
|
||||||
infinity -> infinity;
|
infinity -> infinity;
|
||||||
I -> timer:seconds(I)
|
I -> timer:seconds(I)
|
||||||
end,
|
end,
|
||||||
@ -498,26 +492,3 @@ clean_cache(Node) ->
|
|||||||
-spec clean_cache() -> ok.
|
-spec clean_cache() -> ok.
|
||||||
clean_cache() ->
|
clean_cache() ->
|
||||||
ejabberd_cluster:eval_everywhere(?MODULE, clean_cache, [node()]).
|
ejabberd_cluster:eval_everywhere(?MODULE, clean_cache, [node()]).
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(domain_balancing) ->
|
|
||||||
fun (random) -> random;
|
|
||||||
(source) -> source;
|
|
||||||
(destination) -> destination;
|
|
||||||
(bare_source) -> bare_source;
|
|
||||||
(bare_destination) -> bare_destination
|
|
||||||
end;
|
|
||||||
opt_type(domain_balancing_component_number) ->
|
|
||||||
fun (N) when is_integer(N), N > 1 -> N end;
|
|
||||||
opt_type(router_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
|
||||||
opt_type(O) when O == router_use_cache; O == router_cache_missed ->
|
|
||||||
fun(B) when is_boolean(B) -> B end;
|
|
||||||
opt_type(O) when O == router_cache_size; O == router_cache_life_time ->
|
|
||||||
fun(I) when is_integer(I), I>0 -> I;
|
|
||||||
(unlimited) -> infinity;
|
|
||||||
(infinity) -> infinity
|
|
||||||
end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[domain_balancing, domain_balancing_component_number,
|
|
||||||
router_db_type, router_use_cache, router_cache_size,
|
|
||||||
router_cache_missed, router_cache_life_time].
|
|
||||||
|
@ -100,8 +100,12 @@ register_route(Domain, ServerHost, _LocalHint, N, Pid) ->
|
|||||||
|
|
||||||
unregister_route(Domain, undefined, Pid) ->
|
unregister_route(Domain, undefined, Pid) ->
|
||||||
F = fun () ->
|
F = fun () ->
|
||||||
case mnesia:match_object(
|
case mnesia:select(
|
||||||
#route{domain = Domain, pid = Pid, _ = '_'}) of
|
route,
|
||||||
|
ets:fun2ms(
|
||||||
|
fun(#route{domain = D, pid = P} = R)
|
||||||
|
when D == Domain, P == Pid -> R
|
||||||
|
end)) of
|
||||||
[R] -> mnesia:delete_object(R);
|
[R] -> mnesia:delete_object(R);
|
||||||
_ -> ok
|
_ -> ok
|
||||||
end
|
end
|
||||||
@ -109,8 +113,12 @@ unregister_route(Domain, undefined, Pid) ->
|
|||||||
transaction(F);
|
transaction(F);
|
||||||
unregister_route(Domain, _, Pid) ->
|
unregister_route(Domain, _, Pid) ->
|
||||||
F = fun () ->
|
F = fun () ->
|
||||||
case mnesia:match_object(
|
case mnesia:select(
|
||||||
#route{domain = Domain, pid = Pid, _ = '_'}) of
|
route,
|
||||||
|
ets:fun2ms(
|
||||||
|
fun(#route{domain = D, pid = P} = R)
|
||||||
|
when D == Domain, P == Pid -> R
|
||||||
|
end)) of
|
||||||
[R] ->
|
[R] ->
|
||||||
I = R#route.local_hint,
|
I = R#route.local_hint,
|
||||||
ServerHost = R#route.server_host,
|
ServerHost = R#route.server_host,
|
||||||
@ -147,8 +155,10 @@ init([]) ->
|
|||||||
mnesia:subscribe({table, route, simple}),
|
mnesia:subscribe({table, route, simple}),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun (Pid) -> erlang:monitor(process, Pid) end,
|
fun (Pid) -> erlang:monitor(process, Pid) end,
|
||||||
mnesia:dirty_select(route,
|
mnesia:dirty_select(
|
||||||
[{#route{pid = '$1', _ = '_'}, [], ['$1']}])),
|
route,
|
||||||
|
ets:fun2ms(
|
||||||
|
fun(#route{pid = Pid}) -> Pid end))),
|
||||||
{ok, #state{}}.
|
{ok, #state{}}.
|
||||||
|
|
||||||
handle_call(_Request, _From, State) ->
|
handle_call(_Request, _From, State) ->
|
||||||
@ -166,8 +176,12 @@ handle_info({mnesia_table_event, _}, State) ->
|
|||||||
{noreply, State};
|
{noreply, State};
|
||||||
handle_info({'DOWN', _Ref, _Type, Pid, _Info}, State) ->
|
handle_info({'DOWN', _Ref, _Type, Pid, _Info}, State) ->
|
||||||
F = fun () ->
|
F = fun () ->
|
||||||
Es = mnesia:select(route,
|
Es = mnesia:select(
|
||||||
[{#route{pid = Pid, _ = '_'}, [], ['$_']}]),
|
route,
|
||||||
|
ets:fun2ms(
|
||||||
|
fun(#route{pid = P} = E)
|
||||||
|
when P == Pid -> E
|
||||||
|
end)),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(E) ->
|
fun(E) ->
|
||||||
if is_integer(E#route.local_hint) ->
|
if is_integer(E#route.local_hint) ->
|
||||||
|
@ -67,7 +67,7 @@ get_all_routes() ->
|
|||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
route_schema() ->
|
route_schema() ->
|
||||||
{record_info(fields, route), #route{}}.
|
{record_info(fields, route), #route{domain = <<>>, server_host = <<>>}}.
|
||||||
|
|
||||||
clean_table() ->
|
clean_table() ->
|
||||||
?DEBUG("Cleaning Riak 'route' table...", []),
|
?DEBUG("Cleaning Riak 'route' table...", []),
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
-module(ejabberd_router_sql).
|
-module(ejabberd_router_sql).
|
||||||
-behaviour(ejabberd_router).
|
-behaviour(ejabberd_router).
|
||||||
|
|
||||||
-compile([{parse_transform, ejabberd_sql_pt}]).
|
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([init/0, register_route/5, unregister_route/3, find_routes/1,
|
-export([init/0, register_route/5, unregister_route/3, find_routes/1,
|
||||||
|
@ -27,8 +27,6 @@
|
|||||||
|
|
||||||
-protocol({xep, 220, '1.1'}).
|
-protocol({xep, 220, '1.1'}).
|
||||||
|
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
@ -44,15 +42,14 @@
|
|||||||
list_temporarily_blocked_hosts/0,
|
list_temporarily_blocked_hosts/0,
|
||||||
external_host_overloaded/1, is_temporarly_blocked/1,
|
external_host_overloaded/1, is_temporarly_blocked/1,
|
||||||
get_commands_spec/0, zlib_enabled/1, get_idle_timeout/1,
|
get_commands_spec/0, zlib_enabled/1, get_idle_timeout/1,
|
||||||
tls_required/1, tls_verify/1, tls_enabled/1, tls_options/2,
|
tls_required/1, tls_enabled/1, tls_options/2,
|
||||||
host_up/1, host_down/1, queue_type/1]).
|
host_up/1, host_down/1, queue_type/1]).
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([init/1, handle_call/3, handle_cast/2,
|
-export([init/1, handle_call/3, handle_cast/2,
|
||||||
handle_info/2, terminate/2, code_change/3]).
|
handle_info/2, terminate/2, code_change/3]).
|
||||||
|
|
||||||
-export([get_info_s2s_connections/1,
|
-export([get_info_s2s_connections/1]).
|
||||||
transform_options/1, opt_type/1]).
|
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("xmpp.hrl").
|
-include("xmpp.hrl").
|
||||||
@ -131,19 +128,21 @@ is_temporarly_blocked(Host) ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec remove_connection({binary(), binary()},
|
-spec remove_connection({binary(), binary()}, pid()) -> ok.
|
||||||
pid()) -> {atomic, ok} | ok | {aborted, any()}.
|
|
||||||
|
|
||||||
remove_connection(FromTo, Pid) ->
|
remove_connection(FromTo, Pid) ->
|
||||||
case catch mnesia:dirty_match_object(s2s,
|
case mnesia:dirty_match_object(s2s, #s2s{fromto = FromTo, pid = Pid}) of
|
||||||
#s2s{fromto = FromTo, pid = Pid})
|
|
||||||
of
|
|
||||||
[#s2s{pid = Pid}] ->
|
[#s2s{pid = Pid}] ->
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:delete_object(#s2s{fromto = FromTo, pid = Pid})
|
mnesia:delete_object(#s2s{fromto = FromTo, pid = Pid})
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F);
|
case mnesia:transaction(F) of
|
||||||
_ -> ok
|
{atomic, _} -> ok;
|
||||||
|
{aborted, Reason} ->
|
||||||
|
?ERROR_MSG("Failed to unregister s2s connection: "
|
||||||
|
"Mnesia failure: ~p", [Reason])
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec have_connection({binary(), binary()}) -> boolean().
|
-spec have_connection({binary(), binary()}) -> boolean().
|
||||||
@ -195,36 +194,32 @@ dirty_get_connections() ->
|
|||||||
|
|
||||||
-spec tls_options(binary(), [proplists:property()]) -> [proplists:property()].
|
-spec tls_options(binary(), [proplists:property()]) -> [proplists:property()].
|
||||||
tls_options(LServer, DefaultOpts) ->
|
tls_options(LServer, DefaultOpts) ->
|
||||||
TLSOpts1 = case get_certfile(LServer) of
|
TLSOpts1 = case ejabberd_pkix:get_certfile(LServer) of
|
||||||
undefined -> DefaultOpts;
|
error -> DefaultOpts;
|
||||||
CertFile ->
|
{ok, CertFile} ->
|
||||||
lists:keystore(certfile, 1, DefaultOpts,
|
lists:keystore(certfile, 1, DefaultOpts,
|
||||||
{certfile, CertFile})
|
{certfile, CertFile})
|
||||||
end,
|
end,
|
||||||
TLSOpts2 = case ejabberd_config:get_option(
|
TLSOpts2 = case ejabberd_option:s2s_ciphers(LServer) of
|
||||||
{s2s_ciphers, LServer}) of
|
|
||||||
undefined -> TLSOpts1;
|
undefined -> TLSOpts1;
|
||||||
Ciphers -> lists:keystore(ciphers, 1, TLSOpts1,
|
Ciphers -> lists:keystore(ciphers, 1, TLSOpts1,
|
||||||
{ciphers, Ciphers})
|
{ciphers, Ciphers})
|
||||||
end,
|
end,
|
||||||
TLSOpts3 = case ejabberd_config:get_option(
|
TLSOpts3 = case ejabberd_option:s2s_protocol_options(LServer) of
|
||||||
{s2s_protocol_options, LServer}) of
|
|
||||||
undefined -> TLSOpts2;
|
undefined -> TLSOpts2;
|
||||||
ProtoOpts -> lists:keystore(protocol_options, 1, TLSOpts2,
|
ProtoOpts -> lists:keystore(protocol_options, 1, TLSOpts2,
|
||||||
{protocol_options, ProtoOpts})
|
{protocol_options, ProtoOpts})
|
||||||
end,
|
end,
|
||||||
TLSOpts4 = case ejabberd_config:get_option(
|
TLSOpts4 = case ejabberd_option:s2s_dhfile(LServer) of
|
||||||
{s2s_dhfile, LServer}) of
|
|
||||||
undefined -> TLSOpts3;
|
undefined -> TLSOpts3;
|
||||||
DHFile -> lists:keystore(dhfile, 1, TLSOpts3,
|
DHFile -> lists:keystore(dhfile, 1, TLSOpts3,
|
||||||
{dhfile, DHFile})
|
{dhfile, DHFile})
|
||||||
end,
|
end,
|
||||||
TLSOpts5 = case get_cafile(LServer) of
|
TLSOpts5 = case lists:keymember(cafile, 1, TLSOpts4) of
|
||||||
undefined -> TLSOpts4;
|
true -> TLSOpts4;
|
||||||
CAFile -> lists:keystore(cafile, 1, TLSOpts4,
|
false -> [{cafile, get_cafile(LServer)}|TLSOpts4]
|
||||||
{cafile, CAFile})
|
|
||||||
end,
|
end,
|
||||||
case ejabberd_config:get_option({s2s_tls_compression, LServer}) of
|
case ejabberd_option:s2s_tls_compression(LServer) of
|
||||||
undefined -> TLSOpts5;
|
undefined -> TLSOpts5;
|
||||||
false -> [compression_none | TLSOpts5];
|
false -> [compression_none | TLSOpts5];
|
||||||
true -> lists:delete(compression_none, TLSOpts5)
|
true -> lists:delete(compression_none, TLSOpts5)
|
||||||
@ -233,12 +228,7 @@ tls_options(LServer, DefaultOpts) ->
|
|||||||
-spec tls_required(binary()) -> boolean().
|
-spec tls_required(binary()) -> boolean().
|
||||||
tls_required(LServer) ->
|
tls_required(LServer) ->
|
||||||
TLS = use_starttls(LServer),
|
TLS = use_starttls(LServer),
|
||||||
TLS == required orelse TLS == required_trusted.
|
TLS == required.
|
||||||
|
|
||||||
-spec tls_verify(binary()) -> boolean().
|
|
||||||
tls_verify(LServer) ->
|
|
||||||
TLS = use_starttls(LServer),
|
|
||||||
TLS == required_trusted.
|
|
||||||
|
|
||||||
-spec tls_enabled(binary()) -> boolean().
|
-spec tls_enabled(binary()) -> boolean().
|
||||||
tls_enabled(LServer) ->
|
tls_enabled(LServer) ->
|
||||||
@ -247,38 +237,25 @@ tls_enabled(LServer) ->
|
|||||||
|
|
||||||
-spec zlib_enabled(binary()) -> boolean().
|
-spec zlib_enabled(binary()) -> boolean().
|
||||||
zlib_enabled(LServer) ->
|
zlib_enabled(LServer) ->
|
||||||
ejabberd_config:get_option({s2s_zlib, LServer}, false).
|
ejabberd_option:s2s_zlib(LServer).
|
||||||
|
|
||||||
-spec use_starttls(binary()) -> boolean() | optional | required | required_trusted.
|
-spec use_starttls(binary()) -> boolean() | optional | required.
|
||||||
use_starttls(LServer) ->
|
use_starttls(LServer) ->
|
||||||
ejabberd_config:get_option({s2s_use_starttls, LServer}, false).
|
ejabberd_option:s2s_use_starttls(LServer).
|
||||||
|
|
||||||
-spec get_idle_timeout(binary()) -> non_neg_integer() | infinity.
|
-spec get_idle_timeout(binary()) -> non_neg_integer() | infinity.
|
||||||
get_idle_timeout(LServer) ->
|
get_idle_timeout(LServer) ->
|
||||||
ejabberd_config:get_option({s2s_timeout, LServer}, timer:minutes(10)).
|
ejabberd_option:s2s_timeout(LServer).
|
||||||
|
|
||||||
-spec queue_type(binary()) -> ram | file.
|
-spec queue_type(binary()) -> ram | file.
|
||||||
queue_type(LServer) ->
|
queue_type(LServer) ->
|
||||||
ejabberd_config:get_option(
|
ejabberd_option:s2s_queue_type(LServer).
|
||||||
{s2s_queue_type, LServer},
|
|
||||||
ejabberd_config:default_queue_type(LServer)).
|
|
||||||
|
|
||||||
-spec get_certfile(binary()) -> file:filename_all() | undefined.
|
|
||||||
get_certfile(LServer) ->
|
|
||||||
case ejabberd_pkix:get_certfile(LServer) of
|
|
||||||
{ok, CertFile} ->
|
|
||||||
CertFile;
|
|
||||||
error ->
|
|
||||||
ejabberd_config:get_option(
|
|
||||||
{domain_certfile, LServer},
|
|
||||||
ejabberd_config:get_option({s2s_certfile, LServer}))
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec get_cafile(binary()) -> file:filename_all() | undefined.
|
-spec get_cafile(binary()) -> file:filename_all() | undefined.
|
||||||
get_cafile(LServer) ->
|
get_cafile(LServer) ->
|
||||||
case ejabberd_config:get_option({s2s_cafile, LServer}) of
|
case ejabberd_option:s2s_cafile(LServer) of
|
||||||
undefined ->
|
undefined ->
|
||||||
ejabberd_pkix:ca_file();
|
ejabberd_option:ca_file();
|
||||||
File ->
|
File ->
|
||||||
File
|
File
|
||||||
end.
|
end.
|
||||||
@ -286,22 +263,26 @@ get_cafile(LServer) ->
|
|||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
update_tables(),
|
update_tables(),
|
||||||
ejabberd_mnesia:create(?MODULE, s2s,
|
ejabberd_mnesia:create(?MODULE, s2s,
|
||||||
[{ram_copies, [node()]},
|
[{ram_copies, [node()]},
|
||||||
{type, bag},
|
{type, bag},
|
||||||
{attributes, record_info(fields, s2s)}]),
|
{attributes, record_info(fields, s2s)}]),
|
||||||
mnesia:subscribe(system),
|
case mnesia:subscribe(system) of
|
||||||
|
{ok, _} ->
|
||||||
ejabberd_commands:register_commands(get_commands_spec()),
|
ejabberd_commands:register_commands(get_commands_spec()),
|
||||||
ejabberd_mnesia:create(?MODULE, temporarily_blocked,
|
ejabberd_mnesia:create(
|
||||||
|
?MODULE, temporarily_blocked,
|
||||||
[{ram_copies, [node()]},
|
[{ram_copies, [node()]},
|
||||||
{attributes, record_info(fields, temporarily_blocked)}]),
|
{attributes, record_info(fields, temporarily_blocked)}]),
|
||||||
ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
|
ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
|
||||||
ejabberd_hooks:add(host_down, ?MODULE, host_down, 60),
|
ejabberd_hooks:add(host_down, ?MODULE, host_down, 60),
|
||||||
lists:foreach(fun host_up/1, ejabberd_config:get_myhosts()),
|
lists:foreach(fun host_up/1, ejabberd_option:hosts()),
|
||||||
{ok, #state{}}.
|
{ok, #state{}};
|
||||||
|
{error, Reason} ->
|
||||||
|
{stop, Reason}
|
||||||
|
end.
|
||||||
|
|
||||||
handle_call(_Request, _From, State) ->
|
handle_call(_Request, _From, State) ->
|
||||||
{reply, ok, State}.
|
{reply, ok, State}.
|
||||||
@ -319,7 +300,7 @@ handle_info(_Info, State) -> {noreply, State}.
|
|||||||
|
|
||||||
terminate(_Reason, _State) ->
|
terminate(_Reason, _State) ->
|
||||||
ejabberd_commands:unregister_commands(get_commands_spec()),
|
ejabberd_commands:unregister_commands(get_commands_spec()),
|
||||||
lists:foreach(fun host_down/1, ejabberd_config:get_myhosts()),
|
lists:foreach(fun host_down/1, ejabberd_option:hosts()),
|
||||||
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50),
|
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50),
|
||||||
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 60),
|
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 60),
|
||||||
ok.
|
ok.
|
||||||
@ -508,16 +489,16 @@ new_connection(MyServer, Server, From, FromTo,
|
|||||||
[]
|
[]
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec max_s2s_connections_number({binary(), binary()}) -> integer().
|
-spec max_s2s_connections_number({binary(), binary()}) -> pos_integer().
|
||||||
max_s2s_connections_number({From, To}) ->
|
max_s2s_connections_number({From, To}) ->
|
||||||
case acl:match_rule(From, max_s2s_connections, jid:make(To)) of
|
case ejabberd_shaper:match(From, max_s2s_connections, jid:make(To)) of
|
||||||
Max when is_integer(Max) -> Max;
|
Max when is_integer(Max) -> Max;
|
||||||
_ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER
|
_ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec max_s2s_connections_number_per_node({binary(), binary()}) -> integer().
|
-spec max_s2s_connections_number_per_node({binary(), binary()}) -> pos_integer().
|
||||||
max_s2s_connections_number_per_node({From, To}) ->
|
max_s2s_connections_number_per_node({From, To}) ->
|
||||||
case acl:match_rule(From, max_s2s_connections_per_node, jid:make(To)) of
|
case ejabberd_shaper:match(From, max_s2s_connections_per_node, jid:make(To)) of
|
||||||
Max when is_integer(Max) -> Max;
|
Max when is_integer(Max) -> Max;
|
||||||
_ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER_PER_NODE
|
_ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER_PER_NODE
|
||||||
end.
|
end.
|
||||||
@ -537,11 +518,11 @@ needed_connections_number(Ls, MaxS2SConnectionsNumber,
|
|||||||
-spec is_service(jid(), jid()) -> boolean().
|
-spec is_service(jid(), jid()) -> boolean().
|
||||||
is_service(From, To) ->
|
is_service(From, To) ->
|
||||||
LFromDomain = From#jid.lserver,
|
LFromDomain = From#jid.lserver,
|
||||||
case ejabberd_config:get_option({route_subdomains, LFromDomain}, local) of
|
case ejabberd_option:route_subdomains(LFromDomain) of
|
||||||
s2s -> % bypass RFC 3920 10.3
|
s2s -> % bypass RFC 3920 10.3
|
||||||
false;
|
false;
|
||||||
local ->
|
local ->
|
||||||
Hosts = ejabberd_config:get_myhosts(),
|
Hosts = ejabberd_option:hosts(),
|
||||||
P = fun (ParentDomain) ->
|
P = fun (ParentDomain) ->
|
||||||
lists:member(ParentDomain, Hosts)
|
lists:member(ParentDomain, Hosts)
|
||||||
end,
|
end,
|
||||||
@ -602,32 +583,15 @@ stop_s2s_connections() ->
|
|||||||
fun({_Id, Pid, _Type, _Module}) ->
|
fun({_Id, Pid, _Type, _Module}) ->
|
||||||
supervisor:terminate_child(ejabberd_s2s_out_sup, Pid)
|
supervisor:terminate_child(ejabberd_s2s_out_sup, Pid)
|
||||||
end, supervisor:which_children(ejabberd_s2s_out_sup)),
|
end, supervisor:which_children(ejabberd_s2s_out_sup)),
|
||||||
mnesia:clear_table(s2s),
|
_ = mnesia:clear_table(s2s),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% Update Mnesia tables
|
%%% Update Mnesia tables
|
||||||
|
|
||||||
update_tables() ->
|
update_tables() ->
|
||||||
case catch mnesia:table_info(s2s, type) of
|
_ = mnesia:delete_table(local_s2s),
|
||||||
bag -> ok;
|
ok.
|
||||||
{'EXIT', _} -> ok;
|
|
||||||
_ -> mnesia:delete_table(s2s)
|
|
||||||
end,
|
|
||||||
case catch mnesia:table_info(s2s, attributes) of
|
|
||||||
[fromto, node, key] ->
|
|
||||||
mnesia:transform_table(s2s, ignore, [fromto, pid]),
|
|
||||||
mnesia:clear_table(s2s);
|
|
||||||
[fromto, pid, key] ->
|
|
||||||
mnesia:transform_table(s2s, ignore, [fromto, pid]),
|
|
||||||
mnesia:clear_table(s2s);
|
|
||||||
[fromto, pid] -> ok;
|
|
||||||
{'EXIT', _} -> ok
|
|
||||||
end,
|
|
||||||
case lists:member(local_s2s, mnesia:system_info(tables)) of
|
|
||||||
true -> mnesia:delete_table(local_s2s);
|
|
||||||
false -> ok
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% Check if host is in blacklist or white list
|
%% Check if host is in blacklist or white list
|
||||||
allow_host(MyServer, S2SHost) ->
|
allow_host(MyServer, S2SHost) ->
|
||||||
@ -635,7 +599,7 @@ allow_host(MyServer, S2SHost) ->
|
|||||||
not is_temporarly_blocked(S2SHost).
|
not is_temporarly_blocked(S2SHost).
|
||||||
|
|
||||||
allow_host1(MyHost, S2SHost) ->
|
allow_host1(MyHost, S2SHost) ->
|
||||||
Rule = ejabberd_config:get_option({s2s_access, MyHost}, all),
|
Rule = ejabberd_option:s2s_access(MyHost),
|
||||||
JID = jid:make(S2SHost),
|
JID = jid:make(S2SHost),
|
||||||
case acl:match_rule(MyHost, Rule, JID) of
|
case acl:match_rule(MyHost, Rule, JID) of
|
||||||
deny -> false;
|
deny -> false;
|
||||||
@ -648,30 +612,6 @@ allow_host1(MyHost, S2SHost) ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
transform_options(Opts) ->
|
|
||||||
lists:foldl(fun transform_options/2, [], Opts).
|
|
||||||
|
|
||||||
transform_options({{s2s_host, Host}, Action}, Opts) ->
|
|
||||||
?WARNING_MSG("Option 's2s_host' is deprecated. "
|
|
||||||
"The option is still supported but it is better to "
|
|
||||||
"fix your config: use access rules instead.", []),
|
|
||||||
ACLName = misc:binary_to_atom(
|
|
||||||
iolist_to_binary(["s2s_access_", Host])),
|
|
||||||
[{acl, ACLName, {server, Host}},
|
|
||||||
{access, s2s, [{Action, ACLName}]},
|
|
||||||
{s2s_access, s2s} |
|
|
||||||
Opts];
|
|
||||||
transform_options({s2s_default_policy, Action}, Opts) ->
|
|
||||||
?WARNING_MSG("Option 's2s_default_policy' is deprecated. "
|
|
||||||
"The option is still supported but it is better to "
|
|
||||||
"fix your config: "
|
|
||||||
"use 's2s_access' with an access rule.", []),
|
|
||||||
[{access, s2s, [{Action, all}]},
|
|
||||||
{s2s_access, s2s} |
|
|
||||||
Opts];
|
|
||||||
transform_options(Opt, Opts) ->
|
|
||||||
[Opt|Opts].
|
|
||||||
|
|
||||||
%% Get information about S2S connections of the specified type.
|
%% Get information about S2S connections of the specified type.
|
||||||
%% @spec (Type) -> [Info]
|
%% @spec (Type) -> [Info]
|
||||||
%% where Type = in | out
|
%% where Type = in | out
|
||||||
@ -704,51 +644,3 @@ get_s2s_state(S2sPid) ->
|
|||||||
{badrpc, _} -> [{status, error}]
|
{badrpc, _} -> [{status, error}]
|
||||||
end,
|
end,
|
||||||
[{s2s_pid, S2sPid} | Infos].
|
[{s2s_pid, S2sPid} | Infos].
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(route_subdomains) ->
|
|
||||||
fun (s2s) -> s2s;
|
|
||||||
(local) -> local
|
|
||||||
end;
|
|
||||||
opt_type(s2s_access) ->
|
|
||||||
fun acl:access_rules_validator/1;
|
|
||||||
opt_type(s2s_ciphers) -> fun iolist_to_binary/1;
|
|
||||||
opt_type(s2s_dhfile) -> fun misc:try_read_file/1;
|
|
||||||
opt_type(s2s_cafile) -> fun misc:try_read_file/1;
|
|
||||||
opt_type(s2s_protocol_options) ->
|
|
||||||
fun (Options) -> str:join(Options, <<"|">>) end;
|
|
||||||
opt_type(s2s_tls_compression) ->
|
|
||||||
fun (true) -> true;
|
|
||||||
(false) -> false
|
|
||||||
end;
|
|
||||||
opt_type(s2s_use_starttls) ->
|
|
||||||
fun (true) -> true;
|
|
||||||
(false) -> false;
|
|
||||||
(optional) -> optional;
|
|
||||||
(required) -> required;
|
|
||||||
(required_trusted) ->
|
|
||||||
?WARNING_MSG("The value 'required_trusted' of option "
|
|
||||||
"'s2s_use_starttls' is deprected and will be "
|
|
||||||
"unsupported in future releases. Instead, "
|
|
||||||
"set it to 'required' and make sure "
|
|
||||||
"mod_s2s_dialback is *NOT* loaded", []),
|
|
||||||
required_trusted
|
|
||||||
end;
|
|
||||||
opt_type(s2s_zlib) ->
|
|
||||||
fun(true) ->
|
|
||||||
ejabberd:start_app(ezlib),
|
|
||||||
true;
|
|
||||||
(false) ->
|
|
||||||
false
|
|
||||||
end;
|
|
||||||
opt_type(s2s_timeout) ->
|
|
||||||
fun(I) when is_integer(I), I >= 0 -> timer:seconds(I);
|
|
||||||
(infinity) -> infinity;
|
|
||||||
(unlimited) -> infinity
|
|
||||||
end;
|
|
||||||
opt_type(s2s_queue_type) ->
|
|
||||||
fun(ram) -> ram; (file) -> file end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[route_subdomains, s2s_access, s2s_zlib,
|
|
||||||
s2s_ciphers, s2s_dhfile, s2s_cafile, s2s_protocol_options,
|
|
||||||
s2s_tls_compression, s2s_use_starttls, s2s_timeout, s2s_queue_type].
|
|
||||||
|
@ -22,9 +22,11 @@
|
|||||||
-module(ejabberd_s2s_in).
|
-module(ejabberd_s2s_in).
|
||||||
-behaviour(xmpp_stream_in).
|
-behaviour(xmpp_stream_in).
|
||||||
-behaviour(ejabberd_listener).
|
-behaviour(ejabberd_listener).
|
||||||
|
-dialyzer([{no_fail_call, [stop/1, process_closed/2]},
|
||||||
|
{no_return, process_closed/2}]).
|
||||||
|
|
||||||
%% ejabberd_listener callbacks
|
%% ejabberd_listener callbacks
|
||||||
-export([start/3, start_link/3, accept/1, listen_opt_type/1, listen_options/0]).
|
-export([start/3, start_link/3, accept/1, listen_options/0]).
|
||||||
%% xmpp_stream_in callbacks
|
%% xmpp_stream_in callbacks
|
||||||
-export([init/1, handle_call/3, handle_cast/2,
|
-export([init/1, handle_call/3, handle_cast/2,
|
||||||
handle_info/2, terminate/2, code_change/3]).
|
handle_info/2, terminate/2, code_change/3]).
|
||||||
@ -252,11 +254,11 @@ init([State, Opts]) ->
|
|||||||
false -> [compression_none | TLSOpts1];
|
false -> [compression_none | TLSOpts1];
|
||||||
true -> TLSOpts1
|
true -> TLSOpts1
|
||||||
end,
|
end,
|
||||||
Timeout = ejabberd_config:negotiation_timeout(),
|
Timeout = ejabberd_option:negotiation_timeout(),
|
||||||
State1 = State#{tls_options => TLSOpts2,
|
State1 = State#{tls_options => TLSOpts2,
|
||||||
auth_domains => sets:new(),
|
auth_domains => sets:new(),
|
||||||
xmlns => ?NS_SERVER,
|
xmlns => ?NS_SERVER,
|
||||||
lang => ejabberd_config:get_mylang(),
|
lang => ejabberd_option:language(),
|
||||||
server => ejabberd_config:get_myname(),
|
server => ejabberd_config:get_myname(),
|
||||||
lserver => ejabberd_config:get_myname(),
|
lserver => ejabberd_config:get_myname(),
|
||||||
server_host => ejabberd_config:get_myname(),
|
server_host => ejabberd_config:get_myname(),
|
||||||
@ -337,20 +339,11 @@ set_idle_timeout(State) ->
|
|||||||
-spec change_shaper(state(), binary()) -> state().
|
-spec change_shaper(state(), binary()) -> state().
|
||||||
change_shaper(#{shaper := ShaperName, server_host := ServerHost} = State,
|
change_shaper(#{shaper := ShaperName, server_host := ServerHost} = State,
|
||||||
RServer) ->
|
RServer) ->
|
||||||
Shaper = acl:match_rule(ServerHost, ShaperName, jid:make(RServer)),
|
Shaper = ejabberd_shaper:match(ServerHost, ShaperName, jid:make(RServer)),
|
||||||
xmpp_stream_in:change_shaper(State, ejabberd_shaper:new(Shaper)).
|
xmpp_stream_in:change_shaper(State, ejabberd_shaper:new(Shaper)).
|
||||||
|
|
||||||
listen_opt_type(certfile = Opt) ->
|
|
||||||
fun(S) ->
|
|
||||||
?WARNING_MSG("Listening option '~s' for ~s is deprecated, use "
|
|
||||||
"'certfiles' global option instead", [Opt, ?MODULE]),
|
|
||||||
{ok, File} = ejabberd_pkix:add_certfile(S),
|
|
||||||
File
|
|
||||||
end.
|
|
||||||
|
|
||||||
listen_options() ->
|
listen_options() ->
|
||||||
[{shaper, none},
|
[{shaper, none},
|
||||||
{certfile, undefined},
|
|
||||||
{ciphers, undefined},
|
{ciphers, undefined},
|
||||||
{dhfile, undefined},
|
{dhfile, undefined},
|
||||||
{cafile, undefined},
|
{cafile, undefined},
|
||||||
|
@ -21,10 +21,9 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(ejabberd_s2s_out).
|
-module(ejabberd_s2s_out).
|
||||||
-behaviour(xmpp_stream_out).
|
-behaviour(xmpp_stream_out).
|
||||||
-behaviour(ejabberd_config).
|
-dialyzer([{no_fail_call, [stop/1, process_closed/2, handle_timeout/1]},
|
||||||
|
{no_return, [process_closed/2, handle_timeout/1]}]).
|
||||||
|
|
||||||
%% ejabberd_config callbacks
|
|
||||||
-export([opt_type/1, transform_options/1]).
|
|
||||||
%% xmpp_stream_out callbacks
|
%% xmpp_stream_out callbacks
|
||||||
-export([tls_options/1, tls_required/1, tls_verify/1, tls_enabled/1,
|
-export([tls_options/1, tls_required/1, tls_verify/1, tls_enabled/1,
|
||||||
connect_timeout/1, address_families/1, default_port/1,
|
connect_timeout/1, address_families/1, default_port/1,
|
||||||
@ -77,8 +76,7 @@ connect(Ref) ->
|
|||||||
close(Ref) ->
|
close(Ref) ->
|
||||||
xmpp_stream_out:close(Ref).
|
xmpp_stream_out:close(Ref).
|
||||||
|
|
||||||
-spec close(pid(), atom()) -> ok;
|
-spec close(pid(), atom()) -> ok.
|
||||||
(state(), atom()) -> state().
|
|
||||||
close(Ref, Reason) ->
|
close(Ref, Reason) ->
|
||||||
xmpp_stream_out:close(Ref, Reason).
|
xmpp_stream_out:close(Ref, Reason).
|
||||||
|
|
||||||
@ -184,30 +182,26 @@ tls_options(#{server := LServer}) ->
|
|||||||
tls_required(#{server := LServer}) ->
|
tls_required(#{server := LServer}) ->
|
||||||
ejabberd_s2s:tls_required(LServer).
|
ejabberd_s2s:tls_required(LServer).
|
||||||
|
|
||||||
tls_verify(#{server := LServer}) ->
|
tls_verify(#{server := LServer} = State) ->
|
||||||
ejabberd_s2s:tls_verify(LServer).
|
ejabberd_hooks:run_fold(s2s_out_tls_verify, LServer, true, [State]).
|
||||||
|
|
||||||
tls_enabled(#{server := LServer}) ->
|
tls_enabled(#{server := LServer}) ->
|
||||||
ejabberd_s2s:tls_enabled(LServer).
|
ejabberd_s2s:tls_enabled(LServer).
|
||||||
|
|
||||||
connect_timeout(#{server := LServer}) ->
|
connect_timeout(#{server := LServer}) ->
|
||||||
ejabberd_config:get_option(
|
ejabberd_option:outgoing_s2s_timeout(LServer).
|
||||||
{outgoing_s2s_timeout, LServer},
|
|
||||||
timer:seconds(10)).
|
|
||||||
|
|
||||||
default_port(#{server := LServer}) ->
|
default_port(#{server := LServer}) ->
|
||||||
ejabberd_config:get_option({outgoing_s2s_port, LServer}, 5269).
|
ejabberd_option:outgoing_s2s_port(LServer).
|
||||||
|
|
||||||
address_families(#{server := LServer}) ->
|
address_families(#{server := LServer}) ->
|
||||||
ejabberd_config:get_option(
|
ejabberd_option:outgoing_s2s_families(LServer).
|
||||||
{outgoing_s2s_families, LServer},
|
|
||||||
[inet, inet6]).
|
|
||||||
|
|
||||||
dns_retries(#{server := LServer}) ->
|
dns_retries(#{server := LServer}) ->
|
||||||
ejabberd_config:get_option({s2s_dns_retries, LServer}, 2).
|
ejabberd_option:s2s_dns_retries(LServer).
|
||||||
|
|
||||||
dns_timeout(#{server := LServer}) ->
|
dns_timeout(#{server := LServer}) ->
|
||||||
ejabberd_config:get_option({s2s_dns_timeout, LServer}, timer:seconds(10)).
|
ejabberd_option:s2s_dns_timeout(LServer).
|
||||||
|
|
||||||
handle_auth_success(Mech, #{socket := Socket, ip := IP,
|
handle_auth_success(Mech, #{socket := Socket, ip := IP,
|
||||||
remote_server := RServer,
|
remote_server := RServer,
|
||||||
@ -269,11 +263,11 @@ init([#{server := LServer, remote_server := RServer} = State, Opts]) ->
|
|||||||
{_, N} -> N;
|
{_, N} -> N;
|
||||||
false -> unlimited
|
false -> unlimited
|
||||||
end,
|
end,
|
||||||
Timeout = ejabberd_config:negotiation_timeout(),
|
Timeout = ejabberd_option:negotiation_timeout(),
|
||||||
State1 = State#{on_route => queue,
|
State1 = State#{on_route => queue,
|
||||||
queue => p1_queue:new(QueueType, QueueLimit),
|
queue => p1_queue:new(QueueType, QueueLimit),
|
||||||
xmlns => ?NS_SERVER,
|
xmlns => ?NS_SERVER,
|
||||||
lang => ejabberd_config:get_mylang(),
|
lang => ejabberd_option:language(),
|
||||||
server_host => ServerHost,
|
server_host => ServerHost,
|
||||||
shaper => none},
|
shaper => none},
|
||||||
State2 = xmpp_stream_out:set_timeout(State1, Timeout),
|
State2 = xmpp_stream_out:set_timeout(State1, Timeout),
|
||||||
@ -314,8 +308,8 @@ terminate(Reason, #{server := LServer,
|
|||||||
normal -> State;
|
normal -> State;
|
||||||
_ -> State#{stop_reason => internal_failure}
|
_ -> State#{stop_reason => internal_failure}
|
||||||
end,
|
end,
|
||||||
bounce_queue(State1),
|
State2 = bounce_queue(State1),
|
||||||
bounce_message_queue(State1).
|
bounce_message_queue(State2).
|
||||||
|
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
@ -374,7 +368,7 @@ mk_bounce_error(_Lang, _State) ->
|
|||||||
|
|
||||||
-spec get_delay() -> non_neg_integer().
|
-spec get_delay() -> non_neg_integer().
|
||||||
get_delay() ->
|
get_delay() ->
|
||||||
MaxDelay = ejabberd_config:get_option(s2s_max_retry_delay, 300),
|
MaxDelay = ejabberd_option:s2s_max_retry_delay(),
|
||||||
p1_rand:uniform(MaxDelay).
|
p1_rand:uniform(MaxDelay).
|
||||||
|
|
||||||
-spec set_idle_timeout(state()) -> state().
|
-spec set_idle_timeout(state()) -> state().
|
||||||
@ -400,76 +394,3 @@ format_error(queue_full) ->
|
|||||||
<<"Stream queue is overloaded">>;
|
<<"Stream queue is overloaded">>;
|
||||||
format_error(Reason) ->
|
format_error(Reason) ->
|
||||||
xmpp_stream_out:format_error(Reason).
|
xmpp_stream_out:format_error(Reason).
|
||||||
|
|
||||||
transform_options(Opts) ->
|
|
||||||
lists:foldl(fun transform_options/2, [], Opts).
|
|
||||||
|
|
||||||
transform_options({outgoing_s2s_options, Families, Timeout}, Opts) ->
|
|
||||||
?WARNING_MSG("Option 'outgoing_s2s_options' is deprecated. "
|
|
||||||
"The option is still supported "
|
|
||||||
"but it is better to fix your config: "
|
|
||||||
"use 'outgoing_s2s_timeout' and "
|
|
||||||
"'outgoing_s2s_families' instead.", []),
|
|
||||||
maybe_report_huge_timeout(outgoing_s2s_timeout, Timeout),
|
|
||||||
[{outgoing_s2s_families, Families},
|
|
||||||
{outgoing_s2s_timeout, Timeout}
|
|
||||||
| Opts];
|
|
||||||
transform_options({s2s_dns_options, S2SDNSOpts}, AllOpts) ->
|
|
||||||
?WARNING_MSG("Option 's2s_dns_options' is deprecated. "
|
|
||||||
"The option is still supported "
|
|
||||||
"but it is better to fix your config: "
|
|
||||||
"use 's2s_dns_timeout' and "
|
|
||||||
"'s2s_dns_retries' instead", []),
|
|
||||||
lists:foldr(
|
|
||||||
fun({timeout, T}, AccOpts) ->
|
|
||||||
maybe_report_huge_timeout(s2s_dns_timeout, T),
|
|
||||||
[{s2s_dns_timeout, T}|AccOpts];
|
|
||||||
({retries, R}, AccOpts) ->
|
|
||||||
[{s2s_dns_retries, R}|AccOpts];
|
|
||||||
(_, AccOpts) ->
|
|
||||||
AccOpts
|
|
||||||
end, AllOpts, S2SDNSOpts);
|
|
||||||
transform_options({Opt, T}, Opts)
|
|
||||||
when Opt == outgoing_s2s_timeout; Opt == s2s_dns_timeout ->
|
|
||||||
maybe_report_huge_timeout(Opt, T),
|
|
||||||
[{Opt, T}|Opts];
|
|
||||||
transform_options(Opt, Opts) ->
|
|
||||||
[Opt|Opts].
|
|
||||||
|
|
||||||
maybe_report_huge_timeout(Opt, T) when is_integer(T), T >= 1000 ->
|
|
||||||
?WARNING_MSG("value '~p' of option '~p' is too big, "
|
|
||||||
"are you sure you have set seconds?",
|
|
||||||
[T, Opt]);
|
|
||||||
maybe_report_huge_timeout(_, _) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(outgoing_s2s_families) ->
|
|
||||||
fun(Families) ->
|
|
||||||
lists:map(
|
|
||||||
fun(ipv4) -> inet;
|
|
||||||
(ipv6) -> inet6
|
|
||||||
end, Families)
|
|
||||||
end;
|
|
||||||
opt_type(outgoing_s2s_port) ->
|
|
||||||
fun (I) when is_integer(I), I > 0, I < 65536 -> I end;
|
|
||||||
opt_type(outgoing_s2s_timeout) ->
|
|
||||||
fun(TimeOut) when is_integer(TimeOut), TimeOut > 0 ->
|
|
||||||
timer:seconds(TimeOut);
|
|
||||||
(unlimited) ->
|
|
||||||
infinity;
|
|
||||||
(infinity) ->
|
|
||||||
infinity
|
|
||||||
end;
|
|
||||||
opt_type(s2s_dns_retries) ->
|
|
||||||
fun (I) when is_integer(I), I >= 0 -> I end;
|
|
||||||
opt_type(s2s_dns_timeout) ->
|
|
||||||
fun(I) when is_integer(I), I>=0 -> timer:seconds(I);
|
|
||||||
(infinity) -> infinity;
|
|
||||||
(unlimited) -> infinity
|
|
||||||
end;
|
|
||||||
opt_type(s2s_max_retry_delay) ->
|
|
||||||
fun (I) when is_integer(I), I > 0 -> I end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[outgoing_s2s_families, outgoing_s2s_port, outgoing_s2s_timeout,
|
|
||||||
s2s_dns_retries, s2s_dns_timeout, s2s_max_retry_delay].
|
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
%% ejabberd_listener callbacks
|
%% ejabberd_listener callbacks
|
||||||
-export([start/3, start_link/3, accept/1]).
|
-export([start/3, start_link/3, accept/1]).
|
||||||
-export([listen_opt_type/1, listen_options/0, transform_listen_option/2]).
|
-export([listen_opt_type/1, listen_options/0]).
|
||||||
%% xmpp_stream_in callbacks
|
%% xmpp_stream_in callbacks
|
||||||
-export([init/1, handle_info/2, terminate/2, code_change/3]).
|
-export([init/1, handle_info/2, terminate/2, code_change/3]).
|
||||||
-export([handle_stream_start/2, handle_auth_success/4, handle_auth_failure/4,
|
-export([handle_stream_start/2, handle_auth_success/4, handle_auth_failure/4,
|
||||||
@ -65,8 +65,7 @@ send(Stream, Pkt) ->
|
|||||||
close(Ref) ->
|
close(Ref) ->
|
||||||
xmpp_stream_in:close(Ref).
|
xmpp_stream_in:close(Ref).
|
||||||
|
|
||||||
-spec close(pid(), atom()) -> ok;
|
-spec close(pid(), atom()) -> ok.
|
||||||
(state(), atom()) -> state().
|
|
||||||
close(Ref, Reason) ->
|
close(Ref, Reason) ->
|
||||||
xmpp_stream_in:close(Ref, Reason).
|
xmpp_stream_in:close(Ref, Reason).
|
||||||
|
|
||||||
@ -100,12 +99,12 @@ init([State, Opts]) ->
|
|||||||
true -> TLSOpts1
|
true -> TLSOpts1
|
||||||
end,
|
end,
|
||||||
GlobalRoutes = proplists:get_value(global_routes, Opts, true),
|
GlobalRoutes = proplists:get_value(global_routes, Opts, true),
|
||||||
Timeout = ejabberd_config:negotiation_timeout(),
|
Timeout = ejabberd_option:negotiation_timeout(),
|
||||||
State1 = xmpp_stream_in:change_shaper(State, ejabberd_shaper:new(Shaper)),
|
State1 = xmpp_stream_in:change_shaper(State, ejabberd_shaper:new(Shaper)),
|
||||||
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
|
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
|
||||||
State3 = State2#{access => Access,
|
State3 = State2#{access => Access,
|
||||||
xmlns => ?NS_COMPONENT,
|
xmlns => ?NS_COMPONENT,
|
||||||
lang => ejabberd_config:get_mylang(),
|
lang => ejabberd_option:language(),
|
||||||
server => ejabberd_config:get_myname(),
|
server => ejabberd_config:get_myname(),
|
||||||
host_opts => dict:from_list(HostOpts1),
|
host_opts => dict:from_list(HostOpts1),
|
||||||
stream_version => undefined,
|
stream_version => undefined,
|
||||||
@ -263,45 +262,30 @@ check_from(From, #{host_opts := HostOpts}) ->
|
|||||||
random_password() ->
|
random_password() ->
|
||||||
str:sha(p1_rand:bytes(20)).
|
str:sha(p1_rand:bytes(20)).
|
||||||
|
|
||||||
transform_listen_option({hosts, Hosts, O}, Opts) ->
|
|
||||||
case lists:keyfind(hosts, 1, Opts) of
|
|
||||||
{_, PrevHostOpts} ->
|
|
||||||
NewHostOpts =
|
|
||||||
lists:foldl(
|
|
||||||
fun(H, Acc) ->
|
|
||||||
dict:append_list(H, O, Acc)
|
|
||||||
end, dict:from_list(PrevHostOpts), Hosts),
|
|
||||||
[{hosts, dict:to_list(NewHostOpts)}|
|
|
||||||
lists:keydelete(hosts, 1, Opts)];
|
|
||||||
_ ->
|
|
||||||
[{hosts, [{H, O} || H <- Hosts]}|Opts]
|
|
||||||
end;
|
|
||||||
transform_listen_option({host, Host, Os}, Opts) ->
|
|
||||||
transform_listen_option({hosts, [Host], Os}, Opts);
|
|
||||||
transform_listen_option(Opt, Opts) ->
|
|
||||||
[Opt|Opts].
|
|
||||||
|
|
||||||
listen_opt_type(shaper_rule) ->
|
listen_opt_type(shaper_rule) ->
|
||||||
fun(V) ->
|
econf:and_then(
|
||||||
|
econf:shaper(),
|
||||||
|
fun(S) ->
|
||||||
?WARNING_MSG("Listening option 'shaper_rule' of module ~s "
|
?WARNING_MSG("Listening option 'shaper_rule' of module ~s "
|
||||||
"is renamed to 'shaper'", [?MODULE]),
|
"is renamed to 'shaper'. Please adjust your "
|
||||||
acl:shaper_rules_validator(V)
|
"configuration", [?MODULE]),
|
||||||
end;
|
S
|
||||||
listen_opt_type(check_from) -> fun(B) when is_boolean(B) -> B end;
|
end);
|
||||||
listen_opt_type(password) -> fun iolist_to_binary/1;
|
listen_opt_type(check_from) ->
|
||||||
|
econf:bool();
|
||||||
|
listen_opt_type(password) ->
|
||||||
|
econf:binary();
|
||||||
listen_opt_type(hosts) ->
|
listen_opt_type(hosts) ->
|
||||||
fun(HostOpts) ->
|
econf:and_then(
|
||||||
lists:map(
|
econf:map(
|
||||||
|
econf:domain(),
|
||||||
|
econf:options(
|
||||||
|
#{password => econf:binary()})),
|
||||||
fun({Host, Opts}) ->
|
fun({Host, Opts}) ->
|
||||||
Password = case proplists:get_value(password, Opts) of
|
{Host, proplists:get_value(password, Opts)}
|
||||||
undefined -> undefined;
|
end);
|
||||||
P -> iolist_to_binary(P)
|
|
||||||
end,
|
|
||||||
{iolist_to_binary(Host), Password}
|
|
||||||
end, HostOpts)
|
|
||||||
end;
|
|
||||||
listen_opt_type(global_routes) ->
|
listen_opt_type(global_routes) ->
|
||||||
fun(B) when is_boolean(B) -> B end.
|
econf:bool().
|
||||||
|
|
||||||
listen_options() ->
|
listen_options() ->
|
||||||
[{access, all},
|
[{access, all},
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% File : ejabberd_shaper.erl
|
|
||||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
|
||||||
%%% Purpose : Functions to control connections traffic
|
|
||||||
%%% Created : 9 Feb 2003 by Alexey Shchepin <alexey@process-one.net>
|
|
||||||
%%%
|
|
||||||
%%%
|
|
||||||
%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
|
%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
|
||||||
%%%
|
%%%
|
||||||
%%% This program is free software; you can redistribute it and/or
|
%%% This program is free software; you can redistribute it and/or
|
||||||
@ -22,131 +16,225 @@
|
|||||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
%%%
|
%%%
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
-module(ejabberd_shaper).
|
-module(ejabberd_shaper).
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-author('alexey@process-one.net').
|
-export([start_link/0, new/1, update/2, match/3, get_max_rate/1]).
|
||||||
|
-export([reload_from_config/0]).
|
||||||
-export([start_link/0, new/1, update/2,
|
-export([validator/1, shaper_rules_validator/0]).
|
||||||
get_max_rate/1, transform_options/1, load_from_config/0,
|
|
||||||
opt_type/1]).
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
terminate/2, code_change/3]).
|
terminate/2, code_change/3]).
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
-record(shaper, {name :: {atom(), global},
|
-type state() :: #{hosts := [binary()]}.
|
||||||
maxrate :: integer(),
|
|
||||||
burst_size :: integer()}).
|
|
||||||
|
|
||||||
-record(state, {}).
|
|
||||||
|
|
||||||
-type shaper() :: none | p1_shaper:state().
|
-type shaper() :: none | p1_shaper:state().
|
||||||
-export_type([shaper/0]).
|
-type shaper_rate() :: {pos_integer(), pos_integer()} | pos_integer() | infinity.
|
||||||
|
-type shaper_rule() :: {atom() | pos_integer(), [acl:access_rule()]}.
|
||||||
|
-type shaper_rate_rule() :: {shaper_rate(), [acl:access_rule()]}.
|
||||||
|
|
||||||
|
-export_type([shaper/0, shaper_rule/0, shaper_rate/0]).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
-spec start_link() -> {ok, pid()} | {error, any()}.
|
-spec start_link() -> {ok, pid()} | {error, any()}.
|
||||||
start_link() ->
|
start_link() ->
|
||||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||||
|
|
||||||
|
-spec match(global | binary(), atom() | [shaper_rule()],
|
||||||
|
jid:jid() | jid:ljid() | inet:ip_address() | acl:match()) -> none | shaper_rate().
|
||||||
|
match(_, none, _) -> none;
|
||||||
|
match(_, infinity, _) -> infinity;
|
||||||
|
match(Host, Shaper, Match) when is_map(Match) ->
|
||||||
|
Rules = if is_atom(Shaper) -> read_shaper_rules(Shaper, Host);
|
||||||
|
true -> Shaper
|
||||||
|
end,
|
||||||
|
Rate = acl:match_rules(Host, Rules, Match, none),
|
||||||
|
read_shaper(Rate);
|
||||||
|
match(Host, Shaper, IP) when tuple_size(IP) == 4; tuple_size(IP) == 8 ->
|
||||||
|
match(Host, Shaper, #{ip => IP});
|
||||||
|
match(Host, Shaper, JID) ->
|
||||||
|
match(Host, Shaper, #{usr => jid:tolower(JID)}).
|
||||||
|
|
||||||
|
-spec get_max_rate(none | shaper_rate()) -> none | pos_integer().
|
||||||
|
get_max_rate({Rate, _}) -> Rate;
|
||||||
|
get_max_rate(Rate) when is_integer(Rate), Rate > 0 -> Rate;
|
||||||
|
get_max_rate(_) -> none.
|
||||||
|
|
||||||
|
-spec new(none | shaper_rate()) -> shaper().
|
||||||
|
new({Rate, Burst}) -> p1_shaper:new(Rate, Burst);
|
||||||
|
new(Rate) when is_integer(Rate), Rate > 0 -> p1_shaper:new(Rate);
|
||||||
|
new(_) -> none.
|
||||||
|
|
||||||
|
-spec update(shaper(), non_neg_integer()) -> {shaper(), non_neg_integer()}.
|
||||||
|
update(none, _Size) -> {none, 0};
|
||||||
|
update(Shaper1, Size) ->
|
||||||
|
Shaper2 = p1_shaper:update(Shaper1, Size),
|
||||||
|
?DEBUG("Shaper update:~n~s =>~n~s",
|
||||||
|
[p1_shaper:pp(Shaper1), p1_shaper:pp(Shaper2)]),
|
||||||
|
Shaper2.
|
||||||
|
|
||||||
|
-spec validator(shaper | shaper_rules) -> econf:validator().
|
||||||
|
validator(shaper) ->
|
||||||
|
econf:options(
|
||||||
|
#{'_' => shaper_validator()},
|
||||||
|
[{disallowed, reserved()}, {return, map}, unique]);
|
||||||
|
validator(shaper_rules) ->
|
||||||
|
econf:options(
|
||||||
|
#{'_' => shaper_rules_validator()},
|
||||||
|
[{disallowed, reserved()}, unique]).
|
||||||
|
|
||||||
|
-spec shaper_rules_validator() -> econf:validator().
|
||||||
|
shaper_rules_validator() ->
|
||||||
|
fun(L) when is_list(L) ->
|
||||||
|
lists:map(
|
||||||
|
fun({K, V}) ->
|
||||||
|
{(shaper_name())(K), (acl:access_validator())(V)};
|
||||||
|
(N) ->
|
||||||
|
{(shaper_name())(N), [{acl, all}]}
|
||||||
|
end, lists:flatten(L));
|
||||||
|
(N) ->
|
||||||
|
[{(shaper_name())(N), [{acl, all}]}]
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec reload_from_config() -> ok.
|
||||||
|
reload_from_config() ->
|
||||||
|
gen_server:call(?MODULE, reload_from_config, timer:minutes(1)).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% gen_server callbacks
|
||||||
|
%%%===================================================================
|
||||||
init([]) ->
|
init([]) ->
|
||||||
ejabberd_mnesia:create(?MODULE, shaper,
|
create_tabs(),
|
||||||
[{ram_copies, [node()]},
|
Hosts = ejabberd_option:hosts(),
|
||||||
{local_content, true},
|
load_from_config([], Hosts),
|
||||||
{attributes, record_info(fields, shaper)}]),
|
ejabberd_hooks:add(config_reloaded, ?MODULE, reload_from_config, 20),
|
||||||
ejabberd_hooks:add(config_reloaded, ?MODULE, load_from_config, 20),
|
{ok, #{hosts => Hosts}}.
|
||||||
load_from_config(),
|
|
||||||
{ok, #state{}}.
|
|
||||||
|
|
||||||
handle_call(_Request, _From, State) ->
|
-spec handle_call(term(), term(), state()) -> {reply, ok, state()} | {noreply, state()}.
|
||||||
Reply = ok,
|
handle_call(reload_from_config, _, #{hosts := OldHosts} = State) ->
|
||||||
{reply, Reply, State}.
|
NewHosts = ejabberd_option:hosts(),
|
||||||
|
load_from_config(OldHosts, NewHosts),
|
||||||
handle_cast(_Msg, State) ->
|
{reply, ok, State#{hosts => NewHosts}};
|
||||||
|
handle_call(Request, From, State) ->
|
||||||
|
?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]),
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
handle_info(_Info, State) ->
|
-spec handle_cast(term(), state()) -> {noreply, state()}.
|
||||||
|
handle_cast(Msg, State) ->
|
||||||
|
?WARNING_MSG("Unexpected cast: ~p", [Msg]),
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
|
-spec handle_info(term(), state()) -> {noreply, state()}.
|
||||||
|
handle_info(Info, State) ->
|
||||||
|
?WARNING_MSG("Unexpected info: ~p", [Info]),
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
-spec terminate(any(), state()) -> ok.
|
||||||
terminate(_Reason, _State) ->
|
terminate(_Reason, _State) ->
|
||||||
ok.
|
ejabberd_hooks:delete(config_reloaded, ?MODULE, reload_from_config, 20).
|
||||||
|
|
||||||
|
-spec code_change(term(), state(), term()) -> {ok, state()}.
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
|
||||||
-spec load_from_config() -> ok | {error, any()}.
|
%%%===================================================================
|
||||||
load_from_config() ->
|
%%% Internal functions
|
||||||
Shapers = ejabberd_config:get_option(shaper, []),
|
%%%===================================================================
|
||||||
case mnesia:transaction(
|
%%%===================================================================
|
||||||
fun() ->
|
%%% Table management
|
||||||
|
%%%===================================================================
|
||||||
|
-spec load_from_config([binary()], [binary()]) -> ok.
|
||||||
|
load_from_config(OldHosts, NewHosts) ->
|
||||||
|
?DEBUG("Loading shaper rules from config", []),
|
||||||
|
Shapers = ejabberd_option:shaper(),
|
||||||
|
ets:insert(shaper, maps:to_list(Shapers)),
|
||||||
|
ets:insert(
|
||||||
|
shaper_rules,
|
||||||
|
lists:flatmap(
|
||||||
|
fun(Host) ->
|
||||||
|
lists:flatmap(
|
||||||
|
fun({Name, List}) ->
|
||||||
|
case resolve_shapers(Name, List, Shapers) of
|
||||||
|
[] -> [];
|
||||||
|
List1 ->
|
||||||
|
[{{Name, Host}, List1}]
|
||||||
|
end
|
||||||
|
end, ejabberd_option:shaper_rules(Host))
|
||||||
|
end, [global|NewHosts])),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun({Name, MaxRate, BurstSize}) ->
|
fun(Host) ->
|
||||||
mnesia:write(
|
ets:match_delete(shaper_rules, {{'_', Host}, '_'})
|
||||||
#shaper{name = {Name, global},
|
end, OldHosts -- NewHosts),
|
||||||
maxrate = MaxRate,
|
?DEBUG("Shaper rules loaded successfully", []).
|
||||||
burst_size = BurstSize})
|
|
||||||
end,
|
-spec create_tabs() -> ok.
|
||||||
Shapers)
|
create_tabs() ->
|
||||||
end) of
|
_ = mnesia:delete_table(shaper),
|
||||||
{atomic, ok} ->
|
_ = ets:new(shaper, [named_table, {read_concurrency, true}]),
|
||||||
ok;
|
_ = ets:new(shaper_rules, [named_table, {read_concurrency, true}]),
|
||||||
Err ->
|
ok.
|
||||||
{error, Err}
|
|
||||||
|
-spec read_shaper_rules(atom(), global | binary()) -> [shaper_rate_rule()].
|
||||||
|
read_shaper_rules(Name, Host) ->
|
||||||
|
case ets:lookup(shaper_rules, {Name, Host}) of
|
||||||
|
[{_, Rule}] -> Rule;
|
||||||
|
[] -> []
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get_max_rate(atom()) -> none | non_neg_integer().
|
-spec read_shaper(atom() | shaper_rate()) -> none | shaper_rate().
|
||||||
get_max_rate(none) ->
|
read_shaper(Name) when is_atom(Name), Name /= none, Name /= infinity ->
|
||||||
none;
|
case ets:lookup(shaper, Name) of
|
||||||
get_max_rate(Name) ->
|
[{_, Rate}] -> Rate;
|
||||||
case ets:lookup(shaper, {Name, global}) of
|
[] -> none
|
||||||
[#shaper{maxrate = R}] ->
|
end;
|
||||||
R;
|
read_shaper(Rate) ->
|
||||||
[] ->
|
Rate.
|
||||||
none
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec new(atom()) -> shaper().
|
%%%===================================================================
|
||||||
new(none) ->
|
%%% Validators
|
||||||
none;
|
%%%===================================================================
|
||||||
new(Name) ->
|
shaper_name() ->
|
||||||
case ets:lookup(shaper, {Name, global}) of
|
econf:either(
|
||||||
[#shaper{maxrate = R, burst_size = B}] ->
|
econf:and_then(
|
||||||
p1_shaper:new(R, B);
|
econf:atom(),
|
||||||
[] ->
|
fun(infinite) -> infinity;
|
||||||
none
|
(unlimited) -> infinity;
|
||||||
end.
|
(A) -> A
|
||||||
|
end),
|
||||||
|
econf:pos_int()).
|
||||||
|
|
||||||
-spec update(shaper(), integer()) -> {shaper(), integer()}.
|
shaper_validator() ->
|
||||||
update(none, _Size) -> {none, 0};
|
econf:either(
|
||||||
update(Shaper, Size) ->
|
econf:and_then(
|
||||||
Result = p1_shaper:update(Shaper, Size),
|
econf:options(
|
||||||
?DEBUG("Shaper update:~n~s =>~n~s",
|
#{rate => econf:pos_int(),
|
||||||
[p1_shaper:pp(Shaper), p1_shaper:pp(Result)]),
|
burst_size => econf:pos_int()},
|
||||||
Result.
|
[unique, {required, [rate]}, {return, map}]),
|
||||||
|
fun(#{rate := Rate} = Map) ->
|
||||||
|
{Rate, maps:get(burst_size, Map, Rate)}
|
||||||
|
end),
|
||||||
|
econf:pos_int(infinity)).
|
||||||
|
|
||||||
transform_options(Opts) ->
|
%%%===================================================================
|
||||||
lists:foldl(fun transform_options/2, [], Opts).
|
%%% Aux
|
||||||
|
%%%===================================================================
|
||||||
|
reserved() ->
|
||||||
|
[none, infinite, unlimited, infinity].
|
||||||
|
|
||||||
transform_options({shaper, Name, {maxrate, N}}, Opts) ->
|
-spec resolve_shapers(atom(), [shaper_rule()], #{atom() => shaper_rate()}) -> [shaper_rate_rule()].
|
||||||
[{shaper, [{Name, N}]} | Opts];
|
resolve_shapers(ShaperRule, Rules, Shapers) ->
|
||||||
transform_options({shaper, Name, none}, Opts) ->
|
lists:filtermap(
|
||||||
[{shaper, [{Name, none}]} | Opts];
|
fun({Name, Rule}) when is_atom(Name), Name /= none, Name /= infinity ->
|
||||||
transform_options({shaper, List}, Opts) when is_list(List) ->
|
try {true, {maps:get(Name, Shapers), Rule}}
|
||||||
R = lists:map(
|
catch _:{badkey, _} ->
|
||||||
fun({Name, Args}) when is_list(Args) ->
|
?WARNING_MSG(
|
||||||
MaxRate = proplists:get_value(rate, Args, 1000),
|
"Shaper rule '~s' refers to unknown shaper: ~s",
|
||||||
BurstSize = proplists:get_value(burst_size, Args, MaxRate),
|
[ShaperRule, Name]),
|
||||||
{Name, MaxRate, BurstSize};
|
false
|
||||||
({Name, Val}) ->
|
end;
|
||||||
{Name, Val, Val}
|
(_) ->
|
||||||
end, List),
|
true
|
||||||
[{shaper, R} | Opts];
|
end, Rules).
|
||||||
transform_options(Opt, Opts) ->
|
|
||||||
[Opt | Opts].
|
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(shaper) -> fun(V) -> V end;
|
|
||||||
opt_type(_) -> [shaper].
|
|
||||||
|
@ -45,8 +45,8 @@ start_link(_, _, _) ->
|
|||||||
-else.
|
-else.
|
||||||
%% API
|
%% API
|
||||||
-export([tcp_init/2, udp_init/2, udp_recv/5, start/3,
|
-export([tcp_init/2, udp_init/2, udp_recv/5, start/3,
|
||||||
start_link/3, accept/1, listen_options/0]).
|
start_link/3, accept/1]).
|
||||||
|
-export([listen_opt_type/1, listen_options/0]).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% API
|
%%% API
|
||||||
@ -80,15 +80,13 @@ set_certfile(Opts) ->
|
|||||||
{ok, CertFile} ->
|
{ok, CertFile} ->
|
||||||
[{certfile, CertFile}|Opts];
|
[{certfile, CertFile}|Opts];
|
||||||
error ->
|
error ->
|
||||||
case ejabberd_config:get_option({domain_certfile, ejabberd_config:get_myname()}) of
|
Opts
|
||||||
undefined ->
|
|
||||||
Opts;
|
|
||||||
CertFile ->
|
|
||||||
[{certfile, CertFile}|Opts]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
listen_opt_type(certfile) ->
|
||||||
|
econf:pem().
|
||||||
|
|
||||||
listen_options() ->
|
listen_options() ->
|
||||||
[{tls, false},
|
[{tls, false},
|
||||||
{certfile, undefined}].
|
{certfile, undefined}].
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
|
|
||||||
-module(ejabberd_sm).
|
-module(ejabberd_sm).
|
||||||
|
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-ifndef(GEN_SERVER).
|
-ifndef(GEN_SERVER).
|
||||||
@ -83,7 +81,7 @@
|
|||||||
]).
|
]).
|
||||||
|
|
||||||
-export([init/1, handle_call/3, handle_cast/2,
|
-export([init/1, handle_call/3, handle_cast/2,
|
||||||
handle_info/2, terminate/2, code_change/3, opt_type/1]).
|
handle_info/2, terminate/2, code_change/3]).
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
@ -117,11 +115,12 @@
|
|||||||
start_link() ->
|
start_link() ->
|
||||||
?GEN_SERVER:start_link({local, ?MODULE}, ?MODULE, [], []).
|
?GEN_SERVER:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||||
|
|
||||||
-spec stop() -> ok.
|
-spec stop() -> ok | {error, atom()}.
|
||||||
stop() ->
|
stop() ->
|
||||||
supervisor:terminate_child(ejabberd_sup, ?MODULE),
|
case supervisor:terminate_child(ejabberd_sup, ?MODULE) of
|
||||||
supervisor:delete_child(ejabberd_sup, ?MODULE),
|
ok -> supervisor:delete_child(ejabberd_sup, ?MODULE);
|
||||||
ok.
|
Err -> Err
|
||||||
|
end.
|
||||||
|
|
||||||
-spec route(jid(), term()) -> ok.
|
-spec route(jid(), term()) -> ok.
|
||||||
%% @doc route arbitrary term to c2s process(es)
|
%% @doc route arbitrary term to c2s process(es)
|
||||||
@ -477,7 +476,7 @@ init([]) ->
|
|||||||
ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
|
ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
|
||||||
ejabberd_hooks:add(host_down, ?MODULE, host_down, 60),
|
ejabberd_hooks:add(host_down, ?MODULE, host_down, 60),
|
||||||
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50),
|
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50),
|
||||||
lists:foreach(fun host_up/1, ejabberd_config:get_myhosts()),
|
lists:foreach(fun host_up/1, ejabberd_option:hosts()),
|
||||||
ejabberd_commands:register_commands(get_commands_spec()),
|
ejabberd_commands:register_commands(get_commands_spec()),
|
||||||
{ok, #state{}};
|
{ok, #state{}};
|
||||||
{error, Why} ->
|
{error, Why} ->
|
||||||
@ -497,7 +496,7 @@ handle_info(Info, State) ->
|
|||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
terminate(_Reason, _State) ->
|
terminate(_Reason, _State) ->
|
||||||
lists:foreach(fun host_down/1, ejabberd_config:get_myhosts()),
|
lists:foreach(fun host_down/1, ejabberd_option:hosts()),
|
||||||
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50),
|
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50),
|
||||||
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 60),
|
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 60),
|
||||||
ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 50),
|
ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 50),
|
||||||
@ -860,9 +859,8 @@ check_max_sessions(LUser, LServer) ->
|
|||||||
%% Defaults to infinity
|
%% Defaults to infinity
|
||||||
-spec get_max_user_sessions(binary(), binary()) -> infinity | non_neg_integer().
|
-spec get_max_user_sessions(binary(), binary()) -> infinity | non_neg_integer().
|
||||||
get_max_user_sessions(LUser, Host) ->
|
get_max_user_sessions(LUser, Host) ->
|
||||||
case acl:match_rule(Host, max_user_sessions,
|
case ejabberd_shaper:match(Host, max_user_sessions,
|
||||||
jid:make(LUser, Host))
|
jid:make(LUser, Host)) of
|
||||||
of
|
|
||||||
Max when is_integer(Max) -> Max;
|
Max when is_integer(Max) -> Max;
|
||||||
infinity -> infinity;
|
infinity -> infinity;
|
||||||
_ -> ?MAX_USER_SESSIONS
|
_ -> ?MAX_USER_SESSIONS
|
||||||
@ -882,15 +880,13 @@ force_update_presence({LUser, LServer}) ->
|
|||||||
-spec get_sm_backend(binary()) -> module().
|
-spec get_sm_backend(binary()) -> module().
|
||||||
|
|
||||||
get_sm_backend(Host) ->
|
get_sm_backend(Host) ->
|
||||||
DBType = ejabberd_config:get_option(
|
DBType = ejabberd_option:sm_db_type(Host),
|
||||||
{sm_db_type, Host},
|
|
||||||
ejabberd_config:default_ram_db(Host, ?MODULE)),
|
|
||||||
list_to_atom("ejabberd_sm_" ++ atom_to_list(DBType)).
|
list_to_atom("ejabberd_sm_" ++ atom_to_list(DBType)).
|
||||||
|
|
||||||
-spec get_sm_backends() -> [module()].
|
-spec get_sm_backends() -> [module()].
|
||||||
|
|
||||||
get_sm_backends() ->
|
get_sm_backends() ->
|
||||||
lists:usort([get_sm_backend(Host) || Host <- ejabberd_config:get_myhosts()]).
|
lists:usort([get_sm_backend(Host) || Host <- ejabberd_option:hosts()]).
|
||||||
|
|
||||||
-spec get_vh_by_backend(module()) -> [binary()].
|
-spec get_vh_by_backend(module()) -> [binary()].
|
||||||
|
|
||||||
@ -898,7 +894,7 @@ get_vh_by_backend(Mod) ->
|
|||||||
lists:filter(
|
lists:filter(
|
||||||
fun(Host) ->
|
fun(Host) ->
|
||||||
get_sm_backend(Host) == Mod
|
get_sm_backend(Host) == Mod
|
||||||
end, ejabberd_config:get_myhosts()).
|
end, ejabberd_option:hosts()).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%%% Cache stuff
|
%%% Cache stuff
|
||||||
@ -914,15 +910,9 @@ init_cache() ->
|
|||||||
|
|
||||||
-spec cache_opts() -> [proplists:property()].
|
-spec cache_opts() -> [proplists:property()].
|
||||||
cache_opts() ->
|
cache_opts() ->
|
||||||
MaxSize = ejabberd_config:get_option(
|
MaxSize = ejabberd_option:sm_cache_size(),
|
||||||
sm_cache_size,
|
CacheMissed = ejabberd_option:sm_cache_missed(),
|
||||||
ejabberd_config:cache_size(global)),
|
LifeTime = case ejabberd_option:sm_cache_life_time() of
|
||||||
CacheMissed = ejabberd_config:get_option(
|
|
||||||
sm_cache_missed,
|
|
||||||
ejabberd_config:cache_missed(global)),
|
|
||||||
LifeTime = case ejabberd_config:get_option(
|
|
||||||
sm_cache_life_time,
|
|
||||||
ejabberd_config:cache_life_time(global)) of
|
|
||||||
infinity -> infinity;
|
infinity -> infinity;
|
||||||
I -> timer:seconds(I)
|
I -> timer:seconds(I)
|
||||||
end,
|
end,
|
||||||
@ -949,10 +939,7 @@ clean_cache() ->
|
|||||||
use_cache(Mod, LServer) ->
|
use_cache(Mod, LServer) ->
|
||||||
case erlang:function_exported(Mod, use_cache, 1) of
|
case erlang:function_exported(Mod, use_cache, 1) of
|
||||||
true -> Mod:use_cache(LServer);
|
true -> Mod:use_cache(LServer);
|
||||||
false ->
|
false -> ejabberd_option:sm_use_cache(LServer)
|
||||||
ejabberd_config:get_option(
|
|
||||||
{sm_use_cache, LServer},
|
|
||||||
ejabberd_config:use_cache(LServer))
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec use_cache() -> boolean().
|
-spec use_cache() -> boolean().
|
||||||
@ -961,7 +948,7 @@ use_cache() ->
|
|||||||
fun(Host) ->
|
fun(Host) ->
|
||||||
Mod = get_sm_backend(Host),
|
Mod = get_sm_backend(Host),
|
||||||
use_cache(Mod, Host)
|
use_cache(Mod, Host)
|
||||||
end, ejabberd_config:get_myhosts()).
|
end, ejabberd_option:hosts()).
|
||||||
|
|
||||||
-spec cache_nodes(module(), binary()) -> [node()].
|
-spec cache_nodes(module(), binary()) -> [node()].
|
||||||
cache_nodes(Mod, LServer) ->
|
cache_nodes(Mod, LServer) ->
|
||||||
@ -1041,16 +1028,3 @@ kick_user(User, Server, Resource) ->
|
|||||||
|
|
||||||
make_sid() ->
|
make_sid() ->
|
||||||
{misc:unique_timestamp(), self()}.
|
{misc:unique_timestamp(), self()}.
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(sm_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
|
||||||
opt_type(O) when O == sm_use_cache; O == sm_cache_missed ->
|
|
||||||
fun(B) when is_boolean(B) -> B end;
|
|
||||||
opt_type(O) when O == sm_cache_size; O == sm_cache_life_time ->
|
|
||||||
fun(I) when is_integer(I), I>0 -> I;
|
|
||||||
(unlimited) -> infinity;
|
|
||||||
(infinity) -> infinity
|
|
||||||
end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[sm_db_type, sm_use_cache, sm_cache_size, sm_cache_missed,
|
|
||||||
sm_cache_life_time].
|
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
-module(ejabberd_sm_sql).
|
-module(ejabberd_sm_sql).
|
||||||
|
|
||||||
-compile([{parse_transform, ejabberd_sql_pt}]).
|
|
||||||
|
|
||||||
-behaviour(ejabberd_sm).
|
-behaviour(ejabberd_sm).
|
||||||
|
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
|
|
||||||
-module(ejabberd_sql).
|
-module(ejabberd_sql).
|
||||||
|
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-behaviour(p1_fsm).
|
-behaviour(p1_fsm).
|
||||||
@ -65,8 +63,7 @@
|
|||||||
code_change/4]).
|
code_change/4]).
|
||||||
|
|
||||||
-export([connecting/2, connecting/3,
|
-export([connecting/2, connecting/3,
|
||||||
session_established/2, session_established/3,
|
session_established/2, session_established/3]).
|
||||||
opt_type/1]).
|
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("ejabberd_sql_pt.hrl").
|
-include("ejabberd_sql_pt.hrl").
|
||||||
@ -86,24 +83,12 @@
|
|||||||
|
|
||||||
-define(TOP_LEVEL_TXN, 0).
|
-define(TOP_LEVEL_TXN, 0).
|
||||||
|
|
||||||
-define(PGSQL_PORT, 5432).
|
|
||||||
|
|
||||||
-define(MYSQL_PORT, 3306).
|
|
||||||
|
|
||||||
-define(MSSQL_PORT, 1433).
|
|
||||||
|
|
||||||
-define(MAX_TRANSACTION_RESTARTS, 10).
|
-define(MAX_TRANSACTION_RESTARTS, 10).
|
||||||
|
|
||||||
-define(KEEPALIVE_QUERY, [<<"SELECT 1;">>]).
|
-define(KEEPALIVE_QUERY, [<<"SELECT 1;">>]).
|
||||||
|
|
||||||
-define(PREPARE_KEY, ejabberd_sql_prepare).
|
-define(PREPARE_KEY, ejabberd_sql_prepare).
|
||||||
|
|
||||||
-ifdef(NEW_SQL_SCHEMA).
|
|
||||||
-define(USE_NEW_SCHEMA_DEFAULT, true).
|
|
||||||
-else.
|
|
||||||
-define(USE_NEW_SCHEMA_DEFAULT, false).
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
%%-define(DBGFSM, true).
|
%%-define(DBGFSM, true).
|
||||||
|
|
||||||
-ifdef(DBGFSM).
|
-ifdef(DBGFSM).
|
||||||
@ -128,13 +113,15 @@ start_link(Host, StartInterval) ->
|
|||||||
[Host, StartInterval],
|
[Host, StartInterval],
|
||||||
fsm_limit_opts() ++ (?FSMOPTS)).
|
fsm_limit_opts() ++ (?FSMOPTS)).
|
||||||
|
|
||||||
-type sql_query() :: [sql_query() | binary()] | #sql_query{} |
|
-type sql_query_simple() :: [sql_query() | binary()] | #sql_query{} |
|
||||||
fun(() -> any()) | fun((atom(), _) -> any()).
|
fun(() -> any()) | fun((atom(), _) -> any()).
|
||||||
|
-type sql_query() :: sql_query_simple() |
|
||||||
|
[{atom() | {atom(), any()}, sql_query_simple()}].
|
||||||
-type sql_query_result() :: {updated, non_neg_integer()} |
|
-type sql_query_result() :: {updated, non_neg_integer()} |
|
||||||
{error, binary()} |
|
{error, binary()} |
|
||||||
{selected, [binary()],
|
{selected, [binary()], [[binary()]]} |
|
||||||
[[binary()]]} |
|
{selected, [any()]} |
|
||||||
{selected, [any()]}.
|
ok.
|
||||||
|
|
||||||
-spec sql_query(binary(), sql_query()) -> sql_query_result().
|
-spec sql_query(binary(), sql_query()) -> sql_query_result().
|
||||||
|
|
||||||
@ -300,39 +287,41 @@ sqlite_db(Host) ->
|
|||||||
|
|
||||||
-spec sqlite_file(binary()) -> string().
|
-spec sqlite_file(binary()) -> string().
|
||||||
sqlite_file(Host) ->
|
sqlite_file(Host) ->
|
||||||
case ejabberd_config:get_option({sql_database, Host}) of
|
case ejabberd_option:sql_database(Host) of
|
||||||
undefined ->
|
undefined ->
|
||||||
{ok, Cwd} = file:get_cwd(),
|
Path = ["sqlite", atom_to_list(node()),
|
||||||
filename:join([Cwd, "sqlite", atom_to_list(node()),
|
binary_to_list(Host), "ejabberd.db"],
|
||||||
binary_to_list(Host), "ejabberd.db"]);
|
case file:get_cwd() of
|
||||||
|
{ok, Cwd} ->
|
||||||
|
filename:join([Cwd|Path]);
|
||||||
|
{error, Reason} ->
|
||||||
|
?ERROR_MSG("Failed to get current directory: ~s",
|
||||||
|
[file:format_error(Reason)]),
|
||||||
|
filename:join(Path)
|
||||||
|
end;
|
||||||
File ->
|
File ->
|
||||||
binary_to_list(File)
|
binary_to_list(File)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
use_new_schema() ->
|
use_new_schema() ->
|
||||||
ejabberd_config:get_option(new_sql_schema, ?USE_NEW_SCHEMA_DEFAULT).
|
ejabberd_option:new_sql_schema().
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% Callback functions from gen_fsm
|
%%% Callback functions from gen_fsm
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
init([Host, StartInterval]) ->
|
init([Host, StartInterval]) ->
|
||||||
process_flag(trap_exit, true),
|
process_flag(trap_exit, true),
|
||||||
case ejabberd_config:get_option({sql_keepalive_interval, Host}) of
|
case ejabberd_option:sql_keepalive_interval(Host) of
|
||||||
undefined ->
|
undefined ->
|
||||||
ok;
|
ok;
|
||||||
KeepaliveInterval ->
|
KeepaliveInterval ->
|
||||||
timer:apply_interval(KeepaliveInterval * 1000, ?MODULE,
|
timer:apply_interval(KeepaliveInterval, ?MODULE,
|
||||||
keep_alive, [Host, self()])
|
keep_alive, [Host, self()])
|
||||||
end,
|
end,
|
||||||
[DBType | _] = db_opts(Host),
|
[DBType | _] = db_opts(Host),
|
||||||
p1_fsm:send_event(self(), connect),
|
p1_fsm:send_event(self(), connect),
|
||||||
ejabberd_sql_sup:add_pid(Host, self()),
|
ejabberd_sql_sup:add_pid(Host, self()),
|
||||||
QueueType = case ejabberd_config:get_option({sql_queue_type, Host}) of
|
QueueType = ejabberd_option:sql_queue_type(Host),
|
||||||
undefined ->
|
|
||||||
ejabberd_config:default_queue_type(Host);
|
|
||||||
Type ->
|
|
||||||
Type
|
|
||||||
end,
|
|
||||||
{ok, connecting,
|
{ok, connecting,
|
||||||
#state{db_type = DBType, host = Host,
|
#state{db_type = DBType, host = Host,
|
||||||
pending_requests = p1_queue:new(QueueType, max_fsm_queue()),
|
pending_requests = p1_queue:new(QueueType, max_fsm_queue()),
|
||||||
@ -995,11 +984,13 @@ log(Level, Format, Args) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
db_opts(Host) ->
|
db_opts(Host) ->
|
||||||
Type = ejabberd_config:get_option({sql_type, Host}, odbc),
|
Type = case ejabberd_option:sql_type(Host) of
|
||||||
Server = ejabberd_config:get_option({sql_server, Host}, <<"localhost">>),
|
undefined -> odbc;
|
||||||
Timeout = timer:seconds(
|
T -> T
|
||||||
ejabberd_config:get_option({sql_connect_timeout, Host}, 5)),
|
end,
|
||||||
Transport = case ejabberd_config:get_option({sql_ssl, Host}, false) of
|
Server = ejabberd_option:sql_server(Host),
|
||||||
|
Timeout = ejabberd_option:sql_connect_timeout(Host),
|
||||||
|
Transport = case ejabberd_option:sql_ssl(Host) of
|
||||||
false -> tcp;
|
false -> tcp;
|
||||||
true -> ssl
|
true -> ssl
|
||||||
end,
|
end,
|
||||||
@ -1010,19 +1001,13 @@ db_opts(Host) ->
|
|||||||
sqlite ->
|
sqlite ->
|
||||||
[sqlite, Host];
|
[sqlite, Host];
|
||||||
_ ->
|
_ ->
|
||||||
Port = ejabberd_config:get_option(
|
Port = ejabberd_option:sql_port(Host),
|
||||||
{sql_port, Host},
|
DB = case ejabberd_option:sql_database(Host) of
|
||||||
case Type of
|
undefined -> <<"ejabberd">>;
|
||||||
mssql -> ?MSSQL_PORT;
|
D -> D
|
||||||
mysql -> ?MYSQL_PORT;
|
end,
|
||||||
pgsql -> ?PGSQL_PORT
|
User = ejabberd_option:sql_username(Host),
|
||||||
end),
|
Pass = ejabberd_option:sql_password(Host),
|
||||||
DB = ejabberd_config:get_option({sql_database, Host},
|
|
||||||
<<"ejabberd">>),
|
|
||||||
User = ejabberd_config:get_option({sql_username, Host},
|
|
||||||
<<"ejabberd">>),
|
|
||||||
Pass = ejabberd_config:get_option({sql_password, Host},
|
|
||||||
<<"">>),
|
|
||||||
SSLOpts = get_ssl_opts(Transport, Host),
|
SSLOpts = get_ssl_opts(Transport, Host),
|
||||||
case Type of
|
case Type of
|
||||||
mssql ->
|
mssql ->
|
||||||
@ -1041,15 +1026,15 @@ warn_if_ssl_unsupported(ssl, Type) ->
|
|||||||
?WARNING_MSG("SSL connection is not supported for ~s", [Type]).
|
?WARNING_MSG("SSL connection is not supported for ~s", [Type]).
|
||||||
|
|
||||||
get_ssl_opts(ssl, Host) ->
|
get_ssl_opts(ssl, Host) ->
|
||||||
Opts1 = case ejabberd_config:get_option({sql_ssl_certfile, Host}) of
|
Opts1 = case ejabberd_option:sql_ssl_certfile(Host) of
|
||||||
undefined -> [];
|
undefined -> [];
|
||||||
CertFile -> [{certfile, CertFile}]
|
CertFile -> [{certfile, CertFile}]
|
||||||
end,
|
end,
|
||||||
Opts2 = case ejabberd_config:get_option({sql_ssl_cafile, Host}) of
|
Opts2 = case ejabberd_option:sql_ssl_cafile(Host) of
|
||||||
undefined -> Opts1;
|
undefined -> Opts1;
|
||||||
CAFile -> [{cacertfile, CAFile}|Opts1]
|
CAFile -> [{cacertfile, CAFile}|Opts1]
|
||||||
end,
|
end,
|
||||||
case ejabberd_config:get_option({sql_ssl_verify, Host}, false) of
|
case ejabberd_option:sql_ssl_verify(Host) of
|
||||||
true ->
|
true ->
|
||||||
case lists:keymember(cacertfile, 1, Opts2) of
|
case lists:keymember(cacertfile, 1, Opts2) of
|
||||||
true ->
|
true ->
|
||||||
@ -1068,9 +1053,12 @@ get_ssl_opts(tcp, _) ->
|
|||||||
[].
|
[].
|
||||||
|
|
||||||
init_mssql(Host) ->
|
init_mssql(Host) ->
|
||||||
Server = ejabberd_config:get_option({sql_server, Host}, <<"localhost">>),
|
Server = ejabberd_option:sql_server(Host),
|
||||||
Port = ejabberd_config:get_option({sql_port, Host}, ?MSSQL_PORT),
|
Port = ejabberd_option:sql_port(Host),
|
||||||
DB = ejabberd_config:get_option({sql_database, Host}, <<"ejabberd">>),
|
DB = case ejabberd_option:sql_database(Host) of
|
||||||
|
undefined -> <<"ejabberd">>;
|
||||||
|
D -> D
|
||||||
|
end,
|
||||||
FreeTDS = io_lib:fwrite("[~s]~n"
|
FreeTDS = io_lib:fwrite("[~s]~n"
|
||||||
"\thost = ~s~n"
|
"\thost = ~s~n"
|
||||||
"\tport = ~p~n"
|
"\tport = ~p~n"
|
||||||
@ -1142,8 +1130,7 @@ fsm_limit_opts() ->
|
|||||||
ejabberd_config:fsm_limit_opts([]).
|
ejabberd_config:fsm_limit_opts([]).
|
||||||
|
|
||||||
query_timeout(LServer) ->
|
query_timeout(LServer) ->
|
||||||
timer:seconds(
|
ejabberd_option:sql_query_timeout(LServer).
|
||||||
ejabberd_config:get_option({sql_query_timeout, LServer}, 60)).
|
|
||||||
|
|
||||||
%% ***IMPORTANT*** This error format requires extended_errors turned on.
|
%% ***IMPORTANT*** This error format requires extended_errors turned on.
|
||||||
extended_error({"08S01", _, Reason}) ->
|
extended_error({"08S01", _, Reason}) ->
|
||||||
@ -1186,31 +1173,3 @@ check_error({error, Why}, Query) ->
|
|||||||
{error, Err};
|
{error, Err};
|
||||||
check_error(Result, _Query) ->
|
check_error(Result, _Query) ->
|
||||||
Result.
|
Result.
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(sql_database) -> fun iolist_to_binary/1;
|
|
||||||
opt_type(sql_keepalive_interval) ->
|
|
||||||
fun (I) when is_integer(I), I > 0 -> I end;
|
|
||||||
opt_type(sql_password) -> fun iolist_to_binary/1;
|
|
||||||
opt_type(sql_port) ->
|
|
||||||
fun (P) when is_integer(P), P > 0, P < 65536 -> P end;
|
|
||||||
opt_type(sql_server) -> fun iolist_to_binary/1;
|
|
||||||
opt_type(sql_username) -> fun iolist_to_binary/1;
|
|
||||||
opt_type(sql_ssl) -> fun(B) when is_boolean(B) -> B end;
|
|
||||||
opt_type(sql_ssl_verify) -> fun(B) when is_boolean(B) -> B end;
|
|
||||||
opt_type(sql_ssl_certfile) -> fun ejabberd_pkix:try_certfile/1;
|
|
||||||
opt_type(sql_ssl_cafile) -> fun ejabberd_pkix:try_certfile/1;
|
|
||||||
opt_type(sql_query_timeout) ->
|
|
||||||
fun (I) when is_integer(I), I > 0 -> I end;
|
|
||||||
opt_type(sql_connect_timeout) ->
|
|
||||||
fun (I) when is_integer(I), I > 0 -> I end;
|
|
||||||
opt_type(sql_queue_type) ->
|
|
||||||
fun(ram) -> ram; (file) -> file end;
|
|
||||||
opt_type(new_sql_schema) -> fun(B) when is_boolean(B) -> B end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[sql_database, sql_keepalive_interval,
|
|
||||||
sql_password, sql_port, sql_server,
|
|
||||||
sql_username, sql_ssl, sql_ssl_verify, sql_ssl_certfile,
|
|
||||||
sql_ssl_cafile, sql_queue_type, sql_query_timeout,
|
|
||||||
sql_connect_timeout,
|
|
||||||
new_sql_schema].
|
|
||||||
|
@ -28,9 +28,7 @@
|
|||||||
%% API
|
%% API
|
||||||
-export([parse_transform/2, format_error/1]).
|
-export([parse_transform/2, format_error/1]).
|
||||||
|
|
||||||
%-export([parse/2]).
|
-include("ejabberd_sql.hrl").
|
||||||
|
|
||||||
-include("ejabberd_sql_pt.hrl").
|
|
||||||
|
|
||||||
-record(state, {loc,
|
-record(state, {loc,
|
||||||
'query' = [],
|
'query' = [],
|
||||||
@ -66,10 +64,8 @@
|
|||||||
%% Description:
|
%% Description:
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
parse_transform(AST, _Options) ->
|
parse_transform(AST, _Options) ->
|
||||||
%io:format("PT: ~p~nOpts: ~p~n", [AST, Options]),
|
|
||||||
put(warnings, []),
|
put(warnings, []),
|
||||||
NewAST = top_transform(AST),
|
NewAST = top_transform(AST),
|
||||||
%io:format("NewPT: ~p~n", [NewAST]),
|
|
||||||
NewAST ++ get(warnings).
|
NewAST ++ get(warnings).
|
||||||
|
|
||||||
|
|
||||||
@ -141,7 +137,6 @@ transform(Form) ->
|
|||||||
case erl_syntax:attribute_arguments(Form) of
|
case erl_syntax:attribute_arguments(Form) of
|
||||||
[M | _] ->
|
[M | _] ->
|
||||||
Module = erl_syntax:atom_value(M),
|
Module = erl_syntax:atom_value(M),
|
||||||
%io:format("module ~p~n", [Module]),
|
|
||||||
put(?MOD, Module),
|
put(?MOD, Module),
|
||||||
Form;
|
Form;
|
||||||
_ ->
|
_ ->
|
||||||
@ -158,11 +153,7 @@ top_transform(Forms) when is_list(Forms) ->
|
|||||||
lists:map(
|
lists:map(
|
||||||
fun(Form) ->
|
fun(Form) ->
|
||||||
try
|
try
|
||||||
Form2 = erl_syntax_lib:map(
|
Form2 = erl_syntax_lib:map(fun transform/1, Form),
|
||||||
fun(Node) ->
|
|
||||||
%io:format("asd ~p~n", [Node]),
|
|
||||||
transform(Node)
|
|
||||||
end, Form),
|
|
||||||
Form3 = erl_syntax:revert(Form2),
|
Form3 = erl_syntax:revert(Form2),
|
||||||
Form3
|
Form3
|
||||||
catch
|
catch
|
||||||
@ -517,7 +508,6 @@ parse_upsert(Fields) ->
|
|||||||
"a constant string"})
|
"a constant string"})
|
||||||
end
|
end
|
||||||
end, {[], 0}, Fields),
|
end, {[], 0}, Fields),
|
||||||
%io:format("upsert ~p~n", [{Fields, Fs}]),
|
|
||||||
Fs.
|
Fs.
|
||||||
|
|
||||||
%% key | {Update}
|
%% key | {Update}
|
||||||
|
@ -25,23 +25,14 @@
|
|||||||
|
|
||||||
-module(ejabberd_sql_sup).
|
-module(ejabberd_sql_sup).
|
||||||
|
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-export([start_link/1, init/1, add_pid/2, remove_pid/2,
|
-export([start_link/1, init/1, add_pid/2, remove_pid/2,
|
||||||
get_pids/1, get_random_pid/1, transform_options/1,
|
get_pids/1, get_random_pid/1, reload/1]).
|
||||||
reload/1, opt_type/1]).
|
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include_lib("stdlib/include/ms_transform.hrl").
|
-include_lib("stdlib/include/ms_transform.hrl").
|
||||||
|
|
||||||
-define(PGSQL_PORT, 5432).
|
|
||||||
-define(MYSQL_PORT, 3306).
|
|
||||||
-define(DEFAULT_POOL_SIZE, 10).
|
|
||||||
-define(DEFAULT_SQL_START_INTERVAL, 30).
|
|
||||||
-define(CONNECT_TIMEOUT, 500).
|
|
||||||
|
|
||||||
-record(sql_pool, {host :: binary(),
|
-record(sql_pool, {host :: binary(),
|
||||||
pid :: pid()}).
|
pid :: pid()}).
|
||||||
|
|
||||||
@ -57,7 +48,7 @@ start_link(Host) ->
|
|||||||
?MODULE, [Host]).
|
?MODULE, [Host]).
|
||||||
|
|
||||||
init([Host]) ->
|
init([Host]) ->
|
||||||
Type = ejabberd_config:get_option({sql_type, Host}, odbc),
|
Type = ejabberd_option:sql_type(Host),
|
||||||
PoolSize = get_pool_size(Type, Host),
|
PoolSize = get_pool_size(Type, Host),
|
||||||
case Type of
|
case Type of
|
||||||
sqlite ->
|
sqlite ->
|
||||||
@ -71,7 +62,7 @@ init([Host]) ->
|
|||||||
[child_spec(I, Host) || I <- lists:seq(1, PoolSize)]}}.
|
[child_spec(I, Host) || I <- lists:seq(1, PoolSize)]}}.
|
||||||
|
|
||||||
reload(Host) ->
|
reload(Host) ->
|
||||||
Type = ejabberd_config:get_option({sql_type, Host}, odbc),
|
Type = ejabberd_option:sql_type(Host),
|
||||||
NewPoolSize = get_pool_size(Type, Host),
|
NewPoolSize = get_pool_size(Type, Host),
|
||||||
OldPoolSize = ets:select_count(
|
OldPoolSize = ets:select_count(
|
||||||
sql_pool,
|
sql_pool,
|
||||||
@ -125,12 +116,7 @@ remove_pid(Host, Pid) ->
|
|||||||
|
|
||||||
-spec get_pool_size(atom(), binary()) -> pos_integer().
|
-spec get_pool_size(atom(), binary()) -> pos_integer().
|
||||||
get_pool_size(SQLType, Host) ->
|
get_pool_size(SQLType, Host) ->
|
||||||
PoolSize = ejabberd_config:get_option(
|
PoolSize = ejabberd_option:sql_pool_size(Host),
|
||||||
{sql_pool_size, Host},
|
|
||||||
case SQLType of
|
|
||||||
sqlite -> 1;
|
|
||||||
_ -> ?DEFAULT_POOL_SIZE
|
|
||||||
end),
|
|
||||||
if PoolSize > 1 andalso SQLType == sqlite ->
|
if PoolSize > 1 andalso SQLType == sqlite ->
|
||||||
?WARNING_MSG("it's not recommended to set sql_pool_size > 1 for "
|
?WARNING_MSG("it's not recommended to set sql_pool_size > 1 for "
|
||||||
"sqlite, because it may cause race conditions", []);
|
"sqlite, because it may cause race conditions", []);
|
||||||
@ -140,31 +126,10 @@ get_pool_size(SQLType, Host) ->
|
|||||||
PoolSize.
|
PoolSize.
|
||||||
|
|
||||||
child_spec(I, Host) ->
|
child_spec(I, Host) ->
|
||||||
StartInterval = ejabberd_config:get_option(
|
StartInterval = ejabberd_option:sql_start_interval(Host),
|
||||||
{sql_start_interval, Host},
|
{I, {ejabberd_sql, start_link, [Host, StartInterval]},
|
||||||
?DEFAULT_SQL_START_INTERVAL),
|
|
||||||
{I, {ejabberd_sql, start_link, [Host, timer:seconds(StartInterval)]},
|
|
||||||
transient, 2000, worker, [?MODULE]}.
|
transient, 2000, worker, [?MODULE]}.
|
||||||
|
|
||||||
transform_options(Opts) ->
|
|
||||||
lists:foldl(fun transform_options/2, [], Opts).
|
|
||||||
|
|
||||||
transform_options({odbc_server, {Type, Server, Port, DB, User, Pass}}, Opts) ->
|
|
||||||
[{sql_type, Type},
|
|
||||||
{sql_server, Server},
|
|
||||||
{sql_port, Port},
|
|
||||||
{sql_database, DB},
|
|
||||||
{sql_username, User},
|
|
||||||
{sql_password, Pass}|Opts];
|
|
||||||
transform_options({odbc_server, {mysql, Server, DB, User, Pass}}, Opts) ->
|
|
||||||
transform_options({odbc_server, {mysql, Server, ?MYSQL_PORT, DB, User, Pass}}, Opts);
|
|
||||||
transform_options({odbc_server, {pgsql, Server, DB, User, Pass}}, Opts) ->
|
|
||||||
transform_options({odbc_server, {pgsql, Server, ?PGSQL_PORT, DB, User, Pass}}, Opts);
|
|
||||||
transform_options({odbc_server, {sqlite, DB}}, Opts) ->
|
|
||||||
transform_options({odbc_server, {sqlite, DB}}, Opts);
|
|
||||||
transform_options(Opt, Opts) ->
|
|
||||||
[Opt|Opts].
|
|
||||||
|
|
||||||
check_sqlite_db(Host) ->
|
check_sqlite_db(Host) ->
|
||||||
DB = ejabberd_sql:sqlite_db(Host),
|
DB = ejabberd_sql:sqlite_db(Host),
|
||||||
File = ejabberd_sql:sqlite_file(Host),
|
File = ejabberd_sql:sqlite_file(Host),
|
||||||
@ -234,11 +199,3 @@ read_lines(Fd, File, Acc) ->
|
|||||||
?ERROR_MSG("Failed read from lite.sql, reason: ~p", [Err]),
|
?ERROR_MSG("Failed read from lite.sql, reason: ~p", [Err]),
|
||||||
[]
|
[]
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(sql_pool_size) ->
|
|
||||||
fun (I) when is_integer(I), I > 0 -> I end;
|
|
||||||
opt_type(sql_start_interval) ->
|
|
||||||
fun (I) when is_integer(I), I > 0 -> I end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[sql_pool_size, sql_start_interval].
|
|
||||||
|
@ -57,6 +57,7 @@ tcp_init(Socket, Opts) ->
|
|||||||
ejabberd:start_app(stun),
|
ejabberd:start_app(stun),
|
||||||
stun:tcp_init(Socket, prepare_turn_opts(Opts)).
|
stun:tcp_init(Socket, prepare_turn_opts(Opts)).
|
||||||
|
|
||||||
|
-dialyzer({nowarn_function, udp_init/2}).
|
||||||
udp_init(Socket, Opts) ->
|
udp_init(Socket, Opts) ->
|
||||||
ejabberd:start_app(stun),
|
ejabberd:start_app(stun),
|
||||||
stun:udp_init(Socket, prepare_turn_opts(Opts)).
|
stun:udp_init(Socket, prepare_turn_opts(Opts)).
|
||||||
@ -83,11 +84,11 @@ prepare_turn_opts(Opts) ->
|
|||||||
prepare_turn_opts(Opts, _UseTurn = false) ->
|
prepare_turn_opts(Opts, _UseTurn = false) ->
|
||||||
set_certfile(Opts);
|
set_certfile(Opts);
|
||||||
prepare_turn_opts(Opts, _UseTurn = true) ->
|
prepare_turn_opts(Opts, _UseTurn = true) ->
|
||||||
NumberOfMyHosts = length(ejabberd_config:get_myhosts()),
|
NumberOfMyHosts = length(ejabberd_option:hosts()),
|
||||||
case proplists:get_value(turn_ip, Opts) of
|
case proplists:get_value(turn_ip, Opts) of
|
||||||
undefined ->
|
undefined ->
|
||||||
?WARNING_MSG("option 'turn_ip' is undefined, "
|
?WARNING_MSG("Option 'turn_ip' is undefined, "
|
||||||
"more likely the TURN relay won't be working "
|
"most likely the TURN relay won't be working "
|
||||||
"properly", []);
|
"properly", []);
|
||||||
_ ->
|
_ ->
|
||||||
ok
|
ok
|
||||||
@ -98,11 +99,11 @@ prepare_turn_opts(Opts, _UseTurn = true) ->
|
|||||||
Realm = case proplists:get_value(auth_realm, Opts) of
|
Realm = case proplists:get_value(auth_realm, Opts) of
|
||||||
undefined when AuthType == user ->
|
undefined when AuthType == user ->
|
||||||
if NumberOfMyHosts > 1 ->
|
if NumberOfMyHosts > 1 ->
|
||||||
?WARNING_MSG("you have several virtual "
|
?WARNING_MSG("You have several virtual "
|
||||||
"hosts configured, but option "
|
"hosts configured, but option "
|
||||||
"'auth_realm' is undefined and "
|
"'auth_realm' is undefined and "
|
||||||
"'auth_type' is set to 'user', "
|
"'auth_type' is set to 'user', "
|
||||||
"more likely the TURN relay won't "
|
"most likely the TURN relay won't "
|
||||||
"be working properly. Using ~s as "
|
"be working properly. Using ~s as "
|
||||||
"a fallback", [ejabberd_config:get_myname()]);
|
"a fallback", [ejabberd_config:get_myname()]);
|
||||||
true ->
|
true ->
|
||||||
@ -127,44 +128,32 @@ set_certfile(Opts) ->
|
|||||||
{ok, CertFile} ->
|
{ok, CertFile} ->
|
||||||
[{certfile, CertFile}|Opts];
|
[{certfile, CertFile}|Opts];
|
||||||
error ->
|
error ->
|
||||||
case ejabberd_config:get_option({domain_certfile, Realm}) of
|
Opts
|
||||||
undefined ->
|
|
||||||
Opts;
|
|
||||||
CertFile ->
|
|
||||||
[{certfile, CertFile}|Opts]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
listen_opt_type(use_turn) ->
|
listen_opt_type(use_turn) ->
|
||||||
fun(B) when is_boolean(B) -> B end;
|
econf:bool();
|
||||||
|
listen_opt_type(ip) ->
|
||||||
|
econf:ipv4();
|
||||||
listen_opt_type(turn_ip) ->
|
listen_opt_type(turn_ip) ->
|
||||||
fun(S) ->
|
econf:ipv4();
|
||||||
{ok, Addr} = inet_parse:ipv4_address(binary_to_list(S)),
|
|
||||||
Addr
|
|
||||||
end;
|
|
||||||
listen_opt_type(auth_type) ->
|
listen_opt_type(auth_type) ->
|
||||||
fun(anonymous) -> anonymous;
|
econf:enum([anonymous, user]);
|
||||||
(user) -> user
|
|
||||||
end;
|
|
||||||
listen_opt_type(auth_realm) ->
|
listen_opt_type(auth_realm) ->
|
||||||
fun iolist_to_binary/1;
|
econf:binary();
|
||||||
listen_opt_type(turn_min_port) ->
|
listen_opt_type(turn_min_port) ->
|
||||||
fun(P) when is_integer(P), P > 1024, P < 65536 -> P end;
|
econf:int(1025, 65535);
|
||||||
listen_opt_type(turn_max_port) ->
|
listen_opt_type(turn_max_port) ->
|
||||||
fun(P) when is_integer(P), P > 1024, P < 65536 -> P end;
|
econf:int(1025, 65535);
|
||||||
listen_opt_type(turn_max_allocations) ->
|
listen_opt_type(turn_max_allocations) ->
|
||||||
fun(I) when is_integer(I), I>0 -> I;
|
econf:pos_int(infinity);
|
||||||
(unlimited) -> infinity;
|
|
||||||
(infinity) -> infinity
|
|
||||||
end;
|
|
||||||
listen_opt_type(turn_max_permissions) ->
|
listen_opt_type(turn_max_permissions) ->
|
||||||
fun(I) when is_integer(I), I>0 -> I;
|
econf:pos_int(infinity);
|
||||||
(unlimited) -> infinity;
|
|
||||||
(infinity) -> infinity
|
|
||||||
end;
|
|
||||||
listen_opt_type(server_name) ->
|
listen_opt_type(server_name) ->
|
||||||
fun iolist_to_binary/1.
|
econf:binary();
|
||||||
|
listen_opt_type(certfile) ->
|
||||||
|
econf:pem().
|
||||||
|
|
||||||
listen_options() ->
|
listen_options() ->
|
||||||
[{shaper, none},
|
[{shaper, none},
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
-export([start_link/0, init/1]).
|
-export([start_link/0, init/1]).
|
||||||
|
|
||||||
-define(SHUTDOWN_TIMEOUT, timer:seconds(30)).
|
-define(SHUTDOWN_TIMEOUT, timer:minutes(1)).
|
||||||
|
|
||||||
start_link() ->
|
start_link() ->
|
||||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||||
@ -53,10 +53,9 @@ init([]) ->
|
|||||||
simple_supervisor(ejabberd_service),
|
simple_supervisor(ejabberd_service),
|
||||||
worker(acl),
|
worker(acl),
|
||||||
worker(ejabberd_shaper),
|
worker(ejabberd_shaper),
|
||||||
|
supervisor(ejabberd_db_sup),
|
||||||
supervisor(ejabberd_backend_sup),
|
supervisor(ejabberd_backend_sup),
|
||||||
supervisor(ejabberd_rdbms),
|
supervisor(ejabberd_rdbms),
|
||||||
supervisor(ejabberd_riak_sup),
|
|
||||||
supervisor(ejabberd_redis_sup),
|
|
||||||
worker(ejabberd_iq),
|
worker(ejabberd_iq),
|
||||||
worker(ejabberd_router),
|
worker(ejabberd_router),
|
||||||
worker(ejabberd_router_multicast),
|
worker(ejabberd_router_multicast),
|
||||||
|
@ -25,13 +25,12 @@
|
|||||||
|
|
||||||
-module(ejabberd_system_monitor).
|
-module(ejabberd_system_monitor).
|
||||||
-behaviour(gen_event).
|
-behaviour(gen_event).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
-author('ekhramtsov@process-one.net').
|
-author('ekhramtsov@process-one.net').
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([start/0, opt_type/1, config_reloaded/0]).
|
-export([start/0, config_reloaded/0]).
|
||||||
|
|
||||||
%% gen_event callbacks
|
%% gen_event callbacks
|
||||||
-export([init/1, handle_event/2, handle_call/2,
|
-export([init/1, handle_event/2, handle_call/2,
|
||||||
@ -43,8 +42,8 @@
|
|||||||
|
|
||||||
-define(CHECK_INTERVAL, timer:seconds(30)).
|
-define(CHECK_INTERVAL, timer:seconds(30)).
|
||||||
|
|
||||||
-record(state, {tref :: reference(),
|
-record(state, {tref :: undefined | reference(),
|
||||||
mref :: reference()}).
|
mref :: undefined | reference()}).
|
||||||
-record(proc_stat, {qlen :: non_neg_integer(),
|
-record(proc_stat, {qlen :: non_neg_integer(),
|
||||||
memory :: non_neg_integer(),
|
memory :: non_neg_integer(),
|
||||||
initial_call :: mfa(),
|
initial_call :: mfa(),
|
||||||
@ -134,7 +133,7 @@ handle_overload(State) ->
|
|||||||
handle_overload(_State, Procs) ->
|
handle_overload(_State, Procs) ->
|
||||||
AppPids = get_app_pids(),
|
AppPids = get_app_pids(),
|
||||||
{TotalMsgs, ProcsNum, Apps, Stats} = overloaded_procs(AppPids, Procs),
|
{TotalMsgs, ProcsNum, Apps, Stats} = overloaded_procs(AppPids, Procs),
|
||||||
MaxMsgs = ejabberd_config:get_option(oom_queue, 10000),
|
MaxMsgs = ejabberd_option:oom_queue(),
|
||||||
if TotalMsgs >= MaxMsgs ->
|
if TotalMsgs >= MaxMsgs ->
|
||||||
SortedStats = lists:reverse(lists:keysort(#proc_stat.qlen, Stats)),
|
SortedStats = lists:reverse(lists:keysort(#proc_stat.qlen, Stats)),
|
||||||
error_logger:warning_msg(
|
error_logger:warning_msg(
|
||||||
@ -224,14 +223,14 @@ restart_timer(State) ->
|
|||||||
TRef = erlang:start_timer(?CHECK_INTERVAL, self(), handle_overload),
|
TRef = erlang:start_timer(?CHECK_INTERVAL, self(), handle_overload),
|
||||||
State#state{tref = TRef}.
|
State#state{tref = TRef}.
|
||||||
|
|
||||||
-spec format_apps(dict:dict()) -> io:data().
|
-spec format_apps(dict:dict()) -> iodata().
|
||||||
format_apps(Apps) ->
|
format_apps(Apps) ->
|
||||||
AppList = lists:reverse(lists:keysort(2, dict:to_list(Apps))),
|
AppList = lists:reverse(lists:keysort(2, dict:to_list(Apps))),
|
||||||
string:join(
|
string:join(
|
||||||
[io_lib:format("~p (~b msgs)", [App, Msgs]) || {App, Msgs} <- AppList],
|
[io_lib:format("~p (~b msgs)", [App, Msgs]) || {App, Msgs} <- AppList],
|
||||||
", ").
|
", ").
|
||||||
|
|
||||||
-spec format_top_procs([proc_stat()]) -> io:data().
|
-spec format_top_procs([proc_stat()]) -> iodata().
|
||||||
format_top_procs(Stats) ->
|
format_top_procs(Stats) ->
|
||||||
Stats1 = lists:sublist(Stats, 5),
|
Stats1 = lists:sublist(Stats, 5),
|
||||||
string:join(
|
string:join(
|
||||||
@ -241,7 +240,7 @@ format_top_procs(Stats) ->
|
|||||||
end,Stats1),
|
end,Stats1),
|
||||||
io_lib:nl()).
|
io_lib:nl()).
|
||||||
|
|
||||||
-spec format_proc(proc_stat()) -> io:data().
|
-spec format_proc(proc_stat()) -> iodata().
|
||||||
format_proc(#proc_stat{qlen = Len, memory = Mem, initial_call = InitCall,
|
format_proc(#proc_stat{qlen = Len, memory = Mem, initial_call = InitCall,
|
||||||
current_function = CurrFun, ancestors = Ancs,
|
current_function = CurrFun, ancestors = Ancs,
|
||||||
application = App}) ->
|
application = App}) ->
|
||||||
@ -250,7 +249,7 @@ format_proc(#proc_stat{qlen = Len, memory = Mem, initial_call = InitCall,
|
|||||||
"current_function = ~s, ancestors = ~w, application = ~w",
|
"current_function = ~s, ancestors = ~w, application = ~w",
|
||||||
[Len, Mem, format_mfa(InitCall), format_mfa(CurrFun), Ancs, App]).
|
[Len, Mem, format_mfa(InitCall), format_mfa(CurrFun), Ancs, App]).
|
||||||
|
|
||||||
-spec format_mfa(mfa()) -> io:data().
|
-spec format_mfa(mfa()) -> iodata().
|
||||||
format_mfa({M, F, A}) when is_atom(M), is_atom(F), is_integer(A) ->
|
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("~s:~s/~b", [M, F, A]);
|
||||||
format_mfa(WTF) ->
|
format_mfa(WTF) ->
|
||||||
@ -258,7 +257,7 @@ format_mfa(WTF) ->
|
|||||||
|
|
||||||
-spec kill([proc_stat()], non_neg_integer()) -> ok.
|
-spec kill([proc_stat()], non_neg_integer()) -> ok.
|
||||||
kill(Stats, Threshold) ->
|
kill(Stats, Threshold) ->
|
||||||
case ejabberd_config:get_option(oom_killer, true) of
|
case ejabberd_option:oom_killer() of
|
||||||
true ->
|
true ->
|
||||||
do_kill(Stats, Threshold);
|
do_kill(Stats, Threshold);
|
||||||
false ->
|
false ->
|
||||||
@ -308,7 +307,7 @@ kill_proc(Pid) ->
|
|||||||
|
|
||||||
-spec set_oom_watermark() -> ok.
|
-spec set_oom_watermark() -> ok.
|
||||||
set_oom_watermark() ->
|
set_oom_watermark() ->
|
||||||
WaterMark = ejabberd_config:get_option(oom_watermark, 80),
|
WaterMark = ejabberd_option:oom_watermark(),
|
||||||
memsup:set_sysmem_high_watermark(WaterMark/100).
|
memsup:set_sysmem_high_watermark(WaterMark/100).
|
||||||
|
|
||||||
-spec maybe_restart_app(atom()) -> any().
|
-spec maybe_restart_app(atom()) -> any().
|
||||||
@ -316,11 +315,3 @@ maybe_restart_app(lager) ->
|
|||||||
ejabberd_logger:restart();
|
ejabberd_logger:restart();
|
||||||
maybe_restart_app(_) ->
|
maybe_restart_app(_) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
opt_type(oom_killer) ->
|
|
||||||
fun(B) when is_boolean(B) -> B end;
|
|
||||||
opt_type(oom_watermark) ->
|
|
||||||
fun(I) when is_integer(I), I>0, I<100 -> I end;
|
|
||||||
opt_type(oom_queue) ->
|
|
||||||
fun(I) when is_integer(I), I>0 -> I end;
|
|
||||||
opt_type(_) -> [oom_killer, oom_watermark, oom_queue].
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -37,12 +37,11 @@
|
|||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
-module(ejabberd_websocket).
|
-module(ejabberd_websocket).
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
-protocol({rfc, 6455}).
|
-protocol({rfc, 6455}).
|
||||||
|
|
||||||
-author('ecestari@process-one.net').
|
-author('ecestari@process-one.net').
|
||||||
|
|
||||||
-export([socket_handoff/5, opt_type/1]).
|
-export([socket_handoff/5]).
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
@ -429,27 +428,4 @@ websocket_close(Socket, WsHandleLoopPid, SocketMode, _CloseCode) ->
|
|||||||
SocketMode:close(Socket).
|
SocketMode:close(Socket).
|
||||||
|
|
||||||
get_origin() ->
|
get_origin() ->
|
||||||
ejabberd_config:get_option(websocket_origin, []).
|
ejabberd_option:websocket_origin().
|
||||||
|
|
||||||
opt_type(websocket_ping_interval) ->
|
|
||||||
fun (I) when is_integer(I), I >= 0 -> I end;
|
|
||||||
opt_type(websocket_timeout) ->
|
|
||||||
fun (I) when is_integer(I), I > 0 -> I end;
|
|
||||||
opt_type(websocket_origin) ->
|
|
||||||
fun Verify(V) when is_binary(V) ->
|
|
||||||
Verify([V]);
|
|
||||||
Verify([]) ->
|
|
||||||
[];
|
|
||||||
Verify([<<"null">> | R]) ->
|
|
||||||
[<<"null">> | Verify(R)];
|
|
||||||
Verify([null | R]) ->
|
|
||||||
[<<"null">> | Verify(R)];
|
|
||||||
Verify([V | R]) when is_binary(V) ->
|
|
||||||
URIs = [_|_] = lists:filtermap(
|
|
||||||
fun(<<>>) -> false;
|
|
||||||
(URI) -> {true, misc:try_url(URI)}
|
|
||||||
end, re:split(V, "\\s+")),
|
|
||||||
[str:join(URIs, <<" ">>) | Verify(R)]
|
|
||||||
end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[websocket_ping_interval, websocket_timeout, websocket_origin].
|
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
-author('badlop@process-one.net').
|
-author('badlop@process-one.net').
|
||||||
|
|
||||||
-export([start/3, start_link/3, handler/2, process/2, accept/1,
|
-export([start/3, start_link/3, handler/2, process/2, accept/1,
|
||||||
transform_listen_option/2, listen_opt_type/1, listen_options/0]).
|
listen_opt_type/1, listen_options/0]).
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("ejabberd_http.hrl").
|
-include("ejabberd_http.hrl").
|
||||||
@ -233,7 +233,8 @@ process(_, _) ->
|
|||||||
%% -----------------------------
|
%% -----------------------------
|
||||||
%% Access verification
|
%% Access verification
|
||||||
%% -----------------------------
|
%% -----------------------------
|
||||||
|
-spec extract_auth([{user | server | token | password, binary()}]) ->
|
||||||
|
map() | {error, not_found | expired | invalid_auth}.
|
||||||
extract_auth(AuthList) ->
|
extract_auth(AuthList) ->
|
||||||
?DEBUG("AUTHLIST ~p", [AuthList]),
|
?DEBUG("AUTHLIST ~p", [AuthList]),
|
||||||
try get_attrs([user, server, token], AuthList) of
|
try get_attrs([user, server, token], AuthList) of
|
||||||
@ -306,10 +307,6 @@ handler(#state{get_auth = true, auth = noauth, ip = IP} = State,
|
|||||||
build_fault_response(-118,
|
build_fault_response(-118,
|
||||||
"Invalid oauth token",
|
"Invalid oauth token",
|
||||||
[]);
|
[]);
|
||||||
{error, Value} ->
|
|
||||||
build_fault_response(-118,
|
|
||||||
"Invalid authentication data: ~p",
|
|
||||||
[Value]);
|
|
||||||
Auth ->
|
Auth ->
|
||||||
handler(State#state{get_auth = false, auth = Auth#{ip => IP, caller_module => ?MODULE}},
|
handler(State#state{get_auth = false, auth = Auth#{ip => IP, caller_module => ?MODULE}},
|
||||||
{call, Method, Arguments})
|
{call, Method, Arguments})
|
||||||
@ -341,15 +338,10 @@ handler(_State,
|
|||||||
handler(State, {call, Command, []}) ->
|
handler(State, {call, Command, []}) ->
|
||||||
handler(State, {call, Command, [{struct, []}]});
|
handler(State, {call, Command, [{struct, []}]});
|
||||||
handler(State,
|
handler(State,
|
||||||
{call, Command, [{struct, AttrL}]} = Payload) ->
|
{call, Command, [{struct, AttrL}]}) ->
|
||||||
case ejabberd_commands:get_command_format(Command, State#state.auth) of
|
{ArgsF, ResultF} = ejabberd_commands:get_command_format(Command, State#state.auth),
|
||||||
{error, command_unknown} ->
|
|
||||||
build_fault_response(-112, "Unknown call: ~p",
|
|
||||||
[Payload]);
|
|
||||||
{ArgsF, ResultF} ->
|
|
||||||
try_do_command(State#state.access_commands,
|
try_do_command(State#state.access_commands,
|
||||||
State#state.auth, Command, AttrL, ArgsF, ResultF)
|
State#state.auth, Command, AttrL, ArgsF, ResultF);
|
||||||
end;
|
|
||||||
%% If no other guard matches
|
%% If no other guard matches
|
||||||
handler(_State, Payload) ->
|
handler(_State, Payload) ->
|
||||||
build_fault_response(-112, "Unknown call: ~p",
|
build_fault_response(-112, "Unknown call: ~p",
|
||||||
@ -400,14 +392,8 @@ build_fault_response(Code, ParseString, ParseArgs) ->
|
|||||||
do_command(AccessCommands, Auth, Command, AttrL, ArgsF,
|
do_command(AccessCommands, Auth, Command, AttrL, ArgsF,
|
||||||
ResultF) ->
|
ResultF) ->
|
||||||
ArgsFormatted = format_args(AttrL, ArgsF),
|
ArgsFormatted = format_args(AttrL, ArgsF),
|
||||||
Auth2 = case AccessCommands of
|
Auth2 = Auth#{extra_permissions => AccessCommands},
|
||||||
V when is_list(V) ->
|
Result = ejabberd_commands:execute_command2(Command, ArgsFormatted, Auth2),
|
||||||
Auth#{extra_permissions => AccessCommands};
|
|
||||||
_ ->
|
|
||||||
Auth
|
|
||||||
end,
|
|
||||||
Result =
|
|
||||||
ejabberd_commands:execute_command2(Command, ArgsFormatted, Auth2),
|
|
||||||
ResultFormatted = format_result(Result, ResultF),
|
ResultFormatted = format_result(Result, ResultF),
|
||||||
{command_result, ResultFormatted}.
|
{command_result, ResultFormatted}.
|
||||||
|
|
||||||
@ -555,17 +541,6 @@ make_status(false) -> 1;
|
|||||||
make_status(error) -> 1;
|
make_status(error) -> 1;
|
||||||
make_status(_) -> 1.
|
make_status(_) -> 1.
|
||||||
|
|
||||||
transform_listen_option({access_commands, ACOpts}, Opts) ->
|
|
||||||
NewACOpts = lists:map(
|
|
||||||
fun({AName, ACmds, AOpts}) ->
|
|
||||||
{AName, [{commands, ACmds}, {options, AOpts}]};
|
|
||||||
(Opt) ->
|
|
||||||
Opt
|
|
||||||
end, ACOpts),
|
|
||||||
[{access_commands, NewACOpts}|Opts];
|
|
||||||
transform_listen_option(Opt, Opts) ->
|
|
||||||
[Opt|Opts].
|
|
||||||
|
|
||||||
listen_opt_type(access_commands) ->
|
listen_opt_type(access_commands) ->
|
||||||
fun(Opts) ->
|
fun(Opts) ->
|
||||||
lists:map(
|
lists:map(
|
||||||
|
@ -25,14 +25,12 @@
|
|||||||
|
|
||||||
-module(eldap_utils).
|
-module(eldap_utils).
|
||||||
|
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
-author('mremond@process-one.net').
|
-author('mremond@process-one.net').
|
||||||
|
|
||||||
-export([generate_subfilter/1, find_ldap_attrs/2, check_filter/1,
|
-export([generate_subfilter/1, find_ldap_attrs/2, check_filter/1,
|
||||||
get_ldap_attr/2, get_user_part/2, make_filter/2,
|
get_ldap_attr/2, get_user_part/2, make_filter/2,
|
||||||
get_state/2, case_insensitive_match/2, get_config/2,
|
get_state/2, case_insensitive_match/2,
|
||||||
decode_octet_string/3, uids_domain_subst/2, opt_type/1,
|
decode_octet_string/3, uids_domain_subst/2]).
|
||||||
options/1]).
|
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("eldap.hrl").
|
-include("eldap.hrl").
|
||||||
@ -170,62 +168,6 @@ uids_domain_subst(Host, UIDs) ->
|
|||||||
end,
|
end,
|
||||||
UIDs).
|
UIDs).
|
||||||
|
|
||||||
-spec get_config(binary(), list()) -> eldap_config().
|
|
||||||
|
|
||||||
get_config(Host, Opts) ->
|
|
||||||
Servers = get_opt(ldap_servers, Host, Opts, [<<"localhost">>]),
|
|
||||||
Backups = get_opt(ldap_backups, Host, Opts, []),
|
|
||||||
Encrypt = get_opt(ldap_encrypt, Host, Opts, none),
|
|
||||||
TLSVerify = get_opt(ldap_tls_verify, Host, Opts, false),
|
|
||||||
TLSCertFile = get_opt(ldap_tls_certfile, Host, Opts),
|
|
||||||
TLSCAFile = get_opt(ldap_tls_cacertfile, Host, Opts),
|
|
||||||
TLSDepth = get_opt(ldap_tls_depth, Host, Opts),
|
|
||||||
Port = case get_opt(ldap_port, Host, Opts) of
|
|
||||||
undefined ->
|
|
||||||
case Encrypt of
|
|
||||||
tls -> ?LDAPS_PORT;
|
|
||||||
starttls -> ?LDAP_PORT;
|
|
||||||
_ -> ?LDAP_PORT
|
|
||||||
end;
|
|
||||||
P ->
|
|
||||||
P
|
|
||||||
end,
|
|
||||||
RootDN = get_opt(ldap_rootdn, Host, Opts, <<"">>),
|
|
||||||
Password = get_opt(ldap_password, Host, Opts, <<"">>),
|
|
||||||
Base = get_opt(ldap_base, Host, Opts, <<"">>),
|
|
||||||
OldDerefAliases = get_opt(deref_aliases, Host, Opts, unspecified),
|
|
||||||
DerefAliases =
|
|
||||||
if OldDerefAliases == unspecified ->
|
|
||||||
get_opt(ldap_deref_aliases, Host, Opts, never);
|
|
||||||
true ->
|
|
||||||
?WARNING_MSG("Option 'deref_aliases' is deprecated. "
|
|
||||||
"The option is still supported "
|
|
||||||
"but it is better to fix your config: "
|
|
||||||
"use 'ldap_deref_aliases' instead.", []),
|
|
||||||
OldDerefAliases
|
|
||||||
end,
|
|
||||||
#eldap_config{servers = Servers,
|
|
||||||
backups = Backups,
|
|
||||||
tls_options = [{encrypt, Encrypt},
|
|
||||||
{tls_verify, TLSVerify},
|
|
||||||
{tls_certfile, TLSCertFile},
|
|
||||||
{tls_cacertfile, TLSCAFile},
|
|
||||||
{tls_depth, TLSDepth}],
|
|
||||||
port = Port,
|
|
||||||
dn = RootDN,
|
|
||||||
password = Password,
|
|
||||||
base = Base,
|
|
||||||
deref_aliases = DerefAliases}.
|
|
||||||
|
|
||||||
get_opt(Opt, Host, Opts) ->
|
|
||||||
get_opt(Opt, Host, Opts, undefined).
|
|
||||||
|
|
||||||
get_opt(Opt, Host, Opts, Default) ->
|
|
||||||
case proplists:get_value(Opt, Opts) of
|
|
||||||
undefined -> ejabberd_config:get_option({Opt, Host}, Default);
|
|
||||||
Value -> Value
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%----------------------------------------
|
%%----------------------------------------
|
||||||
%% Borrowed from asn1rt_ber_bin_v2.erl
|
%% Borrowed from asn1rt_ber_bin_v2.erl
|
||||||
%%----------------------------------------
|
%%----------------------------------------
|
||||||
@ -332,87 +274,3 @@ collect_parts_bit([{?N_BIT_STRING,<<Unused,Bits/binary>>}|Rest],Acc,Uacc) ->
|
|||||||
collect_parts_bit(Rest,[Bits|Acc],Unused+Uacc);
|
collect_parts_bit(Rest,[Bits|Acc],Unused+Uacc);
|
||||||
collect_parts_bit([],Acc,Uacc) ->
|
collect_parts_bit([],Acc,Uacc) ->
|
||||||
list_to_binary([Uacc|lists:reverse(Acc)]).
|
list_to_binary([Uacc|lists:reverse(Acc)]).
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(deref_aliases) ->
|
|
||||||
fun(unspecified) -> unspecified;
|
|
||||||
(never) -> never;
|
|
||||||
(searching) -> searching;
|
|
||||||
(finding) -> finding;
|
|
||||||
(always) -> always
|
|
||||||
end;
|
|
||||||
opt_type(ldap_backups) ->
|
|
||||||
fun (L) -> [iolist_to_binary(H) || H <- L] end;
|
|
||||||
opt_type(ldap_base) -> fun iolist_to_binary/1;
|
|
||||||
opt_type(ldap_deref_aliases) ->
|
|
||||||
fun (never) -> never;
|
|
||||||
(searching) -> searching;
|
|
||||||
(finding) -> finding;
|
|
||||||
(always) -> always
|
|
||||||
end;
|
|
||||||
opt_type(ldap_encrypt) ->
|
|
||||||
fun (tls) -> tls;
|
|
||||||
(starttls) -> starttls;
|
|
||||||
(none) -> none
|
|
||||||
end;
|
|
||||||
opt_type(ldap_password) -> fun iolist_to_binary/1;
|
|
||||||
opt_type(ldap_port) ->
|
|
||||||
fun(undefined) -> undefined;
|
|
||||||
(I) when is_integer(I), I > 0 -> I
|
|
||||||
end;
|
|
||||||
opt_type(ldap_rootdn) -> fun iolist_to_binary/1;
|
|
||||||
opt_type(ldap_servers) ->
|
|
||||||
fun (L) -> [iolist_to_binary(H) || H <- L] end;
|
|
||||||
opt_type(ldap_tls_certfile) ->
|
|
||||||
fun(undefined) -> undefined;
|
|
||||||
(S) -> binary_to_list(ejabberd_pkix:try_certfile(S))
|
|
||||||
end;
|
|
||||||
opt_type(ldap_tls_cacertfile) ->
|
|
||||||
fun(undefined) -> undefined;
|
|
||||||
(S) -> binary_to_list(misc:try_read_file(S))
|
|
||||||
end;
|
|
||||||
opt_type(ldap_tls_depth) ->
|
|
||||||
fun(undefined) -> undefined;
|
|
||||||
(I) when is_integer(I), I >= 0 -> I
|
|
||||||
end;
|
|
||||||
opt_type(ldap_tls_verify) ->
|
|
||||||
fun (hard) -> hard;
|
|
||||||
(soft) -> soft;
|
|
||||||
(false) -> false
|
|
||||||
end;
|
|
||||||
opt_type(ldap_filter) ->
|
|
||||||
fun(<<"">>) -> <<"">>;
|
|
||||||
(F) -> check_filter(F)
|
|
||||||
end;
|
|
||||||
opt_type(ldap_uids) ->
|
|
||||||
fun (Us) ->
|
|
||||||
lists:map(fun ({U, P}) ->
|
|
||||||
{iolist_to_binary(U), iolist_to_binary(P)};
|
|
||||||
({U}) -> {iolist_to_binary(U)};
|
|
||||||
(U) -> {iolist_to_binary(U)}
|
|
||||||
end,
|
|
||||||
lists:flatten(Us))
|
|
||||||
end;
|
|
||||||
opt_type(_) ->
|
|
||||||
[deref_aliases, ldap_backups, ldap_base, ldap_uids,
|
|
||||||
ldap_deref_aliases, ldap_encrypt, ldap_password,
|
|
||||||
ldap_port, ldap_rootdn, ldap_servers, ldap_filter,
|
|
||||||
ldap_tls_certfile, ldap_tls_cacertfile, ldap_tls_depth,
|
|
||||||
ldap_tls_verify].
|
|
||||||
|
|
||||||
options(_) ->
|
|
||||||
[{deref_aliases, unspecified},
|
|
||||||
{ldap_backups, []},
|
|
||||||
{ldap_base, <<"">>},
|
|
||||||
{ldap_uids, [{<<"uid">>, <<"%u">>}]},
|
|
||||||
{ldap_deref_aliases, never},
|
|
||||||
{ldap_encrypt, none},
|
|
||||||
{ldap_password, <<"">>},
|
|
||||||
{ldap_port, undefined},
|
|
||||||
{ldap_rootdn, <<"">>},
|
|
||||||
{ldap_servers, [<<"localhost">>]},
|
|
||||||
{ldap_filter, <<"">>},
|
|
||||||
{ldap_tls_certfile, undefined},
|
|
||||||
{ldap_tls_cacertfile, undefined},
|
|
||||||
{ldap_tls_depth, undefined},
|
|
||||||
{ldap_tls_verify, false}].
|
|
||||||
|
@ -22,9 +22,10 @@
|
|||||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
%%%
|
%%%
|
||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
|
|
||||||
-module(elixir_logger_backend).
|
-module(elixir_logger_backend).
|
||||||
|
|
||||||
|
-ifdef(ELIXIR_ENABLED).
|
||||||
|
|
||||||
-behaviour(gen_event).
|
-behaviour(gen_event).
|
||||||
|
|
||||||
-export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,
|
-export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,
|
||||||
@ -120,3 +121,5 @@ severity_to_level(error) -> error;
|
|||||||
severity_to_level(critical) -> error;
|
severity_to_level(critical) -> error;
|
||||||
severity_to_level(alert) -> error;
|
severity_to_level(alert) -> error;
|
||||||
severity_to_level(emergency) -> error.
|
severity_to_level(emergency) -> error.
|
||||||
|
|
||||||
|
-endif.
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
-module(ext_mod).
|
-module(ext_mod).
|
||||||
|
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
-author("Christophe Romain <christophe.romain@process-one.net>").
|
-author("Christophe Romain <christophe.romain@process-one.net>").
|
||||||
|
|
||||||
@ -34,7 +33,7 @@
|
|||||||
installed_command/0, installed/0, installed/1,
|
installed_command/0, installed/0, installed/1,
|
||||||
install/1, uninstall/1, upgrade/0, upgrade/1, add_paths/0,
|
install/1, uninstall/1, upgrade/0, upgrade/1, add_paths/0,
|
||||||
add_sources/1, add_sources/2, del_sources/1, modules_dir/0,
|
add_sources/1, add_sources/2, del_sources/1, modules_dir/0,
|
||||||
config_dir/0, opt_type/1, get_commands_spec/0]).
|
config_dir/0, get_commands_spec/0]).
|
||||||
|
|
||||||
-export([compile_erlang_file/2, compile_elixir_file/2]).
|
-export([compile_erlang_file/2, compile_elixir_file/2]).
|
||||||
|
|
||||||
@ -221,7 +220,7 @@ install(Package) when is_binary(Package) ->
|
|||||||
case compile_and_install(Module, Attrs) of
|
case compile_and_install(Module, Attrs) of
|
||||||
ok ->
|
ok ->
|
||||||
code:add_patha(module_ebin_dir(Module)),
|
code:add_patha(module_ebin_dir(Module)),
|
||||||
ejabberd_config:reload_file(),
|
ejabberd_config:reload(),
|
||||||
case erlang:function_exported(Module, post_install, 0) of
|
case erlang:function_exported(Module, post_install, 0) of
|
||||||
true -> Module:post_install();
|
true -> Module:post_install();
|
||||||
_ -> ok
|
_ -> ok
|
||||||
@ -243,12 +242,12 @@ uninstall(Package) when is_binary(Package) ->
|
|||||||
_ -> ok
|
_ -> ok
|
||||||
end,
|
end,
|
||||||
[catch gen_mod:stop_module(Host, Module)
|
[catch gen_mod:stop_module(Host, Module)
|
||||||
|| Host <- ejabberd_config:get_myhosts()],
|
|| Host <- ejabberd_option:hosts()],
|
||||||
code:purge(Module),
|
code:purge(Module),
|
||||||
code:delete(Module),
|
code:delete(Module),
|
||||||
code:del_path(module_ebin_dir(Module)),
|
code:del_path(module_ebin_dir(Module)),
|
||||||
delete_path(module_lib_dir(Module)),
|
delete_path(module_lib_dir(Module)),
|
||||||
ejabberd_config:reload_file();
|
ejabberd_config:reload();
|
||||||
false ->
|
false ->
|
||||||
{error, not_installed}
|
{error, not_installed}
|
||||||
end.
|
end.
|
||||||
@ -464,7 +463,7 @@ short_spec({Module, Attrs}) when is_atom(Module), is_list(Attrs) ->
|
|||||||
{Module, proplists:get_value(summary, Attrs, "")}.
|
{Module, proplists:get_value(summary, Attrs, "")}.
|
||||||
|
|
||||||
is_contrib_allowed() ->
|
is_contrib_allowed() ->
|
||||||
ejabberd_config:get_option(allow_contrib_modules, true).
|
ejabberd_option:allow_contrib_modules().
|
||||||
|
|
||||||
%% -- build functions
|
%% -- build functions
|
||||||
|
|
||||||
@ -597,6 +596,7 @@ compile_erlang_file(Dest, File, ErlOptions) ->
|
|||||||
{error, E, W} -> {error, {compilation_failed, File, E, W}}
|
{error, E, W} -> {error, {compilation_failed, File, E, W}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-ifdef(ELIXIR_ENABLED).
|
||||||
compile_elixir_file(Dest, File) when is_list(Dest) and is_list(File) ->
|
compile_elixir_file(Dest, File) when is_list(Dest) and is_list(File) ->
|
||||||
compile_elixir_file(list_to_binary(Dest), list_to_binary(File));
|
compile_elixir_file(list_to_binary(Dest), list_to_binary(File));
|
||||||
|
|
||||||
@ -606,6 +606,10 @@ compile_elixir_file(Dest, File) ->
|
|||||||
catch
|
catch
|
||||||
_ -> {error, {compilation_failed, File}}
|
_ -> {error, {compilation_failed, File}}
|
||||||
end.
|
end.
|
||||||
|
-else.
|
||||||
|
compile_elixir_file(_, File) ->
|
||||||
|
{error, {compilation_failed, File}}.
|
||||||
|
-endif.
|
||||||
|
|
||||||
install(Module, Spec, SrcDir, LibDir) ->
|
install(Module, Spec, SrcDir, LibDir) ->
|
||||||
{ok, CurDir} = file:get_cwd(),
|
{ok, CurDir} = file:get_cwd(),
|
||||||
@ -682,11 +686,3 @@ format({Key, Val}) when is_binary(Val) ->
|
|||||||
{Key, binary_to_list(Val)};
|
{Key, binary_to_list(Val)};
|
||||||
format({Key, Val}) -> % TODO: improve Yaml parsing
|
format({Key, Val}) -> % TODO: improve Yaml parsing
|
||||||
{Key, Val}.
|
{Key, Val}.
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(allow_contrib_modules) ->
|
|
||||||
fun (false) -> false;
|
|
||||||
(no) -> false;
|
|
||||||
(_) -> true
|
|
||||||
end;
|
|
||||||
opt_type(_) -> [allow_contrib_modules].
|
|
||||||
|
@ -78,11 +78,11 @@ remove_user(User, Server, Password) ->
|
|||||||
|
|
||||||
-spec prog_name(binary()) -> string() | undefined.
|
-spec prog_name(binary()) -> string() | undefined.
|
||||||
prog_name(Host) ->
|
prog_name(Host) ->
|
||||||
ejabberd_config:get_option({extauth_program, Host}).
|
ejabberd_option:extauth_program(Host).
|
||||||
|
|
||||||
-spec pool_name(binary()) -> atom().
|
-spec pool_name(binary()) -> atom().
|
||||||
pool_name(Host) ->
|
pool_name(Host) ->
|
||||||
case ejabberd_config:get_option({extauth_pool_name, Host}) of
|
case ejabberd_option:extauth_pool_name(Host) of
|
||||||
undefined ->
|
undefined ->
|
||||||
list_to_atom("extauth_pool_" ++ binary_to_list(Host));
|
list_to_atom("extauth_pool_" ++ binary_to_list(Host));
|
||||||
Name ->
|
Name ->
|
||||||
@ -95,7 +95,7 @@ worker_name(Pool, N) ->
|
|||||||
|
|
||||||
-spec pool_size(binary()) -> pos_integer().
|
-spec pool_size(binary()) -> pos_integer().
|
||||||
pool_size(Host) ->
|
pool_size(Host) ->
|
||||||
case ejabberd_config:get_option({extauth_pool_size, Host}) of
|
case ejabberd_option:extauth_pool_size(Host) of
|
||||||
undefined ->
|
undefined ->
|
||||||
try erlang:system_info(logical_processors)
|
try erlang:system_info(logical_processors)
|
||||||
catch _:_ -> 1
|
catch _:_ -> 1
|
||||||
|
@ -27,12 +27,9 @@
|
|||||||
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-behaviour(ejabberd_config).
|
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([add_iq_handler/5, remove_iq_handler/3, handle/1, handle/2,
|
-export([add_iq_handler/5, remove_iq_handler/3, handle/1, handle/2,
|
||||||
check_type/1, transform_module_options/1,
|
start/1, get_features/2]).
|
||||||
opt_type/1, start/1, get_features/2]).
|
|
||||||
%% Deprecated functions
|
%% Deprecated functions
|
||||||
-export([add_iq_handler/6, handle/5, iqdisc/1]).
|
-export([add_iq_handler/6, handle/5, iqdisc/1]).
|
||||||
-deprecated([{add_iq_handler, 6}, {handle, 5}, {iqdisc, 1}]).
|
-deprecated([{add_iq_handler, 6}, {handle, 5}, {iqdisc, 1}]).
|
||||||
@ -135,29 +132,10 @@ process_iq(Module, Function, #iq{lang = Lang, sub_els = [El]} = IQ) ->
|
|||||||
xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang))
|
xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec check_type(any()) -> no_queue.
|
|
||||||
check_type(_Type) ->
|
|
||||||
?WARNING_MSG("Option 'iqdisc' is deprecated and has no effect anymore", []),
|
|
||||||
no_queue.
|
|
||||||
|
|
||||||
-spec iqdisc(binary() | global) -> no_queue.
|
-spec iqdisc(binary() | global) -> no_queue.
|
||||||
iqdisc(_Host) ->
|
iqdisc(_Host) ->
|
||||||
no_queue.
|
no_queue.
|
||||||
|
|
||||||
-spec transform_module_options([{atom(), any()}]) -> [{atom(), any()}].
|
|
||||||
|
|
||||||
transform_module_options(Opts) ->
|
|
||||||
lists:map(
|
|
||||||
fun({iqdisc, {queues, N}}) ->
|
|
||||||
{iqdisc, N};
|
|
||||||
(Opt) ->
|
|
||||||
Opt
|
|
||||||
end, Opts).
|
|
||||||
|
|
||||||
-spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
|
|
||||||
opt_type(iqdisc) -> fun check_type/1;
|
|
||||||
opt_type(_) -> [iqdisc].
|
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% Deprecated API
|
%% Deprecated API
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
|
905
src/gen_mod.erl
905
src/gen_mod.erl
File diff suppressed because it is too large
Load Diff
@ -184,7 +184,7 @@
|
|||||||
{result, {[pubsubItem()], undefined | rsm_set()}}.
|
{result, {[pubsubItem()], undefined | rsm_set()}}.
|
||||||
|
|
||||||
-callback get_last_items(nodeIdx(), jid(), undefined | rsm_set()) ->
|
-callback get_last_items(nodeIdx(), jid(), undefined | rsm_set()) ->
|
||||||
{result, {[pubsubItem()], undefined | rsm_set()}}.
|
{result, [pubsubItem()]}.
|
||||||
|
|
||||||
-callback get_item(NodeIdx :: nodeIdx(),
|
-callback get_item(NodeIdx :: nodeIdx(),
|
||||||
ItemId :: itemId(),
|
ItemId :: itemId(),
|
||||||
|
@ -96,7 +96,7 @@
|
|||||||
Parents :: [nodeId()]) ->
|
Parents :: [nodeId()]) ->
|
||||||
{ok, NodeIdx::nodeIdx()} |
|
{ok, NodeIdx::nodeIdx()} |
|
||||||
{error, stanza_error()} |
|
{error, stanza_error()} |
|
||||||
{error, {virtual, {host(), nodeId()}}}.
|
{error, {virtual, {host(), nodeId()} | nodeId()}}.
|
||||||
|
|
||||||
-callback delete_node(Host :: host(),
|
-callback delete_node(Host :: host(),
|
||||||
NodeId :: nodeId()) ->
|
NodeId :: nodeId()) ->
|
||||||
|
113
src/misc.erl
113
src/misc.erl
@ -39,7 +39,8 @@
|
|||||||
css_dir/0, img_dir/0, js_dir/0, msgs_dir/0, sql_dir/0, lua_dir/0,
|
css_dir/0, img_dir/0, js_dir/0, msgs_dir/0, sql_dir/0, lua_dir/0,
|
||||||
read_css/1, read_img/1, read_js/1, read_lua/1, try_url/1,
|
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,
|
intersection/2, format_val/1, cancel_timer/1, unique_timestamp/0,
|
||||||
is_mucsub_message/1, best_match/2]).
|
is_mucsub_message/1, best_match/2, pmap/2, peach/2, format_exception/4,
|
||||||
|
parse_ip_mask/1, match_ip_mask/3]).
|
||||||
|
|
||||||
%% Deprecated functions
|
%% Deprecated functions
|
||||||
-export([decode_base64/1, encode_base64/1]).
|
-export([decode_base64/1, encode_base64/1]).
|
||||||
@ -206,10 +207,10 @@ hex_to_base64(Hex) ->
|
|||||||
url_encode(A) ->
|
url_encode(A) ->
|
||||||
url_encode(A, <<>>).
|
url_encode(A, <<>>).
|
||||||
|
|
||||||
-spec expand_keyword(binary(), binary(), binary()) -> binary().
|
-spec expand_keyword(iodata(), iodata(), iodata()) -> binary().
|
||||||
expand_keyword(Keyword, Input, Replacement) ->
|
expand_keyword(Keyword, Input, Replacement) ->
|
||||||
Parts = binary:split(Input, Keyword, [global]),
|
re:replace(Input, Keyword, Replacement,
|
||||||
str:join(Parts, Replacement).
|
[{return, binary}, global]).
|
||||||
|
|
||||||
binary_to_atom(Bin) ->
|
binary_to_atom(Bin) ->
|
||||||
erlang:binary_to_atom(Bin, utf8).
|
erlang:binary_to_atom(Bin, utf8).
|
||||||
@ -412,7 +413,7 @@ format_val(Term) ->
|
|||||||
_ -> [io_lib:nl(), S]
|
_ -> [io_lib:nl(), S]
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec cancel_timer(reference()) -> ok.
|
-spec cancel_timer(reference() | undefined) -> ok.
|
||||||
cancel_timer(TRef) when is_reference(TRef) ->
|
cancel_timer(TRef) when is_reference(TRef) ->
|
||||||
case erlang:cancel_timer(TRef) of
|
case erlang:cancel_timer(TRef) of
|
||||||
false ->
|
false ->
|
||||||
@ -425,18 +426,106 @@ cancel_timer(TRef) when is_reference(TRef) ->
|
|||||||
cancel_timer(_) ->
|
cancel_timer(_) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
-spec best_match(atom(), [atom()]) -> atom().
|
-spec best_match(atom(), [atom()]) -> atom();
|
||||||
|
(binary(), [binary()]) -> binary().
|
||||||
best_match(Pattern, []) ->
|
best_match(Pattern, []) ->
|
||||||
Pattern;
|
Pattern;
|
||||||
best_match(Pattern, Opts) ->
|
best_match(Pattern, Opts) ->
|
||||||
String = atom_to_list(Pattern),
|
F = if is_atom(Pattern) -> fun atom_to_list/1;
|
||||||
|
is_binary(Pattern) -> fun binary_to_list/1
|
||||||
|
end,
|
||||||
|
String = F(Pattern),
|
||||||
{Ds, _} = lists:mapfoldl(
|
{Ds, _} = lists:mapfoldl(
|
||||||
fun(Opt, Cache) ->
|
fun(Opt, Cache) ->
|
||||||
{Distance, Cache1} = ld(String, atom_to_list(Opt), Cache),
|
{Distance, Cache1} = ld(String, F(Opt), Cache),
|
||||||
{{Distance, Opt}, Cache1}
|
{{Distance, Opt}, Cache1}
|
||||||
end, #{}, Opts),
|
end, #{}, Opts),
|
||||||
element(2, lists:min(Ds)).
|
element(2, lists:min(Ds)).
|
||||||
|
|
||||||
|
-spec pmap(fun((T1) -> T2), [T1]) -> [T2].
|
||||||
|
pmap(Fun, [_,_|_] = List) ->
|
||||||
|
Self = self(),
|
||||||
|
lists:map(
|
||||||
|
fun({Pid, Ref}) ->
|
||||||
|
receive
|
||||||
|
{Pid, Ret} ->
|
||||||
|
receive
|
||||||
|
{'DOWN', Ref, _, _, _} ->
|
||||||
|
Ret
|
||||||
|
end;
|
||||||
|
{'DOWN', Ref, _, _, Reason} ->
|
||||||
|
exit(Reason)
|
||||||
|
end
|
||||||
|
end, [spawn_monitor(
|
||||||
|
fun() -> Self ! {self(), Fun(X)} end)
|
||||||
|
|| X <- List]);
|
||||||
|
pmap(Fun, List) ->
|
||||||
|
lists:map(Fun, List).
|
||||||
|
|
||||||
|
-spec peach(fun((T) -> any()), [T]) -> ok.
|
||||||
|
peach(Fun, [_,_|_] = List) ->
|
||||||
|
Self = self(),
|
||||||
|
lists:foreach(
|
||||||
|
fun({Pid, Ref}) ->
|
||||||
|
receive
|
||||||
|
Pid ->
|
||||||
|
receive
|
||||||
|
{'DOWN', Ref, _, _, _} ->
|
||||||
|
ok
|
||||||
|
end;
|
||||||
|
{'DOWN', Ref, _, _, Reason} ->
|
||||||
|
exit(Reason)
|
||||||
|
end
|
||||||
|
end, [spawn_monitor(
|
||||||
|
fun() -> Fun(X), Self ! self() end)
|
||||||
|
|| X <- List]);
|
||||||
|
peach(Fun, List) ->
|
||||||
|
lists:foreach(Fun, List).
|
||||||
|
|
||||||
|
format_exception(Level, Class, Reason, Stacktrace) ->
|
||||||
|
erl_error:format_exception(
|
||||||
|
Level, Class, Reason, Stacktrace,
|
||||||
|
fun(_M, _F, _A) -> false end,
|
||||||
|
fun(Term, I) ->
|
||||||
|
io_lib:print(Term, I, 80, -1)
|
||||||
|
end).
|
||||||
|
|
||||||
|
-spec parse_ip_mask(binary()) -> {ok, {inet:ip4_address(), 0..32}} |
|
||||||
|
{ok, {inet:ip6_address(), 0..128}} |
|
||||||
|
error.
|
||||||
|
parse_ip_mask(S) ->
|
||||||
|
case econf:validate(econf:ip_mask(), S) of
|
||||||
|
{ok, _} = Ret -> Ret;
|
||||||
|
_ -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec match_ip_mask(inet:ip_address(), inet:ip_address(), 0..128) -> boolean().
|
||||||
|
match_ip_mask({_, _, _, _} = IP, {_, _, _, _} = Net, Mask) ->
|
||||||
|
IPInt = ip_to_integer(IP),
|
||||||
|
NetInt = ip_to_integer(Net),
|
||||||
|
M = bnot (1 bsl (32 - Mask) - 1),
|
||||||
|
IPInt band M =:= NetInt band M;
|
||||||
|
match_ip_mask({_, _, _, _, _, _, _, _} = IP,
|
||||||
|
{_, _, _, _, _, _, _, _} = Net, Mask) ->
|
||||||
|
IPInt = ip_to_integer(IP),
|
||||||
|
NetInt = ip_to_integer(Net),
|
||||||
|
M = bnot (1 bsl (128 - Mask) - 1),
|
||||||
|
IPInt band M =:= NetInt band M;
|
||||||
|
match_ip_mask({_, _, _, _} = IP,
|
||||||
|
{0, 0, 0, 0, 0, 16#FFFF, _, _} = Net, Mask) ->
|
||||||
|
IPInt = ip_to_integer({0, 0, 0, 0, 0, 16#FFFF, 0, 0}) + ip_to_integer(IP),
|
||||||
|
NetInt = ip_to_integer(Net),
|
||||||
|
M = bnot (1 bsl (128 - Mask) - 1),
|
||||||
|
IPInt band M =:= NetInt band M;
|
||||||
|
match_ip_mask({0, 0, 0, 0, 0, 16#FFFF, _, _} = IP,
|
||||||
|
{_, _, _, _} = Net, Mask) ->
|
||||||
|
IPInt = ip_to_integer(IP) - ip_to_integer({0, 0, 0, 0, 0, 16#FFFF, 0, 0}),
|
||||||
|
NetInt = ip_to_integer(Net),
|
||||||
|
M = bnot (1 bsl (32 - Mask) - 1),
|
||||||
|
IPInt band M =:= NetInt band M;
|
||||||
|
match_ip_mask(_, _, _) ->
|
||||||
|
false.
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
@ -515,3 +604,11 @@ ld([_|ST] = S, [_|TT] = T, Cache) ->
|
|||||||
L = 1 + lists:min([L1, L2, L3]),
|
L = 1 + lists:min([L1, L2, L3]),
|
||||||
{L, maps:put({S, T}, L, C3)}
|
{L, maps:put({S, T}, L, C3)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec ip_to_integer(inet:ip_address()) -> non_neg_integer().
|
||||||
|
ip_to_integer({IP1, IP2, IP3, IP4}) ->
|
||||||
|
IP1 bsl 8 bor IP2 bsl 8 bor IP3 bsl 8 bor IP4;
|
||||||
|
ip_to_integer({IP1, IP2, IP3, IP4, IP5, IP6, IP7,
|
||||||
|
IP8}) ->
|
||||||
|
IP1 bsl 16 bor IP2 bsl 16 bor IP3 bsl 16 bor IP4 bsl 16
|
||||||
|
bor IP5 bsl 16 bor IP6 bsl 16 bor IP7 bsl 16 bor IP8.
|
||||||
|
@ -93,8 +93,7 @@ reload(_Host, _NewOpts, _OldOpts) ->
|
|||||||
get_local_commands(Acc, _From,
|
get_local_commands(Acc, _From,
|
||||||
#jid{server = Server, lserver = LServer} = _To, <<"">>,
|
#jid{server = Server, lserver = LServer} = _To, <<"">>,
|
||||||
Lang) ->
|
Lang) ->
|
||||||
Display = gen_mod:get_module_opt(LServer, ?MODULE,
|
Display = mod_adhoc_opt:report_commands_node(LServer),
|
||||||
report_commands_node),
|
|
||||||
case Display of
|
case Display of
|
||||||
false -> Acc;
|
false -> Acc;
|
||||||
_ ->
|
_ ->
|
||||||
@ -121,8 +120,7 @@ get_local_commands(Acc, _From, _To, _Node, _Lang) ->
|
|||||||
|
|
||||||
get_sm_commands(Acc, _From,
|
get_sm_commands(Acc, _From,
|
||||||
#jid{lserver = LServer} = To, <<"">>, Lang) ->
|
#jid{lserver = LServer} = To, <<"">>, Lang) ->
|
||||||
Display = gen_mod:get_module_opt(LServer, ?MODULE,
|
Display = mod_adhoc_opt:report_commands_node(LServer),
|
||||||
report_commands_node),
|
|
||||||
case Display of
|
case Display of
|
||||||
false -> Acc;
|
false -> Acc;
|
||||||
_ ->
|
_ ->
|
||||||
@ -275,7 +273,7 @@ depends(_Host, _Opts) ->
|
|||||||
[].
|
[].
|
||||||
|
|
||||||
mod_opt_type(report_commands_node) ->
|
mod_opt_type(report_commands_node) ->
|
||||||
fun (B) when is_boolean(B) -> B end.
|
econf:bool().
|
||||||
|
|
||||||
mod_options(_Host) ->
|
mod_options(_Host) ->
|
||||||
[{report_commands_node, false}].
|
[{report_commands_node, false}].
|
||||||
|
13
src/mod_adhoc_opt.erl
Normal file
13
src/mod_adhoc_opt.erl
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
%% Generated automatically
|
||||||
|
%% DO NOT EDIT: run `make options` instead
|
||||||
|
|
||||||
|
-module(mod_adhoc_opt).
|
||||||
|
|
||||||
|
-export([report_commands_node/1]).
|
||||||
|
|
||||||
|
-spec report_commands_node(gen_mod:opts() | global | binary()) -> boolean().
|
||||||
|
report_commands_node(Opts) when is_map(Opts) ->
|
||||||
|
gen_mod:get_opt(report_commands_node, Opts);
|
||||||
|
report_commands_node(Host) ->
|
||||||
|
gen_mod:get_module_opt(Host, mod_adhoc, report_commands_node).
|
||||||
|
|
@ -1534,7 +1534,7 @@ stats(Name) ->
|
|||||||
case Name of
|
case Name of
|
||||||
<<"uptimeseconds">> -> trunc(element(1, erlang:statistics(wall_clock))/1000);
|
<<"uptimeseconds">> -> trunc(element(1, erlang:statistics(wall_clock))/1000);
|
||||||
<<"processes">> -> length(erlang:processes());
|
<<"processes">> -> length(erlang:processes());
|
||||||
<<"registeredusers">> -> lists:foldl(fun(Host, Sum) -> ejabberd_auth:count_users(Host) + Sum end, 0, ejabberd_config:get_myhosts());
|
<<"registeredusers">> -> lists:foldl(fun(Host, Sum) -> ejabberd_auth:count_users(Host) + Sum end, 0, ejabberd_option:hosts());
|
||||||
<<"onlineusersnode">> -> length(ejabberd_sm:dirty_get_my_sessions_list());
|
<<"onlineusersnode">> -> length(ejabberd_sm:dirty_get_my_sessions_list());
|
||||||
<<"onlineusers">> -> length(ejabberd_sm:dirty_get_sessions_list())
|
<<"onlineusers">> -> length(ejabberd_sm:dirty_get_sessions_list())
|
||||||
end.
|
end.
|
||||||
|
@ -81,7 +81,7 @@ update_sql() ->
|
|||||||
_ ->
|
_ ->
|
||||||
update_sql(Host)
|
update_sql(Host)
|
||||||
end
|
end
|
||||||
end, ejabberd_config:get_myhosts()),
|
end, ejabberd_option:hosts()),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
-record(state, {host :: binary(),
|
-record(state, {host :: binary(),
|
||||||
@ -90,7 +90,7 @@ update_sql() ->
|
|||||||
|
|
||||||
update_sql(Host) ->
|
update_sql(Host) ->
|
||||||
LHost = jid:nameprep(Host),
|
LHost = jid:nameprep(Host),
|
||||||
DBType = ejabberd_config:get_option({sql_type, LHost}, undefined),
|
DBType = ejabberd_option:sql_type(LHost),
|
||||||
IsSupported =
|
IsSupported =
|
||||||
case DBType of
|
case DBType of
|
||||||
pgsql -> true;
|
pgsql -> true;
|
||||||
|
@ -85,8 +85,8 @@ stop(Host) ->
|
|||||||
gen_mod:stop_child(?MODULE, Host).
|
gen_mod:stop_child(?MODULE, Host).
|
||||||
|
|
||||||
reload(Host, NewOpts, OldOpts) ->
|
reload(Host, NewOpts, OldOpts) ->
|
||||||
NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE),
|
NewMod = gen_mod:db_mod(NewOpts, ?MODULE),
|
||||||
OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE),
|
OldMod = gen_mod:db_mod(OldOpts, ?MODULE),
|
||||||
if NewMod /= OldMod ->
|
if NewMod /= OldMod ->
|
||||||
NewMod:init(Host, NewOpts);
|
NewMod:init(Host, NewOpts);
|
||||||
true ->
|
true ->
|
||||||
@ -102,7 +102,7 @@ depends(_Host, _Opts) ->
|
|||||||
%%====================================================================
|
%%====================================================================
|
||||||
init([Host, Opts]) ->
|
init([Host, Opts]) ->
|
||||||
process_flag(trap_exit, true),
|
process_flag(trap_exit, true),
|
||||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
Mod = gen_mod:db_mod(Opts, ?MODULE),
|
||||||
Mod:init(Host, Opts),
|
Mod:init(Host, Opts),
|
||||||
init_cache(Mod, Host, Opts),
|
init_cache(Mod, Host, Opts),
|
||||||
ejabberd_hooks:add(local_send_to_resource_hook, Host,
|
ejabberd_hooks:add(local_send_to_resource_hook, Host,
|
||||||
@ -663,7 +663,7 @@ announce_motd(#message{to = To} = Packet) ->
|
|||||||
announce_motd(To#jid.lserver, Packet).
|
announce_motd(To#jid.lserver, Packet).
|
||||||
|
|
||||||
announce_all_hosts_motd(Packet) ->
|
announce_all_hosts_motd(Packet) ->
|
||||||
Hosts = ejabberd_config:get_myhosts(),
|
Hosts = ejabberd_option:hosts(),
|
||||||
[announce_motd(Host, Packet) || Host <- Hosts].
|
[announce_motd(Host, Packet) || Host <- Hosts].
|
||||||
|
|
||||||
announce_motd(Host, Packet) ->
|
announce_motd(Host, Packet) ->
|
||||||
@ -678,7 +678,7 @@ announce_motd_update(#message{to = To} = Packet) ->
|
|||||||
announce_motd_update(To#jid.lserver, Packet).
|
announce_motd_update(To#jid.lserver, Packet).
|
||||||
|
|
||||||
announce_all_hosts_motd_update(Packet) ->
|
announce_all_hosts_motd_update(Packet) ->
|
||||||
Hosts = ejabberd_config:get_myhosts(),
|
Hosts = ejabberd_option:hosts(),
|
||||||
[announce_motd_update(Host, Packet) || Host <- Hosts].
|
[announce_motd_update(Host, Packet) || Host <- Hosts].
|
||||||
|
|
||||||
announce_motd_update(LServer, Packet) ->
|
announce_motd_update(LServer, Packet) ->
|
||||||
@ -696,7 +696,7 @@ announce_all_hosts_motd_delete(_Packet) ->
|
|||||||
fun(Host) ->
|
fun(Host) ->
|
||||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||||
delete_motd(Mod, Host)
|
delete_motd(Mod, Host)
|
||||||
end, ejabberd_config:get_myhosts()).
|
end, ejabberd_option:hosts()).
|
||||||
|
|
||||||
-spec send_motd({presence(), ejabberd_c2s:state()}) -> {presence(), ejabberd_c2s:state()}.
|
-spec send_motd({presence(), ejabberd_c2s:state()}) -> {presence(), ejabberd_c2s:state()}.
|
||||||
send_motd({_, #{pres_last := _}} = Acc) ->
|
send_motd({_, #{pres_last := _}} = Acc) ->
|
||||||
@ -829,7 +829,7 @@ send_announcement_to_all(Host, SubjectS, BodyS) ->
|
|||||||
-spec get_access(global | binary()) -> atom().
|
-spec get_access(global | binary()) -> atom().
|
||||||
|
|
||||||
get_access(Host) ->
|
get_access(Host) ->
|
||||||
gen_mod:get_module_opt(Host, ?MODULE, access).
|
mod_announce_opt:access(Host).
|
||||||
|
|
||||||
-spec add_store_hint(stanza()) -> stanza().
|
-spec add_store_hint(stanza()) -> stanza().
|
||||||
add_store_hint(El) ->
|
add_store_hint(El) ->
|
||||||
@ -853,9 +853,9 @@ init_cache(Mod, Host, Opts) ->
|
|||||||
|
|
||||||
-spec cache_opts(gen_mod:opts()) -> [proplists:property()].
|
-spec cache_opts(gen_mod:opts()) -> [proplists:property()].
|
||||||
cache_opts(Opts) ->
|
cache_opts(Opts) ->
|
||||||
MaxSize = gen_mod:get_opt(cache_size, Opts),
|
MaxSize = mod_announce_opt:cache_size(Opts),
|
||||||
CacheMissed = gen_mod:get_opt(cache_missed, Opts),
|
CacheMissed = mod_announce_opt:cache_missed(Opts),
|
||||||
LifeTime = case gen_mod:get_opt(cache_life_time, Opts) of
|
LifeTime = case mod_announce_opt:cache_life_time(Opts) of
|
||||||
infinity -> infinity;
|
infinity -> infinity;
|
||||||
I -> timer:seconds(I)
|
I -> timer:seconds(I)
|
||||||
end,
|
end,
|
||||||
@ -865,7 +865,7 @@ cache_opts(Opts) ->
|
|||||||
use_cache(Mod, Host) ->
|
use_cache(Mod, Host) ->
|
||||||
case erlang:function_exported(Mod, use_cache, 1) of
|
case erlang:function_exported(Mod, use_cache, 1) of
|
||||||
true -> Mod:use_cache(Host);
|
true -> Mod:use_cache(Host);
|
||||||
false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
|
false -> mod_announce_opt:use_cache(Host)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec cache_nodes(module(), binary()) -> [node()].
|
-spec cache_nodes(module(), binary()) -> [node()].
|
||||||
@ -897,19 +897,23 @@ import(LServer, {sql, _}, DBType, Tab, List) ->
|
|||||||
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||||
Mod:import(LServer, Tab, List).
|
Mod:import(LServer, Tab, List).
|
||||||
|
|
||||||
mod_opt_type(access) -> fun acl:access_rules_validator/1;
|
mod_opt_type(access) ->
|
||||||
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
econf:acl();
|
||||||
mod_opt_type(O) when O == cache_life_time; O == cache_size ->
|
mod_opt_type(db_type) ->
|
||||||
fun (I) when is_integer(I), I > 0 -> I;
|
econf:well_known(db_type, ?MODULE);
|
||||||
(infinity) -> infinity
|
mod_opt_type(use_cache) ->
|
||||||
end;
|
econf:well_known(use_cache, ?MODULE);
|
||||||
mod_opt_type(O) when O == use_cache; O == cache_missed ->
|
mod_opt_type(cache_size) ->
|
||||||
fun (B) when is_boolean(B) -> B end.
|
econf:well_known(cache_size, ?MODULE);
|
||||||
|
mod_opt_type(cache_missed) ->
|
||||||
|
econf:well_known(cache_missed, ?MODULE);
|
||||||
|
mod_opt_type(cache_life_time) ->
|
||||||
|
econf:well_known(cache_life_time, ?MODULE).
|
||||||
|
|
||||||
mod_options(Host) ->
|
mod_options(Host) ->
|
||||||
[{access, none},
|
[{access, none},
|
||||||
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
|
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
|
||||||
{use_cache, ejabberd_config:use_cache(Host)},
|
{use_cache, ejabberd_option:use_cache(Host)},
|
||||||
{cache_size, ejabberd_config:cache_size(Host)},
|
{cache_size, ejabberd_option:cache_size(Host)},
|
||||||
{cache_missed, ejabberd_config:cache_missed(Host)},
|
{cache_missed, ejabberd_option:cache_missed(Host)},
|
||||||
{cache_life_time, ejabberd_config:cache_life_time(Host)}].
|
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
|
||||||
|
@ -98,10 +98,10 @@ set_motd_user(LUser, LServer) ->
|
|||||||
end,
|
end,
|
||||||
transaction(F).
|
transaction(F).
|
||||||
|
|
||||||
need_transform(#motd{server = S}) when is_list(S) ->
|
need_transform({motd, S, _}) when is_list(S) ->
|
||||||
?INFO_MSG("Mnesia table 'motd' will be converted to binary", []),
|
?INFO_MSG("Mnesia table 'motd' will be converted to binary", []),
|
||||||
true;
|
true;
|
||||||
need_transform(#motd_users{us = {U, S}}) when is_list(U) orelse is_list(S) ->
|
need_transform({motd_users, {U, S}, _}) when is_list(U) orelse is_list(S) ->
|
||||||
?INFO_MSG("Mnesia table 'motd_users' will be converted to binary", []),
|
?INFO_MSG("Mnesia table 'motd_users' will be converted to binary", []),
|
||||||
true;
|
true;
|
||||||
need_transform(_) ->
|
need_transform(_) ->
|
||||||
|
48
src/mod_announce_opt.erl
Normal file
48
src/mod_announce_opt.erl
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
%% Generated automatically
|
||||||
|
%% DO NOT EDIT: run `make options` instead
|
||||||
|
|
||||||
|
-module(mod_announce_opt).
|
||||||
|
|
||||||
|
-export([access/1]).
|
||||||
|
-export([cache_life_time/1]).
|
||||||
|
-export([cache_missed/1]).
|
||||||
|
-export([cache_size/1]).
|
||||||
|
-export([db_type/1]).
|
||||||
|
-export([use_cache/1]).
|
||||||
|
|
||||||
|
-spec access(gen_mod:opts() | global | binary()) -> 'none' | acl:acl().
|
||||||
|
access(Opts) when is_map(Opts) ->
|
||||||
|
gen_mod:get_opt(access, Opts);
|
||||||
|
access(Host) ->
|
||||||
|
gen_mod:get_module_opt(Host, mod_announce, access).
|
||||||
|
|
||||||
|
-spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
|
||||||
|
cache_life_time(Opts) when is_map(Opts) ->
|
||||||
|
gen_mod:get_opt(cache_life_time, Opts);
|
||||||
|
cache_life_time(Host) ->
|
||||||
|
gen_mod:get_module_opt(Host, mod_announce, cache_life_time).
|
||||||
|
|
||||||
|
-spec cache_missed(gen_mod:opts() | global | binary()) -> boolean().
|
||||||
|
cache_missed(Opts) when is_map(Opts) ->
|
||||||
|
gen_mod:get_opt(cache_missed, Opts);
|
||||||
|
cache_missed(Host) ->
|
||||||
|
gen_mod:get_module_opt(Host, mod_announce, cache_missed).
|
||||||
|
|
||||||
|
-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
|
||||||
|
cache_size(Opts) when is_map(Opts) ->
|
||||||
|
gen_mod:get_opt(cache_size, Opts);
|
||||||
|
cache_size(Host) ->
|
||||||
|
gen_mod:get_module_opt(Host, mod_announce, cache_size).
|
||||||
|
|
||||||
|
-spec db_type(gen_mod:opts() | global | binary()) -> atom().
|
||||||
|
db_type(Opts) when is_map(Opts) ->
|
||||||
|
gen_mod:get_opt(db_type, Opts);
|
||||||
|
db_type(Host) ->
|
||||||
|
gen_mod:get_module_opt(Host, mod_announce, db_type).
|
||||||
|
|
||||||
|
-spec use_cache(gen_mod:opts() | global | binary()) -> boolean().
|
||||||
|
use_cache(Opts) when is_map(Opts) ->
|
||||||
|
gen_mod:get_opt(use_cache, Opts);
|
||||||
|
use_cache(Host) ->
|
||||||
|
gen_mod:get_module_opt(Host, mod_announce, use_cache).
|
||||||
|
|
@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
-behaviour(mod_announce).
|
-behaviour(mod_announce).
|
||||||
|
|
||||||
-compile([{parse_transform, ejabberd_sql_pt}]).
|
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([init/2, set_motd_users/2, set_motd/2, delete_motd/1,
|
-export([init/2, set_motd_users/2, set_motd/2, delete_motd/1,
|
||||||
|
@ -35,7 +35,8 @@
|
|||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("pubsub.hrl").
|
-include("pubsub.hrl").
|
||||||
|
|
||||||
-type convert_rules() :: {default | eimp:img_type(), eimp:img_type()}.
|
-opaque convert_rule() :: {default | eimp:img_type(), eimp:img_type()}.
|
||||||
|
-export_type([convert_rule/0]).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% API
|
%%% API
|
||||||
@ -75,7 +76,7 @@ pubsub_publish_item(LServer, ?NS_AVATAR_METADATA,
|
|||||||
#avatar_meta{info = []} ->
|
#avatar_meta{info = []} ->
|
||||||
delete_vcard_avatar(From);
|
delete_vcard_avatar(From);
|
||||||
#avatar_meta{info = Info} ->
|
#avatar_meta{info = Info} ->
|
||||||
Rules = get_converting_rules(LServer),
|
Rules = mod_avatar_opt:convert(LServer),
|
||||||
case get_meta_info(Info, Rules) of
|
case get_meta_info(Info, Rules) of
|
||||||
#avatar_info{type = MimeType, id = ID, url = <<"">>} = I ->
|
#avatar_info{type = MimeType, id = ID, url = <<"">>} = I ->
|
||||||
case get_avatar_data(Host, ID) of
|
case get_avatar_data(Host, ID) of
|
||||||
@ -168,7 +169,7 @@ get_sm_features(Acc, _From, _To, _Node, _Lang) ->
|
|||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
-spec get_meta_info([avatar_info()], convert_rules()) -> avatar_info().
|
-spec get_meta_info([avatar_info()], [convert_rule()]) -> avatar_info().
|
||||||
get_meta_info(Info, Rules) ->
|
get_meta_info(Info, Rules) ->
|
||||||
case lists:foldl(
|
case lists:foldl(
|
||||||
fun(_, #avatar_info{} = Acc) ->
|
fun(_, #avatar_info{} = Acc) ->
|
||||||
@ -317,7 +318,7 @@ publish_avatar(#iq{from = JID} = IQ, Meta, MimeType, Data, ItemID) ->
|
|||||||
{error, eimp:error_reason() | base64_error} |
|
{error, eimp:error_reason() | base64_error} |
|
||||||
pass.
|
pass.
|
||||||
convert_avatar(LUser, LServer, VCard) ->
|
convert_avatar(LUser, LServer, VCard) ->
|
||||||
case get_converting_rules(LServer) of
|
case mod_avatar_opt:convert(LServer) of
|
||||||
[] ->
|
[] ->
|
||||||
pass;
|
pass;
|
||||||
Rules ->
|
Rules ->
|
||||||
@ -329,19 +330,19 @@ convert_avatar(LUser, LServer, VCard) ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec convert_avatar(binary(), binary(), binary(), convert_rules()) ->
|
-spec convert_avatar(binary(), binary(), binary(), [convert_rule()]) ->
|
||||||
{ok, eimp:img_type(), binary()} |
|
{ok, binary(), binary()} |
|
||||||
{error, eimp:error_reason()} |
|
{error, eimp:error_reason()} |
|
||||||
pass.
|
pass.
|
||||||
convert_avatar(LUser, LServer, Data, Rules) ->
|
convert_avatar(LUser, LServer, Data, Rules) ->
|
||||||
Type = get_type(Data),
|
Type = get_type(Data),
|
||||||
NewType = convert_to_type(Type, Rules),
|
NewType = convert_to_type(Type, Rules),
|
||||||
if NewType == undefined orelse Type == NewType ->
|
if NewType == undefined ->
|
||||||
pass;
|
pass;
|
||||||
true ->
|
true ->
|
||||||
?DEBUG("Converting avatar of ~s@~s: ~s -> ~s",
|
?DEBUG("Converting avatar of ~s@~s: ~s -> ~s",
|
||||||
[LUser, LServer, Type, NewType]),
|
[LUser, LServer, Type, NewType]),
|
||||||
RateLimit = gen_mod:get_module_opt(LServer, ?MODULE, rate_limit),
|
RateLimit = mod_avatar_opt:rate_limit(LServer),
|
||||||
Opts = [{limit_by, {LUser, LServer}},
|
Opts = [{limit_by, {LUser, LServer}},
|
||||||
{rate_limit, RateLimit}],
|
{rate_limit, RateLimit}],
|
||||||
case eimp:convert(Data, NewType, Opts) of
|
case eimp:convert(Data, NewType, Opts) of
|
||||||
@ -401,15 +402,11 @@ stop_with_error(Lang, Reason) ->
|
|||||||
Txt = eimp:format_error(Reason),
|
Txt = eimp:format_error(Reason),
|
||||||
{stop, xmpp:err_internal_server_error(Txt, Lang)}.
|
{stop, xmpp:err_internal_server_error(Txt, Lang)}.
|
||||||
|
|
||||||
-spec get_converting_rules(binary()) -> convert_rules().
|
|
||||||
get_converting_rules(LServer) ->
|
|
||||||
gen_mod:get_module_opt(LServer, ?MODULE, convert).
|
|
||||||
|
|
||||||
-spec get_type(binary()) -> eimp:img_type() | unknown.
|
-spec get_type(binary()) -> eimp:img_type() | unknown.
|
||||||
get_type(Data) ->
|
get_type(Data) ->
|
||||||
eimp:get_type(Data).
|
eimp:get_type(Data).
|
||||||
|
|
||||||
-spec convert_to_type(eimp:img_type() | unknown, convert_rules()) ->
|
-spec convert_to_type(eimp:img_type() | unknown, [convert_rule()]) ->
|
||||||
eimp:img_type() | undefined.
|
eimp:img_type() | undefined.
|
||||||
convert_to_type(unknown, _Rules) ->
|
convert_to_type(unknown, _Rules) ->
|
||||||
undefined;
|
undefined;
|
||||||
@ -417,6 +414,8 @@ convert_to_type(Type, Rules) ->
|
|||||||
case proplists:get_value(Type, Rules) of
|
case proplists:get_value(Type, Rules) of
|
||||||
undefined ->
|
undefined ->
|
||||||
proplists:get_value(default, Rules);
|
proplists:get_value(default, Rules);
|
||||||
|
Type ->
|
||||||
|
undefined;
|
||||||
T ->
|
T ->
|
||||||
T
|
T
|
||||||
end.
|
end.
|
||||||
@ -435,38 +434,23 @@ decode_mime_type(MimeType) ->
|
|||||||
encode_mime_type(Type) ->
|
encode_mime_type(Type) ->
|
||||||
<<"image/", (atom_to_binary(Type, latin1))/binary>>.
|
<<"image/", (atom_to_binary(Type, latin1))/binary>>.
|
||||||
|
|
||||||
-spec fail(atom()) -> no_return().
|
mod_opt_type(convert) ->
|
||||||
fail(Format) ->
|
Formats = eimp:supported_formats(),
|
||||||
FormatS = case Format of
|
econf:and_then(
|
||||||
webp -> "WebP";
|
fun(_) when Formats == [] ->
|
||||||
png -> "PNG";
|
econf:fail(eimp_error);
|
||||||
jpeg -> "JPEG";
|
(V) ->
|
||||||
gif -> "GIF";
|
V
|
||||||
_ -> ""
|
|
||||||
end,
|
end,
|
||||||
if FormatS /= "" ->
|
econf:options(
|
||||||
?WARNING_MSG("ejabberd is not compiled with ~s support", [FormatS]);
|
maps:from_list(
|
||||||
true ->
|
[{Type, econf:enum(Formats)}
|
||||||
ok
|
|| Type <- [default|Formats]])));
|
||||||
end,
|
|
||||||
erlang:error(badarg).
|
|
||||||
|
|
||||||
mod_opt_type({convert, From}) ->
|
|
||||||
fun(To) when is_atom(To), To /= From ->
|
|
||||||
case eimp:is_supported(From) orelse From == default of
|
|
||||||
false ->
|
|
||||||
fail(From);
|
|
||||||
true ->
|
|
||||||
case eimp:is_supported(To) orelse To == undefined of
|
|
||||||
false -> fail(To);
|
|
||||||
true -> To
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
mod_opt_type(rate_limit) ->
|
mod_opt_type(rate_limit) ->
|
||||||
fun(I) when is_integer(I), I > 0 -> I end.
|
econf:pos_int().
|
||||||
|
|
||||||
|
-spec mod_options(binary()) -> [{convert, [?MODULE:convert_rule()]} |
|
||||||
|
{atom(), any()}].
|
||||||
mod_options(_) ->
|
mod_options(_) ->
|
||||||
[{rate_limit, 10},
|
[{rate_limit, 10},
|
||||||
{convert,
|
{convert, []}].
|
||||||
[{T, undefined} || T <- [default|eimp:supported_formats()]]}].
|
|
||||||
|
20
src/mod_avatar_opt.erl
Normal file
20
src/mod_avatar_opt.erl
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
%% Generated automatically
|
||||||
|
%% DO NOT EDIT: run `make options` instead
|
||||||
|
|
||||||
|
-module(mod_avatar_opt).
|
||||||
|
|
||||||
|
-export([convert/1]).
|
||||||
|
-export([rate_limit/1]).
|
||||||
|
|
||||||
|
-spec convert(gen_mod:opts() | global | binary()) -> [mod_avatar:convert_rule()].
|
||||||
|
convert(Opts) when is_map(Opts) ->
|
||||||
|
gen_mod:get_opt(convert, Opts);
|
||||||
|
convert(Host) ->
|
||||||
|
gen_mod:get_module_opt(Host, mod_avatar, convert).
|
||||||
|
|
||||||
|
-spec rate_limit(gen_mod:opts() | global | binary()) -> pos_integer().
|
||||||
|
rate_limit(Opts) when is_map(Opts) ->
|
||||||
|
gen_mod:get_opt(rate_limit, Opts);
|
||||||
|
rate_limit(Host) ->
|
||||||
|
gen_mod:get_module_opt(Host, mod_avatar, rate_limit).
|
||||||
|
|
@ -90,8 +90,8 @@ filter_subscription(Acc, #presence{meta = #{captcha := passed}}) ->
|
|||||||
filter_subscription(Acc, #presence{from = From, to = To, lang = Lang,
|
filter_subscription(Acc, #presence{from = From, to = To, lang = Lang,
|
||||||
id = SID, type = subscribe} = Pres) ->
|
id = SID, type = subscribe} = Pres) ->
|
||||||
LServer = To#jid.lserver,
|
LServer = To#jid.lserver,
|
||||||
case gen_mod:get_module_opt(LServer, ?MODULE, drop) andalso
|
case mod_block_strangers_opt:drop(LServer) andalso
|
||||||
gen_mod:get_module_opt(LServer, ?MODULE, captcha) andalso
|
mod_block_strangers_opt:captcha(LServer) andalso
|
||||||
need_check(Pres) of
|
need_check(Pres) of
|
||||||
true ->
|
true ->
|
||||||
case check_subscription(From, To) of
|
case check_subscription(From, To) of
|
||||||
@ -106,7 +106,7 @@ filter_subscription(Acc, #presence{from = From, to = To, lang = Lang,
|
|||||||
Msg = #message{from = BTo, to = From,
|
Msg = #message{from = BTo, to = From,
|
||||||
id = ID, body = Body,
|
id = ID, body = Body,
|
||||||
sub_els = CaptchaEls},
|
sub_els = CaptchaEls},
|
||||||
case gen_mod:get_module_opt(LServer, ?MODULE, log) of
|
case mod_block_strangers_opt:log(LServer) of
|
||||||
true ->
|
true ->
|
||||||
?INFO_MSG("Challenge subscription request "
|
?INFO_MSG("Challenge subscription request "
|
||||||
"from stranger ~s to ~s with "
|
"from stranger ~s to ~s with "
|
||||||
@ -151,8 +151,8 @@ check_message(#message{from = From, to = To, lang = Lang} = Msg) ->
|
|||||||
true ->
|
true ->
|
||||||
case check_subscription(From, To) of
|
case check_subscription(From, To) of
|
||||||
false ->
|
false ->
|
||||||
Drop = gen_mod:get_module_opt(LServer, ?MODULE, drop),
|
Drop = mod_block_strangers_opt:drop(LServer),
|
||||||
Log = gen_mod:get_module_opt(LServer, ?MODULE, log),
|
Log = mod_block_strangers_opt:log(LServer),
|
||||||
if
|
if
|
||||||
Log ->
|
Log ->
|
||||||
?INFO_MSG("~s message from stranger ~s to ~s",
|
?INFO_MSG("~s message from stranger ~s to ~s",
|
||||||
@ -199,8 +199,8 @@ need_check(Pkt) ->
|
|||||||
_ ->
|
_ ->
|
||||||
false
|
false
|
||||||
end,
|
end,
|
||||||
AllowLocalUsers = gen_mod:get_module_opt(LServer, ?MODULE, allow_local_users),
|
AllowLocalUsers = mod_block_strangers_opt:allow_local_users(LServer),
|
||||||
Access = gen_mod:get_module_opt(LServer, ?MODULE, access),
|
Access = mod_block_strangers_opt:access(LServer),
|
||||||
not (IsSelf orelse IsEmpty
|
not (IsSelf orelse IsEmpty
|
||||||
orelse acl:match_rule(LServer, Access, From) == allow
|
orelse acl:match_rule(LServer, Access, From) == allow
|
||||||
orelse ((AllowLocalUsers orelse From#jid.luser == <<"">>)
|
orelse ((AllowLocalUsers orelse From#jid.luser == <<"">>)
|
||||||
@ -215,7 +215,7 @@ check_subscription(From, To) ->
|
|||||||
false;
|
false;
|
||||||
false ->
|
false ->
|
||||||
%% Check if the contact's server is in the roster
|
%% Check if the contact's server is in the roster
|
||||||
gen_mod:get_module_opt(LocalServer, ?MODULE, allow_transports)
|
mod_block_strangers_opt:allow_transports(LocalServer)
|
||||||
andalso mod_roster:is_subscribed(jid:make(RemoteServer), To);
|
andalso mod_roster:is_subscribed(jid:make(RemoteServer), To);
|
||||||
true ->
|
true ->
|
||||||
true
|
true
|
||||||
@ -230,19 +230,18 @@ sets_bare_member({U, S, <<"">>} = LBJID, Set) ->
|
|||||||
depends(_Host, _Opts) ->
|
depends(_Host, _Opts) ->
|
||||||
[].
|
[].
|
||||||
|
|
||||||
mod_opt_type(drop) ->
|
|
||||||
fun (B) when is_boolean(B) -> B end;
|
|
||||||
mod_opt_type(log) ->
|
|
||||||
fun (B) when is_boolean(B) -> B end;
|
|
||||||
mod_opt_type(allow_local_users) ->
|
|
||||||
fun (B) when is_boolean(B) -> B end;
|
|
||||||
mod_opt_type(allow_transports) ->
|
|
||||||
fun (B) when is_boolean(B) -> B end;
|
|
||||||
mod_opt_type(captcha) ->
|
|
||||||
fun (B) when is_boolean(B) -> B end;
|
|
||||||
mod_opt_type(access) ->
|
mod_opt_type(access) ->
|
||||||
fun acl:access_rules_validator/1.
|
econf:acl();
|
||||||
|
mod_opt_type(drop) ->
|
||||||
|
econf:bool();
|
||||||
|
mod_opt_type(log) ->
|
||||||
|
econf:bool();
|
||||||
|
mod_opt_type(captcha) ->
|
||||||
|
econf:bool();
|
||||||
|
mod_opt_type(allow_local_users) ->
|
||||||
|
econf:bool();
|
||||||
|
mod_opt_type(allow_transports) ->
|
||||||
|
econf:bool().
|
||||||
|
|
||||||
mod_options(_) ->
|
mod_options(_) ->
|
||||||
[{access, none},
|
[{access, none},
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user