Don't validate an option in gen_mod:get*opt() functions

The changes are very similar to those from previous commit:
* Now there is no need to pass validating function in
  gen_mod:get_opt() and gen_mod:get_module_opt() functions,
  because the modules' configuration keeps already validated values.
* New functions gen_mod:get_opt/2 and gen_mod:get_module_opt/3 are
  introduced.
* Functions gen_mod:get_opt/4 and get_module_opt/5 are deprecated.
  If the functions are still called, the "function" argument is
  simply ignored.
* Validating callback Mod:listen_opt_type/1 is introduced to validate
  listening options at startup.
This commit is contained in:
Evgeniy Khramtsov 2017-04-30 19:01:47 +03:00
parent 2b63d07329
commit fddd6110e0
67 changed files with 809 additions and 1238 deletions

View File

@ -364,24 +364,11 @@ parse_options(Host) ->
Eldap_ID = misc:atom_to_binary(gen_mod:get_module_proc(Host, ?MODULE)),
Bind_Eldap_ID = misc:atom_to_binary(
gen_mod:get_module_proc(Host, bind_ejabberd_auth_ldap)),
UIDsTemp = gen_mod:get_opt(
{ldap_uids, Host}, [],
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, [{<<"uid">>, <<"%u">>}]),
UIDsTemp = ejabberd_config:get_option(
{ldap_uids, Host}, [{<<"uid">>, <<"%u">>}]),
UIDs = eldap_utils:uids_domain_subst(Host, UIDsTemp),
SubFilter = eldap_utils:generate_subfilter(UIDs),
UserFilter = case gen_mod:get_opt(
{ldap_filter, Host}, [],
fun eldap_utils:check_filter/1, <<"">>) of
UserFilter = case ejabberd_config:get_option({ldap_filter, Host}, <<"">>) of
<<"">> ->
SubFilter;
F ->
@ -390,20 +377,8 @@ parse_options(Host) ->
SearchFilter = eldap_filter:do_sub(UserFilter,
[{<<"%u">>, <<"*">>}]),
{DNFilter, DNFilterAttrs} =
gen_mod:get_opt({ldap_dn_filter, Host}, [],
fun([{DNF, DNFA}]) ->
NewDNFA = case DNFA of
undefined ->
[];
_ ->
[iolist_to_binary(A)
|| A <- DNFA]
end,
NewDNF = eldap_utils:check_filter(DNF),
{NewDNF, NewDNFA}
end, {undefined, []}),
LocalFilter = gen_mod:get_opt(
{ldap_local_filter, Host}, [], fun(V) -> V end),
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,
bind_eldap_id = Bind_Eldap_ID,
servers = Cfg#eldap_config.servers,

View File

