25
1
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:
Evgeniy Khramtsov 2018-03-03 21:09:27 +03:00
commit 63dba3fd64
39 changed files with 499 additions and 371 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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).

View File

@ -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), "", []).

View File

@ -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 ->

View File

@ -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]),

View File

@ -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(

View File

@ -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)).

View File

@ -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.

View File

@ -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, _} ->

View File

@ -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].

View File

@ -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]).

View File

@ -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].

View File

@ -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.

View File

@ -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]}.

View File

@ -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].

View File

@ -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 ->
[]

View File

@ -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),

View File

@ -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},

View File

@ -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)

View File

@ -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()}].

View File

@ -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;

View File

@ -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(

View File

@ -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;

View File

@ -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

View File

@ -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
%%----------------------------

View File

@ -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}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View File

@ -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().

View File

@ -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
%%%===================================================================

View File

@ -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

View File

@ -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.

View File

@ -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().

View File

@ -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)

View File

@ -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