]> granicus.if.org Git - ejabberd/commitdiff
mod_client_state: Add "queue_pep" option
authorHolger Weiss <holger@zedat.fu-berlin.de>
Tue, 17 May 2016 20:12:04 +0000 (22:12 +0200)
committerHolger Weiss <holger@zedat.fu-berlin.de>
Tue, 17 May 2016 20:12:04 +0000 (22:12 +0200)
If the new "queue_pep" option is enabled and the client is inactive, PEP
notifications are throttled in a similar way to presence stanzas and
chat states.  Only the most recent notification of a given node and
payload type will be queued from a given contact.

src/mod_client_state.erl
test/ejabberd_SUITE.erl
test/ejabberd_SUITE_data/ejabberd.yml

index f51a7fd241680277c43855b6daaa385165ade5e4..4e5938f066fc62fae304b1fd2317d49ed911c341 100644 (file)
@@ -31,8 +31,8 @@
 -behavior(gen_mod).
 
 -export([start/2, stop/1, add_stream_feature/2,
-        filter_presence/3, filter_chat_states/3, filter_other/3, flush_queue/2,
-        mod_opt_type/1]).
+        filter_presence/3, filter_chat_states/3, filter_pep/3, filter_other/3,
+        flush_queue/2, mod_opt_type/1]).
 
 -include("ejabberd.hrl").
 -include("logger.hrl").
 -define(CSI_QUEUE_MAX, 100).
 
 start(Host, Opts) ->
-    QueuePresence = gen_mod:get_opt(queue_presence, Opts,
-                                   fun(B) when is_boolean(B) -> B end,
-                                   true),
-    QueueChatStates = gen_mod:get_opt(queue_chat_states, Opts,
-                                     fun(B) when is_boolean(B) -> B end,
-                                     true),
-    if QueuePresence; QueueChatStates ->
+    QueuePresence =
+       gen_mod:get_opt(queue_presence, Opts,
+                       fun(B) when is_boolean(B) -> B end,
+                       true),
+    QueueChatStates =
+       gen_mod:get_opt(queue_chat_states, Opts,
+                       fun(B) when is_boolean(B) -> B end,
+                       true),
+    QueuePEP =
+       gen_mod:get_opt(queue_pep, Opts,
+                       fun(B) when is_boolean(B) -> B end,
+                       false),
+    if QueuePresence; QueueChatStates; QueuePEP ->
           ejabberd_hooks:add(c2s_post_auth_features, Host, ?MODULE,
                              add_stream_feature, 50),
           if QueuePresence ->
@@ -60,6 +66,11 @@ start(Host, Opts) ->
                                     filter_chat_states, 50);
              true -> ok
           end,
+          if QueuePEP ->
+                 ejabberd_hooks:add(csi_filter_stanza, Host, ?MODULE,
+                                    filter_pep, 50);
+             true -> ok
+          end,
           ejabberd_hooks:add(csi_filter_stanza, Host, ?MODULE,
                              filter_other, 100),
           ejabberd_hooks:add(csi_flush_queue, Host, ?MODULE,
@@ -68,13 +79,19 @@ start(Host, Opts) ->
     end.
 
 stop(Host) ->
-    QueuePresence = gen_mod:get_module_opt(Host, ?MODULE, queue_presence,
-                                          fun(B) when is_boolean(B) -> B end,
-                                          true),
-    QueueChatStates = gen_mod:get_module_opt(Host, ?MODULE, queue_chat_states,
-                                            fun(B) when is_boolean(B) -> B end,
-                                            true),
-    if QueuePresence; QueueChatStates ->
+    QueuePresence =
+       gen_mod:get_module_opt(Host, ?MODULE, queue_presence,
+                              fun(B) when is_boolean(B) -> B end,
+                              true),
+    QueueChatStates =
+       gen_mod:get_module_opt(Host, ?MODULE, queue_chat_states,
+                              fun(B) when is_boolean(B) -> B end,
+                              true),
+    QueuePEP =
+       gen_mod:get_module_opt(Host, ?MODULE, queue_pep,
+                              fun(B) when is_boolean(B) -> B end,
+                              false),
+    if QueuePresence; QueueChatStates; QueuePEP ->
           ejabberd_hooks:delete(c2s_post_auth_features, Host, ?MODULE,
                                 add_stream_feature, 50),
           if QueuePresence ->
@@ -87,6 +104,11 @@ stop(Host) ->
                                        filter_chat_states, 50);
              true -> ok
           end,
