25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-24 16:23:40 +01:00

Add option user_mucsub_from_muc_archive to mod_muc

This option disable storing separate mucsub message for each individual
subscriber but instead when user fetches archive virtual mucsub messages
are generated from muc archives.
This commit is contained in:
Paweł Chmielowski 2019-03-28 17:42:25 +01:00
parent 063869603a
commit 8e05fd1d24
4 changed files with 150 additions and 18 deletions

View File

@ -76,8 +76,12 @@
-callback remove_from_archive(binary(), binary(), jid() | none) -> ok | {error, any()}. -callback remove_from_archive(binary(), binary(), jid() | none) -> ok | {error, any()}.
-callback is_empty_for_user(binary(), binary()) -> boolean(). -callback is_empty_for_user(binary(), binary()) -> boolean().
-callback is_empty_for_room(binary(), binary(), binary()) -> boolean(). -callback is_empty_for_room(binary(), binary(), binary()) -> boolean().
-callback select_with_mucsub(binary(), jid(), jid(), mam_query:result(),
#rsm_set{} | undefined) ->
{[{binary(), non_neg_integer(), xmlel()}], boolean(), count()} |
{error, db_failure}.
-optional_callbacks([use_cache/1, cache_nodes/1]). -optional_callbacks([use_cache/1, cache_nodes/1, select_with_mucsub/5]).
%%%=================================================================== %%%===================================================================
%%% API %%% API
@ -886,16 +890,20 @@ may_enter_room(From, MUCState) ->
store_msg(Pkt, LUser, LServer, Peer, Dir) -> store_msg(Pkt, LUser, LServer, Peer, Dir) ->
case get_prefs(LUser, LServer) of case get_prefs(LUser, LServer) of
{ok, Prefs} -> {ok, Prefs} ->
case {should_archive_peer(LUser, LServer, Prefs, Peer), Pkt} of UseMucArchive = gen_mod:get_module_opt(LServer, ?MODULE, user_mucsub_from_muc_archive),
{true, #message{meta = #{sm_copy := true}}} -> StoredInMucMam = UseMucArchive andalso xmpp:get_meta(Pkt, in_muc_mam, false),
case {should_archive_peer(LUser, LServer, Prefs, Peer), Pkt, StoredInMucMam} of
{true, #message{meta = #{sm_copy := true}}, _} ->
ok; % Already stored. ok; % Already stored.
{true, _} -> {true, _, true} ->
ok; % Stored in muc archive.
{true, _, _} ->
case ejabberd_hooks:run_fold(store_mam_message, LServer, Pkt, case ejabberd_hooks:run_fold(store_mam_message, LServer, Pkt,
[LUser, LServer, Peer, <<"">>, chat, Dir]) of [LUser, LServer, Peer, <<"">>, chat, Dir]) of
#message{} -> ok; #message{} -> ok;
_ -> pass _ -> pass
end; end;
{false, _} -> {false, _, _} ->
pass pass
end; end;
{error, _} -> {error, _} ->
@ -1073,10 +1081,118 @@ select(LServer, JidRequestor, JidArchive, Query, RSM, MsgType) ->
true -> true ->
{[], true, 0}; {[], true, 0};
false -> false ->
Mod = gen_mod:db_mod(LServer, ?MODULE), case {MsgType, gen_mod:get_module_opt(LServer, ?MODULE, user_mucsub_from_muc_archive)} of
Mod:select(LServer, JidRequestor, JidArchive, Query, RSM, MsgType) {chat, true} ->
select_with_mucsub(LServer, JidRequestor, JidArchive, Query, RSM);
_ ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:select(LServer, JidRequestor, JidArchive, Query, RSM, MsgType)
end
end. end.
select_with_mucsub(LServer, JidRequestor, JidArchive, Query, RSM) ->
MucHosts = mod_muc_admin:find_hosts(LServer),
Mod = gen_mod:db_mod(LServer, ?MODULE),
case proplists:get_value(with, Query) of
#jid{lserver = WithLServer} = MucJid ->
case lists:member(WithLServer, MucHosts) of
true ->
select(LServer, JidRequestor, MucJid, Query, RSM,
{groupchat, member, #state{config = #config{mam = true}}});
_ ->
Mod:select(LServer, JidRequestor, JidArchive, Query, RSM, chat)
end;
_ ->
case erlang:function_exported(Mod, select_with_mucsub, 5) of
true ->
Mod:select_with_mucsub(LServer, JidRequestor, JidArchive, Query, RSM);
false ->
case Mod:select(LServer, JidRequestor, JidArchive, Query, RSM, chat) of
{error, _} = Err ->
Err;
{Entries, All, Count} ->
{Dir, Max} = case RSM of
#rsm_set{max = M, before = V} when is_binary(V) ->
{desc, M};
#rsm_set{max = M} ->
{asc, M};
_ ->
{asc, undefined}
end,
SubRooms = case mod_muc_admin:find_hosts(LServer) of
[First|_] ->
mod_muc:get_subscribed_rooms(First, JidRequestor);
_ ->
[]
end,
SubRoomJids = [Jid || #muc_subscription{jid = Jid} <- SubRooms],
{E2, A2, C2} = lists:foldl(
fun(MucJid, {E0, A0, C0}) ->
case select(LServer, JidRequestor, MucJid, Query, RSM,
{groupchat, member, #state{config = #config{mam = true}}}) of
{error, _} ->
{E0, A0, C0};
{E, A, C} ->
{lists:keymerge(2, E0, wrap_as_mucsub(E, JidRequestor)),
A0 andalso A, C0 + C}
end
end, {Entries, All, Count}, SubRoomJids),
case {Dir, Max} of
{_, undefined} ->
{E2, A2, C2};
{desc, _} ->
Start = case length(E2) of
Len when Len < Max -> 1;
Len -> Len - Max + 1
end,
Sub = lists:sublist(E2, Start, Max),
{Sub, if Sub == E2 -> A2; true -> false end, C2};
_ ->
Sub = lists:sublist(E2, 1, Max),
{Sub, if Sub == E2 -> A2; true -> false end, C2}
end
end
end
end.
wrap_as_mucsub(Messages, #jid{lserver = LServer} = Requester) ->
ReqBare = jid:remove_resource(Requester),
ReqServer = jid:make(<<>>, LServer, <<>>),
[{T1, T2, wrap_as_mucsub(M, ReqBare, ReqServer)} || {T1, T2, M} <- Messages].
wrap_as_mucsub(Message, Requester, ReqServer) ->
case Message of
#forwarded{delay = #delay{stamp = Stamp, desc = Desc},
sub_els = [#message{from = From, sub_els = SubEls} = Msg]} ->
{L1, SubEls2} = case lists:keytake(mam_archived, 1, xmpp:decode(SubEls)) of
{value, Arch, Rest} ->
{[Arch#mam_archived{by = Requester}], Rest};
_ ->
{[], SubEls}
end,
{Sid, L2, SubEls3} = case lists:keytake(stanza_id, 1, SubEls2) of
{value, #stanza_id{id = Sid0} = SID, Rest2} ->
{Sid0, [SID#stanza_id{by = Requester} | L1], Rest2};
_ ->
{p1_rand:get_string(), L1, SubEls2}
end,
Msg2 = Msg#message{to = Requester, sub_els = SubEls3},
#forwarded{delay = #delay{stamp = Stamp, desc = Desc, from = ReqServer},
sub_els = [
#message{from = jid:remove_resource(From), to = Requester,
id = Sid,
sub_els = [#ps_event{
items = #ps_items{
node = ?NS_MUCSUB_NODES_MESSAGES,
items = [#ps_item{
id = Sid,
sub_els = [Msg2]
}]}} | L2]}]};
_ ->
Message
end.
msg_to_el(#archive_msg{timestamp = TS, packet = El, nick = Nick, msg_to_el(#archive_msg{timestamp = TS, packet = El, nick = Nick,
peer = Peer, id = ID}, peer = Peer, id = ID},
MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) -> MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) ->
@ -1265,6 +1381,8 @@ mod_opt_type(request_activates_archiving) ->
fun (B) when is_boolean(B) -> B end; fun (B) when is_boolean(B) -> B end;
mod_opt_type(clear_archive_on_room_destroy) -> mod_opt_type(clear_archive_on_room_destroy) ->
fun (B) when is_boolean(B) -> B end; fun (B) when is_boolean(B) -> B end;
mod_opt_type(user_mucsub_from_muc_archive) ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(access_preferences) -> mod_opt_type(access_preferences) ->
fun acl:access_rules_validator/1. fun acl:access_rules_validator/1.
@ -1275,6 +1393,7 @@ mod_options(Host) ->
{compress_xml, false}, {compress_xml, false},
{clear_archive_on_room_destroy, true}, {clear_archive_on_room_destroy, true},
{access_preferences, all}, {access_preferences, all},
{user_mucsub_from_muc_archive, false},
{db_type, ejabberd_config:default_db(Host, ?MODULE)}, {db_type, ejabberd_config:default_db(Host, ?MODULE)},
{use_cache, ejabberd_config:use_cache(Host)}, {use_cache, ejabberd_config:use_cache(Host)},
{cache_size, ejabberd_config:cache_size(Host)}, {cache_size, ejabberd_config:cache_size(Host)},

View File

@ -65,7 +65,8 @@
iq_set_register_info/5, iq_set_register_info/5,
count_online_rooms_by_user/3, count_online_rooms_by_user/3,
get_online_rooms_by_user/3, get_online_rooms_by_user/3,
can_use_nick/4]). can_use_nick/4,
get_subscribed_rooms/2]).
-export([init/1, handle_call/3, handle_cast/2, -export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3, handle_info/2, terminate/2, code_change/3,
@ -727,6 +728,11 @@ get_room_disco_item({Name, Host, Pid}, Query) ->
{error, notfound} {error, notfound}
end. end.
-spec get_subscribed_rooms(binary(), jid()) -> [#muc_subscription{}].
get_subscribed_rooms(Host, User) ->
ServerHost = ejabberd_router:host_of_route(Host),
get_subscribed_rooms(ServerHost, Host, User).
get_subscribed_rooms(ServerHost, Host, From) -> get_subscribed_rooms(ServerHost, Host, From) ->
LServer = jid:nameprep(ServerHost), LServer = jid:nameprep(ServerHost),
Mod = gen_mod:db_mod(LServer, ?MODULE), Mod = gen_mod:db_mod(LServer, ?MODULE),

View File

@ -41,7 +41,7 @@
set_room_affiliation/4, get_room_affiliations/2, get_room_affiliation/3, set_room_affiliation/4, get_room_affiliations/2, get_room_affiliation/3,
web_menu_main/2, web_page_main/2, web_menu_host/3, web_menu_main/2, web_page_main/2, web_menu_host/3,
subscribe_room/4, unsubscribe_room/2, get_subscribers/2, subscribe_room/4, unsubscribe_room/2, get_subscribers/2,
web_page_host/3, mod_options/1, get_commands_spec/0]). web_page_host/3, mod_options/1, get_commands_spec/0, find_hosts/1]).
-include("logger.hrl"). -include("logger.hrl").
-include("xmpp.hrl"). -include("xmpp.hrl").

View File

@ -4397,9 +4397,17 @@ send_wrapped(From, To, Packet, Node, State) ->
#subscriber{nodes = Nodes, jid = JID} -> #subscriber{nodes = Nodes, jid = JID} ->
case lists:member(Node, Nodes) of case lists:member(Node, Nodes) of
true -> true ->
NewPacket = wrap(From, JID, Packet, Node), MamEnabled = (State#state.config)#config.mam,
Id = case xmpp:get_subtag(Packet, #stanza_id{}) of
#stanza_id{id = Id2} ->
Id2;
_ ->
p1_rand:get_string()
end,
NewPacket = wrap(From, JID, Packet, Node, Id),
NewPacket2 = xmpp:put_meta(NewPacket, in_muc_mam, MamEnabled),
ejabberd_router:route( ejabberd_router:route(
xmpp:set_from_to(NewPacket, State#state.jid, JID)); xmpp:set_from_to(NewPacket2, State#state.jid, JID));
false -> false ->
ok ok
end end
@ -4432,10 +4440,9 @@ send_wrapped(From, To, Packet, Node, State) ->
ejabberd_router:route(xmpp:set_from_to(Packet, From, To)) ejabberd_router:route(xmpp:set_from_to(Packet, From, To))
end. end.
-spec wrap(jid(), jid(), stanza(), binary()) -> message(). -spec wrap(jid(), jid(), stanza(), binary(), binary()) -> message().
wrap(From, To, Packet, Node) -> wrap(From, To, Packet, Node, Id) ->
El = xmpp:set_from_to(Packet, From, To), El = xmpp:set_from_to(Packet, From, To),
Id = p1_rand:get_string(),
#message{ #message{
id = Id, id = Id,
sub_els = [#ps_event{ sub_els = [#ps_event{