mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-26 17:38:45 +01:00
Merge branch 'master' into muc-self-presence
This commit is contained in:
commit
63dba3fd64
@ -109,9 +109,9 @@ CREATE TABLE archive (
|
|||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX i_archive_sh_username_timestamp ON archive (server_host, username, timestamp);
|
CREATE INDEX i_archive_sh_username_timestamp ON archive (server_host, username, timestamp);
|
||||||
|
CREATE INDEX i_archive_sh_username_peer ON archive (server_host, username, peer);
|
||||||
|
CREATE INDEX i_archive_sh_username_bare_peer ON archive (server_host, username, bare_peer);
|
||||||
CREATE INDEX i_archive_sh_timestamp ON archive (server_host, timestamp);
|
CREATE INDEX i_archive_sh_timestamp ON archive (server_host, timestamp);
|
||||||
CREATE INDEX i_archive_sh_peer ON archive (server_host, peer);
|
|
||||||
CREATE INDEX i_archive_sh_bare_peer ON archive (server_host, bare_peer);
|
|
||||||
|
|
||||||
CREATE TABLE archive_prefs (
|
CREATE TABLE archive_prefs (
|
||||||
username text NOT NULL,
|
username text NOT NULL,
|
||||||
|
@ -98,9 +98,9 @@ CREATE TABLE archive (
|
|||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX i_username_timestamp ON archive(username, timestamp);
|
CREATE INDEX i_username_timestamp ON archive(username, timestamp);
|
||||||
|
CREATE INDEX i_archive_username_peer ON archive (username, peer);
|
||||||
|
CREATE INDEX i_archive_username_bare_peer ON archive (username, bare_peer);
|
||||||
CREATE INDEX i_timestamp ON archive(timestamp);
|
CREATE INDEX i_timestamp ON archive(timestamp);
|
||||||
CREATE INDEX i_peer ON archive(peer);
|
|
||||||
CREATE INDEX i_bare_peer ON archive(bare_peer);
|
|
||||||
|
|
||||||
CREATE TABLE archive_prefs (
|
CREATE TABLE archive_prefs (
|
||||||
username text NOT NULL PRIMARY KEY,
|
username text NOT NULL PRIMARY KEY,
|
||||||
|
@ -41,15 +41,15 @@ CREATE TABLE [dbo].[archive] (
|
|||||||
CREATE INDEX [archive_username_timestamp] ON [archive] (username, timestamp)
|
CREATE INDEX [archive_username_timestamp] ON [archive] (username, timestamp)
|
||||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||||
|
|
||||||
|
CREATE INDEX [archive_username_peer] ON [archive] (username, peer)
|
||||||
|
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||||
|
|
||||||
|
CREATE INDEX [archive_username_bare_peer] ON [archive] (username, bare_peer)
|
||||||
|
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||||
|
|
||||||
CREATE INDEX [archive_timestamp] ON [archive] (timestamp)
|
CREATE INDEX [archive_timestamp] ON [archive] (timestamp)
|
||||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||||
|
|
||||||
CREATE INDEX [archive_peer] ON [archive] (peer)
|
|
||||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
|
||||||
|
|
||||||
CREATE INDEX [archive_bare_peer] ON [archive] (bare_peer)
|
|
||||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
|
||||||
|
|
||||||
CREATE TABLE [dbo].[archive_prefs] (
|
CREATE TABLE [dbo].[archive_prefs] (
|
||||||
[username] [varchar] (250) NOT NULL,
|
[username] [varchar] (250) NOT NULL,
|
||||||
[def] [text] NOT NULL,
|
[def] [text] NOT NULL,
|
||||||
|
@ -113,10 +113,10 @@ CREATE TABLE archive (
|
|||||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
CREATE FULLTEXT INDEX i_text ON archive(txt);
|
CREATE FULLTEXT INDEX i_text ON archive(txt);
|
||||||
CREATE INDEX i_archive_sh_username_timestamp USING BTREE ON archive(server_host(191), username,timestamp);
|
CREATE INDEX i_archive_sh_username_timestamp USING BTREE ON archive(server_host(191), username(191), timestamp);
|
||||||
|
CREATE INDEX i_archive_sh_username_peer USING BTREE ON archive(server_host(191), username(191), peer(191));
|
||||||
|
CREATE INDEX i_archive_sh_username_bare_peer USING BTREE ON archive(server_host(191), username(191), bare_peer(191));
|
||||||
CREATE INDEX i_archive_sh_timestamp USING BTREE ON archive(server_host(191), timestamp);
|
CREATE INDEX i_archive_sh_timestamp USING BTREE ON archive(server_host(191), timestamp);
|
||||||
CREATE INDEX i_archive_sh_peer USING BTREE ON archive(server_host(191), peer);
|
|
||||||
CREATE INDEX i_archive_sh_bare_peer USING BTREE ON archive(server_host(191), bare_peer);
|
|
||||||
|
|
||||||
CREATE TABLE archive_prefs (
|
CREATE TABLE archive_prefs (
|
||||||
username varchar(191) NOT NULL,
|
username varchar(191) NOT NULL,
|
||||||
|
@ -102,10 +102,10 @@ CREATE TABLE archive (
|
|||||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
CREATE FULLTEXT INDEX i_text ON archive(txt);
|
CREATE FULLTEXT INDEX i_text ON archive(txt);
|
||||||
CREATE INDEX i_username_timestamp USING BTREE ON archive(username,timestamp);
|
CREATE INDEX i_username_timestamp USING BTREE ON archive(username(191), timestamp);
|
||||||
|
CREATE INDEX i_username_peer USING BTREE ON archive(username(191), peer(191));
|
||||||
|
CREATE INDEX i_username_bare_peer USING BTREE ON archive(username(191), bare_peer(191));
|
||||||
CREATE INDEX i_timestamp USING BTREE ON archive(timestamp);
|
CREATE INDEX i_timestamp USING BTREE ON archive(timestamp);
|
||||||
CREATE INDEX i_peer USING BTREE ON archive(peer);
|
|
||||||
CREATE INDEX i_bare_peer USING BTREE ON archive(bare_peer);
|
|
||||||
|
|
||||||
CREATE TABLE archive_prefs (
|
CREATE TABLE archive_prefs (
|
||||||
username varchar(191) NOT NULL PRIMARY KEY,
|
username varchar(191) NOT NULL PRIMARY KEY,
|
||||||
|
@ -61,15 +61,14 @@
|
|||||||
-- ALTER TABLE spool ALTER COLUMN server_host DROP DEFAULT;
|
-- ALTER TABLE spool ALTER COLUMN server_host DROP DEFAULT;
|
||||||
|
|
||||||
-- ALTER TABLE archive ADD COLUMN server_host text NOT NULL DEFAULT '<HOST>';
|
-- ALTER TABLE archive ADD COLUMN server_host text NOT NULL DEFAULT '<HOST>';
|
||||||
-- DROP INDEX i_username;
|
|
||||||
-- DROP INDEX i_username_timestamp;
|
-- DROP INDEX i_username_timestamp;
|
||||||
|
-- DROP INDEX i_username_peer;
|
||||||
|
-- DROP INDEX i_username_bare_peer;
|
||||||
-- DROP INDEX i_timestamp;
|
-- DROP INDEX i_timestamp;
|
||||||
-- DROP INDEX i_peer;
|
|
||||||
-- DROP INDEX i_bare_peer;
|
|
||||||
-- CREATE INDEX i_archive_sh_username_timestamp ON archive USING btree (server_host, username, timestamp);
|
-- CREATE INDEX i_archive_sh_username_timestamp ON archive USING btree (server_host, username, timestamp);
|
||||||
|
-- CREATE INDEX i_archive_sh_username_peer ON archive USING btree (server_host, username, peer);
|
||||||
|
-- CREATE INDEX i_archive_sh_username_bare_peer ON archive USING btree (server_host, username, bare_peer);
|
||||||
-- CREATE INDEX i_archive_sh_timestamp ON archive USING btree (server_host, timestamp);
|
-- CREATE INDEX i_archive_sh_timestamp ON archive USING btree (server_host, timestamp);
|
||||||
-- CREATE INDEX i_archive_sh_peer ON archive USING btree (server_host, peer);
|
|
||||||
-- CREATE INDEX i_archive_sh_bare_peer ON archive USING btree (server_host, bare_peer);
|
|
||||||
-- ALTER TABLE archive ALTER COLUMN server_host DROP DEFAULT;
|
-- ALTER TABLE archive ALTER COLUMN server_host DROP DEFAULT;
|
||||||
|
|
||||||
-- ALTER TABLE archive_prefs ADD COLUMN server_host text NOT NULL DEFAULT '<HOST>';
|
-- ALTER TABLE archive_prefs ADD COLUMN server_host text NOT NULL DEFAULT '<HOST>';
|
||||||
@ -265,9 +264,9 @@ CREATE TABLE archive (
|
|||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX i_archive_sh_username_timestamp ON archive USING btree (server_host, username, timestamp);
|
CREATE INDEX i_archive_sh_username_timestamp ON archive USING btree (server_host, username, timestamp);
|
||||||
|
CREATE INDEX i_archive_sh_username_peer ON archive USING btree (server_host, username, peer);
|
||||||
|
CREATE INDEX i_archive_sh_username_bare_peer ON archive USING btree (server_host, username, bare_peer);
|
||||||
CREATE INDEX i_archive_sh_timestamp ON archive USING btree (server_host, timestamp);
|
CREATE INDEX i_archive_sh_timestamp ON archive USING btree (server_host, timestamp);
|
||||||
CREATE INDEX i_archive_sh_peer ON archive USING btree (server_host, peer);
|
|
||||||
CREATE INDEX i_archive_sh_bare_peer ON archive USING btree (server_host, bare_peer);
|
|
||||||
|
|
||||||
CREATE TABLE archive_prefs (
|
CREATE TABLE archive_prefs (
|
||||||
username text NOT NULL,
|
username text NOT NULL,
|
||||||
|
@ -102,9 +102,9 @@ CREATE TABLE archive (
|
|||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX i_username_timestamp ON archive USING btree (username, timestamp);
|
CREATE INDEX i_username_timestamp ON archive USING btree (username, timestamp);
|
||||||
|
CREATE INDEX i_username_peer ON archive USING btree (username, peer);
|
||||||
|
CREATE INDEX i_username_bare_peer ON archive USING btree (username, bare_peer);
|
||||||
CREATE INDEX i_timestamp ON archive USING btree (timestamp);
|
CREATE INDEX i_timestamp ON archive USING btree (timestamp);
|
||||||
CREATE INDEX i_peer ON archive USING btree (peer);
|
|
||||||
CREATE INDEX i_bare_peer ON archive USING btree (bare_peer);
|
|
||||||
|
|
||||||
CREATE TABLE archive_prefs (
|
CREATE TABLE archive_prefs (
|
||||||
username text NOT NULL PRIMARY KEY,
|
username text NOT NULL PRIMARY KEY,
|
||||||
|
@ -59,8 +59,8 @@
|
|||||||
|
|
||||||
start(_Opts) ->
|
start(_Opts) ->
|
||||||
Fqdn = get_local_fqdn(),
|
Fqdn = get_local_fqdn(),
|
||||||
?INFO_MSG("FQDN used to check DIGEST-MD5 SASL authentication: ~s",
|
?DEBUG("FQDN used to check DIGEST-MD5 SASL authentication: ~s",
|
||||||
[Fqdn]),
|
[Fqdn]),
|
||||||
cyrsasl:register_mechanism(<<"DIGEST-MD5">>, ?MODULE,
|
cyrsasl:register_mechanism(<<"DIGEST-MD5">>, ?MODULE,
|
||||||
digest).
|
digest).
|
||||||
|
|
||||||
|
@ -81,15 +81,8 @@ prepare(ClientIn) ->
|
|||||||
_ -> error
|
_ -> error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
parse(S) -> parse1(binary_to_list(S), "", []).
|
parse(S) ->
|
||||||
|
binary:split(S, <<0>>, [global]).
|
||||||
parse1([0 | Cs], S, T) ->
|
|
||||||
parse1(Cs, "", [list_to_binary(lists:reverse(S)) | T]);
|
|
||||||
parse1([C | Cs], S, T) -> parse1(Cs, [C | S], T);
|
|
||||||
%parse1([], [], T) ->
|
|
||||||
% lists:reverse(T);
|
|
||||||
parse1([], S, T) ->
|
|
||||||
lists:reverse([list_to_binary(lists:reverse(S)) | T]).
|
|
||||||
|
|
||||||
parse_domain(S) -> parse_domain1(binary_to_list(S), "", []).
|
parse_domain(S) -> parse_domain1(binary_to_list(S), "", []).
|
||||||
|
|
||||||
|
@ -526,7 +526,10 @@ db_get_password(User, Server, Mod) ->
|
|||||||
UseCache = use_cache(Mod, Server),
|
UseCache = use_cache(Mod, Server),
|
||||||
case erlang:function_exported(Mod, get_password, 2) of
|
case erlang:function_exported(Mod, get_password, 2) of
|
||||||
false when UseCache ->
|
false when UseCache ->
|
||||||
ets_cache:lookup(?AUTH_CACHE, {User, Server});
|
case ets_cache:lookup(?AUTH_CACHE, {User, Server}) of
|
||||||
|
{ok, exists} -> error;
|
||||||
|
Other -> Other
|
||||||
|
end;
|
||||||
false ->
|
false ->
|
||||||
error;
|
error;
|
||||||
true when UseCache ->
|
true when UseCache ->
|
||||||
@ -544,7 +547,20 @@ db_user_exists(User, Server, Mod) ->
|
|||||||
error ->
|
error ->
|
||||||
case Mod:store_type(Server) of
|
case Mod:store_type(Server) of
|
||||||
external ->
|
external ->
|
||||||
Mod:user_exists(User, Server);
|
case ets_cache:lookup(
|
||||||
|
?AUTH_CACHE, {User, Server},
|
||||||
|
fun() ->
|
||||||
|
case Mod:user_exists(User, Server) of
|
||||||
|
true -> {ok, exists};
|
||||||
|
false -> error;
|
||||||
|
{error, _} = Err -> Err
|
||||||
|
end
|
||||||
|
end) of
|
||||||
|
{ok, _} ->
|
||||||
|
true;
|
||||||
|
error ->
|
||||||
|
false
|
||||||
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
@ -568,7 +584,7 @@ db_check_password(User, AuthzId, Server, ProvidedPassword,
|
|||||||
false ->
|
false ->
|
||||||
error
|
error
|
||||||
end
|
end
|
||||||
end, cache_nodes(Mod, Server)) of
|
end) of
|
||||||
{ok, _} ->
|
{ok, _} ->
|
||||||
true;
|
true;
|
||||||
error ->
|
error ->
|
||||||
|
@ -519,6 +519,7 @@ init([State, Opts]) ->
|
|||||||
TLSRequired = proplists:get_bool(starttls_required, Opts),
|
TLSRequired = proplists:get_bool(starttls_required, Opts),
|
||||||
TLSVerify = proplists:get_bool(tls_verify, Opts),
|
TLSVerify = proplists:get_bool(tls_verify, Opts),
|
||||||
Zlib = proplists:get_bool(zlib, Opts),
|
Zlib = proplists:get_bool(zlib, Opts),
|
||||||
|
Timeout = ejabberd_config:negotiation_timeout(),
|
||||||
State1 = State#{tls_options => TLSOpts2,
|
State1 = State#{tls_options => TLSOpts2,
|
||||||
tls_required => TLSRequired,
|
tls_required => TLSRequired,
|
||||||
tls_enabled => TLSEnabled,
|
tls_enabled => TLSEnabled,
|
||||||
@ -530,7 +531,8 @@ init([State, Opts]) ->
|
|||||||
lserver => ?MYNAME,
|
lserver => ?MYNAME,
|
||||||
access => Access,
|
access => Access,
|
||||||
shaper => Shaper},
|
shaper => Shaper},
|
||||||
ejabberd_hooks:run_fold(c2s_init, {ok, State1}, [Opts]).
|
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
|
||||||
|
ejabberd_hooks:run_fold(c2s_init, {ok, State2}, [Opts]).
|
||||||
|
|
||||||
handle_call(get_presence, From, #{jid := JID} = State) ->
|
handle_call(get_presence, From, #{jid := JID} = State) ->
|
||||||
Pres = case maps:get(pres_last, State, error) of
|
Pres = case maps:get(pres_last, State, error) of
|
||||||
@ -997,6 +999,9 @@ opt_type(_) ->
|
|||||||
(max_stanza_size) -> fun((timeout()) -> timeout());
|
(max_stanza_size) -> fun((timeout()) -> timeout());
|
||||||
(max_fsm_queue) -> fun((timeout()) -> timeout());
|
(max_fsm_queue) -> fun((timeout()) -> timeout());
|
||||||
(stream_management) -> fun((boolean()) -> boolean());
|
(stream_management) -> fun((boolean()) -> boolean());
|
||||||
|
(inet) -> fun((boolean()) -> boolean());
|
||||||
|
(inet6) -> fun((boolean()) -> boolean());
|
||||||
|
(backlog) -> fun((timeout()) -> timeout());
|
||||||
(atom()) -> [atom()].
|
(atom()) -> [atom()].
|
||||||
listen_opt_type(access) -> fun acl:access_rules_validator/1;
|
listen_opt_type(access) -> fun acl:access_rules_validator/1;
|
||||||
listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
|
listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
|
||||||
@ -1029,13 +1034,18 @@ listen_opt_type(stream_management) ->
|
|||||||
?ERROR_MSG("listening option 'stream_management' is ignored: "
|
?ERROR_MSG("listening option 'stream_management' is ignored: "
|
||||||
"use mod_stream_mgmt module", []),
|
"use mod_stream_mgmt module", []),
|
||||||
fun(B) when is_boolean(B) -> B end;
|
fun(B) when is_boolean(B) -> B end;
|
||||||
|
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
|
||||||
|
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
|
||||||
|
listen_opt_type(backlog) ->
|
||||||
|
fun(I) when is_integer(I), I>0 -> I end;
|
||||||
listen_opt_type(O) ->
|
listen_opt_type(O) ->
|
||||||
StreamOpts = mod_stream_mgmt:mod_options(?MYNAME),
|
StreamOpts = mod_stream_mgmt:mod_options(?MYNAME),
|
||||||
case lists:keyfind(O, 1, StreamOpts) of
|
case lists:keyfind(O, 1, StreamOpts) of
|
||||||
false ->
|
false ->
|
||||||
[access, shaper, certfile, ciphers, dhfile, cafile,
|
[access, shaper, certfile, ciphers, dhfile, cafile,
|
||||||
protocol_options, tls, tls_compression, starttls,
|
protocol_options, tls, tls_compression, starttls,
|
||||||
starttls_required, tls_verify, zlib, max_fsm_queue];
|
starttls_required, tls_verify, zlib, max_fsm_queue,
|
||||||
|
backlog, inet, inet6];
|
||||||
_ ->
|
_ ->
|
||||||
?ERROR_MSG("Listening option '~s' is ignored: use '~s' "
|
?ERROR_MSG("Listening option '~s' is ignored: use '~s' "
|
||||||
"option from mod_stream_mgmt module", [O, O]),
|
"option from mod_stream_mgmt module", [O, O]),
|
||||||
|
@ -363,10 +363,6 @@ gen_doc(#ejabberd_commands{name=Name, tags=_Tags, desc=Desc, longdesc=LongDesc,
|
|||||||
args=Args, args_desc=ArgsDesc,
|
args=Args, args_desc=ArgsDesc,
|
||||||
result=Result, result_desc=ResultDesc}=Cmd, HTMLOutput, Langs) ->
|
result=Result, result_desc=ResultDesc}=Cmd, HTMLOutput, Langs) ->
|
||||||
try
|
try
|
||||||
LDesc = case LongDesc of
|
|
||||||
"" -> Desc;
|
|
||||||
_ -> LongDesc
|
|
||||||
end,
|
|
||||||
ArgsText = case ArgsDesc of
|
ArgsText = case ArgsDesc of
|
||||||
none ->
|
none ->
|
||||||
[?TAG(ul, "args-list", [gen_param(AName, Type, undefined, HTMLOutput)
|
[?TAG(ul, "args-list", [gen_param(AName, Type, undefined, HTMLOutput)
|
||||||
@ -393,11 +389,15 @@ gen_doc(#ejabberd_commands{name=Name, tags=_Tags, desc=Desc, longdesc=LongDesc,
|
|||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
[?TAG(h1, [?TAG(strong, atom_to_list(Name)), <<" - ">>, ?RAW(Desc)]),
|
[?TAG(h1, atom_to_list(Name)),
|
||||||
?TAG(p, ?RAW(LDesc)),
|
?TAG(p, ?RAW(Desc)),
|
||||||
?TAG(h2, <<"Arguments:">>), ArgsText,
|
case LongDesc of
|
||||||
?TAG(h2, <<"Result:">>), ResultText,
|
"" -> [];
|
||||||
?TAG(h2, <<"Examples:">>), gen_calls(Cmd, HTMLOutput, Langs)]
|
_ -> ?TAG(p, ?RAW(LongDesc))
|
||||||
|
end,
|
||||||
|
?TAG(h2, <<"Arguments:">>), ArgsText,
|
||||||
|
?TAG(h2, <<"Result:">>), ResultText,
|
||||||
|
?TAG(h2, <<"Examples:">>), gen_calls(Cmd, HTMLOutput, Langs)]
|
||||||
catch
|
catch
|
||||||
_:Ex ->
|
_:Ex ->
|
||||||
throw(iolist_to_binary(io_lib:format(
|
throw(iolist_to_binary(io_lib:format(
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
default_db/1, default_db/2, default_ram_db/1, default_ram_db/2,
|
default_db/1, default_db/2, default_ram_db/1, default_ram_db/2,
|
||||||
default_queue_type/1, queue_dir/0, fsm_limit_opts/1,
|
default_queue_type/1, queue_dir/0, fsm_limit_opts/1,
|
||||||
use_cache/1, cache_size/1, cache_missed/1, cache_life_time/1,
|
use_cache/1, cache_size/1, cache_missed/1, cache_life_time/1,
|
||||||
codec_options/1]).
|
codec_options/1, get_plain_terms_file/2, negotiation_timeout/0]).
|
||||||
|
|
||||||
-export([start/2]).
|
-export([start/2]).
|
||||||
|
|
||||||
@ -1415,6 +1415,8 @@ opt_type(cache_life_time) ->
|
|||||||
(infinity) -> infinity;
|
(infinity) -> infinity;
|
||||||
(unlimited) -> infinity
|
(unlimited) -> infinity
|
||||||
end;
|
end;
|
||||||
|
opt_type(negotiation_timeout) ->
|
||||||
|
fun(T) when T > 0 -> T end;
|
||||||
opt_type(shared_key) ->
|
opt_type(shared_key) ->
|
||||||
fun iolist_to_binary/1;
|
fun iolist_to_binary/1;
|
||||||
opt_type(node_start) ->
|
opt_type(node_start) ->
|
||||||
@ -1425,7 +1427,7 @@ opt_type(_) ->
|
|||||||
[hide_sensitive_log_data, hosts, language, max_fsm_queue,
|
[hide_sensitive_log_data, hosts, language, max_fsm_queue,
|
||||||
default_db, default_ram_db, queue_type, queue_dir, loglevel,
|
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,
|
||||||
shared_key, node_start, validate_stream].
|
shared_key, node_start, validate_stream, negotiation_timeout].
|
||||||
|
|
||||||
-spec may_hide_data(any()) -> any().
|
-spec may_hide_data(any()) -> any().
|
||||||
may_hide_data(Data) ->
|
may_hide_data(Data) ->
|
||||||
@ -1479,3 +1481,7 @@ codec_options(Host) ->
|
|||||||
true -> [];
|
true -> [];
|
||||||
false -> [ignore_els]
|
false -> [ignore_els]
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec negotiation_timeout() -> pos_integer().
|
||||||
|
negotiation_timeout() ->
|
||||||
|
timer:seconds(get_option(negotiation_timeout, 30)).
|
||||||
|
@ -994,6 +994,10 @@ listen_opt_type(default_host) ->
|
|||||||
fun(A) -> A end;
|
fun(A) -> A end;
|
||||||
listen_opt_type(custom_headers) ->
|
listen_opt_type(custom_headers) ->
|
||||||
fun expand_custom_headers/1;
|
fun expand_custom_headers/1;
|
||||||
|
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
|
||||||
|
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
|
||||||
|
listen_opt_type(backlog) ->
|
||||||
|
fun(I) when is_integer(I), I>0 -> I end;
|
||||||
listen_opt_type(_) ->
|
listen_opt_type(_) ->
|
||||||
%% TODO
|
%% TODO
|
||||||
fun(A) -> A end.
|
fun(A) -> A end.
|
||||||
|
@ -144,7 +144,7 @@ noreply(#state{expire = Expire} = State) ->
|
|||||||
-spec encode_id(non_neg_integer(), binary()) -> binary().
|
-spec encode_id(non_neg_integer(), binary()) -> binary().
|
||||||
encode_id(Expire, Rnd) ->
|
encode_id(Expire, Rnd) ->
|
||||||
ExpireBin = integer_to_binary(Expire),
|
ExpireBin = integer_to_binary(Expire),
|
||||||
Node = atom_to_binary(node(), utf8),
|
Node = ejabberd_cluster:node_id(),
|
||||||
CheckSum = calc_checksum(<<ExpireBin/binary, Rnd/binary, Node/binary>>),
|
CheckSum = calc_checksum(<<ExpireBin/binary, Rnd/binary, Node/binary>>),
|
||||||
<<"rr-", ExpireBin/binary, $-, Rnd/binary, $-, CheckSum/binary, $-, Node/binary>>.
|
<<"rr-", ExpireBin/binary, $-, Rnd/binary, $-, CheckSum/binary, $-, Node/binary>>.
|
||||||
|
|
||||||
@ -155,7 +155,7 @@ decode_id(<<"rr-", ID/binary>>) ->
|
|||||||
[Rnd, Rest] = binary:split(Tail, <<"-">>),
|
[Rnd, Rest] = binary:split(Tail, <<"-">>),
|
||||||
[CheckSum, NodeBin] = binary:split(Rest, <<"-">>),
|
[CheckSum, NodeBin] = binary:split(Rest, <<"-">>),
|
||||||
CheckSum = calc_checksum(<<ExpireBin/binary, Rnd/binary, NodeBin/binary>>),
|
CheckSum = calc_checksum(<<ExpireBin/binary, Rnd/binary, NodeBin/binary>>),
|
||||||
Node = erlang:binary_to_existing_atom(NodeBin, utf8),
|
Node = ejabberd_cluster:get_node_by_id(NodeBin),
|
||||||
Expire = binary_to_integer(ExpireBin),
|
Expire = binary_to_integer(ExpireBin),
|
||||||
{ok, Expire, Rnd, Node}
|
{ok, Expire, Rnd, Node}
|
||||||
catch _:{badmatch, _} ->
|
catch _:{badmatch, _} ->
|
||||||
|
@ -259,6 +259,7 @@ init([State, Opts]) ->
|
|||||||
false -> [compression_none | TLSOpts1];
|
false -> [compression_none | TLSOpts1];
|
||||||
true -> TLSOpts1
|
true -> TLSOpts1
|
||||||
end,
|
end,
|
||||||
|
Timeout = ejabberd_config:negotiation_timeout(),
|
||||||
State1 = State#{tls_options => TLSOpts2,
|
State1 = State#{tls_options => TLSOpts2,
|
||||||
auth_domains => sets:new(),
|
auth_domains => sets:new(),
|
||||||
xmlns => ?NS_SERVER,
|
xmlns => ?NS_SERVER,
|
||||||
@ -268,7 +269,8 @@ init([State, Opts]) ->
|
|||||||
server_host => ?MYNAME,
|
server_host => ?MYNAME,
|
||||||
established => false,
|
established => false,
|
||||||
shaper => Shaper},
|
shaper => Shaper},
|
||||||
ejabberd_hooks:run_fold(s2s_in_init, {ok, State1}, [Opts]).
|
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
|
||||||
|
ejabberd_hooks:run_fold(s2s_in_init, {ok, State2}, [Opts]).
|
||||||
|
|
||||||
handle_call(Request, From, #{server_host := LServer} = State) ->
|
handle_call(Request, From, #{server_host := LServer} = State) ->
|
||||||
ejabberd_hooks:run_fold(s2s_in_handle_call, LServer, State, [Request, From]).
|
ejabberd_hooks:run_fold(s2s_in_handle_call, LServer, State, [Request, From]).
|
||||||
@ -356,6 +358,9 @@ change_shaper(#{shaper := ShaperName, server_host := ServerHost} = State,
|
|||||||
(supervisor) -> fun((boolean()) -> boolean());
|
(supervisor) -> fun((boolean()) -> boolean());
|
||||||
(max_stanza_type) -> fun((timeout()) -> timeout());
|
(max_stanza_type) -> fun((timeout()) -> timeout());
|
||||||
(max_fsm_queue) -> fun((pos_integer()) -> pos_integer());
|
(max_fsm_queue) -> fun((pos_integer()) -> pos_integer());
|
||||||
|
(inet) -> fun((boolean()) -> boolean());
|
||||||
|
(inet6) -> fun((boolean()) -> boolean());
|
||||||
|
(backlog) -> fun((timeout()) -> timeout());
|
||||||
(atom()) -> [atom()].
|
(atom()) -> [atom()].
|
||||||
listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
|
listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
|
||||||
listen_opt_type(certfile = Opt) ->
|
listen_opt_type(certfile = Opt) ->
|
||||||
@ -379,6 +384,10 @@ listen_opt_type(max_stanza_size) ->
|
|||||||
end;
|
end;
|
||||||
listen_opt_type(max_fsm_queue) ->
|
listen_opt_type(max_fsm_queue) ->
|
||||||
fun(I) when is_integer(I), I>0 -> I end;
|
fun(I) when is_integer(I), I>0 -> I end;
|
||||||
|
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
|
||||||
|
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
|
||||||
|
listen_opt_type(backlog) ->
|
||||||
|
fun(I) when is_integer(I), I>0 -> I end;
|
||||||
listen_opt_type(_) ->
|
listen_opt_type(_) ->
|
||||||
[shaper, certfile, ciphers, dhfile, cafile, protocol_options,
|
[shaper, certfile, ciphers, dhfile, cafile, protocol_options,
|
||||||
tls_compression, tls, max_fsm_queue].
|
tls_compression, tls, max_fsm_queue, backlog, inet, inet6].
|
||||||
|
@ -270,15 +270,17 @@ init([#{server := LServer, remote_server := RServer} = State, Opts]) ->
|
|||||||
{_, N} -> N;
|
{_, N} -> N;
|
||||||
false -> unlimited
|
false -> unlimited
|
||||||
end,
|
end,
|
||||||
|
Timeout = ejabberd_config:negotiation_timeout(),
|
||||||
State1 = State#{on_route => queue,
|
State1 = State#{on_route => queue,
|
||||||
queue => p1_queue:new(QueueType, QueueLimit),
|
queue => p1_queue:new(QueueType, QueueLimit),
|
||||||
xmlns => ?NS_SERVER,
|
xmlns => ?NS_SERVER,
|
||||||
lang => ?MYLANG,
|
lang => ?MYLANG,
|
||||||
server_host => ServerHost,
|
server_host => ServerHost,
|
||||||
shaper => none},
|
shaper => none},
|
||||||
|
State2 = xmpp_stream_out:set_timeout(State1, Timeout),
|
||||||
?INFO_MSG("Outbound s2s connection started: ~s -> ~s",
|
?INFO_MSG("Outbound s2s connection started: ~s -> ~s",
|
||||||
[LServer, RServer]),
|
[LServer, RServer]),
|
||||||
ejabberd_hooks:run_fold(s2s_out_init, ServerHost, {ok, State1}, [Opts]).
|
ejabberd_hooks:run_fold(s2s_out_init, ServerHost, {ok, State2}, [Opts]).
|
||||||
|
|
||||||
handle_call(Request, From, #{server_host := ServerHost} = State) ->
|
handle_call(Request, From, #{server_host := ServerHost} = State) ->
|
||||||
ejabberd_hooks:run_fold(s2s_out_handle_call, ServerHost, State, [Request, From]).
|
ejabberd_hooks:run_fold(s2s_out_handle_call, ServerHost, State, [Request, From]).
|
||||||
|
@ -100,16 +100,20 @@ init([State, Opts]) ->
|
|||||||
false -> [compression_none | TLSOpts1];
|
false -> [compression_none | TLSOpts1];
|
||||||
true -> TLSOpts1
|
true -> TLSOpts1
|
||||||
end,
|
end,
|
||||||
|
GlobalRoutes = proplists:get_value(global_routes, Opts, true),
|
||||||
|
Timeout = ejabberd_config:negotiation_timeout(),
|
||||||
State1 = xmpp_stream_in:change_shaper(State, Shaper),
|
State1 = xmpp_stream_in:change_shaper(State, Shaper),
|
||||||
State2 = State1#{access => Access,
|
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
|
||||||
xmlns => ?NS_COMPONENT,
|
State3 = State2#{access => Access,
|
||||||
lang => ?MYLANG,
|
xmlns => ?NS_COMPONENT,
|
||||||
server => ?MYNAME,
|
lang => ?MYLANG,
|
||||||
host_opts => dict:from_list(HostOpts1),
|
server => ?MYNAME,
|
||||||
stream_version => undefined,
|
host_opts => dict:from_list(HostOpts1),
|
||||||
tls_options => TLSOpts,
|
stream_version => undefined,
|
||||||
check_from => CheckFrom},
|
tls_options => TLSOpts,
|
||||||
ejabberd_hooks:run_fold(component_init, {ok, State2}, [Opts]).
|
global_routes => GlobalRoutes,
|
||||||
|
check_from => CheckFrom},
|
||||||
|
ejabberd_hooks:run_fold(component_init, {ok, State3}, [Opts]).
|
||||||
|
|
||||||
handle_stream_start(_StreamStart,
|
handle_stream_start(_StreamStart,
|
||||||
#{remote_server := RemoteServer,
|
#{remote_server := RemoteServer,
|
||||||
@ -140,7 +144,7 @@ get_password_fun(#{remote_server := RemoteServer,
|
|||||||
host_opts := HostOpts}) ->
|
host_opts := HostOpts}) ->
|
||||||
fun(_) ->
|
fun(_) ->
|
||||||
case dict:find(RemoteServer, HostOpts) of
|
case dict:find(RemoteServer, HostOpts) of
|
||||||
{ok, Password} ->
|
{ok, Password} ->
|
||||||
{Password, undefined};
|
{Password, undefined};
|
||||||
error ->
|
error ->
|
||||||
?INFO_MSG("(~s) Domain ~s is unconfigured for "
|
?INFO_MSG("(~s) Domain ~s is unconfigured for "
|
||||||
@ -153,16 +157,22 @@ get_password_fun(#{remote_server := RemoteServer,
|
|||||||
|
|
||||||
handle_auth_success(_, Mech, _,
|
handle_auth_success(_, Mech, _,
|
||||||
#{remote_server := RemoteServer, host_opts := HostOpts,
|
#{remote_server := RemoteServer, host_opts := HostOpts,
|
||||||
socket := Socket, ip := IP} = State) ->
|
socket := Socket, ip := IP,
|
||||||
|
global_routes := GlobalRoutes} = State) ->
|
||||||
?INFO_MSG("(~s) Accepted external component ~s authentication "
|
?INFO_MSG("(~s) Accepted external component ~s authentication "
|
||||||
"for ~s from ~s",
|
"for ~s from ~s",
|
||||||
[xmpp_socket:pp(Socket), Mech, RemoteServer,
|
[xmpp_socket:pp(Socket), Mech, RemoteServer,
|
||||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
|
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
|
||||||
lists:foreach(
|
Routes = if GlobalRoutes ->
|
||||||
fun (H) ->
|
dict:fetch_keys(HostOpts);
|
||||||
ejabberd_router:register_route(H, ?MYNAME),
|
true ->
|
||||||
ejabberd_hooks:run(component_connected, [H])
|
[RemoteServer]
|
||||||
end, dict:fetch_keys(HostOpts)),
|
end,
|
||||||
|
lists:foreach(
|
||||||
|
fun(H) ->
|
||||||
|
ejabberd_router:register_route(H, ?MYNAME),
|
||||||
|
ejabberd_hooks:run(component_connected, [H])
|
||||||
|
end, Routes),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
handle_auth_failure(_, Mech, Reason,
|
handle_auth_failure(_, Mech, Reason,
|
||||||
@ -180,11 +190,11 @@ handle_authenticated_packet(Pkt0, #{ip := {IP, _}, lang := Lang} = State)
|
|||||||
Pkt = xmpp:put_meta(Pkt0, ip, IP),
|
Pkt = xmpp:put_meta(Pkt0, ip, IP),
|
||||||
From = xmpp:get_from(Pkt),
|
From = xmpp:get_from(Pkt),
|
||||||
case check_from(From, State) of
|
case check_from(From, State) of
|
||||||
true ->
|
true ->
|
||||||
ejabberd_router:route(Pkt),
|
ejabberd_router:route(Pkt),
|
||||||
State;
|
State;
|
||||||
false ->
|
false ->
|
||||||
Txt = <<"Improper domain part of 'from' attribute">>,
|
Txt = <<"Improper domain part of 'from' attribute">>,
|
||||||
Err = xmpp:serr_invalid_from(Txt, Lang),
|
Err = xmpp:serr_invalid_from(Txt, Lang),
|
||||||
xmpp_stream_in:send(State, Err)
|
xmpp_stream_in:send(State, Err)
|
||||||
end;
|
end;
|
||||||
@ -193,7 +203,7 @@ handle_authenticated_packet(_Pkt, State) ->
|
|||||||
|
|
||||||
handle_info({route, Packet}, #{access := Access} = State) ->
|
handle_info({route, Packet}, #{access := Access} = State) ->
|
||||||
case acl:match_rule(global, Access, xmpp:get_from(Packet)) of
|
case acl:match_rule(global, Access, xmpp:get_from(Packet)) of
|
||||||
allow ->
|
allow ->
|
||||||
xmpp_stream_in:send(State, Packet);
|
xmpp_stream_in:send(State, Packet);
|
||||||
deny ->
|
deny ->
|
||||||
Lang = xmpp:get_lang(Packet),
|
Lang = xmpp:get_lang(Packet),
|
||||||
@ -205,17 +215,27 @@ handle_info(Info, State) ->
|
|||||||
?ERROR_MSG("Unexpected info: ~p", [Info]),
|
?ERROR_MSG("Unexpected info: ~p", [Info]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
terminate(Reason, #{stream_state := StreamState, host_opts := HostOpts}) ->
|
terminate(Reason, #{stream_state := StreamState,
|
||||||
|
host_opts := HostOpts,
|
||||||
|
remote_server := RemoteServer,
|
||||||
|
global_routes := GlobalRoutes}) ->
|
||||||
case StreamState of
|
case StreamState of
|
||||||
established ->
|
established ->
|
||||||
|
Routes = if GlobalRoutes ->
|
||||||
|
dict:fetch_keys(HostOpts);
|
||||||
|
true ->
|
||||||
|
[RemoteServer]
|
||||||
|
end,
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(H) ->
|
fun(H) ->
|
||||||
ejabberd_router:unregister_route(H),
|
ejabberd_router:unregister_route(H),
|
||||||
ejabberd_hooks:run(component_disconnected, [H, Reason])
|
ejabberd_hooks:run(component_disconnected, [H, Reason])
|
||||||
end, dict:fetch_keys(HostOpts));
|
end, Routes);
|
||||||
_ ->
|
_ ->
|
||||||
ok
|
ok
|
||||||
end.
|
end;
|
||||||
|
terminate(_Reason, _State) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
@ -268,9 +288,12 @@ transform_listen_option(Opt, Opts) ->
|
|||||||
(check_from) -> fun((boolean()) -> boolean());
|
(check_from) -> fun((boolean()) -> boolean());
|
||||||
(password) -> fun((boolean()) -> boolean());
|
(password) -> fun((boolean()) -> boolean());
|
||||||
(hosts) -> fun(([{binary(), [{password, binary()}]}]) ->
|
(hosts) -> fun(([{binary(), [{password, binary()}]}]) ->
|
||||||
[{binary(), binary() | undefined}]);
|
[{binary(), binary() | undefined}]);
|
||||||
(max_stanza_type) -> fun((timeout()) -> timeout());
|
(max_stanza_type) -> fun((timeout()) -> timeout());
|
||||||
(max_fsm_queue) -> fun((pos_integer()) -> pos_integer());
|
(max_fsm_queue) -> fun((pos_integer()) -> pos_integer());
|
||||||
|
(inet) -> fun((boolean()) -> boolean());
|
||||||
|
(inet6) -> fun((boolean()) -> boolean());
|
||||||
|
(backlog) -> fun((timeout()) -> timeout());
|
||||||
(atom()) -> [atom()].
|
(atom()) -> [atom()].
|
||||||
listen_opt_type(access) -> fun acl:access_rules_validator/1;
|
listen_opt_type(access) -> fun acl:access_rules_validator/1;
|
||||||
listen_opt_type(shaper_rule) -> fun acl:shaper_rules_validator/1;
|
listen_opt_type(shaper_rule) -> fun acl:shaper_rules_validator/1;
|
||||||
@ -299,6 +322,8 @@ listen_opt_type(hosts) ->
|
|||||||
{iolist_to_binary(Host), Password}
|
{iolist_to_binary(Host), Password}
|
||||||
end, HostOpts)
|
end, HostOpts)
|
||||||
end;
|
end;
|
||||||
|
listen_opt_type(global_routes) ->
|
||||||
|
fun(B) when is_boolean(B) -> B end;
|
||||||
listen_opt_type(max_stanza_size) ->
|
listen_opt_type(max_stanza_size) ->
|
||||||
fun(I) when is_integer(I) -> I;
|
fun(I) when is_integer(I) -> I;
|
||||||
(unlimited) -> infinity;
|
(unlimited) -> infinity;
|
||||||
@ -306,7 +331,11 @@ listen_opt_type(max_stanza_size) ->
|
|||||||
end;
|
end;
|
||||||
listen_opt_type(max_fsm_queue) ->
|
listen_opt_type(max_fsm_queue) ->
|
||||||
fun(I) when is_integer(I), I>0 -> I end;
|
fun(I) when is_integer(I), I>0 -> I end;
|
||||||
|
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
|
||||||
|
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
|
||||||
|
listen_opt_type(backlog) ->
|
||||||
|
fun(I) when is_integer(I), I>0 -> I end;
|
||||||
listen_opt_type(_) ->
|
listen_opt_type(_) ->
|
||||||
[access, shaper_rule, certfile, ciphers, dhfile, cafile, tls,
|
[access, shaper_rule, certfile, ciphers, dhfile, cafile, tls,
|
||||||
protocol_options, tls_compression, password, hosts, check_from,
|
protocol_options, tls_compression, password, hosts, check_from,
|
||||||
max_fsm_queue].
|
max_fsm_queue, global_routes, backlog, inet, inet6].
|
||||||
|
@ -172,8 +172,10 @@ listen_opt_type(turn_max_permissions) ->
|
|||||||
end;
|
end;
|
||||||
listen_opt_type(server_name) ->
|
listen_opt_type(server_name) ->
|
||||||
fun iolist_to_binary/1;
|
fun iolist_to_binary/1;
|
||||||
|
listen_opt_type(backlog) ->
|
||||||
|
fun(I) when is_integer(I), I>0 -> I end;
|
||||||
listen_opt_type(_) ->
|
listen_opt_type(_) ->
|
||||||
[shaper, auth_type, auth_realm, tls, certfile, turn_min_port,
|
[shaper, auth_type, auth_realm, tls, certfile, turn_min_port,
|
||||||
turn_max_port, turn_max_allocations, turn_max_permissions,
|
turn_max_port, turn_max_allocations, turn_max_permissions,
|
||||||
server_name].
|
server_name, backlog].
|
||||||
-endif.
|
-endif.
|
||||||
|
@ -30,150 +30,58 @@
|
|||||||
|
|
||||||
-export([start_link/0, init/1]).
|
-export([start_link/0, init/1]).
|
||||||
|
|
||||||
|
-define(SHUTDOWN_TIMEOUT, timer:seconds(30)).
|
||||||
|
|
||||||
start_link() ->
|
start_link() ->
|
||||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||||
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
Hooks =
|
|
||||||
{ejabberd_hooks,
|
|
||||||
{ejabberd_hooks, start_link, []},
|
|
||||||
permanent,
|
|
||||||
brutal_kill,
|
|
||||||
worker,
|
|
||||||
[ejabberd_hooks]},
|
|
||||||
Cluster = {ejabberd_cluster,
|
|
||||||
{ejabberd_cluster, start_link, []},
|
|
||||||
permanent,
|
|
||||||
5000,
|
|
||||||
worker,
|
|
||||||
[ejabberd_cluster]},
|
|
||||||
S2S =
|
|
||||||
{ejabberd_s2s,
|
|
||||||
{ejabberd_s2s, start_link, []},
|
|
||||||
permanent,
|
|
||||||
brutal_kill,
|
|
||||||
worker,
|
|
||||||
[ejabberd_s2s]},
|
|
||||||
Captcha =
|
|
||||||
{ejabberd_captcha,
|
|
||||||
{ejabberd_captcha, start_link, []},
|
|
||||||
permanent,
|
|
||||||
brutal_kill,
|
|
||||||
worker,
|
|
||||||
[ejabberd_captcha]},
|
|
||||||
Listener =
|
|
||||||
{ejabberd_listener,
|
|
||||||
{ejabberd_listener, start_link, []},
|
|
||||||
permanent,
|
|
||||||
infinity,
|
|
||||||
supervisor,
|
|
||||||
[ejabberd_listener]},
|
|
||||||
S2SInSupervisor =
|
|
||||||
{ejabberd_s2s_in_sup,
|
|
||||||
{ejabberd_tmp_sup, start_link,
|
|
||||||
[ejabberd_s2s_in_sup, ejabberd_s2s_in]},
|
|
||||||
permanent,
|
|
||||||
infinity,
|
|
||||||
supervisor,
|
|
||||||
[ejabberd_tmp_sup]},
|
|
||||||
S2SOutSupervisor =
|
|
||||||
{ejabberd_s2s_out_sup,
|
|
||||||
{ejabberd_tmp_sup, start_link,
|
|
||||||
[ejabberd_s2s_out_sup, ejabberd_s2s_out]},
|
|
||||||
permanent,
|
|
||||||
infinity,
|
|
||||||
supervisor,
|
|
||||||
[ejabberd_tmp_sup]},
|
|
||||||
ServiceSupervisor =
|
|
||||||
{ejabberd_service_sup,
|
|
||||||
{ejabberd_tmp_sup, start_link,
|
|
||||||
[ejabberd_service_sup, ejabberd_service]},
|
|
||||||
permanent,
|
|
||||||
infinity,
|
|
||||||
supervisor,
|
|
||||||
[ejabberd_tmp_sup]},
|
|
||||||
BackendSupervisor = {ejabberd_backend_sup,
|
|
||||||
{ejabberd_backend_sup, start_link, []},
|
|
||||||
permanent, infinity, supervisor,
|
|
||||||
[ejabberd_backend_sup]},
|
|
||||||
ACL = {acl, {acl, start_link, []},
|
|
||||||
permanent, 5000, worker, [acl]},
|
|
||||||
Shaper = {shaper, {shaper, start_link, []},
|
|
||||||
permanent, 5000, worker, [shaper]},
|
|
||||||
SQLSupervisor = {ejabberd_rdbms,
|
|
||||||
{ejabberd_rdbms, start_link, []},
|
|
||||||
permanent, infinity, supervisor, [ejabberd_rdbms]},
|
|
||||||
RiakSupervisor = {ejabberd_riak_sup,
|
|
||||||
{ejabberd_riak_sup, start_link, []},
|
|
||||||
permanent, infinity, supervisor, [ejabberd_riak_sup]},
|
|
||||||
RedisSupervisor = {ejabberd_redis_sup,
|
|
||||||
{ejabberd_redis_sup, start_link, []},
|
|
||||||
permanent, infinity, supervisor, [ejabberd_redis_sup]},
|
|
||||||
Router = {ejabberd_router, {ejabberd_router, start_link, []},
|
|
||||||
permanent, 5000, worker, [ejabberd_router]},
|
|
||||||
RouterMulticast = {ejabberd_router_multicast,
|
|
||||||
{ejabberd_router_multicast, start_link, []},
|
|
||||||
permanent, 5000, worker, [ejabberd_router_multicast]},
|
|
||||||
Local = {ejabberd_local, {ejabberd_local, start_link, []},
|
|
||||||
permanent, 5000, worker, [ejabberd_local]},
|
|
||||||
SM = {ejabberd_sm, {ejabberd_sm, start_link, []},
|
|
||||||
permanent, 5000, worker, [ejabberd_sm]},
|
|
||||||
GenModSupervisor = {ejabberd_gen_mod_sup, {gen_mod, start_link, []},
|
|
||||||
permanent, infinity, supervisor, [gen_mod]},
|
|
||||||
ExtMod = {ext_mod, {ext_mod, start_link, []},
|
|
||||||
permanent, 5000, worker, [ext_mod]},
|
|
||||||
Auth = {ejabberd_auth, {ejabberd_auth, start_link, []},
|
|
||||||
permanent, 5000, worker, [ejabberd_auth]},
|
|
||||||
OAuth = {ejabberd_oauth, {ejabberd_oauth, start_link, []},
|
|
||||||
permanent, 5000, worker, [ejabberd_oauth]},
|
|
||||||
Translation = {translate, {translate, start_link, []},
|
|
||||||
permanent, 5000, worker, [translate]},
|
|
||||||
AccessPerms = {ejabberd_access_permissions,
|
|
||||||
{ejabberd_access_permissions, start_link, []},
|
|
||||||
permanent, 5000, worker, [ejabberd_access_permissions]},
|
|
||||||
Ctl = {ejabberd_ctl, {ejabberd_ctl, start_link, []},
|
|
||||||
permanent, 5000, worker, [ejabberd_ctl]},
|
|
||||||
Commands = {ejabberd_commands, {ejabberd_commands, start_link, []},
|
|
||||||
permanent, 5000, worker, [ejabberd_commands]},
|
|
||||||
Admin = {ejabberd_admin, {ejabberd_admin, start_link, []},
|
|
||||||
permanent, 5000, worker, [ejabberd_admin]},
|
|
||||||
CyrSASL = {cyrsasl, {cyrsasl, start_link, []},
|
|
||||||
permanent, 5000, worker, [cyrsasl]},
|
|
||||||
PKIX = {ejabberd_pkix, {ejabberd_pkix, start_link, []},
|
|
||||||
permanent, 5000, worker, [ejabberd_pkix]},
|
|
||||||
ACME = {ejabberd_acme, {ejabberd_acme, start_link, []},
|
|
||||||
permanent, 5000, worker, [ejabberd_acme]},
|
|
||||||
IQ = {ejabberd_iq, {ejabberd_iq, start_link, []},
|
|
||||||
permanent, 5000, worker, [ejabberd_iq]},
|
|
||||||
{ok, {{one_for_one, 10, 1},
|
{ok, {{one_for_one, 10, 1},
|
||||||
[Hooks,
|
[worker(ejabberd_hooks),
|
||||||
Cluster,
|
worker(ejabberd_cluster),
|
||||||
CyrSASL,
|
worker(cyrsasl),
|
||||||
Translation,
|
worker(translate),
|
||||||
AccessPerms,
|
worker(ejabberd_access_permissions),
|
||||||
Ctl,
|
worker(ejabberd_ctl),
|
||||||
Commands,
|
worker(ejabberd_commands),
|
||||||
Admin,
|
worker(ejabberd_admin),
|
||||||
PKIX,
|
worker(ejabberd_pkix),
|
||||||
ACME,
|
worker(ejabberd_acme),
|
||||||
Listener,
|
supervisor(ejabberd_listener),
|
||||||
S2S,
|
worker(ejabberd_s2s),
|
||||||
S2SInSupervisor,
|
simple_supervisor(ejabberd_s2s_in),
|
||||||
S2SOutSupervisor,
|
simple_supervisor(ejabberd_s2s_out),
|
||||||
ServiceSupervisor,
|
simple_supervisor(ejabberd_service),
|
||||||
ACL,
|
worker(acl),
|
||||||
Shaper,
|
worker(shaper),
|
||||||
BackendSupervisor,
|
supervisor(ejabberd_backend_sup),
|
||||||
SQLSupervisor,
|
supervisor(ejabberd_rdbms),
|
||||||
RiakSupervisor,
|
supervisor(ejabberd_riak_sup),
|
||||||
RedisSupervisor,
|
supervisor(ejabberd_redis_sup),
|
||||||
IQ,
|
worker(ejabberd_iq),
|
||||||
Router,
|
worker(ejabberd_router),
|
||||||
RouterMulticast,
|
worker(ejabberd_router_multicast),
|
||||||
Local,
|
worker(ejabberd_local),
|
||||||
SM,
|
worker(ejabberd_sm),
|
||||||
Captcha,
|
worker(ejabberd_captcha),
|
||||||
ExtMod,
|
worker(ext_mod),
|
||||||
GenModSupervisor,
|
supervisor(ejabberd_gen_mod_sup, gen_mod),
|
||||||
Auth,
|
worker(ejabberd_auth),
|
||||||
OAuth]}}.
|
worker(ejabberd_oauth)]}}.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
worker(Mod) ->
|
||||||
|
{Mod, {Mod, start_link, []}, permanent, ?SHUTDOWN_TIMEOUT, worker, [Mod]}.
|
||||||
|
|
||||||
|
supervisor(Mod) ->
|
||||||
|
supervisor(Mod, Mod).
|
||||||
|
|
||||||
|
supervisor(Name, Mod) ->
|
||||||
|
{Name, {Mod, start_link, []}, permanent, infinity, supervisor, [Mod]}.
|
||||||
|
|
||||||
|
simple_supervisor(Mod) ->
|
||||||
|
Name = list_to_atom(atom_to_list(Mod) ++ "_sup"),
|
||||||
|
{Name, {ejabberd_tmp_sup, start_link, [Name, Mod]},
|
||||||
|
permanent, infinity, supervisor, [ejabberd_tmp_sup]}.
|
||||||
|
@ -581,5 +581,9 @@ listen_opt_type(maxsessions) ->
|
|||||||
fun(I) when is_integer(I), I>0 -> I end;
|
fun(I) when is_integer(I), I>0 -> I end;
|
||||||
listen_opt_type(timeout) ->
|
listen_opt_type(timeout) ->
|
||||||
fun(I) when is_integer(I), I>0 -> I end;
|
fun(I) when is_integer(I), I>0 -> I end;
|
||||||
|
listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
|
||||||
|
listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
|
||||||
|
listen_opt_type(backlog) ->
|
||||||
|
fun(I) when is_integer(I), I>0 -> I end;
|
||||||
listen_opt_type(_) ->
|
listen_opt_type(_) ->
|
||||||
[access_commands, maxsessions, timeout].
|
[access_commands, maxsessions, timeout, backlog, inet, inet6].
|
||||||
|
@ -516,9 +516,16 @@ get_validators(Host, {Module, SubMods}) ->
|
|||||||
[] ->
|
[] ->
|
||||||
case have_validators(Module) of
|
case have_validators(Module) of
|
||||||
false ->
|
false ->
|
||||||
?WARNING_MSG("Third-party module '~s' doesn't export "
|
case code:ensure_loaded(Module) of
|
||||||
"options validator; consider to upgrade "
|
{module, _} ->
|
||||||
"the module", [Module]),
|
?WARNING_MSG("Third-party module '~s' doesn't export "
|
||||||
|
"options validator; consider to upgrade "
|
||||||
|
"the module", [Module]);
|
||||||
|
_ ->
|
||||||
|
%% Silently ignore this, the error will be
|
||||||
|
%% generated later
|
||||||
|
ok
|
||||||
|
end,
|
||||||
undef;
|
undef;
|
||||||
true ->
|
true ->
|
||||||
[]
|
[]
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
add_rosteritem/7, delete_rosteritem/4,
|
add_rosteritem/7, delete_rosteritem/4,
|
||||||
process_rosteritems/5, get_roster/2, push_roster/3,
|
process_rosteritems/5, get_roster/2, push_roster/3,
|
||||||
push_roster_all/1, push_alltoall/2,
|
push_roster_all/1, push_alltoall/2,
|
||||||
|
push_roster_item/5, build_roster_item/3,
|
||||||
|
|
||||||
% Private storage
|
% Private storage
|
||||||
private_get/4, private_set/3,
|
private_get/4, private_set/3,
|
||||||
@ -1506,11 +1507,12 @@ srg_get_info(Group, Host) ->
|
|||||||
Os when is_list(Os) -> Os;
|
Os when is_list(Os) -> Os;
|
||||||
error -> []
|
error -> []
|
||||||
end,
|
end,
|
||||||
[{misc:atom_to_binary(Title), btl(Value)} || {Title, Value} <- Opts].
|
[{misc:atom_to_binary(Title), to_list(Value)} || {Title, Value} <- Opts].
|
||||||
|
|
||||||
btl([]) -> [];
|
to_list([]) -> [];
|
||||||
btl([B|L]) -> [btl(B)|btl(L)];
|
to_list([H|T]) -> [to_list(H)|to_list(T)];
|
||||||
btl(B) -> binary_to_list(B).
|
to_list(E) when is_atom(E) -> atom_to_list(E);
|
||||||
|
to_list(E) -> binary_to_list(E).
|
||||||
|
|
||||||
srg_get_members(Group, Host) ->
|
srg_get_members(Group, Host) ->
|
||||||
Members = mod_shared_roster:get_group_explicit_users(Host,Group),
|
Members = mod_shared_roster:get_group_explicit_users(Host,Group),
|
||||||
|
@ -199,8 +199,10 @@ need_check(Pkt) ->
|
|||||||
false
|
false
|
||||||
end,
|
end,
|
||||||
AllowLocalUsers = gen_mod:get_module_opt(LServer, ?MODULE, allow_local_users),
|
AllowLocalUsers = gen_mod:get_module_opt(LServer, ?MODULE, allow_local_users),
|
||||||
not (IsEmpty orelse ((AllowLocalUsers orelse From#jid.luser == <<"">>)
|
Access = gen_mod:get_module_opt(LServer, ?MODULE, access),
|
||||||
andalso ejabberd_router:is_my_host(From#jid.lserver))).
|
not (IsEmpty orelse acl:match_rule(LServer, Access, From) == allow
|
||||||
|
orelse ((AllowLocalUsers orelse From#jid.luser == <<"">>)
|
||||||
|
andalso ejabberd_router:is_my_host(From#jid.lserver))).
|
||||||
|
|
||||||
-spec check_subscription(jid(), jid()) -> boolean().
|
-spec check_subscription(jid(), jid()) -> boolean().
|
||||||
check_subscription(From, To) ->
|
check_subscription(From, To) ->
|
||||||
@ -265,10 +267,14 @@ mod_opt_type(allow_local_users) ->
|
|||||||
mod_opt_type(allow_transports) ->
|
mod_opt_type(allow_transports) ->
|
||||||
fun (B) when is_boolean(B) -> B end;
|
fun (B) when is_boolean(B) -> B end;
|
||||||
mod_opt_type(captcha) ->
|
mod_opt_type(captcha) ->
|
||||||
fun (B) when is_boolean(B) -> B end.
|
fun (B) when is_boolean(B) -> B end;
|
||||||
|
mod_opt_type(access) ->
|
||||||
|
fun acl:access_rules_validator/1.
|
||||||
|
|
||||||
|
|
||||||
mod_options(_) ->
|
mod_options(_) ->
|
||||||
[{drop, true},
|
[{access, none},
|
||||||
|
{drop, true},
|
||||||
{log, false},
|
{log, false},
|
||||||
{captcha, false},
|
{captcha, false},
|
||||||
{allow_local_users, true},
|
{allow_local_users, true},
|
||||||
|
@ -234,10 +234,11 @@ process_unblock(#iq{from = From} = IQ, LJIDs) ->
|
|||||||
|
|
||||||
-spec broadcast_event(jid(), block() | unblock()) -> ok.
|
-spec broadcast_event(jid(), block() | unblock()) -> ok.
|
||||||
broadcast_event(#jid{luser = LUser, lserver = LServer} = From, Event) ->
|
broadcast_event(#jid{luser = LUser, lserver = LServer} = From, Event) ->
|
||||||
|
BFrom = jid:remove_resource(From),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(R) ->
|
fun(R) ->
|
||||||
To = jid:replace_resource(From, R),
|
To = jid:replace_resource(From, R),
|
||||||
IQ = #iq{type = set, from = From, to = To,
|
IQ = #iq{type = set, from = BFrom, to = To,
|
||||||
id = <<"push", (randoms:get_string())/binary>>,
|
id = <<"push", (randoms:get_string())/binary>>,
|
||||||
sub_els = [Event]},
|
sub_els = [Event]},
|
||||||
ejabberd_router:route(IQ)
|
ejabberd_router:route(IQ)
|
||||||
|
@ -115,10 +115,10 @@ iq_handler(#iq{type = set, lang = Lang, from = From,
|
|||||||
{U, S, R} = jid:tolower(From),
|
{U, S, R} = jid:tolower(From),
|
||||||
Result = case El of
|
Result = case El of
|
||||||
#carbons_enable{} ->
|
#carbons_enable{} ->
|
||||||
?INFO_MSG("carbons enabled for user ~s@~s/~s", [U,S,R]),
|
?DEBUG("Carbons enabled for user ~s@~s/~s", [U,S,R]),
|
||||||
enable(S, U, R, ?NS_CARBONS_2);
|
enable(S, U, R, ?NS_CARBONS_2);
|
||||||
#carbons_disable{} ->
|
#carbons_disable{} ->
|
||||||
?INFO_MSG("carbons disabled for user ~s@~s/~s", [U,S,R]),
|
?DEBUG("Carbons disabled for user ~s@~s/~s", [U,S,R]),
|
||||||
disable(S, U, R)
|
disable(S, U, R)
|
||||||
end,
|
end,
|
||||||
case Result of
|
case Result of
|
||||||
@ -164,9 +164,9 @@ user_receive_packet({Packet, #{jid := JID} = C2SState}) ->
|
|||||||
stanza() | {stop, stanza()}.
|
stanza() | {stop, stanza()}.
|
||||||
check_and_forward(JID, To, Packet, Direction)->
|
check_and_forward(JID, To, Packet, Direction)->
|
||||||
case is_chat_message(Packet) andalso
|
case is_chat_message(Packet) andalso
|
||||||
not is_muc_pm(To, Packet) andalso
|
not is_received_muc_pm(To, Packet, Direction) andalso
|
||||||
xmpp:has_subtag(Packet, #carbons_private{}) == false andalso
|
not xmpp:has_subtag(Packet, #carbons_private{}) andalso
|
||||||
xmpp:has_subtag(Packet, #hint{type = 'no-copy'}) == false of
|
not xmpp:has_subtag(Packet, #hint{type = 'no-copy'}) of
|
||||||
true ->
|
true ->
|
||||||
case is_carbon_copy(Packet) of
|
case is_carbon_copy(Packet) of
|
||||||
false ->
|
false ->
|
||||||
@ -282,9 +282,12 @@ is_chat_message(#message{type = normal, body = [_|_]}) ->
|
|||||||
is_chat_message(_) ->
|
is_chat_message(_) ->
|
||||||
false.
|
false.
|
||||||
|
|
||||||
is_muc_pm(#jid{lresource = <<>>}, _Packet) ->
|
-spec is_received_muc_pm(jid(), message(), direction()) -> boolean().
|
||||||
|
is_received_muc_pm(#jid{lresource = <<>>}, _Packet, _Direction) ->
|
||||||
false;
|
false;
|
||||||
is_muc_pm(_To, Packet) ->
|
is_received_muc_pm(_To, _Packet, sent) ->
|
||||||
|
false;
|
||||||
|
is_received_muc_pm(_To, Packet, received) ->
|
||||||
xmpp:has_subtag(Packet, #muc_user{}).
|
xmpp:has_subtag(Packet, #muc_user{}).
|
||||||
|
|
||||||
-spec list(binary(), binary()) -> [{Resource :: binary(), Namespace :: binary()}].
|
-spec list(binary(), binary()) -> [{Resource :: binary(), Namespace :: binary()}].
|
||||||
|
@ -589,7 +589,7 @@ create_slot(#state{service_url = undefined,
|
|||||||
case ejabberd_hooks:run_fold(http_upload_slot_request, ServerHost, allow,
|
case ejabberd_hooks:run_fold(http_upload_slot_request, ServerHost, allow,
|
||||||
[JID, UserDir, Size, Lang]) of
|
[JID, UserDir, Size, Lang]) of
|
||||||
allow ->
|
allow ->
|
||||||
RandStr = make_rand_string(SecretLength),
|
RandStr = randoms:get_alphanum_string(SecretLength),
|
||||||
FileStr = make_file_string(File),
|
FileStr = make_file_string(File),
|
||||||
?INFO_MSG("Got HTTP upload slot for ~s (file: ~s)",
|
?INFO_MSG("Got HTTP upload slot for ~s (file: ~s)",
|
||||||
[jid:encode(JID), File]),
|
[jid:encode(JID), File]),
|
||||||
@ -687,27 +687,6 @@ make_user_string(#jid{luser = U}, node) ->
|
|||||||
make_file_string(File) ->
|
make_file_string(File) ->
|
||||||
re:replace(File, <<"[^a-zA-Z0-9_.-]">>, <<$_>>, [global, {return, binary}]).
|
re:replace(File, <<"[^a-zA-Z0-9_.-]">>, <<$_>>, [global, {return, binary}]).
|
||||||
|
|
||||||
-spec make_rand_string(non_neg_integer()) -> binary().
|
|
||||||
|
|
||||||
make_rand_string(Length) ->
|
|
||||||
list_to_binary(make_rand_string([], Length)).
|
|
||||||
|
|
||||||
-spec make_rand_string(string(), non_neg_integer()) -> string().
|
|
||||||
|
|
||||||
make_rand_string(S, 0) -> S;
|
|
||||||
make_rand_string(S, N) -> make_rand_string([make_rand_char() | S], N - 1).
|
|
||||||
|
|
||||||
-spec make_rand_char() -> char().
|
|
||||||
|
|
||||||
make_rand_char() ->
|
|
||||||
map_int_to_char(randoms:uniform(0, 61)).
|
|
||||||
|
|
||||||
-spec map_int_to_char(0..61) -> char().
|
|
||||||
|
|
||||||
map_int_to_char(N) when N =< 9 -> N + 48; % Digit.
|
|
||||||
map_int_to_char(N) when N =< 35 -> N + 55; % Upper-case character.
|
|
||||||
map_int_to_char(N) when N =< 61 -> N + 61. % Lower-case character.
|
|
||||||
|
|
||||||
-spec yield_content_type(binary()) -> binary().
|
-spec yield_content_type(binary()) -> binary().
|
||||||
|
|
||||||
yield_content_type(<<"">>) -> ?DEFAULT_CONTENT_TYPE;
|
yield_content_type(<<"">>) -> ?DEFAULT_CONTENT_TYPE;
|
||||||
|
@ -156,9 +156,9 @@ privacy_check_packet(allow, C2SState,
|
|||||||
case xmpp:has_subtag(IQ, #last{}) of
|
case xmpp:has_subtag(IQ, #last{}) of
|
||||||
true ->
|
true ->
|
||||||
#jid{luser = LUser, lserver = LServer} = To,
|
#jid{luser = LUser, lserver = LServer} = To,
|
||||||
{Sub, _} = ejabberd_hooks:run_fold(
|
{Sub, _, _} = ejabberd_hooks:run_fold(
|
||||||
roster_get_jid_info, LServer,
|
roster_get_jid_info, LServer,
|
||||||
{none, []}, [LUser, LServer, From]),
|
{none, none, []}, [LUser, LServer, From]),
|
||||||
if Sub == from; Sub == both ->
|
if Sub == from; Sub == both ->
|
||||||
Pres = #presence{from = To, to = From},
|
Pres = #presence{from = To, to = From},
|
||||||
case ejabberd_hooks:run_fold(
|
case ejabberd_hooks:run_fold(
|
||||||
|
@ -67,9 +67,10 @@
|
|||||||
-callback select(binary(), jid(), jid(), mam_query:result(),
|
-callback select(binary(), jid(), jid(), mam_query:result(),
|
||||||
#rsm_set{} | undefined, chat | groupchat) ->
|
#rsm_set{} | undefined, chat | groupchat) ->
|
||||||
{[{binary(), non_neg_integer(), xmlel()}], boolean(), non_neg_integer()}.
|
{[{binary(), non_neg_integer(), xmlel()}], boolean(), non_neg_integer()}.
|
||||||
-callback use_cache(binary(), gen_mod:opts()) -> boolean().
|
-callback use_cache(binary()) -> boolean().
|
||||||
|
-callback cache_nodes(binary()) -> [node()].
|
||||||
|
|
||||||
-optional_callbacks([use_cache/2]).
|
-optional_callbacks([use_cache/1, cache_nodes/1]).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% API
|
%%% API
|
||||||
@ -77,7 +78,7 @@
|
|||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||||
Mod:init(Host, Opts),
|
Mod:init(Host, Opts),
|
||||||
init_cache(Host, Opts),
|
init_cache(Mod, Host, Opts),
|
||||||
register_iq_handlers(Host),
|
register_iq_handlers(Host),
|
||||||
ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE,
|
ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE,
|
||||||
sm_receive_packet, 50),
|
sm_receive_packet, 50),
|
||||||
@ -113,19 +114,24 @@ start(Host, Opts) ->
|
|||||||
ejabberd_commands:register_commands(get_commands_spec()),
|
ejabberd_commands:register_commands(get_commands_spec()),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
use_cache(Host, Opts) ->
|
use_cache(Mod, Host) ->
|
||||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
|
||||||
case erlang:function_exported(Mod, use_cache, 2) of
|
case erlang:function_exported(Mod, use_cache, 2) of
|
||||||
true -> Mod:use_cache(Host, Opts);
|
true -> Mod:use_cache(Host);
|
||||||
false -> gen_mod:get_opt(use_cache, Opts)
|
false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
init_cache(Host, Opts) ->
|
cache_nodes(Mod, Host) ->
|
||||||
case use_cache(Host, Opts) of
|
case erlang:function_exported(Mod, cache_nodes, 1) of
|
||||||
|
true -> Mod:cache_nodes(Host);
|
||||||
|
false -> ejabberd_cluster:get_nodes()
|
||||||
|
end.
|
||||||
|
|
||||||
|
init_cache(Mod, Host, Opts) ->
|
||||||
|
case use_cache(Mod, Host) of
|
||||||
true ->
|
true ->
|
||||||
ets_cache:new(archive_prefs_cache, cache_opts(Opts));
|
ets_cache:new(archive_prefs_cache, cache_opts(Opts));
|
||||||
false ->
|
false ->
|
||||||
ok
|
ets_cache:delete(archive_prefs_cache)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
cache_opts(Opts) ->
|
cache_opts(Opts) ->
|
||||||
@ -185,7 +191,7 @@ reload(Host, NewOpts, OldOpts) ->
|
|||||||
true ->
|
true ->
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
ets_cache:setopts(archive_prefs_cache, cache_opts(NewOpts)),
|
init_cache(NewMod, Host, NewOpts),
|
||||||
case gen_mod:is_equal_opt(assume_mam_usage, NewOpts, OldOpts) of
|
case gen_mod:is_equal_opt(assume_mam_usage, NewOpts, OldOpts) of
|
||||||
{false, true, _} ->
|
{false, true, _} ->
|
||||||
ejabberd_hooks:add(message_is_archived, Host, ?MODULE,
|
ejabberd_hooks:add(message_is_archived, Host, ?MODULE,
|
||||||
@ -236,8 +242,13 @@ remove_user(User, Server) ->
|
|||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Mod:remove_user(LUser, LServer),
|
Mod:remove_user(LUser, LServer),
|
||||||
ets_cache:delete(archive_prefs_cache, {LUser, LServer},
|
case use_cache(Mod, LServer) of
|
||||||
ejabberd_cluster:get_nodes()).
|
true ->
|
||||||
|
ets_cache:delete(archive_prefs_cache, {LUser, LServer},
|
||||||
|
cache_nodes(Mod, LServer));
|
||||||
|
false ->
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
-spec remove_room(binary(), binary(), binary()) -> ok.
|
-spec remove_room(binary(), binary(), binary()) -> ok.
|
||||||
remove_room(LServer, Name, Host) ->
|
remove_room(LServer, Name, Host) ->
|
||||||
@ -792,16 +803,26 @@ write_prefs(LUser, LServer, Host, Default, Always, Never) ->
|
|||||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||||
case Mod:write_prefs(LUser, LServer, Prefs, Host) of
|
case Mod:write_prefs(LUser, LServer, Prefs, Host) of
|
||||||
ok ->
|
ok ->
|
||||||
ets_cache:delete(archive_prefs_cache, {LUser, LServer},
|
case use_cache(Mod, LServer) of
|
||||||
ejabberd_cluster:get_nodes());
|
true ->
|
||||||
|
ets_cache:delete(archive_prefs_cache, {LUser, LServer},
|
||||||
|
cache_nodes(Mod, LServer));
|
||||||
|
false ->
|
||||||
|
ok
|
||||||
|
end;
|
||||||
_Err ->
|
_Err ->
|
||||||
{error, db_failure}
|
{error, db_failure}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_prefs(LUser, LServer) ->
|
get_prefs(LUser, LServer) ->
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Res = ets_cache:lookup(archive_prefs_cache, {LUser, LServer},
|
Res = case use_cache(Mod, LServer) of
|
||||||
fun() -> Mod:get_prefs(LUser, LServer) end),
|
true ->
|
||||||
|
ets_cache:lookup(archive_prefs_cache, {LUser, LServer},
|
||||||
|
fun() -> Mod:get_prefs(LUser, LServer) end);
|
||||||
|
false ->
|
||||||
|
Mod:get_prefs(LUser, LServer)
|
||||||
|
end,
|
||||||
case Res of
|
case Res of
|
||||||
{ok, Prefs} ->
|
{ok, Prefs} ->
|
||||||
Prefs;
|
Prefs;
|
||||||
@ -831,10 +852,16 @@ maybe_activate_mam(LUser, LServer) ->
|
|||||||
case ActivateOpt of
|
case ActivateOpt of
|
||||||
true ->
|
true ->
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Res = ets_cache:lookup(archive_prefs_cache, {LUser, LServer},
|
Res = case use_cache(Mod, LServer) of
|
||||||
fun() ->
|
true ->
|
||||||
Mod:get_prefs(LUser, LServer)
|
ets_cache:lookup(archive_prefs_cache,
|
||||||
end),
|
{LUser, LServer},
|
||||||
|
fun() ->
|
||||||
|
Mod:get_prefs(LUser, LServer)
|
||||||
|
end);
|
||||||
|
false ->
|
||||||
|
Mod:get_prefs(LUser, LServer)
|
||||||
|
end,
|
||||||
case Res of
|
case Res of
|
||||||
{ok, _Prefs} ->
|
{ok, _Prefs} ->
|
||||||
ok;
|
ok;
|
||||||
|
@ -107,8 +107,7 @@
|
|||||||
-callback unregister_online_user(binary(), ljid(), binary(), binary()) -> any().
|
-callback unregister_online_user(binary(), ljid(), binary(), binary()) -> any().
|
||||||
-callback count_online_rooms_by_user(binary(), binary(), binary()) -> non_neg_integer().
|
-callback count_online_rooms_by_user(binary(), binary(), binary()) -> non_neg_integer().
|
||||||
-callback get_online_rooms_by_user(binary(), binary(), binary()) -> [{binary(), binary()}].
|
-callback get_online_rooms_by_user(binary(), binary(), binary()) -> [{binary(), binary()}].
|
||||||
-callback get_subscribed_rooms(binary(), binary(), jid()) ->
|
-callback get_subscribed_rooms(binary(), binary(), jid()) -> [ljid()] | [].
|
||||||
{ok, [{ljid(), binary(), [binary()]}]} | {error, any()}.
|
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% API
|
%% API
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
get_user_rooms/2, get_room_occupants/2,
|
get_user_rooms/2, get_room_occupants/2,
|
||||||
get_room_occupants_number/2, send_direct_invitation/5,
|
get_room_occupants_number/2, send_direct_invitation/5,
|
||||||
change_room_option/4, get_room_options/2,
|
change_room_option/4, get_room_options/2,
|
||||||
set_room_affiliation/4, get_room_affiliations/2,
|
set_room_affiliation/4, get_room_affiliations/2, get_room_affiliation/3,
|
||||||
web_menu_main/2, web_page_main/2, web_menu_host/3,
|
web_menu_main/2, web_page_main/2, web_menu_host/3,
|
||||||
subscribe_room/4, unsubscribe_room/2, get_subscribers/2,
|
subscribe_room/4, unsubscribe_room/2, get_subscribers/2,
|
||||||
web_page_host/3, mod_options/1, get_commands_spec/0]).
|
web_page_host/3, mod_options/1, get_commands_spec/0]).
|
||||||
@ -313,8 +313,17 @@ get_commands_spec() ->
|
|||||||
{affiliation, atom},
|
{affiliation, atom},
|
||||||
{reason, string}
|
{reason, string}
|
||||||
]}}
|
]}}
|
||||||
}}}
|
}}},
|
||||||
].
|
#ejabberd_commands{name = get_room_affiliation, tags = [muc_room],
|
||||||
|
desc = "Get affiliation of a user in MUC room",
|
||||||
|
module = ?MODULE, function = get_room_affiliation,
|
||||||
|
args_desc = ["Room name", "MUC service", "User JID"],
|
||||||
|
args_example = ["room1", "muc.example.com", "user1@example.com"],
|
||||||
|
result_desc = "Affiliation of the user",
|
||||||
|
result_example = member,
|
||||||
|
args = [{name, binary}, {service, binary}, {jid, binary}],
|
||||||
|
result = {affiliation, atom}}
|
||||||
|
].
|
||||||
|
|
||||||
|
|
||||||
%%%
|
%%%
|
||||||
@ -569,8 +578,10 @@ prepare_room_info(Room_info) ->
|
|||||||
%% ok | error
|
%% ok | error
|
||||||
%% @doc Create a room immediately with the default options.
|
%% @doc Create a room immediately with the default options.
|
||||||
create_room(Name1, Host1, ServerHost) ->
|
create_room(Name1, Host1, ServerHost) ->
|
||||||
create_room_with_opts(Name1, Host1, ServerHost, []),
|
case create_room_with_opts(Name1, Host1, ServerHost, []) of
|
||||||
change_room_option(Name1, Host1, <<"persistent">>, <<"true">>).
|
ok -> change_room_option(Name1, Host1, <<"persistent">>, <<"true">>);
|
||||||
|
Error -> Error
|
||||||
|
end.
|
||||||
|
|
||||||
create_room_with_opts(Name1, Host1, ServerHost, CustomRoomOpts) ->
|
create_room_with_opts(Name1, Host1, ServerHost, CustomRoomOpts) ->
|
||||||
true = (error /= (Name = jid:nodeprep(Name1))),
|
true = (error /= (Name = jid:nodeprep(Name1))),
|
||||||
@ -1032,6 +1043,25 @@ get_room_affiliations(Name, Service) ->
|
|||||||
throw({error, "The room does not exist."})
|
throw({error, "The room does not exist."})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%%----------------------------
|
||||||
|
%% Get Room Affiliation
|
||||||
|
%%----------------------------
|
||||||
|
|
||||||
|
%% @spec(Name::binary(), Service::binary(), JID::binary()) ->
|
||||||
|
%% {Affiliation::string()}
|
||||||
|
%% @doc Get affiliation of a user in the room Name@Service.
|
||||||
|
|
||||||
|
get_room_affiliation(Name, Service, JID) ->
|
||||||
|
case mod_muc:find_online_room(Name, Service) of
|
||||||
|
{ok, Pid} ->
|
||||||
|
%% Get the PID of the online room, then request its state
|
||||||
|
{ok, StateData} = p1_fsm:sync_send_all_state_event(Pid, get_state),
|
||||||
|
UserJID = jid:decode(JID),
|
||||||
|
mod_muc_room:get_affiliation(UserJID, StateData);
|
||||||
|
error ->
|
||||||
|
throw({error, "The room does not exist."})
|
||||||
|
end.
|
||||||
|
|
||||||
%%----------------------------
|
%%----------------------------
|
||||||
%% Change Room Affiliation
|
%% Change Room Affiliation
|
||||||
%%----------------------------
|
%%----------------------------
|
||||||
|
@ -37,7 +37,9 @@
|
|||||||
get_role/2,
|
get_role/2,
|
||||||
get_affiliation/2,
|
get_affiliation/2,
|
||||||
is_occupant_or_admin/2,
|
is_occupant_or_admin/2,
|
||||||
route/2]).
|
route/2,
|
||||||
|
expand_opts/1,
|
||||||
|
config_fields/0]).
|
||||||
|
|
||||||
%% gen_fsm callbacks
|
%% gen_fsm callbacks
|
||||||
-export([init/1,
|
-export([init/1,
|
||||||
@ -506,7 +508,7 @@ handle_event(_Event, StateName, StateData) ->
|
|||||||
{next_state, StateName, StateData}.
|
{next_state, StateName, StateData}.
|
||||||
|
|
||||||
handle_sync_event({get_disco_item, Filter, JID, Lang}, _From, StateName, StateData) ->
|
handle_sync_event({get_disco_item, Filter, JID, Lang}, _From, StateName, StateData) ->
|
||||||
Len = ?DICT:size(StateData#state.users),
|
Len = ?DICT:size(StateData#state.nicks),
|
||||||
Reply = case (Filter == all) or (Filter == Len) or ((Filter /= 0) and (Len /= 0)) of
|
Reply = case (Filter == all) or (Filter == Len) or ((Filter /= 0) and (Len /= 0)) of
|
||||||
true ->
|
true ->
|
||||||
get_roomdesc_reply(JID, StateData,
|
get_roomdesc_reply(JID, StateData,
|
||||||
@ -3609,6 +3611,35 @@ make_opts(StateData) ->
|
|||||||
{subject_author, StateData#state.subject_author},
|
{subject_author, StateData#state.subject_author},
|
||||||
{subscribers, Subscribers}].
|
{subscribers, Subscribers}].
|
||||||
|
|
||||||
|
expand_opts(CompactOpts) ->
|
||||||
|
DefConfig = #config{},
|
||||||
|
Fields = record_info(fields, config),
|
||||||
|
{_, Opts1} =
|
||||||
|
lists:foldl(
|
||||||
|
fun(Field, {Pos, Opts}) ->
|
||||||
|
case lists:keyfind(Field, 1, CompactOpts) of
|
||||||
|
false ->
|
||||||
|
DefV = element(Pos, DefConfig),
|
||||||
|
DefVal = case (?SETS):is_set(DefV) of
|
||||||
|
true -> (?SETS):to_list(DefV);
|
||||||
|
false -> DefV
|
||||||
|
end,
|
||||||
|
{Pos+1, [{Field, DefVal}|Opts]};
|
||||||
|
{_, Val} ->
|
||||||
|
{Pos+1, [{Field, Val}|Opts]}
|
||||||
|
end
|
||||||
|
end, {2, []}, Fields),
|
||||||
|
SubjectAuthor = proplists:get_value(subject_author, CompactOpts, <<"">>),
|
||||||
|
Subject = proplists:get_value(subject, CompactOpts, <<"">>),
|
||||||
|
Subscribers = proplists:get_value(subscribers, CompactOpts, []),
|
||||||
|
[{subject, Subject},
|
||||||
|
{subject_author, SubjectAuthor},
|
||||||
|
{subscribers, Subscribers}
|
||||||
|
| lists:reverse(Opts1)].
|
||||||
|
|
||||||
|
config_fields() ->
|
||||||
|
[subject, subject_author, subscribers | record_info(fields, config)].
|
||||||
|
|
||||||
-spec destroy_room(muc_destroy(), state()) -> {result, undefined, stop}.
|
-spec destroy_room(muc_destroy(), state()) -> {result, undefined, stop}.
|
||||||
destroy_room(DEl, StateData) ->
|
destroy_room(DEl, StateData) ->
|
||||||
Destroy = DEl#muc_destroy{xmlns = ?NS_MUC_USER},
|
Destroy = DEl#muc_destroy{xmlns = ?NS_MUC_USER},
|
||||||
@ -3703,7 +3734,7 @@ process_iq_disco_info(From, #iq{type = get, lang = Lang,
|
|||||||
-spec iq_disco_info_extras(binary(), state()) -> xdata().
|
-spec iq_disco_info_extras(binary(), state()) -> xdata().
|
||||||
iq_disco_info_extras(Lang, StateData) ->
|
iq_disco_info_extras(Lang, StateData) ->
|
||||||
Fs1 = [{description, (StateData#state.config)#config.description},
|
Fs1 = [{description, (StateData#state.config)#config.description},
|
||||||
{occupants, ?DICT:size(StateData#state.users)},
|
{occupants, ?DICT:size(StateData#state.nicks)},
|
||||||
{contactjid, get_owners(StateData)}],
|
{contactjid, get_owners(StateData)}],
|
||||||
Fs2 = case (StateData#state.config)#config.pubsub of
|
Fs2 = case (StateData#state.config)#config.pubsub of
|
||||||
Node when is_binary(Node), Node /= <<"">> ->
|
Node when is_binary(Node), Node /= <<"">> ->
|
||||||
@ -3938,20 +3969,18 @@ get_roomdesc_tail(StateData, Lang) ->
|
|||||||
true -> <<"">>;
|
true -> <<"">>;
|
||||||
_ -> translate:translate(Lang, <<"private, ">>)
|
_ -> translate:translate(Lang, <<"private, ">>)
|
||||||
end,
|
end,
|
||||||
Len = (?DICT):size(StateData#state.users),
|
Len = (?DICT):size(StateData#state.nicks),
|
||||||
<<" (", Desc/binary, (integer_to_binary(Len))/binary, ")">>.
|
<<" (", Desc/binary, (integer_to_binary(Len))/binary, ")">>.
|
||||||
|
|
||||||
-spec get_mucroom_disco_items(state()) -> disco_items().
|
-spec get_mucroom_disco_items(state()) -> disco_items().
|
||||||
get_mucroom_disco_items(StateData) ->
|
get_mucroom_disco_items(StateData) ->
|
||||||
Items = lists:map(
|
Items = ?DICT:fold(
|
||||||
fun({_LJID, Info}) ->
|
fun(Nick, _, Acc) ->
|
||||||
Nick = Info#user.nick,
|
[#disco_item{jid = jid:make(StateData#state.room,
|
||||||
#disco_item{jid = jid:make(StateData#state.room,
|
StateData#state.host,
|
||||||
StateData#state.host,
|
Nick),
|
||||||
Nick),
|
name = Nick}|Acc]
|
||||||
name = Nick}
|
end, [], StateData#state.nicks),
|
||||||
end,
|
|
||||||
(?DICT):to_list(StateData#state.users)),
|
|
||||||
#disco_items{items = Items}.
|
#disco_items{items = Items}.
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
@ -121,13 +121,14 @@ s2s_out_init(Acc, _Opts) ->
|
|||||||
|
|
||||||
s2s_out_closed(#{server := LServer,
|
s2s_out_closed(#{server := LServer,
|
||||||
remote_server := RServer,
|
remote_server := RServer,
|
||||||
|
lang := Lang,
|
||||||
db_verify := {StreamID, _Key, _Pid}} = State, Reason) ->
|
db_verify := {StreamID, _Key, _Pid}} = State, Reason) ->
|
||||||
%% Outbound s2s verificating connection (created at step 1) is
|
%% Outbound s2s verificating connection (created at step 1) is
|
||||||
%% closed suddenly without receiving the response.
|
%% closed suddenly without receiving the response.
|
||||||
%% Building a response on our own
|
%% Building a response on our own
|
||||||
Response = #db_verify{from = RServer, to = LServer,
|
Response = #db_verify{from = RServer, to = LServer,
|
||||||
id = StreamID, type = error,
|
id = StreamID, type = error,
|
||||||
sub_els = [mk_error(Reason)]},
|
sub_els = [mk_error(Reason, Lang)]},
|
||||||
s2s_out_packet(State, Response);
|
s2s_out_packet(State, Response);
|
||||||
s2s_out_closed(State, _Reason) ->
|
s2s_out_closed(State, _Reason) ->
|
||||||
State.
|
State.
|
||||||
@ -171,7 +172,7 @@ s2s_out_downgraded(#{db_enabled := true,
|
|||||||
s2s_out_downgraded(State, _) ->
|
s2s_out_downgraded(State, _) ->
|
||||||
State.
|
State.
|
||||||
|
|
||||||
s2s_in_packet(#{stream_id := StreamID} = State,
|
s2s_in_packet(#{stream_id := StreamID, lang := Lang} = State,
|
||||||
#db_result{from = From, to = To, key = Key, type = undefined}) ->
|
#db_result{from = From, to = To, key = Key, type = undefined}) ->
|
||||||
%% Received dialback request, section 2.2.1, step 1
|
%% Received dialback request, section 2.2.1, step 1
|
||||||
try
|
try
|
||||||
@ -186,7 +187,7 @@ s2s_in_packet(#{stream_id := StreamID} = State,
|
|||||||
{stop,
|
{stop,
|
||||||
send_db_result(State,
|
send_db_result(State,
|
||||||
#db_verify{from = From, to = To, type = error,
|
#db_verify{from = From, to = To, type = error,
|
||||||
sub_els = [mk_error(Reason)]})}
|
sub_els = [mk_error(Reason, Lang)]})}
|
||||||
end;
|
end;
|
||||||
s2s_in_packet(State, #db_verify{to = To, from = From, key = Key,
|
s2s_in_packet(State, #db_verify{to = To, from = From, key = Key,
|
||||||
id = StreamID, type = undefined}) ->
|
id = StreamID, type = undefined}) ->
|
||||||
@ -204,7 +205,7 @@ s2s_in_packet(State, Pkt) when is_record(Pkt, db_result);
|
|||||||
s2s_in_packet(State, _) ->
|
s2s_in_packet(State, _) ->
|
||||||
State.
|
State.
|
||||||
|
|
||||||
s2s_in_recv(State, El, {error, Why}) ->
|
s2s_in_recv(#{lang := Lang} = State, El, {error, Why}) ->
|
||||||
case xmpp:get_name(El) of
|
case xmpp:get_name(El) of
|
||||||
Tag when Tag == <<"db:result">>;
|
Tag when Tag == <<"db:result">>;
|
||||||
Tag == <<"db:verify">> ->
|
Tag == <<"db:verify">> ->
|
||||||
@ -212,7 +213,7 @@ s2s_in_recv(State, El, {error, Why}) ->
|
|||||||
T when T /= <<"valid">>,
|
T when T /= <<"valid">>,
|
||||||
T /= <<"invalid">>,
|
T /= <<"invalid">>,
|
||||||
T /= <<"error">> ->
|
T /= <<"error">> ->
|
||||||
Err = xmpp:make_error(El, mk_error({codec_error, Why})),
|
Err = xmpp:make_error(El, mk_error({codec_error, Why}, Lang)),
|
||||||
{stop, ejabberd_s2s_in:send(State, Err)};
|
{stop, ejabberd_s2s_in:send(State, Err)};
|
||||||
_ ->
|
_ ->
|
||||||
State
|
State
|
||||||
@ -316,17 +317,17 @@ check_from_to(From, To) ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec mk_error(term()) -> stanza_error().
|
-spec mk_error(term(), binary()) -> stanza_error().
|
||||||
mk_error(forbidden) ->
|
mk_error(forbidden, Lang) ->
|
||||||
xmpp:err_forbidden(<<"Access denied by service policy">>, ?MYLANG);
|
xmpp:err_forbidden(<<"Access denied by service policy">>, Lang);
|
||||||
mk_error(host_unknown) ->
|
mk_error(host_unknown, Lang) ->
|
||||||
xmpp:err_not_allowed(<<"Host unknown">>, ?MYLANG);
|
xmpp:err_not_allowed(<<"Host unknown">>, Lang);
|
||||||
mk_error({codec_error, Why}) ->
|
mk_error({codec_error, Why}, Lang) ->
|
||||||
xmpp:err_bad_request(xmpp:io_format_error(Why), ?MYLANG);
|
xmpp:err_bad_request(xmpp:io_format_error(Why), Lang);
|
||||||
mk_error({_Class, _Reason} = Why) ->
|
mk_error({_Class, _Reason} = Why, Lang) ->
|
||||||
Txt = xmpp_stream_out:format_error(Why),
|
Txt = xmpp_stream_out:format_error(Why),
|
||||||
xmpp:err_remote_server_not_found(Txt, ?MYLANG);
|
xmpp:err_remote_server_not_found(Txt, Lang);
|
||||||
mk_error(_) ->
|
mk_error(_, _) ->
|
||||||
xmpp:err_internal_server_error().
|
xmpp:err_internal_server_error().
|
||||||
|
|
||||||
-spec format_error(db_result()) -> binary().
|
-spec format_error(db_result()) -> binary().
|
||||||
|
@ -176,14 +176,24 @@ c2s_authenticated_packet(#{mgmt_state := MgmtState} = State, Pkt)
|
|||||||
c2s_authenticated_packet(State, Pkt) ->
|
c2s_authenticated_packet(State, Pkt) ->
|
||||||
update_num_stanzas_in(State, Pkt).
|
update_num_stanzas_in(State, Pkt).
|
||||||
|
|
||||||
c2s_handle_recv(#{lang := Lang} = State, El, {error, Why}) ->
|
c2s_handle_recv(#{mgmt_state := MgmtState,
|
||||||
|
lang := Lang} = State, El, {error, Why}) ->
|
||||||
Xmlns = xmpp:get_ns(El),
|
Xmlns = xmpp:get_ns(El),
|
||||||
|
IsStanza = xmpp:is_stanza(El),
|
||||||
if Xmlns == ?NS_STREAM_MGMT_2; Xmlns == ?NS_STREAM_MGMT_3 ->
|
if Xmlns == ?NS_STREAM_MGMT_2; Xmlns == ?NS_STREAM_MGMT_3 ->
|
||||||
Txt = xmpp:io_format_error(Why),
|
Txt = xmpp:io_format_error(Why),
|
||||||
Err = #sm_failed{reason = 'bad-request',
|
Err = #sm_failed{reason = 'bad-request',
|
||||||
text = xmpp:mk_text(Txt, Lang),
|
text = xmpp:mk_text(Txt, Lang),
|
||||||
xmlns = Xmlns},
|
xmlns = Xmlns},
|
||||||
send(State, Err);
|
send(State, Err);
|
||||||
|
IsStanza andalso (MgmtState == pending orelse MgmtState == active) ->
|
||||||
|
State1 = update_num_stanzas_in(State, El),
|
||||||
|
case xmpp:get_type(El) of
|
||||||
|
<<"result">> -> State1;
|
||||||
|
<<"error">> -> State1;
|
||||||
|
_ ->
|
||||||
|
State1#{mgmt_force_enqueue => true}
|
||||||
|
end;
|
||||||
true ->
|
true ->
|
||||||
State
|
State
|
||||||
end;
|
end;
|
||||||
@ -193,24 +203,24 @@ c2s_handle_recv(State, _, _) ->
|
|||||||
c2s_handle_send(#{mgmt_state := MgmtState, mod := Mod,
|
c2s_handle_send(#{mgmt_state := MgmtState, mod := Mod,
|
||||||
lang := Lang} = State, Pkt, SendResult)
|
lang := Lang} = State, Pkt, SendResult)
|
||||||
when MgmtState == pending; MgmtState == active ->
|
when MgmtState == pending; MgmtState == active ->
|
||||||
|
IsStanza = xmpp:is_stanza(Pkt),
|
||||||
case Pkt of
|
case Pkt of
|
||||||
_ when ?is_stanza(Pkt) ->
|
_ when IsStanza ->
|
||||||
Meta = xmpp:get_meta(Pkt),
|
case need_to_enqueue(State, Pkt) of
|
||||||
case maps:get(mgmt_is_resent, Meta, false) of
|
{true, State1} ->
|
||||||
false ->
|
case mgmt_queue_add(State1, Pkt) of
|
||||||
case mgmt_queue_add(State, Pkt) of
|
#{mgmt_max_queue := exceeded} = State2 ->
|
||||||
#{mgmt_max_queue := exceeded} = State1 ->
|
State3 = State2#{mgmt_resend => false},
|
||||||
State2 = State1#{mgmt_resend => false},
|
|
||||||
Err = xmpp:serr_policy_violation(
|
Err = xmpp:serr_policy_violation(
|
||||||
<<"Too many unacked stanzas">>, Lang),
|
<<"Too many unacked stanzas">>, Lang),
|
||||||
send(State2, Err);
|
send(State3, Err);
|
||||||
State1 when SendResult == ok ->
|
State2 when SendResult == ok ->
|
||||||
send_rack(State1);
|
send_rack(State2);
|
||||||
State1 ->
|
State2 ->
|
||||||
State1
|
State2
|
||||||
end;
|
end;
|
||||||
true ->
|
{false, State1} ->
|
||||||
State
|
State1
|
||||||
end;
|
end;
|
||||||
#stream_error{} ->
|
#stream_error{} ->
|
||||||
case MgmtState of
|
case MgmtState of
|
||||||
@ -242,13 +252,13 @@ c2s_handle_info(#{mgmt_ack_timer := TRef, jid := JID, mod := Mod} = State,
|
|||||||
[jid:encode(JID)]),
|
[jid:encode(JID)]),
|
||||||
State1 = Mod:close(State),
|
State1 = Mod:close(State),
|
||||||
{stop, transition_to_pending(State1)};
|
{stop, transition_to_pending(State1)};
|
||||||
c2s_handle_info(#{mgmt_state := pending,
|
c2s_handle_info(#{mgmt_state := pending, lang := Lang,
|
||||||
mgmt_pending_timer := TRef, jid := JID, mod := Mod} = State,
|
mgmt_pending_timer := TRef, jid := JID, mod := Mod} = State,
|
||||||
{timeout, TRef, pending_timeout}) ->
|
{timeout, TRef, pending_timeout}) ->
|
||||||
?DEBUG("Timed out waiting for resumption of stream for ~s",
|
?DEBUG("Timed out waiting for resumption of stream for ~s",
|
||||||
[jid:encode(JID)]),
|
[jid:encode(JID)]),
|
||||||
Txt = <<"Timed out waiting for stream resumption">>,
|
Txt = <<"Timed out waiting for stream resumption">>,
|
||||||
Err = xmpp:serr_connection_timeout(Txt, ?MYLANG),
|
Err = xmpp:serr_connection_timeout(Txt, Lang),
|
||||||
Mod:stop(State#{mgmt_state => timeout,
|
Mod:stop(State#{mgmt_state => timeout,
|
||||||
stop_reason => {stream, {out, Err}}});
|
stop_reason => {stream, {out, Err}}});
|
||||||
c2s_handle_info(#{jid := JID} = State, {_Ref, {resume, OldState}}) ->
|
c2s_handle_info(#{jid := JID} = State, {_Ref, {resume, OldState}}) ->
|
||||||
@ -269,8 +279,8 @@ c2s_closed(State, _Reason) ->
|
|||||||
State.
|
State.
|
||||||
|
|
||||||
c2s_terminated(#{mgmt_state := resumed, jid := JID} = State, _Reason) ->
|
c2s_terminated(#{mgmt_state := resumed, jid := JID} = State, _Reason) ->
|
||||||
?INFO_MSG("Closing former stream of resumed session for ~s",
|
?DEBUG("Closing former stream of resumed session for ~s",
|
||||||
[jid:encode(JID)]),
|
[jid:encode(JID)]),
|
||||||
bounce_message_queue(),
|
bounce_message_queue(),
|
||||||
{stop, State};
|
{stop, State};
|
||||||
c2s_terminated(#{mgmt_state := MgmtState, mgmt_stanzas_in := In, sid := SID,
|
c2s_terminated(#{mgmt_state := MgmtState, mgmt_stanzas_in := In, sid := SID,
|
||||||
@ -361,15 +371,15 @@ handle_enable(#{mgmt_timeout := DefaultTimeout,
|
|||||||
DefaultTimeout
|
DefaultTimeout
|
||||||
end,
|
end,
|
||||||
Res = if Timeout > 0 ->
|
Res = if Timeout > 0 ->
|
||||||
?INFO_MSG("Stream management with resumption enabled for ~s",
|
?DEBUG("Stream management with resumption enabled for ~s",
|
||||||
[jid:encode(JID)]),
|
[jid:encode(JID)]),
|
||||||
#sm_enabled{xmlns = Xmlns,
|
#sm_enabled{xmlns = Xmlns,
|
||||||
id = make_resume_id(State),
|
id = make_resume_id(State),
|
||||||
resume = true,
|
resume = true,
|
||||||
max = Timeout};
|
max = Timeout};
|
||||||
true ->
|
true ->
|
||||||
?INFO_MSG("Stream management without resumption enabled for ~s",
|
?DEBUG("Stream management without resumption enabled for ~s",
|
||||||
[jid:encode(JID)]),
|
[jid:encode(JID)]),
|
||||||
#sm_enabled{xmlns = Xmlns}
|
#sm_enabled{xmlns = Xmlns}
|
||||||
end,
|
end,
|
||||||
State1 = State#{mgmt_state => active,
|
State1 = State#{mgmt_state => active,
|
||||||
@ -491,7 +501,7 @@ resend_rack(#{mgmt_ack_timer := _,
|
|||||||
resend_rack(State) ->
|
resend_rack(State) ->
|
||||||
State.
|
State.
|
||||||
|
|
||||||
-spec mgmt_queue_add(state(), xmpp_element()) -> state().
|
-spec mgmt_queue_add(state(), xmlel() | xmpp_element()) -> state().
|
||||||
mgmt_queue_add(#{mgmt_stanzas_out := NumStanzasOut,
|
mgmt_queue_add(#{mgmt_stanzas_out := NumStanzasOut,
|
||||||
mgmt_queue := Queue} = State, Pkt) ->
|
mgmt_queue := Queue} = State, Pkt) ->
|
||||||
NewNum = case NumStanzasOut of
|
NewNum = case NumStanzasOut of
|
||||||
@ -531,8 +541,13 @@ resend_unacked_stanzas(#{mgmt_state := MgmtState,
|
|||||||
[p1_queue:len(Queue), jid:encode(JID)]),
|
[p1_queue:len(Queue), jid:encode(JID)]),
|
||||||
p1_queue:foldl(
|
p1_queue:foldl(
|
||||||
fun({_, Time, Pkt}, AccState) ->
|
fun({_, Time, Pkt}, AccState) ->
|
||||||
NewPkt = add_resent_delay_info(AccState, Pkt, Time),
|
Pkt1 = add_resent_delay_info(AccState, Pkt, Time),
|
||||||
send(AccState, xmpp:put_meta(NewPkt, mgmt_is_resent, true))
|
Pkt2 = if ?is_stanza(Pkt1) ->
|
||||||
|
xmpp:put_meta(Pkt1, mgmt_is_resent, true);
|
||||||
|
true ->
|
||||||
|
Pkt1
|
||||||
|
end,
|
||||||
|
send(AccState, Pkt2)
|
||||||
end, State, Queue);
|
end, State, Queue);
|
||||||
resend_unacked_stanzas(State) ->
|
resend_unacked_stanzas(State) ->
|
||||||
State.
|
State.
|
||||||
@ -646,6 +661,8 @@ inherit_session_state(#{user := U, server := S,
|
|||||||
{error, Msg}
|
{error, Msg}
|
||||||
catch exit:{noproc, _} ->
|
catch exit:{noproc, _} ->
|
||||||
{error, <<"Previous session PID is dead">>};
|
{error, <<"Previous session PID is dead">>};
|
||||||
|
exit:{normal, _} ->
|
||||||
|
{error, <<"Previous session PID has exited">>};
|
||||||
exit:{timeout, _} ->
|
exit:{timeout, _} ->
|
||||||
{error, <<"Session state copying timed out">>}
|
{error, <<"Session state copying timed out">>}
|
||||||
end
|
end
|
||||||
@ -669,6 +686,7 @@ add_resent_delay_info(#{lserver := LServer}, El, Time)
|
|||||||
when is_record(El, message); is_record(El, presence) ->
|
when is_record(El, message); is_record(El, presence) ->
|
||||||
xmpp_util:add_delay_info(El, jid:make(LServer), Time, <<"Resent">>);
|
xmpp_util:add_delay_info(El, jid:make(LServer), Time, <<"Resent">>);
|
||||||
add_resent_delay_info(_State, El, _Time) ->
|
add_resent_delay_info(_State, El, _Time) ->
|
||||||
|
%% TODO
|
||||||
El.
|
El.
|
||||||
|
|
||||||
-spec send(state(), xmpp_element()) -> state().
|
-spec send(state(), xmpp_element()) -> state().
|
||||||
@ -711,6 +729,14 @@ bounce_message_queue() ->
|
|||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec need_to_enqueue(state(), xmlel() | stanza()) -> {boolean(), state()}.
|
||||||
|
need_to_enqueue(State, Pkt) when ?is_stanza(Pkt) ->
|
||||||
|
{not xmpp:get_meta(Pkt, mgmt_is_resent, false), State};
|
||||||
|
need_to_enqueue(#{mgmt_force_enqueue := true} = State, #xmlel{}) ->
|
||||||
|
{true, maps:remove(mgmt_is_resent, State)};
|
||||||
|
need_to_enqueue(State, _) ->
|
||||||
|
{false, State}.
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Configuration processing
|
%%% Configuration processing
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
@ -201,8 +201,10 @@ convert_data(Host, "offline", User, [Data]) ->
|
|||||||
fun({_, RawXML}) ->
|
fun({_, RawXML}) ->
|
||||||
case deserialize(RawXML) of
|
case deserialize(RawXML) of
|
||||||
[El] ->
|
[El] ->
|
||||||
Msg = el_to_offline_msg(LUser, LServer, El),
|
case el_to_offline_msg(LUser, LServer, El) of
|
||||||
ok = mod_offline:store_offline_msg(Msg);
|
[Msg] -> ok = mod_offline:store_offline_msg(Msg);
|
||||||
|
[] -> ok
|
||||||
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
ok
|
ok
|
||||||
end
|
end
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-export([get_string/0, uniform/0, uniform/1, uniform/2, bytes/1,
|
-export([get_string/0, uniform/0, uniform/1, uniform/2, bytes/1,
|
||||||
round_robin/1]).
|
round_robin/1, get_alphanum_string/1]).
|
||||||
|
|
||||||
-define(THRESHOLD, 16#10000000000000000).
|
-define(THRESHOLD, 16#10000000000000000).
|
||||||
|
|
||||||
@ -71,3 +71,21 @@ bytes(N) ->
|
|||||||
-spec round_robin(pos_integer()) -> non_neg_integer().
|
-spec round_robin(pos_integer()) -> non_neg_integer().
|
||||||
round_robin(N) ->
|
round_robin(N) ->
|
||||||
p1_time_compat:unique_integer([monotonic, positive]) rem N.
|
p1_time_compat:unique_integer([monotonic, positive]) rem N.
|
||||||
|
|
||||||
|
-spec get_alphanum_string(non_neg_integer()) -> binary().
|
||||||
|
get_alphanum_string(Length) ->
|
||||||
|
list_to_binary(get_alphanum_string([], Length)).
|
||||||
|
|
||||||
|
-spec get_alphanum_string(string(), non_neg_integer()) -> string().
|
||||||
|
get_alphanum_string(S, 0) -> S;
|
||||||
|
get_alphanum_string(S, N) ->
|
||||||
|
get_alphanum_string([make_rand_char() | S], N - 1).
|
||||||
|
|
||||||
|
-spec make_rand_char() -> char().
|
||||||
|
make_rand_char() ->
|
||||||
|
map_int_to_char(uniform(0, 61)).
|
||||||
|
|
||||||
|
-spec map_int_to_char(0..61) -> char().
|
||||||
|
map_int_to_char(N) when N =< 9 -> N + 48; % Digit.
|
||||||
|
map_int_to_char(N) when N =< 35 -> N + 55; % Upper-case character.
|
||||||
|
map_int_to_char(N) when N =< 61 -> N + 61. % Lower-case character.
|
||||||
|
@ -836,13 +836,13 @@ process_sasl_success(Props, ServerOut,
|
|||||||
AuthModule = proplists:get_value(auth_module, Props),
|
AuthModule = proplists:get_value(auth_module, Props),
|
||||||
Socket1 = xmpp_socket:reset_stream(Socket),
|
Socket1 = xmpp_socket:reset_stream(Socket),
|
||||||
State0 = State#{socket => Socket1},
|
State0 = State#{socket => Socket1},
|
||||||
State1 = send_pkt(State0, #sasl_success{text = ServerOut}),
|
State1 = try Mod:handle_auth_success(User, Mech, AuthModule, State0)
|
||||||
|
catch _:undef -> State
|
||||||
|
end,
|
||||||
case is_disconnected(State1) of
|
case is_disconnected(State1) of
|
||||||
true -> State1;
|
true -> State1;
|
||||||
false ->
|
false ->
|
||||||
State2 = try Mod:handle_auth_success(User, Mech, AuthModule, State1)
|
State2 = send_pkt(State1, #sasl_success{text = ServerOut}),
|
||||||
catch _:undef -> State1
|
|
||||||
end,
|
|
||||||
case is_disconnected(State2) of
|
case is_disconnected(State2) of
|
||||||
true -> State2;
|
true -> State2;
|
||||||
false ->
|
false ->
|
||||||
@ -867,16 +867,22 @@ process_sasl_continue(ServerOut, NewSASLState, State) ->
|
|||||||
process_sasl_failure(Err, User,
|
process_sasl_failure(Err, User,
|
||||||
#{mod := Mod, sasl_mech := Mech, lang := Lang} = State) ->
|
#{mod := Mod, sasl_mech := Mech, lang := Lang} = State) ->
|
||||||
{Reason, Text} = format_sasl_error(Mech, Err),
|
{Reason, Text} = format_sasl_error(Mech, Err),
|
||||||
State1 = send_pkt(State, #sasl_failure{reason = Reason,
|
State1 = try Mod:handle_auth_failure(User, Mech, Text, State)
|
||||||
text = xmpp:mk_text(Text, Lang)}),
|
catch _:undef -> State
|
||||||
|
end,
|
||||||
case is_disconnected(State1) of
|
case is_disconnected(State1) of
|
||||||
true -> State1;
|
true -> State1;
|
||||||
false ->
|
false ->
|
||||||
State2 = try Mod:handle_auth_failure(User, Mech, Text, State1)
|
State2 = send_pkt(State1,
|
||||||
catch _:undef -> State1
|
#sasl_failure{reason = Reason,
|
||||||
end,
|
text = xmpp:mk_text(Text, Lang)}),
|
||||||
State3 = maps:remove(sasl_state, maps:remove(sasl_mech, State2)),
|
case is_disconnected(State2) of
|
||||||
State3#{stream_state => wait_for_sasl_request}
|
true -> State2;
|
||||||
|
false ->
|
||||||
|
State3 = maps:remove(sasl_state,
|
||||||
|
maps:remove(sasl_mech, State2)),
|
||||||
|
State3#{stream_state => wait_for_sasl_request}
|
||||||
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec process_sasl_abort(state()) -> state().
|
-spec process_sasl_abort(state()) -> state().
|
||||||
|
@ -13,6 +13,10 @@ def read():
|
|||||||
write(True)
|
write(True)
|
||||||
elif cmd == 'isuser':
|
elif cmd == 'isuser':
|
||||||
u, s = pkt.split(':', 2)[1:]
|
u, s = pkt.split(':', 2)[1:]
|
||||||
|
if u == "wrong":
|
||||||
|
write(False)
|
||||||
|
else:
|
||||||
|
write(True)
|
||||||
elif cmd == 'setpass':
|
elif cmd == 'setpass':
|
||||||
u, s, p = pkt.split(':', 3)[1:]
|
u, s, p = pkt.split(':', 3)[1:]
|
||||||
write(True)
|
write(True)
|
||||||
|
@ -89,8 +89,14 @@ init_config(Config) ->
|
|||||||
ConfigPath = filename:join([CWD, "ejabberd.yml"]),
|
ConfigPath = filename:join([CWD, "ejabberd.yml"]),
|
||||||
ok = file:write_file(ConfigPath, CfgContent2),
|
ok = file:write_file(ConfigPath, CfgContent2),
|
||||||
setup_ejabberd_lib_path(Config),
|
setup_ejabberd_lib_path(Config),
|
||||||
ok = application:load(sasl),
|
case application:load(sasl) of
|
||||||
ok = application:load(mnesia),
|
ok -> ok;
|
||||||
|
{error, {already_loaded, _}} -> ok
|
||||||
|
end,
|
||||||
|
case application:load(mnesia) of
|
||||||
|
ok -> ok;
|
||||||
|
{error, {already_loaded, _}} -> ok
|
||||||
|
end,
|
||||||
case application:load(ejabberd) of
|
case application:load(ejabberd) of
|
||||||
ok -> ok;
|
ok -> ok;
|
||||||
{error, {already_loaded, _}} -> ok
|
{error, {already_loaded, _}} -> ok
|
||||||
|
Loading…
Reference in New Issue
Block a user