mirror of
https://github.com/processone/ejabberd
synced 2025-10-03 09:49:18 +02: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:
parent
063869603a
commit
8e05fd1d24
4 changed files with 150 additions and 18 deletions
133
src/mod_mam.erl
133
src/mod_mam.erl
|
@ -76,8 +76,12 @@
|
|||
-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().
|
||||
-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
|
||||
|
@ -886,16 +890,20 @@ may_enter_room(From, MUCState) ->
|
|||
store_msg(Pkt, LUser, LServer, Peer, Dir) ->
|
||||
case get_prefs(LUser, LServer) of
|
||||
{ok, Prefs} ->
|
||||
case {should_archive_peer(LUser, LServer, Prefs, Peer), Pkt} of
|
||||
{true, #message{meta = #{sm_copy := true}}} ->
|
||||
UseMucArchive = gen_mod:get_module_opt(LServer, ?MODULE, user_mucsub_from_muc_archive),
|
||||
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.
|
||||
{true, _} ->
|
||||
{true, _, true} ->
|
||||
ok; % Stored in muc archive.
|
||||
{true, _, _} ->
|
||||
case ejabberd_hooks:run_fold(store_mam_message, LServer, Pkt,
|
||||
[LUser, LServer, Peer, <<"">>, chat, Dir]) of
|
||||
#message{} -> ok;
|
||||
_ -> pass
|
||||
end;
|
||||
{false, _} ->
|
||||
{false, _, _} ->
|
||||
pass
|
||||
end;
|
||||
{error, _} ->
|
||||
|
@ -1073,10 +1081,118 @@ select(LServer, JidRequestor, JidArchive, Query, RSM, MsgType) ->
|
|||
true ->
|
||||
{[], true, 0};
|
||||
false ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:select(LServer, JidRequestor, JidArchive, Query, RSM, MsgType)
|
||||
case {MsgType, gen_mod:get_module_opt(LServer, ?MODULE, user_mucsub_from_muc_archive)} of
|
||||
{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.
|
||||
|
||||
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,
|
||||
peer = Peer, id = ID},
|
||||
MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) ->
|
||||
|
@ -1265,6 +1381,8 @@ 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_opt_type(user_mucsub_from_muc_archive) ->
|
||||
fun (B) when is_boolean(B) -> B end;
|
||||
mod_opt_type(access_preferences) ->
|
||||
fun acl:access_rules_validator/1.
|
||||
|
||||
|
@ -1275,6 +1393,7 @@ mod_options(Host) ->
|
|||
{compress_xml, false},
|
||||
{clear_archive_on_room_destroy, true},
|
||||
{access_preferences, all},
|
||||
{user_mucsub_from_muc_archive, false},
|
||||
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
|
||||
{use_cache, ejabberd_config:use_cache(Host)},
|
||||
{cache_size, ejabberd_config:cache_size(Host)},
|
||||
|
|
|
@ -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,
|
||||
get_subscribed_rooms/2]).
|
||||
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3,
|
||||
|
@ -727,6 +728,11 @@ get_room_disco_item({Name, Host, Pid}, Query) ->
|
|||
{error, notfound}
|
||||
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) ->
|
||||
LServer = jid:nameprep(ServerHost),
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2, stop/1, reload/3, depends/2,
|
||||
-export([start/2, stop/1, reload/3, depends/2,
|
||||
muc_online_rooms/1, muc_online_rooms_by_regex/2,
|
||||
muc_register_nick/3, muc_unregister_nick/2,
|
||||
create_room_with_opts/4, create_room/3, destroy_room/2,
|
||||
|
@ -41,7 +41,7 @@
|
|||
set_room_affiliation/4, get_room_affiliations/2, get_room_affiliation/3,
|
||||
web_menu_main/2, web_page_main/2, web_menu_host/3,
|
||||
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("xmpp.hrl").
|
||||
|
@ -100,18 +100,18 @@ get_commands_spec() ->
|
|||
desc = "List existing rooms ('global' to get all vhosts) by regex",
|
||||
policy = admin,
|
||||
module = ?MODULE, function = muc_online_rooms_by_regex,
|
||||
args_desc = ["Server domain where the MUC service is, or 'global' for all",
|
||||
args_desc = ["Server domain where the MUC service is, or 'global' for all",
|
||||
"Regex pattern for room name"],
|
||||
args_example = ["example.com", "^prefix"],
|
||||
result_desc = "List of rooms with summary",
|
||||
result_example = [{"room1@muc.example.com", "true", 10},
|
||||
result_example = [{"room1@muc.example.com", "true", 10},
|
||||
{"room2@muc.example.com", "false", 10}],
|
||||
args = [{host, binary}, {regex, binary}],
|
||||
result = {rooms, {list, {room, {tuple,
|
||||
[{jid, string},
|
||||
{public, string},
|
||||
{participants, integer}
|
||||
]}}}}},
|
||||
]}}}}},
|
||||
#ejabberd_commands{name = muc_register_nick, tags = [muc],
|
||||
desc = "Register a nick to a User JID in the MUC service of a server",
|
||||
module = ?MODULE, function = muc_register_nick,
|
||||
|
|
|
@ -4397,9 +4397,17 @@ send_wrapped(From, To, Packet, Node, State) ->
|
|||
#subscriber{nodes = Nodes, jid = JID} ->
|
||||
case lists:member(Node, Nodes) of
|
||||
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(
|
||||
xmpp:set_from_to(NewPacket, State#state.jid, JID));
|
||||
xmpp:set_from_to(NewPacket2, State#state.jid, JID));
|
||||
false ->
|
||||
ok
|
||||
end
|
||||
|
@ -4432,10 +4440,9 @@ send_wrapped(From, To, Packet, Node, State) ->
|
|||
ejabberd_router:route(xmpp:set_from_to(Packet, From, To))
|
||||
end.
|
||||
|
||||
-spec wrap(jid(), jid(), stanza(), binary()) -> message().
|
||||
wrap(From, To, Packet, Node) ->
|
||||
-spec wrap(jid(), jid(), stanza(), binary(), binary()) -> message().
|
||||
wrap(From, To, Packet, Node, Id) ->
|
||||
El = xmpp:set_from_to(Packet, From, To),
|
||||
Id = p1_rand:get_string(),
|
||||
#message{
|
||||
id = Id,
|
||||
sub_els = [#ps_event{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue