mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-22 17:28:25 +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:
parent
063869603a
commit
8e05fd1d24
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…
Reference in New Issue
Block a user