mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-24 17:29:28 +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_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_peer ON archive (server_host, peer);
|
||||
CREATE INDEX i_archive_sh_bare_peer ON archive (server_host, bare_peer);
|
||||
|
||||
CREATE TABLE archive_prefs (
|
||||
username text NOT NULL,
|
||||
|
@ -98,9 +98,9 @@ CREATE TABLE archive (
|
||||
);
|
||||
|
||||
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_peer ON archive(peer);
|
||||
CREATE INDEX i_bare_peer ON archive(bare_peer);
|
||||
|
||||
CREATE TABLE archive_prefs (
|
||||
username text NOT NULL PRIMARY KEY,
|
||||
|
@ -41,15 +41,15 @@ CREATE TABLE [dbo].[archive] (
|
||||
CREATE INDEX [archive_username_timestamp] ON [archive] (username, timestamp)
|
||||
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)
|
||||
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] (
|
||||
[username] [varchar] (250) NOT NULL,
|
||||
[def] [text] NOT NULL,
|
||||
|
@ -113,10 +113,10 @@ CREATE TABLE archive (
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
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_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 (
|
||||
username varchar(191) NOT NULL,
|
||||
|
@ -102,10 +102,10 @@ CREATE TABLE archive (
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
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_peer USING BTREE ON archive(peer);
|
||||
CREATE INDEX i_bare_peer USING BTREE ON archive(bare_peer);
|
||||
|
||||
CREATE TABLE archive_prefs (
|
||||
username varchar(191) NOT NULL PRIMARY KEY,
|
||||
|
@ -61,15 +61,14 @@
|
||||
-- ALTER TABLE spool ALTER COLUMN server_host DROP DEFAULT;
|
||||
|
||||
-- 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_peer;
|
||||
-- DROP INDEX i_username_bare_peer;
|
||||
-- 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_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_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_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_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_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 (
|
||||
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_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_peer ON archive USING btree (peer);
|
||||
CREATE INDEX i_bare_peer ON archive USING btree (bare_peer);
|
||||
|
||||
CREATE TABLE archive_prefs (
|
||||
username text NOT NULL PRIMARY KEY,
|
||||
|
@ -59,8 +59,8 @@
|
||||
|
||||
start(_Opts) ->
|
||||
Fqdn = get_local_fqdn(),
|
||||
?INFO_MSG("FQDN used to check DIGEST-MD5 SASL authentication: ~s",
|
||||
[Fqdn]),
|
||||
?DEBUG("FQDN used to check DIGEST-MD5 SASL authentication: ~s",
|
||||
[Fqdn]),
|
||||
cyrsasl:register_mechanism(<<"DIGEST-MD5">>, ?MODULE,
|
||||
digest).
|
||||
|
||||
|
@ -81,15 +81,8 @@ prepare(ClientIn) ->
|
||||
_ -> error
|
||||
end.
|
||||
|
||||
parse(S) -> parse1(binary_to_list(S), "", []).
|
||||
|
||||
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(S) ->
|
||||
binary:split(S, <<0>>, [global]).
|
||||
|
||||
parse_domain(S) -> parse_domain1(binary_to_list(S), "", []).
|
||||
|
||||
|
@ -526,7 +526,10 @@ db_get_password(User, Server, Mod) ->
|
||||
UseCache = use_cache(Mod, Server),
|
||||
case erlang:function_exported(Mod, get_password, 2) of
|
||||
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 ->
|
||||
error;
|
||||
true when UseCache ->
|
||||
@ -544,7 +547,20 @@ db_user_exists(User, Server, Mod) ->
|
||||
error ->
|
||||
case Mod:store_type(Server) of
|
||||
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
|
||||
end
|
||||
@ -568,7 +584,7 @@ db_check_password(User, AuthzId, Server, ProvidedPassword,
|
||||
false ->
|
||||
error
|
||||
end
|
||||
end, cache_nodes(Mod, Server)) of
|
||||
end) of
|
||||
{ok, _} ->
|
||||
true;
|
||||
error ->
|
||||
|
@ -519,6 +519,7 @@ init([State, Opts]) ->
|
||||
TLSRequired = proplists:get_bool(starttls_required, Opts),
|
||||
TLSVerify = proplists:get_bool(tls_verify, Opts),
|
||||
Zlib = proplists:get_bool(zlib, Opts),
|
||||
Timeout = ejabberd_config:negotiation_timeout(),
|
||||
State1 = State#{tls_options => TLSOpts2,
|
||||
tls_required => TLSRequired,
|
||||
tls_enabled => TLSEnabled,
|
||||
@ -530,7 +531,8 @@ init([State, Opts]) ->
|
||||
lserver => ?MYNAME,
|
||||
access => Access,
|
||||
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) ->
|
||||
Pres = case maps:get(pres_last, State, error) of
|
||||
@ -997,6 +999,9 @@ opt_type(_) ->
|
||||
(max_stanza_size) -> fun((timeout()) -> timeout());
|
||||
(max_fsm_queue) -> fun((timeout()) -> timeout());
|
||||
(stream_management) -> fun((boolean()) -> boolean());
|
||||
(inet) -> fun((boolean()) -> boolean());
|
||||
(inet6) -> fun((boolean()) -> boolean());
|
||||
(backlog) -> fun((timeout()) -> timeout());
|
||||
(atom()) -> [atom()].
|
||||
listen_opt_type(access) -> fun acl:access_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: "
|
||||
"use mod_stream_mgmt module", []),
|
||||
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) ->
|
||||
StreamOpts = mod_stream_mgmt:mod_options(?MYNAME),
|
||||
case lists:keyfind(O, 1, StreamOpts) of
|
||||
false ->
|
||||
[access, shaper, certfile, ciphers, dhfile, cafile,
|
||||
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' "
|
||||
"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,
|
||||
result=Result, result_desc=ResultDesc}=Cmd, HTMLOutput, Langs) ->
|
||||
try
|
||||
LDesc = case LongDesc of
|
||||
"" -> Desc;
|
||||
_ -> LongDesc
|
||||
end,
|
||||
ArgsText = case ArgsDesc of
|
||||
none ->
|
||||
[?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,
|
||||
|
||||
[?TAG(h1, [?TAG(strong, atom_to_list(Name)), <<" - ">>, ?RAW(Desc)]),
|
||||
?TAG(p, ?RAW(LDesc)),
|
||||
?TAG(h2, <<"Arguments:">>), ArgsText,
|
||||
?TAG(h2, <<"Result:">>), ResultText,
|
||||
?TAG(h2, <<"Examples:">>), gen_calls(Cmd, HTMLOutput, Langs)]
|
||||
[?TAG(h1, atom_to_list(Name)),
|
||||
?TAG(p, ?RAW(Desc)),
|
||||
case LongDesc of
|
||||
"" -> [];
|
||||
_ -> ?TAG(p, ?RAW(LongDesc))
|
||||
end,
|
||||
?TAG(h2, <<"Arguments:">>), ArgsText,
|
||||
?TAG(h2, <<"Result:">>), ResultText,
|
||||
?TAG(h2, <<"Examples:">>), gen_calls(Cmd, HTMLOutput, Langs)]
|
||||
catch
|
||||
_:Ex ->
|
||||
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_queue_type/1, queue_dir/0, fsm_limit_opts/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]).
|
||||
|
||||
@ -1415,6 +1415,8 @@ opt_type(cache_life_time) ->
|
||||
(infinity) -> infinity;
|
||||
(unlimited) -> infinity
|
||||
end;
|
||||
opt_type(negotiation_timeout) ->
|
||||
fun(T) when T > 0 -> T end;
|
||||
opt_type(shared_key) ->
|
||||
fun iolist_to_binary/1;
|
||||
opt_type(node_start) ->
|
||||
@ -1425,7 +1427,7 @@ 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,
|
||||
shared_key, node_start, validate_stream].
|
||||
shared_key, node_start, validate_stream, negotiation_timeout].
|
||||
|
||||
-spec may_hide_data(any()) -> any().
|
||||
may_hide_data(Data) ->
|
||||
@ -1479,3 +1481,7 @@ codec_options(Host) ->
|
||||
true -> [];
|
||||
false -> [ignore_els]
|
||||
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;
|
||||
listen_opt_type(custom_headers) ->
|
||||
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(_) ->
|
||||
%% TODO
|
||||
fun(A) -> A end.
|
||||
|
@ -144,7 +144,7 @@ noreply(#state{expire = Expire} = State) ->
|
||||
-spec encode_id(non_neg_integer(), binary()) -> binary().
|
||||
encode_id(Expire, Rnd) ->
|
||||
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>>),
|
||||
<<"rr-", ExpireBin/binary, $-, Rnd/binary, $-, CheckSum/binary, $-, Node/binary>>.
|
||||
|
||||
@ -155,7 +155,7 @@ decode_id(<<"rr-", ID/binary>>) ->
|
||||
[Rnd, Rest] = binary:split(Tail, <<"-">>),
|
||||
[CheckSum, NodeBin] = binary:split(Rest, <<"-">>),
|
||||
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),
|
||||
{ok, Expire, Rnd, Node}
|
||||
catch _:{badmatch, _} ->
|
||||
|
@ -259,6 +259,7 @@ init([State, Opts]) ->
|
||||
false -> [compression_none | TLSOpts1];
|
||||
true -> TLSOpts1
|
||||
end,
|
||||
Timeout = ejabberd_config:negotiation_timeout(),
|
||||
State1 = State#{tls_options => TLSOpts2,
|
||||
auth_domains => sets:new(),
|
||||
xmlns => ?NS_SERVER,
|
||||
@ -268,7 +269,8 @@ init([State, Opts]) ->
|
||||
server_host => ?MYNAME,
|
||||
established => false,
|
||||
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) ->
|
||||
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());
|
||||
(max_stanza_type) -> fun((timeout()) -> timeout());
|
||||
(max_fsm_queue) -> fun((pos_integer()) -> pos_integer());
|
||||
(inet) -> fun((boolean()) -> boolean());
|
||||
(inet6) -> fun((boolean()) -> boolean());
|
||||
(backlog) -> fun((timeout()) -> timeout());
|
||||
(atom()) -> [atom()].
|
||||
listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
|
||||
listen_opt_type(certfile = Opt) ->
|
||||
@ -379,6 +384,10 @@ listen_opt_type(max_stanza_size) ->
|
||||
end;
|
||||
listen_opt_type(max_fsm_queue) ->
|
||||
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(_) ->
|
||||
[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;
|
||||
false -> unlimited
|
||||
end,
|
||||
Timeout = ejabberd_config:negotiation_timeout(),
|
||||
State1 = State#{on_route => queue,
|
||||
queue => p1_queue:new(QueueType, QueueLimit),
|
||||
xmlns => ?NS_SERVER,
|
||||
lang => ?MYLANG,
|
||||
server_host => ServerHost,
|
||||
shaper => none},
|
||||
State2 = xmpp_stream_out:set_timeout(State1, Timeout),
|
||||
?INFO_MSG("Outbound s2s connection started: ~s -> ~s",
|
||||
[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) ->
|
||||
ejabberd_hooks:run_fold(s2s_out_handle_call, ServerHost, State, [Request, From]).
|
||||
|
@ -100,16 +100,20 @@ init([State, Opts]) ->
|
||||
false -> [compression_none | TLSOpts1];
|
||||
true -> TLSOpts1
|
||||
end,
|
||||
GlobalRoutes = proplists:get_value(global_routes, Opts, true),
|
||||
Timeout = ejabberd_config:negotiation_timeout(),
|
||||
State1 = xmpp_stream_in:change_shaper(State, Shaper),
|
||||
State2 = State1#{access => Access,
|
||||
xmlns => ?NS_COMPONENT,
|
||||
lang => ?MYLANG,
|
||||
server => ?MYNAME,
|
||||
host_opts => dict:from_list(HostOpts1),
|
||||
stream_version => undefined,
|
||||
tls_options => TLSOpts,
|
||||
check_from => CheckFrom},
|
||||
ejabberd_hooks:run_fold(component_init, {ok, State2}, [Opts]).
|
||||
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
|
||||
State3 = State2#{access => Access,
|
||||
xmlns => ?NS_COMPONENT,
|
||||
lang => ?MYLANG,
|
||||
server => ?MYNAME,
|
||||
host_opts => dict:from_list(HostOpts1),
|
||||
stream_version => undefined,
|
||||
tls_options => TLSOpts,
|
||||
global_routes => GlobalRoutes,
|
||||
check_from => CheckFrom},
|
||||
ejabberd_hooks:run_fold(component_init, {ok, State3}, [Opts]).
|
||||
|
||||
handle_stream_start(_StreamStart,
|
||||
#{remote_server := RemoteServer,
|
||||
@ -140,7 +144,7 @@ get_password_fun(#{remote_server := RemoteServer,
|
||||
host_opts := HostOpts}) ->
|
||||
fun(_) ->
|
||||
case dict:find(RemoteServer, HostOpts) of
|
||||
{ok, Password} ->
|
||||
{ok, Password} ->
|
||||
{Password, undefined};
|
||||
error ->
|
||||
?INFO_MSG("(~s) Domain ~s is unconfigured for "
|
||||
@ -153,16 +157,22 @@ get_password_fun(#{remote_server := RemoteServer,
|
||||
|
||||
handle_auth_success(_, Mech, _,
|
||||
#{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 "
|
||||
"for ~s from ~s",
|
||||
[xmpp_socket:pp(Socket), Mech, RemoteServer,
|
||||
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
|
||||
lists:foreach(
|
||||
fun (H) ->
|
||||
ejabberd_router:register_route(H, ?MYNAME),
|
||||
ejabberd_hooks:run(component_connected, [H])
|
||||
end, dict:fetch_keys(HostOpts)),
|
||||
Routes = if GlobalRoutes ->
|
||||
dict:fetch_keys(HostOpts);
|
||||
true ->
|
||||
[RemoteServer]
|
||||
end,
|
||||
lists:foreach(
|
||||
fun(H) ->
|
||||
ejabberd_router:register_route(H, ?MYNAME),
|
||||
ejabberd_hooks:run(component_connected, [H])
|
||||
end, Routes),
|
||||
State.
|
||||
|
||||
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),
|
||||
From = xmpp:get_from(Pkt),
|
||||
case check_from(From, State) of
|
||||
true ->
|
||||
true ->
|
||||
ejabberd_router:route(Pkt),
|
||||
State;
|
||||
false ->
|
||||
Txt = <<"Improper domain part of 'from' attribute">>,
|
||||
false ->
|
||||
Txt = <<"Improper domain part of 'from' attribute">>,
|
||||
Err = xmpp:serr_invalid_from(Txt, Lang),
|
||||
xmpp_stream_in:send(State, Err)
|
||||
end;
|
||||
@ -193,7 +203,7 @@ handle_authenticated_packet(_Pkt, State) ->
|
||||
|
||||
handle_info({route, Packet}, #{access := Access} = State) ->
|
||||
case acl:match_rule(global, Access, xmpp:get_from(Packet)) of
|
||||
allow ->
|
||||
allow ->
|
||||
xmpp_stream_in:send(State, Packet);
|
||||
deny ->
|
||||
Lang = xmpp:get_lang(Packet),
|
||||
@ -205,17 +215,27 @@ handle_info(Info, State) ->
|
||||
?ERROR_MSG("Unexpected info: ~p", [Info]),
|
||||
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
|
||||
established ->
|
||||
Routes = if GlobalRoutes ->
|
||||
dict:fetch_keys(HostOpts);
|
||||
true ->
|
||||
[RemoteServer]
|
||||
end,
|
||||
lists:foreach(
|
||||
fun(H) ->
|
||||
ejabberd_router:unregister_route(H),
|
||||
ejabberd_router:unregister_route(H),
|
||||
ejabberd_hooks:run(component_disconnected, [H, Reason])
|
||||
end, dict:fetch_keys(HostOpts));
|
||||
end, Routes);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
ok
|
||||
end;
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
@ -268,9 +288,12 @@ transform_listen_option(Opt, Opts) ->
|
||||
(check_from) -> fun((boolean()) -> boolean());
|
||||
(password) -> fun((boolean()) -> boolean());
|
||||
(hosts) -> fun(([{binary(), [{password, binary()}]}]) ->
|
||||
[{binary(), binary() | undefined}]);
|
||||
[{binary(), binary() | undefined}]);
|
||||
(max_stanza_type) -> fun((timeout()) -> timeout());
|
||||
(max_fsm_queue) -> fun((pos_integer()) -> pos_integer());
|
||||
(inet) -> fun((boolean()) -> boolean());
|
||||
(inet6) -> fun((boolean()) -> boolean());
|
||||
(backlog) -> fun((timeout()) -> timeout());
|
||||
(atom()) -> [atom()].
|
||||
listen_opt_type(access) -> fun acl:access_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}
|
||||
end, HostOpts)
|
||||
end;
|
||||
listen_opt_type(global_routes) ->
|
||||
fun(B) when is_boolean(B) -> B end;
|
||||
listen_opt_type(max_stanza_size) ->
|
||||
fun(I) when is_integer(I) -> I;
|
||||
(unlimited) -> infinity;
|
||||
@ -306,7 +331,11 @@ listen_opt_type(max_stanza_size) ->
|
||||
end;
|
||||
listen_opt_type(max_fsm_queue) ->
|
||||
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(_) ->
|
||||
[access, shaper_rule, certfile, ciphers, dhfile, cafile, tls,
|
||||
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;
|
||||
listen_opt_type(server_name) ->
|
||||
fun iolist_to_binary/1;
|
||||
listen_opt_type(backlog) ->
|
||||
fun(I) when is_integer(I), I>0 -> I end;
|
||||
listen_opt_type(_) ->
|
||||
[shaper, auth_type, auth_realm, tls, certfile, turn_min_port,
|
||||
turn_max_port, turn_max_allocations, turn_max_permissions,
|
||||
server_name].
|
||||
server_name, backlog].
|
||||
-endif.
|
||||
|
@ -30,150 +30,58 @@
|
||||
|
||||
-export([start_link/0, init/1]).
|
||||
|
||||
-define(SHUTDOWN_TIMEOUT, timer:seconds(30)).
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
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},
|
||||
[Hooks,
|
||||
Cluster,
|
||||
CyrSASL,
|
||||
Translation,
|
||||
AccessPerms,
|
||||
Ctl,
|
||||
Commands,
|
||||
Admin,
|
||||
PKIX,
|
||||
ACME,
|
||||
Listener,
|
||||
S2S,
|
||||
S2SInSupervisor,
|
||||
S2SOutSupervisor,
|
||||
ServiceSupervisor,
|
||||
ACL,
|
||||
Shaper,
|
||||
BackendSupervisor,
|
||||
SQLSupervisor,
|
||||
RiakSupervisor,
|
||||
RedisSupervisor,
|
||||
IQ,
|
||||
Router,
|
||||
RouterMulticast,
|
||||
Local,
|
||||
SM,
|
||||
Captcha,
|
||||
ExtMod,
|
||||
GenModSupervisor,
|
||||
Auth,
|
||||
OAuth]}}.
|
||||
[worker(ejabberd_hooks),
|
||||
worker(ejabberd_cluster),
|
||||
worker(cyrsasl),
|
||||
worker(translate),
|
||||
worker(ejabberd_access_permissions),
|
||||
worker(ejabberd_ctl),
|
||||
worker(ejabberd_commands),
|
||||
worker(ejabberd_admin),
|
||||
worker(ejabberd_pkix),
|
||||
worker(ejabberd_acme),
|
||||
supervisor(ejabberd_listener),
|
||||
worker(ejabberd_s2s),
|
||||
simple_supervisor(ejabberd_s2s_in),
|
||||
simple_supervisor(ejabberd_s2s_out),
|
||||
simple_supervisor(ejabberd_service),
|
||||
worker(acl),
|
||||
worker(shaper),
|
||||
supervisor(ejabberd_backend_sup),
|
||||
supervisor(ejabberd_rdbms),
|
||||
supervisor(ejabberd_riak_sup),
|
||||
supervisor(ejabberd_redis_sup),
|
||||
worker(ejabberd_iq),
|
||||
worker(ejabberd_router),
|
||||
worker(ejabberd_router_multicast),
|
||||
worker(ejabberd_local),
|
||||
worker(ejabberd_sm),
|
||||
worker(ejabberd_captcha),
|
||||
worker(ext_mod),
|
||||
supervisor(ejabberd_gen_mod_sup, gen_mod),
|
||||
worker(ejabberd_auth),
|
||||
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;
|
||||
listen_opt_type(timeout) ->
|
||||
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(_) ->
|
||||
[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
|
||||
false ->
|
||||
?WARNING_MSG("Third-party module '~s' doesn't export "
|
||||
"options validator; consider to upgrade "
|
||||
"the module", [Module]),
|
||||
case code:ensure_loaded(Module) of
|
||||
{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;
|
||||
true ->
|
||||
[]
|
||||
|
@ -59,6 +59,7 @@
|
||||
add_rosteritem/7, delete_rosteritem/4,
|
||||
process_rosteritems/5, get_roster/2, push_roster/3,
|
||||
push_roster_all/1, push_alltoall/2,
|
||||
push_roster_item/5, build_roster_item/3,
|
||||
|
||||
% Private storage
|
||||
private_get/4, private_set/3,
|
||||
@ -1506,11 +1507,12 @@ srg_get_info(Group, Host) ->
|
||||
Os when is_list(Os) -> Os;
|
||||
error -> []
|
||||
end,
|
||||
[{misc:atom_to_binary(Title), btl(Value)} || {Title, Value} <- Opts].
|
||||
[{misc:atom_to_binary(Title), to_list(Value)} || {Title, Value} <- Opts].
|
||||
|
||||
btl([]) -> [];
|
||||
btl([B|L]) -> [btl(B)|btl(L)];
|
||||
btl(B) -> binary_to_list(B).
|
||||
to_list([]) -> [];
|
||||
to_list([H|T]) -> [to_list(H)|to_list(T)];
|
||||
to_list(E) when is_atom(E) -> atom_to_list(E);
|
||||
to_list(E) -> binary_to_list(E).
|
||||
|
||||
srg_get_members(Group, Host) ->
|
||||
Members = mod_shared_roster:get_group_explicit_users(Host,Group),
|
||||
|
@ -199,8 +199,10 @@ need_check(Pkt) ->
|
||||
false
|
||||
end,
|
||||
AllowLocalUsers = gen_mod:get_module_opt(LServer, ?MODULE, allow_local_users),
|
||||
not (IsEmpty orelse ((AllowLocalUsers orelse From#jid.luser == <<"">>)
|
||||
andalso ejabberd_router:is_my_host(From#jid.lserver))).
|
||||
Access = gen_mod:get_module_opt(LServer, ?MODULE, access),
|
||||
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().
|
||||
check_subscription(From, To) ->
|
||||
@ -265,10 +267,14 @@ mod_opt_type(allow_local_users) ->
|
||||
mod_opt_type(allow_transports) ->
|
||||
fun (B) when is_boolean(B) -> B end;
|
||||
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(_) ->
|
||||
[{drop, true},
|
||||
[{access, none},
|
||||
{drop, true},
|
||||
{log, false},
|
||||
{captcha, false},
|
||||
{allow_local_users, true},
|
||||
|
@ -234,10 +234,11 @@ process_unblock(#iq{from = From} = IQ, LJIDs) ->
|
||||
|
||||
-spec broadcast_event(jid(), block() | unblock()) -> ok.
|
||||
broadcast_event(#jid{luser = LUser, lserver = LServer} = From, Event) ->
|
||||
BFrom = jid:remove_resource(From),
|
||||
lists:foreach(
|
||||
fun(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>>,
|
||||
sub_els = [Event]},
|
||||
ejabberd_router:route(IQ)
|
||||
|
@ -115,10 +115,10 @@ iq_handler(#iq{type = set, lang = Lang, from = From,
|
||||
{U, S, R} = jid:tolower(From),
|
||||
Result = case El of
|
||||
#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);
|
||||
#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)
|
||||
end,
|
||||
case Result of
|
||||
@ -164,9 +164,9 @@ user_receive_packet({Packet, #{jid := JID} = C2SState}) ->
|
||||
stanza() | {stop, stanza()}.
|
||||
check_and_forward(JID, To, Packet, Direction)->
|
||||
case is_chat_message(Packet) andalso
|
||||
not is_muc_pm(To, Packet) andalso
|
||||
xmpp:has_subtag(Packet, #carbons_private{}) == false andalso
|
||||
xmpp:has_subtag(Packet, #hint{type = 'no-copy'}) == false of
|
||||
not is_received_muc_pm(To, Packet, Direction) andalso
|
||||
not xmpp:has_subtag(Packet, #carbons_private{}) andalso
|
||||
not xmpp:has_subtag(Packet, #hint{type = 'no-copy'}) of
|
||||
true ->
|
||||
case is_carbon_copy(Packet) of
|
||||
false ->
|
||||
@ -282,9 +282,12 @@ is_chat_message(#message{type = normal, body = [_|_]}) ->
|
||||
is_chat_message(_) ->
|
||||
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;
|
||||
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{}).
|
||||
|
||||
-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,
|
||||
[JID, UserDir, Size, Lang]) of
|
||||
allow ->
|
||||
RandStr = make_rand_string(SecretLength),
|
||||
RandStr = randoms:get_alphanum_string(SecretLength),
|
||||
FileStr = make_file_string(File),
|
||||
?INFO_MSG("Got HTTP upload slot for ~s (file: ~s)",
|
||||
[jid:encode(JID), File]),
|
||||
@ -687,27 +687,6 @@ make_user_string(#jid{luser = U}, node) ->
|
||||
make_file_string(File) ->
|
||||
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().
|
||||
|
||||
yield_content_type(<<"">>) -> ?DEFAULT_CONTENT_TYPE;
|
||||
|
@ -156,9 +156,9 @@ privacy_check_packet(allow, C2SState,
|
||||
case xmpp:has_subtag(IQ, #last{}) of
|
||||
true ->
|
||||
#jid{luser = LUser, lserver = LServer} = To,
|
||||
{Sub, _} = ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, LServer,
|
||||
{none, []}, [LUser, LServer, From]),
|
||||
{Sub, _, _} = ejabberd_hooks:run_fold(
|
||||
roster_get_jid_info, LServer,
|
||||
{none, none, []}, [LUser, LServer, From]),
|
||||
if Sub == from; Sub == both ->
|
||||
Pres = #presence{from = To, to = From},
|
||||
case ejabberd_hooks:run_fold(
|
||||
|
@ -67,9 +67,10 @@
|
||||
-callback select(binary(), jid(), jid(), mam_query:result(),
|
||||
#rsm_set{} | undefined, chat | groupchat) ->
|
||||
{[{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
|
||||
@ -77,7 +78,7 @@
|
||||
start(Host, Opts) ->
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, Opts),
|
||||
init_cache(Host, Opts),
|
||||
init_cache(Mod, Host, Opts),
|
||||
register_iq_handlers(Host),
|
||||
ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE,
|
||||
sm_receive_packet, 50),
|
||||
@ -113,19 +114,24 @@ start(Host, Opts) ->
|
||||
ejabberd_commands:register_commands(get_commands_spec()),
|
||||
ok.
|
||||
|
||||
use_cache(Host, Opts) ->
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
use_cache(Mod, Host) ->
|
||||
case erlang:function_exported(Mod, use_cache, 2) of
|
||||
true -> Mod:use_cache(Host, Opts);
|
||||
false -> gen_mod:get_opt(use_cache, Opts)
|
||||
true -> Mod:use_cache(Host);
|
||||
false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
|
||||
end.
|
||||
|
||||
init_cache(Host, Opts) ->
|
||||
case use_cache(Host, Opts) of
|
||||
cache_nodes(Mod, Host) ->
|
||||
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 ->
|
||||
ets_cache:new(archive_prefs_cache, cache_opts(Opts));
|
||||
false ->
|
||||
ok
|
||||
ets_cache:delete(archive_prefs_cache)
|
||||
end.
|
||||
|
||||
cache_opts(Opts) ->
|
||||
@ -185,7 +191,7 @@ reload(Host, NewOpts, OldOpts) ->
|
||||
true ->
|
||||
ok
|
||||
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
|
||||
{false, true, _} ->
|
||||
ejabberd_hooks:add(message_is_archived, Host, ?MODULE,
|
||||
@ -236,8 +242,13 @@ remove_user(User, Server) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:remove_user(LUser, LServer),
|
||||
ets_cache:delete(archive_prefs_cache, {LUser, LServer},
|
||||
ejabberd_cluster:get_nodes()).
|
||||
case use_cache(Mod, LServer) of
|
||||
true ->
|
||||
ets_cache:delete(archive_prefs_cache, {LUser, LServer},
|
||||
cache_nodes(Mod, LServer));
|
||||
false ->
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec remove_room(binary(), binary(), binary()) -> ok.
|
||||
remove_room(LServer, Name, Host) ->
|
||||
@ -792,16 +803,26 @@ write_prefs(LUser, LServer, Host, Default, Always, Never) ->
|
||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||
case Mod:write_prefs(LUser, LServer, Prefs, Host) of
|
||||
ok ->
|
||||
ets_cache:delete(archive_prefs_cache, {LUser, LServer},
|
||||
ejabberd_cluster:get_nodes());
|
||||
case use_cache(Mod, LServer) of
|
||||
true ->
|
||||
ets_cache:delete(archive_prefs_cache, {LUser, LServer},
|
||||
cache_nodes(Mod, LServer));
|
||||
false ->
|
||||
ok
|
||||
end;
|
||||
_Err ->
|
||||
{error, db_failure}
|
||||
end.
|
||||
|
||||
get_prefs(LUser, LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Res = ets_cache:lookup(archive_prefs_cache, {LUser, LServer},
|
||||
fun() -> Mod:get_prefs(LUser, LServer) end),
|
||||
Res = case use_cache(Mod, LServer) of
|
||||
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
|
||||
{ok, Prefs} ->
|
||||
Prefs;
|
||||
@ -831,10 +852,16 @@ maybe_activate_mam(LUser, LServer) ->
|
||||
case ActivateOpt of
|
||||
true ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Res = ets_cache:lookup(archive_prefs_cache, {LUser, LServer},
|
||||
fun() ->
|
||||
Mod:get_prefs(LUser, LServer)
|
||||
end),
|
||||
Res = case use_cache(Mod, LServer) of
|
||||
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
|
||||
{ok, _Prefs} ->
|
||||
ok;
|
||||
|
@ -107,8 +107,7 @@
|
||||
-callback unregister_online_user(binary(), ljid(), binary(), binary()) -> any().
|
||||
-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_subscribed_rooms(binary(), binary(), jid()) ->
|
||||
{ok, [{ljid(), binary(), [binary()]}]} | {error, any()}.
|
||||
-callback get_subscribed_rooms(binary(), binary(), jid()) -> [ljid()] | [].
|
||||
|
||||
%%====================================================================
|
||||
%% API
|
||||
@ -727,7 +726,7 @@ iq_get_register_info(ServerHost, Host, From, Lang) ->
|
||||
instructions = [Inst], fields = Fields},
|
||||
#register{nick = Nick,
|
||||
registered = Registered,
|
||||
instructions =
|
||||
instructions =
|
||||
translate:translate(
|
||||
Lang, <<"You need a client that supports x:data "
|
||||
"to register the nickname">>),
|
||||
|
@ -37,7 +37,7 @@
|
||||
get_user_rooms/2, get_room_occupants/2,
|
||||
get_room_occupants_number/2, send_direct_invitation/5,
|
||||
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,
|
||||
subscribe_room/4, unsubscribe_room/2, get_subscribers/2,
|
||||
web_page_host/3, mod_options/1, get_commands_spec/0]).
|
||||
@ -313,8 +313,17 @@ get_commands_spec() ->
|
||||
{affiliation, atom},
|
||||
{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
|
||||
%% @doc Create a room immediately with the default options.
|
||||
create_room(Name1, Host1, ServerHost) ->
|
||||
create_room_with_opts(Name1, Host1, ServerHost, []),
|
||||
change_room_option(Name1, Host1, <<"persistent">>, <<"true">>).
|
||||
case create_room_with_opts(Name1, Host1, ServerHost, []) of
|
||||
ok -> change_room_option(Name1, Host1, <<"persistent">>, <<"true">>);
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
create_room_with_opts(Name1, Host1, ServerHost, CustomRoomOpts) ->
|
||||
true = (error /= (Name = jid:nodeprep(Name1))),
|
||||
@ -1032,6 +1043,25 @@ get_room_affiliations(Name, Service) ->
|
||||
throw({error, "The room does not exist."})
|
||||
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
|
||||
%%----------------------------
|
||||
|
@ -37,7 +37,9 @@
|
||||
get_role/2,
|
||||
get_affiliation/2,
|
||||
is_occupant_or_admin/2,
|
||||
route/2]).
|
||||
route/2,
|
||||
expand_opts/1,
|
||||
config_fields/0]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1,
|
||||
@ -506,7 +508,7 @@ handle_event(_Event, StateName, StateData) ->
|
||||
{next_state, 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
|
||||
true ->
|
||||
get_roomdesc_reply(JID, StateData,
|
||||
@ -3609,6 +3611,35 @@ make_opts(StateData) ->
|
||||
{subject_author, StateData#state.subject_author},
|
||||
{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}.
|
||||
destroy_room(DEl, StateData) ->
|
||||
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().
|
||||
iq_disco_info_extras(Lang, StateData) ->
|
||||
Fs1 = [{description, (StateData#state.config)#config.description},
|
||||
{occupants, ?DICT:size(StateData#state.users)},
|
||||
{occupants, ?DICT:size(StateData#state.nicks)},
|
||||
{contactjid, get_owners(StateData)}],
|
||||
Fs2 = case (StateData#state.config)#config.pubsub of
|
||||
Node when is_binary(Node), Node /= <<"">> ->
|
||||
@ -3938,20 +3969,18 @@ get_roomdesc_tail(StateData, Lang) ->
|
||||
true -> <<"">>;
|
||||
_ -> translate:translate(Lang, <<"private, ">>)
|
||||
end,
|
||||
Len = (?DICT):size(StateData#state.users),
|
||||
Len = (?DICT):size(StateData#state.nicks),
|
||||
<<" (", Desc/binary, (integer_to_binary(Len))/binary, ")">>.
|
||||
|
||||
-spec get_mucroom_disco_items(state()) -> disco_items().
|
||||
get_mucroom_disco_items(StateData) ->
|
||||
Items = lists:map(
|
||||
fun({_LJID, Info}) ->
|
||||
Nick = Info#user.nick,
|
||||
#disco_item{jid = jid:make(StateData#state.room,
|
||||
StateData#state.host,
|
||||
Nick),
|
||||
name = Nick}
|
||||
end,
|
||||
(?DICT):to_list(StateData#state.users)),
|
||||
Items = ?DICT:fold(
|
||||
fun(Nick, _, Acc) ->
|
||||
[#disco_item{jid = jid:make(StateData#state.room,
|
||||
StateData#state.host,
|
||||
Nick),
|
||||
name = Nick}|Acc]
|
||||
end, [], StateData#state.nicks),
|
||||
#disco_items{items = Items}.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
@ -121,13 +121,14 @@ s2s_out_init(Acc, _Opts) ->
|
||||
|
||||
s2s_out_closed(#{server := LServer,
|
||||
remote_server := RServer,
|
||||
lang := Lang,
|
||||
db_verify := {StreamID, _Key, _Pid}} = State, Reason) ->
|
||||
%% Outbound s2s verificating connection (created at step 1) is
|
||||
%% closed suddenly without receiving the response.
|
||||
%% Building a response on our own
|
||||
Response = #db_verify{from = RServer, to = LServer,
|
||||
id = StreamID, type = error,
|
||||
sub_els = [mk_error(Reason)]},
|
||||
sub_els = [mk_error(Reason, Lang)]},
|
||||
s2s_out_packet(State, Response);
|
||||
s2s_out_closed(State, _Reason) ->
|
||||
State.
|
||||
@ -171,7 +172,7 @@ s2s_out_downgraded(#{db_enabled := true,
|
||||
s2s_out_downgraded(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}) ->
|
||||
%% Received dialback request, section 2.2.1, step 1
|
||||
try
|
||||
@ -186,7 +187,7 @@ s2s_in_packet(#{stream_id := StreamID} = State,
|
||||
{stop,
|
||||
send_db_result(State,
|
||||
#db_verify{from = From, to = To, type = error,
|
||||
sub_els = [mk_error(Reason)]})}
|
||||
sub_els = [mk_error(Reason, Lang)]})}
|
||||
end;
|
||||
s2s_in_packet(State, #db_verify{to = To, from = From, key = Key,
|
||||
id = StreamID, type = undefined}) ->
|
||||
@ -204,7 +205,7 @@ s2s_in_packet(State, Pkt) when is_record(Pkt, db_result);
|
||||
s2s_in_packet(State, _) ->
|
||||
State.
|
||||
|
||||
s2s_in_recv(State, El, {error, Why}) ->
|
||||
s2s_in_recv(#{lang := Lang} = State, El, {error, Why}) ->
|
||||
case xmpp:get_name(El) of
|
||||
Tag when Tag == <<"db:result">>;
|
||||
Tag == <<"db:verify">> ->
|
||||
@ -212,7 +213,7 @@ s2s_in_recv(State, El, {error, Why}) ->
|
||||
T when T /= <<"valid">>,
|
||||
T /= <<"invalid">>,
|
||||
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)};
|
||||
_ ->
|
||||
State
|
||||
@ -316,17 +317,17 @@ check_from_to(From, To) ->
|
||||
end
|
||||
end.
|
||||
|
||||
-spec mk_error(term()) -> stanza_error().
|
||||
mk_error(forbidden) ->
|
||||
xmpp:err_forbidden(<<"Access denied by service policy">>, ?MYLANG);
|
||||
mk_error(host_unknown) ->
|
||||
xmpp:err_not_allowed(<<"Host unknown">>, ?MYLANG);
|
||||
mk_error({codec_error, Why}) ->
|
||||
xmpp:err_bad_request(xmpp:io_format_error(Why), ?MYLANG);
|
||||
mk_error({_Class, _Reason} = Why) ->
|
||||
-spec mk_error(term(), binary()) -> stanza_error().
|
||||
mk_error(forbidden, Lang) ->
|
||||
xmpp:err_forbidden(<<"Access denied by service policy">>, Lang);
|
||||
mk_error(host_unknown, Lang) ->
|
||||
xmpp:err_not_allowed(<<"Host unknown">>, Lang);
|
||||
mk_error({codec_error, Why}, Lang) ->
|
||||
xmpp:err_bad_request(xmpp:io_format_error(Why), Lang);
|
||||
mk_error({_Class, _Reason} = Why, Lang) ->
|
||||
Txt = xmpp_stream_out:format_error(Why),
|
||||
xmpp:err_remote_server_not_found(Txt, ?MYLANG);
|
||||
mk_error(_) ->
|
||||
xmpp:err_remote_server_not_found(Txt, Lang);
|
||||
mk_error(_, _) ->
|
||||
xmpp:err_internal_server_error().
|
||||
|
||||
-spec format_error(db_result()) -> binary().
|
||||
|
@ -176,14 +176,24 @@ c2s_authenticated_packet(#{mgmt_state := MgmtState} = State, Pkt)
|
||||
c2s_authenticated_packet(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),
|
||||
IsStanza = xmpp:is_stanza(El),
|
||||
if Xmlns == ?NS_STREAM_MGMT_2; Xmlns == ?NS_STREAM_MGMT_3 ->
|
||||
Txt = xmpp:io_format_error(Why),
|
||||
Err = #sm_failed{reason = 'bad-request',
|
||||
text = xmpp:mk_text(Txt, Lang),
|
||||
xmlns = Xmlns},
|
||||
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 ->
|
||||
State
|
||||
end;
|
||||
@ -193,24 +203,24 @@ c2s_handle_recv(State, _, _) ->
|
||||
c2s_handle_send(#{mgmt_state := MgmtState, mod := Mod,
|
||||
lang := Lang} = State, Pkt, SendResult)
|
||||
when MgmtState == pending; MgmtState == active ->
|
||||
IsStanza = xmpp:is_stanza(Pkt),
|
||||
case Pkt of
|
||||
_ when ?is_stanza(Pkt) ->
|
||||
Meta = xmpp:get_meta(Pkt),
|
||||
case maps:get(mgmt_is_resent, Meta, false) of
|
||||
false ->
|
||||
case mgmt_queue_add(State, Pkt) of
|
||||
#{mgmt_max_queue := exceeded} = State1 ->
|
||||
State2 = State1#{mgmt_resend => false},
|
||||
_ when IsStanza ->
|
||||
case need_to_enqueue(State, Pkt) of
|
||||
{true, State1} ->
|
||||
case mgmt_queue_add(State1, Pkt) of
|
||||
#{mgmt_max_queue := exceeded} = State2 ->
|
||||
State3 = State2#{mgmt_resend => false},
|
||||
Err = xmpp:serr_policy_violation(
|
||||
<<"Too many unacked stanzas">>, Lang),
|
||||
send(State2, Err);
|
||||
State1 when SendResult == ok ->
|
||||
send_rack(State1);
|
||||
State1 ->
|
||||
State1
|
||||
send(State3, Err);
|
||||
State2 when SendResult == ok ->
|
||||
send_rack(State2);
|
||||
State2 ->
|
||||
State2
|
||||
end;
|
||||
true ->
|
||||
State
|
||||
{false, State1} ->
|
||||
State1
|
||||
end;
|
||||
#stream_error{} ->
|
||||
case MgmtState of
|
||||
@ -242,13 +252,13 @@ c2s_handle_info(#{mgmt_ack_timer := TRef, jid := JID, mod := Mod} = State,
|
||||
[jid:encode(JID)]),
|
||||
State1 = Mod:close(State),
|
||||
{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,
|
||||
{timeout, TRef, pending_timeout}) ->
|
||||
?DEBUG("Timed out waiting for resumption of stream for ~s",
|
||||
[jid:encode(JID)]),
|
||||
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,
|
||||
stop_reason => {stream, {out, Err}}});
|
||||
c2s_handle_info(#{jid := JID} = State, {_Ref, {resume, OldState}}) ->
|
||||
@ -269,8 +279,8 @@ c2s_closed(State, _Reason) ->
|
||||
State.
|
||||
|
||||
c2s_terminated(#{mgmt_state := resumed, jid := JID} = State, _Reason) ->
|
||||
?INFO_MSG("Closing former stream of resumed session for ~s",
|
||||
[jid:encode(JID)]),
|
||||
?DEBUG("Closing former stream of resumed session for ~s",
|
||||
[jid:encode(JID)]),
|
||||
bounce_message_queue(),
|
||||
{stop, State};
|
||||
c2s_terminated(#{mgmt_state := MgmtState, mgmt_stanzas_in := In, sid := SID,
|
||||
@ -361,15 +371,15 @@ handle_enable(#{mgmt_timeout := DefaultTimeout,
|
||||
DefaultTimeout
|
||||
end,
|
||||
Res = if Timeout > 0 ->
|
||||
?INFO_MSG("Stream management with resumption enabled for ~s",
|
||||
[jid:encode(JID)]),
|
||||
?DEBUG("Stream management with resumption enabled for ~s",
|
||||
[jid:encode(JID)]),
|
||||
#sm_enabled{xmlns = Xmlns,
|
||||
id = make_resume_id(State),
|
||||
resume = true,
|
||||
max = Timeout};
|
||||
true ->
|
||||
?INFO_MSG("Stream management without resumption enabled for ~s",
|
||||
[jid:encode(JID)]),
|
||||
?DEBUG("Stream management without resumption enabled for ~s",
|
||||
[jid:encode(JID)]),
|
||||
#sm_enabled{xmlns = Xmlns}
|
||||
end,
|
||||
State1 = State#{mgmt_state => active,
|
||||
@ -491,7 +501,7 @@ resend_rack(#{mgmt_ack_timer := _,
|
||||
resend_rack(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 := Queue} = State, Pkt) ->
|
||||
NewNum = case NumStanzasOut of
|
||||
@ -531,8 +541,13 @@ resend_unacked_stanzas(#{mgmt_state := MgmtState,
|
||||
[p1_queue:len(Queue), jid:encode(JID)]),
|
||||
p1_queue:foldl(
|
||||
fun({_, Time, Pkt}, AccState) ->
|
||||
NewPkt = add_resent_delay_info(AccState, Pkt, Time),
|
||||
send(AccState, xmpp:put_meta(NewPkt, mgmt_is_resent, true))
|
||||
Pkt1 = add_resent_delay_info(AccState, Pkt, Time),
|
||||
Pkt2 = if ?is_stanza(Pkt1) ->
|
||||
xmpp:put_meta(Pkt1, mgmt_is_resent, true);
|
||||
true ->
|
||||
Pkt1
|
||||
end,
|
||||
send(AccState, Pkt2)
|
||||
end, State, Queue);
|
||||
resend_unacked_stanzas(State) ->
|
||||
State.
|
||||
@ -646,6 +661,8 @@ inherit_session_state(#{user := U, server := S,
|
||||
{error, Msg}
|
||||
catch exit:{noproc, _} ->
|
||||
{error, <<"Previous session PID is dead">>};
|
||||
exit:{normal, _} ->
|
||||
{error, <<"Previous session PID has exited">>};
|
||||
exit:{timeout, _} ->
|
||||
{error, <<"Session state copying timed out">>}
|
||||
end
|
||||
@ -669,6 +686,7 @@ add_resent_delay_info(#{lserver := LServer}, El, Time)
|
||||
when is_record(El, message); is_record(El, presence) ->
|
||||
xmpp_util:add_delay_info(El, jid:make(LServer), Time, <<"Resent">>);
|
||||
add_resent_delay_info(_State, El, _Time) ->
|
||||
%% TODO
|
||||
El.
|
||||
|
||||
-spec send(state(), xmpp_element()) -> state().
|
||||
@ -711,6 +729,14 @@ bounce_message_queue() ->
|
||||
ok
|
||||
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
|
||||
%%%===================================================================
|
||||
|
@ -201,8 +201,10 @@ convert_data(Host, "offline", User, [Data]) ->
|
||||
fun({_, RawXML}) ->
|
||||
case deserialize(RawXML) of
|
||||
[El] ->
|
||||
Msg = el_to_offline_msg(LUser, LServer, El),
|
||||
ok = mod_offline:store_offline_msg(Msg);
|
||||
case el_to_offline_msg(LUser, LServer, El) of
|
||||
[Msg] -> ok = mod_offline:store_offline_msg(Msg);
|
||||
[] -> ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
|
@ -28,7 +28,7 @@
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-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).
|
||||
|
||||
@ -71,3 +71,21 @@ bytes(N) ->
|
||||
-spec round_robin(pos_integer()) -> non_neg_integer().
|
||||
round_robin(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),
|
||||
Socket1 = xmpp_socket:reset_stream(Socket),
|
||||
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
|
||||
true -> State1;
|
||||
false ->
|
||||
State2 = try Mod:handle_auth_success(User, Mech, AuthModule, State1)
|
||||
catch _:undef -> State1
|
||||
end,
|
||||
State2 = send_pkt(State1, #sasl_success{text = ServerOut}),
|
||||
case is_disconnected(State2) of
|
||||
true -> State2;
|
||||
false ->
|
||||
@ -867,16 +867,22 @@ process_sasl_continue(ServerOut, NewSASLState, State) ->
|
||||
process_sasl_failure(Err, User,
|
||||
#{mod := Mod, sasl_mech := Mech, lang := Lang} = State) ->
|
||||
{Reason, Text} = format_sasl_error(Mech, Err),
|
||||
State1 = send_pkt(State, #sasl_failure{reason = Reason,
|
||||
text = xmpp:mk_text(Text, Lang)}),
|
||||
State1 = try Mod:handle_auth_failure(User, Mech, Text, State)
|
||||
catch _:undef -> State
|
||||
end,
|
||||
case is_disconnected(State1) of
|
||||
true -> State1;
|
||||
false ->
|
||||
State2 = try Mod:handle_auth_failure(User, Mech, Text, State1)
|
||||
catch _:undef -> State1
|
||||
end,
|
||||
State3 = maps:remove(sasl_state, maps:remove(sasl_mech, State2)),
|
||||
State3#{stream_state => wait_for_sasl_request}
|
||||
State2 = send_pkt(State1,
|
||||
#sasl_failure{reason = Reason,
|
||||
text = xmpp:mk_text(Text, Lang)}),
|
||||
case is_disconnected(State2) of
|
||||
true -> State2;
|
||||
false ->
|
||||
State3 = maps:remove(sasl_state,
|
||||
maps:remove(sasl_mech, State2)),
|
||||
State3#{stream_state => wait_for_sasl_request}
|
||||
end
|
||||
end.
|
||||
|
||||
-spec process_sasl_abort(state()) -> state().
|
||||
|
@ -13,6 +13,10 @@ def read():
|
||||
write(True)
|
||||
elif cmd == 'isuser':
|
||||
u, s = pkt.split(':', 2)[1:]
|
||||
if u == "wrong":
|
||||
write(False)
|
||||
else:
|
||||
write(True)
|
||||
elif cmd == 'setpass':
|
||||
u, s, p = pkt.split(':', 3)[1:]
|
||||
write(True)
|
||||
|
@ -89,8 +89,14 @@ init_config(Config) ->
|
||||
ConfigPath = filename:join([CWD, "ejabberd.yml"]),
|
||||
ok = file:write_file(ConfigPath, CfgContent2),
|
||||
setup_ejabberd_lib_path(Config),
|
||||
ok = application:load(sasl),
|
||||
ok = application:load(mnesia),
|
||||
case application:load(sasl) of
|
||||
ok -> ok;
|
||||
{error, {already_loaded, _}} -> ok
|
||||
end,
|
||||
case application:load(mnesia) of
|
||||
ok -> ok;
|
||||
{error, {already_loaded, _}} -> ok
|
||||
end,
|
||||
case application:load(ejabberd) of
|
||||
ok -> ok;
|
||||
{error, {already_loaded, _}} -> ok
|
||||
|
Loading…
Reference in New Issue
Block a user