XEP-0198: Let MAM take care of pending messages
authorHolger Weiss <holger@zedat.fu-berlin.de>
Fri, 15 Jan 2016 00:08:22 +0000 (01:08 +0100)
committerHolger Weiss <holger@zedat.fu-berlin.de>
Fri, 15 Jan 2016 00:08:22 +0000 (01:08 +0100)
If a stream management session times out for a user who appears to be
using MAM, drop any unacknowledged messages rather than resending or
bouncing them.  This avoids duplicates or bogus error messages.

However, this is only done if the new mod_mam option "assume_mam_usage"
is set to 'if_enabled' or 'on_request'.  In the former case, a user is
assumed to be using MAM if archiving is enabled for his account.  In the
latter case, MAM usage is assumed only if archiving was explicitly
requested by the client, or if archiving was enabled by means of
mod_mam's "request_activates_archiving" option.

src/ejabberd_c2s.erl
src/mod_mam.erl

index 0035ed6d183581e52eb5a57361696689fa87ed6d..021fb97397dc6300293fde5f6eaede619c523d49 100644 (file)
@@ -2878,7 +2878,16 @@ handle_unacked_stanzas(StateData)
                      ?DEBUG("Dropping forwarded message stanza from ~s",
                             [xml:get_attr_s(<<"from">>, El#xmlel.attrs)]);
                  false ->
-                     ReRoute(From, To, El, Time)
+                     case ejabberd_hooks:run_fold(message_is_archived,
+                                                  StateData#state.server,
+                                                  false,
+                                                  [StateData, From,
+                                                   StateData#state.jid, El]) of
+                       true ->
+                           ok;
+                       false ->
+                           ReRoute(From, To, El, Time)
+                     end
                end
        end,
     handle_unacked_stanzas(StateData, F);
index c20a941df713913075e1f4961b07770e8a098494..f4943b3d286dfc3138a21e8d7d4df2ae8358d73c 100644 (file)
@@ -36,7 +36,7 @@
 -export([user_send_packet/4, user_receive_packet/5,
         process_iq_v0_2/3, process_iq_v0_3/3, disco_sm_features/5,
         remove_user/2, remove_user/3, mod_opt_type/1, muc_process_iq/4,
-        muc_filter_message/5, delete_old_messages/2]).
+        muc_filter_message/5, message_is_archived/5, delete_old_messages/2]).
 
 -include_lib("stdlib/include/ms_transform.hrl").
 -include("jlib.hrl").
@@ -105,6 +105,17 @@ start(Host, Opts) ->
                       remove_user, 50),
     ejabberd_hooks:add(anonymous_purge_hook, Host, ?MODULE,
                       remove_user, 50),
+    case gen_mod:get_opt(assume_mam_usage, Opts,
+                        fun(if_enabled) -> if_enabled;
+                           (on_request) -> on_request;
+                           (never) -> never
+                        end, never) of
+       never ->
+           ok;
+       _ ->
+           ejabberd_hooks:add(message_is_archived, Host, ?MODULE,
+                              message_is_archived, 50)
+    end,
     ejabberd_commands:register_commands(commands()),
     ok.
 
@@ -150,6 +161,17 @@ stop(Host) ->
                          remove_user, 50),
     ejabberd_hooks:delete(anonymous_purge_hook, Host,
                          ?MODULE, remove_user, 50),
+    case gen_mod:get_module_opt(Host, ?MODULE, assume_mam_usage,
+                               fun(if_enabled) -> if_enabled;
+                                  (on_request) -> on_request;
+                                  (never) -> never
+                               end, never) of
+       never ->
+           ok;
+       _ ->
+           ejabberd_hooks:delete(message_is_archived, Host, ?MODULE,
+                                 message_is_archived, 50)
+    end,
     ejabberd_commands:unregister_commands(commands()),
     ok.
 
@@ -307,6 +329,34 @@ disco_sm_features({result, OtherFeatures},
 disco_sm_features(Acc, _From, _To, _Node, _Lang) ->
     Acc.
 
+message_is_archived(true, _C2SState, _Peer, _JID, _Pkt) ->
+    true;
+message_is_archived(false, C2SState, Peer,
+                   #jid{luser = LUser, lserver = LServer}, Pkt) ->
+    Res = case gen_mod:get_module_opt(LServer, ?MODULE, assume_mam_usage,
+                                     fun(if_enabled) -> if_enabled;
+                                        (on_request) -> on_request;
+                                        (never) -> never
+                                     end, never) of
+             if_enabled ->
+                 get_prefs(LUser, LServer);
+             on_request ->
+                 DBType = gen_mod:db_type(LServer, ?MODULE),
+                 cache_tab:lookup(archive_prefs, {LUser, LServer},
+                                  fun() ->
+                                          get_prefs(LUser, LServer, DBType)
+                                  end);
+             never ->
+                 error
+         end,
+    case Res of
+       {ok, Prefs} ->
+           should_archive(strip_my_archived_tag(Pkt, LServer), LServer)
+               andalso should_archive_peer(C2SState, Prefs, Peer);
+       error ->
+           false
+    end.
+
 delete_old_messages(TypeBin, Days) when TypeBin == <<"chat">>;
                                        TypeBin == <<"groupchat">>;
                                        TypeBin == <<"all">> ->
@@ -1318,6 +1368,11 @@ commands() ->
                        args = [{type, binary}, {days, integer}],
                        result = {res, rescode}}].
 
+mod_opt_type(assume_mam_usage) ->
+    fun(if_enabled) -> if_enabled;
+       (on_request) -> on_request;
+       (never) -> never
+    end;
 mod_opt_type(cache_life_time) ->
     fun (I) when is_integer(I), I > 0 -> I end;
 mod_opt_type(cache_size) ->
@@ -1334,5 +1389,5 @@ mod_opt_type(request_activates_archiving) ->
 mod_opt_type(store_body_only) ->
     fun (B) when is_boolean(B) -> B end;
 mod_opt_type(_) ->
-    [cache_life_time, cache_size, db_type, default, iqdisc,
+    [assume_mam_usage, cache_life_time, cache_size, db_type, default, iqdisc,
      request_activates_archiving, store_body_only].