diff options
author | Evgeniy Khramtsov <ekhramtsov@process-one.net> | 2016-04-15 15:11:31 +0300 |
---|---|---|
committer | Evgeniy Khramtsov <ekhramtsov@process-one.net> | 2016-04-15 15:11:31 +0300 |
commit | 52571e8624b0a48797ca24ec05e47a5c9477a12a (patch) | |
tree | 248ab9b1e50c808465777e27bd9f225f9a3e7e97 /src/mod_mam_mnesia.erl | |
parent | Clean mod_offline.erl from DB specific code (diff) |
Clean mod_mam.erl from DB specific code
Diffstat (limited to 'src/mod_mam_mnesia.erl')
-rw-r--r-- | src/mod_mam_mnesia.erl | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl new file mode 100644 index 00000000..007ef5eb --- /dev/null +++ b/src/mod_mam_mnesia.erl @@ -0,0 +1,178 @@ +%%%------------------------------------------------------------------- +%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net> +%%% @copyright (C) 2016, Evgeny Khramtsov +%%% @doc +%%% +%%% @end +%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net> +%%%------------------------------------------------------------------- +-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]). + +-include_lib("stdlib/include/ms_transform.hrl"). +-include("jlib.hrl"). +-include("mod_mam.hrl"). + +-define(BIN_GREATER_THAN(A, B), + ((A > B andalso byte_size(A) == byte_size(B)) + orelse byte_size(A) > byte_size(B))). +-define(BIN_LESS_THAN(A, B), + ((A < B andalso byte_size(A) == byte_size(B)) + orelse byte_size(A) < byte_size(B))). + +%%%=================================================================== +%%% API +%%%=================================================================== +init(_Host, _Opts) -> + mnesia:create_table(archive_msg, + [{disc_only_copies, [node()]}, + {type, bag}, + {attributes, record_info(fields, archive_msg)}]), + mnesia:create_table(archive_prefs, + [{disc_only_copies, [node()]}, + {attributes, record_info(fields, archive_prefs)}]). + +remove_user(LUser, LServer) -> + US = {LUser, LServer}, + F = fun () -> + mnesia:delete({archive_msg, US}), + mnesia:delete({archive_prefs, US}) + end, + mnesia:transaction(F). + +remove_room(_LServer, LName, LHost) -> + remove_user(LName, LHost). + +delete_old_messages(global, TimeStamp, Type) -> + MS = ets:fun2ms(fun(#archive_msg{timestamp = MsgTS, + type = MsgType} = Msg) + when MsgTS < TimeStamp, + MsgType == Type orelse Type == all -> + Msg + end), + OldMsgs = mnesia:dirty_select(archive_msg, MS), + lists:foreach(fun(Rec) -> + ok = mnesia:dirty_delete_object(Rec) + end, OldMsgs). + +extended_fields() -> + []. + +store(Pkt, _, {LUser, LServer}, Type, Peer, Nick, _Dir) -> + LPeer = {PUser, PServer, _} = jid:tolower(Peer), + TS = p1_time_compat:timestamp(), + ID = jlib:integer_to_binary(now_to_usec(TS)), + case mnesia:dirty_write( + #archive_msg{us = {LUser, LServer}, + id = ID, + timestamp = TS, + peer = LPeer, + bare_peer = {PUser, PServer, <<>>}, + type = Type, + nick = Nick, + packet = Pkt}) of + ok -> + {ok, ID}; + Err -> + Err + end. + +write_prefs(_LUser, _LServer, Prefs, _ServerHost) -> + mnesia:dirty_write(Prefs). + +get_prefs(LUser, LServer) -> + case mnesia:dirty_read(archive_prefs, {LUser, LServer}) of + [Prefs] -> + {ok, Prefs}; + _ -> + error + end. + +select(_LServer, JidRequestor, + #jid{luser = LUser, lserver = LServer} = JidArchive, + Start, End, With, RSM, MsgType) -> + MS = make_matchspec(LUser, LServer, Start, End, With), + Msgs = mnesia:dirty_select(archive_msg, MS), + SortedMsgs = lists:keysort(#archive_msg.timestamp, Msgs), + {FilteredMsgs, IsComplete} = filter_by_rsm(SortedMsgs, RSM), + Count = length(Msgs), + {lists:map( + fun(Msg) -> + {Msg#archive_msg.id, + jlib:binary_to_integer(Msg#archive_msg.id), + mod_mam:msg_to_el(Msg, MsgType, JidRequestor, JidArchive)} + end, FilteredMsgs), IsComplete, Count}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +now_to_usec({MSec, Sec, USec}) -> + (MSec*1000000 + Sec)*1000000 + USec. + +make_matchspec(LUser, LServer, Start, End, {_, _, <<>>} = With) -> + ets:fun2ms( + fun(#archive_msg{timestamp = TS, + us = US, + bare_peer = BPeer} = Msg) + when Start =< TS, End >= TS, + US == {LUser, LServer}, + BPeer == With -> + Msg + end); +make_matchspec(LUser, LServer, Start, End, {_, _, _} = With) -> + ets:fun2ms( + fun(#archive_msg{timestamp = TS, + us = US, + peer = Peer} = Msg) + when Start =< TS, End >= TS, + US == {LUser, LServer}, + Peer == With -> + Msg + end); +make_matchspec(LUser, LServer, Start, End, none) -> + ets:fun2ms( + fun(#archive_msg{timestamp = TS, + us = US, + peer = Peer} = Msg) + when Start =< TS, End >= TS, + US == {LUser, LServer} -> + Msg + end). + +filter_by_rsm(Msgs, none) -> + {Msgs, true}; +filter_by_rsm(_Msgs, #rsm_in{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 /= <<"">> -> + lists:filter( + fun(#archive_msg{id = I}) -> + ?BIN_GREATER_THAN(I, ID) + end, Msgs); + before when ID /= <<"">> -> + lists:foldl( + fun(#archive_msg{id = I} = Msg, Acc) + when ?BIN_LESS_THAN(I, ID) -> + [Msg|Acc]; + (_, Acc) -> + Acc + end, [], Msgs); + before when ID == <<"">> -> + lists:reverse(Msgs); + _ -> + Msgs + end, + filter_by_max(NewMsgs, Max). + +filter_by_max(Msgs, undefined) -> + {Msgs, true}; +filter_by_max(Msgs, Len) when is_integer(Len), Len >= 0 -> + {lists:sublist(Msgs, Len), length(Msgs) =< Len}; +filter_by_max(_Msgs, _Junk) -> + {[], true}. |