From 0e93f70e386d1513c1809f38352053ee289cce14 Mon Sep 17 00:00:00 2001 From: Robert Schuh Date: Thu, 8 Apr 2021 16:33:54 +0200 Subject: allow shared roster group placeholder in mqtt topic --- src/acl.erl | 1 + src/mod_mqtt.erl | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) (limited to 'src') diff --git a/src/acl.erl b/src/acl.erl index 64a88269b..5db79eba9 100644 --- a/src/acl.erl +++ b/src/acl.erl @@ -25,6 +25,7 @@ -export([match_rules/4, match_acls/3]). -export([access_rules_validator/0, access_validator/0]). -export([validator/1, validators/0]). +-export([loaded_shared_roster_module/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). diff --git a/src/mod_mqtt.erl b/src/mod_mqtt.erl index 24d033892..8734c778d 100644 --- a/src/mod_mqtt.erl +++ b/src/mod_mqtt.erl @@ -600,6 +600,23 @@ match([H|T1], [<<"%c">>|T2], U, S, R) -> R -> match(T1, T2, U, S, R); _ -> false end; +match([H|T1], [<<"%g">>|T2], U, S, R) -> + case jid:resourceprep(H) of + H -> + case acl:loaded_shared_roster_module(S) of + undefined -> false; + Mod -> + case Mod:get_group_opts(S, H) of + error -> false; + _ -> + case Mod:is_user_in_group({U, S}, H, S) of + true -> match(T1, T2, U, S, R); + _ -> false + end + end + end; + _ -> false + end; match([H|T1], [H|T2], U, S, R) -> match(T1, T2, U, S, R); match([], [], _, _, _) -> -- cgit v1.2.3 From b5bafca6408d72bc2d6c52dd7f6f0dcbc86fa5ec Mon Sep 17 00:00:00 2001 From: Pouriya Jahanbakhsh Date: Sat, 12 Jun 2021 20:27:30 +0430 Subject: ref: fix WS typos --- src/ejabberd_http_ws.erl | 16 ++++++------ src/ejabberd_websocket.erl | 64 +++++++++++++++++++++++----------------------- 2 files changed, 40 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/ejabberd_http_ws.erl b/src/ejabberd_http_ws.erl index 911a69a58..eb2100a42 100644 --- a/src/ejabberd_http_ws.erl +++ b/src/ejabberd_http_ws.erl @@ -51,7 +51,7 @@ active = false :: boolean(), c2s_pid :: pid(), ws :: {#ws{}, pid()}, - rfc_compilant = undefined :: boolean() | undefined}). + rfc_compliant = undefined :: boolean() | undefined}). %-define(DBGFSM, true). @@ -166,7 +166,7 @@ handle_event({new_shaper, Shaper}, StateName, #state{ws = {_, WsPid}} = StateDat {next_state, StateName, StateData}. handle_sync_event({send_xml, Packet}, _From, StateName, - #state{ws = {_, WsPid}, rfc_compilant = R} = StateData) -> + #state{ws = {_, WsPid}, rfc_compliant = R} = StateData) -> Packet2 = case {case R of undefined -> true; V -> V end, Packet} of {true, {xmlstreamstart, _, Attrs}} -> Attrs2 = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>} | @@ -215,7 +215,7 @@ handle_sync_event({send_xml, Packet}, _From, StateName, StateName end, {reply, ok, SN2, StateData}; -handle_sync_event(close, _From, StateName, #state{ws = {_, WsPid}, rfc_compilant = true} = StateData) +handle_sync_event(close, _From, StateName, #state{ws = {_, WsPid}, rfc_compliant = true} = StateData) when StateName /= stream_end_sent -> Close = #xmlel{name = <<"close">>, attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>}]}, @@ -313,7 +313,7 @@ get_human_html_xmlel() -> "client that supports it.">>}]}]}]}. -parse(#state{rfc_compilant = C} = State, Data) -> +parse(#state{rfc_compliant = C} = State, Data) -> case C of undefined -> P = fxml_stream:new(self()), @@ -321,13 +321,13 @@ parse(#state{rfc_compilant = C} = State, Data) -> fxml_stream:close(P2), case parsed_items([]) of error -> - {State#state{rfc_compilant = true}, <<"parse error">>}; + {State#state{rfc_compliant = true}, <<"parse error">>}; [] -> - {State#state{rfc_compilant = true}, <<"parse error">>}; + {State#state{rfc_compliant = true}, <<"parse error">>}; [{xmlstreamstart, <<"open">>, _} | _] -> - parse(State#state{rfc_compilant = true}, Data); + parse(State#state{rfc_compliant = true}, Data); _ -> - parse(State#state{rfc_compilant = false}, Data) + parse(State#state{rfc_compliant = false}, Data) end; true -> El = fxml_stream:parse_element(Data), diff --git a/src/ejabberd_websocket.erl b/src/ejabberd_websocket.erl index 5eb5ab1a2..5f9d284a6 100644 --- a/src/ejabberd_websocket.erl +++ b/src/ejabberd_websocket.erl @@ -187,32 +187,32 @@ find_subprotocol(Headers) -> end. -ws_loop(FrameInfo, Socket, WsHandleLoopPid, SocketMode, Shaper) -> +ws_loop(FrameInfo, Socket, WsHandleLoopPid, SockMod, Shaper) -> receive {DataType, _Socket, Data} when DataType =:= tcp orelse DataType =:= raw -> - case handle_data(DataType, FrameInfo, Data, Socket, WsHandleLoopPid, SocketMode, Shaper) of + case handle_data(DataType, FrameInfo, Data, Socket, WsHandleLoopPid, SockMod, Shaper) of {error, Error} -> ?DEBUG("TLS decode error ~p", [Error]), - websocket_close(Socket, WsHandleLoopPid, SocketMode, 1002); % protocol error + websocket_close(Socket, WsHandleLoopPid, SockMod, 1002); % protocol error {NewFrameInfo, ToSend, NewShaper} -> - lists:foreach(fun(Pkt) -> SocketMode:send(Socket, Pkt) + lists:foreach(fun(Pkt) -> SockMod:send(Socket, Pkt) end, ToSend), - ws_loop(NewFrameInfo, Socket, WsHandleLoopPid, SocketMode, NewShaper) + ws_loop(NewFrameInfo, Socket, WsHandleLoopPid, SockMod, NewShaper) end; {new_shaper, NewShaper} -> NewShaper = case NewShaper of none when Shaper /= none -> - activate(Socket, SocketMode, true), none; + activate(Socket, SockMod, true), none; _ -> NewShaper end, - ws_loop(FrameInfo, Socket, WsHandleLoopPid, SocketMode, NewShaper); + ws_loop(FrameInfo, Socket, WsHandleLoopPid, SockMod, NewShaper); {tcp_closed, _Socket} -> ?DEBUG("TCP connection was closed, exit", []), - websocket_close(Socket, WsHandleLoopPid, SocketMode, 0); + websocket_close(Socket, WsHandleLoopPid, SockMod, 0); {tcp_error, Socket, Reason} -> ?DEBUG("TCP connection error: ~ts", [inet:format_error(Reason)]), - websocket_close(Socket, WsHandleLoopPid, SocketMode, 0); + websocket_close(Socket, WsHandleLoopPid, SockMod, 0); {'DOWN', Ref, process, WsHandleLoopPid, Reason} -> Code = case Reason of normal -> @@ -224,39 +224,39 @@ ws_loop(FrameInfo, Socket, WsHandleLoopPid, SocketMode, Shaper) -> 1011 % internal error end, erlang:demonitor(Ref), - websocket_close(Socket, WsHandleLoopPid, SocketMode, Code); + websocket_close(Socket, WsHandleLoopPid, SockMod, Code); {text_with_reply, Data, Sender} -> - SocketMode:send(Socket, encode_frame(Data, 1)), + SockMod:send(Socket, encode_frame(Data, 1)), Sender ! {text_reply, self()}, ws_loop(FrameInfo, Socket, WsHandleLoopPid, - SocketMode, Shaper); + SockMod, Shaper); {data_with_reply, Data, Sender} -> - SocketMode:send(Socket, encode_frame(Data, 2)), + SockMod:send(Socket, encode_frame(Data, 2)), Sender ! {data_reply, self()}, ws_loop(FrameInfo, Socket, WsHandleLoopPid, - SocketMode, Shaper); + SockMod, Shaper); {text, Data} -> - SocketMode:send(Socket, encode_frame(Data, 1)), + SockMod:send(Socket, encode_frame(Data, 1)), ws_loop(FrameInfo, Socket, WsHandleLoopPid, - SocketMode, Shaper); + SockMod, Shaper); {data, Data} -> - SocketMode:send(Socket, encode_frame(Data, 2)), + SockMod:send(Socket, encode_frame(Data, 2)), ws_loop(FrameInfo, Socket, WsHandleLoopPid, - SocketMode, Shaper); + SockMod, Shaper); {ping, Data} -> - SocketMode:send(Socket, encode_frame(Data, 9)), + SockMod:send(Socket, encode_frame(Data, 9)), ws_loop(FrameInfo, Socket, WsHandleLoopPid, - SocketMode, Shaper); + SockMod, Shaper); shutdown -> ?DEBUG("Shutdown request received, closing websocket " "with pid ~p", [self()]), - websocket_close(Socket, WsHandleLoopPid, SocketMode, 1001); % going away + websocket_close(Socket, WsHandleLoopPid, SockMod, 1001); % going away _Ignored -> ?WARNING_MSG("Received unexpected message, ignoring: ~p", [_Ignored]), ws_loop(FrameInfo, Socket, WsHandleLoopPid, - SocketMode, Shaper) + SockMod, Shaper) end. encode_frame(Data, Opcode) -> @@ -421,7 +421,7 @@ handle_data(tcp, FrameInfo, Data, Socket, WsHandleLoopPid, fast_tls, Shaper) -> handle_data(_, FrameInfo, Data, Socket, WsHandleLoopPid, SockMod, Shaper) -> handle_data_int(FrameInfo, Data, Socket, WsHandleLoopPid, SockMod, Shaper). -handle_data_int(FrameInfo, Data, Socket, WsHandleLoopPid, SocketMode, Shaper) -> +handle_data_int(FrameInfo, Data, Socket, WsHandleLoopPid, SockMod, Shaper) -> {NewFrameInfo, Recv, Send} = process_frame(FrameInfo, Data), lists:foreach(fun (El) -> case El of @@ -434,27 +434,27 @@ handle_data_int(FrameInfo, Data, Socket, WsHandleLoopPid, SocketMode, Shaper) -> end end, Recv), - {NewFrameInfo, Send, handle_shaping(Data, Socket, SocketMode, Shaper)}. + {NewFrameInfo, Send, handle_shaping(Data, Socket, SockMod, Shaper)}. websocket_close(Socket, WsHandleLoopPid, - SocketMode, CloseCode) when CloseCode > 0 -> + SockMod, CloseCode) when CloseCode > 0 -> Frame = encode_frame(<>, 8), - SocketMode:send(Socket, Frame), - websocket_close(Socket, WsHandleLoopPid, SocketMode, 0); -websocket_close(Socket, WsHandleLoopPid, SocketMode, _CloseCode) -> + SockMod:send(Socket, Frame), + websocket_close(Socket, WsHandleLoopPid, SockMod, 0); +websocket_close(Socket, WsHandleLoopPid, SockMod, _CloseCode) -> WsHandleLoopPid ! closed, - SocketMode:close(Socket). + SockMod:close(Socket). get_origin() -> ejabberd_option:websocket_origin(). -handle_shaping(_Data, _Socket, _SocketMode, none) -> +handle_shaping(_Data, _Socket, _SockMod, none) -> none; -handle_shaping(Data, Socket, SocketMode, Shaper) -> +handle_shaping(Data, Socket, SockMod, Shaper) -> {NewShaper, Pause} = ejabberd_shaper:update(Shaper, byte_size(Data)), if Pause > 0 -> activate_after(Socket, self(), Pause); - true -> activate(Socket, SocketMode, once) + true -> activate(Socket, SockMod, once) end, NewShaper. -- cgit v1.2.3 From 9d4c01d425a9ef31b9ed26a1e90f886752966203 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 25 Jun 2021 01:30:10 +0200 Subject: mod_push_keepalive: Fix 'resume_timeout' docs The default 'resume_timeout' value is 72 hours, not 72 minutes. --- src/mod_push_keepalive.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mod_push_keepalive.erl b/src/mod_push_keepalive.erl index 83b89ff50..aba4fbfae 100644 --- a/src/mod_push_keepalive.erl +++ b/src/mod_push_keepalive.erl @@ -87,7 +87,7 @@ mod_opt_type(wake_on_timeout) -> econf:bool(). mod_options(_Host) -> - [{resume_timeout, timer:seconds(259200)}, + [{resume_timeout, timer:hours(72)}, {wake_on_start, false}, {wake_on_timeout, true}]. @@ -111,7 +111,7 @@ mod_doc() -> "notification is issued. Once that happened, the " "resumption timeout configured for the 'mod_stream_mgmt' " "module is restored. " - "The default value is '72' minutes.")}}, + "The default value is '72' hours.")}}, {wake_on_start, #{value => "true | false", desc => -- cgit v1.2.3 From d5adcaea61dd0007cd0dbd77fb123b62c1fda876 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 25 Jun 2021 12:32:40 +0200 Subject: Add support for rebar3 to "make rel" --- src/ejabberd.app.src.script | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/ejabberd.app.src.script b/src/ejabberd.app.src.script index 8248d9c16..67a7a470d 100644 --- a/src/ejabberd.app.src.script +++ b/src/ejabberd.app.src.script @@ -20,10 +20,14 @@ Vars = case file:consult(filename:join([filename:dirname(SCRIPT), "..", "vars.co cache_tab, eimp, fast_tls, + fast_xml, fast_yaml, lager, + p1_acme, p1_utils, pkix, + stringprep, + yconf, xmpp]}, {mod, {ejabberd_app, []}}]}. -- cgit v1.2.3 From d7c9809c59f3d1870acb404274d4bad6d0e1d8a6 Mon Sep 17 00:00:00 2001 From: Linus Jahn Date: Sun, 27 Jun 2021 00:50:50 +0200 Subject: mod_mix: Use disco identity conference/mix Probably someone has forgotten to update it here. https://xmpp.org/extensions/xep-0369.html#example-5 Fixes #2901. --- src/mod_mix.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/mod_mix.erl b/src/mod_mix.erl index 1c43bc8a7..dfe81af58 100644 --- a/src/mod_mix.erl +++ b/src/mod_mix.erl @@ -166,7 +166,7 @@ process_disco_info(#iq{type = get, to = #jid{luser = <<>>} = To, [ServerHost, ?MODULE, <<"">>, Lang]), Name = mod_mix_opt:name(ServerHost), Identity = #identity{category = <<"conference">>, - type = <<"text">>, + type = <<"mix">>, name = translate:translate(Lang, Name)}, Features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS, ?NS_MIX_CORE_0, ?NS_MIX_CORE_SEARCHABLE_0, -- cgit v1.2.3 From 795addca7dfb9b418c7757dffcbae17127d64357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 24 Jun 2021 15:15:13 +0200 Subject: Try to limit serial access when checking api permissions --- src/ejabberd_access_permissions.erl | 68 +++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/ejabberd_access_permissions.erl b/src/ejabberd_access_permissions.erl index 2122c063f..916475df9 100644 --- a/src/ejabberd_access_permissions.erl +++ b/src/ejabberd_access_permissions.erl @@ -46,6 +46,7 @@ code_change/3]). -define(SERVER, ?MODULE). +-define(CACHE_TAB, access_permissions_cache). -record(state, {definitions = none :: none | [definition()]}). @@ -71,17 +72,45 @@ %%%=================================================================== -spec can_access(atom(), caller_info()) -> allow | deny. can_access(Cmd, CallerInfo) -> - gen_server:call(?MODULE, {can_access, Cmd, CallerInfo}). + Defs0 = show_current_definitions(), + CallerModule = maps:get(caller_module, CallerInfo, none), + Host = maps:get(caller_host, CallerInfo, global), + Tag = maps:get(tag, CallerInfo, none), + Defs = maps:get(extra_permissions, CallerInfo, []) ++ Defs0, + Res = lists:foldl( + fun({Name, _} = Def, none) -> + case matches_definition(Def, Cmd, CallerModule, Tag, Host, CallerInfo) of + true -> + ?DEBUG("Command '~p' execution allowed by rule " + "'~ts' (CallerInfo=~p)", [Cmd, Name, CallerInfo]), + allow; + _ -> + none + end; + (_, Val) -> + Val + end, none, Defs), + case Res of + allow -> allow; + _ -> + ?DEBUG("Command '~p' execution denied " + "(CallerInfo=~p)", [Cmd, CallerInfo]), + deny + end. -spec invalidate() -> ok. invalidate() -> - gen_server:cast(?MODULE, invalidate). + gen_server:cast(?MODULE, invalidate), + ets_cache:delete(?CACHE_TAB, definitions). -spec show_current_definitions() -> [definition()]. show_current_definitions() -> - gen_server:call(?MODULE, show_current_definitions). - + ets_cache:lookup(?CACHE_TAB, definitions, + fun() -> + {cache, gen_server:call(?MODULE, show_current_definitions)} + end). start_link() -> + ets_cache:new(?CACHE_TAB, [{max_size, 2}]), gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). %%%=================================================================== @@ -90,38 +119,11 @@ start_link() -> -spec init([]) -> {ok, state()}. init([]) -> ejabberd_hooks:add(config_reloaded, ?MODULE, invalidate, 90), + ets_cache:new(access_permissions), {ok, #state{}}. --spec handle_call({can_access, atom(), caller_info()} | - show_current_definitions | term(), +-spec handle_call(show_current_definitions | term(), term(), state()) -> {reply, term(), state()}. -handle_call({can_access, Cmd, CallerInfo}, _From, State) -> - CallerModule = maps:get(caller_module, CallerInfo, none), - Host = maps:get(caller_host, CallerInfo, global), - Tag = maps:get(tag, CallerInfo, none), - {State2, Defs0} = get_definitions(State), - Defs = maps:get(extra_permissions, CallerInfo, []) ++ Defs0, - Res = lists:foldl( - fun({Name, _} = Def, none) -> - case matches_definition(Def, Cmd, CallerModule, Tag, Host, CallerInfo) of - true -> - ?DEBUG("Command '~p' execution allowed by rule " - "'~ts' (CallerInfo=~p)", [Cmd, Name, CallerInfo]), - allow; - _ -> - none - end; - (_, Val) -> - Val - end, none, Defs), - Res2 = case Res of - allow -> allow; - _ -> - ?DEBUG("Command '~p' execution denied " - "(CallerInfo=~p)", [Cmd, CallerInfo]), - deny - end, - {reply, Res2, State2}; handle_call(show_current_definitions, _From, State) -> {State2, Defs} = get_definitions(State), {reply, Defs, State2}; -- cgit v1.2.3 From 6e900d6a8f90d5acdc98f72a410fb1d2d75604ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 24 Jun 2021 15:16:02 +0200 Subject: Add send_timeout option to listener --- src/ejabberd_listener.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 135fbbf57..0e2bd2919 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -113,10 +113,11 @@ init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) -> _ -> {Port, SockOpts} end, + ExtraOpts2 = lists:keydelete(socket_timeout, 1, ExtraOpts), case gen_udp:open(Port2, [binary, {active, false}, {reuseaddr, true} | - ExtraOpts]) of + ExtraOpts2]) of {ok, Socket} -> case inet:sockname(Socket) of {ok, {Addr, Port1}} -> @@ -195,7 +196,6 @@ listen_tcp(Port, SockOpts) -> {active, false}, {reuseaddr, true}, {nodelay, true}, - {send_timeout, ?TCP_SEND_TIMEOUT}, {send_timeout_close, true}, {keepalive, true} | ExtraOpts]), case Res of @@ -682,6 +682,8 @@ listen_opt_type(max_stanza_size) -> econf:pos_int(infinity); listen_opt_type(max_fsm_queue) -> econf:pos_int(); +listen_opt_type(send_timeout) -> + econf:timeout(seconds, true); listen_opt_type(shaper) -> econf:shaper(); listen_opt_type(access) -> @@ -694,6 +696,7 @@ listen_options() -> {transport, tcp}, {ip, {0,0,0,0}}, {accept_interval, 0}, + {send_timeout, 15000}, {backlog, 5}, {use_proxy_protocol, false}, {supervisor, true}]. -- cgit v1.2.3 From b669e4499c4a37716ab7235f22e6cb58d01057b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 28 Jun 2021 12:18:19 +0200 Subject: Fix dialyzer warning --- src/ejabberd_listener.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 0e2bd2919..987bfd423 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -683,7 +683,7 @@ listen_opt_type(max_stanza_size) -> listen_opt_type(max_fsm_queue) -> econf:pos_int(); listen_opt_type(send_timeout) -> - econf:timeout(seconds, true); + econf:timeout(second, true); listen_opt_type(shaper) -> econf:shaper(); listen_opt_type(access) -> -- cgit v1.2.3 From e3e4dae58394446189beeb782852c31f24a6976a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 28 Jun 2021 12:53:30 +0200 Subject: Yet another dialyzer warning fix --- src/ejabberd_listener.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 987bfd423..0b46dde2d 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -683,7 +683,7 @@ listen_opt_type(max_stanza_size) -> listen_opt_type(max_fsm_queue) -> econf:pos_int(); listen_opt_type(send_timeout) -> - econf:timeout(second, true); + econf:timeout(second, infinity); listen_opt_type(shaper) -> econf:shaper(); listen_opt_type(access) -> -- cgit v1.2.3 From bb0c6e1e024a095a37db823372eb56bfd716a356 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 1 Jul 2021 13:17:00 +0200 Subject: Show messages with next configuration steps when installing a module --- src/ext_mod.erl | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'src') diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 9ff53ca8c..5353019bb 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -638,6 +638,7 @@ install(Module, Spec, SrcDir, LibDir) -> Errors = lists:dropwhile(fun({_, ok}) -> true; (_) -> false end, Files1++Files2), + inform_module_configuration(Module, LibDir, Files1), Result = case Errors of [{F, {error, E}}|_] -> {error, {F, E}}; @@ -649,6 +650,24 @@ install(Module, Spec, SrcDir, LibDir) -> file:set_cwd(CurDir), Result. +inform_module_configuration(Module, LibDir, Files1) -> + Res = lists:filter(fun({[$c, $o, $n, $f |_], ok}) -> true; + (_) -> false + end, Files1), + case Res of + [{ConfigPath, ok}] -> + FullConfigPath = filename:join(LibDir, ConfigPath), + io:format("Module ~p has been installed and started.~n" + "It's configured in the file:~n ~s~n" + "Configure the module in that file, or remove it~n" + "and configure in your main ejabberd.yml~n", + [Module, FullConfigPath]); + [] -> + io:format("Module ~p has been installed.~n" + "Now you can configure it in your ejabberd.yml~n", + [Module]) + end. + %% -- minimalist rebar spec parser, only support git fetch_rebar_deps(SrcDir) -> -- cgit v1.2.3 From 509331a563e98c37c055b786ba95ce65c52b1d6d Mon Sep 17 00:00:00 2001 From: Emmet McPoland Date: Wed, 7 Jul 2021 16:44:58 +0100 Subject: Correctly strip only other bcc addresses i.e. bcc receiver should still be able to see their bcc address element and no other bcc address element --- src/mod_multicast.erl | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mod_multicast.erl b/src/mod_multicast.erl index 530bfecea..161d3a4c4 100644 --- a/src/mod_multicast.erl +++ b/src/mod_multicast.erl @@ -392,8 +392,9 @@ perform(From, Packet, _, {route_single, Group}) -> lists:foreach( fun(ToUser) -> + Group_others = strip_other_bcc(ToUser, Group#group.others), route_packet(From, ToUser, Packet, - Group#group.others, Group#group.addresses) + Group_others, Group#group.addresses) end, Group#group.dests); perform(From, Packet, _, {{route_multicast, JID, RLimits}, Group}) -> @@ -424,6 +425,21 @@ strip_addresses_element(Packet) -> throw(eadsele) end. +%%%------------------------- +%%% Strip third-party bcc 'addresses' +%%%------------------------- + +strip_other_bcc(#dest{jid_jid = ToUserJid}, Group_others) -> + lists:filter( + fun(#address{jid = JID, type = Type}) -> + case {JID, Type} of + {ToUserJid, bcc} -> true; + {_, bcc} -> false; + _ -> true + end + end, + Group_others). + %%%------------------------- %%% Split Addresses %%%------------------------- @@ -545,7 +561,6 @@ build_other_xml(Dests) -> case Dest#dest.type of to -> [add_delivered(XML) | R]; cc -> [add_delivered(XML) | R]; - bcc -> R; _ -> [XML | R] end end, -- cgit v1.2.3 From 271a9f097d251fb76912f935f83b29bae833799b Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 7 Jul 2021 17:32:55 +0200 Subject: After create_room, store in DB if it's persistent (#3632) --- src/mod_muc_admin.erl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index a595d00c3..47b28f7a8 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -696,7 +696,7 @@ create_room_with_opts(Name1, Host1, ServerHost1, CustomRoomOpts) -> lists:keysort(1, DefRoomOpts)), case mod_muc:create_room(Host, Name, RoomOpts) of ok -> - ok; + maybe_store_room(ServerHost, Host, Name, RoomOpts); {error, _} -> throw({error, "Unable to start room"}) end; @@ -705,6 +705,15 @@ create_room_with_opts(Name1, Host1, ServerHost1, CustomRoomOpts) -> end end. +maybe_store_room(ServerHost, Host, Name, RoomOpts) -> + case proplists:get_bool(persistent, RoomOpts) of + true -> + {atomic, ok} = mod_muc:store_room(ServerHost, Host, Name, RoomOpts), + ok; + false -> + ok + end. + %% Create the room only in the database. %% It is required to restart the MUC service for the room to appear. muc_create_room(ServerHost, {Name, Host, _}, DefRoomOpts) -> -- cgit v1.2.3 From 2e2667bbd7f00e465f9f58a2699069fdb2c60a4e Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 8 Jul 2021 11:57:37 +0200 Subject: Update documentation: mod_muc ram_db_type supports SQL since 17.04 (#3632) --- src/mod_muc.erl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 0139db430..46bfd20e5 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1386,11 +1386,10 @@ mod_doc() -> "store room information. The default is the storage defined " "by the global option 'default_db', or 'mnesia' if omitted.")}}, {ram_db_type, - #{value => "mnesia", + #{value => "mnesia | sql", desc => ?T("Define the type of volatile (in-memory) storage where the module " - "will store room information. The only available value for this " - "module is 'mnesia'.")}}, + "will store room information ('muc_online_room' and 'muc_online_users').")}}, {hibernation_timeout, #{value => "infinity | Seconds", desc => -- cgit v1.2.3 From 0de6f1c5383fef45adaf494f9db4ccf6a1b35df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 13 Jul 2021 16:01:25 +0200 Subject: Use multicast routing for more packets generated by muc --- src/ejabberd_c2s.erl | 2 +- src/ejabberd_router_multicast.erl | 49 ++++++++++--- src/mod_muc_room.erl | 144 +++++++++++++++++++++++++++++--------- 3 files changed, 153 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index ada7653b6..3218cce51 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -880,7 +880,7 @@ get_priority_from_presence(#presence{priority = Prio}) -> -spec route_multiple(state(), [jid()], stanza()) -> ok. route_multiple(#{lserver := LServer}, JIDs, Pkt) -> From = xmpp:get_from(Pkt), - ejabberd_router_multicast:route_multicast(From, LServer, JIDs, Pkt). + ejabberd_router_multicast:route_multicast(From, LServer, JIDs, Pkt, false). get_subscription(#jid{luser = LUser, lserver = LServer}, JID) -> {Subscription, _, _} = ejabberd_hooks:run_fold( diff --git a/src/ejabberd_router_multicast.erl b/src/ejabberd_router_multicast.erl index 0d9d6b1d4..6e0201c90 100644 --- a/src/ejabberd_router_multicast.erl +++ b/src/ejabberd_router_multicast.erl @@ -30,7 +30,7 @@ -behaviour(gen_server). %% API --export([route_multicast/4, +-export([route_multicast/5, register_route/1, unregister_route/1 ]). @@ -58,9 +58,11 @@ start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). --spec route_multicast(jid(), binary(), [jid()], stanza()) -> ok. -route_multicast(From, Domain, Destinations, Packet) -> - case catch do_route(Domain, Destinations, xmpp:set_from(Packet, From)) of +-spec route_multicast(jid(), binary(), [jid()], stanza(), boolean()) -> ok. +route_multicast(From0, Domain0, Destinations0, Packet0, Wrapped0) -> + {From, Domain, Destinations, Packet, Wrapped} = + ejabberd_hooks:run_fold(multicast_route, {From0, Domain0, Destinations0, Packet0, Wrapped0}, []), + case catch do_route(Domain, Destinations, xmpp:set_from(Packet, From), Wrapped) of {'EXIT', Reason} -> ?ERROR_MSG("~p~nwhen processing: ~p", [Reason, {From, Domain, Destinations, Packet}]); @@ -157,7 +159,7 @@ handle_cast(Msg, State) -> %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- handle_info({route_multicast, Domain, Destinations, Packet}, State) -> - case catch do_route(Domain, Destinations, Packet) of + case catch do_route(Domain, Destinations, Packet, false) of {'EXIT', Reason} -> ?ERROR_MSG("~p~nwhen processing: ~p", [Reason, {Domain, Destinations, Packet}]); @@ -204,13 +206,41 @@ terminate(_Reason, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. +-spec update_to_in_wrapped(stanza(), jid()) -> stanza(). +update_to_in_wrapped(Packet, To) -> + case Packet of + #message{sub_els = [#ps_event{ + items = #ps_items{ + items = [#ps_item{ + sub_els = [Internal] + } = PSItem] + } = PSItems + } = PSEvent]} -> + Internal2 = xmpp:set_to(Internal, To), + PSItem2 = PSItem#ps_item{sub_els = Internal2}, + PSItems2 = PSItems#ps_items{items = PSItem2}, + PSEvent2 = PSEvent#ps_event{items = PSItems2}, + Packet#message{sub_els = [PSEvent2]}; + _ -> + Packet + end. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- %% From = #jid %% Destinations = [#jid] --spec do_route(binary(), [jid()], stanza()) -> any(). -do_route(Domain, Destinations, Packet) -> +-spec do_route(binary(), [jid()], stanza(), boolean()) -> any(). +do_route(Domain, Destinations, Packet, true) -> + ?DEBUG("Route multicast:~n~ts~nDomain: ~ts~nDestinations: ~ts~n", + [xmpp:pp(Packet), Domain, + str:join([jid:encode(To) || To <- Destinations], <<", ">>)]), + lists:foreach( + fun(To) -> + Packet2 = update_to_in_wrapped(Packet, To), + ejabberd_router:route(Packet2) + end, Destinations); +do_route(Domain, Destinations, Packet, false) -> ?DEBUG("Route multicast:~n~ts~nDomain: ~ts~nDestinations: ~ts~n", [xmpp:pp(Packet), Domain, str:join([jid:encode(To) || To <- Destinations], <<", ">>)]), @@ -236,4 +266,7 @@ pick_multicast_pid(Rs) -> -spec do_route_normal([jid()], stanza()) -> any(). do_route_normal(Destinations, Packet) -> - [ejabberd_router:route(xmpp:set_to(Packet, To)) || To <- Destinations]. + lists:foreach( + fun(To) -> + ejabberd_router:route(xmpp:set_to(Packet, To)) + end, Destinations). diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 2fa08dc79..1c9710c60 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -4624,37 +4624,55 @@ store_room_no_checks(StateData, ChangesHints) -> -spec send_subscriptions_change_notifications(jid(), binary(), subscribe|unsubscribe, state()) -> ok. send_subscriptions_change_notifications(From, Nick, Type, State) -> - maps:fold(fun(_, #subscriber{nodes = Nodes, jid = JID}, _) -> - case lists:member(?NS_MUCSUB_NODES_SUBSCRIBERS, Nodes) of + {WJ, WN} = + maps:fold( + fun({WithJid, WithNick} = Res, #subscriber{nodes = Nodes, jid = JID}, _) -> + case lists:member(?NS_MUCSUB_NODES_SUBSCRIBERS, Nodes) of + true -> + case (State#state.config)#config.anonymous == false orelse + get_role(JID, State) == moderator orelse + get_default_role(get_affiliation(JID, State), State) == moderator of true -> - ShowJid = case (State#state.config)#config.anonymous == false orelse - get_role(JID, State) == moderator orelse - get_default_role(get_affiliation(JID, State), State) == moderator of - true -> true; - _ -> false - end, - Payload = case {Type, ShowJid} of - {subscribe, true} -> - #muc_subscribe{jid = From, nick = Nick}; - {subscribe, _} -> - #muc_subscribe{nick = Nick}; - {unsubscribe, true} -> - #muc_unsubscribe{jid = From, nick = Nick}; - {unsubscribe, _} -> - #muc_unsubscribe{nick = Nick} - end, - Packet = #message{ - sub_els = [#ps_event{ - items = #ps_items{ - node = ?NS_MUCSUB_NODES_SUBSCRIBERS, - items = [#ps_item{ - id = p1_rand:get_string(), - sub_els = [Payload]}]}}]}, - ejabberd_router:route(xmpp:set_from_to(Packet, State#state.jid, JID)); - false -> - ok - end - end, ok, State#state.subscribers). + {[JID | WithJid], WithNick}; + _ -> + {WithJid, [JID | WithNick]} + end; + false -> + Res + end + end, ok, State#state.subscribers), + if WJ /= [] -> + Payload1 = case Type of + subscribe -> #muc_subscribe{jid = From, nick = Nick}; + _ -> #muc_unsubscribe{jid = From, nick = Nick} + end, + Packet1 = #message{ + sub_els = [#ps_event{ + items = #ps_items{ + node = ?NS_MUCSUB_NODES_SUBSCRIBERS, + items = [#ps_item{ + id = p1_rand:get_string(), + sub_els = [Payload1]}]}}]}, + ejabberd_router_multicast:route_multicast(State#state.jid, State#state.server_host, + WJ, Packet1, true); + true -> ok + end, + if WN /= [] -> + Payload2 = case Type of + subscribe -> #muc_subscribe{nick = Nick}; + _ -> #muc_unsubscribe{nick = Nick} + end, + Packet2 = #message{ + sub_els = [#ps_event{ + items = #ps_items{ + node = ?NS_MUCSUB_NODES_SUBSCRIBERS, + items = [#ps_item{ + id = p1_rand:get_string(), + sub_els = [Payload2]}]}}]}, + ejabberd_router_multicast:route_multicast(State#state.jid, State#state.server_host, + WN, Packet2, true); + true -> ok + end. -spec send_wrapped(jid(), jid(), stanza(), binary(), state()) -> ok. send_wrapped(From, To, Packet, Node, State) -> @@ -4727,10 +4745,70 @@ wrap(From, To, Packet, Node, Id) -> -spec send_wrapped_multiple(jid(), users(), stanza(), binary(), state()) -> ok. send_wrapped_multiple(From, Users, Packet, Node, State) -> + {Dir, Wra} = maps:fold( - fun(_, #user{jid = To}, _) -> - send_wrapped(From, To, Packet, Node, State) - end, ok, Users). + fun(_, #user{jid = To, last_presence = LP}, {Direct, Wrapped} = Res) -> + IsOffline = LP == undefined, + if IsOffline -> + LBareTo = jid:tolower(jid:remove_resource(To)), + case maps:find(LBareTo, State#state.subscribers) of + {ok, #subscriber{nodes = Nodes}} -> + case lists:member(Node, Nodes) of + true -> + {Direct, [To | Wrapped]}; + _ -> + Res + end; + _ -> + Res + end; + true -> + {[To | Direct], Wrapped} + end + end, {[],[]}, Users), + case Dir of + [] -> ok; + _ -> + case Packet of + #presence{type = unavailable} -> + case xmpp:get_subtag(Packet, #muc_user{}) of + #muc_user{destroy = Destroy, + status_codes = Codes} -> + case Destroy /= undefined orelse + (lists:member(110,Codes) andalso + not lists:member(303, Codes)) of + true -> + ejabberd_router_multicast:route_multicast( + State#state.jid, State#state.server_host, Dir, + #presence{id = p1_rand:get_string(), + type = unavailable}, false); + false -> + ok + end; + _ -> + false + end; + _ -> + ok + end, + ejabberd_router_multicast:route_multicast(State#state.jid, State#state.server_host, + Dir, Packet, false) + end, + case Wra of + [] -> ok; + _ -> + MamEnabled = (State#state.config)#config.mam, + Id = case xmpp:get_subtag(Packet, #stanza_id{by = #jid{}}) of + #stanza_id{id = Id2} -> + Id2; + _ -> + p1_rand:get_string() + end, + NewPacket = wrap(From, State#state.jid, Packet, Node, Id), + NewPacket2 = xmpp:put_meta(NewPacket, in_muc_mam, MamEnabled), + ejabberd_router_multicast:route_multicast(State#state.jid, State#state.server_host, + Wra, NewPacket2, true) + end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Detect messange stanzas that don't have meaningful content -- cgit v1.2.3 From a07029dcad76294a749d74946cae670a8cf3c12d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 13 Jul 2021 17:56:16 +0200 Subject: Fix previous commit to pass all tests --- src/ejabberd_router_multicast.erl | 8 ++++---- src/mod_muc_room.erl | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/ejabberd_router_multicast.erl b/src/ejabberd_router_multicast.erl index 6e0201c90..54abf671c 100644 --- a/src/ejabberd_router_multicast.erl +++ b/src/ejabberd_router_multicast.erl @@ -217,12 +217,12 @@ update_to_in_wrapped(Packet, To) -> } = PSItems } = PSEvent]} -> Internal2 = xmpp:set_to(Internal, To), - PSItem2 = PSItem#ps_item{sub_els = Internal2}, - PSItems2 = PSItems#ps_items{items = PSItem2}, + PSItem2 = PSItem#ps_item{sub_els = [Internal2]}, + PSItems2 = PSItems#ps_items{items = [PSItem2]}, PSEvent2 = PSEvent#ps_event{items = PSItems2}, - Packet#message{sub_els = [PSEvent2]}; + xmpp:set_to(Packet#message{sub_els = [PSEvent2]}, To); _ -> - Packet + xmpp:set_to(Packet, To) end. %%-------------------------------------------------------------------- diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 1c9710c60..eb12a9470 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -4626,7 +4626,7 @@ store_room_no_checks(StateData, ChangesHints) -> send_subscriptions_change_notifications(From, Nick, Type, State) -> {WJ, WN} = maps:fold( - fun({WithJid, WithNick} = Res, #subscriber{nodes = Nodes, jid = JID}, _) -> + fun(_, #subscriber{nodes = Nodes, jid = JID}, {WithJid, WithNick} = Res) -> case lists:member(?NS_MUCSUB_NODES_SUBSCRIBERS, Nodes) of true -> case (State#state.config)#config.anonymous == false orelse @@ -4640,7 +4640,7 @@ send_subscriptions_change_notifications(From, Nick, Type, State) -> false -> Res end - end, ok, State#state.subscribers), + end, {[], []}, State#state.subscribers), if WJ /= [] -> Payload1 = case Type of subscribe -> #muc_subscribe{jid = From, nick = Nick}; @@ -4779,7 +4779,7 @@ send_wrapped_multiple(From, Users, Packet, Node, State) -> not lists:member(303, Codes)) of true -> ejabberd_router_multicast:route_multicast( - State#state.jid, State#state.server_host, Dir, + From, State#state.server_host, Dir, #presence{id = p1_rand:get_string(), type = unavailable}, false); false -> @@ -4791,7 +4791,7 @@ send_wrapped_multiple(From, Users, Packet, Node, State) -> _ -> ok end, - ejabberd_router_multicast:route_multicast(State#state.jid, State#state.server_host, + ejabberd_router_multicast:route_multicast(From, State#state.server_host, Dir, Packet, false) end, case Wra of @@ -4804,7 +4804,7 @@ send_wrapped_multiple(From, Users, Packet, Node, State) -> _ -> p1_rand:get_string() end, - NewPacket = wrap(From, State#state.jid, Packet, Node, Id), + NewPacket = wrap(From, undefined, Packet, Node, Id), NewPacket2 = xmpp:put_meta(NewPacket, in_muc_mam, MamEnabled), ejabberd_router_multicast:route_multicast(State#state.jid, State#state.server_host, Wra, NewPacket2, true) -- cgit v1.2.3 From 5beaf50c6799f87748ce68380fd4f2acbe773fac Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 13 Jul 2021 20:43:44 +0200 Subject: Fix spec: xmpp:set_from_to/3 allows undefined as second argument --- src/mod_muc_room.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index eb12a9470..fc03e50ef 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -4731,7 +4731,7 @@ send_wrapped(From, To, Packet, Node, State) -> ejabberd_router:route(xmpp:set_from_to(Packet, From, To)) end. --spec wrap(jid(), jid(), stanza(), binary(), binary()) -> message(). +-spec wrap(jid(), undefined | jid(), stanza(), binary(), binary()) -> message(). wrap(From, To, Packet, Node, Id) -> El = xmpp:set_from_to(Packet, From, To), #message{ -- cgit v1.2.3 From e22ed8081af971d5e875f6ad4a0a9714b82c67e2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 8 Jul 2021 12:48:19 +0200 Subject: Provide proper error message when create_room fails with invalid_service --- src/mod_muc_admin.erl | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 47b28f7a8..88ae041f9 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -700,6 +700,8 @@ create_room_with_opts(Name1, Host1, ServerHost1, CustomRoomOpts) -> {error, _} -> throw({error, "Unable to start room"}) end; + invalid_service -> + throw({error, "Invalid 'service'"}); _ -> throw({error, "Room already exists"}) end -- cgit v1.2.3 From 27c69f263cf2c193b7ce052f54bccfe0cceafcca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 14 Jul 2021 13:58:03 +0200 Subject: Allow multicast hook registering by host --- src/ejabberd_router_multicast.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ejabberd_router_multicast.erl b/src/ejabberd_router_multicast.erl index 54abf671c..e97ccb837 100644 --- a/src/ejabberd_router_multicast.erl +++ b/src/ejabberd_router_multicast.erl @@ -39,7 +39,7 @@ %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). + terminate/2, code_change/3, update_to_in_wrapped/2]). -include("logger.hrl"). -include_lib("xmpp/include/xmpp.hrl"). @@ -61,7 +61,7 @@ start_link() -> -spec route_multicast(jid(), binary(), [jid()], stanza(), boolean()) -> ok. route_multicast(From0, Domain0, Destinations0, Packet0, Wrapped0) -> {From, Domain, Destinations, Packet, Wrapped} = - ejabberd_hooks:run_fold(multicast_route, {From0, Domain0, Destinations0, Packet0, Wrapped0}, []), + ejabberd_hooks:run_fold(multicast_route, Domain0, {From0, Domain0, Destinations0, Packet0, Wrapped0}, []), case catch do_route(Domain, Destinations, xmpp:set_from(Packet, From), Wrapped) of {'EXIT', Reason} -> ?ERROR_MSG("~p~nwhen processing: ~p", -- cgit v1.2.3 From 3afaacab76e271912ff1f5650b5d22e59bda9274 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 15 Jul 2021 17:19:32 +0200 Subject: With the recent changes it seems mod_mix supports 0.14.1 (#3634) --- src/mod_mix.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/mod_mix.erl b/src/mod_mix.erl index dfe81af58..82b5e41a4 100644 --- a/src/mod_mix.erl +++ b/src/mod_mix.erl @@ -24,7 +24,7 @@ -module(mod_mix). -behaviour(gen_mod). -behaviour(gen_server). --protocol({xep, 369, '0.13.0'}). +-protocol({xep, 369, '0.14.1'}). %% API -export([route/1]). -- cgit v1.2.3 From e0c9a6308dcf29a611f5b06675167953ee3ce398 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 13 Jul 2021 23:45:17 +0200 Subject: erlang:phash is deprecated in OTP 24, let's use phash2 --- src/ejabberd_router.erl | 6 +++--- src/ejabberd_s2s.erl | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl index 4939ae1e8..492beb6d3 100644 --- a/src/ejabberd_router.erl +++ b/src/ejabberd_router.erl @@ -424,15 +424,15 @@ balancing_route(From, To, Packet, Rs) -> Value = erlang:system_time(), case [R || R <- Rs, node(R#route.pid) == node()] of [] -> - R = lists:nth(erlang:phash(Value, length(Rs)), Rs), + R = lists:nth(erlang:phash2(Value, length(Rs))+1, Rs), do_route(Packet, R); LRs -> - R = lists:nth(erlang:phash(Value, length(LRs)), LRs), + R = lists:nth(erlang:phash2(Value, length(LRs))+1, LRs), do_route(Packet, R) end; Value -> SRs = lists:ukeysort(#route.local_hint, Rs), - R = lists:nth(erlang:phash(Value, length(SRs)), SRs), + R = lists:nth(erlang:phash2(Value, length(SRs))+1, SRs), do_route(Packet, R) end. diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index 21c799c6c..8057c9a35 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -429,8 +429,8 @@ choose_pid(From, Pids) -> Ps -> Ps end, Pid = - lists:nth(erlang:phash(jid:remove_resource(From), - length(Pids1)), + lists:nth(erlang:phash2(jid:remove_resource(From), + length(Pids1))+1, Pids1), ?DEBUG("Using ejabberd_s2s_out ~p~n", [Pid]), Pid. -- cgit v1.2.3 From c3169e9eeab15f47b33a14a6b93cec67c38193a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 20 Jul 2021 17:07:40 +0200 Subject: Typo --- src/ejabberd_listener.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 0b46dde2d..93fd2e0fd 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -113,7 +113,7 @@ init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) -> _ -> {Port, SockOpts} end, - ExtraOpts2 = lists:keydelete(socket_timeout, 1, ExtraOpts), + ExtraOpts2 = lists:keydelete(send_timeout, 1, ExtraOpts), case gen_udp:open(Port2, [binary, {active, false}, {reuseaddr, true} | -- cgit v1.2.3 From 50242cec789187d1d470180cc94555ce56061d10 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 20 Jul 2021 19:19:58 +0200 Subject: Annotate the srg_create command as changed in 21.07 --- src/mod_admin_extra.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index abbed635d..04cb39cfb 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -662,6 +662,7 @@ get_commands_spec() -> "For example:\n" " ejabberdctl srg_create group3 myserver.com " "name desc \\\"group1\\\\ngroup2\\\"", + note = "changed in 21.07", module = ?MODULE, function = srg_create, args = [{group, binary}, {host, binary}, {label, binary}, {description, binary}, {display, binary}], -- cgit v1.2.3 From 6e4e5a019071db565cb2c4ac6089e8ef809142a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 23 Jul 2021 10:14:45 +0200 Subject: Add missing fields from config inside mod_muc_admin:change_options --- src/mod_muc_admin.erl | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 88ae041f9..72a29a45b 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -1166,6 +1166,7 @@ change_option(Option, Value, Config) -> anonymous -> Config#config{anonymous = Value}; captcha_protected -> Config#config{captcha_protected = Value}; description -> Config#config{description = Value}; + lang -> Config#config{lang = Value}; logging -> Config#config{logging = Value}; mam -> Config#config{mam = Value}; max_users -> Config#config{max_users = Value}; @@ -1178,8 +1179,10 @@ change_option(Option, Value, Config) -> presence_broadcast -> Config#config{presence_broadcast = Value}; public -> Config#config{public = Value}; public_list -> Config#config{public_list = Value}; + pubsub -> Config#config{pubsub = Value}; title -> Config#config{title = Value}; vcard -> Config#config{vcard = Value}; + vcard_xupdate -> Config#config{vcard_xupdate = Value}; voice_request_min_interval -> Config#config{voice_request_min_interval = Value} end. -- cgit v1.2.3 From 103e98b8da5abd7713c3ef8e9e54b004c4935a09 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 28 Jul 2021 18:22:39 +0200 Subject: mod_push: Fix handling of MUC/Sub messages Don't fail to include the sender/body of MUC/Sub messages if the recipient is offline. Closes #3651. --- src/mod_push.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/mod_push.erl b/src/mod_push.erl index 14ee02910..b4dd48234 100644 --- a/src/mod_push.erl +++ b/src/mod_push.erl @@ -405,7 +405,7 @@ c2s_stanza(State, #stream_error{}, _SendResult) -> c2s_stanza(#{push_enabled := true, mgmt_state := pending} = State, Pkt, _SendResult) -> ?DEBUG("Notifying client of stanza", []), - notify(State, unwrap_message(Pkt), get_direction(Pkt)), + notify(State, Pkt, get_direction(Pkt)), State; c2s_stanza(State, _Pkt, _SendResult) -> State. @@ -454,7 +454,7 @@ c2s_session_pending(#{push_enabled := true, mgmt_queue := Queue} = State) -> {Pkt, Dir} = case mod_stream_mgmt:queue_find( fun is_incoming_chat_msg/1, Queue) of none -> {none, undefined}; - Pkt0 -> {unwrap_message(Pkt0), get_direction(Pkt0)} + Pkt0 -> {Pkt0, get_direction(Pkt0)} end, notify(State, Pkt, Dir), State; @@ -536,7 +536,8 @@ notify(LUser, LServer, Clients, Pkt, Dir) -> -spec notify(binary(), ljid(), binary(), xdata(), xmpp_element() | xmlel() | none, direction(), fun((iq() | timeout) -> any())) -> ok. -notify(LServer, PushLJID, Node, XData, Pkt, Dir, HandleResponse) -> +notify(LServer, PushLJID, Node, XData, Pkt0, Dir, HandleResponse) -> + Pkt = unwrap_message(Pkt0), From = jid:make(LServer), Summary = make_summary(LServer, Pkt, Dir), Item = #ps_item{sub_els = [#push_notification{xdata = Summary}]}, @@ -714,7 +715,7 @@ make_summary(Host, #message{from = From} = Pkt, recv) -> make_summary(_Host, _Pkt, _Dir) -> undefined. --spec unwrap_message(stanza()) -> stanza(). +-spec unwrap_message(Stanza) -> Stanza when Stanza :: stanza() | none. unwrap_message(#message{meta = #{carbon_copy := true}} = Msg) -> misc:unwrap_carbon(Msg); unwrap_message(#message{type = normal} = Msg) -> -- cgit v1.2.3 From 2050cdffb457c4adbf5070de2ea84c59b2dc61a3 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 28 Jul 2021 18:53:15 +0200 Subject: PubSub: Use configured 'max_items' by default If clients don't ask for a specific 'max_items' limit, use the value of mod_pubsub's 'max_items_node' option as default, rather than the hard-coded ?MAXITEMS value. This makes sure clients cannot circumvent a smaller, configured limit. --- src/mod_pubsub.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index fecb35341..b0dca4b0d 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -3406,7 +3406,7 @@ max_items(Host, Options) -> case get_option(Options, max_items) of I when is_integer(I), I < 0 -> 0; I when is_integer(I) -> I; - _ -> ?MAXITEMS + _ -> get_max_items_node(Host) end; false -> case get_option(Options, send_last_published_item) of -- cgit v1.2.3 From ccb4328d06bd39a10c2ecc109a6647cf1eafdaa9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 29 Jul 2021 23:13:17 +0200 Subject: Store who defines a command, specially when defined by ejabberd modules --- src/ejabberd_commands.erl | 6 +++++- src/mod_admin_extra.erl | 2 +- src/mod_admin_update_sql.erl | 2 +- src/mod_fail2ban.erl | 2 +- src/mod_mam.erl | 2 +- src/mod_muc_admin.erl | 2 +- src/mod_private.erl | 2 +- src/mod_push.erl | 2 +- 8 files changed, 12 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index 0f86f23d3..8d875e82f 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -41,6 +41,7 @@ get_tags_commands/0, get_tags_commands/1, register_commands/1, + register_commands/2, unregister_commands/1, get_commands_spec/0, get_commands_definition/0, @@ -129,10 +130,13 @@ code_change(_OldVsn, State, _Extra) -> -spec register_commands([ejabberd_commands()]) -> ok. register_commands(Commands) -> + register_commands(unknown, Commands). + +register_commands(Definer, Commands) -> lists:foreach( fun(Command) -> %% XXX check if command exists - mnesia:dirty_write(Command) + mnesia:dirty_write(Command#ejabberd_commands{definer = Definer}) %% ?DEBUG("This command is already defined:~n~p", [Command]) end, Commands), diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 04cb39cfb..fe0dd2757 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -92,7 +92,7 @@ %%% start(_Host, _Opts) -> - ejabberd_commands:register_commands(get_commands_spec()). + ejabberd_commands:register_commands(?MODULE, get_commands_spec()). stop(Host) -> case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index 0215a5172..eae481385 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -46,7 +46,7 @@ %%% start(_Host, _Opts) -> - ejabberd_commands:register_commands(get_commands_spec()). + ejabberd_commands:register_commands(?MODULE, get_commands_spec()). stop(_Host) -> ejabberd_commands:unregister_commands(get_commands_spec()). diff --git a/src/mod_fail2ban.erl b/src/mod_fail2ban.erl index 99ff3d127..8751653d8 100644 --- a/src/mod_fail2ban.erl +++ b/src/mod_fail2ban.erl @@ -107,7 +107,7 @@ c2s_stream_started(#{ip := {Addr, _}} = State, _) -> start(Host, Opts) -> catch ets:new(failed_auth, [named_table, public, {heir, erlang:group_leader(), none}]), - ejabberd_commands:register_commands(get_commands_spec()), + ejabberd_commands:register_commands(?MODULE, get_commands_spec()), gen_mod:start_child(?MODULE, Host, Opts). stop(Host) -> diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 12542bfa5..e894f5f7d 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -148,7 +148,7 @@ start(Host, Opts) -> ejabberd_hooks:add(check_create_room, Host, ?MODULE, check_create_room, 50) end, - ejabberd_commands:register_commands(get_commands_spec()), + ejabberd_commands:register_commands(?MODULE, get_commands_spec()), ok; Err -> Err diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 72a29a45b..360d414b1 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -57,7 +57,7 @@ %%---------------------------- start(Host, _Opts) -> - ejabberd_commands:register_commands(get_commands_spec()), + ejabberd_commands:register_commands(?MODULE, get_commands_spec()), ejabberd_hooks:add(webadmin_menu_main, ?MODULE, web_menu_main, 50), ejabberd_hooks:add(webadmin_menu_host, Host, ?MODULE, web_menu_host, 50), ejabberd_hooks:add(webadmin_page_main, ?MODULE, web_page_main, 50), diff --git a/src/mod_private.erl b/src/mod_private.erl index ad36c8494..36f1e8d2c 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -66,7 +66,7 @@ start(Host, Opts) -> ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50), ejabberd_hooks:add(pubsub_publish_item, Host, ?MODULE, pubsub_publish_item, 50), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE, ?MODULE, process_sm_iq), - ejabberd_commands:register_commands(get_commands_spec()). + ejabberd_commands:register_commands(?MODULE, get_commands_spec()). stop(Host) -> ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50), diff --git a/src/mod_push.erl b/src/mod_push.erl index b4dd48234..ea1c3e649 100644 --- a/src/mod_push.erl +++ b/src/mod_push.erl @@ -98,7 +98,7 @@ start(Host, Opts) -> init_cache(Mod, Host, Opts), register_iq_handlers(Host), register_hooks(Host), - ejabberd_commands:register_commands(get_commands_spec()). + ejabberd_commands:register_commands(?MODULE, get_commands_spec()). -spec stop(binary()) -> ok. stop(Host) -> -- cgit v1.2.3 From 41808a63a0c7ccd5deb3498c4946f88f1ddd1960 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 29 Jul 2021 23:13:53 +0200 Subject: Show definer module in "ejabberdctl help" when it's a gen_mod --- src/ejabberd_ctl.erl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index ce642727f..04e383d53 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -827,6 +827,7 @@ print_usage_command(Cmd, MaxC, ShCode, Version) -> print_usage_command2(Cmd, C, MaxC, ShCode) -> #ejabberd_commands{ tags = TagsAtoms, + definer = Definer, desc = Desc, args = ArgsDef, longdesc = LongDesc, @@ -851,6 +852,15 @@ print_usage_command2(Cmd, C, MaxC, ShCode) -> TagsFmt = [" ",?B("Tags"),":", prepare_long_line(8, MaxC, [?G(atom_to_list(TagA)) || TagA <- TagsAtoms])], + IsDefinerMod = case Definer of + unknown -> true; + _ -> lists:member(gen_mod, proplists:get_value(behaviour, Definer:module_info(attributes))) + end, + ModuleFmt = case IsDefinerMod of + true -> [" ",?B("Module"),": ", atom_to_list(Definer), "\n\n"]; + false -> [] + end, + DescFmt = [" ",?B("Description"),":", prepare_description(15, MaxC, Desc)], LongDescFmt = case LongDesc of @@ -866,7 +876,7 @@ print_usage_command2(Cmd, C, MaxC, ShCode) -> case Cmd of "help" -> ok; _ -> print([NameFmt, "\n", ArgsFmt, "\n", ReturnsFmt, - "\n\n", XmlrpcFmt, TagsFmt, "\n\n", DescFmt, "\n\n"], []) + "\n\n", XmlrpcFmt, TagsFmt, "\n\n", ModuleFmt, DescFmt, "\n\n"], []) end, print([LongDescFmt, NoteEjabberdctl], []). -- cgit v1.2.3 From b22779f0185924de8bed2773ca2144389b1337ba Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 29 Jul 2021 23:14:34 +0200 Subject: Show tags and definer module in generated API document when it's a gen_mod --- src/ejabberd_commands_doc.erl | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index 4981577b6..582908723 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -360,8 +360,8 @@ gen_param(Name, Type, Desc, HTMLOutput) -> [?TAG(dt, [?TAG_R(strong, atom_to_list(Name)), <<" :: ">>, ?RAW(format_type(Type))]), ?TAG(dd, ?RAW(Desc))]. -gen_doc(#ejabberd_commands{name=Name, tags=_Tags, desc=Desc, longdesc=LongDesc, - args=Args, args_desc=ArgsDesc, note=Note, +gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, + args=Args, args_desc=ArgsDesc, note=Note, definer=Definer, result=Result, result_desc=ResultDesc}=Cmd, HTMLOutput, Langs) -> try ArgsText = case ArgsDesc of @@ -389,6 +389,17 @@ gen_doc(#ejabberd_commands{name=Name, tags=_Tags, desc=Desc, longdesc=LongDesc, [?TAG(dl, [gen_param(RName, Type, ResultDesc, HTMLOutput)])] end end, + TagsText = [?RAW(atom_to_list(Tag) ++ " ") || Tag <- Tags], + IsDefinerMod = case Definer of + unknown -> true; + _ -> lists:member(gen_mod, proplists:get_value(behaviour, Definer:module_info(attributes))) + end, + ModuleText = case IsDefinerMod of + true -> + [?TAG(h2, <<"Module:">>), ?TAG(p, ?RAW(atom_to_list(Definer)))]; + false -> + [] + end, NoteEl = case Note of "" -> []; _ -> ?TAG('div', "note-down", ?RAW(Note)) @@ -403,6 +414,8 @@ gen_doc(#ejabberd_commands{name=Name, tags=_Tags, desc=Desc, longdesc=LongDesc, end, ?TAG(h2, <<"Arguments:">>), ArgsText, ?TAG(h2, <<"Result:">>), ResultText, + ?TAG(h2, <<"Tags:">>), ?TAG(p, TagsText)] + ++ ModuleText ++ [ ?TAG(h2, <<"Examples:">>), gen_calls(Cmd, HTMLOutput, Langs)] catch _:Ex -> @@ -421,12 +434,13 @@ find_commands_definitions() -> lists:flatmap(fun(P) -> Mod = list_to_atom(filename:rootname(P)), code:ensure_loaded(Mod), - case erlang:function_exported(Mod, get_commands_spec, 0) of + Cs = case erlang:function_exported(Mod, get_commands_spec, 0) of true -> apply(Mod, get_commands_spec, []); _ -> [] - end + end, + [C#ejabberd_commands{definer = Mod} || C <- Cs] end, filelib:wildcard("*.beam", Path)) end. -- cgit v1.2.3 From ab5e726176d7165a07faf896d8839892c03ad392 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 30 Jul 2021 00:58:08 +0200 Subject: Use the most specific tag for ejabberd commands with several ones --- src/ejabberd_admin.erl | 12 ++++++------ src/ext_mod.erl | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 2eeb97b99..23becc7f2 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -119,12 +119,12 @@ get_commands_spec() -> desc = "Restart ejabberd gracefully", module = init, function = restart, args = [], result = {res, rescode}}, - #ejabberd_commands{name = reopen_log, tags = [logs, server], + #ejabberd_commands{name = reopen_log, tags = [logs], desc = "Reopen the log files", policy = admin, module = ?MODULE, function = reopen_log, args = [], result = {res, rescode}}, - #ejabberd_commands{name = rotate_log, tags = [logs, server], + #ejabberd_commands{name = rotate_log, tags = [logs], desc = "Rotate the log files", module = ?MODULE, function = rotate_log, args = [], result = {res, rescode}}, @@ -139,14 +139,14 @@ get_commands_spec() -> args_example = [60, <<"Server will stop now.">>], args = [{delay, integer}, {announcement, string}], result = {res, rescode}}, - #ejabberd_commands{name = get_loglevel, tags = [logs, server], + #ejabberd_commands{name = get_loglevel, tags = [logs], desc = "Get the current loglevel", module = ejabberd_logger, function = get, result_desc = "Tuple with the log level number, its keyword and description", result_example = warning, args = [], result = {levelatom, atom}}, - #ejabberd_commands{name = set_loglevel, tags = [logs, server], + #ejabberd_commands{name = set_loglevel, tags = [logs], desc = "Set the loglevel", module = ?MODULE, function = set_loglevel, args_desc = ["Desired logging level: none | emergency | alert | critical " @@ -200,7 +200,7 @@ get_commands_spec() -> result_example = [<<"example.com">>, <<"anon.example.com">>], args = [], result = {vhosts, {list, {vhost, string}}}}, - #ejabberd_commands{name = reload_config, tags = [server, config], + #ejabberd_commands{name = reload_config, tags = [config], desc = "Reload config file in memory", module = ?MODULE, function = reload_config, args = [], @@ -268,7 +268,7 @@ get_commands_spec() -> args_example = ["/var/lib/ejabberd/", "example.com"], args = [{dir, string}, {host, string}], result = {res, rescode}}, - #ejabberd_commands{name = delete_mnesia, tags = [mnesia, sql], + #ejabberd_commands{name = delete_mnesia, tags = [mnesia], desc = "Delete elements in Mnesia database for a given vhost", module = ejd2sql, function = delete, args_desc = ["Vhost which content will be deleted in Mnesia database"], diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 5353019bb..b3e88a9cb 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -84,14 +84,14 @@ code_change(_OldVsn, State, _Extra) -> %% -- ejabberd commands get_commands_spec() -> [#ejabberd_commands{name = modules_update_specs, - tags = [admin,modules], + tags = [modules], desc = "Update the module source code from Git", longdesc = "A connection to Internet is required", module = ?MODULE, function = update, args = [], result = {res, rescode}}, #ejabberd_commands{name = modules_available, - tags = [admin,modules], + tags = [modules], desc = "List the contributed modules available to install", module = ?MODULE, function = available_command, result_desc = "List of tuples with module name and description", @@ -103,7 +103,7 @@ get_commands_spec() -> [{name, atom}, {summary, string}]}}}}}, #ejabberd_commands{name = modules_installed, - tags = [admin,modules], + tags = [modules], desc = "List the contributed modules already installed", module = ?MODULE, function = installed_command, result_desc = "List of tuples with module name and description", @@ -115,7 +115,7 @@ get_commands_spec() -> [{name, atom}, {summary, string}]}}}}}, #ejabberd_commands{name = module_install, - tags = [admin,modules], + tags = [modules], desc = "Compile, install and start an available contributed module", module = ?MODULE, function = install, args_desc = ["Module name"], @@ -123,7 +123,7 @@ get_commands_spec() -> args = [{module, binary}], result = {res, rescode}}, #ejabberd_commands{name = module_uninstall, - tags = [admin,modules], + tags = [modules], desc = "Uninstall a contributed module", module = ?MODULE, function = uninstall, args_desc = ["Module name"], @@ -131,7 +131,7 @@ get_commands_spec() -> args = [{module, binary}], result = {res, rescode}}, #ejabberd_commands{name = module_upgrade, - tags = [admin,modules], + tags = [modules], desc = "Upgrade the running code of an installed module", longdesc = "In practice, this uninstalls and installs the module", module = ?MODULE, function = upgrade, @@ -140,7 +140,7 @@ get_commands_spec() -> args = [{module, binary}], result = {res, rescode}}, #ejabberd_commands{name = module_check, - tags = [admin,modules], + tags = [modules], desc = "Check the contributed module repository compliance", module = ?MODULE, function = check, args_desc = ["Module name"], -- cgit v1.2.3 From 99ffd9bb9564768d90a20d5c8920b6abf8b7c190 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Mon, 2 Aug 2021 21:09:55 +0200 Subject: mod_pubsub: Fix check_opt_range/3 spec --- src/mod_pubsub.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index b0dca4b0d..d49a11817 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -3548,7 +3548,8 @@ decode_get_pending(#xdata{fields = Fs}, Lang) -> {error, xmpp:err_resource_constraint(Txt, Lang)} end. --spec check_opt_range(atom(), [proplists:property()], non_neg_integer()) -> boolean(). +-spec check_opt_range(atom(), [proplists:property()], + non_neg_integer() | undefined) -> boolean(). check_opt_range(_Opt, _Opts, undefined) -> true; check_opt_range(Opt, Opts, Max) -> -- cgit v1.2.3 From d7e330c8eff3bb40a301705bfab7a9510b2c6fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 4 Aug 2021 15:30:29 +0200 Subject: Allow storing non-composing x:events in offline --- src/mod_offline.erl | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/mod_offline.erl b/src/mod_offline.erl index c3fda25db..ff95767b6 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -572,6 +572,16 @@ check_event(#message{from = From, to = To, id = ID, type = Type} = Msg) -> sub_els = [#xevent{id = ID, offline = true}]}, ejabberd_router:route(NewMsg), true; + % Don't store composing events + #xevent{id = V, composing = true} when V /= undefined -> + false; + % Nor composing stopped events + #xevent{id = V, composing = false, delivered = false, + displayed = false, offline = false} when V /= undefined -> + false; + % But store other received notifications + #xevent{id = V} when V /= undefined -> + true; _ -> false end. -- cgit v1.2.3 From fdfd202a30b740ab30c014c2d459cf1c0539f1fa Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 3 Aug 2021 17:54:49 +0200 Subject: Determine the default handlerid at runtime Apparently Elixir's default is not called 'default' --- src/ejabberd_logger.erl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl index 5ace8115f..05e197b3e 100644 --- a/src/ejabberd_logger.erl +++ b/src/ejabberd_logger.erl @@ -288,10 +288,11 @@ start(Level) -> ConsoleFmtConfig = FmtConfig#{template => console_template()}, try ok = logger:set_primary_config(level, Level), - ok = logger:update_formatter_config(default, ConsoleFmtConfig), + DefaultHandlerId = get_default_handlerid(), + ok = logger:update_formatter_config(DefaultHandlerId, ConsoleFmtConfig), case quiet_mode() of true -> - ok = logger:set_handler_config(default, level, critical); + ok = logger:set_handler_config(DefaultHandlerId, level, critical); _ -> ok end, @@ -319,6 +320,13 @@ start(Level) -> Err end. +get_default_handlerid() -> + Ids = logger:get_handler_ids(), + case lists:member(default, Ids) of + true -> default; + false -> hd(Ids) + end. + -spec restart() -> ok. restart() -> ok. -- cgit v1.2.3 From 3e942bf4acac02eccba837b0f1e9ce4f6f69aff4 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 7 Aug 2021 12:57:57 +0200 Subject: mod_mam_sql: Remove duplicated functions --- src/mod_mam_sql.erl | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index e5069b9a2..269b4c963 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -72,7 +72,7 @@ remove_from_archive(LUser, LServer, WithJid) -> end. delete_old_messages(ServerHost, TimeStamp, Type) -> - TS = now_to_usec(TimeStamp), + TS = misc:now_to_usec(TimeStamp), case Type of all -> ejabberd_sql:sql_query( @@ -315,7 +315,7 @@ export(_Server) -> id = _ID, timestamp = TS, peer = Peer, type = Type, nick = Nick, packet = Pkt}) when LServer == Host -> - TStmp = now_to_usec(TS), + TStmp = misc:now_to_usec(TS), SUser = case Type of chat -> LUser; groupchat -> jid:encode({LUser, LServer, <<>>}) @@ -372,16 +372,6 @@ is_empty_for_room(LServer, LName, LHost) -> %%%=================================================================== %%% Internal functions %%%=================================================================== -now_to_usec({MSec, Sec, USec}) -> - (MSec*1000000 + Sec)*1000000 + USec. - -usec_to_now(Int) -> - Secs = Int div 1000000, - USec = Int rem 1000000, - MSec = Secs div 1000000, - Sec = Secs rem 1000000, - {MSec, Sec, USec}. - make_sql_query(User, LServer, MAMQuery, RSM, ExtraUsernames) -> Start = proplists:get_value(start, MAMQuery), End = proplists:get_value('end', MAMQuery), @@ -432,14 +422,14 @@ make_sql_query(User, LServer, MAMQuery, RSM, ExtraUsernames) -> StartClause = case Start of {_, _, _} -> [<<" and timestamp >= ">>, - integer_to_binary(now_to_usec(Start))]; + integer_to_binary(misc:now_to_usec(Start))]; _ -> [] end, EndClause = case End of {_, _, _} -> [<<" and timestamp <= ">>, - integer_to_binary(now_to_usec(End))]; + integer_to_binary(misc:now_to_usec(End))]; _ -> [] end, @@ -526,7 +516,7 @@ make_archive_el(User, TS, XML, Peer, Kind, Nick, MsgType, JidRequestor, JidArchi TSInt -> try jid:decode(Peer) of PeerJID -> - Now = usec_to_now(TSInt), + Now = misc:usec_to_now(TSInt), PeerLJID = jid:tolower(PeerJID), T = case Kind of <<"">> -> chat; -- cgit v1.2.3 From 8f8de0403bab0a819420db69bb44bdb2a474d137 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 20 Aug 2021 20:30:11 +0200 Subject: PubSub: Support 'max_items=max' node configuration Let clients request the maximum limit for the node configuration option 'max_items' by specifying the special value 'max' instead of an integer. This was added to XEP-0060, revision 1.17.0 (and clarified in revision 1.20.0). Thanks to Ammonit Measurement GmbH for sponsoring this work. --- src/mod_pubsub.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index d49a11817..49a54c9b4 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -3553,8 +3553,10 @@ decode_get_pending(#xdata{fields = Fs}, Lang) -> check_opt_range(_Opt, _Opts, undefined) -> true; check_opt_range(Opt, Opts, Max) -> - Val = proplists:get_value(Opt, Opts, Max), - Val =< Max. + case proplists:get_value(Opt, Opts, Max) of + max -> true; + Val -> Val =< Max + end. -spec get_max_items_node(host()) -> undefined | non_neg_integer(). get_max_items_node(Host) -> @@ -3708,6 +3710,7 @@ features() -> <<"access-whitelist">>, % OPTIONAL <<"collections">>, % RECOMMENDED <<"config-node">>, % RECOMMENDED + <<"config-node-max">>, <<"create-and-configure">>, % RECOMMENDED <<"item-ids">>, % RECOMMENDED <<"last-published">>, % RECOMMENDED -- cgit v1.2.3 From 1b0e59bb139f83d1f027ece08606446c30d35ae5 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 21 Aug 2021 12:29:37 +0200 Subject: PubSub: Support unlimited number of items Allow for setting the mod_pubsub option 'max_items_node' to 'unlimited'. If clients then request a 'max_items' limit of 'max', old items aren't deleted when publishing new ones. Thanks to Ammonit Measurement GmbH for sponsoring this work. --- src/mod_pubsub.erl | 14 ++++++++------ src/node_flat.erl | 3 ++- src/node_flat_sql.erl | 3 ++- 3 files changed, 12 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 49a54c9b4..c6f485077 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -210,7 +210,7 @@ pep_mapping :: [{binary(), binary()}], ignore_pep_from_offline :: boolean(), last_item_cache :: boolean(), - max_items_node :: non_neg_integer(), + max_items_node :: non_neg_integer()|unlimited, max_subscriptions_node :: non_neg_integer()|undefined, default_node_config :: [{atom(), binary()|boolean()|integer()|atom()}], nodetree :: binary(), @@ -3399,7 +3399,7 @@ node_config(_, _, []) -> %% @doc

Return the maximum number of items for a given node.

%%

Unlimited means that there is no limit in the number of items that can %% be stored.

--spec max_items(host(), [{atom(), any()}]) -> non_neg_integer(). +-spec max_items(host(), [{atom(), any()}]) -> non_neg_integer() | unlimited. max_items(Host, Options) -> case get_option(Options, persist_items) of true -> @@ -3549,16 +3549,18 @@ decode_get_pending(#xdata{fields = Fs}, Lang) -> end. -spec check_opt_range(atom(), [proplists:property()], - non_neg_integer() | undefined) -> boolean(). + non_neg_integer() | unlimited | undefined) -> boolean(). check_opt_range(_Opt, _Opts, undefined) -> true; +check_opt_range(_Opt, _Opts, unlimited) -> + true; check_opt_range(Opt, Opts, Max) -> case proplists:get_value(Opt, Opts, Max) of max -> true; Val -> Val =< Max end. --spec get_max_items_node(host()) -> undefined | non_neg_integer(). +-spec get_max_items_node(host()) -> undefined | unlimited | non_neg_integer(). get_max_items_node(Host) -> config(Host, max_items_node, undefined). @@ -4150,7 +4152,7 @@ mod_opt_type(ignore_pep_from_offline) -> mod_opt_type(last_item_cache) -> econf:bool(); mod_opt_type(max_items_node) -> - econf:non_neg_int(); + econf:non_neg_int(unlimited); mod_opt_type(max_nodes_discoitems) -> econf:non_neg_int(infinity); mod_opt_type(max_subscriptions_node) -> @@ -4277,7 +4279,7 @@ mod_doc() -> "and allows to raise user connection rate. The cost " "is memory usage, as every item is stored in memory.")}}, {max_items_node, - #{value => "MaxItems", + #{value => "non_neg_integer() | infinity", desc => ?T("Define the maximum number of items that can be " "stored in a node. Default value is: '10'.")}}, diff --git a/src/node_flat.erl b/src/node_flat.erl index 4a2a60971..fe08be258 100644 --- a/src/node_flat.erl +++ b/src/node_flat.erl @@ -375,7 +375,8 @@ publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload, or (Subscribed == true)) -> {error, xmpp:err_forbidden()}; true -> - if MaxItems > 0 -> + if MaxItems > 0; + MaxItems == unlimited -> Now = erlang:timestamp(), case get_item(Nidx, ItemId) of {result, #pubsub_item{creation = {_, GenKey}} = OldItem} -> diff --git a/src/node_flat_sql.erl b/src/node_flat_sql.erl index 1e197a51d..1309c2886 100644 --- a/src/node_flat_sql.erl +++ b/src/node_flat_sql.erl @@ -247,7 +247,8 @@ publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload, or (Subscribed == true)) -> {error, xmpp:err_forbidden()}; true -> - if MaxItems > 0 -> + if MaxItems > 0; + MaxItems == unlimited -> Now = erlang:timestamp(), case get_item(Nidx, ItemId) of {result, #pubsub_item{creation = {_, GenKey}} = OldItem} -> -- cgit v1.2.3 From 29751a6174d181f80bbcde7c92f8cc958eee85a1 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 21 Aug 2021 20:02:58 +0200 Subject: PubSub: Optimize publishing on large nodes (SQL) Avoid an unnecessary SQL query while publishing an item on a PubSub node without 'max_items' limit. The query in question can be expensive if the node has a large number of items. Thanks to Ammonit Measurement GmbH for sponsoring this work. --- src/node_flat_sql.erl | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/node_flat_sql.erl b/src/node_flat_sql.erl index 1309c2886..5033eda51 100644 --- a/src/node_flat_sql.erl +++ b/src/node_flat_sql.erl @@ -259,14 +259,14 @@ publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload, {result, _} -> {error, xmpp:err_forbidden()}; _ -> - Items = [ItemId | itemids(Nidx, GenKey)], - {result, {_NI, OI}} = remove_extra_items(Nidx, MaxItems, Items), + OldIds = maybe_remove_extra_items(Nidx, MaxItems, + GenKey, ItemId), set_item(#pubsub_item{ itemid = {ItemId, Nidx}, creation = {Now, GenKey}, modification = {Now, SubKey}, payload = Payload}), - {result, {default, broadcast, OI}} + {result, {default, broadcast, OldIds}} end; true -> {result, {default, broadcast, []}} @@ -934,6 +934,16 @@ update_subscription(Nidx, JID, Subscription) -> "-affiliation='n'" ]). +-spec maybe_remove_extra_items(mod_pubsub:nodeIdx(), + non_neg_integer() | unlimited, ljid(), + mod_pubsub:itemId()) -> [mod_pubsub:itemId()]. +maybe_remove_extra_items(_Nidx, unlimited, _GenKey, _ItemId) -> + []; +maybe_remove_extra_items(Nidx, MaxItems, GenKey, ItemId) -> + ItemIds = [ItemId | itemids(Nidx, GenKey)], + {result, {_NewIds, OldIds}} = remove_extra_items(Nidx, MaxItems, ItemIds), + OldIds. + -spec decode_jid(SJID :: binary()) -> ljid(). decode_jid(SJID) -> jid:tolower(jid:decode(SJID)). -- cgit v1.2.3 From 8d5025076f53abc0474c86a0f879ff1736714844 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 22 Aug 2021 12:44:50 +0200 Subject: PubSub: Add delete_old_pubsub_items command Add a command for keeping only the specified number of items on each node and removing all older items. This might be especially useful if nodes may be configured to have no 'max_items' limit. Thanks to Ammonit Measurement GmbH for sponsoring this work. --- src/gen_pubsub_node.erl | 5 +++++ src/gen_pubsub_nodetree.erl | 3 +++ src/mod_pubsub.erl | 53 ++++++++++++++++++++++++++++++++++++++++++++- src/node_flat.erl | 13 ++++++++++- src/node_flat_sql.erl | 22 ++++++++++++++++--- src/node_pep.erl | 6 ++++- src/node_pep_sql.erl | 6 ++++- src/nodetree_tree.erl | 11 +++++++++- src/nodetree_tree_sql.erl | 31 +++++++++++++++++++++++++- src/nodetree_virtual.erl | 6 ++++- 10 files changed, 146 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/gen_pubsub_node.erl b/src/gen_pubsub_node.erl index 5bdebdfc6..625e490fc 100644 --- a/src/gen_pubsub_node.erl +++ b/src/gen_pubsub_node.erl @@ -122,6 +122,11 @@ {result, {default, broadcast}} | {error, stanza_error()}. +-callback remove_extra_items(NodeIdx :: nodeIdx(), + Max_Items :: unlimited | non_neg_integer()) -> + {result, {[itemId()], [itemId()]} + }. + -callback remove_extra_items(NodeIdx :: nodeIdx(), Max_Items :: unlimited | non_neg_integer(), ItemIds :: [itemId()]) -> diff --git a/src/gen_pubsub_nodetree.erl b/src/gen_pubsub_nodetree.erl index 5a24db2c4..b6b73b8cb 100644 --- a/src/gen_pubsub_nodetree.erl +++ b/src/gen_pubsub_nodetree.erl @@ -67,6 +67,9 @@ -callback get_nodes(Host :: host())-> [pubsubNode()]. +-callback get_all_nodes(Host :: host()) -> + [pubsubNode()]. + -callback get_parentnodes(Host :: host(), NodeId :: nodeId(), From :: jid:jid()) -> diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index c6f485077..8792b2ab9 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -45,6 +45,7 @@ -include("mod_roster.hrl"). -include("translate.hrl"). -include("ejabberd_stacktrace.hrl"). +-include("ejabberd_commands.hrl"). -define(STDTREE, <<"tree">>). -define(STDNODE, <<"flat">>). @@ -93,6 +94,9 @@ handle_call/3, handle_cast/2, handle_info/2, mod_doc/0, terminate/2, code_change/3, depends/2, mod_opt_type/1, mod_options/1]). +%% ejabberd commands +-export([get_commands_spec/0, delete_old_items/1]). + -export([route/1]). %%==================================================================== @@ -337,6 +341,7 @@ init([ServerHost|_]) -> false -> ok end, + ejabberd_commands:register_commands(?MODULE, get_commands_spec()), NodeTree = config(ServerHost, nodetree), Plugins = config(ServerHost, plugins), PepMapping = config(ServerHost, pep_mapping), @@ -806,7 +811,13 @@ terminate(_Reason, gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_COMMANDS), terminate_plugins(Host, ServerHost, Plugins, TreePlugin), ejabberd_router:unregister_route(Host) - end, Hosts). + end, Hosts), + case gen_mod:is_loaded_elsewhere(ServerHost, ?MODULE) of + false -> + ejabberd_commands:unregister_commands(get_commands_spec()); + true -> + ok + end. %%-------------------------------------------------------------------- %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} @@ -4142,6 +4153,46 @@ purge_offline(Host, LJID, Node) -> {error, xmpp:err_internal_server_error(Txt, Lang)} end. +-spec delete_old_items(non_neg_integer()) -> ok | error. +delete_old_items(N) -> + Results = lists:flatmap( + fun(Host) -> + case tree_action(Host, get_all_nodes, [Host]) of + Nodes when is_list(Nodes) -> + lists:map( + fun(#pubsub_node{id = Nidx, type = Type}) -> + case node_action(Host, Type, + remove_extra_items, + [Nidx , N]) of + {result, _} -> + ok; + {error, _} -> + error + end + end, Nodes); + _ -> + error + end + end, ejabberd_option:hosts()), + case lists:member(error, Results) of + true -> + error; + false -> + ok + end. + +-spec get_commands_spec() -> [ejabberd_commands()]. +get_commands_spec() -> + [#ejabberd_commands{name = delete_old_pubsub_items, tags = [purge], + desc = "Keep only NUMBER of PubSub items per node", + module = ?MODULE, function = delete_old_items, + args_desc = ["Number of items to keep per node"], + args = [{number, integer}], + result = {res, rescode}, + result_desc = "0 if command failed, 1 when succeeded", + args_example = [1000], + result_example = ok}]. + -spec mod_opt_type(atom()) -> econf:validator(). mod_opt_type(access_createnode) -> econf:acl(); diff --git a/src/node_flat.erl b/src/node_flat.erl index fe08be258..97f149f9c 100644 --- a/src/node_flat.erl +++ b/src/node_flat.erl @@ -39,7 +39,8 @@ -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, - publish_item/7, delete_item/4, remove_extra_items/3, + publish_item/7, delete_item/4, + remove_extra_items/2, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, @@ -403,6 +404,16 @@ publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload, end end. +remove_extra_items(Nidx, MaxItems) -> + {result, States} = get_states(Nidx), + Records = States ++ mnesia:read({pubsub_orphan, Nidx}), + ItemIds = lists:flatmap(fun(#pubsub_state{items = Is}) -> + Is; + (#pubsub_orphan{items = Is}) -> + Is + end, Records), + remove_extra_items(Nidx, MaxItems, ItemIds). + %% @doc

This function is used to remove extra items, most notably when the %% maximum number of items has been reached.

%%

This function is used internally by the core PubSub module, as no diff --git a/src/node_flat_sql.erl b/src/node_flat_sql.erl index 5033eda51..724958eb1 100644 --- a/src/node_flat_sql.erl +++ b/src/node_flat_sql.erl @@ -40,9 +40,10 @@ -include("translate.hrl"). -export([init/3, terminate/2, options/0, features/0, - create_node_permission/6, create_node/2, delete_node/1, - purge_node/2, subscribe_node/8, unsubscribe_node/4, - publish_item/7, delete_item/4, remove_extra_items/3, + create_node_permission/6, create_node/2, delete_node/1, purge_node/2, + subscribe_node/8, unsubscribe_node/4, + publish_item/7, delete_item/4, + remove_extra_items/2, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, @@ -273,6 +274,9 @@ publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload, end end. +remove_extra_items(Nidx, MaxItems) -> + remove_extra_items(Nidx, MaxItems, itemids(Nidx)). + remove_extra_items(_Nidx, unlimited, ItemIds) -> {result, {ItemIds, []}}; remove_extra_items(Nidx, MaxItems, ItemIds) -> @@ -863,6 +867,18 @@ first_in_list(Pred, [H | T]) -> _ -> first_in_list(Pred, T) end. +itemids(Nidx) -> + case catch + ejabberd_sql:sql_query_t( + ?SQL("select @(itemid)s from pubsub_item where " + "nodeid=%(Nidx)d order by modification desc")) + of + {selected, RItems} -> + [ItemId || {ItemId} <- RItems]; + _ -> + [] + end. + itemids(Nidx, {_U, _S, _R} = JID) -> SJID = encode_jid(JID), SJIDLike = <<(encode_jid_like(JID))/binary, "/%">>, diff --git a/src/node_pep.erl b/src/node_pep.erl index 58c3050a0..44388ca31 100644 --- a/src/node_pep.erl +++ b/src/node_pep.erl @@ -35,7 +35,8 @@ -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, - publish_item/7, delete_item/4, remove_extra_items/3, + publish_item/7, delete_item/4, + remove_extra_items/2, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, @@ -135,6 +136,9 @@ publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) -> node_flat:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts). +remove_extra_items(Nidx, MaxItems) -> + node_flat:remove_extra_items(Nidx, MaxItems). + remove_extra_items(Nidx, MaxItems, ItemIds) -> node_flat:remove_extra_items(Nidx, MaxItems, ItemIds). diff --git a/src/node_pep_sql.erl b/src/node_pep_sql.erl index 7b21aa901..c0cf2b166 100644 --- a/src/node_pep_sql.erl +++ b/src/node_pep_sql.erl @@ -37,7 +37,8 @@ -export([init/3, terminate/2, options/0, features/0, create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, - publish_item/7, delete_item/4, remove_extra_items/3, + publish_item/7, delete_item/4, + remove_extra_items/2, remove_extra_items/3, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, @@ -92,6 +93,9 @@ publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) -> node_flat_sql:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts). +remove_extra_items(Nidx, MaxItems) -> + node_flat_sql:remove_extra_items(Nidx, MaxItems). + remove_extra_items(Nidx, MaxItems, ItemIds) -> node_flat_sql:remove_extra_items(Nidx, MaxItems, ItemIds). diff --git a/src/nodetree_tree.erl b/src/nodetree_tree.erl index fe15f3323..853c1fb93 100644 --- a/src/nodetree_tree.erl +++ b/src/nodetree_tree.erl @@ -46,7 +46,8 @@ -export([init/3, terminate/2, options/0, set_node/1, get_node/3, get_node/2, get_node/1, get_nodes/2, - get_nodes/1, get_parentnodes/3, get_parentnodes_tree/3, + get_nodes/1, get_all_nodes/1, + get_parentnodes/3, get_parentnodes_tree/3, get_subnodes/3, get_subnodes_tree/3, create_node/6, delete_node/2]). @@ -98,6 +99,14 @@ get_nodes(Host, Limit) -> {Nodes, _} -> Nodes end. +get_all_nodes({_U, _S, _R} = Owner) -> + Host = jid:tolower(jid:remove_resource(Owner)), + mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'}); +get_all_nodes(Host) -> + mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'}) + ++ mnesia:match_object(#pubsub_node{nodeid = {{'_', Host, '_'}, '_'}, + _ = '_'}). + get_parentnodes(Host, Node, _From) -> case catch mnesia:read({pubsub_node, {Host, Node}}) of [Record] when is_record(Record, pubsub_node) -> diff --git a/src/nodetree_tree_sql.erl b/src/nodetree_tree_sql.erl index d68355202..402c50901 100644 --- a/src/nodetree_tree_sql.erl +++ b/src/nodetree_tree_sql.erl @@ -45,7 +45,8 @@ -export([init/3, terminate/2, options/0, set_node/1, get_node/3, get_node/2, get_node/1, get_nodes/2, - get_nodes/1, get_parentnodes/3, get_parentnodes_tree/3, + get_nodes/1, get_all_nodes/1, + get_parentnodes/3, get_parentnodes_tree/3, get_subnodes/3, get_subnodes_tree/3, create_node/6, delete_node/2]). @@ -165,6 +166,34 @@ get_nodes(Host, Limit) -> [] end. +get_all_nodes({_U, _S, _R} = JID) -> + SubKey = jid:tolower(JID), + GenKey = jid:remove_resource(SubKey), + EncKey = node_flat_sql:encode_jid(GenKey), + Pattern = <<(node_flat_sql:encode_jid_like(GenKey))/binary, "/%">>, + case ejabberd_sql:sql_query_t( + ?SQL("select @(node)s, @(parent)s, @(plugin)s, @(nodeid)d " + "from pubsub_node where host=%(EncKey)s " + "or host like %(Pattern)s %ESCAPE")) of + {selected, RItems} -> + [raw_to_node(GenKey, Item) || Item <- RItems]; + _ -> + [] + end; +get_all_nodes(Host) -> + Pattern1 = <<"%@", Host/binary>>, + Pattern2 = <<"%@", Host/binary, "/%">>, + case ejabberd_sql:sql_query_t( + ?SQL("select @(node)s, @(parent)s, @(plugin)s, @(nodeid)d " + "from pubsub_node where host=%(Host)s " + "or host like %(Pattern1)s " + "or host like %(Pattern2)s %ESCAPE")) of + {selected, RItems} -> + [raw_to_node(Host, Item) || Item <- RItems]; + _ -> + [] + end. + get_parentnodes(Host, Node, _From) -> case get_node(Host, Node) of Record when is_record(Record, pubsub_node) -> diff --git a/src/nodetree_virtual.erl b/src/nodetree_virtual.erl index 9cf7a80ca..c0274a795 100644 --- a/src/nodetree_virtual.erl +++ b/src/nodetree_virtual.erl @@ -38,7 +38,8 @@ -export([init/3, terminate/2, options/0, set_node/1, get_node/3, get_node/2, get_node/1, get_nodes/2, - get_nodes/1, get_parentnodes/3, get_parentnodes_tree/3, + get_nodes/1, get_all_nodes/1, + get_parentnodes/3, get_parentnodes_tree/3, get_subnodes/3, get_subnodes_tree/3, create_node/6, delete_node/2]). @@ -71,6 +72,9 @@ get_nodes(Host) -> get_nodes(_Host, _Limit) -> []. +get_all_nodes(_Host) -> + []. + get_parentnodes(_Host, _Node, _From) -> []. -- cgit v1.2.3 From 8af66b08316abc8efe2f549d202ded5aee703f94 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 18 Aug 2021 16:40:13 +0200 Subject: Update API Reference page menu name and order --- src/ejabberd_commands_doc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index 582908723..f63326182 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -480,7 +480,7 @@ generate_md_output(File, RegExp, Languages) -> end, Cmds2), Cmds4 = [maybe_add_policy_arguments(Cmd) || Cmd <- Cmds3], Langs = binary:split(Languages, <<",">>, [global]), - Header = <<"---\ntitle: Administration API reference\ntoc: true\nmenu: Administration API\norder: 40\n" + Header = <<"---\ntitle: Administration API reference\ntoc: true\nmenu: API Reference\norder: 1\n" "// Autogenerated with 'ejabberdctl gen_markdown_doc_for_commands'\n---\n\n" "This section describes API of ejabberd.">>, Out = lists:map(fun(C) -> gen_doc(C, false, Langs) end, Cmds4), -- cgit v1.2.3 From b7f7713faefeab6f45994c0c940497b1c3ee7619 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 18 Aug 2021 13:39:17 +0200 Subject: Add example config to mod_http_api documentation --- src/mod_http_api.erl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 0a3942024..0f494bb3a 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -533,4 +533,14 @@ mod_doc() -> "in the request_handlers, add a 'vN'. " "For example: '/api/v2: mod_http_api'"), "", ?T("To run a command, send a POST request to the corresponding " - "URL: 'http://localhost:5280/api/'")]}. + "URL: 'http://localhost:5280/api/'")], + example => + ["listen:", + " -", + " port: 5280", + " module: ejabberd_http", + " request_handlers:", + " /api: mod_http_api", + "", + "modules:", + " mod_http_api: {}"]}. -- cgit v1.2.3 From 69d362595e5aab86383f0596a0613af233358d31 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 18 Aug 2021 13:40:09 +0200 Subject: Remove obsolete mod_register_web ideas and improve documentation --- src/mod_register_web.erl | 56 +++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/mod_register_web.erl b/src/mod_register_web.erl index 9c2179302..28bac0558 100644 --- a/src/mod_register_web.erl +++ b/src/mod_register_web.erl @@ -23,32 +23,6 @@ %%% %%%---------------------------------------------------------------------- -%%% IDEAS: -%%% -%%% * Implement those options, already present in mod_register: -%%% + access -%%% + captcha_protected -%%% + password_strength -%%% + welcome_message -%%% + registration_timeout -%%% -%%% * Improve this module to allow each virtual host to have different -%%% options. See http://support.process-one.net/browse/EJAB-561 -%%% -%%% * Check that all the text is translatable. -%%% -%%% * Add option to use a custom CSS file, or custom CSS lines. -%%% -%%% * Don't hardcode the "register" path in URL. -%%% -%%% * Allow private email during register, and store in custom table. -%%% * Optionally require private email to register. -%%% * Optionally require email confirmation to register. -%%% * Allow to set a private email address anytime. -%%% * Allow to recover password using private email to confirm (mod_passrecover) -%%% * Optionally require invitation -%%% * Optionally register request is forwarded to admin, no account created. - -module(mod_register_web). -author('badlop@process-one.net'). @@ -625,13 +599,27 @@ mod_doc() -> ?T("- Register a new account on the server."), "", ?T("- Change the password from an existing account on the server."), "", ?T("- Unregister an existing account on the server."), "", - ?T("This module supports CAPTCHA image to register a new account. " - "To enable this feature, configure the options 'captcha\_cmd' " - "and 'captcha\_url', which are documented in the section with " - "top-level options."), "", - ?T("As an example usage, the users of the host 'example.org' can " - "visit the page: 'https://example.org:5281/register/' It is " + ?T("This module supports http://../basic/#captcha[CAPTCHA] " + "to register a new account. " + "To enable this feature, configure the " + "top-level _`captcha_cmd`_ and " + "top-level _`captcha_url`_ options."), "", + ?T("As an example usage, the users of the host 'localhost' can " + "visit the page: 'https://localhost:5280/register/' It is " "important to include the last / character in the URL, " "otherwise the subpages URL will be incorrect."), "", - ?T("The module depends on 'mod_register' where all the configuration " - "is performed.")]}. + ?T("This module is enabled in 'listen' -> 'ejabberd_http' -> " + "http://../listen-options/#request-handlers[request_handlers], " + "no need to enable in 'modules'."), + ?T("The module depends on _`mod_register`_ where all the " + "configuration is performed.")], + example => + ["listen:", + " -", + " port: 5280", + " module: ejabberd_http", + " request_handlers:", + " /register: mod_register_web", + "", + "modules:", + " mod_register: {}"]}. -- cgit v1.2.3 From 94fb0a65b0af8a26c21c08d2e43d3528a4bffdc6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 21 Aug 2021 23:20:35 +0200 Subject: Change set_master command tag from mnesia to cluster --- src/ejabberd_admin.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 23becc7f2..9cebd0bb5 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -326,7 +326,7 @@ get_commands_spec() -> args_example = ["example.com", "/var/lib/ejabberd/example.com.sql"], args = [{host, string}, {file, string}], result = {res, rescode}}, - #ejabberd_commands{name = set_master, tags = [mnesia], + #ejabberd_commands{name = set_master, tags = [cluster], desc = "Set master node of the clustered Mnesia tables", longdesc = "If you provide as nodename \"self\", this " "node will be set as its own master.", -- cgit v1.2.3 From 9446b251fd0aa6b858ad35bf779841659252f2d0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 23 Aug 2021 11:51:01 +0200 Subject: Export function, so ACME API commands are listed in the documentation --- src/ejabberd_acme.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ejabberd_acme.erl b/src/ejabberd_acme.erl index 42f17baa1..93ce3c815 100644 --- a/src/ejabberd_acme.erl +++ b/src/ejabberd_acme.erl @@ -27,7 +27,8 @@ %% Hooks -export([ejabberd_started/0, register_certfiles/0, cert_expired/2]). %% ejabberd commands --export([request_certificate/1, revoke_certificate/1, list_certificates/0]). +-export([get_commands_spec/0, request_certificate/1, + revoke_certificate/1, list_certificates/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, format_status/2]). -- cgit v1.2.3 From f5038b86f83d811e33c81e3282f9469c9f370dd9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 23 Aug 2021 12:48:06 +0200 Subject: Copy log_rotate_count explanation from docs site --- src/ejabberd_options_doc.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 64eb850d8..8f6ab45b1 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -799,7 +799,8 @@ doc() -> #{value => ?T("Number"), desc => ?T("The number of rotated log files to keep. " - "The default value is '1'.")}}, + "The default value is '1', which means that only keeps " + "`ejabberd.log.0`, `error.log.0` and `crash.log.0`.")}}, {log_rotate_size, #{value => "pos_integer() | infinity", desc => -- cgit v1.2.3 From 30ae66e99e4a7499886436b195311ff012f09fda Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 23 Aug 2021 13:40:19 +0200 Subject: Improve formatting and add sections links --- src/ejabberd_options_doc.erl | 30 +++++++++++++++++------------- src/mod_admin_update_sql.erl | 2 +- src/mod_http_api.erl | 4 ++-- src/mod_http_upload.erl | 5 +++-- src/mod_mqtt.erl | 5 +++-- src/mod_register.erl | 11 +++++------ 6 files changed, 31 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 8f6ab45b1..ee1cf9add 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -313,10 +313,12 @@ doc() -> {anonymous_protocol, #{value => "login_anon | sasl_anon | both", desc => - ?T("'login_anon' means that the anonymous login method will be used. " - "'sasl_anon' means that the SASL Anonymous method will be used. " - "'both' means that SASL Anonymous and login anonymous are both " - "enabled. The default value is 'sasl_anon'.")}}, + [?T("Define what anonymous protocol will be used: "), "", + ?T("* 'login_anon' means that the anonymous login method will be used. "), "", + ?T("* 'sasl_anon' means that the SASL Anonymous method will be used. "), "", + ?T("* 'both' means that SASL Anonymous and login anonymous are both " + "enabled."), "", + ?T("The default value is 'sasl_anon'."), ""]}}, {api_permissions, #{value => "[Permission, ...]", desc => @@ -359,26 +361,28 @@ doc() -> desc => ?T("This is used by the contributed module " "'ejabberd_auth_http' that can be installed from the " - "'ejabberd-contrib' Git repository. Please refer to that " + "https://github.com/processone/ejabberd-contrib[ejabberd-contrib] " + "Git repository. Please refer to that " "module's README file for details.")}}, {auth_password_format, #{value => "plain | scram", note => "improved in 20.01", desc => - ?T("The option defines in what format the users passwords " - "are stored. 'plain': The password is stored as plain text " + [?T("The option defines in what format the users passwords " + "are stored:"), "", + ?T("* 'plain': The password is stored as plain text " "in the database. This is risky because the passwords " "can be read if your database gets compromised. " "This is the default value. This format allows clients to " "authenticate using: the old Jabber Non-SASL (XEP-0078), " - "SASL PLAIN, SASL DIGEST-MD5, and SASL SCRAM-SHA-1. " - "'scram': The password is not stored, only some information " + "SASL PLAIN, SASL DIGEST-MD5, and SASL SCRAM-SHA-1. "), "", + ?T("* 'scram': The password is not stored, only some information " "that allows to verify the hash provided by the client. " "It is impossible to obtain the original plain password " "from the stored information; for this reason, when this " "value is configured it cannot be changed to plain anymore. " "This format allows clients to authenticate using: " - "SASL PLAIN and SASL SCRAM-SHA-1.")}}, + "SASL PLAIN and SASL SCRAM-SHA-1.")]}}, {auth_scram_hash, #{value => "sha | sha256 | sha512", desc => @@ -449,13 +453,13 @@ doc() -> {captcha_cmd, #{value => ?T("Path"), desc => - ?T("Full path to a script that generates CAPTCHA images. " + ?T("Full path to a script that generates http://../basic/#captcha[CAPTCHA] images. " "There is no default value: when this option is not " "set, CAPTCHA functionality is completely disabled.")}}, {captcha_limit, #{value => "pos_integer() | infinity", desc => - ?T("Maximum number of CAPTCHA generated images per minute for " + ?T("Maximum number of http://../basic/#captcha[CAPTCHA] generated images per minute for " "any given JID. The option is intended to protect the server " "from CAPTCHA DoS. The default value is 'infinity'.")}}, {captcha_host, @@ -464,7 +468,7 @@ doc() -> {captcha_url, #{value => ?T("URL"), desc => - ?T("An URL where CAPTCHA requests should be sent. NOTE: you need " + ?T("An URL where http://../basic/#captcha[CAPTCHA] requests should be sent. NOTE: you need " "to configure 'request_handlers' for 'ejabberd_http' listener " "as well. There is no default value.")}}, {certfiles, diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index eae481385..c87849748 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -364,6 +364,6 @@ mod_doc() -> #{desc => ?T("This module can be used to update existing SQL database " "from the default to the new schema. Check the section " - "http://../database-ldap/#default-and-new-schemas[Default and New Schemas] for details. " + "http://../database/#default-and-new-schemas[Default and New Schemas] for details. " "Please note that only PostgreSQL is supported. " "When the module is loaded use 'update_sql' ejabberdctl command.")}. diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 0f494bb3a..427833584 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -527,8 +527,8 @@ mod_doc() -> [?T("This module provides a ReST API to call ejabberd commands " "using JSON data."), "", ?T("To use this module, in addition to adding it to the 'modules' " - "section, you must also add it to 'request_handlers' of some " - "listener."), "", + "section, you must also enable it in 'listen' -> 'ejabberd_http' -> " + "http://../listen-options/#request-handlers[request_handlers]."), "", ?T("To use a specific API version N, when defining the URL path " "in the request_handlers, add a 'vN'. " "For example: '/api/v2: mod_http_api'"), "", diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index 0cc8d8488..040cb6085 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -232,8 +232,9 @@ mod_doc() -> "[XEP-0363: HTTP File Upload]. If the request is accepted, " "the client receives a URL for uploading the file and " "another URL from which that file can later be downloaded."), "", - ?T("In order to use this module, it must be configured as " - "a 'request_handler' for 'ejabberd_http' listener.")], + ?T("In order to use this module, it must be enabled " + "in 'listen' -> 'ejabberd_http' -> " + "http://../listen-options/#request-handlers[request_handlers].")], opts => [{host, #{desc => ?T("Deprecated. Use 'hosts' instead.")}}, diff --git a/src/mod_mqtt.erl b/src/mod_mqtt.erl index 8734c778d..a1621cc01 100644 --- a/src/mod_mqtt.erl +++ b/src/mod_mqtt.erl @@ -278,8 +278,9 @@ listen_options() -> %%%=================================================================== mod_doc() -> #{desc => - ?T("This module adds support for the MQTT protocol " - "version '3.1.1' and '5.0'. Remember to configure " + ?T("This module adds " + "https://docs.ejabberd.im/admin/guide/mqtt/[support for the MQTT] " + "protocol version '3.1.1' and '5.0'. Remember to configure " "'mod_mqtt' in 'modules' and 'listen' sections."), opts => [{access_subscribe, diff --git a/src/mod_register.erl b/src/mod_register.erl index a864c0df2..919440b23 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -638,9 +638,9 @@ mod_doc() -> ?T("* Register a new account on the server."), "", ?T("* Change the password from an existing account on the server."), "", ?T("* Delete an existing account on the server."), "", - ?T("This module reads also another option defined globally for the " - "server: 'registration_timeout'. Please check that option " - "documentation in the section with top-level options.")], + ?T("This module reads also the top-level _`registration_timeout`_ " + "option defined globally for the server, " + "so please check that option documentation too.")], opts => [{access, #{value => ?T("AccessName"), @@ -664,9 +664,8 @@ mod_doc() -> {captcha_protected, #{value => "true | false", desc => - ?T("Protect registrations with CAPTCHA (see section " - "https://docs.ejabberd.im/admin/configuration/basic/#captcha[CAPTCHA] " - "of the Configuration Guide). The default is 'false'.")}}, + ?T("Protect registrations with http://../basic/#captcha[CAPTCHA]. " + "The default is 'false'.")}}, {ip_access, #{value => ?T("AccessName"), desc => -- cgit v1.2.3 From 506e2f3b972a7e1b6d4fdd40836b9574c4937908 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 21 Aug 2021 18:31:21 +0200 Subject: Use specific syntax so modules and top-level will be links If we use _`whatever`_ here in ejabberd man pages, it is converted to *`whatever`* in markdown, and docs.ejabberd.im/Makefile converts to the proper links --- src/ejabberd_options_doc.erl | 142 ++++++++++++++++++++--------------------- src/mod_admin_update_sql.erl | 2 +- src/mod_announce.erl | 14 ++-- src/mod_avatar.erl | 4 +- src/mod_bosh.erl | 12 ++-- src/mod_caps.erl | 12 ++-- src/mod_configure.erl | 2 +- src/mod_delegation.erl | 2 +- src/mod_http_upload.erl | 2 +- src/mod_last.erl | 10 +-- src/mod_mam.erl | 14 ++-- src/mod_mix.erl | 6 +- src/mod_mix_pam.erl | 10 +-- src/mod_mqtt.erl | 14 ++-- src/mod_muc.erl | 4 +- src/mod_muc_admin.erl | 2 +- src/mod_muc_log.erl | 2 +- src/mod_offline.erl | 8 +-- src/mod_ping.erl | 4 +- src/mod_privacy.erl | 12 ++-- src/mod_private.erl | 10 +-- src/mod_privilege.erl | 2 +- src/mod_pubsub.erl | 4 +- src/mod_push.erl | 10 +-- src/mod_push_keepalive.erl | 8 +-- src/mod_roster.erl | 14 ++-- src/mod_shared_roster.erl | 14 ++-- src/mod_shared_roster_ldap.erl | 2 +- src/mod_stream_mgmt.erl | 6 +- src/mod_vcard.erl | 10 +-- src/mod_vcard_ldap.erl | 2 +- src/mod_vcard_xupdate.erl | 12 ++-- 32 files changed, 186 insertions(+), 186 deletions(-) (limited to 'src') diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index ee1cf9add..28a6f6333 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -64,8 +64,8 @@ doc() -> "erased from cache. The default value is 'one hour'. " "Several modules have a similar option; and some core " "ejabberd parts support similar options too, see " - "'auth_cache_life_time', 'oauth_cache_life_time', " - "'router_cache_life_time', and 'sm_cache_life_time'.")}}, + "_`auth_cache_life_time`_, _`oauth_cache_life_time`_, " + "_`router_cache_life_time`_, and _`sm_cache_life_time`_.")}}, {cache_missed, #{value => "true | false", desc => @@ -73,12 +73,12 @@ doc() -> "an attempt to lookup for a value in a database and " "this value is not found and the option is set to 'true', " "this attempt will be cached and no attempts will be " - "performed until the cache expires (see 'cache_life_time'). " + "performed until the cache expires (see _`cache_life_time`_). " "Usually you don't want to change it. Default is 'true'. " "Several modules have a similar option; and some core " "ejabberd parts support similar options too, see " - "'auth_cache_missed', 'oauth_cache_missed', " - "'router_cache_missed', and 'sm_cache_missed'.")}}, + "_`auth_cache_missed`_, _`oauth_cache_missed`_, " + "_`router_cache_missed`_, and _`sm_cache_missed`_.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => @@ -93,16 +93,16 @@ doc() -> "performance. The default value is '1000'. " "Several modules have a similar option; and some core " "ejabberd parts support similar options too, see " - "'auth_cache_size', 'oauth_cache_size', " - "'router_cache_size', and 'sm_cache_size'.")}}, + "_`auth_cache_size`_, _`oauth_cache_size`_, " + "_`router_cache_size`_, and _`sm_cache_size`_.")}}, {use_cache, #{value => "true | false", desc => ?T("Enable or disable cache. The default is 'true'. " "Several modules have a similar option; and some core " "ejabberd parts support similar options too, see " - "'auth_use_cache', 'oauth_use_cache', 'router_use_cache', " - "and 'sm_use_cache'.")}}, + "_`auth_use_cache`_, _`oauth_use_cache`_, _`router_use_cache`_, " + "and _`sm_use_cache`_.")}}, {default_db, #{value => "mnesia | sql", desc => @@ -122,14 +122,14 @@ doc() -> "Modules may have its own value of the option. " "The value of 'ram' means that queues will be kept in memory. " "If value 'file' is set, you may also specify directory " - "in 'queue_dir' option where file queues will be placed. " + "in _`queue_dir`_ option where file queues will be placed. " "The default value is 'ram'.")}}, {version, #{value => "string()", desc => ?T("The option can be used to set custom ejabberd version, " "that will be used by different parts of ejabberd, for " - "example by 'mod_version' module. The default value is " + "example by _`mod_version`_ module. The default value is " "obtained at compile time from the underlying version " "control system.")}}, {acl, @@ -141,7 +141,7 @@ doc() -> "has name 'ACLName': it can be any string except 'all' or 'none' " "(those are predefined names for the rules that match all or nothing " "respectively). The name 'ACLName' can be referenced from other " - "parts of the configuration file, for example in 'access_rules' " + "parts of the configuration file, for example in _`access_rules`_ " "option. The rules of 'ACLName' are represented by mapping " "'pass:[{ACLType: ACLValue}]'. These can be one of the following:")}, [{user, @@ -225,7 +225,7 @@ doc() -> "of the configuration file (mostly from 'access' options of " "ejabberd modules). Each rule definition may contain " "arbitrary number of 'allow' or 'deny' sections, and each " - "section may contain any number of ACL rules (see 'acl' option). " + "section may contain any number of ACL rules (see _`acl`_ option). " "There are no access rules defined by default."), example => ["access_rules:", @@ -336,18 +336,18 @@ doc() -> {auth_cache_life_time, #{value => "timeout()", desc => - ?T("Same as 'cache_life_time', but applied to authentication cache " - "only. If not set, the value from 'cache_life_time' will be used.")}}, + ?T("Same as _`cache_life_time`_, but applied to authentication cache " + "only. If not set, the value from _`cache_life_time`_ will be used.")}}, {auth_cache_missed, #{value => "true | false", desc => - ?T("Same as 'cache_missed', but applied to authentication cache " - "only. If not set, the value from 'cache_missed' will be used.")}}, + ?T("Same as _`cache_missed`_, but applied to authentication cache " + "only. If not set, the value from _`cache_missed`_ will be used.")}}, {auth_cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as 'cache_size', but applied to authentication cache " - "only. If not set, the value from 'cache_size' will be used.")}}, + ?T("Same as _`cache_size`_, but applied to authentication cache " + "only. If not set, the value from _`cache_size`_ will be used.")}}, {auth_method, #{value => "[mnesia | sql | anonymous | external | jwt | ldap | pam, ...]", desc => @@ -393,8 +393,8 @@ doc() -> {auth_use_cache, #{value => "true | false", desc => - ?T("Same as 'use_cache', but applied to authentication cache " - "only. If not set, the value from 'use_cache' will be used.")}}, + ?T("Same as _`use_cache`_, but applied to authentication cache " + "only. If not set, the value from _`use_cache`_ will be used.")}}, {c2s_cafile, #{value => ?T("Path"), desc => @@ -464,7 +464,7 @@ doc() -> "from CAPTCHA DoS. The default value is 'infinity'.")}}, {captcha_host, #{value => "String", - desc => ?T("Deprecated. Use 'captcha_url' instead.")}}, + desc => ?T("Deprecated. Use _`captcha_url`_ instead.")}}, {captcha_url, #{value => ?T("URL"), desc => @@ -688,7 +688,7 @@ doc() -> #{value => "[Host, ...]", desc => ?T("A list of IP addresses or DNS names of LDAP backup servers. " - "When no servers listed in 'ldap_servers' option are reachable, " + "When no servers listed in _`ldap_servers`_ option are reachable, " "ejabberd will try to connect to these backup servers. " "The default is an empty list, i.e. no backup servers specified. " "WARNING: ejabberd doesn't try to reconnect back to the main " @@ -795,7 +795,7 @@ doc() -> "the result set. There is no default value, which means the " "result is not filtered. WARNING: Since this filter makes " "additional LDAP lookups, use it only as the last resort: " - "try to define all filter rules in 'ldap_filter' option if possible."), + "try to define all filter rules in _`ldap_filter`_ option if possible."), example => ["ldap_dn_filter:", " \"(&(name=%s)(owner=%D)(user=%u@%d))\": [sn]"]}}, @@ -866,13 +866,13 @@ doc() -> {oauth_cache_life_time, #{value => "timeout()", desc => - ?T("Same as 'cache_life_time', but applied to OAuth cache " - "only. If not set, the value from 'cache_life_time' will be used.")}}, + ?T("Same as _`cache_life_time`_, but applied to OAuth cache " + "only. If not set, the value from _`cache_life_time`_ will be used.")}}, {oauth_cache_missed, #{value => "true | false", desc => - ?T("Same as 'cache_missed', but applied to OAuth cache " - "only. If not set, the value from 'cache_missed' will be used.")}}, + ?T("Same as _`cache_missed`_, but applied to OAuth cache " + "only. If not set, the value from _`cache_missed`_ will be used.")}}, {oauth_cache_rest_failure_life_time, #{value => "timeout()", note => "added in 21.01", @@ -882,8 +882,8 @@ doc() -> {oauth_cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as 'cache_size', but applied to OAuth cache " - "only. If not set, the value from 'cache_size' will be used.")}}, + ?T("Same as _`cache_size`_, but applied to OAuth cache " + "only. If not set, the value from _`cache_size`_ will be used.")}}, {oauth_client_id_check, #{value => "allow | db | deny", desc => @@ -893,13 +893,13 @@ doc() -> {oauth_use_cache, #{value => "true | false", desc => - ?T("Same as 'use_cache', but applied to OAuth cache " - "only. If not set, the value from 'use_cache' will be used.")}}, + ?T("Same as _`use_cache`_, but applied to OAuth cache " + "only. If not set, the value from _`use_cache`_ will be used.")}}, {oauth_db_type, #{value => "mnesia | sql", desc => ?T("Database backend to use for OAuth authentication. " - "The default value is picked from 'default_db' option, or " + "The default value is picked from _`default_db`_ option, or " "if it's not set, 'mnesia' will be used.")}}, {oauth_expire, #{value => "timeout()", @@ -913,7 +913,7 @@ doc() -> desc => ?T("Enable or disable OOM (out-of-memory) killer. " "When system memory raises above the limit defined in " - "'oom_watermark' option, ejabberd triggers OOM killer " + "_`oom_watermark`_ option, ejabberd triggers OOM killer " "to terminate most memory consuming Erlang processes. " "Note that in order to maintain functionality, ejabberd only " "attempts to kill transient processes, such as those managing " @@ -924,14 +924,14 @@ doc() -> desc => ?T("Trigger OOM killer when some of the running Erlang processes " "have messages queue above this 'Size'. Note that " - "such processes won't be killed if 'oom_killer' option is set " + "such processes won't be killed if _`oom_killer`_ option is set " "to 'false' or if 'oom_watermark' is not reached yet.")}}, {oom_watermark, #{value => ?T("Percent"), desc => ?T("A percent of total system memory consumed at which " "OOM killer should be activated with some of the processes " - "possibly be killed (see 'oom_killer' option). Later, when " + "possibly be killed (see _`oom_killer`_ option). Later, when " "memory drops below this 'Percent', OOM killer is deactivated. " "The default value is '80' percents.")}}, {outgoing_s2s_families, @@ -984,7 +984,7 @@ doc() -> {queue_dir, #{value => ?T("Directory"), desc => - ?T("If 'queue_type' option is set to 'file', use this 'Directory' " + ?T("If _`queue_type`_ option is set to 'file', use this 'Directory' " "to store file queues. The default is to keep queues inside " "Mnesia directory.")}}, {redis_connect_timeout, @@ -1014,8 +1014,8 @@ doc() -> #{value => "ram | file", desc => ?T("The type of request queue for the Redis server. " - "See description of 'queue_type' option for the explanation. " - "The default value is the value defined in 'queue_type' " + "See description of _`queue_type`_ option for the explanation. " + "The default value is the value defined in _`queue_type`_ " "or 'ram' if the latter is not set.")}}, {redis_server, #{value => ?T("Hostname"), @@ -1025,7 +1025,7 @@ doc() -> {registration_timeout, #{value => "timeout()", desc => - ?T("This is a global option for module 'mod_register'. " + ?T("This is a global option for module _`mod_register`_. " "It limits the frequency of registrations from a given " "IP or username. So, a user that tries to register a " "new account from the same IP address or JID during " @@ -1048,29 +1048,29 @@ doc() -> {router_cache_life_time, #{value => "timeout()", desc => - ?T("Same as 'cache_life_time', but applied to routing table cache " - "only. If not set, the value from 'cache_life_time' will be used.")}}, + ?T("Same as _`cache_life_time`_, but applied to routing table cache " + "only. If not set, the value from _`cache_life_time`_ will be used.")}}, {router_cache_missed, #{value => "true | false", desc => - ?T("Same as 'cache_missed', but applied to routing table cache " - "only. If not set, the value from 'cache_missed' will be used.")}}, + ?T("Same as _`cache_missed`_, but applied to routing table cache " + "only. If not set, the value from _`cache_missed`_ will be used.")}}, {router_cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as 'cache_size', but applied to routing table cache " - "only. If not set, the value from 'cache_size' will be used.")}}, + ?T("Same as _`cache_size`_, but applied to routing table cache " + "only. If not set, the value from _`cache_size`_ will be used.")}}, {router_db_type, #{value => "mnesia | redis | sql", desc => ?T("Database backend to use for routing information. " - "The default value is picked from 'default_ram_db' option, or " + "The default value is picked from _`default_ram_db`_ option, or " "if it's not set, 'mnesia' will be used.")}}, {router_use_cache, #{value => "true | false", desc => - ?T("Same as 'use_cache', but applied to routing table cache " - "only. If not set, the value from 'use_cache' will be used.")}}, + ?T("Same as _`use_cache`_, but applied to routing table cache " + "only. If not set, the value from _`use_cache`_ will be used.")}}, {rpc_timeout, #{value => "timeout()", desc => @@ -1147,8 +1147,8 @@ doc() -> #{value => "ram | file", desc => ?T("The type of a queue for s2s packets. " - "See description of 'queue_type' option for the explanation. " - "The default value is the value defined in 'queue_type' " + "See description of _`queue_type`_ option for the explanation. " + "The default value is the value defined in _`queue_type`_ " "or 'ram' if the latter is not set.")}}, {s2s_timeout, #{value => "timeout()", @@ -1176,7 +1176,7 @@ doc() -> desc => ?T("The option defines a set of shapers. Every shaper is assigned " "a name 'ShaperName' that can be used in other parts of the " - "configuration file, such as 'shaper_rules' option. The shaper " + "configuration file, such as _`shaper_rules`_ option. The shaper " "itself is defined by its 'Rate', where 'Rate' stands for the " "maximum allowed incoming rate in **bytes** per second. " "When a connection exceeds this limit, ejabberd stops reading " @@ -1192,9 +1192,9 @@ doc() -> #{value => "{ShaperRuleName: {Number|ShaperName: ACLRule|ACLName}}", desc => ?T("An entry allowing to declaring shaper to use for matching user/hosts. " - "Semantics is similar to 'access_rules' option, the only difference is " + "Semantics is similar to _`access_rules`_ option, the only difference is " "that instead using 'allow' or 'deny', a name of a shaper (defined in " - "'shaper' option) or a positive number should be used."), + "_`shaper`_ option) or a positive number should be used."), example => ["shaper_rules:", " connections_limit:", @@ -1210,29 +1210,29 @@ doc() -> {sm_cache_life_time, #{value => "timeout()", desc => - ?T("Same as 'cache_life_time', but applied to client sessions table cache " - "only. If not set, the value from 'cache_life_time' will be used.")}}, + ?T("Same as _`cache_life_time`_, but applied to client sessions table cache " + "only. If not set, the value from _`cache_life_time`_ will be used.")}}, {sm_cache_missed, #{value => "true | false", desc => - ?T("Same as 'cache_missed', but applied to client sessions table cache " - "only. If not set, the value from 'cache_missed' will be used.")}}, + ?T("Same as _`cache_missed`_, but applied to client sessions table cache " + "only. If not set, the value from _`cache_missed`_ will be used.")}}, {sm_cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as 'cache_size', but applied to client sessions table cache " - "only. If not set, the value from 'cache_size' will be used.")}}, + ?T("Same as _`cache_size`_, but applied to client sessions table cache " + "only. If not set, the value from _`cache_size`_ will be used.")}}, {sm_db_type, #{value => "mnesia | redis | sql", desc => ?T("Database backend to use for client sessions information. " - "The default value is picked from 'default_ram_db' option, or " + "The default value is picked from _`default_ram_db`_ option, or " "if it's not set, 'mnesia' will be used.")}}, {sm_use_cache, #{value => "true | false", desc => - ?T("Same as 'use_cache', but applied to client sessions table cache " - "only. If not set, the value from 'use_cache' will be used.")}}, + ?T("Same as _`use_cache`_, but applied to client sessions table cache " + "only. If not set, the value from _`use_cache`_ will be used.")}}, {sql_type, #{value => "mssql | mysql | odbc | pgsql | sqlite", desc => @@ -1258,7 +1258,7 @@ doc() -> note => "added in 20.12", desc => ?T("Path to the ODBC driver to use to connect to a Microsoft SQL " - "Server database. This option is only valid if the 'sql_type' " + "Server database. This option is only valid if the _`sql_type`_ " "option is set to 'mssql'. " "The default value is: 'libtdsodbc.so'")}}, {sql_password, @@ -1293,8 +1293,8 @@ doc() -> #{value => "ram | file", desc => ?T("The type of a request queue for the SQL server. " - "See description of 'queue_type' option for the explanation. " - "The default value is the value defined in 'queue_type' " + "See description of _`queue_type`_ option for the explanation. " + "The default value is the value defined in _`queue_type`_ " "or 'ram' if the latter is not set.")}}, {sql_server, #{value => ?T("Host"), @@ -1312,15 +1312,15 @@ doc() -> #{value => ?T("Path"), desc => ?T("A path to a file with CA root certificates that will " - "be used to verify SQL connections. Implies 'sql_ssl' " - "and 'sql_ssl_verify' options are set to 'true'. " + "be used to verify SQL connections. Implies _`sql_ssl`_ " + "and _`sql_ssl_verify`_ options are set to 'true'. " "There is no default which means " "certificate verification is disabled.")}}, {sql_ssl_certfile, #{value => ?T("Path"), desc => ?T("A path to a certificate file that will be used " - "for SSL connections to the SQL server. Implies 'sql_ssl' " + "for SSL connections to the SQL server. Implies _`sql_ssl`_ " "option is set to 'true'. There is no default which means " "ejabberd won't provide a client certificate to the SQL " "server.")}}, @@ -1328,8 +1328,8 @@ doc() -> #{value => "true | false", desc => ?T("Whether to verify SSL connection to the SQL server against " - "CA root certificates defined in 'sql_ssl_cafile' option. " - "Implies 'sql_ssl' option is set to 'true'. " + "CA root certificates defined in _`sql_ssl_cafile`_ option. " + "Implies _`sql_ssl`_ option is set to 'true'. " "The default value is 'false'.")}}, {sql_start_interval, #{value => "timeout()", diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index c87849748..4e932fe83 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -366,4 +366,4 @@ mod_doc() -> "from the default to the new schema. Check the section " "http://../database/#default-and-new-schemas[Default and New Schemas] for details. " "Please note that only PostgreSQL is supported. " - "When the module is loaded use 'update_sql' ejabberdctl command.")}. + "When the module is loaded use _`update_sql`_ API.")}. diff --git a/src/mod_announce.erl b/src/mod_announce.erl index e0501d96b..21213615c 100644 --- a/src/mod_announce.erl +++ b/src/mod_announce.erl @@ -930,7 +930,7 @@ mod_doc() -> "should be disabled for instances of ejabberd with hundreds of " "thousands users."), "", ?T("The Ad-hoc Commands are listed in the Server Discovery. " - "For this feature to work, 'mod_adhoc' must be enabled."), "", + "For this feature to work, _`mod_adhoc`_ must be enabled."), "", ?T("The specific JIDs where messages can be sent are listed below. " "The first JID in each entry will apply only to the specified " "virtual host example.org, while the JID between brackets " @@ -940,7 +940,7 @@ mod_doc() -> "online and connected to several resources, only the resource " "with the highest priority will receive the message. " "If the registered user is not connected, the message will be " - "stored offline in assumption that offline storage (see 'mod_offline') " + "stored offline in assumption that offline storage (see _`mod_offline`_) " "is enabled."), "- example.org/announce/online (example.org/announce/all-hosts/online)::", ?T("The message is sent to all connected users. If the user is " @@ -965,20 +965,20 @@ mod_doc() -> {db_type, #{value => "mnesia | sql", desc => - ?T("Same as top-level 'default_db' option, but applied to this module only.")}}, + ?T("Same as top-level _`default_db`_ option, but applied to this module only.")}}, {use_cache, #{value => "true | false", desc => - ?T("Same as top-level 'use_cache' option, but applied to this module only.")}}, + ?T("Same as top-level _`use_cache`_ option, but applied to this module only.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as top-level 'cache_size' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_size`_ option, but applied to this module only.")}}, {cache_missed, #{value => "true | false", desc => - ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_missed`_ option, but applied to this module only.")}}, {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}. + ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}]}. diff --git a/src/mod_avatar.erl b/src/mod_avatar.erl index 3ad55f6d0..09329853d 100644 --- a/src/mod_avatar.erl +++ b/src/mod_avatar.erl @@ -469,8 +469,8 @@ mod_doc() -> "[XEP-0398: User Avatar to vCard-Based Avatars Conversion]."), "", ?T("Also, the module supports conversion between avatar " "image formats on the fly."), "", - ?T("The module depends on 'mod_vcard', 'mod_vcard_xupdate' and " - "'mod_pubsub'.")], + ?T("The module depends on _`mod_vcard`_, _`mod_vcard_xupdate`_ and " + "_`mod_pubsub`_.")], opts => [{convert, #{value => "{From: To}", diff --git a/src/mod_bosh.erl b/src/mod_bosh.erl index 1770d27a7..37a7fa809 100644 --- a/src/mod_bosh.erl +++ b/src/mod_bosh.erl @@ -240,27 +240,27 @@ mod_doc() -> {queue_type, #{value => "ram | file", desc => - ?T("Same as top-level 'queue_type' option, but applied to this module only.")}}, + ?T("Same as top-level _`queue_type`_ option, but applied to this module only.")}}, {ram_db_type, #{value => "mnesia | sql | redis", desc => - ?T("Same as 'default_ram_db' but applied to this module only.")}}, + ?T("Same as _`default_ram_db`_ but applied to this module only.")}}, {use_cache, #{value => "true | false", desc => - ?T("Same as top-level 'use_cache' option, but applied to this module only.")}}, + ?T("Same as top-level _`use_cache`_ option, but applied to this module only.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as top-level 'cache_size' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_size`_ option, but applied to this module only.")}}, {cache_missed, #{value => "true | false", desc => - ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_missed`_ option, but applied to this module only.")}}, {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}], + ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}], example => ["listen:", " -", diff --git a/src/mod_caps.erl b/src/mod_caps.erl index 92869fc7d..c8f548169 100644 --- a/src/mod_caps.erl +++ b/src/mod_caps.erl @@ -595,25 +595,25 @@ mod_doc() -> "https://xmpp.org/extensions/xep-0115.html" "[XEP-0115: Entity Capabilities]."), ?T("The main purpose of the module is to provide " - "PEP functionality (see 'mod_pubsub').")], + "PEP functionality (see _`mod_pubsub`_).")], opts => [{db_type, #{value => "mnesia | sql", desc => - ?T("Same as top-level 'default_db' option, but applied to this module only.")}}, + ?T("Same as top-level _`default_db`_ option, but applied to this module only.")}}, {use_cache, #{value => "true | false", desc => - ?T("Same as top-level 'use_cache' option, but applied to this module only.")}}, + ?T("Same as top-level _`use_cache`_ option, but applied to this module only.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as top-level 'cache_size' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_size`_ option, but applied to this module only.")}}, {cache_missed, #{value => "true | false", desc => - ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_missed`_ option, but applied to this module only.")}}, {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}. + ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}]}. diff --git a/src/mod_configure.erl b/src/mod_configure.erl index 99a20ef3a..32c7ebb31 100644 --- a/src/mod_configure.erl +++ b/src/mod_configure.erl @@ -1564,4 +1564,4 @@ mod_doc() -> ?T("The module provides server configuration functionality via " "https://xmpp.org/extensions/xep-0050.html" "[XEP-0050: Ad-Hoc Commands]. This module requires " - "'mod_adhoc' to be loaded.")}. + "_`mod_adhoc`_ to be loaded.")}. diff --git a/src/mod_delegation.erl b/src/mod_delegation.erl index a588e81ef..3ae3b8a51 100644 --- a/src/mod_delegation.erl +++ b/src/mod_delegation.erl @@ -95,7 +95,7 @@ mod_doc() -> ?T("WARNING: Security issue: Namespace delegation gives components " "access to sensitive data, so permission should be granted " "carefully, only if you trust the component."), "", - ?T("NOTE: This module is complementary to 'mod_privilege' but can " + ?T("NOTE: This module is complementary to _`mod_privilege`_ but can " "also be used separately.")], opts => [{namespaces, diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index 040cb6085..3303f17c0 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -331,7 +331,7 @@ mod_doc() -> "replaced with the virtual host name. NOTE: if GET requests " "are handled by 'mod_http_upload', the 'get_url' must match the " "'put_url'. Setting it to a different value only makes " - "sense if an external web server or 'mod_http_fileserver' " + "sense if an external web server or _`mod_http_fileserver`_ " "is used to serve the uploaded files.")}}, {service_url, #{desc => ?T("Deprecated.")}}, diff --git a/src/mod_last.erl b/src/mod_last.erl index 295a546f2..a7d36c791 100644 --- a/src/mod_last.erl +++ b/src/mod_last.erl @@ -344,20 +344,20 @@ mod_doc() -> [{db_type, #{value => "mnesia | sql", desc => - ?T("Same as top-level 'default_db' option, but applied to this module only.")}}, + ?T("Same as top-level _`default_db`_ option, but applied to this module only.")}}, {use_cache, #{value => "true | false", desc => - ?T("Same as top-level 'use_cache' option, but applied to this module only.")}}, + ?T("Same as top-level _`use_cache`_ option, but applied to this module only.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as top-level 'cache_size' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_size`_ option, but applied to this module only.")}}, {cache_missed, #{value => "true | false", desc => - ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_missed`_ option, but applied to this module only.")}}, {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}. + ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}]}. diff --git a/src/mod_mam.erl b/src/mod_mam.erl index e894f5f7d..abb2333cc 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -1456,7 +1456,7 @@ mod_doc() -> #{value => "true | false", desc => ?T("This option determines how ejabberd's " - "stream management code (see 'mod_stream_mgmt') " + "stream management code (see _`mod_stream_mgmt`_) " "handles unacknowledged messages when the " "connection is lost. Usually, such messages are " "either bounced or resent. However, neither is " @@ -1495,28 +1495,28 @@ mod_doc() -> #{value => "true | false", desc => ?T("Whether to destroy message archive of a room " - "(see 'mod_muc') when it gets destroyed. " + "(see _`mod_muc`_) when it gets destroyed. " "The default value is 'true'.")}}, {db_type, #{value => "mnesia | sql", desc => - ?T("Same as top-level 'default_db' option, but applied to this module only.")}}, + ?T("Same as top-level _`default_db`_ option, but applied to this module only.")}}, {use_cache, #{value => "true | false", desc => - ?T("Same as top-level 'use_cache' option, but applied to this module only.")}}, + ?T("Same as top-level _`use_cache`_ option, but applied to this module only.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as top-level 'cache_size' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_size`_ option, but applied to this module only.")}}, {cache_missed, #{value => "true | false", desc => - ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_missed`_ option, but applied to this module only.")}}, {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}, {user_mucsub_from_muc_archive, #{value => "true | false", desc => diff --git a/src/mod_mix.erl b/src/mod_mix.erl index 82b5e41a4..002ef5696 100644 --- a/src/mod_mix.erl +++ b/src/mod_mix.erl @@ -106,12 +106,12 @@ mod_doc() -> "experimental feature, updated in 19.02, and is not " "yet ready to use in production. It's asserted that " "the MIX protocol is going to replace the MUC protocol " - "in the future (see 'mod_muc')."), "", + "in the future (see _`mod_muc`_)."), "", ?T("To learn more about how to use that feature, you can refer to " "our tutorial: https://docs.ejabberd.im/tutorials/mix-010/" "[Getting started with XEP-0369: Mediated Information " "eXchange (MIX) v0.1]."), "", - ?T("The module depends on 'mod_mam'.")], + ?T("The module depends on _`mod_mam`_.")], opts => [{access_create, #{value => ?T("AccessName"), @@ -136,7 +136,7 @@ mod_doc() -> {db_type, #{value => "mnesia | sql", desc => - ?T("Same as top-level 'default_db' option, but applied to this module only.")}}]}. + ?T("Same as top-level _`default_db`_ option, but applied to this module only.")}}]}. -spec route(stanza()) -> ok. route(#iq{} = IQ) -> diff --git a/src/mod_mix_pam.erl b/src/mod_mix_pam.erl index c6348b92f..1fa5c1861 100644 --- a/src/mod_mix_pam.erl +++ b/src/mod_mix_pam.erl @@ -120,23 +120,23 @@ mod_doc() -> [{db_type, #{value => "mnesia | sql", desc => - ?T("Same as top-level 'default_db' option, but applied to this module only.")}}, + ?T("Same as top-level _`default_db`_ option, but applied to this module only.")}}, {use_cache, #{value => "true | false", desc => - ?T("Same as top-level 'use_cache' option, but applied to this module only.")}}, + ?T("Same as top-level _`use_cache`_ option, but applied to this module only.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as top-level 'cache_size' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_size`_ option, but applied to this module only.")}}, {cache_missed, #{value => "true | false", desc => - ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_missed`_ option, but applied to this module only.")}}, {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}. + ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}]}. -spec bounce_sm_packet({term(), stanza()}) -> {term(), stanza()}. bounce_sm_packet({_, #message{to = #jid{lresource = <<>>} = To, diff --git a/src/mod_mqtt.erl b/src/mod_mqtt.erl index a1621cc01..5d00408df 100644 --- a/src/mod_mqtt.erl +++ b/src/mod_mqtt.erl @@ -327,37 +327,37 @@ mod_doc() -> {queue_type, #{value => "ram | file", desc => - ?T("Same as top-level 'queue_type' option, " + ?T("Same as top-level _`queue_type`_ option, " "but applied to this module only.")}}, {ram_db_type, #{value => "mnesia", desc => - ?T("Same as top-level 'default_ram_db' option, " + ?T("Same as top-level _`default_ram_db`_ option, " "but applied to this module only.")}}, {db_type, #{value => "mnesia | sql", desc => - ?T("Same as top-level 'default_db' option, " + ?T("Same as top-level _`default_db`_ option, " "but applied to this module only.")}}, {use_cache, #{value => "true | false", desc => - ?T("Same as top-level 'use_cache' option, " + ?T("Same as top-level _`use_cache`_ option, " "but applied to this module only.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as top-level 'cache_size' option, " + ?T("Same as top-level _`cache_size`_ option, " "but applied to this module only.")}}, {cache_missed, #{value => "true | false", desc => - ?T("Same as top-level 'cache_missed' option, " + ?T("Same as top-level _`cache_missed`_ option, " "but applied to this module only.")}}, {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level 'cache_life_time' option, " + ?T("Same as top-level _`cache_life_time`_ option, " "but applied to this module only.")}}]}. %%%=================================================================== diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 46bfd20e5..ccecb6b66 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1517,7 +1517,7 @@ mod_doc() -> {queue_type, #{value => "ram | file", desc => - ?T("Same as top-level 'queue_type' option, but applied to this module only.")}}, + ?T("Same as top-level _`queue_type`_ option, but applied to this module only.")}}, {regexp_room_id, #{value => "string()", desc => @@ -1634,7 +1634,7 @@ mod_doc() -> {logging, #{value => "true | false", desc => - ?T("The public messages are logged using 'mod_muc_log'. " + ?T("The public messages are logged using _`mod_muc_log`_. " "The default value is 'false'.")}}, {members_by_default, #{value => "true | false", diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 360d414b1..2abeee45c 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -1413,4 +1413,4 @@ mod_doc() -> [?T("This module provides commands to administer local MUC " "services and their MUC rooms. It also provides simple " "WebAdmin pages to view the existing rooms."), "", - ?T("This module depends on 'mod_muc'.")]}. + ?T("This module depends on _`mod_muc`_.")]}. diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index 53223a8eb..8bcbc8bc0 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -1021,7 +1021,7 @@ mod_doc() -> ?T("- URLs on messages and subjects are converted to hyperlinks."), "", ?T("- Timezone used on timestamps is shown on the log files."), "", ?T("- A custom link can be added on top of each page."), "", - ?T("The module depends on 'mod_muc'.")], + ?T("The module depends on _`mod_muc`_.")], opts => [{access_log, #{value => ?T("AccessName"), diff --git a/src/mod_offline.erl b/src/mod_offline.erl index ff95767b6..1d367eb72 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -1325,19 +1325,19 @@ mod_doc() -> {db_type, #{value => "mnesia | sql", desc => - ?T("Same as top-level 'default_db' option, but applied to this module only.")}}, + ?T("Same as top-level _`default_db`_ option, but applied to this module only.")}}, {use_cache, #{value => "true | false", desc => - ?T("Same as top-level 'use_cache' option, but applied to this module only.")}}, + ?T("Same as top-level _`use_cache`_ option, but applied to this module only.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as top-level 'cache_size' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_size`_ option, but applied to this module only.")}}, {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}], + ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}], example => [{?T("This example allows power users to have as much as 5000 " "offline messages, administrators up to 2000, and all the " diff --git a/src/mod_ping.erl b/src/mod_ping.erl index 483f2e834..eee55825e 100644 --- a/src/mod_ping.erl +++ b/src/mod_ping.erl @@ -317,8 +317,8 @@ mod_doc() -> "server ping request in less than period defined " "in 'ping_ack_timeout' option: " "'kill' means destroying the underlying connection, " - "'none' means to do nothing. NOTE: when 'mod_stream_mgmt' " - "module is loaded and stream management is enabled by " + "'none' means to do nothing. NOTE: when _`mod_stream_mgmt`_ " + "is loaded and stream management is enabled by " "a client, killing the client connection doesn't mean " "killing the client session - the session will be kept " "alive in order to give the client a chance to resume it. " diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl index 4f15b80c4..5ac26c2f5 100644 --- a/src/mod_privacy.erl +++ b/src/mod_privacy.erl @@ -887,25 +887,25 @@ mod_doc() -> "https://xmpp.org/extensions/xep-0191.html" "[XEP-0191: Blocking Command] which is implemented by " "'mod_blocking' module. However, you still need " - "'mod_privacy' loaded in order for 'mod_blocking' to work.")], + "'mod_privacy' loaded in order for _`mod_blocking`_ to work.")], opts => [{db_type, #{value => "mnesia | sql", desc => - ?T("Same as top-level 'default_db' option, but applied to this module only.")}}, + ?T("Same as top-level _`default_db`_ option, but applied to this module only.")}}, {use_cache, #{value => "true | false", desc => - ?T("Same as top-level 'use_cache' option, but applied to this module only.")}}, + ?T("Same as top-level _`use_cache`_ option, but applied to this module only.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as top-level 'cache_size' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_size`_ option, but applied to this module only.")}}, {cache_missed, #{value => "true | false", desc => - ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_missed`_ option, but applied to this module only.")}}, {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}. + ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}]}. diff --git a/src/mod_private.erl b/src/mod_private.erl index 36f1e8d2c..436aae222 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -128,23 +128,23 @@ mod_doc() -> [{db_type, #{value => "mnesia | sql", desc => - ?T("Same as top-level 'default_db' option, but applied to this module only.")}}, + ?T("Same as top-level _`default_db`_ option, but applied to this module only.")}}, {use_cache, #{value => "true | false", desc => - ?T("Same as top-level 'use_cache' option, but applied to this module only.")}}, + ?T("Same as top-level _`use_cache`_ option, but applied to this module only.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as top-level 'cache_size' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_size`_ option, but applied to this module only.")}}, {cache_missed, #{value => "true | false", desc => - ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_missed`_ option, but applied to this module only.")}}, {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}. + ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}]}. -spec get_sm_features({error, stanza_error()} | empty | {result, [binary()]}, jid(), jid(), binary(), binary()) -> diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index a6d4ba446..353a8da27 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -106,7 +106,7 @@ mod_doc() -> ?T("WARNING: Security issue: Privileged access gives components " "access to sensitive data, so permission should be granted " "carefully, only if you trust a component."), "", - ?T("NOTE: This module is complementary to 'mod_delegation', " + ?T("NOTE: This module is complementary to _`mod_delegation`_, " "but can also be used separately.")], opts => [{roster, diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 8792b2ab9..2e40d8f0e 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -4269,7 +4269,7 @@ mod_doc() -> "(https://xmpp.org/extensions/xep-0163.html" "[XEP-0163: Personal Eventing via Pubsub]) " "is enabled in the default ejabberd configuration file, " - "and it requires 'mod_caps'.")], + "and it requires _`mod_caps`_.")], opts => [{access_createnode, #{value => "AccessName", @@ -4282,7 +4282,7 @@ mod_doc() -> {db_type, #{value => "mnesia | sql", desc => - ?T("Same as top-level 'default_db' option, but applied to " + ?T("Same as top-level _`default_db`_ option, but applied to " "this module only.")}}, {default_node_config, #{value => "List of Key:Value", diff --git a/src/mod_push.erl b/src/mod_push.erl index ea1c3e649..5477c5792 100644 --- a/src/mod_push.erl +++ b/src/mod_push.erl @@ -191,23 +191,23 @@ mod_doc() -> {db_type, #{value => "mnesia | sql", desc => - ?T("Same as top-level 'default_db' option, but applied to this module only.")}}, + ?T("Same as top-level _`default_db`_ option, but applied to this module only.")}}, {use_cache, #{value => "true | false", desc => - ?T("Same as top-level 'use_cache' option, but applied to this module only.")}}, + ?T("Same as top-level _`use_cache`_ option, but applied to this module only.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as top-level 'cache_size' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_size`_ option, but applied to this module only.")}}, {cache_missed, #{value => "true | false", desc => - ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_missed`_ option, but applied to this module only.")}}, {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}. + ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}]}. %%-------------------------------------------------------------------- %% ejabberd command callback. diff --git a/src/mod_push_keepalive.erl b/src/mod_push_keepalive.erl index aba4fbfae..e0e83f1e1 100644 --- a/src/mod_push_keepalive.erl +++ b/src/mod_push_keepalive.erl @@ -94,13 +94,13 @@ mod_options(_Host) -> mod_doc() -> #{desc => [?T("This module tries to keep the stream management " - "session (see 'mod_stream_mgmt') of a disconnected " + "session (see _`mod_stream_mgmt`_) of a disconnected " "mobile client alive if the client enabled push " "notifications for that session. However, the normal " "session resumption timeout is restored once a push " "notification is issued, so the session will be closed " "if the client doesn't respond to push notifications."), "", - ?T("The module depends on 'mod_push'.")], + ?T("The module depends on _`mod_push`_.")], opts => [{resume_timeout, #{value => "timeout()", @@ -109,8 +109,8 @@ mod_doc() -> "the session of a disconnected push client times out. " "This timeout is only in effect as long as no push " "notification is issued. Once that happened, the " - "resumption timeout configured for the 'mod_stream_mgmt' " - "module is restored. " + "resumption timeout configured for _`mod_stream_mgmt`_ " + "is restored. " "The default value is '72' hours.")}}, {wake_on_start, #{value => "true | false", diff --git a/src/mod_roster.erl b/src/mod_roster.erl index f204c9211..94cae4950 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -1345,29 +1345,29 @@ mod_doc() -> "This option does not affect the client in any way. " "This option is only useful if option 'versioning' is " "set to 'true'. The default value is 'false'. " - "IMPORTANT: if you use 'mod_shared_roster' or " - "'mod_shared_roster_ldap', you must set the value " + "IMPORTANT: if you use _`mod_shared_roster`_ or " + " _`mod_shared_roster_ldap`_, you must set the value " "of the option to 'false'.")}}, {db_type, #{value => "mnesia | sql", desc => - ?T("Same as top-level 'default_db' option, but applied to this module only.")}}, + ?T("Same as top-level _`default_db`_ option, but applied to this module only.")}}, {use_cache, #{value => "true | false", desc => - ?T("Same as top-level 'use_cache' option, but applied to this module only.")}}, + ?T("Same as top-level _`use_cache`_ option, but applied to this module only.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as top-level 'cache_size' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_size`_ option, but applied to this module only.")}}, {cache_missed, #{value => "true | false", desc => - ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_missed`_ option, but applied to this module only.")}}, {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}], + ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}], example => ["modules:", " ...", diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 16cc96a75..13ff90466 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -1266,7 +1266,7 @@ mod_doc() -> ?T("- Displayed: A list of groups that will be in the " "rosters of this group's members. A group of other vhost can " "be identified with 'groupid@vhost'."), "", - ?T("This module depends on 'mod_roster'. " + ?T("This module depends on _`mod_roster`_. " "If not enabled, roster queries will return 503 errors.")], opts => [{db_type, @@ -1274,25 +1274,25 @@ mod_doc() -> desc => ?T("Define the type of storage where the module will create " "the tables and store user information. The default is " - "the storage defined by the global option 'default_db', " + "the storage defined by the top-level _`default_db`_ option, " "or 'mnesia' if omitted. If 'sql' value is defined, " "make sure you have defined the database.")}}, - {use_cache, + {use_cache, #{value => "true | false", desc => - ?T("Same as top-level 'use_cache' option, but applied to this module only.")}}, + ?T("Same as top-level _`use_cache`_ option, but applied to this module only.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as top-level 'cache_size' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_size`_ option, but applied to this module only.")}}, {cache_missed, #{value => "true | false", desc => - ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_missed`_ option, but applied to this module only.")}}, {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}], + ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}], example => [{?T("Take the case of a computer club that wants all its members " "seeing each other in their rosters. To achieve this, they " diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl index 93c08e0c3..08fbe8793 100644 --- a/src/mod_shared_roster_ldap.erl +++ b/src/mod_shared_roster_ldap.erl @@ -796,7 +796,7 @@ mod_doc() -> "disable the check. Default value is 'true'.")}}] ++ [{Opt, #{desc => - {?T("Same as top-level '~s' option, but " + {?T("Same as top-level _`~s`_ option, but " "applied to this module only."), [Opt]}}} || Opt <- [ldap_backups, ldap_base, ldap_uids, ldap_deref_aliases, ldap_encrypt, ldap_password, ldap_port, ldap_rootdn, diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index b9443e5d2..5d2998500 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -962,12 +962,12 @@ mod_doc() -> {queue_type, #{value => "ram | file", desc => - ?T("Same as top-level 'queue_type' option, but applied to this module only.")}}, + ?T("Same as top-level _`queue_type`_ option, but applied to this module only.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as top-level 'cache_size' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_size`_ option, but applied to this module only.")}}, {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}. + ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}]}. diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index e7cfff819..8e0d32a4a 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -640,23 +640,23 @@ mod_doc() -> {db_type, #{value => "mnesia | sql | ldap", desc => - ?T("Same as top-level 'default_db' option, but applied to this module only.")}}, + ?T("Same as top-level _`default_db`_ option, but applied to this module only.")}}, {use_cache, #{value => "true | false", desc => - ?T("Same as top-level 'use_cache' option, but applied to this module only.")}}, + ?T("Same as top-level _`use_cache`_ option, but applied to this module only.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as top-level 'cache_size' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_size`_ option, but applied to this module only.")}}, {cache_missed, #{value => "true | false", desc => - ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_missed`_ option, but applied to this module only.")}}, {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}, {vcard, #{value => ?T("vCard"), desc => diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl index c81c058f5..bc6e7ebca 100644 --- a/src/mod_vcard_ldap.erl +++ b/src/mod_vcard_ldap.erl @@ -571,7 +571,7 @@ mod_doc() -> }]}}] ++ [{Opt, #{desc => - {?T("Same as top-level '~s' option, but " + {?T("Same as top-level _`~s`_ option, but " "applied to this module only."), [Opt]}}} || Opt <- [ldap_base, ldap_servers, ldap_uids, ldap_deref_aliases, ldap_encrypt, ldap_password, diff --git a/src/mod_vcard_xupdate.erl b/src/mod_vcard_xupdate.erl index ab8df2c60..59ebc7f71 100644 --- a/src/mod_vcard_xupdate.erl +++ b/src/mod_vcard_xupdate.erl @@ -228,26 +228,26 @@ mod_doc() -> "frequently their presence. However, the overhead is significantly " "reduced by the use of caching, so you probably don't want " "to set 'use_cache' to 'false'."), "", - ?T("The module depends on 'mod_vcard'."), "", + ?T("The module depends on _`mod_vcard`_."), "", ?T("NOTE: Nowadays https://xmpp.org/extensions/xep-0153.html" "[XEP-0153] is used mostly as \"read-only\", i.e. modern " "clients don't publish their avatars inside vCards. Thus " "in the majority of cases the module is only used along " - "with 'mod_avatar' module for providing backward compatibility.")], + "with _`mod_avatar`_ for providing backward compatibility.")], opts => [{use_cache, #{value => "true | false", desc => - ?T("Same as top-level 'use_cache' option, but applied to this module only.")}}, + ?T("Same as top-level _`use_cache`_ option, but applied to this module only.")}}, {cache_size, #{value => "pos_integer() | infinity", desc => - ?T("Same as top-level 'cache_size' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_size`_ option, but applied to this module only.")}}, {cache_missed, #{value => "true | false", desc => - ?T("Same as top-level 'cache_missed' option, but applied to this module only.")}}, + ?T("Same as top-level _`cache_missed`_ option, but applied to this module only.")}}, {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}. + ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}]}. -- cgit v1.2.3 From ac4f2402610e9c494421a443c7f765dc2fe074c1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 23 Aug 2021 13:53:54 +0200 Subject: Produce module names with specific syntax, docs Makefile will convert to links --- src/ejabberd_commands_doc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index f63326182..b2be8a6a6 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -396,7 +396,7 @@ gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, end, ModuleText = case IsDefinerMod of true -> - [?TAG(h2, <<"Module:">>), ?TAG(p, ?RAW(atom_to_list(Definer)))]; + [?TAG(h2, <<"Module:">>), ?TAG(p, ?RAW("*`"++atom_to_list(Definer)++"`*"))]; false -> [] end, -- cgit v1.2.3 From 655dcbcb7467db3cb0f89cf99d34cc2244e6c84f Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 21 Aug 2021 20:50:02 +0200 Subject: New command to produce markdown with tags and their associated commands --- src/ejabberd_commands.erl | 10 ++++++++++ src/ejabberd_commands_doc.erl | 22 ++++++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index 8d875e82f..c00b1469a 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -91,6 +91,16 @@ get_commands_spec() -> "that will have example invocation include in markdown document"], result_desc = "0 if command failed, 1 when succeeded", args_example = ["/home/me/docs/api.html", "mod_admin", "java,json"], + result_example = ok}, + #ejabberd_commands{name = gen_markdown_doc_for_tags, tags = [documentation], + desc = "Generates markdown documentation for ejabberd_commands", + module = ejabberd_commands_doc, function = generate_tags_md, + args = [{file, binary}], + result = {res, rescode}, + args_desc = ["Path to file where generated " + "documentation should be stored"], + result_desc = "0 if command failed, 1 when succeeded", + args_example = ["/home/me/docs/tags.md"], result_example = ok}]. start_link() -> diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index b2be8a6a6..05948fa6c 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -28,6 +28,7 @@ -export([generate_html_output/3]). -export([generate_md_output/3]). +-export([generate_tags_md/1]). -include("ejabberd_commands.hrl"). @@ -360,6 +361,13 @@ gen_param(Name, Type, Desc, HTMLOutput) -> [?TAG(dt, [?TAG_R(strong, atom_to_list(Name)), <<" :: ">>, ?RAW(format_type(Type))]), ?TAG(dd, ?RAW(Desc))]. +make_tags(HTMLOutput) -> + TagsList = ejabberd_commands:get_tags_commands(1000000), + lists:map(fun(T) -> gen_tags(T, HTMLOutput) end, TagsList). + +gen_tags({TagName, Commands}, HTMLOutput) -> + [?TAG(h1, TagName) | [?TAG(p, ?RAW("* *`"++C++"`*")) || C <- Commands]]. + gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, args=Args, args_desc=ArgsDesc, note=Note, definer=Definer, result=Result, result_desc=ResultDesc}=Cmd, HTMLOutput, Langs) -> @@ -389,7 +397,7 @@ gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, [?TAG(dl, [gen_param(RName, Type, ResultDesc, HTMLOutput)])] end end, - TagsText = [?RAW(atom_to_list(Tag) ++ " ") || Tag <- Tags], + TagsText = [?RAW("*`"++atom_to_list(Tag)++"`* ") || Tag <- Tags], IsDefinerMod = case Definer of unknown -> true; _ -> lists:member(gen_mod, proplists:get_value(behaviour, Definer:module_info(attributes))) @@ -482,13 +490,23 @@ generate_md_output(File, RegExp, Languages) -> Langs = binary:split(Languages, <<",">>, [global]), Header = <<"---\ntitle: Administration API reference\ntoc: true\nmenu: API Reference\norder: 1\n" "// Autogenerated with 'ejabberdctl gen_markdown_doc_for_commands'\n---\n\n" - "This section describes API of ejabberd.">>, + "This section describes API of ejabberd.\n">>, Out = lists:map(fun(C) -> gen_doc(C, false, Langs) end, Cmds4), {ok, Fh} = file:open(File, [write]), io:format(Fh, "~ts~ts", [Header, Out]), file:close(Fh), ok. +generate_tags_md(File) -> + Header = <<"---\ntitle: API Tags\ntoc: true\nmenu: API Tags\norder: 2\n" + "// Autogenerated with 'ejabberdctl gen_markdown_doc_for_tags'\n---\n\n" + "This section enumerates the tags and their associated API.\n">>, + Tags = make_tags(false), + {ok, Fh} = file:open(File, [write]), + io:format(Fh, "~ts~ts", [Header, Tags]), + file:close(Fh), + ok. + html_pre() -> " -- cgit v1.2.3 From 8b6c90c2d9ce6029b793f9715e1f3c57554eb2cb Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 23 Aug 2021 15:39:01 +0200 Subject: Tell dialyzer that gen_tags only cares about markdown output, not html --- src/ejabberd_commands_doc.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index 05948fa6c..eec334d04 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -365,6 +365,7 @@ make_tags(HTMLOutput) -> TagsList = ejabberd_commands:get_tags_commands(1000000), lists:map(fun(T) -> gen_tags(T, HTMLOutput) end, TagsList). +-dialyzer({no_match, gen_tags/2}). gen_tags({TagName, Commands}, HTMLOutput) -> [?TAG(h1, TagName) | [?TAG(p, ?RAW("* *`"++C++"`*")) || C <- Commands]]. -- cgit v1.2.3 From 4d0503b6b36c7bc704f3eb12b10b2db30b7add01 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 23 Aug 2021 15:49:52 +0200 Subject: Fix syntax in mod_disco example configuration --- src/mod_disco.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mod_disco.erl b/src/mod_disco.erl index cf888bc4e..deb9d15a2 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -464,11 +464,11 @@ mod_doc() -> " -", " modules: all", " name: abuse-addresses", - " urls: [mailto:abuse@shakespeare.lit]", + " urls: [\"mailto:abuse@shakespeare.lit\"]", " -", " modules: [mod_muc]", " name: \"Web chatroom logs\"", - " urls: [http://www.example.org/muc-logs]", + " urls: [\"http://www.example.org/muc-logs\"]", " -", " modules: [mod_disco]", " name: feedback-addresses", -- cgit v1.2.3 From c952cc420b345a0e43fc4e6d819b3edb3ac12cb1 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Mon, 23 Aug 2021 21:28:15 +0200 Subject: node_flat_sql: Avoid catch-all clauses for RSM Explicitly catch invalid and timestamps specified by clients in RSM queries, but crash on other errors, rather than silently ignoring those. --- src/node_flat_sql.erl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/node_flat_sql.erl b/src/node_flat_sql.erl index 724958eb1..240dc3760 100644 --- a/src/node_flat_sql.erl +++ b/src/node_flat_sql.erl @@ -1064,15 +1064,14 @@ rsm_page(Count, Index, Offset, Items) -> last = Last}. encode_stamp(Stamp) -> - case catch xmpp_util:decode_timestamp(Stamp) of - {MS,S,US} -> encode_now({MS,S,US}); - _ -> Stamp + try xmpp_util:decode_timestamp(Stamp) of + Now -> + encode_now(Now) + catch _:{bad_timestamp, _} -> + Stamp % We should return a proper error to the client instead. end. decode_stamp(Stamp) -> - case catch xmpp_util:encode_timestamp(decode_now(Stamp)) of - TimeStamp when is_binary(TimeStamp) -> TimeStamp; - _ -> Stamp - end. + xmpp_util:encode_timestamp(decode_now(Stamp)). encode_now({T1, T2, T3}) -> <<(misc:i2l(T1, 6))/binary, ":", -- cgit v1.2.3 From ebf03a3745ac4ba2089adc1ef872adfcfc081d56 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Mon, 23 Aug 2021 22:04:03 +0200 Subject: node_flat: Avoid catch-all clauses for RSM Apply the change made in the previous commit to Mnesia storage as well. --- src/node_flat.erl | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/node_flat.erl b/src/node_flat.erl index 97f149f9c..c597b9ce9 100644 --- a/src/node_flat.erl +++ b/src/node_flat.erl @@ -957,15 +957,12 @@ rsm_page(Count, Index, Offset, Items) -> last = Last}. encode_stamp(Stamp) -> - case catch xmpp_util:decode_timestamp(Stamp) of - {MS,S,US} -> {MS,S,US}; - _ -> Stamp + try xmpp_util:decode_timestamp(Stamp) + catch _:{bad_timestamp, _} -> + Stamp % We should return a proper error to the client instead. end. decode_stamp(Stamp) -> - case catch xmpp_util:encode_timestamp(Stamp) of - TimeStamp when is_binary(TimeStamp) -> TimeStamp; - _ -> Stamp - end. + xmpp_util:encode_timestamp(Stamp). transform({pubsub_state, {Id, Nidx}, Is, A, Ss}) -> {pubsub_state, {Id, Nidx}, Nidx, Is, A, Ss}; -- cgit v1.2.3 From b0da69f050399b376c93f8fc2a6898c6a974a83c Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 4 Aug 2021 10:25:39 +0200 Subject: Send ping from server, not bare user JID (#3658) --- src/mod_ping.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/mod_ping.erl b/src/mod_ping.erl index eee55825e..f51b929f1 100644 --- a/src/mod_ping.erl +++ b/src/mod_ping.erl @@ -154,7 +154,7 @@ handle_info({iq_reply, timeout, JID}, State) -> {noreply, State#state{timers = Timers}}; handle_info({timeout, _TRef, {ping, JID}}, State) -> Host = State#state.host, - From = jid:remove_resource(JID), + From = jid:make(Host), IQ = #iq{from = From, to = JID, type = get, sub_els = [#ping{}]}, ejabberd_router:route_iq(IQ, JID, gen_mod:get_module_proc(Host, ?MODULE), -- cgit v1.2.3 From f77686481a041812e99f46cbb1ee3d6d2770e9de Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 26 Aug 2021 17:21:23 +0200 Subject: Add internal links in WebAdmin Vhosts page --- src/ejabberd_web_admin.erl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 7dc11b571..e699ffc78 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -602,12 +602,14 @@ list_vhosts2(Lang, Hosts) -> [?AC(<<"../server/", Host/binary, "/">>, Host)]), - ?XAC(<<"td">>, + ?XAE(<<"td">>, [{<<"class">>, <<"alignright">>}], - (pretty_string_int(RegisteredUsers))), - ?XAC(<<"td">>, + [?AC(<<"../server/", Host/binary, "/users/">>, + pretty_string_int(RegisteredUsers))]), + ?XAE(<<"td">>, [{<<"class">>, <<"alignright">>}], - (pretty_string_int(OnlineUsers)))]) + [?AC(<<"../server/", Host/binary, "/users/">>, + pretty_string_int(RegisteredUsers))]), end, SHosts)))])]. -- cgit v1.2.3 From 91350ad472a13cac23af62212e0ad93c656ba2b0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 27 Aug 2021 13:39:06 +0200 Subject: Fix WebAdmin recent change --- src/ejabberd_web_admin.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index e699ffc78..ee50a0031 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -608,8 +608,8 @@ list_vhosts2(Lang, Hosts) -> pretty_string_int(RegisteredUsers))]), ?XAE(<<"td">>, [{<<"class">>, <<"alignright">>}], - [?AC(<<"../server/", Host/binary, "/users/">>, - pretty_string_int(RegisteredUsers))]), + [?AC(<<"../server/", Host/binary, "/online-users/">>, + pretty_string_int(OnlineUsers))])]) end, SHosts)))])]. -- cgit v1.2.3 From caf07692dbdabcd2e083530b18ee7a4eda90ea9b Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 5 Sep 2021 13:24:51 +0200 Subject: mod_register_web: Handle unknown host gracefully MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Return a proper error message on registration attempts against unknown hosts, rather than crashing. Thanks to Ingo Jürgensmann for reporting the bug. --- src/mod_register_web.erl | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/mod_register_web.erl b/src/mod_register_web.erl index 28bac0558..0e216c81c 100644 --- a/src/mod_register_web.erl +++ b/src/mod_register_web.erl @@ -507,14 +507,18 @@ form_del_get(Host, Lang) -> %% {error, not_allowed} | %% {error, invalid_jid} register_account(Username, Host, Password) -> - Access = mod_register_opt:access(Host), - case jid:make(Username, Host) of - error -> {error, invalid_jid}; - JID -> - case acl:match_rule(Host, Access, JID) of - deny -> {error, not_allowed}; - allow -> register_account2(Username, Host, Password) - end + try mod_register_opt:access(Host) of + Access -> + case jid:make(Username, Host) of + error -> {error, invalid_jid}; + JID -> + case acl:match_rule(Host, Access, JID) of + deny -> {error, not_allowed}; + allow -> register_account2(Username, Host, Password) + end + end + catch _:{module_not_loaded, mod_register, _Host} -> + {error, host_unknown} end. register_account2(Username, Host, Password) -> @@ -577,6 +581,8 @@ get_error_text({error, password_incorrect}) -> ?T("Incorrect password"); get_error_text({error, invalid_jid}) -> ?T("The username is not valid"); +get_error_text({error, host_unknown}) -> + ?T("Host unknown"); get_error_text({error, not_allowed}) -> ?T("Not allowed"); get_error_text({error, account_doesnt_exist}) -> -- cgit v1.2.3 From 868387a405093fad5df3d1ac09b3a4ec01e91800 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 5 Sep 2021 20:00:05 +0200 Subject: mod_http_upload_quota: Avoid 'max_days' race Try to spread clean-up runs for multiple hosts, rather than scheduling them in parallel. This should reduce I/O spikes, and avoid race conditions where multiple processes detect and then try to delete the same old files (if multiple hosts have the same 'docroot'). Fixes #3497. --- src/mod_http_upload_quota.erl | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/mod_http_upload_quota.erl b/src/mod_http_upload_quota.erl index 48b7b1958..5ed7fcefb 100644 --- a/src/mod_http_upload_quota.erl +++ b/src/mod_http_upload_quota.erl @@ -27,7 +27,6 @@ -author('holger@zedat.fu-berlin.de'). -define(TIMEOUT, timer:hours(24)). --define(INITIAL_TIMEOUT, timer:minutes(10)). -define(FORMAT(Error), file:format_error(Error)). -behaviour(gen_server). @@ -64,7 +63,7 @@ max_days :: pos_integer() | infinity, docroot :: binary(), disk_usage = #{} :: disk_usage(), - timers :: [timer:tref()]}). + timer :: reference() | undefined}). -type disk_usage() :: #{{binary(), binary()} => non_neg_integer()}. -type state() :: #state{}. @@ -166,12 +165,11 @@ init([ServerHost|_]) -> DocRoot1 = mod_http_upload_opt:docroot(ServerHost), DocRoot2 = mod_http_upload:expand_home(str:strip(DocRoot1, right, $/)), DocRoot3 = mod_http_upload:expand_host(DocRoot2, ServerHost), - Timers = if MaxDays == infinity -> []; - true -> - {ok, T1} = timer:send_after(?INITIAL_TIMEOUT, sweep), - {ok, T2} = timer:send_interval(?TIMEOUT, sweep), - [T1, T2] - end, + Timer = if MaxDays == infinity -> undefined; + true -> + Timeout = p1_rand:uniform(?TIMEOUT div 2), + erlang:send_after(Timeout, self(), sweep) + end, ejabberd_hooks:add(http_upload_slot_request, ServerHost, ?MODULE, handle_slot_request, 50), {ok, #state{server_host = ServerHost, @@ -179,7 +177,7 @@ init([ServerHost|_]) -> access_hard_quota = AccessHardQuota, max_days = MaxDays, docroot = DocRoot3, - timers = Timers}}. + timer = Timer}}. -spec handle_call(_, {pid(), _}, state()) -> {noreply, state()}. handle_call(Request, From, State) -> @@ -249,6 +247,7 @@ handle_info(sweep, #state{server_host = ServerHost, max_days = MaxDays} = State) when is_integer(MaxDays), MaxDays > 0 -> ?DEBUG("Got 'sweep' message for ~ts", [ServerHost]), + Timer = erlang:send_after(?TIMEOUT, self(), sweep), case file:list_dir(DocRoot) of {ok, Entries} -> BackThen = secs_since_epoch() - (MaxDays * 86400), @@ -264,17 +263,17 @@ handle_info(sweep, #state{server_host = ServerHost, ?ERROR_MSG("Cannot open document root ~ts: ~ts", [DocRoot, ?FORMAT(Error)]) end, - {noreply, State}; + {noreply, State#state{timer = Timer}}; handle_info(Info, State) -> ?ERROR_MSG("Unexpected info: ~p", [Info]), {noreply, State}. -spec terminate(normal | shutdown | {shutdown, _} | _, state()) -> ok. -terminate(Reason, #state{server_host = ServerHost, timers = Timers}) -> +terminate(Reason, #state{server_host = ServerHost, timer = Timer}) -> ?DEBUG("Stopping upload quota process for ~ts: ~p", [ServerHost, Reason]), ejabberd_hooks:delete(http_upload_slot_request, ServerHost, ?MODULE, handle_slot_request, 50), - lists:foreach(fun timer:cancel/1, Timers). + misc:cancel_timer(Timer). -spec code_change({down, _} | _, state(), _) -> {ok, state()}. code_change(_OldVsn, #state{server_host = ServerHost} = State, _Extra) -> -- cgit v1.2.3 From 3114ce4ed28e039803af732c1d29f6d0982fba29 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 8 Sep 2021 18:34:20 +0200 Subject: ejabberd_admin: Fix ejabberd_piefxis commands These days, the ejabberd_piefxis commands expect their arguments to be handed over as binary strings. --- src/ejabberd_admin.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 9cebd0bb5..0174cd7ff 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -254,19 +254,19 @@ get_commands_spec() -> module = ejabberd_piefxis, function = import_file, args_desc = ["Full path to the PIEFXIS file"], args_example = ["/var/lib/ejabberd/example.com.xml"], - args = [{file, string}], result = {res, rescode}}, + args = [{file, binary}], result = {res, rescode}}, #ejabberd_commands{name = export_piefxis, tags = [mnesia], desc = "Export data of all users in the server to PIEFXIS files (XEP-0227)", module = ejabberd_piefxis, function = export_server, args_desc = ["Full path to a directory"], args_example = ["/var/lib/ejabberd/"], - args = [{dir, string}], result = {res, rescode}}, + args = [{dir, binary}], result = {res, rescode}}, #ejabberd_commands{name = export_piefxis_host, tags = [mnesia], desc = "Export data of users in a host to PIEFXIS files (XEP-0227)", module = ejabberd_piefxis, function = export_host, args_desc = ["Full path to a directory", "Vhost to export"], args_example = ["/var/lib/ejabberd/", "example.com"], - args = [{dir, string}, {host, string}], result = {res, rescode}}, + args = [{dir, binary}, {host, binary}], result = {res, rescode}}, #ejabberd_commands{name = delete_mnesia, tags = [mnesia], desc = "Delete elements in Mnesia database for a given vhost", -- cgit v1.2.3 From 5abc03ff8fdd8bd68626637814ec65ee24d1adf7 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Mon, 13 Sep 2021 08:15:11 +0300 Subject: Optimize MucSub processing --- src/mod_muc.erl | 12 +- src/mod_muc_room.erl | 336 ++++++++++++++++++++++++++++++++++----------------- src/mod_muc_sql.erl | 24 +++- 3 files changed, 256 insertions(+), 116 deletions(-) (limited to 'src') diff --git a/src/mod_muc.erl b/src/mod_muc.erl index ccecb6b66..c8c1d4126 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -40,6 +40,7 @@ room_destroyed/4, store_room/4, store_room/5, + store_changes/4, restore_room/3, forget_room/3, create_room/3, @@ -91,6 +92,7 @@ -callback init(binary(), gen_mod:opts()) -> any(). -callback import(binary(), binary(), [binary()]) -> ok. -callback store_room(binary(), binary(), binary(), list(), list()|undefined) -> {atomic, any()}. +-callback store_changes(binary(), binary(), binary(), list()) -> {atomic, any()}. -callback restore_room(binary(), binary(), binary()) -> muc_room_opts() | error. -callback forget_room(binary(), binary(), binary()) -> {atomic, any()}. -callback can_use_nick(binary(), binary(), jid(), binary()) -> boolean(). @@ -111,7 +113,8 @@ -callback get_subscribed_rooms(binary(), binary(), jid()) -> {ok, [{jid(), binary(), [binary()]}]} | {error, db_failure}. --optional_callbacks([get_subscribed_rooms/3]). +-optional_callbacks([get_subscribed_rooms/3, + store_changes/4]). %%==================================================================== %% API @@ -313,6 +316,11 @@ store_room(ServerHost, Host, Name, Opts, ChangesHints) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:store_room(LServer, Host, Name, Opts, ChangesHints). +store_changes(ServerHost, Host, Name, ChangesHints) -> + LServer = jid:nameprep(ServerHost), + Mod = gen_mod:db_mod(LServer, ?MODULE), + Mod:store_changes(LServer, Host, Name, ChangesHints). + restore_room(ServerHost, Host, Name) -> LServer = jid:nameprep(ServerHost), Mod = gen_mod:db_mod(LServer, ?MODULE), @@ -570,7 +578,7 @@ unhibernate_room(ServerHost, Host, Room) -> case RMod:find_online_room(ServerHost, Room, Host) of error -> Proc = procname(ServerHost, {Room, Host}), - case ?GEN_SERVER:call(Proc, {unhibernate, Room, Host}) of + case ?GEN_SERVER:call(Proc, {unhibernate, Room, Host}, 20000) of {ok, _} = R -> R; _ -> error end; diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index fc03e50ef..035e851fd 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -641,7 +641,7 @@ handle_event({service_message, Msg}, _StateName, MessagePkt = #message{type = groupchat, body = xmpp:mk_text(Msg)}, send_wrapped_multiple( StateData#state.jid, - get_users_and_subscribers(StateData), + get_users_and_subscribers_with_node(?NS_MUCSUB_NODES_MESSAGES, StateData), MessagePkt, ?NS_MUCSUB_NODES_MESSAGES, StateData), @@ -705,7 +705,7 @@ handle_sync_event({change_state, NewStateData}, _From, true -> ok; _ -> - erlang:put(muc_subscribers, NewStateData#state.subscribers) + erlang:put(muc_subscribers, NewStateData#state.muc_subscribers#muc_subscribers.subscribers) end, {reply, {ok, NewStateData}, StateName, NewStateData}; handle_sync_event({process_item_change, Item, UJID}, _From, StateName, StateData) -> @@ -717,8 +717,10 @@ handle_sync_event({process_item_change, Item, UJID}, _From, StateName, StateData {reply, {ok, NSD}, StateName, NSD} end; handle_sync_event(get_subscribers, _From, StateName, StateData) -> - JIDs = lists:map(fun jid:make/1, - maps:keys(StateData#state.subscribers)), + JIDs = muc_subscribers_fold( + fun(_LBareJID, #subscriber{jid = JID}, Acc) -> + [JID | Acc] + end, [], StateData#state.muc_subscribers), {reply, {ok, JIDs}, StateName, StateData}; handle_sync_event({muc_subscribe, From, Nick, Nodes}, _From, StateName, StateData) -> @@ -762,7 +764,8 @@ handle_sync_event({muc_unsubscribe, From}, _From, StateName, {reply, {error, get_error_text(Err)}, StateName, StateData} end; handle_sync_event({is_subscribed, From}, _From, StateName, StateData) -> - IsSubs = try maps:get(jid:split(From), StateData#state.subscribers) of + IsSubs = try muc_subscribers_get( + jid:split(From), StateData#state.muc_subscribers) of #subscriber{nick = Nick, nodes = Nodes} -> {true, Nick, Nodes} catch _:{badkey, _} -> false end, @@ -899,7 +902,8 @@ terminate(Reason, _StateName, _ -> ok end, tab_remove_online_user(JID, StateData) - end, [], get_users_and_subscribers(StateData)), + end, [], get_users_and_subscribers_with_node( + ?NS_MUCSUB_NODES_PARTICIPANTS, StateData)), disable_hibernate_timer(StateData), case StateData#state.hibernate_timer of @@ -991,7 +995,7 @@ process_groupchat_message(#message{from = From, lang = Lang} = Packet, StateData end, send_wrapped_multiple( jid:replace_resource(StateData#state.jid, FromNick), - get_users_and_subscribers(StateData), + get_users_and_subscribers_with_node(Node, StateData), NewPacket, Node, NewStateData1), NewStateData2 = case has_body_or_subject(NewPacket) of true -> @@ -1197,8 +1201,8 @@ get_participant_data(From, StateData) -> #user{nick = FromNick, role = Role} -> {FromNick, Role} catch _:{badkey, _} -> - try maps:get(jid:tolower(jid:remove_resource(From)), - StateData#state.subscribers) of + try muc_subscribers_get(jid:tolower(jid:remove_resource(From)), + StateData#state.muc_subscribers) of #subscriber{nick = FromNick} -> {FromNick, none} catch _:{badkey, _} -> @@ -1329,7 +1333,7 @@ maybe_strip_status_from_presence(From, Packet, StateData) -> close_room_if_temporary_and_empty(StateData1) -> case not (StateData1#state.config)#config.persistent andalso maps:size(StateData1#state.users) == 0 - andalso maps:size(StateData1#state.subscribers) == 0 of + andalso muc_subscribers_size(StateData1#state.muc_subscribers) == 0 of true -> ?INFO_MSG("Destroyed MUC room ~ts because it's temporary " "and empty", @@ -1342,6 +1346,17 @@ close_room_if_temporary_and_empty(StateData1) -> -spec get_users_and_subscribers(state()) -> users(). get_users_and_subscribers(StateData) -> + get_users_and_subscribers_aux( + StateData#state.muc_subscribers#muc_subscribers.subscribers, + StateData). + +-spec get_users_and_subscribers_with_node(binary(), state()) -> users(). +get_users_and_subscribers_with_node(Node, StateData) -> + get_users_and_subscribers_aux( + muc_subscribers_get_by_node(Node, StateData#state.muc_subscribers), + StateData). + +get_users_and_subscribers_aux(Subscribers, StateData) -> OnlineSubscribers = maps:fold( fun(LJID, _, Acc) -> LBareJID = jid:remove_resource(LJID), @@ -1365,7 +1380,7 @@ get_users_and_subscribers(StateData) -> true -> Acc end - end, StateData#state.users, StateData#state.subscribers). + end, StateData#state.users, Subscribers). -spec is_user_online(jid(), state()) -> boolean(). is_user_online(JID, StateData) -> @@ -1375,7 +1390,7 @@ is_user_online(JID, StateData) -> -spec is_subscriber(jid(), state()) -> boolean(). is_subscriber(JID, StateData) -> LJID = jid:tolower(jid:remove_resource(JID)), - maps:is_key(LJID, StateData#state.subscribers). + muc_subscribers_is_key(LJID, StateData#state.muc_subscribers). %% Check if the user is occupant of the room, or at least is an admin or owner. -spec is_occupant_or_admin(jid(), state()) -> boolean(). @@ -1869,16 +1884,15 @@ set_subscriber(JID, Nick, Nodes, #state{room = Room, host = Host, server_host = ServerHost} = StateData) -> BareJID = jid:remove_resource(JID), LBareJID = jid:tolower(BareJID), - Subscribers = maps:put(LBareJID, - #subscriber{jid = BareJID, - nick = Nick, - nodes = Nodes}, - StateData#state.subscribers), - Nicks = maps:put(Nick, [LBareJID], StateData#state.subscriber_nicks), - NewStateData = StateData#state{subscribers = Subscribers, - subscriber_nicks = Nicks}, + MUCSubscribers = + muc_subscribers_put( + #subscriber{jid = BareJID, + nick = Nick, + nodes = Nodes}, + StateData#state.muc_subscribers), + NewStateData = StateData#state{muc_subscribers = MUCSubscribers}, store_room(NewStateData, [{add_subscription, BareJID, Nick, Nodes}]), - case not maps:is_key(LBareJID, StateData#state.subscribers) of + case not muc_subscribers_is_key(LBareJID, StateData#state.muc_subscribers) of true -> send_subscriptions_change_notifications(BareJID, Nick, subscribe, NewStateData), ejabberd_hooks:run(muc_subscribed, ServerHost, [ServerHost, Room, Host, BareJID]); @@ -1956,7 +1970,8 @@ add_user_presence_un(JID, Presence, StateData) -> -spec find_jids_by_nick(binary(), state()) -> [jid()]. find_jids_by_nick(Nick, StateData) -> Users = case maps:get(Nick, StateData#state.nicks, []) of - [] -> maps:get(Nick, StateData#state.subscriber_nicks, []); + [] -> muc_subscribers_get_by_nick( + Nick, StateData#state.muc_subscribers); Us -> Us end, [jid:make(LJID) || LJID <- Users]. @@ -2020,10 +2035,10 @@ is_nick_change(JID, Nick, StateData) -> nick_collision(User, Nick, StateData) -> UserOfNick = case find_jid_by_nick(Nick, StateData) of false -> - try maps:get(Nick, StateData#state.subscriber_nicks) of - [J] -> J - catch _:{badkey, _} -> false - end; + case muc_subscribers_get_by_nick(Nick, StateData#state.muc_subscribers) of + [J] -> J; + [] -> false + end; J -> J end, (UserOfNick /= false andalso @@ -2433,6 +2448,11 @@ send_new_presence(NJID, Reason, IsInitialPresence, StateData, OldStateData) -> false -> {none, #presence{type = unavailable}} end, Affiliation = get_affiliation(LJID, StateData), + Node1 = case is_ra_changed(NJID, IsInitialPresence, StateData, OldStateData) of + true -> ?NS_MUCSUB_NODES_AFFILIATIONS; + false -> ?NS_MUCSUB_NODES_PRESENCE + end, + Node2 = ?NS_MUCSUB_NODES_PARTICIPANTS, UserMap = case is_room_overcrowded(StateData) orelse (not (presence_broadcast_allowed(NJID, StateData) orelse @@ -2440,7 +2460,10 @@ send_new_presence(NJID, Reason, IsInitialPresence, StateData, OldStateData) -> true -> #{LNJID => UserInfo}; false -> - get_users_and_subscribers(StateData) + %% TODO: optimize further + UM1 = get_users_and_subscribers_with_node(Node1, StateData), + UM2 = get_users_and_subscribers_with_node(Node2, StateData), + maps:merge(UM1, UM2) end, maps:fold( fun(LUJID, Info, _) -> @@ -2465,10 +2488,6 @@ send_new_presence(NJID, Reason, IsInitialPresence, StateData, OldStateData) -> Packet = xmpp:set_subtag( Pres, #muc_user{items = [Item], status_codes = StatusCodes}), - Node1 = case is_ra_changed(NJID, IsInitialPresence, StateData, OldStateData) of - true -> ?NS_MUCSUB_NODES_AFFILIATIONS; - false -> ?NS_MUCSUB_NODES_PRESENCE - end, send_wrapped(jid:replace_resource(StateData#state.jid, Nick), Info#user.jid, Packet, Node1, StateData), Type = xmpp:get_type(Packet), @@ -2476,7 +2495,6 @@ send_new_presence(NJID, Reason, IsInitialPresence, StateData, OldStateData) -> IsOccupant = Info#user.last_presence /= undefined, if (IsSubscriber and not IsOccupant) and (IsInitialPresence or (Type == unavailable)) -> - Node2 = ?NS_MUCSUB_NODES_PARTICIPANTS, send_wrapped(jid:replace_resource(StateData#state.jid, Nick), Info#user.jid, Packet, Node2, StateData); true -> @@ -2607,11 +2625,13 @@ send_nick_changing(JID, OldNick, StateData, end; (_, _, _) -> ok - end, ok, get_users_and_subscribers(StateData)). + end, ok, get_users_and_subscribers_with_node( + ?NS_MUCSUB_NODES_PRESENCE, StateData)). -spec maybe_send_affiliation(jid(), affiliation(), state()) -> ok. maybe_send_affiliation(JID, Affiliation, StateData) -> LJID = jid:tolower(JID), + %% TODO: there should be a better way to check IsOccupant Users = get_users_and_subscribers(StateData), IsOccupant = case LJID of {LUser, LServer, <<"">>} -> @@ -2637,7 +2657,8 @@ send_affiliation(JID, Affiliation, StateData) -> role = none}, Message = #message{id = p1_rand:get_string(), sub_els = [#muc_user{items = [Item]}]}, - Users = get_users_and_subscribers(StateData), + Users = get_users_and_subscribers_with_node( + ?NS_MUCSUB_NODES_AFFILIATIONS, StateData), Recipients = case (StateData#state.config)#config.anonymous of true -> maps:filter(fun(_, #user{role = moderator}) -> @@ -3271,6 +3292,13 @@ send_kickban_presence1(MJID, UJID, Reason, Code, Affiliation, StateData) -> #user{jid = RealJID, nick = Nick} = maps:get(jid:tolower(UJID), StateData#state.users), ActorNick = get_actor_nick(MJID, StateData), + %% TODO: optimize further + UserMap = + maps:merge( + get_users_and_subscribers_with_node( + ?NS_MUCSUB_NODES_AFFILIATIONS, StateData), + get_users_and_subscribers_with_node( + ?NS_MUCSUB_NODES_PARTICIPANTS, StateData)), maps:fold( fun(LJID, Info, _) -> IsSelfPresence = jid:tolower(UJID) == LJID, @@ -3304,7 +3332,7 @@ send_kickban_presence1(MJID, UJID, Reason, Code, Affiliation, true -> ok end - end, ok, get_users_and_subscribers(StateData)). + end, ok, UserMap). -spec get_actor_nick(undefined | jid(), state()) -> binary(). get_actor_nick(undefined, _StateData) -> @@ -3720,7 +3748,8 @@ send_config_change_info(New, #state{config = Old} = StateData) -> id = p1_rand:get_string(), sub_els = [#muc_user{status_codes = Codes}]}, send_wrapped_multiple(StateData#state.jid, - get_users_and_subscribers(StateData), + get_users_and_subscribers_with_node( + ?NS_MUCSUB_NODES_CONFIG, StateData), Message, ?NS_MUCSUB_NODES_CONFIG, StateData); @@ -3872,26 +3901,23 @@ set_opts([{Opt, Val} | Opts], StateData) -> StateData#state{config = (StateData#state.config)#config{lang = Val}}; subscribers -> - {Subscribers, Nicks} = - lists:foldl( - fun({JID, Nick, Nodes}, {SubAcc, NickAcc}) -> - BareJID = case JID of - #jid{} -> jid:remove_resource(JID); - _ -> - ?ERROR_MSG("Invalid subscriber JID in set_opts ~p", [JID]), - jid:remove_resource(jid:make(JID)) - end, - LBareJID = jid:tolower(BareJID), - {maps:put( - LBareJID, - #subscriber{jid = BareJID, - nick = Nick, - nodes = Nodes}, - SubAcc), - maps:put(Nick, [LBareJID], NickAcc)} - end, {#{}, #{}}, Val), - StateData#state{subscribers = Subscribers, - subscriber_nicks = Nicks}; + MUCSubscribers = + lists:foldl( + fun({JID, Nick, Nodes}, MUCSubs) -> + BareJID = + case JID of + #jid{} -> jid:remove_resource(JID); + _ -> + ?ERROR_MSG("Invalid subscriber JID in set_opts ~p", [JID]), + jid:remove_resource(jid:make(JID)) + end, + muc_subscribers_put( + #subscriber{jid = BareJID, + nick = Nick, + nodes = Nodes}, + MUCSubs) + end, muc_subscribers_new(), Val), + StateData#state{muc_subscribers = MUCSubscribers}; affiliations -> StateData#state{affiliations = maps:from_list(Val)}; subject -> @@ -3926,12 +3952,12 @@ set_vcard_xupdate(State) -> -spec make_opts(state()) -> [{atom(), any()}]. make_opts(StateData) -> Config = StateData#state.config, - Subscribers = maps:fold( + Subscribers = muc_subscribers_fold( fun(_LJID, Sub, Acc) -> [{Sub#subscriber.jid, Sub#subscriber.nick, Sub#subscriber.nodes}|Acc] - end, [], StateData#state.subscribers), + end, [], StateData#state.muc_subscribers), [?MAKE_CONFIG_OPT(#config.title), ?MAKE_CONFIG_OPT(#config.description), ?MAKE_CONFIG_OPT(#config.allow_change_subj), ?MAKE_CONFIG_OPT(#config.allow_query_users), @@ -4013,7 +4039,8 @@ destroy_room(DEl, StateData) -> send_wrapped(jid:replace_resource(StateData#state.jid, Nick), Info#user.jid, Packet, ?NS_MUCSUB_NODES_CONFIG, StateData) - end, ok, get_users_and_subscribers(StateData)), + end, ok, get_users_and_subscribers_with_node( + ?NS_MUCSUB_NODES_CONFIG, StateData)), forget_room(StateData), {result, undefined, stop}. @@ -4248,30 +4275,35 @@ process_iq_mucsub(From, sub_els = [#muc_subscribe{nick = Nick}]} = Packet, StateData) -> LBareJID = jid:tolower(jid:remove_resource(From)), - try maps:get(LBareJID, StateData#state.subscribers) of + try muc_subscribers_get(LBareJID, StateData#state.muc_subscribers) of #subscriber{nick = Nick1} when Nick1 /= Nick -> Nodes = get_subscription_nodes(Packet), - case {nick_collision(From, Nick, StateData), - mod_muc:can_use_nick(StateData#state.server_host, - StateData#state.host, - From, Nick)} of - {true, _} -> + case nick_collision(From, Nick, StateData) of + true -> ErrText = ?T("That nickname is already in use by another occupant"), {error, xmpp:err_conflict(ErrText, Lang)}; - {_, false} -> - Err = case Nick of - <<>> -> - xmpp:err_jid_malformed(?T("Nickname can't be empty"), - Lang); - _ -> - xmpp:err_conflict(?T("That nickname is registered" - " by another person"), Lang) - end, - {error, Err}; - _ -> - NewStateData = set_subscriber(From, Nick, Nodes, StateData), - {result, subscribe_result(Packet), NewStateData} - end; + false -> + case mod_muc:can_use_nick(StateData#state.server_host, + StateData#state.host, + From, Nick) of + false -> + Err = case Nick of + <<>> -> + xmpp:err_jid_malformed( + ?T("Nickname can't be empty"), + Lang); + _ -> + xmpp:err_conflict( + ?T("That nickname is registered" + " by another person"), Lang) + end, + {error, Err}; + true -> + NewStateData = + set_subscriber(From, Nick, Nodes, StateData), + {result, subscribe_result(Packet), NewStateData} + end + end; #subscriber{} -> Nodes = get_subscription_nodes(Packet), NewStateData = set_subscriber(From, Nick, Nodes, StateData), @@ -4298,12 +4330,9 @@ process_iq_mucsub(From, #iq{type = set, sub_els = [#muc_unsubscribe{}]}, #state{room = Room, host = Host, server_host = ServerHost} = StateData) -> BareJID = jid:remove_resource(From), LBareJID = jid:tolower(BareJID), - try maps:get(LBareJID, StateData#state.subscribers) of - #subscriber{nick = Nick} -> - Nicks = maps:remove(Nick, StateData#state.subscriber_nicks), - Subscribers = maps:remove(LBareJID, StateData#state.subscribers), - NewStateData = StateData#state{subscribers = Subscribers, - subscriber_nicks = Nicks}, + try muc_subscribers_remove_exn(LBareJID, StateData#state.muc_subscribers) of + {MUCSubscribers, #subscriber{nick = Nick}} -> + NewStateData = StateData#state{muc_subscribers = MUCSubscribers}, store_room(NewStateData, [{del_subscription, LBareJID}]), send_subscriptions_change_notifications(BareJID, Nick, unsubscribe, StateData), ejabberd_hooks:run(muc_unsubscribed, ServerHost, [ServerHost, Room, Host, BareJID]), @@ -4326,7 +4355,7 @@ process_iq_mucsub(From, #iq{type = get, lang = Lang, true -> ShowJid = IsModerator orelse (StateData#state.config)#config.anonymous == false, - Subs = maps:fold( + Subs = muc_subscribers_fold( fun(_, #subscriber{jid = J, nick = N, nodes = Nodes}, Acc) -> case ShowJid of true -> @@ -4334,7 +4363,7 @@ process_iq_mucsub(From, #iq{type = get, lang = Lang, _ -> [#muc_subscription{nick = N, events = Nodes}|Acc] end - end, [], StateData#state.subscribers), + end, [], StateData#state.muc_subscribers), {result, #muc_subscriptions{list = Subs}, StateData}; _ -> Txt = ?T("Moderator privileges required"), @@ -4347,8 +4376,7 @@ process_iq_mucsub(_From, #iq{type = get, lang = Lang}, _StateData) -> -spec remove_subscriptions(state()) -> state(). remove_subscriptions(StateData) -> if not (StateData#state.config)#config.allow_subscription -> - StateData#state{subscribers = #{}, - subscriber_nicks = #{}}; + StateData#state{muc_subscribers = muc_subscribers_new()}; true -> StateData end. @@ -4597,7 +4625,7 @@ store_room(StateData, ChangesHints) -> true -> ok; _ -> - erlang:put(muc_subscribers, StateData#state.subscribers) + erlang:put(muc_subscribers, StateData#state.muc_subscribers#muc_subscribers.subscribers) end, ShouldStore = case (StateData#state.config)#config.persistent of true -> @@ -4611,7 +4639,15 @@ store_room(StateData, ChangesHints) -> end end, if ShouldStore -> - store_room_no_checks(StateData, ChangesHints); + case erlang:function_exported(Mod, store_changes, 4) of + true when ChangesHints /= [] -> + mod_muc:store_changes( + StateData#state.server_host, + StateData#state.host, StateData#state.room, + ChangesHints); + _ -> + store_room_no_checks(StateData, ChangesHints) + end; true -> ok end. @@ -4625,22 +4661,19 @@ store_room_no_checks(StateData, ChangesHints) -> -spec send_subscriptions_change_notifications(jid(), binary(), subscribe|unsubscribe, state()) -> ok. send_subscriptions_change_notifications(From, Nick, Type, State) -> {WJ, WN} = - maps:fold( - fun(_, #subscriber{nodes = Nodes, jid = JID}, {WithJid, WithNick} = Res) -> - case lists:member(?NS_MUCSUB_NODES_SUBSCRIBERS, Nodes) of - true -> - case (State#state.config)#config.anonymous == false orelse - get_role(JID, State) == moderator orelse - get_default_role(get_affiliation(JID, State), State) == moderator of - true -> - {[JID | WithJid], WithNick}; - _ -> - {WithJid, [JID | WithNick]} - end; - false -> - Res - end - end, {[], []}, State#state.subscribers), + maps:fold( + fun(_, #subscriber{jid = JID}, {WithJid, WithNick}) -> + case (State#state.config)#config.anonymous == false orelse + get_role(JID, State) == moderator orelse + get_default_role(get_affiliation(JID, State), State) == moderator of + true -> + {[JID | WithJid], WithNick}; + _ -> + {WithJid, [JID | WithNick]} + end + end, {[], []}, + muc_subscribers_get_by_node(?NS_MUCSUB_NODES_SUBSCRIBERS, + State#state.muc_subscribers)), if WJ /= [] -> Payload1 = case Type of subscribe -> #muc_subscribe{jid = From, nick = Nick}; @@ -4684,7 +4717,7 @@ send_wrapped(From, To, Packet, Node, State) -> _ -> false end, if IsOffline -> - try maps:get(LBareTo, State#state.subscribers) of + try muc_subscribers_get(LBareTo, State#state.muc_subscribers) of #subscriber{nodes = Nodes, jid = JID} -> case lists:member(Node, Nodes) of true -> @@ -4751,12 +4784,13 @@ send_wrapped_multiple(From, Users, Packet, Node, State) -> IsOffline = LP == undefined, if IsOffline -> LBareTo = jid:tolower(jid:remove_resource(To)), - case maps:find(LBareTo, State#state.subscribers) of + case muc_subscribers_find(LBareTo, State#state.muc_subscribers) of {ok, #subscriber{nodes = Nodes}} -> case lists:member(Node, Nodes) of true -> {Direct, [To | Wrapped]}; _ -> + %% TODO: check that this branch is never called Res end; _ -> @@ -4810,6 +4844,90 @@ send_wrapped_multiple(From, Users, Packet, Node, State) -> Wra, NewPacket2, true) end. +%%%---------------------------------------------------------------------- +%%% #muc_subscribers API +%%%---------------------------------------------------------------------- + +-spec muc_subscribers_new() -> #muc_subscribers{}. +muc_subscribers_new() -> + #muc_subscribers{}. + +-spec muc_subscribers_get(ljid(), #muc_subscribers{}) -> #subscriber{}. +muc_subscribers_get({_, _, _} = LJID, MUCSubscribers) -> + maps:get(LJID, MUCSubscribers#muc_subscribers.subscribers). + +-spec muc_subscribers_find(ljid(), #muc_subscribers{}) -> + {ok, #subscriber{}} | error. +muc_subscribers_find({_, _, _} = LJID, MUCSubscribers) -> + maps:find(LJID, MUCSubscribers#muc_subscribers.subscribers). + +-spec muc_subscribers_is_key(ljid(), #muc_subscribers{}) -> boolean(). +muc_subscribers_is_key({_, _, _} = LJID, MUCSubscribers) -> + maps:is_key(LJID, MUCSubscribers#muc_subscribers.subscribers). + +-spec muc_subscribers_size(#muc_subscribers{}) -> integer(). +muc_subscribers_size(MUCSubscribers) -> + maps:size(MUCSubscribers#muc_subscribers.subscribers). + +-spec muc_subscribers_fold(Fun, Acc, #muc_subscribers{}) -> Acc when + Fun :: fun((ljid(), #subscriber{}, Acc) -> Acc). +muc_subscribers_fold(Fun, Init, MUCSubscribers) -> + maps:fold(Fun, Init, MUCSubscribers#muc_subscribers.subscribers). + +-spec muc_subscribers_get_by_nick(binary(), #muc_subscribers{}) -> [#subscriber{}]. +muc_subscribers_get_by_nick(Nick, MUCSubscribers) -> + maps:get(Nick, MUCSubscribers#muc_subscribers.subscriber_nicks, []). + +-spec muc_subscribers_get_by_node(binary(), #muc_subscribers{}) -> subscribers(). +muc_subscribers_get_by_node(Node, MUCSubscribers) -> + maps:get(Node, MUCSubscribers#muc_subscribers.subscriber_nodes, #{}). + +-spec muc_subscribers_remove_exn(ljid(), #muc_subscribers{}) -> + {#muc_subscribers{}, #subscriber{}}. +muc_subscribers_remove_exn({_, _, _} = LJID, MUCSubscribers) -> + #muc_subscribers{subscribers = Subs, + subscriber_nicks = SubNicks, + subscriber_nodes = SubNodes} = MUCSubscribers, + Subscriber = maps:get(LJID, Subs), + #subscriber{nick = Nick, nodes = Nodes} = Subscriber, + NewSubNicks = maps:remove(Nick, SubNicks), + NewSubs = maps:remove(LJID, Subs), + NewSubNodes = + lists:foldl( + fun(Node, Acc) -> + NodeSubs = maps:get(Node, Acc, #{}), + NodeSubs2 = maps:remove(LJID, NodeSubs), + maps:put(Node, NodeSubs2, Acc) + end, SubNodes, Nodes), + {#muc_subscribers{subscribers = NewSubs, + subscriber_nicks = NewSubNicks, + subscriber_nodes = NewSubNodes}, Subscriber}. + +-spec muc_subscribers_put(#subscriber{}, #muc_subscribers{}) -> + #muc_subscribers{}. +muc_subscribers_put(Subscriber, MUCSubscribers) -> + #subscriber{jid = JID, + nick = Nick, + nodes = Nodes} = Subscriber, + #muc_subscribers{subscribers = Subs, + subscriber_nicks = SubNicks, + subscriber_nodes = SubNodes} = MUCSubscribers, + LJID = jid:tolower(JID), + NewSubs = maps:put(LJID, Subscriber, Subs), + NewSubNicks = maps:put(Nick, [LJID], SubNicks), + NewSubNodes = + lists:foldl( + fun(Node, Acc) -> + NodeSubs = maps:get(Node, Acc, #{}), + NodeSubs2 = maps:put(LJID, Subscriber, NodeSubs), + maps:put(Node, NodeSubs2, Acc) + end, SubNodes, Nodes), + #muc_subscribers{subscribers = NewSubs, + subscriber_nicks = NewSubNicks, + subscriber_nodes = NewSubNodes}. + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Detect messange stanzas that don't have meaningful content -spec has_body_or_subject(message()) -> boolean(). diff --git a/src/mod_muc_sql.erl b/src/mod_muc_sql.erl index 569cfac49..1310cde7b 100644 --- a/src/mod_muc_sql.erl +++ b/src/mod_muc_sql.erl @@ -29,7 +29,8 @@ -behaviour(mod_muc_room). %% API --export([init/2, store_room/5, restore_room/3, forget_room/3, +-export([init/2, store_room/5, store_changes/4, + restore_room/3, forget_room/3, can_use_nick/4, get_rooms/2, get_nick/3, set_nick/4, import/3, export/1]). -export([register_online_room/4, unregister_online_room/4, find_online_room/3, @@ -83,6 +84,12 @@ store_room(LServer, Host, Name, Opts, ChangesHints) -> end, ejabberd_sql:sql_transaction(LServer, F). +store_changes(LServer, Host, Name, Changes) -> + F = fun () -> + [change_room(Host, Name, Change) || Change <- Changes] + end, + ejabberd_sql:sql_transaction(LServer, F). + change_room(Host, Room, {add_subscription, JID, Nick, Nodes}) -> SJID = jid:encode(JID), SNodes = misc:term_to_expr(Nodes), @@ -185,13 +192,20 @@ get_rooms(LServer, Host) -> {selected, Subs} -> SubsD = lists:foldl( fun({Room, Jid, Nick, Nodes}, Dict) -> - dict:append(Room, {jid:decode(Jid), - Nick, ejabberd_sql:decode_term(Nodes)}, Dict) - end, dict:new(), Subs), + Sub = {jid:decode(Jid), + Nick, ejabberd_sql:decode_term(Nodes)}, + maps:update_with( + Room, + fun(SubAcc) -> + [Sub | SubAcc] + end, + [Sub], + Dict) + end, maps:new(), Subs), lists:map( fun({Room, Opts}) -> OptsD = ejabberd_sql:decode_term(Opts), - OptsD2 = case {dict:find(Room, SubsD), lists:keymember(subscribers, 1, OptsD)} of + OptsD2 = case {maps:find(Room, SubsD), lists:keymember(subscribers, 1, OptsD)} of {_, true} -> store_room(LServer, Host, Room, mod_muc:opts_to_binary(OptsD), undefined), OptsD; -- cgit v1.2.3 From f8167fc5d091bd4eb6352b51843652e38c9b7fe9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 14 Sep 2021 11:24:49 +0200 Subject: Update documentation to match the implemented options values (#3675) --- src/ejabberd_options_doc.erl | 14 ++++++++------ src/mod_admin_extra.erl | 11 +---------- src/mod_http_upload.erl | 5 +++-- src/mod_muc.erl | 9 +++++---- src/mod_ping.erl | 2 +- src/mod_register.erl | 2 +- src/mod_stream_mgmt.erl | 4 +++- src/mod_stun_disco.erl | 2 +- 8 files changed, 23 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 28a6f6333..a725f4572 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -61,7 +61,7 @@ doc() -> desc => ?T("The time of a cached item to keep in cache. " "Once it's expired, the corresponding item is " - "erased from cache. The default value is 'one hour'. " + "erased from cache. The default value is '1 hour'. " "Several modules have a similar option; and some core " "ejabberd parts support similar options too, see " "_`auth_cache_life_time`_, _`oauth_cache_life_time`_, " @@ -382,14 +382,15 @@ doc() -> "from the stored information; for this reason, when this " "value is configured it cannot be changed to plain anymore. " "This format allows clients to authenticate using: " - "SASL PLAIN and SASL SCRAM-SHA-1.")]}}, + "SASL PLAIN and SASL SCRAM-SHA-1."), + ?T("The default value is 'plain'.")]}}, {auth_scram_hash, #{value => "sha | sha256 | sha512", desc => ?T("Hash algorith that should be used to store password in SCRAM format. " "You shouldn't change this if you already have passwords generated with " "a different algorithm - users that have such passwords will not be able " - "to authenticate.")}}, + "to authenticate. The default value is 'sha'.")}}, {auth_use_cache, #{value => "true | false", desc => @@ -678,7 +679,8 @@ doc() -> desc => ?T("The option defines the default language of server strings " "that can be seen by XMPP clients. If an XMPP client does not " - "possess 'xml:lang' attribute, the specified language is used.")}}, + "possess 'xml:lang' attribute, the specified language is used. " + "The default value is '\"en\"'.")}}, {ldap_servers, #{value => "[Host, ...]", desc => @@ -840,7 +842,7 @@ doc() -> "must have identical value on all nodes, or it will lead to subtle " "bugs. Usually leaving default value of this is option is best, " "tweak it only if you know what you are doing. " - "The default value is '1' minute.")}}, + "The default value is '1 minute'.")}}, {new_sql_schema, #{value => "true | false", desc => @@ -1154,7 +1156,7 @@ doc() -> #{value => "timeout()", desc => ?T("A time to wait before closing an idle s2s connection. " - "The default value is '10' minutes.")}}, + "The default value is '10 minutes'.")}}, {s2s_use_starttls, #{value => "true | false | optional | required", desc => diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index fe0dd2757..9834acf01 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -1652,14 +1652,6 @@ mod_doc() -> ?T("If you want to put a group Name with blankspaces, use the " "characters \"\' and \'\" to define when the Name starts and " "ends. See an example below.")], - opts => - [{module_resource, - #{value => ?T("Resource"), - desc => - ?T("Indicate the resource that the XMPP stanzas must use " - "in the FROM or TO JIDs. This is only useful in the " - "'get_vcard*' and 'set_vcard*' commands. The default " - "value is 'mod_admin_extra'.")}}], example => [{?T("With this configuration, vCards can only be modified with " "mod_admin_extra commands:"), @@ -1670,8 +1662,7 @@ mod_doc() -> " vcard_set:", " - allow: adminextraresource", "modules:", - " mod_admin_extra:", - " module_resource: \"modadminextraf8x,31ad\"", + " mod_admin_extra: {}", " mod_vcard:", " access_set: vcard_set"]}, {?T("Content of roster file for 'pushroster' command:"), diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index 3303f17c0..1ff94eb4a 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -321,12 +321,13 @@ mod_doc() -> "used for file uploads. The keyword @HOST@ is replaced " "with the virtual host name. NOTE: different virtual " "hosts cannot use the same PUT URL. " - "The default value is \"https://@HOST@:5443\".")}}, + "The default value is \"https://@HOST@:5443/upload\".")}}, {get_url, #{value => ?T("URL"), desc => ?T("This option specifies the initial part of the GET URLs " - "used for downloading the files. By default, it is set " + "used for downloading the files. The default value is 'undefined'. " + "When this option is 'undefined', this option is set " "to the same value as 'put_url'. The keyword @HOST@ is " "replaced with the virtual host name. NOTE: if GET requests " "are handled by 'mod_http_upload', the 'get_url' must match the " diff --git a/src/mod_muc.erl b/src/mod_muc.erl index c8c1d4126..05b76f2fc 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1366,19 +1366,19 @@ mod_doc() -> desc => ?T("To configure who is allowed to create new rooms at the " "Multi-User Chat service, this option can be used. " - "By default any account in the local ejabberd server is " + "The default value is 'all', which means everyone is " "allowed to create rooms.")}}, {access_persistent, #{value => ?T("AccessName"), desc => ?T("To configure who is allowed to modify the 'persistent' room option. " - "By default any account in the local ejabberd server is allowed to " + "The default value is 'all', which means everyone is allowed to" "modify that option.")}}, {access_mam, #{value => ?T("AccessName"), desc => ?T("To configure who is allowed to modify the 'mam' room option. " - "By default any account in the local ejabberd server is allowed to " + "The default value is 'all', which means everyone is allowed to" "modify that option.")}}, {access_register, #{value => ?T("AccessName"), @@ -1494,7 +1494,8 @@ mod_doc() -> ?T("This option defines after how many users in the room, " "it is considered overcrowded. When a MUC room is considered " "overcrowed, presence broadcasts are limited to reduce load, " - "traffic and excessive presence \"storm\" received by participants.")}}, + "traffic and excessive presence \"storm\" received by participants. " + "The default value is '1000'.")}}, {min_message_interval, #{value => ?T("Number"), desc => diff --git a/src/mod_ping.erl b/src/mod_ping.erl index f51b929f1..f233b2ae8 100644 --- a/src/mod_ping.erl +++ b/src/mod_ping.erl @@ -300,7 +300,7 @@ mod_doc() -> desc => ?T("How long to wait before deeming that a client " "has not answered a given server ping request. " - "The default value is '32' seconds.")}}, + "The default value is 'undefined'.")}}, {send_pings, #{value => "true | false", desc => diff --git a/src/mod_register.erl b/src/mod_register.erl index 919440b23..379318da6 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -679,7 +679,7 @@ mod_doc() -> "https://en.wikipedia.org/wiki/Entropy_(information_theory)" "[Shannon entropy] for passwords. The value 'Entropy' is a " "number of bits of entropy. The recommended minimum is 32 bits. " - "The default is 0, i.e. no checks are performed.")}}, + "The default is '0', i.e. no checks are performed.")}}, {registration_watchers, #{value => "[JID, ...]", desc => diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 5d2998500..f60f6722b 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -970,4 +970,6 @@ mod_doc() -> {cache_life_time, #{value => "timeout()", desc => - ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}]}. + ?T("Same as top-level _`cache_life_time`_ option, " + "but applied to this module only. " + "The default value is '48 hours'.")}}]}. diff --git a/src/mod_stun_disco.erl b/src/mod_stun_disco.erl index bb701b96b..6e7592453 100644 --- a/src/mod_stun_disco.erl +++ b/src/mod_stun_disco.erl @@ -176,7 +176,7 @@ mod_doc() -> "clients. If ejabberd's built-in TURN service is used, " "TURN relays allocated using temporary credentials will " "be terminated shortly after the credentials expired. The " - "default value is '12' hours. Note that restarting the " + "default value is '12 hours'. Note that restarting the " "ejabberd node invalidates any temporary credentials " "offered before the restart unless a 'secret' is " "specified (see below).")}}, -- cgit v1.2.3 From 2f5b15129a326e9c9729779cc609bcecf05be0ee Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 14 Sep 2021 15:13:37 +0200 Subject: Fix previous commit: add forgotten endline blankspaces --- src/mod_muc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 05b76f2fc..b2ebc5c61 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1372,13 +1372,13 @@ mod_doc() -> #{value => ?T("AccessName"), desc => ?T("To configure who is allowed to modify the 'persistent' room option. " - "The default value is 'all', which means everyone is allowed to" + "The default value is 'all', which means everyone is allowed to " "modify that option.")}}, {access_mam, #{value => ?T("AccessName"), desc => ?T("To configure who is allowed to modify the 'mam' room option. " - "The default value is 'all', which means everyone is allowed to" + "The default value is 'all', which means everyone is allowed to " "modify that option.")}}, {access_register, #{value => ?T("AccessName"), -- cgit v1.2.3