From 9188a7b838d9bc73cdc791bff32d60691a083460 Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Mon, 12 Feb 2018 15:12:46 +0100 Subject: [PATCH 01/45] Dialyzer checks always failed because the return value of the function 'get_subscribed_rooms' in 'mod_muc_sql' is different to the defined value in 'mod_muc'. Fix was to update the return value in 'mod_muc.erl'. --- src/mod_muc.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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">>), From 4632f5520ff5cf011037bbcbf2ccc8097a49aef8 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Tue, 13 Feb 2018 11:38:41 +0300 Subject: [PATCH 02/45] Really run use_cache/1 and cache_nodes/1 callbacks for mod_mam --- src/mod_mam.erl | 69 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 21 deletions(-) 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; From 9dbdeba6c107e4f09ed0e83f330bcd7c0278e56f Mon Sep 17 00:00:00 2001 From: Christophe Romain Date: Tue, 13 Feb 2018 11:08:13 +0100 Subject: [PATCH 03/45] Add case on create_room, avoid useless call --- src/mod_muc_admin.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 0bd47dcaa..b1fd27d29 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -569,8 +569,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))), From 60a862392935fa6c9e6256a21ba7b70ebd1d6572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 13 Feb 2018 16:27:39 +0100 Subject: [PATCH 04/45] Change formatting of commands markdown documentation --- src/ejabberd_commands_doc.erl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) 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( From 516f4d03a1370b013a385d677f7b93334e0d11ec Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Wed, 14 Feb 2018 11:42:43 +0300 Subject: [PATCH 05/45] Fix indentation --- src/ejabberd_service.erl | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl index 7016cd77d..30ead4fcc 100644 --- a/src/ejabberd_service.erl +++ b/src/ejabberd_service.erl @@ -102,13 +102,13 @@ init([State, Opts]) -> end, 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}, + 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]). handle_stream_start(_StreamStart, @@ -140,7 +140,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 " @@ -158,10 +158,10 @@ handle_auth_success(_, Mech, _, "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]) + lists:foreach( + fun(H) -> + ejabberd_router:register_route(H, ?MYNAME), + ejabberd_hooks:run(component_connected, [H]) end, dict:fetch_keys(HostOpts)), State. @@ -180,11 +180,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 +193,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), @@ -210,11 +210,11 @@ terminate(Reason, #{stream_state := StreamState, host_opts := HostOpts}) -> established -> 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)); _ -> - ok + ok end. code_change(_OldVsn, State, _Extra) -> @@ -268,7 +268,7 @@ 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()); (atom()) -> [atom()]. From 032f79629288a30aa0288f77ec06d210de943d0c Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Wed, 14 Feb 2018 11:53:52 +0300 Subject: [PATCH 06/45] Introduce option 'global_routes' for ejabberd_service The option emulates legacy behaviour which registers all routes defined in `hosts` on a component connected. This behaviour is considered harmful in the case when it's desired to multiplex different components on the same port, so, to disable it, set `global_routes` to `false`. The default value is `true`, e.g. legacy behaviour is emulated: the only reason for this is to maintain backward compatibility with existing deployments. --- src/ejabberd_service.erl | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl index 30ead4fcc..8cfaa6314 100644 --- a/src/ejabberd_service.erl +++ b/src/ejabberd_service.erl @@ -100,6 +100,7 @@ init([State, Opts]) -> false -> [compression_none | TLSOpts1]; true -> TLSOpts1 end, + GlobalRoutes = proplists:get_value(global_routes, Opts, true), State1 = xmpp_stream_in:change_shaper(State, Shaper), State2 = State1#{access => Access, xmlns => ?NS_COMPONENT, @@ -108,6 +109,7 @@ init([State, Opts]) -> 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, State2}, [Opts]). @@ -153,16 +155,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))]), + 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, dict:fetch_keys(HostOpts)), + end, Routes), State. handle_auth_failure(_, Mech, Reason, @@ -299,6 +307,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; @@ -309,4 +319,4 @@ listen_opt_type(max_fsm_queue) -> 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]. From a65500b6aa6de8c57731a07f212abd7aef541646 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Wed, 14 Feb 2018 13:09:27 +0300 Subject: [PATCH 07/45] Fix external components unregistration --- src/ejabberd_service.erl | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl index 8cfaa6314..de4ab1fd2 100644 --- a/src/ejabberd_service.erl +++ b/src/ejabberd_service.erl @@ -213,17 +213,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_hooks:run(component_disconnected, [H, Reason]) - end, dict:fetch_keys(HostOpts)); + end, Routes); _ -> ok - end. + end; +terminate(_Reason, _State) -> + ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. From 51aa9d98a7930edb8597afabc599e767f94779f0 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Thu, 15 Feb 2018 10:18:06 +0300 Subject: [PATCH 08/45] Don't forget to add invalid XML responses to sending queue --- src/mod_stream_mgmt.erl | 47 +++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 48e7ac985..b6b7e8826 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -178,20 +178,36 @@ c2s_authenticated_packet(State, Pkt) -> c2s_handle_recv(#{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 -> + case xmpp:get_type(El) of + <<"result">> -> State; + <<"error">> -> State; + _ -> + Txt = xmpp:io_format_error(Why), + Lang1 = select_lang(Lang, xmpp:get_lang(El)), + Err = xmpp:err_bad_request(Txt, Lang1), + case mgmt_queue_add(State, Err) of + #{mgmt_max_queue := exceeded} = State1 -> + send_policy_violation(State1); + State1 -> + State1 + end + end; true -> State end; c2s_handle_recv(State, _, _) -> State. -c2s_handle_send(#{mgmt_state := MgmtState, mod := Mod, - lang := Lang} = State, Pkt, SendResult) +c2s_handle_send(#{mgmt_state := MgmtState, mod := Mod} = State, + Pkt, SendResult) when MgmtState == pending; MgmtState == active -> case Pkt of _ when ?is_stanza(Pkt) -> @@ -200,10 +216,7 @@ c2s_handle_send(#{mgmt_state := MgmtState, mod := Mod, false -> case mgmt_queue_add(State, Pkt) of #{mgmt_max_queue := exceeded} = State1 -> - State2 = State1#{mgmt_resend => false}, - Err = xmpp:serr_policy_violation( - <<"Too many unacked stanzas">>, Lang), - send(State2, Err); + send_policy_violation(State1); State1 when SendResult == ok -> send_rack(State1); State1 -> @@ -491,7 +504,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 +544,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. @@ -669,6 +687,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 +730,16 @@ bounce_message_queue() -> ok end. +-spec send_policy_violation(state()) -> state(). +send_policy_violation(#{lang := Lang} = State) -> + State1 = State#{mgmt_resend => false}, + Err = xmpp:serr_policy_violation(<<"Too many unacked stanzas">>, Lang), + send(State1, Err). + +-spec select_lang(binary(), binary()) -> binary(). +select_lang(Lang, <<"">>) -> Lang; +select_lang(_, Lang) -> Lang. + %%%=================================================================== %%% Configuration processing %%%=================================================================== From e5ba7c3f3ceed2c65fdc16bd5a1a001544f15b11 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Thu, 15 Feb 2018 10:48:59 +0300 Subject: [PATCH 09/45] Better solution for a previous fix --- src/mod_stream_mgmt.erl | 59 ++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index b6b7e8826..d295053f5 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -190,15 +190,7 @@ c2s_handle_recv(#{lang := Lang} = State, El, {error, Why}) -> <<"result">> -> State; <<"error">> -> State; _ -> - Txt = xmpp:io_format_error(Why), - Lang1 = select_lang(Lang, xmpp:get_lang(El)), - Err = xmpp:err_bad_request(Txt, Lang1), - case mgmt_queue_add(State, Err) of - #{mgmt_max_queue := exceeded} = State1 -> - send_policy_violation(State1); - State1 -> - State1 - end + State#{mgmt_is_resent => false} end; true -> State @@ -206,24 +198,27 @@ c2s_handle_recv(#{lang := Lang} = State, El, {error, Why}) -> c2s_handle_recv(State, _, _) -> State. -c2s_handle_send(#{mgmt_state := MgmtState, mod := Mod} = State, - Pkt, SendResult) +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 -> - send_policy_violation(State1); - State1 when SendResult == ok -> - send_rack(State1); - State1 -> - State1 + _ when IsStanza -> + case need_to_queue(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(State3, Err); + State2 when SendResult == ok -> + send_rack(State2); + State2 -> + State2 end; - true -> - State + {false, State1} -> + State1 end; #stream_error{} -> case MgmtState of @@ -730,15 +725,13 @@ bounce_message_queue() -> ok end. --spec send_policy_violation(state()) -> state(). -send_policy_violation(#{lang := Lang} = State) -> - State1 = State#{mgmt_resend => false}, - Err = xmpp:serr_policy_violation(<<"Too many unacked stanzas">>, Lang), - send(State1, Err). - --spec select_lang(binary(), binary()) -> binary(). -select_lang(Lang, <<"">>) -> Lang; -select_lang(_, Lang) -> Lang. +-spec need_to_queue(state(), xmlel() | stanza()) -> {boolean(), state()}. +need_to_queue(State, Pkt) when ?is_stanza(Pkt) -> + {not xmpp:get_meta(Pkt, mgmt_is_resent, false), State}; +need_to_queue(#{mgmt_is_resent := false} = State, #xmlel{}) -> + {true, maps:remove(mgmt_is_resent, State)}; +need_to_queue(State, _) -> + {false, State}. %%%=================================================================== %%% Configuration processing From 52ded14b7f17abbfbe7ea91982463f12e3a2262e Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Thu, 15 Feb 2018 15:42:55 +0300 Subject: [PATCH 10/45] Update incoming stanzas counter on invalid XML --- src/mod_stream_mgmt.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index d295053f5..549a15df6 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -176,7 +176,8 @@ 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 -> @@ -185,12 +186,13 @@ c2s_handle_recv(#{lang := Lang} = State, El, {error, Why}) -> text = xmpp:mk_text(Txt, Lang), xmlns = Xmlns}, send(State, Err); - IsStanza -> + IsStanza andalso (MgmtState == pending orelse MgmtState == active) -> + State1 = update_num_stanzas_in(State, El), case xmpp:get_type(El) of - <<"result">> -> State; - <<"error">> -> State; + <<"result">> -> State1; + <<"error">> -> State1; _ -> - State#{mgmt_is_resent => false} + State1#{mgmt_is_resent => false} end; true -> State From c102a45fac3748b1dc7c670056847fdb12cbff4d Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Thu, 15 Feb 2018 15:50:20 +0300 Subject: [PATCH 11/45] Rename some keys and functions for clarity --- src/mod_stream_mgmt.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 549a15df6..593368e71 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -192,7 +192,7 @@ c2s_handle_recv(#{mgmt_state := MgmtState, <<"result">> -> State1; <<"error">> -> State1; _ -> - State1#{mgmt_is_resent => false} + State1#{mgmt_force_enqueue => true} end; true -> State @@ -206,7 +206,7 @@ c2s_handle_send(#{mgmt_state := MgmtState, mod := Mod, IsStanza = xmpp:is_stanza(Pkt), case Pkt of _ when IsStanza -> - case need_to_queue(State, Pkt) of + case need_to_enqueue(State, Pkt) of {true, State1} -> case mgmt_queue_add(State1, Pkt) of #{mgmt_max_queue := exceeded} = State2 -> @@ -727,12 +727,12 @@ bounce_message_queue() -> ok end. --spec need_to_queue(state(), xmlel() | stanza()) -> {boolean(), state()}. -need_to_queue(State, Pkt) when ?is_stanza(Pkt) -> +-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_queue(#{mgmt_is_resent := false} = State, #xmlel{}) -> +need_to_enqueue(#{mgmt_force_enqueue := true} = State, #xmlel{}) -> {true, maps:remove(mgmt_is_resent, State)}; -need_to_queue(State, _) -> +need_to_enqueue(State, _) -> {false, State}. %%%=================================================================== From 32e5a3255dbd0f2a6e0fdb011311e4466caf641a Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Fri, 16 Feb 2018 08:28:33 +0300 Subject: [PATCH 12/45] Export aux functions from mod_muc_room --- src/mod_muc_room.erl | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 646d0fdd7..417bd4a15 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, @@ -3568,6 +3570,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}, From f0ccdebf7f3383aade2e4f726290aac6b5fe9edb Mon Sep 17 00:00:00 2001 From: Christophe Romain Date: Fri, 16 Feb 2018 09:52:29 +0100 Subject: [PATCH 13/45] Export helper functions --- src/mod_admin_extra.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 8d530e5c8..b5ed39540 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, From 7e1df0752a286a6a947c99b9b9cc791df69057d6 Mon Sep 17 00:00:00 2001 From: Christophe Romain Date: Fri, 16 Feb 2018 09:53:38 +0100 Subject: [PATCH 14/45] Export helper function --- src/ejabberd_config.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 5ec2556f6..beb44ddc0 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]). -export([start/2]). From 71a856deaafa5dcb706c70b414f9a2219cdec855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 16 Feb 2018 16:49:39 +0100 Subject: [PATCH 15/45] Handle gracefully that mnesia is already loaded when starting tests --- test/suite.erl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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 From f5d208441d97322c3c5e25047b425d2f83565655 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Fri, 16 Feb 2018 20:34:09 +0300 Subject: [PATCH 16/45] Improve example extauth script --- test/ejabberd_SUITE_data/extauth.py | 4 ++++ 1 file changed, 4 insertions(+) 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) From cffdb06b66233d58f2bee763cc14e4eaba3b6454 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Fri, 16 Feb 2018 20:50:22 +0300 Subject: [PATCH 17/45] Cache 'isuser' queries to external auth program --- src/ejabberd_auth.erl | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index df75be9eb..03eee9de4 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:update( + ?AUTH_CACHE, {User, Server}, {ok, exists}, + fun() -> + case Mod:user_exists(User, Server) of + true -> {ok, exists}; + false -> error; + {error, _} = Err -> Err + end + end, cache_nodes(Mod, Server)) of + {ok, _} -> + true; + error -> + false + end; _ -> false end From 5704a980c5b000fdd094fa9adaf1e18c9cfb9ce6 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Sat, 17 Feb 2018 18:53:35 +0300 Subject: [PATCH 18/45] Introduce 'access' option for mod_block_stranger The option is supposed to be used when `allow_local_users` and `allow_transports` are not enough. It's an ACL where `deny` means the message will be rejected (or a CAPTCHA would be generated for a presence), and `allow` means the sender is whitelisted and the stanza will pass through. The default value is `none`, which means nothing is whitelisted. --- src/mod_block_strangers.erl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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}, From d5afc767e662b12d98e4c57c8d037f1c75c03a4f Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Sat, 17 Feb 2018 20:06:50 +0300 Subject: [PATCH 19/45] Fix 'badmatch' crash The crash was introduced in 4b012a99d2bdd6d22f05676e9a7989409e314fca --- src/mod_last.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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( From ff06bdf144c089c5ff58207877027d96eb45b515 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Sun, 18 Feb 2018 09:02:23 +0300 Subject: [PATCH 20/45] Don't ask other nodes to invalidate cache when the key is not updated --- src/ejabberd_auth.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 03eee9de4..861d8f37b 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -547,15 +547,15 @@ db_user_exists(User, Server, Mod) -> error -> case Mod:store_type(Server) of external -> - case ets_cache:update( - ?AUTH_CACHE, {User, Server}, {ok, exists}, + 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, cache_nodes(Mod, Server)) of + end) of {ok, _} -> true; error -> @@ -584,7 +584,7 @@ db_check_password(User, AuthzId, Server, ProvidedPassword, false -> error end - end, cache_nodes(Mod, Server)) of + end) of {ok, _} -> true; error -> From 25abf8b63427370c278872de2bd00e74e04e36e7 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Sun, 18 Feb 2018 11:54:40 +0300 Subject: [PATCH 21/45] Don't inject node name inside "id" attribute Fixes #2284 --- src/ejabberd_iq.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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, _} -> From de49e7631f8ef0e72c46f75efa7ee0b2eabb4289 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Sun, 18 Feb 2018 17:58:51 +0300 Subject: [PATCH 22/45] Push blocking related IQs from bare JID Fixes #2287 --- src/mod_blocking.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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) From e070e6bccb6e59448075dee02a8b5f7ff6bcd2bb Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 19 Feb 2018 21:47:20 +0300 Subject: [PATCH 23/45] Replace ?MYLANG with connection's language wherever possible --- src/mod_s2s_dialback.erl | 31 ++++++++++++++++--------------- src/mod_stream_mgmt.erl | 4 ++-- 2 files changed, 18 insertions(+), 17 deletions(-) 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 593368e71..72589f99a 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -252,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}}) -> From 06c480106f3a3806be7c300c0344e276812c520e Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Mon, 19 Feb 2018 22:07:09 +0300 Subject: [PATCH 24/45] Don't emit validator's warning if the module is not found --- src/gen_mod.erl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) 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 -> [] From a875195940303324a466d30965783cc86a01d916 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 20 Feb 2018 00:44:47 +0100 Subject: [PATCH 25/45] mod_admin_extra: Fix srg_get_info with '@all@' Don't let the srg_get_info command crash if the roster group has '@all@' or the '@online@' users as members. --- src/mod_admin_extra.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index b5ed39540..1f3ec0397 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -1507,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), From d625e24029221cea30ef7078010f16da526ce257 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Tue, 20 Feb 2018 11:38:00 +0300 Subject: [PATCH 26/45] Introduce 'negotiation_timeout' The option can be used to specify a period (in seconds) for a stream negotiation to complete. If the timer fires, the stream is considered as failed and the underlying connection gets closed. This is a global option (you cannot set it per domain) and the default is 30 seconds. --- src/ejabberd_c2s.erl | 4 +++- src/ejabberd_config.erl | 8 +++++++- src/ejabberd_s2s_in.erl | 4 +++- src/ejabberd_s2s_out.erl | 4 +++- src/ejabberd_service.erl | 6 ++++-- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 1e81f4d1a..a523083c8 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 diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index beb44ddc0..09d433ef6 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, get_plain_terms_file/2]). + 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) -> @@ -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_s2s_in.erl b/src/ejabberd_s2s_in.erl index 5345727a2..31a936c8c 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]). 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 de4ab1fd2..816d643eb 100644 --- a/src/ejabberd_service.erl +++ b/src/ejabberd_service.erl @@ -101,8 +101,10 @@ init([State, Opts]) -> 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, + State2 = xmpp_stream_in:set_timeout(State1, Timeout), + State3 = State2#{access => Access, xmlns => ?NS_COMPONENT, lang => ?MYLANG, server => ?MYNAME, @@ -111,7 +113,7 @@ init([State, Opts]) -> tls_options => TLSOpts, global_routes => GlobalRoutes, check_from => CheckFrom}, - ejabberd_hooks:run_fold(component_init, {ok, State2}, [Opts]). + ejabberd_hooks:run_fold(component_init, {ok, State3}, [Opts]). handle_stream_start(_StreamStart, #{remote_server := RemoteServer, From 4bf4193d5590707fdfe14f9bec6952719ce00556 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Tue, 20 Feb 2018 19:47:50 +0300 Subject: [PATCH 27/45] Add 'negotiation_timeout' to the known options list --- src/ejabberd_config.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 09d433ef6..cf3d099cc 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -1427,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) -> From 0acc69e303d3fcb7558ec2759aba05d3d1347e3f Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Wed, 21 Feb 2018 10:25:15 +0300 Subject: [PATCH 28/45] Use nicks in disco#items or disco#info report --- src/mod_muc_room.erl | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 417bd4a15..87ae28797 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -508,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, @@ -3673,7 +3673,7 @@ process_iq_disco_info(_From, #iq{type = get, lang = Lang}, StateData) -> -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 /= <<"">> -> @@ -3906,20 +3906,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}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From ec0f0f7c72059d73d89132bf1e91fad0ef37f1fa Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Wed, 21 Feb 2018 17:12:50 +0300 Subject: [PATCH 29/45] Move some log messages to debug level --- src/mod_carboncopy.erl | 4 ++-- src/mod_stream_mgmt.erl | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index de0c7fc85..dd6a1d646 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 diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 72589f99a..76a17f049 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -279,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, @@ -371,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, From 7a1ed065fe61266d720a265f0ba26944225227b9 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Thu, 22 Feb 2018 00:40:09 +0100 Subject: [PATCH 30/45] mod_carboncopy: Copy outgoing MUC PMs Incoming MUC PMs aren't carbon-copied, as the MUC service usually forks them. However, don't suppress copying of outgoing PMs, where no such forking takes place. --- src/mod_carboncopy.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index dd6a1d646..8794e4087 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -164,7 +164,7 @@ 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 + not is_received_muc_pm(To, Packet, Direction) andalso xmpp:has_subtag(Packet, #carbons_private{}) == false andalso xmpp:has_subtag(Packet, #hint{type = 'no-copy'}) == false of true -> @@ -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()}]. From ea87bdfbe57027b2160bb4ff9d5500ae54126123 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Thu, 22 Feb 2018 00:46:47 +0100 Subject: [PATCH 31/45] mod_carboncopy: Apply cosmetic change The xmpp:has_subtag/2 function returns a boolen() value, so it can be used with the 'not' operator. --- src/mod_carboncopy.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index 8794e4087..5b7983ddf 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -165,8 +165,8 @@ user_receive_packet({Packet, #{jid := JID} = C2SState}) -> check_and_forward(JID, To, Packet, Direction)-> case is_chat_message(Packet) andalso not is_received_muc_pm(To, Packet, Direction) andalso - xmpp:has_subtag(Packet, #carbons_private{}) == false andalso - xmpp:has_subtag(Packet, #hint{type = 'no-copy'}) == false of + 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 -> From c1e5ae53086b990e14d2ed80c2539df5aace0e7a Mon Sep 17 00:00:00 2001 From: Marc Schink Date: Fri, 23 Feb 2018 18:23:54 +0100 Subject: [PATCH 32/45] Move make_rand_string() to 'randoms' module --- src/mod_http_upload.erl | 23 +---------------------- src/randoms.erl | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 23 deletions(-) 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/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. From f7566bd00e687df60fd470b9fdfc5ce6b04f0c7c Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 24 Feb 2018 00:50:20 +0100 Subject: [PATCH 33/45] sql/*: Add username to peer indexes The username is available for all MAM queries in question, and adding it to the indexes can improve the lookup performance significantly. --- sql/lite.new.sql | 4 ++-- sql/lite.sql | 4 ++-- sql/mssql.sql | 12 ++++++------ sql/mysql.new.sql | 6 +++--- sql/mysql.sql | 6 +++--- sql/pg.new.sql | 13 ++++++------- sql/pg.sql | 4 ++-- 7 files changed, 24 insertions(+), 25 deletions(-) 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..9dafe5bb2 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, timestamp); +CREATE INDEX i_archive_sh_username_peer USING BTREE ON archive(server_host(191), username, peer); +CREATE INDEX i_archive_sh_username_bare_peer USING BTREE ON archive(server_host(191), username, bare_peer); 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..64d26fa87 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, timestamp); +CREATE INDEX i_username_peer USING BTREE ON archive(username, peer); +CREATE INDEX i_username_bare_peer USING BTREE ON archive(username, bare_peer); 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, From 22e43ebd8a82c7ffe33bbc52cb619a5e4c7def92 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 24 Feb 2018 21:50:54 +0100 Subject: [PATCH 34/45] mod_stream_mgmt: Cope with exit during resumption Don't crash if the old process exits while it is queried for the session state. --- src/mod_stream_mgmt.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 76a17f049..6ca5a8b39 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -661,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 From c2235860abcbf1711addc94a1fbc980cea8afdd6 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Mon, 26 Feb 2018 00:06:35 +0100 Subject: [PATCH 35/45] xmpp_stream_in: Run auth result callbacks earlier Call Mod:handle_auth_success/4 and Mod:handle_auth_failure/4 before sending the SASL response rather than afterwards. This way, callbacks can send a custom response and disconnect. --- src/xmpp_stream_in.erl | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) 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(). From b2095ebcfe82cdbd735909f9d472173eba4836e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 26 Feb 2018 09:36:56 +0100 Subject: [PATCH 36/45] Simplify code for splitting auth string in cyrsasl This may fix problem from issue #2296 --- src/cyrsasl_plain.erl | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) 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), "", []). From 5912c573ea20eef4768adc572c358cef3e9ebc03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 26 Feb 2018 09:43:04 +0100 Subject: [PATCH 37/45] Use length on fields in mysql indexes --- sql/mysql.new.sql | 6 +++--- sql/mysql.sql | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sql/mysql.new.sql b/sql/mysql.new.sql index 9dafe5bb2..d9c4cb915 100644 --- a/sql/mysql.new.sql +++ b/sql/mysql.new.sql @@ -113,9 +113,9 @@ 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_peer USING BTREE ON archive(server_host(191), username, peer); -CREATE INDEX i_archive_sh_username_bare_peer USING BTREE ON archive(server_host(191), username, bare_peer); +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 TABLE archive_prefs ( diff --git a/sql/mysql.sql b/sql/mysql.sql index 64d26fa87..aea9e454e 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -102,9 +102,9 @@ 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_peer USING BTREE ON archive(username, peer); -CREATE INDEX i_username_bare_peer USING BTREE ON archive(username, bare_peer); +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 TABLE archive_prefs ( From 5054a9933f29d6cc8a88486c624987b1b9f05b17 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 26 Feb 2018 17:22:04 +0100 Subject: [PATCH 38/45] Support for default values in default_room_opts --- src/mod_muc_room.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 87ae28797..35ea746fd 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -3378,6 +3378,8 @@ remove_nonmembers(StateData) -> -spec set_opts([{atom(), any()}], state()) -> state(). set_opts([], StateData) -> StateData; +set_opts([{Opt, Val}, {Opt, _DefaultVal} | Opts], StateData) -> + set_opts([{Opt, Val} | Opts], StateData); set_opts([{Opt, Val} | Opts], StateData) -> NSD = case Opt of title -> From 8a41cfc0f5fb9cab62478dd324b073c480c0a5b8 Mon Sep 17 00:00:00 2001 From: Yusro Tsaqova Date: Sun, 25 Feb 2018 16:28:14 +0700 Subject: [PATCH 39/45] add ejabberd_command to get affiliation of a user in MUC room --- src/mod_muc_admin.erl | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index b1fd27d29..92a68d539 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, {tuple, [{affiliation, atom}]}}} + ]. %%% @@ -1034,6 +1043,26 @@ 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), + Affiliation = mod_muc_room:get_affiliation(UserJID, StateData), + {Affiliation}; + error -> + throw({error, "The room does not exist."}) + end. + %%---------------------------- %% Change Room Affiliation %%---------------------------- From da81590fef1383bc90d3f9ccae4b9e906133637d Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Wed, 28 Feb 2018 16:14:35 +0000 Subject: [PATCH 40/45] Validate additional listen opts The options "inet", "inet6" and "backlog" are valid listen options, but are currently logged as errors (even though they do work): 2018-02-28 16:08:44.141 [error] <0.338.0>@ejabberd_listener:validate_module_option:630 unknown listen option 'backlog' for 'ejabberd_c2s' will be likely ignored, available options are: access, shaper, certfile, ciphers, dhfile, cafile, client_cafile, protocol_options, tls, tls_compression, starttls, starttls_required, tls_verify, zlib, max_fsm_queue This adds the necessary validators so they are correctly recognized. --- src/ejabberd_c2s.erl | 10 +++++++++- src/ejabberd_http.erl | 4 ++++ src/ejabberd_s2s_in.erl | 9 ++++++++- src/ejabberd_service.erl | 9 ++++++++- src/ejabberd_stun.erl | 4 +++- src/ejabberd_xmlrpc.erl | 6 +++++- 6 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index a523083c8..4efdafef2 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -999,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; @@ -1031,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_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_s2s_in.erl b/src/ejabberd_s2s_in.erl index 31a936c8c..a02834c78 100644 --- a/src/ejabberd_s2s_in.erl +++ b/src/ejabberd_s2s_in.erl @@ -358,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) -> @@ -381,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_service.erl b/src/ejabberd_service.erl index 816d643eb..03b768bdf 100644 --- a/src/ejabberd_service.erl +++ b/src/ejabberd_service.erl @@ -291,6 +291,9 @@ transform_listen_option(Opt, Opts) -> [{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; @@ -328,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, global_routes]. + 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_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]. From 76f827ac838012800eae54cb7fd958f912a1af14 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Wed, 28 Feb 2018 21:43:43 +0300 Subject: [PATCH 41/45] Increase log level for DIGEST-MD5 FQDN SASL DIGEST-MD5 anyway deprecated anyway, so not point in logging this. This is now logged in `debug` mode. --- src/cyrsasl_digest.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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). From ad0fd1eac1745199067d03171dee315b77858a2b Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 1 Mar 2018 19:45:16 +0100 Subject: [PATCH 42/45] Simplify result of get_room_affiliation command (#2301) --- src/mod_muc_admin.erl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 92a68d539..6b6c7d8ca 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -320,9 +320,9 @@ get_commands_spec() -> 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}, + result_example = member, args = [{name, binary}, {service, binary}, {jid, binary}], - result = {affiliation, {tuple, [{affiliation, atom}]}}} + result = {affiliation, atom}} ]. @@ -1057,8 +1057,7 @@ get_room_affiliation(Name, Service, JID) -> %% 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), - Affiliation = mod_muc_room:get_affiliation(UserJID, StateData), - {Affiliation}; + mod_muc_room:get_affiliation(UserJID, StateData); error -> throw({error, "The room does not exist."}) end. From dbf1cabdcd17cd232c70e3ca8a5d18b0de122bbb Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 2 Mar 2018 11:43:50 +0100 Subject: [PATCH 43/45] Fix: mod_offline:store_offline_msg/1 expects a message, not list (#2312) --- src/prosody2ejabberd.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 From 0d3637d18fe83ddd7c09cae895a3c7660a44f0f1 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Sat, 3 Mar 2018 18:05:12 +0300 Subject: [PATCH 44/45] Simplify ejabberd_sup code --- src/ejabberd_sup.erl | 192 +++++++++++-------------------------------- 1 file changed, 50 insertions(+), 142 deletions(-) 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]}. From da1a5036febd4200c39dca33f5673097a7d90172 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Sat, 3 Mar 2018 21:08:05 +0300 Subject: [PATCH 45/45] Revert "Support for default values in default_room_opts" This reverts commit 5054a9933f29d6cc8a88486c624987b1b9f05b17. The commit is plain wrong: similar options are not required to be in pair. --- src/mod_muc_room.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 35ea746fd..87ae28797 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -3378,8 +3378,6 @@ remove_nonmembers(StateData) -> -spec set_opts([{atom(), any()}], state()) -> state(). set_opts([], StateData) -> StateData; -set_opts([{Opt, Val}, {Opt, _DefaultVal} | Opts], StateData) -> - set_opts([{Opt, Val} | Opts], StateData); set_opts([{Opt, Val} | Opts], StateData) -> NSD = case Opt of title ->