diff options
Diffstat (limited to 'src/mod_mam_mnesia.erl')
-rw-r--r-- | src/mod_mam_mnesia.erl | 147 |
1 files changed, 104 insertions, 43 deletions
diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl index be14d0fff..1c8e742c8 100644 --- a/src/mod_mam_mnesia.erl +++ b/src/mod_mam_mnesia.erl @@ -1,21 +1,38 @@ %%%------------------------------------------------------------------- -%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net> -%%% @copyright (C) 2016, Evgeny Khramtsov -%%% @doc -%%% -%%% @end +%%% File : mod_mam_mnesia.erl +%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net> %%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net> -%%%------------------------------------------------------------------- +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2019 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + -module(mod_mam_mnesia). -behaviour(mod_mam). %% API -export([init/2, remove_user/2, remove_room/3, delete_old_messages/3, - extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/8]). + extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/6, remove_from_archive/3, + is_empty_for_user/2, is_empty_for_room/3]). -include_lib("stdlib/include/ms_transform.hrl"). --include("jlib.hrl"). +-include("xmpp.hrl"). -include("logger.hrl"). -include("mod_mam.hrl"). @@ -32,13 +49,20 @@ %%% API %%%=================================================================== init(_Host, _Opts) -> - mnesia:create_table(archive_msg, + try + {atomic, _} = ejabberd_mnesia:create( + ?MODULE, archive_msg, [{disc_only_copies, [node()]}, {type, bag}, {attributes, record_info(fields, archive_msg)}]), - mnesia:create_table(archive_prefs, + {atomic, _} = ejabberd_mnesia:create( + ?MODULE, archive_prefs, [{disc_only_copies, [node()]}, - {attributes, record_info(fields, archive_prefs)}]). + {attributes, record_info(fields, archive_prefs)}]), + ok + catch _:{badmatch, _} -> + {error, db_failure} + end. remove_user(LUser, LServer) -> US = {LUser, LServer}, @@ -51,6 +75,29 @@ remove_user(LUser, LServer) -> remove_room(_LServer, LName, LHost) -> remove_user(LName, LHost). +remove_from_archive(LUser, LServer, none) -> + US = {LUser, LServer}, + case mnesia:transaction(fun () -> mnesia:delete({archive_msg, US}) end) of + {atomic, _} -> ok; + {aborted, Reason} -> {error, Reason} + end; +remove_from_archive(LUser, LServer, WithJid) -> + US = {LUser, LServer}, + Peer = jid:remove_resource(jid:split(WithJid)), + F = fun () -> + Msgs = mnesia:select( + archive_msg, + ets:fun2ms( + fun(#archive_msg{us = US1, bare_peer = Peer1} = Msg) + when US1 == US, Peer1 == Peer -> Msg + end)), + lists:foreach(fun mnesia:delete_object/1, Msgs) + end, + case mnesia:transaction(F) of + {atomic, _} -> ok; + {aborted, Reason} -> {error, Reason} + end. + delete_old_messages(global, TimeStamp, Type) -> mnesia:change_table_copy_type(archive_msg, node(), disc_copies), Result = delete_old_user_messages(mnesia:dirty_first(archive_msg), TimeStamp, Type), @@ -75,34 +122,32 @@ delete_old_user_messages(User, TimeStamp, Type) -> ok end end, + NextRecord = mnesia:dirty_next(archive_msg, User), case mnesia:transaction(F) of {atomic, ok} -> - delete_old_user_messages(mnesia:dirty_next(archive_msg, User), - TimeStamp, Type); + delete_old_user_messages(NextRecord, TimeStamp, Type); {aborted, Err} -> - ?ERROR_MSG("Cannot delete old MAM messages: ~s", [Err]), + ?ERROR_MSG("Cannot delete old MAM messages: ~ts", [Err]), Err end. extended_fields() -> []. -store(Pkt, _, {LUser, LServer}, Type, Peer, Nick, _Dir) -> +store(Pkt, _, {LUser, LServer}, Type, Peer, Nick, _Dir, TS) -> case {mnesia:table_info(archive_msg, disc_only_copies), mnesia:table_info(archive_msg, memory)} of {[_|_], TableSize} when TableSize > ?TABLE_SIZE_LIMIT -> - ?ERROR_MSG("MAM archives too large, won't store message for ~s@~s", + ?ERROR_MSG("MAM archives too large, won't store message for ~ts@~ts", [LUser, LServer]), {error, overflow}; _ -> LPeer = {PUser, PServer, _} = jid:tolower(Peer), - TS = p1_time_compat:timestamp(), - ID = jlib:integer_to_binary(now_to_usec(TS)), F = fun() -> mnesia:write( #archive_msg{us = {LUser, LServer}, - id = ID, - timestamp = TS, + id = integer_to_binary(TS), + timestamp = misc:usec_to_now(TS), peer = LPeer, bare_peer = {PUser, PServer, <<>>}, type = Type, @@ -111,9 +156,9 @@ store(Pkt, _, {LUser, LServer}, Type, Peer, Nick, _Dir) -> end, case mnesia:transaction(F) of {atomic, ok} -> - {ok, ID}; + ok; {aborted, Err} -> - ?ERROR_MSG("Cannot add message to MAM archive of ~s@~s: ~s", + ?ERROR_MSG("Cannot add message to MAM archive of ~ts@~ts: ~ts", [LUser, LServer, Err]), Err end @@ -132,28 +177,45 @@ get_prefs(LUser, LServer) -> select(_LServer, JidRequestor, #jid{luser = LUser, lserver = LServer} = JidArchive, - Start, End, With, RSM, MsgType) -> - MS = make_matchspec(LUser, LServer, Start, End, With), + Query, RSM, MsgType) -> + Start = proplists:get_value(start, Query), + End = proplists:get_value('end', Query), + With = proplists:get_value(with, Query), + LWith = if With /= undefined -> jid:tolower(With); + true -> undefined + end, + MS = make_matchspec(LUser, LServer, Start, End, LWith), Msgs = mnesia:dirty_select(archive_msg, MS), SortedMsgs = lists:keysort(#archive_msg.timestamp, Msgs), {FilteredMsgs, IsComplete} = filter_by_rsm(SortedMsgs, RSM), Count = length(Msgs), - Result = {lists:map( + Result = {lists:flatmap( fun(Msg) -> - {Msg#archive_msg.id, - jlib:binary_to_integer(Msg#archive_msg.id), - mod_mam:msg_to_el(Msg, MsgType, JidRequestor, - JidArchive)} + case mod_mam:msg_to_el( + Msg, MsgType, JidRequestor, JidArchive) of + {ok, El} -> + [{Msg#archive_msg.id, + binary_to_integer(Msg#archive_msg.id), + El}]; + {error, _} -> + [] + end end, FilteredMsgs), IsComplete, Count}, erlang:garbage_collect(), Result. +is_empty_for_user(LUser, LServer) -> + mnesia:dirty_read(archive_msg, {LUser, LServer}) == []. + +is_empty_for_room(_LServer, LName, LHost) -> + is_empty_for_user(LName, LHost). + %%%=================================================================== %%% Internal functions %%%=================================================================== -now_to_usec({MSec, Sec, USec}) -> - (MSec*1000000 + Sec)*1000000 + USec. - +make_matchspec(LUser, LServer, Start, undefined, With) -> + %% List is always greater than a tuple + make_matchspec(LUser, LServer, Start, [], With); make_matchspec(LUser, LServer, Start, End, {_, _, <<>>} = With) -> ets:fun2ms( fun(#archive_msg{timestamp = TS, @@ -174,7 +236,7 @@ make_matchspec(LUser, LServer, Start, End, {_, _, _} = With) -> Peer == With -> Msg end); -make_matchspec(LUser, LServer, Start, End, none) -> +make_matchspec(LUser, LServer, Start, End, undefined) -> ets:fun2ms( fun(#archive_msg{timestamp = TS, us = US, @@ -184,28 +246,27 @@ make_matchspec(LUser, LServer, Start, End, none) -> Msg end). -filter_by_rsm(Msgs, none) -> +filter_by_rsm(Msgs, undefined) -> {Msgs, true}; -filter_by_rsm(_Msgs, #rsm_in{max = Max}) when Max < 0 -> +filter_by_rsm(_Msgs, #rsm_set{max = Max}) when Max < 0 -> {[], true}; -filter_by_rsm(Msgs, #rsm_in{max = Max, direction = Direction, id = ID}) -> - NewMsgs = case Direction of - aft when ID /= <<"">> -> +filter_by_rsm(Msgs, #rsm_set{max = Max, before = Before, 'after' = After}) -> + NewMsgs = if is_binary(After), After /= <<"">> -> lists:filter( fun(#archive_msg{id = I}) -> - ?BIN_GREATER_THAN(I, ID) + ?BIN_GREATER_THAN(I, After) end, Msgs); - before when ID /= <<"">> -> + is_binary(Before), Before /= <<"">> -> lists:foldl( fun(#archive_msg{id = I} = Msg, Acc) - when ?BIN_LESS_THAN(I, ID) -> + when ?BIN_LESS_THAN(I, Before) -> [Msg|Acc]; (_, Acc) -> Acc end, [], Msgs); - before when ID == <<"">> -> + is_binary(Before), Before == <<"">> -> lists:reverse(Msgs); - _ -> + true -> Msgs end, filter_by_max(NewMsgs, Max). |