24
1
mirror of https://github.com/processone/ejabberd.git synced 2024-06-16 22:05:29 +02:00

Make it possible to validate second-level options

This commit is contained in:
Evgeniy Khramtsov 2017-05-04 17:34:32 +03:00
parent e790e66f47
commit fb17c1b99f

View File

@ -191,7 +191,7 @@ start_module(Host, Module, Opts) ->
start_module(Host, Module, Opts0, NeedValidation) -> start_module(Host, Module, Opts0, NeedValidation) ->
?DEBUG("loading ~s at ~s", [Module, Host]), ?DEBUG("loading ~s at ~s", [Module, Host]),
Opts = if NeedValidation -> Opts = if NeedValidation ->
validate_opts(Module, Opts0); validate_opts(Host, Module, Opts0);
true -> true ->
Opts0 Opts0
end, end,
@ -244,7 +244,7 @@ reload_modules(Host) ->
fun({Mod, OldOpts}) -> fun({Mod, OldOpts}) ->
case lists:keyfind(Mod, 1, NewMods) of case lists:keyfind(Mod, 1, NewMods) of
{_, NewOpts0} -> {_, NewOpts0} ->
case validate_opts(Mod, NewOpts0) of case validate_opts(Host, Mod, NewOpts0) of
OldOpts -> OldOpts ->
ok; ok;
NewOpts -> NewOpts ->
@ -441,74 +441,95 @@ get_opt_host(Host, Opts, Default) ->
Val = get_opt(host, Opts, Default), Val = get_opt(host, Opts, Default),
ejabberd_regexp:greplace(Val, <<"@HOST@">>, Host). ejabberd_regexp:greplace(Val, <<"@HOST@">>, Host).
-spec get_validators(binary(), module(), opts()) -> {ok, [{atom(), check_fun()}]} | undef.
get_module_mod_opt_type_fun(Module) -> get_validators(Host, Module, Opts) ->
DBSubMods = ejabberd_config:v_dbs_mods(Module), try Module:mod_opt_type('') of
fun(Opt) -> L ->
Res = lists:foldl(fun(Mod, {Funs, ArgsList, _} = Acc) -> SubMods1 = case lists:member(db_type, L) of
case catch Mod:mod_opt_type(Opt) of true -> [db_mod(Host, Opts, Module)];
Fun when is_function(Fun) -> false -> []
{[Fun | Funs], ArgsList, true}; end,
L when is_list(L) -> SubMods2 = case lists:member(ram_db_type, L) of
{Funs, L ++ ArgsList, true}; true -> [ram_db_mod(Host, Opts, Module)];
_ -> false -> []
Acc end,
end {ok, dict:to_list(
end, {[], [], false}, [Module | DBSubMods]), lists:foldl(
case Res of fun(Mod, D) ->
{[], [], false} -> try Mod:mod_opt_type('') of
throw({'EXIT', {undef, mod_opt_type}}); Os ->
{[], Args, _} -> Args; lists:foldl(
{Funs, _, _} -> fun({Opt, SubOpt} = O, Acc) ->
fun(Val) -> try_mod_opt_type(Funs, Val) end F = Mod:mod_opt_type(O),
end dict:append(Opt, {SubOpt, F}, Acc);
(O, Acc) ->
F = Mod:mod_opt_type(O),
dict:store(O, F, Acc)
end, D, Os)
catch _:undef ->
D
end
end, dict:new(), [Module|SubMods1 ++ SubMods2]))}
catch _:undef ->
?WARNING_MSG("module '~s' doesn't export mod_opt_type/1",
[Module]),
undef
end. end.
try_mod_opt_type([Fun|Funs], Val) -> -spec validate_opts(binary(), module(), opts()) -> opts().
try Fun(Val) of validate_opts(Host, Module, Opts) ->
NewVal -> NewVal case get_validators(Host, Module, Opts) of
catch {invalid_syntax, _Error} = E2 -> {ok, Validators} ->
throw(E2); validate_opts(Host, Module, Opts, Validators);
_:_ -> undef ->
try_mod_opt_type(Funs, Val) Opts
end. end.
validate_opts(Module, Opts) -> validate_opts(Host, Module, Opts, Validators) when is_list(Opts) ->
ModOptFun = get_module_mod_opt_type_fun(Module), lists:flatmap(
lists:filtermap( fun({Opt, Val}) when is_atom(Opt) ->
fun({Opt, Val}) -> case lists:keyfind(Opt, 1, Validators) of
case catch ModOptFun(Opt) of {_, VFun} when is_function(VFun) ->
VFun when is_function(VFun) -> validate_opt(Module, Opt, Val, VFun);
try VFun(Val) of {_, SubValidators} ->
NewVal -> try validate_opts(Host, Module, Val, SubValidators) of
{true, {Opt, NewVal}} SubOpts -> [{Opt, SubOpts}]
catch {invalid_syntax, Error} -> catch _:bad_option ->
?ERROR_MSG("ignoring invalid value '~p' for "
"option '~s' of module '~s': ~s",
[Val, Opt, Module, Error]),
false;
_:_ ->
?ERROR_MSG("ignoring invalid value '~p' for " ?ERROR_MSG("ignoring invalid value '~p' for "
"option '~s' of module '~s'", "option '~s' of module '~s'",
[Val, Opt, Module]), [Val, Opt, Module]),
false []
end; end;
L when is_list(L) -> false ->
SOpts = str:join([[$', atom_to_list(A), $'] || A <- L], <<", ">>),
?ERROR_MSG("unknown option '~s' for module '~s' will be" ?ERROR_MSG("unknown option '~s' for module '~s' will be"
" likely ignored, available options are: ~s", " likely ignored, available options are: ~s",
[Opt, Module, SOpts]), [Opt, Module,
true; misc:join_atoms([K || {K, _} <- Validators],
{'EXIT', {undef, _}} -> <<", ">>)]),
?WARNING_MSG("module '~s' doesn't export mod_opt_type/1", [{Opt, Val}]
[Module]),
true
end; end;
(Junk) -> (_) ->
?ERROR_MSG("failed to understand option ~p for module '~s'", erlang:error(bad_option)
[Junk, Module]), end, Opts);
false validate_opts(_, _, _, _) ->
end, Opts). erlang:error(bad_option).
-spec validate_opt(module(), atom(), any(),
[{atom(), check_fun(), any()}]) -> [{atom(), any()}].
validate_opt(Module, Opt, Val, VFun) ->
try VFun(Val) of
NewVal -> [{Opt, NewVal}]
catch {invalid_syntax, Error} ->
?ERROR_MSG("ignoring invalid value '~p' for "
"option '~s' of module '~s': ~s",
[Val, Opt, Module, Error]),
[];
_:_ ->
?ERROR_MSG("ignoring invalid value '~p' for "
"option '~s' of module '~s'",
[Val, Opt, Module]),
[]
end.
-spec db_type(binary() | global, module()) -> db_type(); -spec db_type(binary() | global, module()) -> db_type();
(opts(), module()) -> db_type(). (opts(), module()) -> db_type().