@ -299,10 +299,7 @@ init([#body{attrs = Attrs}, IP, SID]) ->
XMPPVer = get_attr('xmpp:version', Attrs),
XMPPDomain = get_attr(to, Attrs),
{InBuf, Opts} = case gen_mod:get_module_opt(
XMPPDomain,
mod_bosh, prebind,
fun(B) when is_boolean(B) -> B end,
false) of
XMPPDomain, mod_bosh, prebind, false) of
true ->
JID = make_random_jid(XMPPDomain),
{buf_new(XMPPDomain), [{jid, JID} | Opts2]};
@ -315,12 +312,9 @@ init([#body{attrs = Attrs}, IP, SID]) ->
Opts),
Inactivity = gen_mod:get_module_opt(XMPPDomain,
mod_bosh, max_inactivity,
fun(I) when is_integer(I), I>0 -> I end,
?DEFAULT_INACTIVITY),
MaxConcat = gen_mod:get_module_opt(XMPPDomain, mod_bosh, max_concat,
fun(unlimited) -> unlimited;
(N) when is_integer(N), N>0 -> N
end, unlimited),
unlimited),
ShapedReceivers = buf_new(XMPPDomain, ?MAX_SHAPED_REQUESTS_QUEUE_LEN),
State = #state{host = XMPPDomain, sid = SID, ip = IP,
xmpp_ver = XMPPVer, el_ibuf = InBuf,
@ -366,7 +360,6 @@ wait_for_session(#body{attrs = Attrs} = Req, From,
end,
MaxPause = gen_mod:get_module_opt(State#state.host,
mod_bosh, max_pause,
fun(I) when is_integer(I), I>0 -> I end,
?DEFAULT_MAXPAUSE),
Resp = #body{attrs =
[{sid, State#state.sid}, {wait, Wait},
@ -1039,12 +1032,9 @@ buf_new(Host) ->
buf_new(Host, unlimited).
buf_new(Host, Limit) ->
QueueType = case gen_mod:get_module_opt(
Host, mod_bosh, queue_type,
mod_bosh:mod_opt_type(queue_type)) of
undefined -> ejabberd_config:default_queue_type(Host);
T -> T
end,
QueueType = gen_mod:get_module_opt(
Host, mod_bosh, queue_type,
ejabberd_config:default_queue_type(Host)),
p1_queue:new(QueueType, Limit).
buf_in(Xs, Buf) ->

View File

@ -29,7 +29,7 @@
%% ejabberd_socket callbacks
-export([start/2, start_link/2, socket_type/0]).
%% ejabberd_config callbacks
-export([opt_type/1, transform_listen_option/2]).
-export([opt_type/1, listen_opt_type/1, transform_listen_option/2]).
%% xmpp_stream_in callbacks
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3]).
@ -490,30 +490,25 @@ handle_send(Pkt, Result, #{lserver := LServer} = State) ->
ejabberd_hooks:run_fold(c2s_handle_send, LServer, State, [Pkt, Result]).
init([State, Opts]) ->
Access = gen_mod:get_opt(access, Opts, fun acl:access_rules_validator/1, all),
Shaper = gen_mod:get_opt(shaper, Opts, fun acl:shaper_rules_validator/1, none),
Access = gen_mod:get_opt(access, Opts, all),
Shaper = gen_mod:get_opt(shaper, Opts, none),
TLSOpts1 = lists:filter(
fun({certfile, _}) -> true;
({ciphers, _}) -> true;
({dhfile, _}) -> true;
({cafile, _}) -> true;
({protocol_options, _}) -> true;
(_) -> false
end, Opts),
TLSOpts2 = case lists:keyfind(protocol_options, 1, Opts) of
false -> TLSOpts1;
{_, OptString} ->
ProtoOpts = str:join(OptString, <<$|>>),
[{protocol_options, ProtoOpts}|TLSOpts1]
end,
TLSOpts3 = case proplists:get_bool(tls_compression, Opts) of
false -> [compression_none | TLSOpts2];
true -> TLSOpts2
TLSOpts2 = case proplists:get_bool(tls_compression, Opts) of
false -> [compression_none | TLSOpts1];
true -> TLSOpts1
end,
TLSEnabled = proplists:get_bool(starttls, Opts),
TLSRequired = proplists:get_bool(starttls_required, Opts),
TLSVerify = proplists:get_bool(tls_verify, Opts),
Zlib = proplists:get_bool(zlib, Opts),
State1 = State#{tls_options => TLSOpts3,
State1 = State#{tls_options => TLSOpts2,
tls_required => TLSRequired,
tls_enabled => TLSEnabled,
tls_verify => TLSVerify,
@ -660,9 +655,7 @@ process_presence_out(#{user := User, server := Server, lserver := LServer,
send_error(State, Pres, Err);
allow when Type == subscribe; Type == subscribed;
Type == unsubscribe; Type == unsubscribed ->
Access = gen_mod:get_module_opt(LServer, mod_roster, access,
fun(A) when is_atom(A) -> A end,
all),
Access = gen_mod:get_module_opt(LServer, mod_roster, access, all),
MyBareJID = jid:remove_resource(JID),
case acl:match_rule(LServer, Access, MyBareJID) of
deny ->
@ -926,3 +919,36 @@ opt_type(_) ->
[c2s_certfile, c2s_ciphers, c2s_cafile,
c2s_protocol_options, c2s_tls_compression, resource_conflict,
disable_sasl_mechanisms].
listen_opt_type(access) -> fun acl:access_rules_validator/1;
listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
listen_opt_type(certfile) -> opt_type(c2s_certfile);
listen_opt_type(ciphers) -> opt_type(c2s_ciphers);
listen_opt_type(dhfile) -> opt_type(c2s_dhfile);
listen_opt_type(cafile) -> opt_type(c2s_cafile);
listen_opt_type(protocol_options) -> opt_type(c2s_protocol_options);
listen_opt_type(tls_compression) -> opt_type(c2s_tls_compression);
listen_opt_type(tls) -> fun(B) when is_boolean(B) -> B 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) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(supervisor) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(max_stanza_size) ->
fun(I) when is_integer(I) -> I;
(unlimited) -> infinity;
(infinity) -> infinity
end;
listen_opt_type(max_fsm_queue) ->
fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(O) ->
%% This hack should be removed in future releases: it is intended
%% for backward compatibility with ejabberd 17.01 or older
case mod_stream_mgmt:mod_opt_type(O) of
L when is_list(L) ->
[access, shaper, certfile, ciphers, dhfile, cafile,
protocol_options, tls, tls_compression, starttls,
starttls_required, tls_verify, zlib, max_fsm_queue] ++ L;
VFun ->
VFun
end.

View File

@ -614,12 +614,12 @@ execute_check_access(undefined, _Command, _Arguments) ->
execute_check_access(FromJID, #ejabberd_commands{access = AccessRefs} = Command, Arguments) ->
%% TODO Review: Do we have smarter / better way to check rule on other Host than global ?
Host = global,
Rules = lists:map(fun({Mod, AccessName, Default}) ->
gen_mod:get_module_opt(Host, Mod,
AccessName, fun(A) -> A end, Default);
(Default) ->
Default
end, AccessRefs),
Rules = lists:map(
fun({Mod, AccessName, Default}) ->
gen_mod:get_module_opt(Host, Mod, AccessName, Default);
(Default) ->
Default
end, AccessRefs),
case acl:any_rules_allowed(Host, Rules, FromJID) of
true ->
do_execute_command(Command, Arguments);

View File

@ -32,7 +32,7 @@
%% External exports
-export([start/2, start_link/2, become_controller/1,
socket_type/0, receive_headers/1, url_encode/1,
transform_listen_option/2]).
transform_listen_option/2, listen_opt_type/1]).
-export([init/2, opt_type/1]).
@ -100,23 +100,15 @@ init({SockMod, Socket}, Opts) ->
TLSOpts1 = lists:filter(fun ({certfile, _}) -> true;
({ciphers, _}) -> true;
({dhfile, _}) -> true;
({protocol_options, _}) -> true;
(_) -> false
end,
Opts),
TLSOpts2 = case lists:keysearch(protocol_options, 1, Opts) of
{value, {_, O}} ->
[_|ProtocolOptions] = lists:foldl(
fun(X, Acc) -> X ++ Acc end, [],
[["|" | binary_to_list(Opt)] || Opt <- O, is_binary(Opt)]
),
[{protocol_options, iolist_to_binary(ProtocolOptions)} | TLSOpts1];
_ -> TLSOpts1
TLSOpts2 = case proplists:get_bool(tls_compression, Opts) of
false -> [compression_none | TLSOpts1];
true -> TLSOpts1
end,
TLSOpts3 = case proplists:get_bool(tls_compression, Opts) of
false -> [compression_none | TLSOpts2];
true -> TLSOpts2
end,
TLSOpts = [verify_none | TLSOpts3],
TLSOpts = [verify_none | TLSOpts2],
{SockMod1, Socket1} = if TLSEnabled ->
inet:setopts(Socket, [{recbuf, 8192}]),
{ok, TLSSocket} = fast_tls:tcp_to_tls(Socket,
@ -144,33 +136,15 @@ init({SockMod, Socket}, Opts) ->
true -> [{[], ejabberd_xmlrpc}];
false -> []
end,
DefinedHandlers = gen_mod:get_opt(
request_handlers, Opts,
fun(Hs) ->
Hs1 = lists:map(fun
({Mod, Path}) when is_atom(Mod) -> {Path, Mod};
({Path, Mod}) -> {Path, Mod}
end, Hs),
Hs2 = [{str:tokens(
iolist_to_binary(Path), <<"/">>),
Mod} || {Path, Mod} <- Hs1],
[{Path,
case Mod of
mod_http_bind -> mod_bosh;
_ -> Mod
end} || {Path, Mod} <- Hs2]
end, []),
DefinedHandlers = gen_mod:get_opt(request_handlers, Opts, []),
RequestHandlers = DefinedHandlers ++ Captcha ++ Register ++
Admin ++ Bind ++ XMLRPC,
?DEBUG("S: ~p~n", [RequestHandlers]),
DefaultHost = gen_mod:get_opt(default_host, Opts, fun(A) -> A end, undefined),
DefaultHost = gen_mod:get_opt(default_host, Opts, undefined),
{ok, RE} = re:compile(<<"^(?:\\[(.*?)\\]|(.*?))(?::(\\d+))?$">>),
CustomHeaders = gen_mod:get_opt(custom_headers, Opts,
fun expand_custom_headers/1,
[]),
CustomHeaders = gen_mod:get_opt(custom_headers, Opts, []),
?INFO_MSG("started: ~p", [{SockMod1, Socket1}]),
State = #state{sockmod = SockMod1,
@ -929,3 +903,48 @@ opt_type(trusted_proxies) ->
fun (all) -> all;
(TPs) -> [iolist_to_binary(TP) || TP <- TPs] end;
opt_type(_) -> [trusted_proxies].
listen_opt_type(tls) ->
fun(B) when is_boolean(B) -> B end;
listen_opt_type(certfile) ->
fun iolist_to_binary/1;
listen_opt_type(ciphers) ->
fun iolist_to_binary/1;
listen_opt_type(dhfile) ->
fun iolist_to_binary/1;
listen_opt_type(protocol_options) ->
fun(Options) -> str:join(Options, <<"|">>) end;
listen_opt_type(tls_compression) ->
fun(B) when is_boolean(B) -> B end;
listen_opt_type(captcha) ->
fun(B) when is_boolean(B) -> B end;
listen_opt_type(register) ->
fun(B) when is_boolean(B) -> B end;
listen_opt_type(web_admin) ->
fun(B) when is_boolean(B) -> B end;
listen_opt_type(http_bind) ->
fun(B) when is_boolean(B) -> B end;
listen_opt_type(xmlrpc) ->
fun(B) when is_boolean(B) -> B end;
listen_opt_type(request_handlers) ->
fun(Hs) ->
Hs1 = lists:map(fun
({Mod, Path}) when is_atom(Mod) -> {Path, Mod};
({Path, Mod}) -> {Path, Mod}
end, Hs),
Hs2 = [{str:tokens(
iolist_to_binary(Path), <<"/">>),
Mod} || {Path, Mod} <- Hs1],
[{Path,
case Mod of
mod_http_bind -> mod_bosh;
_ -> Mod
end} || {Path, Mod} <- Hs2]
end;
listen_opt_type(default_host) ->
fun(A) -> A end;
listen_opt_type(custom_headers) ->
fun expand_custom_headers/1;
listen_opt_type(_) ->
%% TODO
fun(A) -> A end.

View File

@ -52,7 +52,6 @@ listeners_childspec() ->
Ls = ejabberd_config:get_option(listen, []),
Specs = lists:map(
fun({Port, Module, Opts}) ->
maybe_start_sip(Module),
ets:insert(?MODULE, {Port, Module, Opts}),
{Port,
{?MODULE, start, [Port, Module, Opts]},
@ -82,10 +81,11 @@ report_duplicated_portips(L) ->
end.
start(Port, Module, Opts) ->
NewOpts = validate_module_options(Module, Opts),
%% Check if the module is an ejabberd listener or an independent listener
case Module:socket_type() of
independent -> Module:start_listener(Port, Opts);
_ -> start_dependent(Port, Module, Opts)
independent -> Module:start_listener(Port, NewOpts);
_ -> start_dependent(Port, Module, NewOpts)
end.
%% @spec(Port, Module, Opts) -> {ok, Pid} | {error, ErrorMessage}
@ -356,7 +356,6 @@ start_listener2(Port, Module, Opts) ->
%% It is only required to start the supervisor in some cases.
%% But it doesn't hurt to attempt to start it for any listener.
%% So, it's normal (and harmless) that in most cases this call returns: {error, {already_started, pid()}}
maybe_start_sip(Module),
start_listener_sup(Port, Module, Opts).
start_module_sup(_Port, Module) ->
@ -427,12 +426,6 @@ delete_listener(PortIP, Module, Opts) ->
PortIP1 = {Port, IPT, Proto},
stop_listener(PortIP1, Module).
maybe_start_sip(esip_socket) ->
ejabberd:start_app(esip);
maybe_start_sip(_) ->
ok.
config_reloaded() ->
New = ejabberd_config:get_option(listen, []),
Old = ets:tab2list(?MODULE),
@ -626,6 +619,47 @@ transform_options({listen, LOpts}, Opts) ->
transform_options(Opt, Opts) ->
[Opt|Opts].
-spec validate_module_options(module(), [{atom(), any()}]) -> [{atom(), any()}].
validate_module_options(Module, Opts) ->
try Module:listen_opt_type('') of
_ ->
lists:filtermap(
fun({Opt, Val}) ->
case validate_module_option(Module, Opt, Val) of
{ok, NewVal} -> {true, {Opt, NewVal}};
error -> false
end
end, Opts)
catch _:undef ->
?WARNING_MSG("module '~s' doesn't export listen_opt_type/1",
[Module]),
Opts
end.
-spec validate_module_option(module(), atom(), any()) -> {ok, any()} | error.
validate_module_option(Module, Opt, Val) ->
case Module:listen_opt_type(Opt) of
VFun when is_function(VFun) ->
try VFun(Val) of
NewVal -> {ok, NewVal}
catch {invalid_syntax, Error} ->
?ERROR_MSG("ignoring listen option '~s' with "
"invalid value: ~p: ~s",
[Opt, Val, Error]),
error;
_:_ ->
?ERROR_MSG("ignoring listen option '~s' with "
"invalid value: ~p",
[Opt, Val]),
error
end;
KnownOpts when is_list(KnownOpts) ->
?ERROR_MSG("unknown listen option '~s' for '~s' will be likely "
"ignored, available options are: ~s",
[Opt, Module, misc:join_atoms(KnownOpts, <<", ">>)]),
{ok, Val}
end.
-type transport() :: udp | tcp.
-type port_ip_transport() :: inet:port_number() |
{inet:port_number(), transport()} |
@ -647,7 +681,7 @@ validate_cfg(L) ->
true = ?IS_TRANSPORT(T),
{{Port, IP, T}, Mod, Opts};
({module, Mod}, {Port, _, Opts}) ->
{Port, prepare_mod(Mod), Opts};
{Port, Mod, Opts};
(Opt, {Port, Mod, Opts}) ->
{Port, Mod, [Opt|Opts]}
end, {{5222, all_zero_ip(LOpts), tcp}, ejabberd_c2s, []}, LOpts)
@ -666,13 +700,6 @@ prepare_ip(IP) when is_list(IP) ->
prepare_ip(IP) when is_binary(IP) ->
prepare_ip(binary_to_list(IP)).
prepare_mod(ejabberd_sip) ->
prepare_mod(sip);
prepare_mod(sip) ->
esip_socket;
prepare_mod(Mod) when is_atom(Mod) ->
Mod.
all_zero_ip(Opts) ->
case proplists:get_bool(inet6, Opts) of
true -> {0,0,0,0,0,0,0,0};

View File

@ -27,7 +27,7 @@
%% ejabberd_socket callbacks
-export([start/2, start_link/2, socket_type/0]).
%% ejabberd_config callbacks
-export([opt_type/1]).
-export([opt_type/1, listen_opt_type/1]).
%% xmpp_stream_in callbacks
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3]).
@ -245,25 +245,20 @@ handle_send(Pkt, Result, #{server_host := LServer} = State) ->
State, [Pkt, Result]).
init([State, Opts]) ->
Shaper = gen_mod:get_opt(shaper, Opts, fun acl:shaper_rules_validator/1, none),
Shaper = gen_mod:get_opt(shaper, Opts, none),
TLSOpts1 = lists:filter(
fun({certfile, _}) -> true;
({ciphers, _}) -> true;
({dhfile, _}) -> true;
({cafile, _}) -> true;
({protocol_options, _}) -> true;
(_) -> false
end, Opts),
TLSOpts2 = case lists:keyfind(protocol_options, 1, Opts) of
false -> TLSOpts1;
{_, OptString} ->
ProtoOpts = str:join(OptString, <<$|>>),
[{protocol_options, ProtoOpts}|TLSOpts1]
end,
TLSOpts3 = case proplists:get_bool(tls_compression, Opts) of
false -> [compression_none | TLSOpts2];
true -> TLSOpts2
TLSOpts2 = case proplists:get_bool(tls_compression, Opts) of
false -> [compression_none | TLSOpts1];
true -> TLSOpts1
end,
State1 = State#{tls_options => TLSOpts3,
State1 = State#{tls_options => TLSOpts2,
auth_domains => sets:new(),
xmlns => ?NS_SERVER,
lang => ?MYLANG,
@ -351,3 +346,23 @@ change_shaper(#{shaper := ShaperName, server_host := ServerHost} = State,
opt_type(_) ->
[].
listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
listen_opt_type(certfile) -> ejabberd_s2s:opt_type(s2s_certfile);
listen_opt_type(ciphers) -> ejabberd_s2s:opt_type(s2s_ciphers);
listen_opt_type(dhfile) -> ejabberd_s2s:opt_type(s2s_dhfile);
listen_opt_type(cafile) -> ejabberd_s2s:opt_type(s2s_cafile);
listen_opt_type(protocol_options) -> ejabberd_s2s:opt_type(s2s_protocol_options);
listen_opt_type(tls_compression) -> ejabberd_s2s:opt_type(s2s_tls_compression);
listen_opt_type(tls) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(supervisor) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(max_stanza_size) ->
fun(I) when is_integer(I) -> I;
(unlimited) -> infinity;
(infinity) -> infinity
end;
listen_opt_type(max_fsm_queue) ->
fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(_) ->
[shaper, certfile, ciphers, dhfile, cafile, protocol_options,
tls_compression, tls, max_fsm_queue].

View File

@ -29,7 +29,7 @@
%% ejabberd_socket callbacks
-export([start/2, start_link/2, socket_type/0, close/1, close/2]).
%% ejabberd_config callbacks
-export([opt_type/1, transform_listen_option/2]).
-export([opt_type/1, listen_opt_type/1, transform_listen_option/2]).
%% xmpp_stream_in callbacks
-export([init/1, handle_info/2, terminate/2, code_change/3]).
-export([handle_stream_start/2, handle_auth_success/4, handle_auth_failure/4,
@ -80,49 +80,33 @@ tls_options(#{tls_options := TLSOptions}) ->
TLSOptions.
init([State, Opts]) ->
Access = gen_mod:get_opt(access, Opts, fun acl:access_rules_validator/1, all),
Shaper = gen_mod:get_opt(shaper_rule, Opts, fun acl:shaper_rules_validator/1, none),
HostOpts = case lists:keyfind(hosts, 1, Opts) of
{hosts, HOpts} ->
lists:foldl(
fun({H, Os}, D) ->
P = proplists:get_value(
password, Os,
str:sha(randoms:bytes(20))),
dict:store(H, P, D)
end, dict:new(), HOpts);
false ->
Pass = proplists:get_value(
password, Opts,
str:sha(randoms:bytes(20))),
dict:from_list([{global, Pass}])
end,
CheckFrom = gen_mod:get_opt(check_from, Opts,
fun(Flag) when is_boolean(Flag) -> Flag end,
true),
Access = gen_mod:get_opt(access, Opts, all),
Shaper = gen_mod:get_opt(shaper_rule, Opts, none),
GlobalPassword = gen_mod:get_opt(password, Opts, random_password()),
HostOpts = gen_mod:get_opt(hosts, Opts, [{global, GlobalPassword}]),
HostOpts1 = lists:map(
fun({Host, undefined}) -> {Host, GlobalPassword};
({Host, Password}) -> {Host, Password}
end, HostOpts),
CheckFrom = gen_mod:get_opt(check_from, Opts, true),
TLSOpts1 = lists:filter(
fun({certfile, _}) -> true;
({ciphers, _}) -> true;
({dhfile, _}) -> true;
({cafile, _}) -> true;
({protocol_options, _}) -> true;
(_) -> false
end, Opts),
TLSOpts2 = case lists:keyfind(protocol_options, 1, Opts) of
false -> TLSOpts1;
{_, OptString} ->
ProtoOpts = str:join(OptString, <<$|>>),
[{protocol_options, ProtoOpts}|TLSOpts1]
end,
TLSOpts = case proplists:get_bool(tls_compression, Opts) of
false -> [compression_none | TLSOpts2];
true -> TLSOpts2
false -> [compression_none | TLSOpts1];
true -> TLSOpts1
end,
xmpp_stream_in:change_shaper(State, Shaper),
State1 = State#{access => Access,
xmlns => ?NS_COMPONENT,
lang => ?MYLANG,
server => ?MYNAME,
host_opts => HostOpts,
host_opts => dict:from_list(HostOpts1),
stream_version => undefined,
tls_options => TLSOpts,
check_from => CheckFrom},
@ -254,6 +238,9 @@ check_from(From, #{host_opts := HostOpts}) ->
Server = From#jid.lserver,
dict:is_key(Server, HostOpts).
random_password() ->
str:sha(randoms:bytes(20)).
transform_listen_option({hosts, Hosts, O}, Opts) ->
case lists:keyfind(hosts, 1, Opts) of
{_, PrevHostOpts} ->
@ -273,3 +260,38 @@ transform_listen_option(Opt, Opts) ->
[Opt|Opts].
opt_type(_) -> [].
listen_opt_type(access) -> fun acl:access_rules_validator/1;
listen_opt_type(shaper_rule) -> fun acl:shaper_rules_validator/1;
listen_opt_type(certfile) -> fun iolist_to_binary/1;
listen_opt_type(ciphers) -> fun iolist_to_binary/1;
listen_opt_type(dhfile) -> fun iolist_to_binary/1;
listen_opt_type(cafile) -> fun iolist_to_binary/1;
listen_opt_type(protocol_options) ->
fun(Options) -> str:join(Options, <<"|">>) end;
listen_opt_type(tls_compression) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(tls) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(check_from) -> fun(B) when is_boolean(B) -> B end;
listen_opt_type(password) -> fun iolist_to_binary/1;
listen_opt_type(hosts) ->
fun(HostOpts) ->
lists:map(
fun({Host, Opts}) ->
Password = case proplists:get_value(password, Opts) of
undefined -> undefined;
P -> iolist_to_binary(P)
end,
{iolist_to_binary(Host), Password}
end, HostOpts)
end;
listen_opt_type(max_stanza_size) ->
fun(I) when is_integer(I) -> I;
(unlimited) -> infinity;
(infinity) -> infinity
end;
listen_opt_type(max_fsm_queue) ->
fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(_) ->
[access, shaper_rule, certfile, ciphers, dhfile, cafile, tls,
protocol_options, tls_compression, password, hosts, check_from,
max_fsm_queue].

58
src/ejabberd_sip.erl Normal file
View File

@ -0,0 +1,58 @@
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% Created : 30 Apr 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2013-2017 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_sip).
%% API
-export([tcp_init/2, udp_init/2, udp_recv/5, start/2,
socket_type/0, listen_opt_type/1]).
%%%===================================================================
%%% API
%%%===================================================================
tcp_init(Socket, Opts) ->
ejabberd:start_app(esip),
esip_socket:tcp_init(Socket, Opts).
udp_init(Socket, Opts) ->
ejabberd:start_app(esip),
esip_socket:udp_init(Socket, Opts).
udp_recv(Sock, Addr, Port, Data, Opts) ->
esip_socket:udp_recv(Sock, Addr, Port, Data, Opts).
start(Opaque, Opts) ->
esip_socket:start(Opaque, Opts).
socket_type() ->
raw.
listen_opt_type(certfile) ->
fun iolist_to_binary/1;
listen_opt_type(tls) ->
fun(B) when is_boolean(B) -> B end;
listen_opt_type(_) ->
[tls, certfile].
%%%===================================================================
%%% Internal functions
%%%===================================================================

View File

@ -28,7 +28,7 @@
-protocol({xep, 176, '1.0'}).
-export([tcp_init/2, udp_init/2, udp_recv/5, start/2,
socket_type/0]).
socket_type/0, listen_opt_type/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@ -73,14 +73,9 @@ prepare_turn_opts(Opts, _UseTurn = true) ->
ok
end,
AuthFun = fun ejabberd_auth:get_password_s/2,
Shaper = gen_mod:get_opt(shaper, Opts,
fun(S) when is_atom(S) -> S end,
none),
AuthType = gen_mod:get_opt(auth_type, Opts,
fun(anonymous) -> anonymous;
(user) -> user
end, user),
Realm = case gen_mod:get_opt(auth_realm, Opts, fun iolist_to_binary/1) of
Shaper = gen_mod:get_opt(shaper, Opts, none),
AuthType = gen_mod:get_opt(auth_type, Opts, user),
Realm = case gen_mod:get_opt(auth_realm, Opts) of
undefined when AuthType == user ->
if NumberOfMyHosts > 1 ->
?WARNING_MSG("you have several virtual "
@ -100,3 +95,43 @@ prepare_turn_opts(Opts, _UseTurn = true) ->
MaxRate = shaper:get_max_rate(Shaper),
Realm ++ [{auth_fun, AuthFun},{shaper, MaxRate} |
lists:keydelete(shaper, 1, Opts)].
listen_opt_type(use_turn) ->
fun(B) when is_boolean(B) -> B end;
listen_opt_type(turn_ip) ->
fun(S) ->
{ok, Addr} = inet_parse:ipv4_address(binary_to_list(S)),
Addr
end;
listen_opt_type(shaper) ->
fun acl:shaper_rules_validator/1;
listen_opt_type(auth_type) ->
fun(anonymous) -> anonymous;
(user) -> user
end;
listen_opt_type(auth_realm) ->
fun iolist_to_binary/1;
listen_opt_type(tls) ->
fun(B) when is_boolean(B) -> B end;
listen_opt_type(certfile) ->
fun iolist_to_binary/1;
listen_opt_type(turn_min_port) ->
fun(P) when is_integer(P), P > 0, P =< 65535 -> P end;
listen_opt_type(turn_max_port) ->
fun(P) when is_integer(P), P > 0, P =< 65535 -> P end;
listen_opt_type(turn_max_allocations) ->
fun(I) when is_integer(I), I>0 -> I;
(unlimited) -> infinity;
(infinity) -> infinity
end;
listen_opt_type(turn_max_permissions) ->
fun(I) when is_integer(I), I>0 -> I;
(unlimited) -> infinity;
(infinity) -> infinity
end;
listen_opt_type(server_name) ->
fun iolist_to_binary/1;
listen_opt_type(_) ->
[shaper, auth_type, auth_realm, tls, certfile, turn_min_port,
turn_max_port, turn_max_allocations, turn_max_permissions,
server_name].

View File

@ -75,25 +75,25 @@ get_acl_rule([<<"vhosts">>], _) ->
get_acl_rule([<<"server">>, VHost | _RPath], Method)
when Method =:= 'GET' orelse Method =:= 'HEAD' ->
AC = gen_mod:get_module_opt(VHost, ejabberd_web_admin,
access, fun(A) -> A end, configure),
access, configure),
ACR = gen_mod:get_module_opt(VHost, ejabberd_web_admin,
access_readonly, fun(A) -> A end, webadmin_view),
access_readonly, webadmin_view),
{VHost, [AC, ACR]};
get_acl_rule([<<"server">>, VHost | _RPath], 'POST') ->
AC = gen_mod:get_module_opt(VHost, ejabberd_web_admin,
access, fun(A) -> A end, configure),
access, configure),
{VHost, [AC]};
%% Default rule: only global admins can access any other random page
get_acl_rule(_RPath, Method)
when Method =:= 'GET' orelse Method =:= 'HEAD' ->
AC = gen_mod:get_module_opt(global, ejabberd_web_admin,
access, fun(A) -> A end, configure),
access, configure),
ACR = gen_mod:get_module_opt(global, ejabberd_web_admin,
access_readonly, fun(A) -> A end, webadmin_view),
access_readonly, webadmin_view),
{global, [AC, ACR]};
get_acl_rule(_RPath, 'POST') ->
AC = gen_mod:get_module_opt(global, ejabberd_web_admin,
access, fun(A) -> A end, configure),
access, configure),
{global, [AC]}.
%%%==================================

