mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-22 16:20:52 +01:00
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.
This commit is contained in:
parent
48c5ab59f1
commit
de7dc4affa
@ -25,7 +25,7 @@
|
|||||||
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", "a166f0e"}},
|
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", "a166f0e"}},
|
||||||
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.11"}}},
|
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.11"}}},
|
||||||
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.29"}}},
|
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.29"}}},
|
||||||
{xmpp, ".*", {git, "https://github.com/processone/xmpp", "bb88d59"}},
|
{xmpp, ".*", {git, "https://github.com/processone/xmpp", "626a28e"}},
|
||||||
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.13"}}},
|
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.13"}}},
|
||||||
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
|
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
|
||||||
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.2"}}},
|
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.2"}}},
|
||||||
|
113
src/mod_push.erl
113
src/mod_push.erl
@ -44,7 +44,7 @@
|
|||||||
-export([get_commands_spec/0, delete_old_sessions/1]).
|
-export([get_commands_spec/0, delete_old_sessions/1]).
|
||||||
|
|
||||||
%% API (used by mod_push_keepalive).
|
%% API (used by mod_push_keepalive).
|
||||||
-export([notify/1, notify/3, notify/5]).
|
-export([notify/2, notify/4, notify/6]).
|
||||||
|
|
||||||
%% For IQ callbacks
|
%% For IQ callbacks
|
||||||
-export([delete_session/3]).
|
-export([delete_session/3]).
|
||||||
@ -125,6 +125,12 @@ depends(_Host, _Opts) ->
|
|||||||
[].
|
[].
|
||||||
|
|
||||||
-spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
|
-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) ->
|
mod_opt_type(db_type) ->
|
||||||
fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
|
||||||
mod_opt_type(O) when O == cache_life_time; O == cache_size ->
|
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()}].
|
-spec mod_options(binary()) -> [{atom(), any()}].
|
||||||
mod_options(Host) ->
|
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)},
|
{use_cache, ejabberd_config:use_cache(Host)},
|
||||||
{cache_size, ejabberd_config:cache_size(Host)},
|
{cache_size, ejabberd_config:cache_size(Host)},
|
||||||
{cache_missed, ejabberd_config:cache_missed(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) ->
|
c2s_stanza(State, #stream_error{}, _SendResult) ->
|
||||||
State;
|
State;
|
||||||
c2s_stanza(#{push_enabled := true, mgmt_state := pending} = State,
|
c2s_stanza(#{push_enabled := true, mgmt_state := pending} = State,
|
||||||
_Pkt, _SendResult) ->
|
Pkt, _SendResult) ->
|
||||||
?DEBUG("Notifying client of stanza", []),
|
?DEBUG("Notifying client of stanza", []),
|
||||||
notify(State),
|
notify(State, Pkt),
|
||||||
State;
|
State;
|
||||||
c2s_stanza(State, _Pkt, _SendResult) ->
|
c2s_stanza(State, _Pkt, _SendResult) ->
|
||||||
State.
|
State.
|
||||||
@ -351,7 +359,7 @@ mam_message(#message{} = Pkt, LUser, LServer, _Peer, chat, _Dir) ->
|
|||||||
case drop_online_sessions(LUser, LServer, Clients) of
|
case drop_online_sessions(LUser, LServer, Clients) of
|
||||||
[_|_] = Clients1 ->
|
[_|_] = Clients1 ->
|
||||||
?DEBUG("Notifying ~s@~s of MAM message", [LUser, LServer]),
|
?DEBUG("Notifying ~s@~s of MAM message", [LUser, LServer]),
|
||||||
notify(LUser, LServer, Clients1);
|
notify(LUser, LServer, Clients1, Pkt);
|
||||||
[] ->
|
[] ->
|
||||||
ok
|
ok
|
||||||
end;
|
end;
|
||||||
@ -369,7 +377,7 @@ offline_message(#message{to = #jid{luser = LUser, lserver = LServer}} = Pkt) ->
|
|||||||
case lookup_sessions(LUser, LServer) of
|
case lookup_sessions(LUser, LServer) of
|
||||||
{ok, [_|_] = Clients} ->
|
{ok, [_|_] = Clients} ->
|
||||||
?DEBUG("Notifying ~s@~s of offline message", [LUser, LServer]),
|
?DEBUG("Notifying ~s@~s of offline message", [LUser, LServer]),
|
||||||
notify(LUser, LServer, Clients);
|
notify(LUser, LServer, Clients, Pkt);
|
||||||
_ ->
|
_ ->
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
@ -380,7 +388,8 @@ c2s_session_pending(#{push_enabled := true, mgmt_queue := Queue} = State) ->
|
|||||||
case p1_queue:len(Queue) of
|
case p1_queue:len(Queue) of
|
||||||
Len when Len > 0 ->
|
Len when Len > 0 ->
|
||||||
?DEBUG("Notifying client of unacknowledged stanza(s)", []),
|
?DEBUG("Notifying client of unacknowledged stanza(s)", []),
|
||||||
notify(State),
|
Pkt = queue_find(fun is_message_with_body/1, Queue),
|
||||||
|
notify(State, Pkt),
|
||||||
State;
|
State;
|
||||||
0 ->
|
0 ->
|
||||||
State
|
State
|
||||||
@ -412,17 +421,18 @@ remove_user(LUser, LServer) ->
|
|||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Generate push notifications.
|
%% Generate push notifications.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
-spec notify(c2s_state()) -> ok.
|
-spec notify(c2s_state(), xmpp_element() | xmlel() | none) -> ok.
|
||||||
notify(#{jid := #jid{luser = LUser, lserver = LServer}, sid := {TS, _}}) ->
|
notify(#{jid := #jid{luser = LUser, lserver = LServer}, sid := {TS, _}}, Pkt) ->
|
||||||
case lookup_session(LUser, LServer, TS) of
|
case lookup_session(LUser, LServer, TS) of
|
||||||
{ok, Client} ->
|
{ok, Client} ->
|
||||||
notify(LUser, LServer, [Client]);
|
notify(LUser, LServer, [Client], Pkt);
|
||||||
_Err ->
|
_Err ->
|
||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec notify(binary(), binary(), [push_session()]) -> ok.
|
-spec notify(binary(), binary(), [push_session()],
|
||||||
notify(LUser, LServer, Clients) ->
|
xmpp_element() | xmlel() | none) -> ok.
|
||||||
|
notify(LUser, LServer, Clients, Pkt) ->
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun({TS, PushLJID, Node, XData}) ->
|
fun({TS, PushLJID, Node, XData}) ->
|
||||||
HandleResponse = fun(#iq{type = result}) ->
|
HandleResponse = fun(#iq{type = result}) ->
|
||||||
@ -433,14 +443,16 @@ notify(LUser, LServer, Clients) ->
|
|||||||
(timeout) ->
|
(timeout) ->
|
||||||
ok % Hmm.
|
ok % Hmm.
|
||||||
end,
|
end,
|
||||||
notify(LServer, PushLJID, Node, XData, HandleResponse)
|
notify(LServer, PushLJID, Node, XData, Pkt, HandleResponse)
|
||||||
end, Clients).
|
end, Clients).
|
||||||
|
|
||||||
-spec notify(binary(), ljid(), binary(), xdata(),
|
-spec notify(binary(), ljid(), binary(), xdata(),
|
||||||
|
xmpp_element() | xmlel() | none,
|
||||||
fun((iq() | timeout) -> any())) -> ok.
|
fun((iq() | timeout) -> any())) -> ok.
|
||||||
notify(LServer, PushLJID, Node, XData, HandleResponse) ->
|
notify(LServer, PushLJID, Node, XData, Pkt, HandleResponse) ->
|
||||||
From = jid:make(LServer),
|
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]},
|
PubSub = #pubsub{publish = #ps_publish{node = Node, items = [Item]},
|
||||||
publish_options = XData},
|
publish_options = XData},
|
||||||
IQ = #iq{type = set,
|
IQ = #iq{type = set,
|
||||||
@ -571,6 +583,77 @@ drop_online_sessions(LUser, LServer, Clients) ->
|
|||||||
[Client || {TS, _, _, _} = Client <- Clients,
|
[Client || {TS, _, _, _} = Client <- Clients,
|
||||||
lists:keyfind(TS, 1, SessIDs) == false].
|
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.
|
%% Caching.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -184,7 +184,7 @@ c2s_handle_cast(State, _Msg) ->
|
|||||||
c2s_handle_info(#{push_enabled := true, mgmt_state := pending,
|
c2s_handle_info(#{push_enabled := true, mgmt_state := pending,
|
||||||
jid := JID} = State, {timeout, _, push_keepalive}) ->
|
jid := JID} = State, {timeout, _, push_keepalive}) ->
|
||||||
?INFO_MSG("Waking ~s before session times out", [jid:encode(JID)]),
|
?INFO_MSG("Waking ~s before session times out", [jid:encode(JID)]),
|
||||||
mod_push:notify(State),
|
mod_push:notify(State, none),
|
||||||
{stop, State};
|
{stop, State};
|
||||||
c2s_handle_info(State, _) ->
|
c2s_handle_info(State, _) ->
|
||||||
State.
|
State.
|
||||||
@ -229,7 +229,7 @@ wake_all(LServer) ->
|
|||||||
IgnoreResponse = fun(_) -> ok end,
|
IgnoreResponse = fun(_) -> ok end,
|
||||||
lists:foreach(fun({_, PushLJID, Node, XData}) ->
|
lists:foreach(fun({_, PushLJID, Node, XData}) ->
|
||||||
mod_push:notify(LServer, PushLJID, Node,
|
mod_push:notify(LServer, PushLJID, Node,
|
||||||
XData, IgnoreResponse)
|
XData, none, IgnoreResponse)
|
||||||
end, Sessions);
|
end, Sessions);
|
||||||
error ->
|
error ->
|
||||||
error
|
error
|
||||||
|
Loading…
Reference in New Issue
Block a user