From efb4fd0d1020faa105bf932c06cf4c67fb389952 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 30 Jul 2018 17:26:57 +0200 Subject: [PATCH] New option tombstone_expiry locks recent room creation after destroy (#2546) Setting the new mod_muc option tombstone_expiry to a positive integer will make that any room destroyed gets replaced with a room tombstone. That tombstone cannot be joined, so it blocks accessing the old room JID until the expiry seconds have passed. The default value is 0 seconds, so tombstones are not created. --- src/mod_muc.erl | 31 ++++++++++++++++++++++++++++- src/mod_muc_room.erl | 46 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src/mod_muc.erl b/src/mod_muc.erl index f871c0b77..6b53e38d9 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -435,7 +435,7 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper, {_AccessRoute, AccessCreate, _AccessAdmin, _AccessPersistent} = Access, {Room, _, Nick} = jid:tolower(To), RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), - case RMod:find_online_room(ServerHost, Room, Host) of + case find_online_available_room(RMod, ServerHost, Room, Host) of error -> case is_create_request(Packet) of true -> @@ -463,12 +463,38 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper, Err = xmpp:err_item_not_found(ErrText, Lang), ejabberd_router:route_error(Packet, Err) end; + locked_room -> + Lang = xmpp:get_lang(Packet), + ErrText = <<"Conference room was destroyed">>, + Err = xmpp:err_item_not_found(ErrText, Lang), + ejabberd_router:route_error(Packet, Err); {ok, Pid} -> ?DEBUG("MUC: send to process ~p~n", [Pid]), mod_muc_room:route(Pid, Packet), ok end. +find_online_available_room(RMod, ServerHost, Room, Host) -> + case RMod:find_online_room(ServerHost, Room, Host) of + error -> + error; + {ok, Pid} -> + check_tombstone(Pid) + end. + +-define(TOMBSTONE_REASON, <<"Expiring tombstone">>). + +check_tombstone(Pid) -> + case p1_fsm:sync_send_all_state_event(Pid, check_tombstone) of + not_tombstone -> + {ok, Pid}; + locked -> + locked_room; + expired -> + p1_fsm:send_all_state_event(Pid, {destroy, ?TOMBSTONE_REASON}), + error + end. + -spec process_vcard(iq()) -> iq(). process_vcard(#iq{type = get, lang = Lang, sub_els = [#vcard_temp{}]} = IQ) -> xmpp:make_iq_result( @@ -922,6 +948,8 @@ mod_opt_type(min_presence_interval) -> fun (I) when is_number(I), I >= 0 -> I end; mod_opt_type(room_shaper) -> fun (A) when is_atom(A) -> A end; +mod_opt_type(tombstone_expiry) -> + fun (I) when is_integer(I) -> I end; mod_opt_type(user_message_shaper) -> fun (A) when is_atom(A) -> A end; mod_opt_type(user_presence_shaper) -> @@ -1011,6 +1039,7 @@ mod_options(Host) -> {queue_type, ejabberd_config:default_queue_type(Host)}, {regexp_room_id, <<"">>}, {room_shaper, none}, + {tombstone_expiry, 0}, {user_message_shaper, none}, {user_presence_shaper, none}, {default_room_options, diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index f1acf3cb0..33c58e416 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -533,6 +533,9 @@ handle_sync_event({change_config, Config}, _From, handle_sync_event({change_state, NewStateData}, _From, StateName, _StateData) -> {reply, {ok, NewStateData}, StateName, NewStateData}; +handle_sync_event(check_tombstone, _From, + StateName, StateData) -> + {reply, check_tombstone(StateData), StateName, StateData}; handle_sync_event({process_item_change, Item, UJID}, _From, StateName, StateData) -> case process_item_change(Item, StateData, UJID) of {error, _} = Err -> @@ -3784,11 +3787,52 @@ destroy_room(DEl, StateData) -> case (StateData#state.config)#config.persistent of true -> mod_muc:forget_room(StateData#state.server_host, - StateData#state.host, StateData#state.room); + StateData#state.host, StateData#state.room), + maybe_create_tombstone(DEl, StateData); false -> ok end, {result, undefined, stop}. +-define(TOMBSTONE_NAME, <<"Room tombstone">>). +-define(TOMBSTONE_REASON, <<"Expiring tombstone">>). + +maybe_create_tombstone(DEl, StateData) -> + case DEl#muc_destroy.reason of + ?TOMBSTONE_REASON -> + ok; + _ -> + TombstoneExpiry = gen_mod:get_module_opt(StateData#state.server_host, + mod_muc, tombstone_expiry), + create_tombstone(TombstoneExpiry, StateData) + end. + +create_tombstone(TombstoneExpiry, _) when TombstoneExpiry =< 0 -> + ok; +create_tombstone(TombstoneExpiry, #state{server_host = Server, host = Host, room = Room}) -> + ?INFO_MSG("Creating tombstone for room ~s@~s", [Room, Host]), + Password = integer_to_binary(misc:now_to_usec(now()) + TombstoneExpiry*1000000), + Opts1 = gen_mod:get_module_opt(Server, mod_muc, default_room_options), + {_, Opts2} = proplists:split(Opts1, [title, password, persistent, public]), + Opts3 = [{title, ?TOMBSTONE_NAME}, {password, Password}, {persistent, true}, {public, false} | Opts2], + mod_muc:create_room( + Host, + Room, + jid:make(<<"tombstone">>, <<"">>), + <<"">>, + Opts3). + +check_tombstone(#state{config = Config}) -> + case Config#config.title of + ?TOMBSTONE_NAME -> + case binary_to_integer(Config#config.password) < misc:now_to_usec(now()) of + true -> + expired; + false -> + locked + end; + _ -> not_tombstone + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Disco -- 2.40.0