aboutsummaryrefslogtreecommitdiff
path: root/src/mod_mam.erl
diff options
context:
space:
mode:
authorPaweł Chmielowski <pchmielowski@process-one.net>2019-03-28 17:42:25 +0100
committerPaweł Chmielowski <pchmielowski@process-one.net>2019-03-28 17:42:25 +0100
commit8e05fd1d244c59f2f34b524aad692757b4bbf746 (patch)
tree66b9adf60ec5bcbbe87965b49e5e9d21df838e99 /src/mod_mam.erl
parentInclude id in mucsub notification message (diff)
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.
Diffstat (limited to 'src/mod_mam.erl')
-rw-r--r--src/mod_mam.erl133
1 files changed, 126 insertions, 7 deletions
diff --git a/src/mod_mam.erl b/src/mod_mam.erl
index e066f6d0e..8d672a7b1 100644
--- a/src/mod_mam.erl
+++ b/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)},