diff options
Diffstat (limited to 'src/mod_mam.erl')
-rw-r--r-- | src/mod_mam.erl | 221 |
1 files changed, 126 insertions, 95 deletions
diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 9689a93a2..294d4f401 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -5,7 +5,7 @@ %%% Created : 4 Jul 2013 by Evgeniy Khramtsov <ekhramtsov@process-one.net> %%% %%% -%%% ejabberd, Copyright (C) 2013-2018 ProcessOne +%%% ejabberd, Copyright (C) 2013-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 @@ -52,6 +52,7 @@ -define(MAX_PAGE_SIZE, 250). -type c2s_state() :: ejabberd_c2s:state(). +-type count() :: non_neg_integer() | undefined. -callback init(binary(), gen_mod:opts()) -> any(). -callback remove_user(binary(), binary()) -> any(). @@ -63,10 +64,11 @@ -callback store(xmlel(), binary(), {binary(), binary()}, chat | groupchat, jid(), binary(), recv | send, integer()) -> ok | any(). -callback write_prefs(binary(), binary(), #archive_prefs{}, binary()) -> ok | any(). --callback get_prefs(binary(), binary()) -> {ok, #archive_prefs{}} | error. +-callback get_prefs(binary(), binary()) -> {ok, #archive_prefs{}} | error | {error, db_failure}. -callback select(binary(), jid(), jid(), mam_query:result(), #rsm_set{} | undefined, chat | groupchat) -> - {[{binary(), non_neg_integer(), xmlel()}], boolean(), non_neg_integer()}. + {[{binary(), non_neg_integer(), xmlel()}], boolean(), count()} | + {error, db_failure}. -callback use_cache(binary()) -> boolean(). -callback cache_nodes(binary()) -> [node()]. -callback remove_from_archive(binary(), binary(), jid() | none) -> ok | {error, any()}. @@ -89,42 +91,45 @@ start(Host, Opts) -> ok end, Mod = gen_mod:db_mod(Host, Opts, ?MODULE), - Mod:init(Host, Opts), - init_cache(Mod, Host, Opts), - register_iq_handlers(Host), - ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE, - sm_receive_packet, 50), - ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, - user_receive_packet, 88), - ejabberd_hooks:add(user_send_packet, Host, ?MODULE, - user_send_packet, 88), - ejabberd_hooks:add(user_send_packet, Host, ?MODULE, - user_send_packet_strip_tag, 500), - ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, - offline_message, 50), - ejabberd_hooks:add(muc_filter_message, Host, ?MODULE, - muc_filter_message, 50), - ejabberd_hooks:add(muc_process_iq, Host, ?MODULE, - muc_process_iq, 50), - ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, - disco_sm_features, 50), - ejabberd_hooks:add(remove_user, Host, ?MODULE, - remove_user, 50), - ejabberd_hooks:add(remove_room, Host, ?MODULE, - remove_room, 50), - ejabberd_hooks:add(get_room_config, Host, ?MODULE, - get_room_config, 50), - ejabberd_hooks:add(set_room_option, Host, ?MODULE, - set_room_option, 50), - case gen_mod:get_opt(assume_mam_usage, Opts) of - true -> - ejabberd_hooks:add(message_is_archived, Host, ?MODULE, - message_is_archived, 50); - false -> - ok - end, - ejabberd_commands:register_commands(get_commands_spec()), - ok. + case Mod:init(Host, Opts) of + ok -> + init_cache(Mod, Host, Opts), + register_iq_handlers(Host), + ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE, + sm_receive_packet, 50), + ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, + user_receive_packet, 88), + ejabberd_hooks:add(user_send_packet, Host, ?MODULE, + user_send_packet, 88), + ejabberd_hooks:add(user_send_packet, Host, ?MODULE, + user_send_packet_strip_tag, 500), + ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, + offline_message, 50), + ejabberd_hooks:add(muc_filter_message, Host, ?MODULE, + muc_filter_message, 50), + ejabberd_hooks:add(muc_process_iq, Host, ?MODULE, + muc_process_iq, 50), + ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, + disco_sm_features, 50), + ejabberd_hooks:add(remove_user, Host, ?MODULE, + remove_user, 50), + ejabberd_hooks:add(remove_room, Host, ?MODULE, + remove_room, 50), + ejabberd_hooks:add(get_room_config, Host, ?MODULE, + get_room_config, 50), + ejabberd_hooks:add(set_room_option, Host, ?MODULE, + set_room_option, 50), + case gen_mod:get_opt(assume_mam_usage, Opts) of + true -> + ejabberd_hooks:add(message_is_archived, Host, ?MODULE, + message_is_archived, 50); + false -> + ok + end, + ejabberd_commands:register_commands(get_commands_spec()); + Err -> + Err + end. use_cache(Mod, Host) -> case erlang:function_exported(Mod, use_cache, 2) of @@ -594,37 +599,48 @@ process_iq(#iq{from = #jid{luser = LUser, lserver = LServer}, xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)) end; process_iq(#iq{from = #jid{luser = LUser, lserver = LServer}, - to = #jid{lserver = LServer}, + to = #jid{lserver = LServer}, lang = Lang, type = get, sub_els = [#mam_prefs{xmlns = NS}]} = IQ) -> - Prefs = get_prefs(LUser, LServer), - PrefsEl = prefs_el(Prefs#archive_prefs.default, - Prefs#archive_prefs.always, - Prefs#archive_prefs.never, - NS), - xmpp:make_iq_result(IQ, PrefsEl); + case get_prefs(LUser, LServer) of + {ok, Prefs} -> + PrefsEl = prefs_el(Prefs#archive_prefs.default, + Prefs#archive_prefs.always, + Prefs#archive_prefs.never, + NS), + xmpp:make_iq_result(IQ, PrefsEl); + {error, _} -> + Txt = <<"Database failure">>, + xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)) + end; process_iq(IQ) -> xmpp:make_error(IQ, xmpp:err_not_allowed()). process_iq(LServer, #iq{from = #jid{luser = LUser}, lang = Lang, sub_els = [SubEl]} = IQ, MsgType) -> - case MsgType of - chat -> - maybe_activate_mam(LUser, LServer); - {groupchat, _Role, _MUCState} -> - ok - end, - case SubEl of - #mam_query{rsm = #rsm_set{index = I}} when is_integer(I) -> - Txt = <<"Unsupported <index/> element">>, - xmpp:make_error(IQ, xmpp:err_feature_not_implemented(Txt, Lang)); - #mam_query{rsm = RSM, xmlns = NS} -> - case parse_query(SubEl, Lang) of - {ok, Query} -> - NewRSM = limit_max(RSM, NS), - select_and_send(LServer, Query, NewRSM, IQ, MsgType); - {error, Err} -> - xmpp:make_error(IQ, Err) - end + Ret = case MsgType of + chat -> + maybe_activate_mam(LUser, LServer); + {groupchat, _Role, _MUCState} -> + ok + end, + case Ret of + ok -> + case SubEl of + #mam_query{rsm = #rsm_set{index = I}} when is_integer(I) -> + Txt = <<"Unsupported <index/> element">>, + xmpp:make_error(IQ, xmpp:err_feature_not_implemented(Txt, Lang)); + #mam_query{rsm = RSM, xmlns = NS} -> + case parse_query(SubEl, Lang) of + {ok, Query} -> + NewRSM = limit_max(RSM, NS), + select_and_send(LServer, Query, NewRSM, IQ, MsgType); + {error, Err} -> + xmpp:make_error(IQ, Err) + end + end; + {error, _} -> + Txt = <<"Database failure">>, + xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)) end. -spec should_archive(message(), binary()) -> boolean(). @@ -818,23 +834,27 @@ may_enter_room(From, MUCState) -> -spec store_msg(message(), binary(), binary(), jid(), send | recv) -> ok | pass | any(). store_msg(Pkt, LUser, LServer, Peer, Dir) -> - Prefs = get_prefs(LUser, LServer), - case {should_archive_peer(LUser, LServer, Prefs, Peer), Pkt} of - {true, #message{meta = #{sm_copy := true}}} -> - ok; % Already stored. - {true, _} -> - case ejabberd_hooks:run_fold(store_mam_message, LServer, Pkt, - [LUser, LServer, Peer, chat, Dir]) of - drop -> - pass; - Pkt1 -> - US = {LUser, LServer}, - ID = get_stanza_id(Pkt1), - El = xmpp:encode(Pkt1), - Mod = gen_mod:db_mod(LServer, ?MODULE), - Mod:store(El, LServer, US, chat, Peer, <<"">>, Dir, ID) + case get_prefs(LUser, LServer) of + {ok, Prefs} -> + case {should_archive_peer(LUser, LServer, Prefs, Peer), Pkt} of + {true, #message{meta = #{sm_copy := true}}} -> + ok; % Already stored. + {true, _} -> + case ejabberd_hooks:run_fold(store_mam_message, LServer, Pkt, + [LUser, LServer, Peer, chat, Dir]) of + drop -> + pass; + Pkt1 -> + US = {LUser, LServer}, + ID = get_stanza_id(Pkt1), + El = xmpp:encode(Pkt1), + Mod = gen_mod:db_mod(LServer, ?MODULE), + Mod:store(El, LServer, US, chat, Peer, <<"">>, Dir, ID) + end; + {false, _} -> + pass end; - {false, _} -> + {error, _} -> pass end. @@ -890,18 +910,20 @@ get_prefs(LUser, LServer) -> end, case Res of {ok, Prefs} -> - Prefs; + {ok, Prefs}; + {error, _} -> + {error, db_failure}; error -> ActivateOpt = gen_mod:get_module_opt( LServer, ?MODULE, request_activates_archiving), case ActivateOpt of true -> - #archive_prefs{us = {LUser, LServer}, default = never}; + {ok, #archive_prefs{us = {LUser, LServer}, default = never}}; false -> Default = gen_mod:get_module_opt( LServer, ?MODULE, default), - #archive_prefs{us = {LUser, LServer}, default = Default} + {ok, #archive_prefs{us = {LUser, LServer}, default = Default}} end end. @@ -930,6 +952,8 @@ maybe_activate_mam(LUser, LServer) -> case Res of {ok, _Prefs} -> ok; + {error, _} -> + {error, db_failure}; error -> Default = gen_mod:get_module_opt( LServer, ?MODULE, default), @@ -940,15 +964,21 @@ maybe_activate_mam(LUser, LServer) -> end. select_and_send(LServer, Query, RSM, #iq{from = From, to = To} = IQ, MsgType) -> - {Msgs, IsComplete, Count} = - case MsgType of - chat -> - select(LServer, From, From, Query, RSM, MsgType); - {groupchat, _Role, _MUCState} -> - select(LServer, From, To, Query, RSM, MsgType) - end, - SortedMsgs = lists:keysort(2, Msgs), - send(SortedMsgs, Count, IsComplete, IQ). + Ret = case MsgType of + chat -> + select(LServer, From, From, Query, RSM, MsgType); + {groupchat, _Role, _MUCState} -> + select(LServer, From, To, Query, RSM, MsgType) + end, + case Ret of + {Msgs, IsComplete, Count} -> + SortedMsgs = lists:keysort(2, Msgs), + send(SortedMsgs, Count, IsComplete, IQ); + {error, _} -> + Txt = <<"Database failure">>, + Err = xmpp:err_internal_server_error(Txt, IQ#iq.lang), + xmpp:make_error(IQ, Err) + end. select(_LServer, JidRequestor, JidArchive, Query, RSM, {groupchat, _Role, #state{config = #config{mam = false}, @@ -1045,7 +1075,7 @@ maybe_update_from_to(Pkt, _JidRequestor, _JidArchive, _Peer, chat, _Nick) -> Pkt. -spec send([{binary(), integer(), xmlel()}], - non_neg_integer(), boolean(), iq()) -> iq() | ignore. + count(), boolean(), iq()) -> iq() | ignore. send(Msgs, Count, IsComplete, #iq{from = From, to = To, sub_els = [#mam_query{id = QID, xmlns = NS}]} = IQ) -> @@ -1083,7 +1113,7 @@ send(Msgs, Count, IsComplete, ignore end. --spec make_rsm_out([{binary(), integer(), xmlel()}], non_neg_integer()) -> rsm_set(). +-spec make_rsm_out([{binary(), integer(), xmlel()}], count()) -> rsm_set(). make_rsm_out([], Count) -> #rsm_set{count = Count}; make_rsm_out([{FirstID, _, _}|_] = Msgs, Count) -> @@ -1172,7 +1202,7 @@ mod_opt_type(O) when O == cache_life_time; O == cache_size -> fun (I) when is_integer(I), I > 0 -> I; (infinity) -> infinity end; -mod_opt_type(O) when O == use_cache; O == cache_missed -> +mod_opt_type(O) when O == use_cache; O == cache_missed; O == compress_xml -> fun (B) when is_boolean(B) -> B end; mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; mod_opt_type(default) -> @@ -1187,6 +1217,7 @@ mod_options(Host) -> [{assume_mam_usage, false}, {default, never}, {request_activates_archiving, false}, + {compress_xml, false}, {db_type, ejabberd_config:default_db(Host, ?MODULE)}, {use_cache, ejabberd_config:use_cache(Host)}, {cache_size, ejabberd_config:cache_size(Host)}, |