+          if QueuePEP ->
+                 ejabberd_hooks:delete(csi_filter_stanza, Host, ?MODULE,
+                                       filter_pep, 50);
+             true -> ok
+          end,
           ejabberd_hooks:delete(csi_filter_stanza, Host, ?MODULE,
                                 filter_other, 100),
           ejabberd_hooks:delete(csi_flush_queue, Host, ?MODULE,
@@ -122,6 +144,17 @@ filter_chat_states({C2SState, _OutStanzas} = Acc, Host,
     end;
 filter_chat_states(Acc, _Host, _Stanza) -> Acc.
 
+filter_pep({C2SState, _OutStanzas} = Acc, Host,
+          #xmlel{name = <<"message">>} = Stanza) ->
+    case find_pep(Stanza) of
+      {value, Type} ->
+         ?DEBUG("Got PEP notification", []),
+         queue_add(Type, Stanza, Host, C2SState);
+      false ->
+         Acc
+    end;
+filter_pep(Acc, _Host, _Stanza) -> Acc.
+
 filter_other({C2SState, _OutStanzas}, Host, Stanza) ->
     ?DEBUG("Won't add stanza to CSI queue", []),
     queue_take(Stanza, Host, C2SState).
@@ -132,6 +165,42 @@ flush_queue({C2SState, _OutStanzas}, Host) ->
     NewState = set_queue([], C2SState),
     {stop, {NewState, get_stanzas(Queue, Host)}}.
 
+find_pep(#xmlel{name = <<"message">>} = Stanza) ->
+    From = fxml:get_tag_attr_s(<<"from">>, Stanza),
+    case jid:from_string(From) of
+      #jid{luser = <<>>} -> % It's not PEP.
+         false;
+      _ ->
+         case fxml:get_subtag_with_xmlns(Stanza, <<"event">>,
+                                         ?NS_PUBSUB_EVENT) of
+           #xmlel{children = Els} ->
+               get_pep_node_and_xmlns(fxml:remove_cdata(Els));
+           false ->
+               false
+         end
+    end.
+
+get_pep_node_and_xmlns([#xmlel{name = <<"items">>, attrs = ItemsAttrs,
+                              children = Item}]) ->
+    case {fxml:get_attr(<<"node">>, ItemsAttrs), fxml:remove_cdata(Item)} of
+      {{value, Node}, [#xmlel{name = <<"item">>, children = Payload}]} ->
+         case fxml:remove_cdata(Payload) of
+           [#xmlel{attrs = PayloadAttrs}] ->
+               case fxml:get_attr(<<"xmlns">>, PayloadAttrs) of
+                 {value, XMLNS} ->
+                     {value, {Node, XMLNS}};
+                 false ->
+                     false
+               end;
+           _ ->
+               false
+         end;
+      _ ->
+         false
+    end;
+get_pep_node_and_xmlns(_) ->
+    false.
+
 queue_add(Type, Stanza, Host, C2SState) ->
     case get_queue(C2SState) of
       Queue when length(Queue) >= ?CSI_QUEUE_MAX ->
@@ -179,4 +248,6 @@ mod_opt_type(queue_presence) ->
     fun(B) when is_boolean(B) -> B end;
 mod_opt_type(queue_chat_states) ->
     fun(B) when is_boolean(B) -> B end;
-mod_opt_type(_) -> [queue_presence, queue_chat_states].
+mod_opt_type(queue_pep) ->
+    fun(B) when is_boolean(B) -> B end;
+mod_opt_type(_) -> [queue_presence, queue_chat_states, queue_pep].
index f3f7ebde3a9914bdf2f28b783ca54fc3782855a6..800d5ebf396f3d66072b91909d36e2ec80cfc2dc 100644 (file)
@@ -2255,12 +2255,41 @@ client_state_master(Config) ->
     ChatState = #message{to = Peer, thread = <<"1">>,
                         sub_els = [#chatstate{type = active}]},
     Message = ChatState#message{body = [#text{data = <<"body">>}]},
+    PepPayload = xmpp_codec:encode(#presence{}),
+    PepOne = #message{
+               to = Peer,
+               sub_els =
+                   [#pubsub_event{
+                       items =
+                           [#pubsub_event_items{
+                               node = <<"foo-1">>,
+                               items =
+                                   [#pubsub_event_item{
+                                       id = <<"pep-1">>,
+                                       xml_els = [PepPayload]}]}]}]},
+    PepTwo = #message{
+               to = Peer,
+               sub_els =
+                   [#pubsub_event{
+                       items =
+                           [#pubsub_event_items{
+                               node = <<"foo-2">>,
+                               items =
+                                   [#pubsub_event_item{
+                                       id = <<"pep-2">>,
+                                       xml_els = [PepPayload]}]}]}]},
     %% Wait for the slave to become inactive.
     wait_for_slave(Config),
     %% Should be queued (but see below):
     send(Config, Presence),
     %% Should replace the previous presence in the queue:
     send(Config, Presence#presence{type = unavailable}),
+    %% The following two PEP stanzas should be queued (but see below):
+    send(Config, PepOne),
+    send(Config, PepTwo),
+    %% The following two PEP stanzas should replace the previous two:
+    send(Config, PepOne),
+    send(Config, PepTwo),
     %% Should be queued (but see below):
     send(Config, ChatState),
     %% Should replace the previous chat state in the queue:
@@ -2279,6 +2308,28 @@ client_state_slave(Config) ->
     wait_for_master(Config),
     ?recv1(#presence{from = Peer, type = unavailable,
                     sub_els = [#delay{}]}),
+    #message{
+       from = Peer,
+       sub_els =
+          [#pubsub_event{
+              items =
+                  [#pubsub_event_items{
+                      node = <<"foo-1">>,
+                      items =
+                          [#pubsub_event_item{
+                              id = <<"pep-1">>}]}]},
+           #delay{}]} = recv(),
+    #message{
+       from = Peer,
+       sub_els =
+          [#pubsub_event{
+              items =
+                  [#pubsub_event_items{
+                      node = <<"foo-2">>,
+                      items =
+                          [#pubsub_event_item{
+                              id = <<"pep-2">>}]}]},
+           #delay{}]} = recv(),
     ?recv1(#message{from = Peer, thread = <<"1">>,
                    sub_els = [#chatstate{type = composing},
                               #delay{}]}),
index 30fff88fce53d43badc4b37d3b26d3c177366e2a..1adbcce8a5be9f97d38f581fe45f029d7748ab34 100644 (file)
@@ -215,6 +215,7 @@ Welcome to this XMPP server."
       mod_client_state:
         queue_presence: true
         queue_chat_states: true
+        queue_pep: true
       mod_adhoc: []
       mod_configure: []
       mod_disco: []
@@ -271,6 +272,7 @@ Welcome to this XMPP server."
       mod_client_state:
         queue_presence: true
         queue_chat_states: true
+        queue_pep: true
       mod_adhoc: []
       mod_configure: []
       mod_disco: []