]> granicus.if.org Git - ejabberd/commitdiff
New option tombstone_expiry locks recent room creation after destroy (#2546)
authorBadlop <badlop@process-one.net>
Mon, 30 Jul 2018 15:26:57 +0000 (17:26 +0200)
committerBadlop <badlop@process-one.net>
Mon, 30 Jul 2018 17:24:35 +0000 (19:24 +0200)
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
src/mod_muc_room.erl

index f871c0b77fbe7a036712355b64763c5b297da876..6b53e38d962a68d5d8ad5a03d4bb6977d41d40b9 100644 (file)
@@ -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,
index f1acf3cb091a9bc6095b3d5f5412f084b96fe399..33c58e416fda58cb43a50bda9816af5595d421d8 100644 (file)
@@ -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