aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHolger Weiss <holger@zedat.fu-berlin.de>2018-04-16 23:18:03 +0200
committerHolger Weiss <holger@zedat.fu-berlin.de>2018-04-16 23:18:03 +0200
commitde7dc4affa6c6996bed838b5d4b0b53511962ef0 (patch)
treefdf6face73b13b9af6eaa9fc1153332dc48680d6 /src
parentmod_http_upload*: Remove empty lines after specs (diff)
mod_push: Optionally include message sender/body
Add 'include_sender' and 'include_body' options. If one or both of them are set to 'true', a urn:xmpp:push:summary form with the enabled field(s) is included in push notifications that are generated for messages with a body. The 'include_body' option can instead be set to a static text. In this case, the specified text will be included in place of the actual message body. This can be useful to signal the push service whether the notification was triggered by a message with body (as opposed to other types of traffic) without leaking actual message contents.
Diffstat (limited to 'src')
-rw-r--r--src/mod_push.erl113
-rw-r--r--src/mod_push_keepalive.erl4
2 files changed, 100 insertions, 17 deletions
diff --git a/src/mod_push.erl b/src/mod_push.erl
index 40cfaa625..dd16e87fa 100644
--- a/src/mod_push.erl
+++ b/src/mod_push.erl
@@ -44,7 +44,7 @@
-export([get_commands_spec/0, delete_old_sessions/1]).
%% API (used by mod_push_keepalive).
--export([notify/1, notify/3, notify/5]).
+-export([notify/2, notify/4, notify/6]).
%% For IQ callbacks
-export([delete_session/3]).
@@ -125,6 +125,12 @@ depends(_Host, _Opts) ->
[].
-spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
+mod_opt_type(include_sender) ->
+ fun (B) when is_boolean(B) -> B end;
+mod_opt_type(include_body) ->
+ fun (B) when is_boolean(B) -> B;
+ (S) -> iolist_to_binary(S)
+ end;
mod_opt_type(db_type) ->
fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(O) when O == cache_life_time; O == cache_size ->
@@ -136,7 +142,9 @@ mod_opt_type(O) when O == use_cache; O == cache_missed ->
-spec mod_options(binary()) -> [{atom(), any()}].
mod_options(Host) ->
- [{db_type, ejabberd_config:default_db(Host, ?MODULE)},
+ [{include_sender, false},
+ {include_body, false},
+ {db_type, ejabberd_config:default_db(Host, ?MODULE)},
{use_cache, ejabberd_config:use_cache(Host)},
{cache_size, ejabberd_config:cache_size(Host)},
{cache_missed, ejabberd_config:cache_missed(Host)},
@@ -336,9 +344,9 @@ disable(#jid{luser = LUser, lserver = LServer, lresource = LResource} = JID,
c2s_stanza(State, #stream_error{}, _SendResult) ->
State;
c2s_stanza(#{push_enabled := true, mgmt_state := pending} = State,
- _Pkt, _SendResult) ->
+ Pkt, _SendResult) ->
?DEBUG("Notifying client of stanza", []),
- notify(State),
+ notify(State, Pkt),
State;
c2s_stanza(State, _Pkt, _SendResult) ->
State.
@@ -351,7 +359,7 @@ mam_message(#message{} = Pkt, LUser, LServer, _Peer, chat, _Dir) ->
case drop_online_sessions(LUser, LServer, Clients) of
[_|_] = Clients1 ->
?DEBUG("Notifying ~s@~s of MAM message", [LUser, LServer]),
- notify(LUser, LServer, Clients1);
+ notify(LUser, LServer, Clients1, Pkt);
[] ->
ok
end;
@@ -369,7 +377,7 @@ offline_message(#message{to = #jid{luser = LUser, lserver = LServer}} = Pkt) ->
case lookup_sessions(LUser, LServer) of
{ok, [_|_] = Clients} ->
?DEBUG("Notifying ~s@~s of offline message", [LUser, LServer]),
- notify(LUser, LServer, Clients);
+ notify(LUser, LServer, Clients, Pkt);
_ ->
ok
end,
@@ -380,7 +388,8 @@ c2s_session_pending(#{push_enabled := true, mgmt_queue := Queue} = State) ->
case p1_queue:len(Queue) of
Len when Len > 0 ->
?DEBUG("Notifying client of unacknowledged stanza(s)", []),
- notify(State),
+ Pkt = queue_find(fun is_message_with_body/1, Queue),
+ notify(State, Pkt),
State;
0 ->
State
@@ -412,17 +421,18 @@ remove_user(LUser, LServer) ->
%%--------------------------------------------------------------------
%% Generate push notifications.
%%--------------------------------------------------------------------
--spec notify(c2s_state()) -> ok.
-notify(#{jid := #jid{luser = LUser, lserver = LServer}, sid := {TS, _}}) ->
+-spec notify(c2s_state(), xmpp_element() | xmlel() | none) -> ok.
+notify(#{jid := #jid{luser = LUser, lserver = LServer}, sid := {TS, _}}, Pkt) ->
case lookup_session(LUser, LServer, TS) of
{ok, Client} ->
- notify(LUser, LServer, [Client]);
+ notify(LUser, LServer, [Client], Pkt);
_Err ->
ok
end.
--spec notify(binary(), binary(), [push_session()]) -> ok.
-notify(LUser, LServer, Clients) ->
+-spec notify(binary(), binary(), [push_session()],
+ xmpp_element() | xmlel() | none) -> ok.
+notify(LUser, LServer, Clients, Pkt) ->
lists:foreach(
fun({TS, PushLJID, Node, XData}) ->
HandleResponse = fun(#iq{type = result}) ->
@@ -433,14 +443,16 @@ notify(LUser, LServer, Clients) ->
(timeout) ->
ok % Hmm.
end,
- notify(LServer, PushLJID, Node, XData, HandleResponse)
+ notify(LServer, PushLJID, Node, XData, Pkt, HandleResponse)
end, Clients).
-spec notify(binary(), ljid(), binary(), xdata(),
+ xmpp_element() | xmlel() | none,
fun((iq() | timeout) -> any())) -> ok.
-notify(LServer, PushLJID, Node, XData, HandleResponse) ->
+notify(LServer, PushLJID, Node, XData, Pkt, HandleResponse) ->
From = jid:make(LServer),
- Item = #ps_item{sub_els = [#push_notification{}]},
+ Summary = make_summary(LServer, Pkt),
+ Item = #ps_item{sub_els = [#push_notification{xdata = Summary}]},
PubSub = #pubsub{publish = #ps_publish{node = Node, items = [Item]},
publish_options = XData},
IQ = #iq{type = set,
@@ -571,6 +583,77 @@ drop_online_sessions(LUser, LServer, Clients) ->
[Client || {TS, _, _, _} = Client <- Clients,
lists:keyfind(TS, 1, SessIDs) == false].
+-spec queue_find(fun((stanza()) -> boolean()), p1_queue:queue())
+ -> stanza() | none.
+queue_find(Pred, Queue) ->
+ case p1_queue:out(Queue) of
+ {{value, {_, _, Pkt}}, Queue1} ->
+ case Pred(Pkt) of
+ true ->
+ Pkt;
+ false ->
+ queue_find(Pred, Queue1)
+ end;
+ {empty, _Queue1} ->
+ none
+ end.
+
+-spec make_summary(binary(), xmpp_element() | xmlel() | none)
+ -> xdata() | undefined.
+make_summary(Host, #message{from = From} = Pkt) ->
+ case {gen_mod:get_module_opt(Host, ?MODULE, include_sender),
+ gen_mod:get_module_opt(Host, ?MODULE, include_body)} of
+ {false, false} ->
+ undefined;
+ {IncludeSender, IncludeBody} ->
+ case get_body_text(Pkt) of
+ none ->
+ undefined;
+ Text ->
+ Fields1 = case IncludeBody of
+ StaticText when is_binary(StaticText) ->
+ [{'last-message-body', StaticText}];
+ true ->
+ [{'last-message-body', Text}];
+ false ->
+ []
+ end,
+ Fields2 = case IncludeSender of
+ true ->
+ [{'last-message-sender', From} | Fields1];
+ false ->
+ Fields1
+ end,
+ #xdata{type = submit, fields = push_summary:encode(Fields2)}
+ end
+ end;
+make_summary(_Host, _Pkt) ->
+ undefined.
+
+-spec is_message_with_body(stanza()) -> boolean().
+is_message_with_body(#message{} = Msg) ->
+ get_body_text(Msg) /= none;
+is_message_with_body(_Stanza) ->
+ false.
+
+-spec get_body_text(message()) -> binary() | none.
+get_body_text(#message{body = Body} = Msg) ->
+ case xmpp:get_text(Body) of
+ Text when byte_size(Text) > 0 ->
+ Text;
+ <<>> ->
+ case body_is_encrypted(Msg) of
+ true ->
+ <<"(encrypted)">>;
+ false ->
+ none
+ end
+ end.
+
+-spec body_is_encrypted(message()) -> boolean().
+body_is_encrypted(#message{sub_els = SubEls}) ->
+ lists:keyfind(<<"encrypted">>, #xmlel.name, SubEls) /= false.
+
%%--------------------------------------------------------------------
%% Caching.
%%--------------------------------------------------------------------
diff --git a/src/mod_push_keepalive.erl b/src/mod_push_keepalive.erl
index 750427ee1..7c1815c02 100644
--- a/src/mod_push_keepalive.erl
+++ b/src/mod_push_keepalive.erl
@@ -184,7 +184,7 @@ c2s_handle_cast(State, _Msg) ->
c2s_handle_info(#{push_enabled := true, mgmt_state := pending,
jid := JID} = State, {timeout, _, push_keepalive}) ->
?INFO_MSG("Waking ~s before session times out", [jid:encode(JID)]),
- mod_push:notify(State),
+ mod_push:notify(State, none),
{stop, State};
c2s_handle_info(State, _) ->
State.
@@ -229,7 +229,7 @@ wake_all(LServer) ->
IgnoreResponse = fun(_) -> ok end,
lists:foreach(fun({_, PushLJID, Node, XData}) ->
mod_push:notify(LServer, PushLJID, Node,
- XData, IgnoreResponse)
+ XData, none, IgnoreResponse)
end, Sessions);
error ->
error