diff options
author | Holger Weiss <holger@zedat.fu-berlin.de> | 2019-02-20 17:01:34 +0100 |
---|---|---|
committer | Holger Weiss <holger@zedat.fu-berlin.de> | 2019-02-20 17:01:34 +0100 |
commit | 9c66cc58857f8854654a0edb3bcbd161fbf95bcb (patch) | |
tree | 218046fd5144b47327af8bd890887fc5cf882aca /src | |
parent | Don't crash on malformed 'modules' section (diff) | |
parent | disallow room creation if archive not empty and (diff) |
Merge remote-tracking branch 'processone/pr/2763'
* processone/pr/2763:
disallow room creation if archive not empty and clear_archive_on_room_destroy is false
check if mod_mam is loaded before calling mod_mam:is_empty_for_room
added cmds to list and destroy empty rooms by ejabberdctl
allow check if archive is empty for or user or room
option to prevent archive removal on room destroy
Diffstat (limited to 'src')
-rw-r--r-- | src/mod_mam.erl | 49 | ||||
-rw-r--r-- | src/mod_mam_mnesia.erl | 10 | ||||
-rw-r--r-- | src/mod_mam_sql.erl | 20 | ||||
-rw-r--r-- | src/mod_muc.erl | 14 | ||||
-rw-r--r-- | src/mod_muc_admin.erl | 73 |
5 files changed, 137 insertions, 29 deletions
diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 294d4f401..7dc80e462 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -40,7 +40,8 @@ muc_process_iq/2, muc_filter_message/3, message_is_archived/3, delete_old_messages/2, get_commands_spec/0, msg_to_el/4, get_room_config/4, set_room_option/3, offline_message/1, export/1, - mod_options/1, remove_mam_for_user_with_peer/3, remove_mam_for_user/2]). + mod_options/1, remove_mam_for_user_with_peer/3, remove_mam_for_user/2, + is_empty_for_user/2, is_empty_for_room/3, check_create_room/4]). -include("xmpp.hrl"). -include("logger.hrl"). @@ -72,6 +73,8 @@ -callback use_cache(binary()) -> boolean(). -callback cache_nodes(binary()) -> [node()]. -callback remove_from_archive(binary(), binary(), jid() | none) -> ok | {error, any()}. +-callback is_empty_for_user(binary(), binary()) -> boolean(). +-callback is_empty_for_room(binary(), binary(), binary()) -> boolean(). -optional_callbacks([use_cache/1, cache_nodes/1]). @@ -113,8 +116,6 @@ start(Host, Opts) -> disco_sm_features, 50), ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50), - ejabberd_hooks:add(remove_room, Host, ?MODULE, - remove_room, 50), ejabberd_hooks:add(get_room_config, Host, ?MODULE, get_room_config, 50), ejabberd_hooks:add(set_room_option, Host, ?MODULE, @@ -126,11 +127,22 @@ start(Host, Opts) -> false -> ok end, + case gen_mod:get_opt(clear_archive_on_room_destroy, Opts) of + true -> + ejabberd_hooks:add(remove_room, Host, ?MODULE, + remove_room, 50); + false -> + ejabberd_hooks:add(check_create_room, Host, ?MODULE, + check_create_room, 50) + end, ejabberd_commands:register_commands(get_commands_spec()); Err -> Err end. + + + use_cache(Mod, Host) -> case erlang:function_exported(Mod, use_cache, 2) of true -> Mod:use_cache(Host); @@ -180,8 +192,6 @@ stop(Host) -> disco_sm_features, 50), ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50), - ejabberd_hooks:delete(remove_room, Host, ?MODULE, - remove_room, 50), ejabberd_hooks:delete(get_room_config, Host, ?MODULE, get_room_config, 50), ejabberd_hooks:delete(set_room_option, Host, ?MODULE, @@ -193,6 +203,14 @@ stop(Host) -> false -> ok end, + case gen_mod:get_module_opt(Host, ?MODULE, clear_archive_on_room_destroy) of + true -> + ejabberd_hooks:delete(remove_room, Host, ?MODULE, + remove_room, 50); + false -> + ejabberd_hooks:delete(check_create_room, Host, ?MODULE, + check_create_room, 50) + end, case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of false -> ejabberd_commands:unregister_commands(get_commands_spec()); @@ -559,6 +577,24 @@ export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:export(LServer). +-spec is_empty_for_user(binary(), binary()) -> boolean(). +is_empty_for_user(User, Server) -> + LUser = jid:nodeprep(User), + LServer = jid:nameprep(Server), + Mod = gen_mod:db_mod(LServer, ?MODULE), + Mod:is_empty_for_user(LUser, LServer). + +-spec is_empty_for_room(binary(), binary(), binary()) -> boolean(). +is_empty_for_room(LServer, Name, Host) -> + LName = jid:nodeprep(Name), + LHost = jid:nameprep(Host), + Mod = gen_mod:db_mod(LServer, ?MODULE), + Mod:is_empty_for_room(LServer, LName, LHost). + +-spec check_create_room(boolean(), binary(), binary(), binary()) -> boolean(). +check_create_room(Acc, ServerHost, RoomID, Host) -> + Acc and is_empty_for_room(ServerHost, RoomID, Host). + %%%=================================================================== %%% Internal functions %%%=================================================================== @@ -1211,6 +1247,8 @@ mod_opt_type(default) -> (roster) -> roster end; mod_opt_type(request_activates_archiving) -> + fun (B) when is_boolean(B) -> B end; +mod_opt_type(clear_archive_on_room_destroy) -> fun (B) when is_boolean(B) -> B end. mod_options(Host) -> @@ -1218,6 +1256,7 @@ mod_options(Host) -> {default, never}, {request_activates_archiving, false}, {compress_xml, false}, + {clear_archive_on_room_destroy, true}, {db_type, ejabberd_config:default_db(Host, ?MODULE)}, {use_cache, ejabberd_config:use_cache(Host)}, {cache_size, ejabberd_config:cache_size(Host)}, diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl index 08d551585..f94dd2e49 100644 --- a/src/mod_mam_mnesia.erl +++ b/src/mod_mam_mnesia.erl @@ -28,7 +28,8 @@ %% API -export([init/2, remove_user/2, remove_room/3, delete_old_messages/3, - extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/6, remove_from_archive/3]). + extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/6, remove_from_archive/3, + is_empty_for_user/2, is_empty_for_room/3]). -include_lib("stdlib/include/ms_transform.hrl"). -include("xmpp.hrl"). @@ -198,6 +199,13 @@ select(_LServer, JidRequestor, erlang:garbage_collect(), Result. +is_empty_for_user(LUser, LServer) -> + not lists:member({LUser, LServer}, + mnesia:dirty_all_keys(archive_msg)). + +is_empty_for_room(_LServer, LName, LHost) -> + is_empty_for_user(LName, LHost). + %%%=================================================================== %%% Internal functions %%%=================================================================== diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index bcfa06708..6242de3c6 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -30,7 +30,8 @@ %% API -export([init/2, remove_user/2, remove_room/3, delete_old_messages/3, - extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/6, export/1, remove_from_archive/3]). + extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/6, export/1, remove_from_archive/3, + is_empty_for_user/2, is_empty_for_room/3]). -include_lib("stdlib/include/ms_transform.hrl"). -include("xmpp.hrl"). @@ -264,6 +265,23 @@ export(_Server) -> [] end}]. +is_empty_for_user(LUser, LServer) -> + case ejabberd_sql:sql_query( + LServer, + ?SQL("select @(count(*))d from archive" + " where username=%(LUser)s and %(LServer)H")) of + {selected, [{Res}]} -> + case Res of + 0 -> true; + _ -> false + end; + _ -> false + end. + +is_empty_for_room(LServer, LName, LHost) -> + LUser = jid:encode({LName, LHost, <<>>}), + is_empty_for_user(LUser, LServer). + %%%=================================================================== %%% Internal functions %%%=================================================================== diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 29474e511..ae3c6f9f7 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -65,7 +65,8 @@ iq_set_register_info/5, count_online_rooms_by_user/3, get_online_rooms_by_user/3, - can_use_nick/4]). + can_use_nick/4, + check_create_room/4]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, @@ -112,10 +113,14 @@ %% API %%==================================================================== start(Host, Opts) -> + ejabberd_hooks:add(check_create_room, Host, ?MODULE, + check_create_room, 50), gen_mod:start_child(?MODULE, Host, Opts). stop(Host) -> Rooms = shutdown_rooms(Host), + ejabberd_hooks:delete(check_create_room, Host, ?MODULE, + check_create_room, 50), gen_mod:stop_child(?MODULE, Host), {wait, Rooms}. @@ -442,7 +447,9 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper, true -> case check_user_can_create_room( ServerHost, AccessCreate, From, Room) and - check_create_roomid(ServerHost, Room) of + ejabberd_hooks:run_fold(check_create_room, + ServerHost, true, + [ServerHost, Room, Host]) of true -> {ok, Pid} = start_new_room( Host, ServerHost, Access, @@ -611,9 +618,10 @@ check_user_can_create_room(ServerHost, AccessCreate, _ -> false end. -check_create_roomid(ServerHost, RoomID) -> +check_create_room(Acc, ServerHost, RoomID, _Host) -> Max = gen_mod:get_module_opt(ServerHost, ?MODULE, max_room_id), Regexp = gen_mod:get_module_opt(ServerHost, ?MODULE, regexp_room_id), + Acc and (byte_size(RoomID) =< Max) and (re:run(RoomID, Regexp, [unicode, {capture, none}]) == match). diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 0dac45e8b..494a4ef3f 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -34,6 +34,7 @@ create_room_with_opts/4, create_room/3, destroy_room/2, create_rooms_file/1, destroy_rooms_file/1, rooms_unused_list/2, rooms_unused_destroy/2, + rooms_empty_list/1, rooms_empty_destroy/1, get_user_rooms/2, get_room_occupants/2, get_room_occupants_number/2, send_direct_invitation/5, change_room_option/4, get_room_options/2, @@ -194,6 +195,25 @@ get_commands_spec() -> args = [{host, binary}, {days, integer}], result = {rooms, {list, {room, string}}}}, + #ejabberd_commands{name = rooms_empty_list, tags = [muc], + desc = "List the rooms that have no messages in archive", + module = ?MODULE, function = rooms_empty_list, + args_desc = ["Server host"], + args_example = ["example.com"], + result_desc = "List of empty rooms", + result_example = ["room1@muc.example.com", "room2@muc.example.com"], + args = [{host, binary}], + result = {rooms, {list, {room, string}}}}, + #ejabberd_commands{name = rooms_empty_destroy, tags = [muc], + desc = "Destroy the rooms that have no messages in archive", + module = ?MODULE, function = rooms_empty_destroy, + args_desc = ["Server host"], + args_example = ["example.com"], + result_desc = "List of empty rooms that have been destroyed", + result_example = ["room1@muc.example.com", "room2@muc.example.com"], + args = [{host, binary}], + result = {rooms, {list, {room, string}}}}, + #ejabberd_commands{name = get_user_rooms, tags = [muc], desc = "Get the list of rooms where this user is occupant", module = ?MODULE, function = get_user_rooms, @@ -718,35 +738,41 @@ create_rooms_file(Filename) -> ok. -%%---------------------------- -%% List/Delete Unused Rooms -%%---------------------------- +%%--------------------------------- +%% List/Delete Unused/Empty Rooms +%%--------------------------------- %%--------------- %% Control rooms_unused_list(ServerHost, Days) -> - rooms_unused_report(list, ServerHost, Days). + rooms_report(unused, list, ServerHost, Days). rooms_unused_destroy(ServerHost, Days) -> - rooms_unused_report(destroy, ServerHost, Days). + rooms_report(unused, destroy, ServerHost, Days). -rooms_unused_report(Action, ServerHost, Days) -> - {NA, NP, RP} = muc_unused(Action, ServerHost, Days), - io:format("Unused rooms: ~p out of ~p~n", [NP, NA]), +rooms_empty_list(ServerHost) -> + rooms_report(empty, list, ServerHost, 0). +rooms_empty_destroy(ServerHost) -> + rooms_report(empty, destroy, ServerHost, 0). + + +rooms_report(Method, Action, ServerHost, Days) -> + {NA, NP, RP} = muc_unused(Method, Action, ServerHost, Days), + io:format("rooms ~s: ~p out of ~p~n", [Method, NP, NA]), [<<R/binary, "@", H/binary>> || {R, H, _P} <- RP]. -muc_unused(Action, ServerHost, Last_allowed) -> +muc_unused(Method, Action, ServerHost, Last_allowed) -> %% Get all required info about all existing rooms Rooms_all = get_rooms(ServerHost), %% Decide which ones pass the requirements - Rooms_pass = decide_rooms(Rooms_all, ServerHost, Last_allowed), + Rooms_pass = decide_rooms(Method, Rooms_all, ServerHost, Last_allowed), Num_rooms_all = length(Rooms_all), Num_rooms_pass = length(Rooms_pass), %% Perform the desired action for matching rooms - act_on_rooms(Action, Rooms_pass, ServerHost), + act_on_rooms(Method, Action, Rooms_pass, ServerHost), {Num_rooms_all, Num_rooms_pass, Rooms_pass}. @@ -771,11 +797,11 @@ get_room_state(Room_pid) -> %%--------------- %% Decide -decide_rooms(Rooms, ServerHost, Last_allowed) -> - Decide = fun(R) -> decide_room(R, ServerHost, Last_allowed) end, +decide_rooms(Method, Rooms, ServerHost, Last_allowed) -> + Decide = fun(R) -> decide_room(Method, R, ServerHost, Last_allowed) end, lists:filter(Decide, Rooms). -decide_room({_Room_name, _Host, Room_pid}, ServerHost, Last_allowed) -> +decide_room(unused, {_Room_name, _Host, Room_pid}, ServerHost, Last_allowed) -> C = get_room_config(Room_pid), Persistent = C#config.persistent, @@ -811,6 +837,13 @@ decide_room({_Room_name, _Host, Room_pid}, ServerHost, Last_allowed) -> true; _ -> false + end; +decide_room(empty, {Room_name, Host, _Room_pid}, ServerHost, _Last_allowed) -> + case gen_mod:is_loaded(ServerHost, mod_mam) of + true -> + mod_mam:is_empty_for_room(ServerHost, Room_name, Host); + _ -> + false end. seconds_to_days(S) -> @@ -819,7 +852,7 @@ seconds_to_days(S) -> %%--------------- %% Act -act_on_rooms(Action, Rooms, ServerHost) -> +act_on_rooms(Method, Action, Rooms, ServerHost) -> ServerHosts = [ {A, find_host(A)} || A <- ejabberd_config:get_myhosts() ], Delete = fun({_N, H, _Pid} = Room) -> SH = case ServerHost of @@ -827,7 +860,7 @@ act_on_rooms(Action, Rooms, ServerHost) -> O -> O end, - act_on_room(Action, Room, SH) + act_on_room(Method, Action, Room, SH) end, lists:foreach(Delete, Rooms). @@ -835,13 +868,15 @@ find_serverhost(Host, ServerHosts) -> {value, {ServerHost, Host}} = lists:keysearch(Host, 2, ServerHosts), ServerHost. -act_on_room(destroy, {N, H, Pid}, SH) -> +act_on_room(Method, destroy, {N, H, Pid}, SH) -> + Message = iolist_to_binary(io_lib:format( + <<"Room destroyed by rooms_~s_destroy.">>, [Method])), p1_fsm:send_all_state_event( - Pid, {destroy, <<"Room destroyed by rooms_unused_destroy.">>}), + Pid, {destroy, Message}), mod_muc:room_destroyed(H, N, Pid, SH), mod_muc:forget_room(SH, H, N); -act_on_room(list, _, _) -> +act_on_room(_Method, list, _, _) -> ok. |