mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-22 17:28:25 +01:00
Don't re-define validation functions in multiple places
This commit is contained in:
parent
6658c0d386
commit
7129aebe76
@ -44,7 +44,7 @@
|
||||
get_password_s/2, get_password_with_authmodule/2,
|
||||
is_user_exists/2, is_user_exists_in_other_modules/3,
|
||||
remove_user/2, remove_user/3, plain_password_required/1,
|
||||
store_type/1, entropy/1, backend_type/1]).
|
||||
store_type/1, entropy/1, backend_type/1, password_format/1]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
@ -497,6 +497,11 @@ backend_type(Mod) ->
|
||||
_ -> Mod
|
||||
end.
|
||||
|
||||
password_format(LServer) ->
|
||||
ejabberd_config:get_option({auth_password_format, LServer},
|
||||
opt_type(auth_password_format),
|
||||
plain).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
@ -537,4 +542,8 @@ opt_type(auth_method) ->
|
||||
lists:map(fun(M) -> ejabberd_config:v_db(?MODULE, M) end, V);
|
||||
(V) -> [ejabberd_config:v_db(?MODULE, V)]
|
||||
end;
|
||||
opt_type(_) -> [auth_method].
|
||||
opt_type(auth_password_format) ->
|
||||
fun (plain) -> plain;
|
||||
(scram) -> scram
|
||||
end;
|
||||
opt_type(_) -> [auth_method, auth_password_format].
|
||||
|
@ -381,7 +381,7 @@ parse_options(Host) ->
|
||||
SubFilter = eldap_utils:generate_subfilter(UIDs),
|
||||
UserFilter = case gen_mod:get_opt(
|
||||
{ldap_filter, Host}, [],
|
||||
fun check_filter/1, <<"">>) of
|
||||
fun eldap_utils:check_filter/1, <<"">>) of
|
||||
<<"">> ->
|
||||
SubFilter;
|
||||
F ->
|
||||
@ -399,7 +399,7 @@ parse_options(Host) ->
|
||||
[iolist_to_binary(A)
|
||||
|| A <- DNFA]
|
||||
end,
|
||||
NewDNF = check_filter(DNF),
|
||||
NewDNF = eldap_utils:check_filter(DNF),
|
||||
{NewDNF, NewDNFA}
|
||||
end, {undefined, []}),
|
||||
LocalFilter = gen_mod:get_opt(
|
||||
@ -418,31 +418,15 @@ parse_options(Host) ->
|
||||
sfilter = SearchFilter, lfilter = LocalFilter,
|
||||
dn_filter = DNFilter, dn_filter_attrs = DNFilterAttrs}.
|
||||
|
||||
check_filter(F) ->
|
||||
NewF = iolist_to_binary(F),
|
||||
{ok, _} = eldap_filter:parse(NewF),
|
||||
NewF.
|
||||
|
||||
opt_type(ldap_dn_filter) ->
|
||||
fun ([{DNF, DNFA}]) ->
|
||||
NewDNFA = case DNFA of
|
||||
undefined -> [];
|
||||
_ -> [iolist_to_binary(A) || A <- DNFA]
|
||||
end,
|
||||
NewDNF = check_filter(DNF),
|
||||
NewDNF = eldap_utils:check_filter(DNF),
|
||||
{NewDNF, NewDNFA}
|
||||
end;
|
||||
opt_type(ldap_filter) -> fun check_filter/1;
|
||||
opt_type(ldap_local_filter) -> fun (V) -> V 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(_) ->
|
||||
[ldap_dn_filter, ldap_filter, ldap_local_filter,
|
||||
ldap_uids].
|
||||
[ldap_dn_filter, ldap_local_filter].
|
||||
|
@ -27,8 +27,6 @@
|
||||
|
||||
-compile([{parse_transform, ejabberd_sql_pt}]).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(ejabberd_auth).
|
||||
@ -41,7 +39,7 @@
|
||||
get_vh_registered_users_number/2, get_password/2,
|
||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
||||
remove_user/3, store_type/0, export/1, import/2,
|
||||
plain_password_required/0, opt_type/1]).
|
||||
plain_password_required/0]).
|
||||
-export([need_transform/1, transform/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@ -89,8 +87,7 @@ plain_password_required() ->
|
||||
is_scrammed().
|
||||
|
||||
store_type() ->
|
||||
ejabberd_config:get_option({auth_password_format, ?MYNAME},
|
||||
opt_type(auth_password_format), plain).
|
||||
ejabberd_auth:password_format(?MYNAME).
|
||||
|
||||
check_password(User, AuthzId, Server, Password) ->
|
||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
||||
@ -494,9 +491,3 @@ export(_Server) ->
|
||||
import(LServer, [LUser, Password, _TimeStamp]) ->
|
||||
mnesia:dirty_write(
|
||||
#passwd{us = {LUser, LServer}, password = Password}).
|
||||
|
||||
opt_type(auth_password_format) ->
|
||||
fun (plain) -> plain;
|
||||
(scram) -> scram
|
||||
end;
|
||||
opt_type(_) -> [auth_password_format].
|
||||
|
@ -27,8 +27,6 @@
|
||||
|
||||
-compile([{parse_transform, ejabberd_sql_pt}]).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(ejabberd_auth).
|
||||
@ -42,7 +40,7 @@
|
||||
get_vh_registered_users_number/2, get_password/2,
|
||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
||||
remove_user/3, store_type/0, export/1, import/2,
|
||||
plain_password_required/0, opt_type/1]).
|
||||
plain_password_required/0]).
|
||||
-export([passwd_schema/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@ -272,9 +270,7 @@ remove_user(User, Server, Password) ->
|
||||
%%%
|
||||
|
||||
is_scrammed() ->
|
||||
scram ==
|
||||
ejabberd_config:get_option({auth_password_format, ?MYNAME},
|
||||
opt_type(auth_password_format), plain).
|
||||
scram == ejabberd_auth:password_format(?MYNAME).
|
||||
|
||||
password_to_scram(Password) ->
|
||||
password_to_scram(Password,
|
||||
@ -320,9 +316,3 @@ export(_Server) ->
|
||||
import(LServer, [LUser, Password, _TimeStamp]) ->
|
||||
Passwd = #passwd{us = {LUser, LServer}, password = Password},
|
||||
ejabberd_riak:put(Passwd, passwd_schema(), [{'2i', [{<<"host">>, LServer}]}]).
|
||||
|
||||
opt_type(auth_password_format) ->
|
||||
fun (plain) -> plain;
|
||||
(scram) -> scram
|
||||
end;
|
||||
opt_type(_) -> [auth_password_format].
|
||||
|
@ -27,8 +27,6 @@
|
||||
|
||||
-compile([{parse_transform, ejabberd_sql_pt}]).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(ejabberd_auth).
|
||||
@ -41,7 +39,7 @@
|
||||
get_vh_registered_users_number/2, get_password/2,
|
||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
||||
remove_user/3, store_type/0, plain_password_required/0,
|
||||
convert_to_scram/1, opt_type/1]).
|
||||
convert_to_scram/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@ -408,9 +406,7 @@ remove_user(User, Server, Password) ->
|
||||
%%%
|
||||
|
||||
is_scrammed() ->
|
||||
scram ==
|
||||
ejabberd_config:get_option({auth_password_format, ?MYNAME},
|
||||
opt_type(auth_password_format), plain).
|
||||
scram == ejabberd_auth:password_format(?MYNAME).
|
||||
|
||||
password_to_scram(Password) ->
|
||||
password_to_scram(Password,
|
||||
@ -509,9 +505,3 @@ convert_to_scram(Server) ->
|
||||
Error -> Error
|
||||
end
|
||||
end.
|
||||
|
||||
opt_type(auth_password_format) ->
|
||||
fun (plain) -> plain;
|
||||
(scram) -> scram
|
||||
end;
|
||||
opt_type(_) -> [auth_password_format].
|
||||
|
@ -922,7 +922,6 @@ format_reason(_, _) ->
|
||||
transform_listen_option(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
opt_type(domain_certfile) -> fun iolist_to_binary/1;
|
||||
opt_type(c2s_certfile) -> fun iolist_to_binary/1;
|
||||
opt_type(c2s_ciphers) -> fun iolist_to_binary/1;
|
||||
opt_type(c2s_dhfile) -> fun iolist_to_binary/1;
|
||||
@ -945,6 +944,6 @@ opt_type(disable_sasl_mechanisms) ->
|
||||
(V) -> [str:to_upper(V)]
|
||||
end;
|
||||
opt_type(_) ->
|
||||
[domain_certfile, c2s_certfile, c2s_ciphers, c2s_cafile,
|
||||
[c2s_certfile, c2s_ciphers, c2s_cafile,
|
||||
c2s_protocol_options, c2s_tls_compression, resource_conflict,
|
||||
disable_sasl_mechanisms].
|
||||
|
@ -26,24 +26,21 @@
|
||||
|
||||
-module(ejabberd_c2s_config).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('mremond@process-one.net').
|
||||
|
||||
-export([get_c2s_limits/0, opt_type/1]).
|
||||
-export([get_c2s_limits/0]).
|
||||
|
||||
%% Get first c2s configuration limitations to apply it to other c2s
|
||||
%% connectors.
|
||||
get_c2s_limits() ->
|
||||
case ejabberd_config:get_option(listen, fun(V) -> V end) of
|
||||
undefined -> [];
|
||||
C2SFirstListen ->
|
||||
case lists:keysearch(ejabberd_c2s, 2, C2SFirstListen) of
|
||||
false -> [];
|
||||
{value, {_Port, ejabberd_c2s, Opts}} ->
|
||||
select_opts_values(Opts)
|
||||
end
|
||||
C2SFirstListen = ejabberd_config:get_option(
|
||||
listen, fun ejabberd_listener:validate_cfg/1, []),
|
||||
case lists:keysearch(ejabberd_c2s, 2, C2SFirstListen) of
|
||||
false -> [];
|
||||
{value, {_Port, ejabberd_c2s, Opts}} ->
|
||||
select_opts_values(Opts)
|
||||
end.
|
||||
|
||||
%% Only get access, shaper and max_stanza_size values
|
||||
|
||||
select_opts_values(Opts) ->
|
||||
@ -65,6 +62,3 @@ select_opts_values([{max_stanza_size, Value} | Opts],
|
||||
[{max_stanza_size, Value} | SelectedValues]);
|
||||
select_opts_values([_Opt | Opts], SelectedValues) ->
|
||||
select_opts_values(Opts, SelectedValues).
|
||||
|
||||
opt_type(listen) -> fun (V) -> V end;
|
||||
opt_type(_) -> [listen].
|
||||
|
@ -395,7 +395,8 @@ get_transfer_protocol(PortString) ->
|
||||
get_captcha_transfer_protocol(PortListeners).
|
||||
|
||||
get_port_listeners(PortNumber) ->
|
||||
AllListeners = ejabberd_config:get_option(listen, fun(V) -> V end),
|
||||
AllListeners = ejabberd_config:get_option(
|
||||
listen, fun ejabberd_listener:validate_cfg/1, []),
|
||||
lists:filter(fun (Listener) when is_list(Listener) ->
|
||||
case proplists:get_value(port, Listener) of
|
||||
PortNumber -> true;
|
||||
@ -545,6 +546,5 @@ opt_type(captcha_cmd) ->
|
||||
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(listen) -> fun (V) -> V end;
|
||||
opt_type(_) ->
|
||||
[captcha_cmd, captcha_host, captcha_limit, listen].
|
||||
[captcha_cmd, captcha_host, captcha_limit].
|
||||
|
@ -29,7 +29,7 @@
|
||||
-export([start/0, load_file/1, reload_file/0, read_file/1,
|
||||
get_option/2, get_option/3, add_option/2, has_option/1,
|
||||
get_vh_by_auth_method/1, is_file_readable/1,
|
||||
get_version/0, get_myhosts/0, get_mylang/0,
|
||||
get_version/0, get_myhosts/0, get_mylang/0, get_lang/1,
|
||||
get_ejabberd_config_path/0, is_using_elixir_config/0,
|
||||
prepare_opt_val/4, transform_options/1, collect_options/1,
|
||||
convert_to_yaml/1, convert_to_yaml/2, v_db/2,
|
||||
@ -1067,8 +1067,12 @@ get_myhosts() ->
|
||||
-spec get_mylang() -> binary().
|
||||
|
||||
get_mylang() ->
|
||||
get_lang(global).
|
||||
|
||||
-spec get_lang(global | binary()) -> binary().
|
||||
get_lang(Host) ->
|
||||
get_option(
|
||||
language,
|
||||
{language, Host},
|
||||
fun iolist_to_binary/1,
|
||||
<<"en">>).
|
||||
|
||||
@ -1206,7 +1210,6 @@ transform_terms(Terms) ->
|
||||
%% We could check all ejabberd beams, but this
|
||||
%% slows down start-up procedure :(
|
||||
Mods = [mod_register,
|
||||
mod_last,
|
||||
ejabberd_s2s,
|
||||
ejabberd_listener,
|
||||
ejabberd_sql_sup,
|
||||
@ -1315,6 +1318,10 @@ transform_options(Opt, Opts) when Opt == override_global;
|
||||
Opt == override_acls ->
|
||||
?WARNING_MSG("Ignoring '~s' option which has no effect anymore", [Opt]),
|
||||
Opts;
|
||||
transform_options({node_start, {_, _, _} = Now}, Opts) ->
|
||||
?WARNING_MSG("Old 'node_start' format detected. This is still supported "
|
||||
"but it is better to fix your config.", []),
|
||||
[{node_start, now_to_seconds(Now)}|Opts];
|
||||
transform_options({host_config, Host, HOpts}, Opts) ->
|
||||
{AddOpts, HOpts1} =
|
||||
lists:mapfoldl(
|
||||
@ -1351,6 +1358,10 @@ emit_deprecation_warning(Module, NewModule) ->
|
||||
[Module, NewModule])
|
||||
end.
|
||||
|
||||
-spec now_to_seconds(erlang:timestamp()) -> non_neg_integer().
|
||||
now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
|
||||
MegaSecs * 1000000 + Secs.
|
||||
|
||||
opt_type(hide_sensitive_log_data) ->
|
||||
fun (H) when is_boolean(H) -> H end;
|
||||
opt_type(hosts) ->
|
||||
@ -1388,10 +1399,17 @@ opt_type(cache_life_time) ->
|
||||
(infinity) -> infinity;
|
||||
(unlimited) -> infinity
|
||||
end;
|
||||
opt_type(domain_certfile) ->
|
||||
fun iolist_to_binary/1;
|
||||
opt_type(shared_key) ->
|
||||
fun iolist_to_binary/1;
|
||||
opt_type(node_start) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
opt_type(_) ->
|
||||
[hide_sensitive_log_data, hosts, language, max_fsm_queue,
|
||||
default_db, default_ram_db, queue_type, queue_dir, loglevel,
|
||||
use_cache, cache_size, cache_missed, cache_life_time].
|
||||
use_cache, cache_size, cache_missed, cache_life_time,
|
||||
domain_certfile, shared_key, node_start].
|
||||
|
||||
-spec may_hide_data(any()) -> any().
|
||||
may_hide_data(Data) ->
|
||||
|
@ -34,12 +34,9 @@
|
||||
|
||||
-module(ejabberd_piefxis).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-protocol({xep, 227, '1.0'}).
|
||||
|
||||
-export([import_file/1, export_server/1, export_host/2,
|
||||
opt_type/1]).
|
||||
-export([import_file/1, export_server/1, export_host/2]).
|
||||
|
||||
-define(CHUNK_SIZE, 1024*20). %20k
|
||||
|
||||
@ -169,7 +166,7 @@ export_users([], _Server, _Fd) ->
|
||||
export_user(User, Server, Fd) ->
|
||||
Password = ejabberd_auth:get_password_s(User, Server),
|
||||
LServer = jid:nameprep(Server),
|
||||
PasswordFormat = ejabberd_config:get_option({auth_password_format, LServer}, fun(X) -> X end, plain),
|
||||
PasswordFormat = ejabberd_auth:password_format(LServer),
|
||||
Pass = case Password of
|
||||
{_,_,_,_} ->
|
||||
case PasswordFormat of
|
||||
@ -389,7 +386,7 @@ process_user(#xmlel{name = <<"user">>, attrs = Attrs, children = Els},
|
||||
#state{server = LServer} = State) ->
|
||||
Name = fxml:get_attr_s(<<"name">>, Attrs),
|
||||
Password = fxml:get_attr_s(<<"password">>, Attrs),
|
||||
PasswordFormat = ejabberd_config:get_option({auth_password_format, LServer}, fun(X) -> X end, plain),
|
||||
PasswordFormat = ejabberd_auth:password_format(LServer),
|
||||
Pass = case PasswordFormat of
|
||||
scram ->
|
||||
case Password of
|
||||
@ -596,7 +593,3 @@ make_xinclude(Fn) ->
|
||||
|
||||
print(Fd, String) ->
|
||||
file:write(Fd, String).
|
||||
|
||||
opt_type(auth_password_format) -> fun (X) -> X end;
|
||||
opt_type(_) -> [auth_password_format].
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
-define(GEN_SERVER, gen_server).
|
||||
-endif.
|
||||
-behaviour(?GEN_SERVER).
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
%% API
|
||||
-export([start_link/4,
|
||||
@ -41,7 +42,8 @@
|
||||
starttls/2,
|
||||
compress/2,
|
||||
become_controller/2,
|
||||
close/1]).
|
||||
close/1,
|
||||
opt_type/1]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
@ -59,9 +61,6 @@
|
||||
xml_stream_state :: fxml_stream:xml_stream_state() | undefined,
|
||||
timeout = infinity:: timeout()}).
|
||||
|
||||
-define(HIBERNATE_TIMEOUT, ejabberd_config:get_option(receiver_hibernate, fun(X) when is_integer(X); X == hibernate-> X end, 90000)).
|
||||
|
||||
|
||||
-spec start_link(inet:socket(), atom(), shaper:shaper(),
|
||||
non_neg_integer() | infinity) -> ignore |
|
||||
{error, any()} |
|
||||
@ -137,7 +136,7 @@ handle_call({starttls, TLSSocket}, _From, State) ->
|
||||
case fast_tls:recv_data(TLSSocket, <<"">>) of
|
||||
{ok, TLSData} ->
|
||||
{reply, ok,
|
||||
process_data(TLSData, NewState), ?HIBERNATE_TIMEOUT};
|
||||
process_data(TLSData, NewState), hibernate_timeout()};
|
||||
{error, _} = Err ->
|
||||
{stop, normal, Err, NewState}
|
||||
end;
|
||||
@ -156,31 +155,31 @@ handle_call({compress, Data}, _From,
|
||||
case ezlib:recv_data(ZlibSocket, <<"">>) of
|
||||
{ok, ZlibData} ->
|
||||
{reply, {ok, ZlibSocket},
|
||||
process_data(ZlibData, NewState), ?HIBERNATE_TIMEOUT};
|
||||
process_data(ZlibData, NewState), hibernate_timeout()};
|
||||
{error, _} = Err ->
|
||||
{stop, normal, Err, NewState}
|
||||
end;
|
||||
handle_call(reset_stream, _From, State) ->
|
||||
NewState = reset_parser(State),
|
||||
Reply = ok,
|
||||
{reply, Reply, NewState, ?HIBERNATE_TIMEOUT};
|
||||
{reply, Reply, NewState, hibernate_timeout()};
|
||||
handle_call({become_controller, C2SPid}, _From, State) ->
|
||||
XMLStreamState = fxml_stream:new(C2SPid, State#state.max_stanza_size),
|
||||
NewState = State#state{c2s_pid = C2SPid,
|
||||
xml_stream_state = XMLStreamState},
|
||||
activate_socket(NewState),
|
||||
Reply = ok,
|
||||
{reply, Reply, NewState, ?HIBERNATE_TIMEOUT};
|
||||
{reply, Reply, NewState, hibernate_timeout()};
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok, {reply, Reply, State, ?HIBERNATE_TIMEOUT}.
|
||||
Reply = ok, {reply, Reply, State, hibernate_timeout()}.
|
||||
|
||||
handle_cast({change_shaper, Shaper}, State) ->
|
||||
NewShaperState = shaper:new(Shaper),
|
||||
{noreply, State#state{shaper_state = NewShaperState},
|
||||
?HIBERNATE_TIMEOUT};
|
||||
hibernate_timeout()};
|
||||
handle_cast(close, State) -> {stop, normal, State};
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State, ?HIBERNATE_TIMEOUT}.
|
||||
{noreply, State, hibernate_timeout()}.
|
||||
|
||||
handle_info({Tag, _TCPSocket, Data},
|
||||
#state{socket = Socket, sock_mod = SockMod} = State)
|
||||
@ -191,7 +190,7 @@ handle_info({Tag, _TCPSocket, Data},
|
||||
case fast_tls:recv_data(Socket, Data) of
|
||||
{ok, TLSData} ->
|
||||
{noreply, process_data(TLSData, State),
|
||||
?HIBERNATE_TIMEOUT};
|
||||
hibernate_timeout()};
|
||||
{error, Reason} ->
|
||||
if is_binary(Reason) ->
|
||||
?DEBUG("TLS error = ~s", [Reason]);
|
||||
@ -204,11 +203,11 @@ handle_info({Tag, _TCPSocket, Data},
|
||||
case ezlib:recv_data(Socket, Data) of
|
||||
{ok, ZlibData} ->
|
||||
{noreply, process_data(ZlibData, State),
|
||||
?HIBERNATE_TIMEOUT};
|
||||
hibernate_timeout()};
|
||||
{error, _Reason} -> {stop, normal, State}
|
||||
end;
|
||||
_ ->
|
||||
{noreply, process_data(Data, State), ?HIBERNATE_TIMEOUT}
|
||||
{noreply, process_data(Data, State), hibernate_timeout()}
|
||||
end;
|
||||
handle_info({Tag, _TCPSocket}, State)
|
||||
when (Tag == tcp_closed) or (Tag == ssl_closed) ->
|
||||
@ -216,18 +215,18 @@ handle_info({Tag, _TCPSocket}, State)
|
||||
handle_info({Tag, _TCPSocket, Reason}, State)
|
||||
when (Tag == tcp_error) or (Tag == ssl_error) ->
|
||||
case Reason of
|
||||
timeout -> {noreply, State, ?HIBERNATE_TIMEOUT};
|
||||
timeout -> {noreply, State, hibernate_timeout()};
|
||||
_ -> {stop, normal, State}
|
||||
end;
|
||||
handle_info({timeout, _Ref, activate}, State) ->
|
||||
activate_socket(State),
|
||||
{noreply, State, ?HIBERNATE_TIMEOUT};
|
||||
{noreply, State, hibernate_timeout()};
|
||||
handle_info(timeout, State) ->
|
||||
proc_lib:hibernate(?GEN_SERVER, enter_loop,
|
||||
[?MODULE, [], State]),
|
||||
{noreply, State, ?HIBERNATE_TIMEOUT};
|
||||
{noreply, State, hibernate_timeout()};
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State, ?HIBERNATE_TIMEOUT}.
|
||||
{noreply, State, hibernate_timeout()}.
|
||||
|
||||
terminate(_Reason,
|
||||
#state{xml_stream_state = XMLStreamState,
|
||||
@ -345,3 +344,15 @@ do_call(Pid, Msg) ->
|
||||
_:_ ->
|
||||
{error, einval}
|
||||
end.
|
||||
|
||||
hibernate_timeout() ->
|
||||
ejabberd_config:get_option(receiver_hibernate,
|
||||
opt_type(receiver_hibernate),
|
||||
timer:seconds(90)).
|
||||
|
||||
opt_type(receiver_hibernate) ->
|
||||
fun(I) when is_integer(I), I>0 -> I;
|
||||
(hibernate) -> hibernate
|
||||
end;
|
||||
opt_type(_) ->
|
||||
[receiver_hibernate].
|
||||
|
@ -23,6 +23,7 @@
|
||||
-module(ejabberd_redis_sup).
|
||||
|
||||
-behaviour(supervisor).
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
%% API
|
||||
-export([start_link/0, get_pool_size/0,
|
||||
|
@ -210,7 +210,6 @@ transform_options({riak_server, {S, P}}, Opts) ->
|
||||
transform_options(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
opt_type(modules) -> fun (L) when is_list(L) -> L end;
|
||||
opt_type(riak_pool_size) ->
|
||||
fun (N) when is_integer(N), N >= 1 -> N end;
|
||||
opt_type(riak_port) -> fun (_) -> true end;
|
||||
@ -221,5 +220,5 @@ opt_type(riak_cacertfile) -> fun iolist_to_binary/1;
|
||||
opt_type(riak_username) -> fun iolist_to_binary/1;
|
||||
opt_type(riak_password) -> fun iolist_to_binary/1;
|
||||
opt_type(_) ->
|
||||
[modules, riak_pool_size, riak_port, riak_server,
|
||||
[riak_pool_size, riak_port, riak_server,
|
||||
riak_start_interval, riak_cacertfile, riak_username, riak_password].
|
||||
|
@ -724,7 +724,6 @@ opt_type(route_subdomains) ->
|
||||
end;
|
||||
opt_type(s2s_access) ->
|
||||
fun acl:access_rules_validator/1;
|
||||
opt_type(domain_certfile) -> fun iolist_to_binary/1;
|
||||
opt_type(s2s_certfile) -> fun iolist_to_binary/1;
|
||||
opt_type(s2s_ciphers) -> fun iolist_to_binary/1;
|
||||
opt_type(s2s_dhfile) -> fun iolist_to_binary/1;
|
||||
@ -742,6 +741,8 @@ opt_type(s2s_use_starttls) ->
|
||||
(required) -> required;
|
||||
(required_trusted) -> required_trusted
|
||||
end;
|
||||
opt_type(s2s_zlib) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
opt_type(s2s_timeout) ->
|
||||
fun(I) when is_integer(I), I>=0 -> I;
|
||||
(infinity) -> infinity
|
||||
@ -749,6 +750,6 @@ opt_type(s2s_timeout) ->
|
||||
opt_type(s2s_queue_type) ->
|
||||
fun(ram) -> ram; (file) -> file end;
|
||||
opt_type(_) ->
|
||||
[route_subdomains, s2s_access, s2s_certfile,
|
||||
[route_subdomains, s2s_access, s2s_certfile, s2s_zlib,
|
||||
s2s_ciphers, s2s_dhfile, s2s_cafile, s2s_protocol_options,
|
||||
s2s_tls_compression, s2s_use_starttls, s2s_timeout, s2s_queue_type].
|
||||
|
@ -27,13 +27,12 @@
|
||||
-define(GEN_SERVER, p1_server).
|
||||
-endif.
|
||||
-behaviour(?GEN_SERVER).
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-behaviour(ejabberd_sm).
|
||||
|
||||
-export([init/0, set_session/1, delete_session/1,
|
||||
get_sessions/0, get_sessions/1, get_sessions/2,
|
||||
cache_nodes/1, opt_type/1]).
|
||||
cache_nodes/1]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_cast/2, handle_call/3, handle_info/2,
|
||||
terminate/2, code_change/3, start_link/0]).
|
||||
@ -169,9 +168,6 @@ code_change(_OldVsn, State, _Extra) ->
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
iolist_to_list(IOList) ->
|
||||
binary_to_list(iolist_to_binary(IOList)).
|
||||
|
||||
us_to_key({LUser, LServer}) ->
|
||||
<<"ejabberd:sm:", LUser/binary, "@", LServer/binary>>.
|
||||
|
||||
@ -214,17 +210,3 @@ clean_table() ->
|
||||
catch _:{badmatch, {error, _}} ->
|
||||
?ERROR_MSG("failed to clean redis c2s sessions", [])
|
||||
end.
|
||||
|
||||
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_reconnect_timeout) ->
|
||||
fun (I) when is_integer(I), I > 0 -> I end;
|
||||
opt_type(redis_server) -> fun iolist_to_list/1;
|
||||
opt_type(_) ->
|
||||
[redis_connect_timeout, redis_db, redis_password,
|
||||
redis_port, redis_reconnect_timeout, redis_server].
|
||||
|
@ -1109,13 +1109,6 @@ 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_type) ->
|
||||
fun (mysql) -> mysql;
|
||||
(pgsql) -> pgsql;
|
||||
(sqlite) -> sqlite;
|
||||
(mssql) -> mssql;
|
||||
(odbc) -> odbc
|
||||
end;
|
||||
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;
|
||||
@ -1125,6 +1118,6 @@ opt_type(sql_queue_type) ->
|
||||
fun(ram) -> ram; (file) -> file end;
|
||||
opt_type(_) ->
|
||||
[sql_database, sql_keepalive_interval,
|
||||
sql_password, sql_port, sql_server, sql_type,
|
||||
sql_password, sql_port, sql_server,
|
||||
sql_username, sql_ssl, sql_ssl_verify, sql_ssl_cerfile,
|
||||
sql_ssl_cafile, sql_queue_type].
|
||||
|
@ -230,12 +230,5 @@ 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_type) ->
|
||||
fun (mysql) -> mysql;
|
||||
(pgsql) -> pgsql;
|
||||
(sqlite) -> sqlite;
|
||||
(mssql) -> mssql;
|
||||
(odbc) -> odbc
|
||||
end;
|
||||
opt_type(_) ->
|
||||
[sql_pool_size, sql_start_interval, sql_type].
|
||||
[sql_pool_size, sql_start_interval].
|
||||
|
@ -2980,8 +2980,7 @@ make_menu_item(item, 3, URI, Name, Lang) ->
|
||||
%%%==================================
|
||||
|
||||
|
||||
opt_type(access) -> fun acl:access_rules_validator/1;
|
||||
opt_type(access_readonly) -> fun acl:access_rules_validator/1;
|
||||
opt_type(_) -> [access, access_readonly].
|
||||
opt_type(_) -> [access_readonly].
|
||||
|
||||
%%% vim: set foldmethod=marker foldmarker=%%%%,%%%=:
|
||||
|
@ -28,7 +28,7 @@
|
||||
-behaviour(ejabberd_config).
|
||||
-author('mremond@process-one.net').
|
||||
|
||||
-export([generate_subfilter/1, find_ldap_attrs/2,
|
||||
-export([generate_subfilter/1, find_ldap_attrs/2, check_filter/1,
|
||||
get_ldap_attr/2, get_user_part/2, make_filter/2,
|
||||
get_state/2, case_insensitive_match/2, get_config/2,
|
||||
decode_octet_string/3, uids_domain_subst/2, opt_type/1]).
|
||||
@ -137,6 +137,11 @@ make_filter(Data, UIDs) ->
|
||||
eldap:'and'(Filter)
|
||||
end.
|
||||
|
||||
check_filter(F) ->
|
||||
NewF = iolist_to_binary(F),
|
||||
{ok, _} = eldap_filter:parse(NewF),
|
||||
NewF.
|
||||
|
||||
-spec case_insensitive_match(binary(), binary()) -> boolean().
|
||||
|
||||
case_insensitive_match(X, Y) ->
|
||||
@ -380,8 +385,19 @@ opt_type(ldap_tls_verify) ->
|
||||
(soft) -> soft;
|
||||
(false) -> false
|
||||
end;
|
||||
opt_type(ldap_filter) ->
|
||||
fun check_filter/1;
|
||||
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,
|
||||
[deref_aliases, ldap_backups, ldap_base, ldap_uids,
|
||||
ldap_deref_aliases, ldap_encrypt, ldap_password,
|
||||
ldap_port, ldap_rootdn, ldap_servers,
|
||||
ldap_port, ldap_rootdn, ldap_servers, ldap_filter,
|
||||
ldap_tls_cacertfile, ldap_tls_depth, ldap_tls_verify].
|
||||
|
@ -25,8 +25,6 @@
|
||||
|
||||
-module(mod_last).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-protocol({xep, 12, '2.0'}).
|
||||
@ -36,8 +34,8 @@
|
||||
-export([start/2, stop/1, reload/3, process_local_iq/1, export/1,
|
||||
process_sm_iq/1, on_presence_update/4, import_info/0,
|
||||
import/5, import_start/2, store_last_info/4, get_last_info/2,
|
||||
remove_user/2, transform_options/1, mod_opt_type/1,
|
||||
opt_type/1, register_user/2, depends/2, privacy_check_packet/4]).
|
||||
remove_user/2, mod_opt_type/1,
|
||||
register_user/2, depends/2, privacy_check_packet/4]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@ -130,10 +128,6 @@ get_node_uptime() ->
|
||||
p1_time_compat:system_time(seconds) - Now
|
||||
end.
|
||||
|
||||
-spec now_to_seconds(erlang:timestamp()) -> non_neg_integer().
|
||||
now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
|
||||
MegaSecs * 1000000 + Secs.
|
||||
|
||||
%%%
|
||||
%%% Serve queries about user last online
|
||||
%%%
|
||||
@ -273,23 +267,9 @@ export(LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:export(LServer).
|
||||
|
||||
transform_options(Opts) ->
|
||||
lists:foldl(fun transform_options/2, [], Opts).
|
||||
|
||||
transform_options({node_start, {_, _, _} = Now}, Opts) ->
|
||||
?WARNING_MSG("Old 'node_start' format detected. This is still supported "
|
||||
"but it is better to fix your config.", []),
|
||||
[{node_start, now_to_seconds(Now)}|Opts];
|
||||
transform_options(Opt, Opts) ->
|
||||
[Opt|Opts].
|
||||
|
||||
depends(_Host, _Opts) ->
|
||||
[].
|
||||
|
||||
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
mod_opt_type(_) -> [db_type, iqdisc].
|
||||
|
||||
opt_type(node_start) ->
|
||||
fun (S) when is_integer(S), S >= 0 -> S end;
|
||||
opt_type(_) -> [node_start].
|
||||
|
@ -27,8 +27,6 @@
|
||||
|
||||
-protocol({xep, 334, '0.2'}).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('badlop@process-one.net').
|
||||
|
||||
-behaviour(gen_server).
|
||||
@ -41,7 +39,7 @@
|
||||
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3,
|
||||
mod_opt_type/1, opt_type/1, depends/2]).
|
||||
mod_opt_type/1, depends/2]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@ -182,10 +180,7 @@ init_state(Host, Opts) ->
|
||||
NoFollow = gen_mod:get_opt(spam_prevention, Opts,
|
||||
fun(B) when is_boolean(B) -> B end,
|
||||
true),
|
||||
Lang = ejabberd_config:get_option(
|
||||
{language, Host},
|
||||
fun iolist_to_binary/1,
|
||||
?MYLANG),
|
||||
Lang = ejabberd_config:get_lang(Host),
|
||||
#logstate{host = Host, out_dir = OutDir,
|
||||
dir_type = DirType, dir_name = DirName,
|
||||
file_format = FileFormat, css_file = CSSFile,
|
||||
@ -1242,6 +1237,3 @@ mod_opt_type(_) ->
|
||||
[access_log, cssfile, dirname, dirtype, file_format,
|
||||
file_permissions, outdir, spam_prevention, timezone,
|
||||
top_link].
|
||||
|
||||
opt_type(language) -> fun iolist_to_binary/1;
|
||||
opt_type(_) -> [language].
|
||||
|
@ -499,13 +499,13 @@ parse_options(Host, Opts) ->
|
||||
(true) -> true
|
||||
end, true),
|
||||
ConfigFilter = gen_mod:get_opt({ldap_filter, Host}, Opts,
|
||||
fun check_filter/1, <<"">>),
|
||||
fun eldap_utils:check_filter/1, <<"">>),
|
||||
ConfigUserFilter = gen_mod:get_opt({ldap_ufilter, Host}, Opts,
|
||||
fun check_filter/1, <<"">>),
|
||||
fun eldap_utils:check_filter/1, <<"">>),
|
||||
ConfigGroupFilter = gen_mod:get_opt({ldap_gfilter, Host}, Opts,
|
||||
fun check_filter/1, <<"">>),
|
||||
fun eldap_utils:check_filter/1, <<"">>),
|
||||
RosterFilter = gen_mod:get_opt({ldap_rfilter, Host}, Opts,
|
||||
fun check_filter/1, <<"">>),
|
||||
fun eldap_utils:check_filter/1, <<"">>),
|
||||
SubFilter = <<"(&(", UIDAttr/binary, "=",
|
||||
UIDAttrFormat/binary, ")(", GroupAttr/binary, "=%g))">>,
|
||||
UserSubFilter = case ConfigUserFilter of
|
||||
@ -552,11 +552,6 @@ parse_options(Host, Opts) ->
|
||||
ufilter = UserFilter, rfilter = RosterFilter,
|
||||
gfilter = GroupFilter, auth_check = AuthCheck}.
|
||||
|
||||
check_filter(F) ->
|
||||
NewF = iolist_to_binary(F),
|
||||
{ok, _} = eldap_filter:parse(NewF),
|
||||
NewF.
|
||||
|
||||
init_cache(Host, Opts) ->
|
||||
UseCache = use_cache(Host, Opts),
|
||||
case UseCache of
|
||||
@ -654,8 +649,8 @@ mod_opt_type(ldap_auth_check) ->
|
||||
(false) -> false;
|
||||
(true) -> true
|
||||
end;
|
||||
mod_opt_type(ldap_filter) -> fun check_filter/1;
|
||||
mod_opt_type(ldap_gfilter) -> fun check_filter/1;
|
||||
mod_opt_type(ldap_filter) -> fun eldap_utils:check_filter/1;
|
||||
mod_opt_type(ldap_gfilter) -> fun eldap_utils:check_filter/1;
|
||||
mod_opt_type(O) when O == cache_size;
|
||||
O == cache_life_time ->
|
||||
fun (I) when is_integer(I), I > 0 -> I;
|
||||
@ -672,8 +667,8 @@ mod_opt_type(ldap_memberattr_format_re) ->
|
||||
fun (S) ->
|
||||
Re = iolist_to_binary(S), {ok, MP} = re:compile(Re), MP
|
||||
end;
|
||||
mod_opt_type(ldap_rfilter) -> fun check_filter/1;
|
||||
mod_opt_type(ldap_ufilter) -> fun check_filter/1;
|
||||
mod_opt_type(ldap_rfilter) -> fun eldap_utils:check_filter/1;
|
||||
mod_opt_type(ldap_ufilter) -> fun eldap_utils:check_filter/1;
|
||||
mod_opt_type(ldap_userdesc) -> fun iolist_to_binary/1;
|
||||
mod_opt_type(ldap_useruid) -> fun iolist_to_binary/1;
|
||||
mod_opt_type(_) ->
|
||||
@ -687,9 +682,8 @@ mod_opt_type(_) ->
|
||||
ldap_tls_cacertfile, ldap_tls_certfile, ldap_tls_depth,
|
||||
ldap_tls_verify, use_cache, cache_missed, cache_size, cache_life_time].
|
||||
|
||||
opt_type(ldap_filter) -> fun check_filter/1;
|
||||
opt_type(ldap_gfilter) -> fun check_filter/1;
|
||||
opt_type(ldap_rfilter) -> fun check_filter/1;
|
||||
opt_type(ldap_ufilter) -> fun check_filter/1;
|
||||
opt_type(ldap_gfilter) -> fun eldap_utils:check_filter/1;
|
||||
opt_type(ldap_rfilter) -> fun eldap_utils:check_filter/1;
|
||||
opt_type(ldap_ufilter) -> fun eldap_utils:check_filter/1;
|
||||
opt_type(_) ->
|
||||
[ldap_filter, ldap_gfilter, ldap_rfilter, ldap_ufilter].
|
||||
[ldap_gfilter, ldap_rfilter, ldap_ufilter].
|
||||
|
@ -24,8 +24,6 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(mod_sip_proxy).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-define(GEN_FSM, p1_fsm).
|
||||
-behaviour(?GEN_FSM).
|
||||
|
||||
@ -35,7 +33,7 @@
|
||||
-export([init/1, wait_for_request/2,
|
||||
wait_for_response/2, handle_event/3,
|
||||
handle_sync_event/4, handle_info/3, terminate/3,
|
||||
code_change/4, opt_type/1]).
|
||||
code_change/4]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@ -455,7 +453,3 @@ safe_nameprep(S) ->
|
||||
error -> S;
|
||||
S1 -> S1
|
||||
end.
|
||||
|
||||
opt_type(domain_certfile) -> fun iolist_to_binary/1;
|
||||
opt_type(shared_key) -> fun (V) -> V end;
|
||||
opt_type(_) -> [domain_certfile, shared_key].
|
||||
|
@ -379,7 +379,7 @@ parse_options(Host, Opts) ->
|
||||
SubFilter = eldap_utils:generate_subfilter(UIDs),
|
||||
UserFilter = case gen_mod:get_opt(
|
||||
{ldap_filter, Host}, Opts,
|
||||
fun check_filter/1, <<"">>) of
|
||||
fun eldap_utils:check_filter/1, <<"">>) of
|
||||
<<"">> ->
|
||||
SubFilter;
|
||||
F ->
|
||||
@ -447,12 +447,7 @@ parse_options(Host, Opts) ->
|
||||
search_reported_attrs = SearchReportedAttrs,
|
||||
matches = Matches}.
|
||||
|
||||
check_filter(F) ->
|
||||
NewF = iolist_to_binary(F),
|
||||
{ok, _} = eldap_filter:parse(NewF),
|
||||
NewF.
|
||||
|
||||
mod_opt_type(ldap_filter) -> fun check_filter/1;
|
||||
mod_opt_type(ldap_filter) -> fun eldap_utils:check_filter/1;
|
||||
mod_opt_type(ldap_search_fields) ->
|
||||
fun (Ls) ->
|
||||
[{iolist_to_binary(S), iolist_to_binary(P)}
|
||||
@ -525,17 +520,8 @@ mod_opt_type(_) ->
|
||||
ldap_tls_cacertfile, ldap_tls_certfile, ldap_tls_depth,
|
||||
ldap_tls_verify].
|
||||
|
||||
opt_type(ldap_filter) -> fun check_filter/1;
|
||||
opt_type(ldap_uids) ->
|
||||
fun (Us) ->
|
||||
lists:map(fun ({U, P}) ->
|
||||
{iolist_to_binary(U), iolist_to_binary(P)};
|
||||
({U}) -> {iolist_to_binary(U)}
|
||||
end,
|
||||
Us)
|
||||
end;
|
||||
opt_type(_) ->
|
||||
[ldap_filter, ldap_uids, deref_aliases, ldap_backups, ldap_base,
|
||||
[deref_aliases, ldap_backups, ldap_base,
|
||||
ldap_deref_aliases, ldap_encrypt, ldap_password,
|
||||
ldap_port, ldap_rootdn, ldap_servers,
|
||||
ldap_tls_cacertfile, ldap_tls_certfile, ldap_tls_depth,
|
||||
|
@ -634,13 +634,6 @@ set_roster_version(LUser, Version) ->
|
||||
["!username=%(LUser)s",
|
||||
"version=%(Version)s"]).
|
||||
|
||||
opt_type(sql_type) ->
|
||||
fun (pgsql) -> pgsql;
|
||||
(mysql) -> mysql;
|
||||
(sqlite) -> sqlite;
|
||||
(mssql) -> mssql;
|
||||
(odbc) -> odbc
|
||||
end;
|
||||
opt_type(pgsql_users_number_estimate) ->
|
||||
fun (V) when is_boolean(V) -> V end;
|
||||
opt_type(_) -> [sql_type, pgsql_users_number_estimate].
|
||||
opt_type(_) -> [pgsql_users_number_estimate].
|
||||
|
Loading…
Reference in New Issue
Block a user