aboutsummaryrefslogtreecommitdiff
path: root/src/mod_mam_mnesia.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mod_mam_mnesia.erl')
-rw-r--r--src/mod_mam_mnesia.erl147
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).