2019-06-14 11:33:26 +02:00
|
|
|
%%%----------------------------------------------------------------------
|
2022-02-10 17:21:43 +01:00
|
|
|
%%% ejabberd, Copyright (C) 2002-2022 ProcessOne
|
2019-06-14 11:33:26 +02:00
|
|
|
%%%
|
|
|
|
%%% 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).
|
|
|
|
|
2020-01-08 10:24:51 +01:00
|
|
|
-export([opt_type/1, options/0, globals/0, doc/0]).
|
2019-06-14 11:33:26 +02:00
|
|
|
|
|
|
|
-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(),
|
2019-09-20 11:36:31 +02:00
|
|
|
contact => econf:list_or_single(econf:binary("^[a-zA-Z]+:[^:]+$")),
|
|
|
|
auto => econf:bool(),
|
|
|
|
cert_type => econf:enum([ec, rsa])},
|
2019-06-14 11:33:26 +02:00
|
|
|
[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) ->
|
2019-11-07 10:14:08 +01:00
|
|
|
opt_type(host_config);
|
2019-06-14 11:33:26 +02:00
|
|
|
opt_type(auth_cache_life_time) ->
|
2019-06-15 11:53:16 +02:00
|
|
|
econf:timeout(second, infinity);
|
2019-06-14 11:33:26 +02:00
|
|
|
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));
|
2020-02-03 18:23:00 +01:00
|
|
|
opt_type(auth_opts) ->
|
|
|
|
fun(L) when is_list(L) ->
|
|
|
|
lists:map(
|
|
|
|
fun({host, V}) when is_binary(V) ->
|
|
|
|
{host, V};
|
|
|
|
({connection_pool_size, V}) when is_integer(V) ->
|
|
|
|
{connection_pool_size, V};
|
|
|
|
({connection_opts, V}) when is_list(V) ->
|
|
|
|
{connection_opts, V};
|
|
|
|
({basic_auth, V}) when is_binary(V) ->
|
|
|
|
{basic_auth, V};
|
|
|
|
({path_prefix, V}) when is_binary(V) ->
|
|
|
|
{path_prefix, V}
|
|
|
|
end, L)
|
|
|
|
end;
|
2019-06-14 11:33:26 +02:00
|
|
|
opt_type(auth_password_format) ->
|
|
|
|
econf:enum([plain, scram]);
|
2020-12-08 12:06:52 +01:00
|
|
|
opt_type(auth_scram_hash) ->
|
|
|
|
econf:enum([sha, sha256, sha512]);
|
2019-06-14 11:33:26 +02:00
|
|
|
opt_type(auth_use_cache) ->
|
|
|
|
econf:bool();
|
|
|
|
opt_type(c2s_cafile) ->
|
|
|
|
econf:file();
|
|
|
|
opt_type(c2s_ciphers) ->
|
2019-11-05 15:54:56 +01:00
|
|
|
fun(L) when is_list(L) ->
|
|
|
|
(econf:and_then(
|
|
|
|
econf:list(econf:binary(), [unique]),
|
|
|
|
concat_binary($:)))(L);
|
|
|
|
(B) ->
|
|
|
|
(econf:binary())(B)
|
|
|
|
end;
|
2019-06-14 11:33:26 +02:00
|
|
|
opt_type(c2s_dhfile) ->
|
|
|
|
econf:file();
|
|
|
|
opt_type(c2s_protocol_options) ->
|
|
|
|
econf:and_then(
|
|
|
|
econf:list(econf:binary(), [unique]),
|
2019-11-05 15:54:56 +01:00
|
|
|
concat_binary($|));
|
2019-06-14 11:33:26 +02:00
|
|
|
opt_type(c2s_tls_compression) ->
|
|
|
|
econf:bool();
|
|
|
|
opt_type(ca_file) ->
|
|
|
|
econf:pem();
|
|
|
|
opt_type(cache_life_time) ->
|
2019-06-15 11:53:16 +02:00
|
|
|
econf:timeout(second, infinity);
|
2019-06-14 11:33:26 +02:00
|
|
|
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);
|
2019-06-14 15:06:04 +02:00
|
|
|
opt_type(captcha_url) ->
|
|
|
|
econf:url();
|
2019-06-14 11:33:26 +02:00
|
|
|
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) ->
|
2019-07-18 18:31:12 +02:00
|
|
|
econf:enum([mnesia, sql]);
|
2019-06-14 11:33:26 +02:00
|
|
|
opt_type(default_ram_db) ->
|
2019-07-18 18:31:12 +02:00
|
|
|
econf:enum([mnesia, sql, redis]);
|
2019-06-14 11:33:26 +02:00
|
|
|
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])},
|
2022-04-04 00:14:32 +02:00
|
|
|
[{return, map}, unique]),
|
2019-06-14 11:33:26 +02:00
|
|
|
[{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) ->
|
2019-11-07 10:14:08 +01:00
|
|
|
econf:and_then(
|
2019-06-14 11:33:26 +02:00
|
|
|
econf:and_then(
|
2019-11-07 10:14:08 +01:00
|
|
|
econf:map(econf:domain(), econf:list(econf:any())),
|
|
|
|
fun econf:group_dups/1),
|
|
|
|
econf:map(
|
|
|
|
econf:enum(ejabberd_config:get_option(hosts)),
|
|
|
|
validator(),
|
|
|
|
[unique]));
|
2019-06-14 11:33:26 +02:00
|
|
|
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_rotate_count) ->
|
|
|
|
econf:non_neg_int();
|
|
|
|
opt_type(log_rotate_size) ->
|
Replace lager with built-in new logging API
This change requires Erlang/OTP-21.0 or higher.
The commit also deprecates the following options:
- log_rotate_date
- log_rate_limit
Furthermore, these options have no effect. The logger now fully
relies on log_rotate_size, that cannot be 0 anymore.
The loglevel option now accepts levels in literal formats.
Those are: none, emergency, alert, critical, error, warning, notice, info, debug.
Old integer values (0-5) are still supported and automatically converted
into literal format.
2019-10-18 18:12:32 +02:00
|
|
|
econf:pos_int(infinity);
|
2019-06-14 11:33:26 +02:00
|
|
|
opt_type(loglevel) ->
|
Replace lager with built-in new logging API
This change requires Erlang/OTP-21.0 or higher.
The commit also deprecates the following options:
- log_rotate_date
- log_rate_limit
Furthermore, these options have no effect. The logger now fully
relies on log_rotate_size, that cannot be 0 anymore.
The loglevel option now accepts levels in literal formats.
Those are: none, emergency, alert, critical, error, warning, notice, info, debug.
Old integer values (0-5) are still supported and automatically converted
into literal format.
2019-10-18 18:12:32 +02:00
|
|
|
fun(N) when is_integer(N) ->
|
|
|
|
(econf:and_then(
|
|
|
|
econf:int(0, 5),
|
|
|
|
fun ejabberd_logger:convert_loglevel/1))(N);
|
|
|
|
(Level) ->
|
|
|
|
(econf:enum([none, emergency, alert, critical,
|
|
|
|
error, warning, notice, info, debug]))(Level)
|
|
|
|
end;
|
2019-06-14 11:33:26 +02:00
|
|
|
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) ->
|
2019-07-17 21:15:56 +02:00
|
|
|
econf:timeout(second);
|
2019-06-14 11:33:26 +02:00
|
|
|
opt_type(new_sql_schema) ->
|
|
|
|
econf:bool();
|
|
|
|
opt_type(oauth_access) ->
|
|
|
|
econf:acl();
|
|
|
|
opt_type(oauth_cache_life_time) ->
|
2019-06-15 11:53:16 +02:00
|
|
|
econf:timeout(second, infinity);
|
2019-06-14 11:33:26 +02:00
|
|
|
opt_type(oauth_cache_missed) ->
|
|
|
|
econf:bool();
|
2021-01-27 11:23:39 +01:00
|
|
|
opt_type(oauth_cache_rest_failure_life_time) ->
|
|
|
|
econf:timeout(second, infinity);
|
2019-06-14 11:33:26 +02:00
|
|
|
opt_type(oauth_cache_size) ->
|
|
|
|
econf:pos_int(infinity);
|
|
|
|
opt_type(oauth_db_type) ->
|
|
|
|
econf:db_type(ejabberd_oauth);
|
|
|
|
opt_type(oauth_expire) ->
|
2019-10-15 11:18:29 +02:00
|
|
|
econf:timeout(second);
|
2019-06-14 11:33:26 +02:00
|
|
|
opt_type(oauth_use_cache) ->
|
|
|
|
econf:bool();
|
2019-09-27 19:36:35 +02:00
|
|
|
opt_type(oauth_client_id_check) ->
|
|
|
|
econf:enum([allow, deny, db]);
|
2019-06-14 11:33:26 +02:00
|
|
|
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);
|
2020-09-21 22:18:46 +02:00
|
|
|
opt_type(outgoing_s2s_ipv4_address) ->
|
|
|
|
econf:ipv4();
|
|
|
|
opt_type(outgoing_s2s_ipv6_address) ->
|
|
|
|
econf:ipv6();
|
2019-06-14 11:33:26 +02:00
|
|
|
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) ->
|
2019-07-17 21:15:56 +02:00
|
|
|
econf:timeout(second, infinity);
|
2019-06-14 11:33:26 +02:00
|
|
|
opt_type(resource_conflict) ->
|
|
|
|
econf:enum([setresource, closeold, closenew, acceptnew]);
|
|
|
|
opt_type(router_cache_life_time) ->
|
2019-06-15 11:53:16 +02:00
|
|
|
econf:timeout(second, infinity);
|
2019-06-14 11:33:26 +02:00
|
|
|
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) ->
|
2019-11-05 15:54:56 +01:00
|
|
|
opt_type(c2s_ciphers);
|
2019-06-14 11:33:26 +02:00
|
|
|
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) ->
|
2019-07-17 21:15:56 +02:00
|
|
|
econf:timeout(second);
|
2019-06-14 11:33:26 +02:00
|
|
|
opt_type(s2s_protocol_options) ->
|
2019-11-05 15:54:56 +01:00
|
|
|
opt_type(c2s_protocol_options);
|
2019-06-14 11:33:26 +02:00
|
|
|
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) ->
|
2019-06-15 11:53:16 +02:00
|
|
|
econf:timeout(second, infinity);
|
2019-06-14 11:33:26 +02:00
|
|
|
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();
|
2020-10-08 16:23:34 +02:00
|
|
|
opt_type(sql_odbc_driver) ->
|
|
|
|
econf:binary();
|
2019-06-14 11:33:26 +02:00
|
|
|
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();
|
2019-11-26 17:45:01 +01:00
|
|
|
opt_type(sql_prepared_statements) ->
|
|
|
|
econf:bool();
|
2019-06-14 11:33:26 +02:00
|
|
|
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) ->
|
2019-07-01 04:01:55 +02:00
|
|
|
econf:timeout(second);
|
|
|
|
opt_type(jwt_key) ->
|
2019-07-03 04:32:58 +02:00
|
|
|
econf:and_then(
|
2019-07-18 21:35:16 +02:00
|
|
|
econf:path(),
|
2019-07-03 04:32:58 +02:00
|
|
|
fun(Path) ->
|
|
|
|
case file:read_file(Path) of
|
2019-07-18 21:31:08 +02:00
|
|
|
{ok, Data} ->
|
|
|
|
try jose_jwk:from_binary(Data) of
|
|
|
|
{error, _} -> econf:fail({bad_jwt_key, Path});
|
2019-10-25 15:33:11 +02:00
|
|
|
JWK ->
|
|
|
|
case jose_jwk:to_map(JWK) of
|
|
|
|
{_, #{<<"keys">> := [Key]}} ->
|
|
|
|
jose_jwk:from_map(Key);
|
2019-10-25 17:30:50 +02:00
|
|
|
{_, #{<<"keys">> := [_|_]}} ->
|
2019-10-25 15:33:11 +02:00
|
|
|
econf:fail({bad_jwt_key_set, Path});
|
2019-10-25 17:30:50 +02:00
|
|
|
{_, #{<<"keys">> := _}} ->
|
|
|
|
econf:fail({bad_jwt_key, Path});
|
2019-10-25 15:33:11 +02:00
|
|
|
_ ->
|
|
|
|
JWK
|
|
|
|
end
|
2019-07-18 21:31:08 +02:00
|
|
|
catch _:_ ->
|
|
|
|
econf:fail({bad_jwt_key, Path})
|
|
|
|
end;
|
2019-07-03 04:32:58 +02:00
|
|
|
{error, Reason} ->
|
2019-07-03 18:16:54 +02:00
|
|
|
econf:fail({read_file, Reason, Path})
|
2019-07-03 04:32:58 +02:00
|
|
|
end
|
2019-09-18 17:45:51 +02:00
|
|
|
end);
|
2019-10-25 17:56:18 +02:00
|
|
|
opt_type(jwt_jid_field) ->
|
|
|
|
econf:binary();
|
2019-09-18 17:45:51 +02:00
|
|
|
opt_type(jwt_auth_only_rule) ->
|
|
|
|
econf:atom().
|
2019-06-14 11:33:26 +02:00
|
|
|
|
|
|
|
%% 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()} |
|
2019-11-05 15:54:56 +01:00
|
|
|
{s2s_ciphers, undefined | binary()} |
|
|
|
|
{c2s_ciphers, undefined | binary()} |
|
2019-06-14 11:33:26 +02:00
|
|
|
{websocket_origin, [binary()]} |
|
|
|
|
{disable_sasl_mechanisms, [binary()]} |
|
|
|
|
{s2s_zlib, boolean()} |
|
2019-10-25 14:27:47 +02:00
|
|
|
{loglevel, ejabberd_logger:loglevel()} |
|
2020-02-03 18:23:00 +01:00
|
|
|
{auth_opts, [{any(), any()}]} |
|
2019-06-14 11:33:26 +02:00
|
|
|
{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()]} |
|
2019-07-19 11:01:57 +02:00
|
|
|
{jwt_key, jose_jwk:key() | undefined} |
|
2019-06-14 11:33:26 +02:00
|
|
|
{append_host_config, [{binary(), any()}]} |
|
|
|
|
{host_config, [{binary(), any()}]} |
|
|
|
|
{define_macro, any()} |
|
|
|
|
{include_config_file, any()} |
|
|
|
|
{atom(), any()}].
|
|
|
|
options() ->
|
|
|
|
[%% Top-priority options
|
|
|
|
hosts,
|
Replace lager with built-in new logging API
This change requires Erlang/OTP-21.0 or higher.
The commit also deprecates the following options:
- log_rotate_date
- log_rate_limit
Furthermore, these options have no effect. The logger now fully
relies on log_rotate_size, that cannot be 0 anymore.
The loglevel option now accepts levels in literal formats.
Those are: none, emergency, alert, critical, error, warning, notice, info, debug.
Old integer values (0-5) are still supported and automatically converted
into literal format.
2019-10-18 18:12:32 +02:00
|
|
|
{loglevel, info},
|
2019-06-15 11:53:16 +02:00
|
|
|
{cache_life_time, timer:seconds(3600)},
|
2019-06-14 11:33:26 +02:00
|
|
|
{cache_missed, true},
|
|
|
|
{cache_size, 1000},
|
|
|
|
{use_cache, true},
|
|
|
|
{default_db, mnesia},
|
|
|
|
{default_ram_db, mnesia},
|
|
|
|
{queue_type, ram},
|
2019-06-23 11:23:22 +02:00
|
|
|
{version, ejabberd_config:version()},
|
2019-06-14 11:33:26 +02:00
|
|
|
%% 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},
|
2020-02-03 18:23:00 +01:00
|
|
|
{auth_opts, []},
|
2019-06-14 11:33:26 +02:00
|
|
|
{auth_password_format, plain},
|
2020-12-08 12:06:52 +01:00
|
|
|
{auth_scram_hash, sha},
|
2019-06-14 11:33:26 +02:00
|
|
|
{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},
|
2019-06-14 15:06:04 +02:00
|
|
|
{captcha_url, undefined},
|
2019-06-14 11:33:26 +02:00
|
|
|
{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) ->
|
2019-07-03 18:23:05 +02:00
|
|
|
case ejabberd_config:get_option({ldap_encrypt, Host}) of
|
2019-06-14 11:33:26 +02:00
|
|
|
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, []},
|
Replace lager with built-in new logging API
This change requires Erlang/OTP-21.0 or higher.
The commit also deprecates the following options:
- log_rotate_date
- log_rate_limit
Furthermore, these options have no effect. The logger now fully
relies on log_rotate_size, that cannot be 0 anymore.
The loglevel option now accepts levels in literal formats.
Those are: none, emergency, alert, critical, error, warning, notice, info, debug.
Old integer values (0-5) are still supported and automatically converted
into literal format.
2019-10-18 18:12:32 +02:00
|
|
|
{log_rotate_count, 1},
|
|
|
|
{log_rotate_size, 10*1024*1024},
|
2019-06-14 11:33:26 +02:00
|
|
|
{max_fsm_queue, undefined},
|
|
|
|
{modules, []},
|
|
|
|
{negotiation_timeout, timer:seconds(30)},
|
2019-07-17 21:15:56 +02:00
|
|
|
{net_ticktime, timer:seconds(60)},
|
2019-06-14 11:33:26 +02:00
|
|
|
{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},
|
2021-01-27 11:23:39 +01:00
|
|
|
{oauth_cache_rest_failure_life_time, infinity},
|
2019-06-14 11:33:26 +02:00
|
|
|
{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},
|
2019-09-27 19:36:35 +02:00
|
|
|
{oauth_client_id_check, allow},
|
2019-06-14 11:33:26 +02:00
|
|
|
{oom_killer, true},
|
|
|
|
{oom_queue, 10000},
|
|
|
|
{oom_watermark, 80},
|
|
|
|
{outgoing_s2s_families, [inet, inet6]},
|
2020-09-21 22:18:46 +02:00
|
|
|
{outgoing_s2s_ipv4_address, undefined},
|
|
|
|
{outgoing_s2s_ipv6_address, undefined},
|
2019-06-14 11:33:26 +02:00
|
|
|
{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"},
|
2019-07-17 21:15:56 +02:00
|
|
|
{registration_timeout, timer:seconds(600)},
|
2019-06-14 11:33:26 +02:00
|
|
|
{resource_conflict, acceptnew},
|
|
|
|
{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)},
|
2019-07-17 21:15:56 +02:00
|
|
|
{s2s_max_retry_delay, timer:seconds(300)},
|
2019-06-14 11:33:26 +02:00
|
|
|
{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},
|
2019-08-12 11:21:31 +02:00
|
|
|
{sql_type, odbc},
|
2019-06-14 11:33:26 +02:00
|
|
|
{sql_connect_timeout, timer:seconds(5)},
|
|
|
|
{sql_database, undefined},
|
|
|
|
{sql_keepalive_interval, undefined},
|
|
|
|
{sql_password, <<"">>},
|
2020-10-08 16:23:34 +02:00
|
|
|
{sql_odbc_driver, <<"libtdsodbc.so">>}, % default is FreeTDS driver
|
2019-06-14 11:33:26 +02:00
|
|
|
{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">>},
|
2019-11-26 17:45:01 +01:00
|
|
|
{sql_prepared_statements, true},
|
2019-06-14 11:33:26 +02:00
|
|
|
{trusted_proxies, []},
|
|
|
|
{validate_stream, false},
|
|
|
|
{websocket_origin, []},
|
|
|
|
{websocket_ping_interval, timer:seconds(60)},
|
2019-07-01 04:01:55 +02:00
|
|
|
{websocket_timeout, timer:minutes(5)},
|
2019-09-18 17:45:51 +02:00
|
|
|
{jwt_key, undefined},
|
2019-10-25 17:56:18 +02:00
|
|
|
{jwt_jid_field, <<"jid">>},
|
2019-09-18 17:45:51 +02:00
|
|
|
{jwt_auth_only_rule, none}].
|
2019-06-14 11:33:26 +02:00
|
|
|
|
|
|
|
-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,
|
2019-06-14 15:06:04 +02:00
|
|
|
captcha_url,
|
2019-06-14 11:33:26 +02:00
|
|
|
certfiles,
|
|
|
|
cluster_backend,
|
|
|
|
cluster_nodes,
|
|
|
|
domain_balancing,
|
|
|
|
ext_api_path_oauth,
|
|
|
|
fqdn,
|
|
|
|
hosts,
|
|
|
|
host_config,
|
|
|
|
listen,
|
|
|
|
loglevel,
|
|
|
|
log_rotate_count,
|
|
|
|
log_rotate_size,
|
|
|
|
negotiation_timeout,
|
|
|
|
net_ticktime,
|
|
|
|
new_sql_schema,
|
|
|
|
node_start,
|
|
|
|
oauth_cache_life_time,
|
|
|
|
oauth_cache_missed,
|
|
|
|
oauth_cache_size,
|
2021-01-27 11:23:39 +01:00
|
|
|
oauth_cache_rest_failure_life_time,
|
2019-06-14 11:33:26 +02:00
|
|
|
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,
|
|
|
|
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,
|
2019-08-13 17:30:28 +02:00
|
|
|
trusted_proxies,
|
2019-06-21 20:06:32 +02:00
|
|
|
validate_stream,
|
2019-06-14 11:33:26 +02:00
|
|
|
version,
|
|
|
|
websocket_origin,
|
|
|
|
websocket_ping_interval,
|
|
|
|
websocket_timeout].
|
|
|
|
|
2020-01-08 10:24:51 +01:00
|
|
|
doc() ->
|
|
|
|
ejabberd_options_doc:doc().
|
|
|
|
|
2019-06-14 11:33:26 +02:00
|
|
|
%%%===================================================================
|
|
|
|
%%% Internal functions
|
|
|
|
%%%===================================================================
|
|
|
|
-spec validator() -> econf:validator().
|
|
|
|
validator() ->
|
|
|
|
Disallowed = ejabberd_config:globals(),
|
|
|
|
{Validators, Required} = ejabberd_config:validators(Disallowed),
|
2019-11-07 10:14:08 +01:00
|
|
|
econf:and_then(
|
|
|
|
fun econf:group_dups/1,
|
|
|
|
econf:options(
|
|
|
|
Validators,
|
|
|
|
[{disallowed, Required ++ Disallowed}, unique])).
|
2019-06-14 11:33:26 +02:00
|
|
|
|
|
|
|
-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).
|
|
|
|
|
2019-11-05 15:54:56 +01:00
|
|
|
-spec concat_binary(char()) -> fun(([binary()]) -> binary()).
|
|
|
|
concat_binary(C) ->
|
|
|
|
fun(Opts) -> str:join(Opts, <<C>>) end.
|