aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHolger Weiss <holger@zedat.fu-berlin.de>2016-05-17 22:12:04 +0200
committerHolger Weiss <holger@zedat.fu-berlin.de>2016-05-17 22:12:04 +0200
commit8f72c27b88cd83e50879caf80d8d2546fd19ef2d (patch)
tree554589302b467b08821b7b464a6fe855054c0fac
parentmod_client_state: Queue chat state notifications (diff)
mod_client_state: Add "queue_pep" option
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.
-rw-r--r--src/mod_client_state.erl105
-rw-r--r--test/ejabberd_SUITE.erl51
-rw-r--r--test/ejabberd_SUITE_data/ejabberd.yml2
3 files changed, 141 insertions, 17 deletions
diff --git a/src/mod_client_state.erl b/src/mod_client_state.erl
index f51a7fd24..4e5938f06 100644
--- a/src/mod_client_state.erl
+++ b/src/mod_client_state.erl
@@ -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").
@@ -41,13 +41,19 @@
-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].
diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl
index f3f7ebde3..800d5ebf3 100644
--- a/test/ejabberd_SUITE.erl
+++ b/test/ejabberd_SUITE.erl
@@ -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{}]}),
diff --git a/test/ejabberd_SUITE_data/ejabberd.yml b/test/ejabberd_SUITE_data/ejabberd.yml
index 30fff88fc..1adbcce8a 100644
--- a/test/ejabberd_SUITE_data/ejabberd.yml
+++ b/test/ejabberd_SUITE_data/ejabberd.yml
@@ -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: []