diff --git a/sql/lite.new.sql b/sql/lite.new.sql index 8e9066eb9..ef22fe3d7 100644 --- a/sql/lite.new.sql +++ b/sql/lite.new.sql @@ -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, diff --git a/sql/lite.sql b/sql/lite.sql index e9d2ac6f2..2eecec78a 100644 --- a/sql/lite.sql +++ b/sql/lite.sql @@ -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, diff --git a/sql/mssql.sql b/sql/mssql.sql index 8a24bd5d5..2dbe473c0 100644 --- a/sql/mssql.sql +++ b/sql/mssql.sql @@ -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, diff --git a/sql/mysql.new.sql b/sql/mysql.new.sql index c155794ee..d9c4cb915 100644 --- a/sql/mysql.new.sql +++ b/sql/mysql.new.sql @@ -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, diff --git a/sql/mysql.sql b/sql/mysql.sql index 3e34bbb32..aea9e454e 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -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, diff --git a/sql/pg.new.sql b/sql/pg.new.sql index 3d613a37b..66d934236 100644 --- a/sql/pg.new.sql +++ b/sql/pg.new.sql @@ -61,15 +61,14 @@ -- ALTER TABLE spool ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE archive ADD COLUMN server_host text NOT NULL DEFAULT ''; --- 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 ''; @@ -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, diff --git a/sql/pg.sql b/sql/pg.sql index 48f4a85d4..16456fe0f 100644 --- a/sql/pg.sql +++ b/sql/pg.sql @@ -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, diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl index 68fa82ea5..08edf3020 100644 --- a/src/cyrsasl_digest.erl +++ b/src/cyrsasl_digest.erl @@ -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). diff --git a/src/cyrsasl_plain.erl b/src/cyrsasl_plain.erl index ec52b00a8..3bdcf8476 100644 --- a/src/cyrsasl_plain.erl +++ b/src/cyrsasl_plain.erl @@ -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), "", []). diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index df75be9eb..861d8f37b 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -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 -> diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 1e81f4d1a..4efdafef2 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -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]), diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index b66015bac..a15bfd157 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -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( diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 5ec2556f6..cf3d099cc 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -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)). diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index 05bdc4495..474304a5d 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -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. diff --git a/src/ejabberd_iq.erl b/src/ejabberd_iq.erl index 282ff7e15..2ac07e72a 100644 --- a/src/ejabberd_iq.erl +++ b/src/ejabberd_iq.erl @@ -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(<>), <<"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(<>), - 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, _} -> diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl index 5345727a2..a02834c78 100644 --- a/src/ejabberd_s2s_in.erl +++ b/src/ejabberd_s2s_in.erl @@ -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]. diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl index 9abc0d017..f82d017ea 100644 --- a/src/ejabberd_s2s_out.erl +++ b/src/ejabberd_s2s_out.erl @@ -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]). diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl index 7016cd77d..03b768bdf 100644 --- a/src/ejabberd_service.erl +++ b/src/ejabberd_service.erl @@ -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]. diff --git a/src/ejabberd_stun.erl b/src/ejabberd_stun.erl index 342f6302a..adae05f00 100644 --- a/src/ejabberd_stun.erl +++ b/src/ejabberd_stun.erl @@ -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. diff --git a/src/ejabberd_sup.erl b/src/ejabberd_sup.erl index e829ea61d..f692575c1 100644 --- a/src/ejabberd_sup.erl +++ b/src/ejabberd_sup.erl @@ -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]}. diff --git a/src/ejabberd_xmlrpc.erl b/src/ejabberd_xmlrpc.erl index 3e0e4fb0b..fb5cbd850 100644 --- a/src/ejabberd_xmlrpc.erl +++ b/src/ejabberd_xmlrpc.erl @@ -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]. diff --git a/src/gen_mod.erl b/src/gen_mod.erl index 5f8283df7..a477ec295 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -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 -> [] diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 8d530e5c8..1f3ec0397 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -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), diff --git a/src/mod_block_strangers.erl b/src/mod_block_strangers.erl index b60003c84..d6d5c50cb 100644 --- a/src/mod_block_strangers.erl +++ b/src/mod_block_strangers.erl @@ -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}, diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl index dc49ccb11..e3ac1e3e1 100644 --- a/src/mod_blocking.erl +++ b/src/mod_blocking.erl @@ -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) diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index de0c7fc85..5b7983ddf 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -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()}]. diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index 9f5894499..9a5952926 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -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; diff --git a/src/mod_last.erl b/src/mod_last.erl index e6232e67c..7e53fe5dc 100644 --- a/src/mod_last.erl +++ b/src/mod_last.erl @@ -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( diff --git a/src/mod_mam.erl b/src/mod_mam.erl index bc80d47ed..ac88c82cd 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -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; diff --git a/src/mod_muc.erl b/src/mod_muc.erl index acfbb90a0..debda4cab 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -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">>), diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 0bd47dcaa..6b6c7d8ca 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -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 %%---------------------------- diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 71dd09084..158e937c7 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -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}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/mod_s2s_dialback.erl b/src/mod_s2s_dialback.erl index 6f088b349..d5ba83b0c 100644 --- a/src/mod_s2s_dialback.erl +++ b/src/mod_s2s_dialback.erl @@ -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(). diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 48e7ac985..6ca5a8b39 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -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 %%%=================================================================== diff --git a/src/prosody2ejabberd.erl b/src/prosody2ejabberd.erl index 14d78702e..62cb32417 100644 --- a/src/prosody2ejabberd.erl +++ b/src/prosody2ejabberd.erl @@ -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 diff --git a/src/randoms.erl b/src/randoms.erl index a64012ca3..101241a3c 100644 --- a/src/randoms.erl +++ b/src/randoms.erl @@ -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. diff --git a/src/xmpp_stream_in.erl b/src/xmpp_stream_in.erl index 4f8be911e..55fa3a4bf 100644 --- a/src/xmpp_stream_in.erl +++ b/src/xmpp_stream_in.erl @@ -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(). diff --git a/test/ejabberd_SUITE_data/extauth.py b/test/ejabberd_SUITE_data/extauth.py index 263d6464e..b6a217fcc 100755 --- a/test/ejabberd_SUITE_data/extauth.py +++ b/test/ejabberd_SUITE_data/extauth.py @@ -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) diff --git a/test/suite.erl b/test/suite.erl index b91583d18..960cbdc90 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -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