View File

@ -35,7 +35,7 @@
-author('badlop@process-one.net').
-export([start/2, handler/2, process/2, socket_type/0,
transform_listen_option/2]).
transform_listen_option/2, listen_opt_type/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@ -197,36 +197,7 @@ socket_type() -> raw.
%% HTTP interface
%% -----------------------------
process(_, #request{method = 'POST', data = Data, opts = Opts, ip = {IP, _}}) ->
AccessCommandsOpts = gen_mod:get_opt(access_commands, Opts,
fun(L) when is_list(L) -> L end,
undefined),
AccessCommands =
case AccessCommandsOpts of
undefined -> undefined;
_ ->
lists:flatmap(
fun({Ac, AcOpts}) ->
Commands = gen_mod:get_opt(
commands, lists:flatten(AcOpts),
fun(A) when is_atom(A) ->
A;
(L) when is_list(L) ->
true = lists:all(
fun is_atom/1,
L),
L
end, all),
%% CommOpts = gen_mod:get_opt(
%% options, AcOpts,
%% fun(L) when is_list(L) -> L end,
%% []),
[{<<"ejabberd_xmlrpc compatibility shim">>, {[?MODULE], [{access, Ac}], Commands}}];
(Wrong) ->
?WARNING_MSG("wrong options format for ~p: ~p",
[?MODULE, Wrong]),
[]
end, lists:flatten(AccessCommandsOpts))
end,
AccessCommands = gen_mod:get_opt(access_commands, Opts),
GetAuth = true,
State = #state{access_commands = AccessCommands, get_auth = GetAuth, ip = IP},
case fxml_stream:parse_element(Data) of
@ -590,3 +561,25 @@ transform_listen_option({access_commands, ACOpts}, Opts) ->
[{access_commands, NewACOpts}|Opts];
transform_listen_option(Opt, Opts) ->
[Opt|Opts].
listen_opt_type(access_commands) ->
fun(Opts) ->
lists:map(
fun({Ac, AcOpts}) ->
Commands = case proplists:get_value(
commands, lists:flatten(AcOpts), all) of
Cmd when is_atom(Cmd) -> Cmd;
Cmds when is_list(Cmds) ->
true = lists:all(fun is_atom/1, Cmds),
Cmds
end,
{<<"ejabberd_xmlrpc compatibility shim">>,
{[?MODULE], [{access, Ac}], Commands}}
end, lists:flatten(Opts))
end;
listen_opt_type(maxsessions) ->
fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(timeout) ->
fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(_) ->
[access_commands, maxsessions, timeout].

View File

@ -565,11 +565,7 @@ get_handle(Name) when is_binary(Name) ->
%% process.
%%----------------------------------------------------------------------
init([Hosts, Port, Rootdn, Passwd, Opts]) ->
Encrypt = case gen_mod:get_opt(encrypt, Opts,
fun(tls) -> tls;
(starttls) -> starttls;
(none) -> none
end) of
Encrypt = case gen_mod:get_opt(encrypt, Opts) of
tls -> tls;
_ -> none
end,
@ -581,35 +577,19 @@ init([Hosts, Port, Rootdn, Passwd, Opts]) ->
end;
PT -> PT
end,
CacertOpts = case gen_mod:get_opt(
tls_cacertfile, Opts,
fun(S) when is_binary(S) ->
binary_to_list(S);
(undefined) ->
undefined
end) of
CacertOpts = case gen_mod:get_opt(tls_cacertfile, Opts) of
undefined ->
[];
Path ->
[{cacertfile, Path}]
end,
DepthOpts = case gen_mod:get_opt(
tls_depth, Opts,
fun(I) when is_integer(I), I>=0 ->
I;
(undefined) ->
undefined
end) of
DepthOpts = case gen_mod:get_opt(tls_depth, Opts) of
undefined ->
[];
Depth ->
[{depth, Depth}]
end,
Verify = gen_mod:get_opt(tls_verify, Opts,
fun(hard) -> hard;
(soft) -> soft;
(false) -> false
end, false),
Verify = gen_mod:get_opt(tls_verify, Opts, false),
TLSOpts = if (Verify == hard orelse Verify == soft)
andalso CacertOpts == [] ->
?WARNING_MSG("TLS verification is enabled but no CA "

View File

@ -173,58 +173,25 @@ uids_domain_subst(Host, UIDs) ->
-spec get_config(binary(), list()) -> eldap_config().
get_config(Host, Opts) ->
Servers = gen_mod:get_opt({ldap_servers, Host}, Opts,
fun(L) ->
[iolist_to_binary(H) || H <- L]
end, [<<"localhost">>]),
Backups = gen_mod:get_opt({ldap_backups, Host}, Opts,
fun(L) ->
[iolist_to_binary(H) || H <- L]
end, []),
Encrypt = gen_mod:get_opt({ldap_encrypt, Host}, Opts,
fun(tls) -> tls;
(starttls) -> starttls;
(none) -> none
end, none),
TLSVerify = gen_mod:get_opt({ldap_tls_verify, Host}, Opts,
fun(hard) -> hard;
(soft) -> soft;
(false) -> false
end, false),
TLSCAFile = gen_mod:get_opt({ldap_tls_cacertfile, Host}, Opts,
fun iolist_to_binary/1),
TLSDepth = gen_mod:get_opt({ldap_tls_depth, Host}, Opts,
fun(I) when is_integer(I), I>=0 -> I end),
Servers = gen_mod:get_opt({ldap_servers, Host}, Opts, [<<"localhost">>]),
Backups = gen_mod:get_opt({ldap_backups, Host}, Opts, []),
Encrypt = gen_mod:get_opt({ldap_encrypt, Host}, Opts, none),
TLSVerify = gen_mod:get_opt({ldap_tls_verify, Host}, Opts, false),
TLSCAFile = gen_mod:get_opt({ldap_tls_cacertfile, Host}, Opts),
TLSDepth = gen_mod:get_opt({ldap_tls_depth, Host}, Opts),
Port = gen_mod:get_opt({ldap_port, Host}, Opts,
fun(I) when is_integer(I), I>0 -> I end,
case Encrypt of
tls -> ?LDAPS_PORT;
starttls -> ?LDAP_PORT;
_ -> ?LDAP_PORT
end),
RootDN = gen_mod:get_opt({ldap_rootdn, Host}, Opts,
fun iolist_to_binary/1,
<<"">>),
Password = gen_mod:get_opt({ldap_password, Host}, Opts,
fun iolist_to_binary/1,
<<"">>),
Base = gen_mod:get_opt({ldap_base, Host}, Opts,
fun iolist_to_binary/1,
<<"">>),
OldDerefAliases = gen_mod:get_opt({deref_aliases, Host}, Opts,
fun(never) -> never;
(searching) -> searching;
(finding) -> finding;
(always) -> always
end, unspecified),
case Encrypt of
tls -> ?LDAPS_PORT;
starttls -> ?LDAP_PORT;
_ -> ?LDAP_PORT
end),
RootDN = gen_mod:get_opt({ldap_rootdn, Host}, Opts, <<"">>),
Password = gen_mod:get_opt({ldap_password, Host}, Opts, <<"">>),
Base = gen_mod:get_opt({ldap_base, Host}, Opts, <<"">>),
OldDerefAliases = gen_mod:get_opt({deref_aliases, Host}, Opts, unspecified),
DerefAliases =
if OldDerefAliases == unspecified ->
gen_mod:get_opt({ldap_deref_aliases, Host}, Opts,
fun(never) -> never;
(searching) -> searching;
(finding) -> finding;
(always) -> always
end, never);
gen_mod:get_opt({ldap_deref_aliases, Host}, Opts, never);
true ->
?WARNING_MSG("Option 'deref_aliases' is deprecated. "
"The option is still supported "
@ -377,7 +344,8 @@ opt_type(ldap_port) ->
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_cacertfile) -> fun iolist_to_binary/1;
opt_type(ldap_tls_cacertfile) ->
fun(S) -> binary_to_list(iolist_to_binary(S)) end;
opt_type(ldap_tls_depth) ->
fun (I) when is_integer(I), I >= 0 -> I end;
opt_type(ldap_tls_verify) ->

View File

@ -33,16 +33,18 @@
-export([init/1, start_link/0, start_child/3, start_child/4,
stop_child/1, stop_child/2, config_reloaded/0]).
-export([start_module/2, start_module/3,
stop_module/2, stop_module_keep_config/2, get_opt/3,
get_opt/4, get_opt_host/3, opt_type/1, is_equal_opt/5,
get_module_opt/4, get_module_opt/5, get_module_opt_host/3,
stop_module/2, stop_module_keep_config/2,
get_opt/2, get_opt/3, get_opt_host/3, opt_type/1, is_equal_opt/4,
get_module_opt/3, get_module_opt/4, get_module_opt_host/3,
loaded_modules/1, loaded_modules_with_opts/1,
get_hosts/2, get_module_proc/2, is_loaded/2, is_loaded_elsewhere/2,
start_modules/0, start_modules/1, stop_modules/0, stop_modules/1,
db_mod/2, db_mod/3, ram_db_mod/2, ram_db_mod/3,
db_type/2, db_type/3, ram_db_type/2, ram_db_type/3]).
%%-export([behaviour_info/1]).
%% Deprecated functions
-export([get_opt/4, get_module_opt/5]).
-deprecated([{get_opt, 4}, {get_module_opt, 5}]).
-include("ejabberd.hrl").
-include("logger.hrl").
@ -73,7 +75,7 @@
start_link() ->
case supervisor:start_link({local, ejabberd_gen_mod_sup}, ?MODULE, []) of
{ok, Pid} ->
gen_mod:start_modules(),
start_modules(),
{ok, Pid};
Err ->
Err
@ -303,7 +305,7 @@ stop_modules(Host) ->
Modules = get_modules_options(Host),
lists:foreach(
fun({Module, _Args}) ->
gen_mod:stop_module_keep_config(Host, Module)
stop_module_keep_config(Host, Module)
end, Modules).
-spec stop_module(binary(), atom()) -> error | {aborted, any()} | {atomic, any()}.
@ -351,40 +353,47 @@ wait_for_stop1(MonitorReference) ->
-type check_fun() :: fun((any()) -> any()) | {module(), atom()}.
-spec get_opt(atom() | {atom(), binary()|global}, opts(), check_fun()) -> any().
-spec get_opt(atom() | {atom(), binary() | global}, opts()) -> any().
get_opt(Opt, Opts) ->
get_opt(Opt, Opts, undefined).
get_opt(Opt, Opts, F) ->
get_opt(Opt, Opts, F, undefined).
-spec get_opt(atom() | {atom(), binary()|global}, opts(), check_fun() | any()) -> any().
-spec get_opt(atom() | {atom(), binary()|global}, opts(), check_fun(), any()) -> any().
get_opt({Opt, Host}, Opts, F, Default) ->
case lists:keysearch(Opt, 1, Opts) of
get_opt(Opt, Opts, F) when is_function(F) ->
get_opt(Opt, Opts, undefined);
get_opt({Opt, Host}, Opts, Default) ->
case lists:keyfind(Opt, 1, Opts) of
false ->
ejabberd_config:get_option({Opt, Host}, Default);
{value, {_, Val}} ->
ejabberd_config:prepare_opt_val(Opt, Val, F, Default)
{_, Val} ->
Val
end;
get_opt(Opt, Opts, F, Default) ->
case lists:keysearch(Opt, 1, Opts) of
get_opt(Opt, Opts, Default) ->
case lists:keyfind(Opt, 1, Opts) of
false ->
Default;
{value, {_, Val}} ->
ejabberd_config:prepare_opt_val(Opt, Val, F, Default)
{_, Val} ->
Val
end.
-spec get_module_opt(global | binary(), atom(), atom(), check_fun()) -> any().
-spec get_opt(atom() | {atom(), binary()}, opts(), check_fun(), any()) -> any().
get_opt(Opt, Opts, _, Default) ->
get_opt(Opt, Opts, Default).
get_module_opt(Host, Module, Opt, F) ->
get_module_opt(Host, Module, Opt, F, undefined).
-spec get_module_opt(global | binary(), atom(), atom()) -> any().
-spec get_module_opt(global | binary(), atom(), atom(), check_fun(), any()) -> any().
get_module_opt(Host, Module, Opt) ->
get_module_opt(Host, Module, Opt, undefined).
get_module_opt(global, Module, Opt, F, Default) ->
-spec get_module_opt(global | binary(), atom(), atom(), any()) -> any().
get_module_opt(Host, Module, Opt, F) when is_function(F) ->
get_module_opt(Host, Module, Opt, undefined);
get_module_opt(global, Module, Opt, Default) ->
Hosts = (?MYHOSTS),
[Value | Values] = lists:map(fun (Host) ->
get_module_opt(Host, Module, Opt,
F, Default)
Default)
end,
Hosts),
Same_all = lists:all(fun (Other_value) ->
@ -395,26 +404,28 @@ get_module_opt(global, Module, Opt, F, Default) ->
true -> Value;
false -> Default
end;
get_module_opt(Host, Module, Opt, F, Default) ->
get_module_opt(Host, Module, Opt, Default) ->
OptsList = ets:lookup(ejabberd_modules, {Module, Host}),
case OptsList of
[] -> Default;
[#ejabberd_module{opts = Opts} | _] ->
get_opt(Opt, Opts, F, Default)
get_opt(Opt, Opts, Default)
end.
-spec get_module_opt(global | binary(), atom(), atom(), check_fun(), any()) -> any().
get_module_opt(Host, Module, Opt, _, Default) ->
get_module_opt(Host, Module, Opt, Default).
-spec get_module_opt_host(global | binary(), atom(), binary()) -> binary().
get_module_opt_host(Host, Module, Default) ->
Val = get_module_opt(Host, Module, host,
fun iolist_to_binary/1,
Default),
Val = get_module_opt(Host, Module, host, Default),
ejabberd_regexp:greplace(Val, <<"@HOST@">>, Host).
-spec get_opt_host(binary(), opts(), binary()) -> binary().
get_opt_host(Host, Opts, Default) ->
Val = get_opt(host, Opts, fun iolist_to_binary/1, Default),
Val = get_opt(host, Opts, Default),
ejabberd_regexp:greplace(Val, <<"@HOST@">>, Host).
@ -436,23 +447,19 @@ get_module_mod_opt_type_fun(Module) ->
throw({'EXIT', {undef, mod_opt_type}});
{[], Args, _} -> Args;
{Funs, _, _} ->
fun(Val) ->
lists:any(fun(F) ->