mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-22 17:28:25 +01:00
Merge pull request #298 from weiss/csi
Add support for XEP-0352: Client State Indication (CSI)
This commit is contained in:
commit
c90786527e
@ -66,6 +66,7 @@
|
|||||||
\newcommand{\module}[1]{\texttt{#1}}
|
\newcommand{\module}[1]{\texttt{#1}}
|
||||||
\newcommand{\modadhoc}{\module{mod\_adhoc}}
|
\newcommand{\modadhoc}{\module{mod\_adhoc}}
|
||||||
\newcommand{\modannounce}{\module{mod\_announce}}
|
\newcommand{\modannounce}{\module{mod\_announce}}
|
||||||
|
\newcommand{\modclientstate}{\module{mod\_client\_state}}
|
||||||
\newcommand{\modblocking}{\module{mod\_blocking}}
|
\newcommand{\modblocking}{\module{mod\_blocking}}
|
||||||
\newcommand{\modcaps}{\module{mod\_caps}}
|
\newcommand{\modcaps}{\module{mod\_caps}}
|
||||||
\newcommand{\modcarboncopy}{\module{mod\_carboncopy}}
|
\newcommand{\modcarboncopy}{\module{mod\_carboncopy}}
|
||||||
@ -2781,6 +2782,7 @@ The following table lists all modules included in \ejabberd{}.
|
|||||||
\hline \modblocking{} & Simple Communications Blocking (\xepref{0191}) & \modprivacy{} \\
|
\hline \modblocking{} & Simple Communications Blocking (\xepref{0191}) & \modprivacy{} \\
|
||||||
\hline \modcaps{} & Entity Capabilities (\xepref{0115}) & \\
|
\hline \modcaps{} & Entity Capabilities (\xepref{0115}) & \\
|
||||||
\hline \modcarboncopy{} & Message Carbons (\xepref{0280}) & \\
|
\hline \modcarboncopy{} & Message Carbons (\xepref{0280}) & \\
|
||||||
|
\hline \ahrefloc{modclientstate}{\modclientstate{}} & Filter stanzas for inactive clients & \\
|
||||||
\hline \modconfigure{} & Server configuration using Ad-Hoc & \modadhoc{} \\
|
\hline \modconfigure{} & Server configuration using Ad-Hoc & \modadhoc{} \\
|
||||||
\hline \ahrefloc{moddisco}{\moddisco{}} & Service Discovery (\xepref{0030}) & \\
|
\hline \ahrefloc{moddisco}{\moddisco{}} & Service Discovery (\xepref{0030}) & \\
|
||||||
\hline \ahrefloc{modecho}{\modecho{}} & Echoes XMPP stanzas & \\
|
\hline \ahrefloc{modecho}{\modecho{}} & Echoes XMPP stanzas & \\
|
||||||
@ -3001,6 +3003,38 @@ Note that \modannounce{} can be resource intensive on large
|
|||||||
deployments as it can broadcast lot of messages. This module should be
|
deployments as it can broadcast lot of messages. This module should be
|
||||||
disabled for instances of \ejabberd{} with hundreds of thousands users.
|
disabled for instances of \ejabberd{} with hundreds of thousands users.
|
||||||
|
|
||||||
|
\makesubsection{modclientstate}{\modclientstate{}}
|
||||||
|
\ind{modules!\modclientstate{}}\ind{Client State Indication}
|
||||||
|
\ind{protocols!XEP-0352: Client State Indication}
|
||||||
|
|
||||||
|
This module allows for queueing or dropping certain types of stanzas
|
||||||
|
when a client indicates that the user is not actively using the client
|
||||||
|
at the moment (see \xepref{0352}). This can save bandwidth and
|
||||||
|
resources.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
\begin{description}
|
||||||
|
\titem{drop\_chat\_states: true|false} \ind{options!drop\_chat\_states}
|
||||||
|
Drop most "standalone" Chat State Notifications (as defined in
|
||||||
|
\xepref{0085}) while a client indicates inactivity. The default value
|
||||||
|
is \term{false}.
|
||||||
|
\titem{queue\_presence: true|false} \ind{options!queue\_presence}
|
||||||
|
While a client is inactive, queue presence stanzas that indicate
|
||||||
|
(un)availability. The latest queued stanza of each contact is
|
||||||
|
delivered as soon as the client becomes active again. The default
|
||||||
|
value is \term{false}.
|
||||||
|
\end{description}
|
||||||
|
|
||||||
|
Example:
|
||||||
|
\begin{verbatim}
|
||||||
|
modules:
|
||||||
|
...
|
||||||
|
mod_client_state:
|
||||||
|
drop_chat_states: true
|
||||||
|
queue_presence: true
|
||||||
|
...
|
||||||
|
\end{verbatim}
|
||||||
|
|
||||||
\makesubsection{moddisco}{\moddisco{}}
|
\makesubsection{moddisco}{\moddisco{}}
|
||||||
\ind{modules!\moddisco{}}
|
\ind{modules!\moddisco{}}
|
||||||
\ind{protocols!XEP-0030: Service Discovery}
|
\ind{protocols!XEP-0030: Service Discovery}
|
||||||
|
@ -558,6 +558,9 @@ modules:
|
|||||||
mod_blocking: {} # requires mod_privacy
|
mod_blocking: {} # requires mod_privacy
|
||||||
mod_caps: {}
|
mod_caps: {}
|
||||||
mod_carboncopy: {}
|
mod_carboncopy: {}
|
||||||
|
mod_client_state:
|
||||||
|
drop_chat_states: true
|
||||||
|
queue_presence: false
|
||||||
mod_configure: {} # requires mod_adhoc
|
mod_configure: {} # requires mod_adhoc
|
||||||
mod_disco: {}
|
mod_disco: {}
|
||||||
## mod_echo: {}
|
## mod_echo: {}
|
||||||
|
@ -147,5 +147,6 @@
|
|||||||
-define(NS_CARBONS_2, <<"urn:xmpp:carbons:2">>).
|
-define(NS_CARBONS_2, <<"urn:xmpp:carbons:2">>).
|
||||||
-define(NS_CARBONS_1, <<"urn:xmpp:carbons:1">>).
|
-define(NS_CARBONS_1, <<"urn:xmpp:carbons:1">>).
|
||||||
-define(NS_FORWARD, <<"urn:xmpp:forward:0">>).
|
-define(NS_FORWARD, <<"urn:xmpp:forward:0">>).
|
||||||
|
-define(NS_CLIENT_STATE, <<"urn:xmpp:csi:0">>).
|
||||||
-define(NS_STREAM_MGMT_2, <<"urn:xmpp:sm:2">>).
|
-define(NS_STREAM_MGMT_2, <<"urn:xmpp:sm:2">>).
|
||||||
-define(NS_STREAM_MGMT_3, <<"urn:xmpp:sm:3">>).
|
-define(NS_STREAM_MGMT_3, <<"urn:xmpp:sm:3">>).
|
||||||
|
@ -108,6 +108,8 @@
|
|||||||
auth_module = unknown,
|
auth_module = unknown,
|
||||||
ip,
|
ip,
|
||||||
aux_fields = [],
|
aux_fields = [],
|
||||||
|
csi_state = active,
|
||||||
|
csi_queue = [],
|
||||||
mgmt_state,
|
mgmt_state,
|
||||||
mgmt_xmlns,
|
mgmt_xmlns,
|
||||||
mgmt_queue,
|
mgmt_queue,
|
||||||
@ -475,6 +477,10 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
|||||||
false ->
|
false ->
|
||||||
[]
|
[]
|
||||||
end,
|
end,
|
||||||
|
ClientStateFeature =
|
||||||
|
[#xmlel{name = <<"csi">>,
|
||||||
|
attrs = [{<<"xmlns">>, ?NS_CLIENT_STATE}],
|
||||||
|
children = []}],
|
||||||
StreamFeatures = [#xmlel{name = <<"bind">>,
|
StreamFeatures = [#xmlel{name = <<"bind">>,
|
||||||
attrs = [{<<"xmlns">>, ?NS_BIND}],
|
attrs = [{<<"xmlns">>, ?NS_BIND}],
|
||||||
children = []},
|
children = []},
|
||||||
@ -484,6 +490,7 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
|||||||
++
|
++
|
||||||
RosterVersioningFeature ++
|
RosterVersioningFeature ++
|
||||||
StreamManagementFeature ++
|
StreamManagementFeature ++
|
||||||
|
ClientStateFeature ++
|
||||||
ejabberd_hooks:run_fold(c2s_stream_features,
|
ejabberd_hooks:run_fold(c2s_stream_features,
|
||||||
Server, [], [Server]),
|
Server, [], [Server]),
|
||||||
send_element(StateData,
|
send_element(StateData,
|
||||||
@ -1165,6 +1172,17 @@ wait_for_session(closed, StateData) ->
|
|||||||
session_established({xmlstreamelement, #xmlel{name = Name} = El}, StateData)
|
session_established({xmlstreamelement, #xmlel{name = Name} = El}, StateData)
|
||||||
when ?IS_STREAM_MGMT_TAG(Name) ->
|
when ?IS_STREAM_MGMT_TAG(Name) ->
|
||||||
fsm_next_state(session_established, dispatch_stream_mgmt(El, StateData));
|
fsm_next_state(session_established, dispatch_stream_mgmt(El, StateData));
|
||||||
|
session_established({xmlstreamelement,
|
||||||
|
#xmlel{name = <<"active">>,
|
||||||
|
attrs = [{<<"xmlns">>, ?NS_CLIENT_STATE}]}},
|
||||||
|
StateData) ->
|
||||||
|
NewStateData = csi_queue_flush(StateData),
|
||||||
|
fsm_next_state(session_established, NewStateData#state{csi_state = active});
|
||||||
|
session_established({xmlstreamelement,
|
||||||
|
#xmlel{name = <<"inactive">>,
|
||||||
|
attrs = [{<<"xmlns">>, ?NS_CLIENT_STATE}]}},
|
||||||
|
StateData) ->
|
||||||
|
fsm_next_state(session_established, StateData#state{csi_state = inactive});
|
||||||
session_established({xmlstreamelement, El},
|
session_established({xmlstreamelement, El},
|
||||||
StateData) ->
|
StateData) ->
|
||||||
FromJID = StateData#state.jid,
|
FromJID = StateData#state.jid,
|
||||||
@ -1855,6 +1873,8 @@ send_element(StateData, El) when StateData#state.xml_socket ->
|
|||||||
send_element(StateData, El) ->
|
send_element(StateData, El) ->
|
||||||
send_text(StateData, xml:element_to_binary(El)).
|
send_text(StateData, xml:element_to_binary(El)).
|
||||||
|
|
||||||
|
send_stanza(StateData, Stanza) when StateData#state.csi_state == inactive ->
|
||||||
|
csi_filter_stanza(StateData, Stanza);
|
||||||
send_stanza(StateData, Stanza) when StateData#state.mgmt_state == pending ->
|
send_stanza(StateData, Stanza) when StateData#state.mgmt_state == pending ->
|
||||||
mgmt_queue_add(StateData, Stanza);
|
mgmt_queue_add(StateData, Stanza);
|
||||||
send_stanza(StateData, Stanza) when StateData#state.mgmt_state == active ->
|
send_stanza(StateData, Stanza) when StateData#state.mgmt_state == active ->
|
||||||
@ -1869,18 +1889,14 @@ send_stanza(StateData, Stanza) ->
|
|||||||
send_element(StateData, Stanza),
|
send_element(StateData, Stanza),
|
||||||
StateData.
|
StateData.
|
||||||
|
|
||||||
send_packet(StateData, Packet) when StateData#state.mgmt_state == active;
|
send_packet(StateData, Packet) ->
|
||||||
StateData#state.mgmt_state == pending ->
|
|
||||||
case is_stanza(Packet) of
|
case is_stanza(Packet) of
|
||||||
true ->
|
true ->
|
||||||
send_stanza(StateData, Packet);
|
send_stanza(StateData, Packet);
|
||||||
false ->
|
false ->
|
||||||
send_element(StateData, Packet),
|
send_element(StateData, Packet),
|
||||||
StateData
|
StateData
|
||||||
end;
|
end.
|
||||||
send_packet(StateData, Stanza) ->
|
|
||||||
send_element(StateData, Stanza),
|
|
||||||
StateData.
|
|
||||||
|
|
||||||
send_header(StateData, Server, Version, Lang)
|
send_header(StateData, Server, Version, Lang)
|
||||||
when StateData#state.xml_socket ->
|
when StateData#state.xml_socket ->
|
||||||
@ -2762,9 +2778,11 @@ handle_resume(StateData, Attrs) ->
|
|||||||
#xmlel{name = <<"r">>,
|
#xmlel{name = <<"r">>,
|
||||||
attrs = [{<<"xmlns">>, AttrXmlns}],
|
attrs = [{<<"xmlns">>, AttrXmlns}],
|
||||||
children = []}),
|
children = []}),
|
||||||
|
FlushedState = csi_queue_flush(NewState),
|
||||||
|
NewStateData = FlushedState#state{csi_state = active},
|
||||||
?INFO_MSG("Resumed session for ~s",
|
?INFO_MSG("Resumed session for ~s",
|
||||||
[jlib:jid_to_string(NewState#state.jid)]),
|
[jlib:jid_to_string(NewStateData#state.jid)]),
|
||||||
{ok, NewState};
|
{ok, NewStateData};
|
||||||
{error, El, Msg} ->
|
{error, El, Msg} ->
|
||||||
send_element(StateData, El),
|
send_element(StateData, El),
|
||||||
?INFO_MSG("Cannot resume session for ~s@~s: ~s",
|
?INFO_MSG("Cannot resume session for ~s@~s: ~s",
|
||||||
@ -2953,6 +2971,8 @@ inherit_session_state(#state{user = U, server = S} = StateData, ResumeID) ->
|
|||||||
pres_invis = OldStateData#state.pres_invis,
|
pres_invis = OldStateData#state.pres_invis,
|
||||||
privacy_list = OldStateData#state.privacy_list,
|
privacy_list = OldStateData#state.privacy_list,
|
||||||
aux_fields = OldStateData#state.aux_fields,
|
aux_fields = OldStateData#state.aux_fields,
|
||||||
|
csi_state = OldStateData#state.csi_state,
|
||||||
|
csi_queue = OldStateData#state.csi_queue,
|
||||||
mgmt_xmlns = OldStateData#state.mgmt_xmlns,
|
mgmt_xmlns = OldStateData#state.mgmt_xmlns,
|
||||||
mgmt_queue = OldStateData#state.mgmt_queue,
|
mgmt_queue = OldStateData#state.mgmt_queue,
|
||||||
mgmt_timeout = OldStateData#state.mgmt_timeout,
|
mgmt_timeout = OldStateData#state.mgmt_timeout,
|
||||||
@ -2976,6 +2996,71 @@ make_resume_id(StateData) ->
|
|||||||
{Time, _} = StateData#state.sid,
|
{Time, _} = StateData#state.sid,
|
||||||
jlib:term_to_base64({StateData#state.resource, Time}).
|
jlib:term_to_base64({StateData#state.resource, Time}).
|
||||||
|
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% XEP-0352
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
csi_filter_stanza(#state{csi_state = CsiState, jid = JID} = StateData,
|
||||||
|
Stanza) ->
|
||||||
|
Action = ejabberd_hooks:run_fold(csi_filter_stanza,
|
||||||
|
StateData#state.server,
|
||||||
|
send, [Stanza]),
|
||||||
|
?DEBUG("Going to ~p stanza for inactive client ~p",
|
||||||
|
[Action, jlib:jid_to_string(JID)]),
|
||||||
|
case Action of
|
||||||
|
queue -> csi_queue_add(StateData, Stanza);
|
||||||
|
drop -> StateData;
|
||||||
|
send ->
|
||||||
|
From = xml:get_tag_attr_s(<<"from">>, Stanza),
|
||||||
|
StateData1 = csi_queue_send(StateData, From),
|
||||||
|
StateData2 = send_stanza(StateData1#state{csi_state = active},
|
||||||
|
Stanza),
|
||||||
|
StateData2#state{csi_state = CsiState}
|
||||||
|
end.
|
||||||
|
|
||||||
|
csi_queue_add(#state{csi_queue = Queue, server = Host} = StateData,
|
||||||
|
#xmlel{children = Els} = Stanza) ->
|
||||||
|
From = xml:get_tag_attr_s(<<"from">>, Stanza),
|
||||||
|
Time = calendar:now_to_universal_time(os:timestamp()),
|
||||||
|
DelayTag = [jlib:timestamp_to_xml(Time, utc,
|
||||||
|
jlib:make_jid(<<"">>, Host, <<"">>),
|
||||||
|
<<"Client Inactive">>)],
|
||||||
|
NewStanza = Stanza#xmlel{children = Els ++ DelayTag},
|
||||||
|
case length(StateData#state.csi_queue) >= csi_max_queue(StateData) of
|
||||||
|
true -> csi_queue_add(csi_queue_flush(StateData), NewStanza);
|
||||||
|
false ->
|
||||||
|
NewQueue = lists:keystore(From, 1, Queue, {From, NewStanza}),
|
||||||
|
StateData#state{csi_queue = NewQueue}
|
||||||
|
end.
|
||||||
|
|
||||||
|
csi_queue_send(#state{csi_queue = Queue, csi_state = CsiState} = StateData,
|
||||||
|
From) ->
|
||||||
|
case lists:keytake(From, 1, Queue) of
|
||||||
|
{value, {From, Stanza}, NewQueue} ->
|
||||||
|
NewStateData = send_stanza(StateData#state{csi_state = active},
|
||||||
|
Stanza),
|
||||||
|
NewStateData#state{csi_queue = NewQueue, csi_state = CsiState};
|
||||||
|
false -> StateData
|
||||||
|
end.
|
||||||
|
|
||||||
|
csi_queue_flush(#state{csi_queue = Queue, csi_state = CsiState, jid = JID} =
|
||||||
|
StateData) ->
|
||||||
|
?DEBUG("Flushing CSI queue for ~s", [jlib:jid_to_string(JID)]),
|
||||||
|
NewStateData =
|
||||||
|
lists:foldl(fun({_From, Stanza}, AccState) ->
|
||||||
|
send_stanza(AccState, Stanza)
|
||||||
|
end, StateData#state{csi_state = active}, Queue),
|
||||||
|
NewStateData#state{csi_queue = [], csi_state = CsiState}.
|
||||||
|
|
||||||
|
%% Make sure we won't push too many messages to the XEP-0198 queue when the
|
||||||
|
%% client becomes 'active' again. Otherwise, the client might not manage to
|
||||||
|
%% acknowledge the message flood in time. Also, don't let the queue grow to
|
||||||
|
%% more than 100 stanzas.
|
||||||
|
csi_max_queue(#state{mgmt_max_queue = infinity}) -> 100;
|
||||||
|
csi_max_queue(#state{mgmt_max_queue = Max}) when Max > 200 -> 100;
|
||||||
|
csi_max_queue(#state{mgmt_max_queue = Max}) when Max < 2 -> 1;
|
||||||
|
csi_max_queue(#state{mgmt_max_queue = Max}) -> Max div 2.
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% JID Set memory footprint reduction code
|
%%% JID Set memory footprint reduction code
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
91
src/mod_client_state.erl
Normal file
91
src/mod_client_state.erl
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% File : mod_client_state.erl
|
||||||
|
%%% Author : Holger Weiss
|
||||||
|
%%% Purpose : Filter stanzas sent to inactive clients (XEP-0352)
|
||||||
|
%%% Created : 11 Sep 2014 by Holger Weiss
|
||||||
|
%%%
|
||||||
|
%%%
|
||||||
|
%%% ejabberd, Copyright (C) 2014 ProcessOne
|
||||||
|
%%%
|
||||||
|
%%% This program is free software; you can redistribute it and/or
|
||||||
|
%%% modify it under the terms of the GNU General Public License as
|
||||||
|
%%% published by the Free Software Foundation; either version 2 of the
|
||||||
|
%%% License, or (at your option) any later version.
|
||||||
|
%%%
|
||||||
|
%%% This program is distributed in the hope that it will be useful,
|
||||||
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
%%% General Public License for more details.
|
||||||
|
%%%
|
||||||
|
%%% You should have received a copy of the GNU General Public License along
|
||||||
|
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
%%%
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(mod_client_state).
|
||||||
|
-author('holger@zedat.fu-berlin.de').
|
||||||
|
|
||||||
|
-behavior(gen_mod).
|
||||||
|
|
||||||
|
-export([start/2, stop/1, filter_presence/2, filter_chat_states/2]).
|
||||||
|
|
||||||
|
-include("ejabberd.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
-include("jlib.hrl").
|
||||||
|
|
||||||
|
start(Host, Opts) ->
|
||||||
|
QueuePresence = gen_mod:get_opt(queue_presence, Opts,
|
||||||
|
fun(true) -> true end, false),
|
||||||
|
DropChatStates = gen_mod:get_opt(drop_chat_states, Opts,
|
||||||
|
fun(true) -> true end, false),
|
||||||
|
if QueuePresence ->
|
||||||
|
ejabberd_hooks:add(csi_filter_stanza, Host, ?MODULE,
|
||||||
|
filter_presence, 50);
|
||||||
|
true -> ok
|
||||||
|
end,
|
||||||
|
if DropChatStates ->
|
||||||
|
ejabberd_hooks:add(csi_filter_stanza, Host, ?MODULE,
|
||||||
|
filter_chat_states, 50);
|
||||||
|
true -> ok
|
||||||
|
end,
|
||||||
|
ok.
|
||||||
|
|
||||||
|
stop(Host) ->
|
||||||
|
ejabberd_hooks:delete(csi_filter_stanza, Host, ?MODULE,
|
||||||
|
filter_presence, 50),
|
||||||
|
ejabberd_hooks:delete(csi_filter_stanza, Host, ?MODULE,
|
||||||
|
filter_chat_states, 50),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
filter_presence(_Action, #xmlel{name = <<"presence">>, attrs = Attrs}) ->
|
||||||
|
case xml:get_attr(<<"type">>, Attrs) of
|
||||||
|
{value, Type} when Type /= <<"unavailable">> ->
|
||||||
|
?DEBUG("Got important presence stanza", []),
|
||||||
|
{stop, send};
|
||||||
|
_ ->
|
||||||
|
?DEBUG("Got availability presence stanza", []),
|
||||||
|
{stop, queue}
|
||||||
|
end;
|
||||||
|
filter_presence(Action, _Stanza) -> Action.
|
||||||
|
|
||||||
|
filter_chat_states(_Action, #xmlel{name = <<"message">>} = Stanza) ->
|
||||||
|
%% All XEP-0085 chat states except for <gone/>:
|
||||||
|
ChatStates = [<<"active">>, <<"inactive">>, <<"composing">>, <<"paused">>],
|
||||||
|
Stripped =
|
||||||
|
lists:foldl(fun(ChatState, AccStanza) ->
|
||||||
|
xml:remove_subtags(AccStanza, ChatState,
|
||||||
|
{<<"xmlns">>, ?NS_CHATSTATES})
|
||||||
|
end, Stanza, ChatStates),
|
||||||
|
case Stripped of
|
||||||
|
#xmlel{children = [#xmlel{name = <<"thread">>}]} ->
|
||||||
|
?DEBUG("Got standalone chat state notification", []),
|
||||||
|
{stop, drop};
|
||||||
|
#xmlel{children = []} ->
|
||||||
|
?DEBUG("Got standalone chat state notification", []),
|
||||||
|
{stop, drop};
|
||||||
|
_ ->
|
||||||
|
?DEBUG("Got message with chat state notification", []),
|
||||||
|
{stop, send}
|
||||||
|
end;
|
||||||
|
filter_chat_states(Action, _Stanza) -> Action.
|
@ -237,6 +237,8 @@ db_tests(mnesia) ->
|
|||||||
[offline_master, offline_slave]},
|
[offline_master, offline_slave]},
|
||||||
{test_carbons, [parallel],
|
{test_carbons, [parallel],
|
||||||
[carbons_master, carbons_slave]},
|
[carbons_master, carbons_slave]},
|
||||||
|
{test_client_state, [parallel],
|
||||||
|
[client_state_master, client_state_slave]},
|
||||||
{test_muc, [parallel],
|
{test_muc, [parallel],
|
||||||
[muc_master, muc_slave]},
|
[muc_master, muc_slave]},
|
||||||
{test_announce, [sequence],
|
{test_announce, [sequence],
|
||||||
@ -1505,6 +1507,42 @@ carbons_slave(Config) ->
|
|||||||
#presence{from = Peer, type = unavailable} = recv(),
|
#presence{from = Peer, type = unavailable} = recv(),
|
||||||
disconnect(Config).
|
disconnect(Config).
|
||||||
|
|
||||||
|
client_state_master(Config) ->
|
||||||
|
Peer = ?config(slave, Config),
|
||||||
|
Presence = #presence{to = Peer},
|
||||||
|
Message = #message{to = Peer, thread = <<"1">>,
|
||||||
|
sub_els = [#chatstate_active{}]},
|
||||||
|
wait_for_slave(Config),
|
||||||
|
%% Should be queued (but see below):
|
||||||
|
send(Config, Presence),
|
||||||
|
%% Should be sent immediately, together with the previous presence:
|
||||||
|
send(Config, Message#message{body = [#text{data = <<"body">>}]}),
|
||||||
|
%% Should be dropped:
|
||||||
|
send(Config, Message),
|
||||||
|
%% Should be queued (but see below):
|
||||||
|
send(Config, Presence),
|
||||||
|
%% Should replace the previous presence in the queue:
|
||||||
|
send(Config, Presence#presence{type = unavailable}),
|
||||||
|
wait_for_slave(Config),
|
||||||
|
%% Should be sent immediately, as the client is active again.
|
||||||
|
send(Config, Message),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
|
client_state_slave(Config) ->
|
||||||
|
true = ?config(csi, Config),
|
||||||
|
Peer = ?config(master, Config),
|
||||||
|
send(Config, #csi_inactive{}),
|
||||||
|
wait_for_master(Config),
|
||||||
|
#presence{from = Peer, sub_els = [#delay{}]} = recv(),
|
||||||
|
#message{from = Peer, thread = <<"1">>, sub_els = [#chatstate_active{}],
|
||||||
|
body = [#text{data = <<"body">>}]} = recv(),
|
||||||
|
wait_for_master(Config),
|
||||||
|
send(Config, #csi_active{}),
|
||||||
|
?recv2(#presence{from = Peer, type = unavailable, sub_els = [#delay{}]},
|
||||||
|
#message{from = Peer, thread = <<"1">>,
|
||||||
|
sub_els = [#chatstate_active{}]}),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Aux functions
|
%%% Aux functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
@ -143,6 +143,9 @@ Welcome to this XMPP server."
|
|||||||
db_type: internal
|
db_type: internal
|
||||||
mod_carboncopy:
|
mod_carboncopy:
|
||||||
db_type: internal
|
db_type: internal
|
||||||
|
mod_client_state:
|
||||||
|
drop_chat_states: true
|
||||||
|
queue_presence: true
|
||||||
mod_adhoc: []
|
mod_adhoc: []
|
||||||
mod_configure: []
|
mod_configure: []
|
||||||
mod_disco: []
|
mod_disco: []
|
||||||
|
@ -155,6 +155,8 @@ wait_auth_SASL_result(Config) ->
|
|||||||
lists:foldl(
|
lists:foldl(
|
||||||
fun(#feature_sm{}, ConfigAcc) ->
|
fun(#feature_sm{}, ConfigAcc) ->
|
||||||
set_opt(sm, true, ConfigAcc);
|
set_opt(sm, true, ConfigAcc);
|
||||||
|
(#feature_csi{}, ConfigAcc) ->
|
||||||
|
set_opt(csi, true, ConfigAcc);
|
||||||
(_, ConfigAcc) ->
|
(_, ConfigAcc) ->
|
||||||
ConfigAcc
|
ConfigAcc
|
||||||
end, Config, Fs);
|
end, Config, Fs);
|
||||||
|
@ -47,6 +47,14 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
|
|||||||
decode_feature_sm(<<"urn:xmpp:sm:2">>, IgnoreEls, _el);
|
decode_feature_sm(<<"urn:xmpp:sm:2">>, IgnoreEls, _el);
|
||||||
{<<"sm">>, <<"urn:xmpp:sm:3">>} ->
|
{<<"sm">>, <<"urn:xmpp:sm:3">>} ->
|
||||||
decode_feature_sm(<<"urn:xmpp:sm:3">>, IgnoreEls, _el);
|
decode_feature_sm(<<"urn:xmpp:sm:3">>, IgnoreEls, _el);
|
||||||
|
{<<"inactive">>, <<"urn:xmpp:csi:0">>} ->
|
||||||
|
decode_csi_inactive(<<"urn:xmpp:csi:0">>, IgnoreEls,
|
||||||
|
_el);
|
||||||
|
{<<"active">>, <<"urn:xmpp:csi:0">>} ->
|
||||||
|
decode_csi_active(<<"urn:xmpp:csi:0">>, IgnoreEls, _el);
|
||||||
|
{<<"csi">>, <<"urn:xmpp:csi:0">>} ->
|
||||||
|
decode_feature_csi(<<"urn:xmpp:csi:0">>, IgnoreEls,
|
||||||
|
_el);
|
||||||
{<<"sent">>, <<"urn:xmpp:carbons:2">>} ->
|
{<<"sent">>, <<"urn:xmpp:carbons:2">>} ->
|
||||||
decode_carbons_sent(<<"urn:xmpp:carbons:2">>, IgnoreEls,
|
decode_carbons_sent(<<"urn:xmpp:carbons:2">>, IgnoreEls,
|
||||||
_el);
|
_el);
|
||||||
@ -163,6 +171,26 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
|
|||||||
_el);
|
_el);
|
||||||
{<<"delay">>, <<"urn:xmpp:delay">>} ->
|
{<<"delay">>, <<"urn:xmpp:delay">>} ->
|
||||||
decode_delay(<<"urn:xmpp:delay">>, IgnoreEls, _el);
|
decode_delay(<<"urn:xmpp:delay">>, IgnoreEls, _el);
|
||||||
|
{<<"paused">>,
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>} ->
|
||||||
|
decode_chatstate_paused(<<"http://jabber.org/protocol/chatstates">>,
|
||||||
|
IgnoreEls, _el);
|
||||||
|
{<<"inactive">>,
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>} ->
|
||||||
|
decode_chatstate_inactive(<<"http://jabber.org/protocol/chatstates">>,
|
||||||
|
IgnoreEls, _el);
|
||||||
|
{<<"gone">>,
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>} ->
|
||||||
|
decode_chatstate_gone(<<"http://jabber.org/protocol/chatstates">>,
|
||||||
|
IgnoreEls, _el);
|
||||||
|
{<<"composing">>,
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>} ->
|
||||||
|
decode_chatstate_composing(<<"http://jabber.org/protocol/chatstates">>,
|
||||||
|
IgnoreEls, _el);
|
||||||
|
{<<"active">>,
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>} ->
|
||||||
|
decode_chatstate_active(<<"http://jabber.org/protocol/chatstates">>,
|
||||||
|
IgnoreEls, _el);
|
||||||
{<<"headers">>,
|
{<<"headers">>,
|
||||||
<<"http://jabber.org/protocol/shim">>} ->
|
<<"http://jabber.org/protocol/shim">>} ->
|
||||||
decode_shim_headers(<<"http://jabber.org/protocol/shim">>,
|
decode_shim_headers(<<"http://jabber.org/protocol/shim">>,
|
||||||
@ -998,6 +1026,9 @@ is_known_tag({xmlel, _name, _attrs, _} = _el) ->
|
|||||||
{<<"enable">>, <<"urn:xmpp:sm:3">>} -> true;
|
{<<"enable">>, <<"urn:xmpp:sm:3">>} -> true;
|
||||||
{<<"sm">>, <<"urn:xmpp:sm:2">>} -> true;
|
{<<"sm">>, <<"urn:xmpp:sm:2">>} -> true;
|
||||||
{<<"sm">>, <<"urn:xmpp:sm:3">>} -> true;
|
{<<"sm">>, <<"urn:xmpp:sm:3">>} -> true;
|
||||||
|
{<<"inactive">>, <<"urn:xmpp:csi:0">>} -> true;
|
||||||
|
{<<"active">>, <<"urn:xmpp:csi:0">>} -> true;
|
||||||
|
{<<"csi">>, <<"urn:xmpp:csi:0">>} -> true;
|
||||||
{<<"sent">>, <<"urn:xmpp:carbons:2">>} -> true;
|
{<<"sent">>, <<"urn:xmpp:carbons:2">>} -> true;
|
||||||
{<<"received">>, <<"urn:xmpp:carbons:2">>} -> true;
|
{<<"received">>, <<"urn:xmpp:carbons:2">>} -> true;
|
||||||
{<<"private">>, <<"urn:xmpp:carbons:2">>} -> true;
|
{<<"private">>, <<"urn:xmpp:carbons:2">>} -> true;
|
||||||
@ -1074,6 +1105,21 @@ is_known_tag({xmlel, _name, _attrs, _} = _el) ->
|
|||||||
true;
|
true;
|
||||||
{<<"x">>, <<"jabber:x:delay">>} -> true;
|
{<<"x">>, <<"jabber:x:delay">>} -> true;
|
||||||
{<<"delay">>, <<"urn:xmpp:delay">>} -> true;
|
{<<"delay">>, <<"urn:xmpp:delay">>} -> true;
|
||||||
|
{<<"paused">>,
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>} ->
|
||||||
|
true;
|
||||||
|
{<<"inactive">>,
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>} ->
|
||||||
|
true;
|
||||||
|
{<<"gone">>,
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>} ->
|
||||||
|
true;
|
||||||
|
{<<"composing">>,
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>} ->
|
||||||
|
true;
|
||||||
|
{<<"active">>,
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>} ->
|
||||||
|
true;
|
||||||
{<<"headers">>,
|
{<<"headers">>,
|
||||||
<<"http://jabber.org/protocol/shim">>} ->
|
<<"http://jabber.org/protocol/shim">>} ->
|
||||||
true;
|
true;
|
||||||
@ -1856,6 +1902,26 @@ encode({pubsub, _, _, _, _, _, _, _, _} = Pubsub) ->
|
|||||||
encode({shim, _} = Headers) ->
|
encode({shim, _} = Headers) ->
|
||||||
encode_shim_headers(Headers,
|
encode_shim_headers(Headers,
|
||||||
[{<<"xmlns">>, <<"http://jabber.org/protocol/shim">>}]);
|
[{<<"xmlns">>, <<"http://jabber.org/protocol/shim">>}]);
|
||||||
|
encode({chatstate_active} = Active) ->
|
||||||
|
encode_chatstate_active(Active,
|
||||||
|
[{<<"xmlns">>,
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>}]);
|
||||||
|
encode({chatstate_composing} = Composing) ->
|
||||||
|
encode_chatstate_composing(Composing,
|
||||||
|
[{<<"xmlns">>,
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>}]);
|
||||||
|
encode({chatstate_gone} = Gone) ->
|
||||||
|
encode_chatstate_gone(Gone,
|
||||||
|
[{<<"xmlns">>,
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>}]);
|
||||||
|
encode({chatstate_inactive} = Inactive) ->
|
||||||
|
encode_chatstate_inactive(Inactive,
|
||||||
|
[{<<"xmlns">>,
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>}]);
|
||||||
|
encode({chatstate_paused} = Paused) ->
|
||||||
|
encode_chatstate_paused(Paused,
|
||||||
|
[{<<"xmlns">>,
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>}]);
|
||||||
encode({delay, _, _} = Delay) ->
|
encode({delay, _, _} = Delay) ->
|
||||||
encode_delay(Delay,
|
encode_delay(Delay,
|
||||||
[{<<"xmlns">>, <<"urn:xmpp:delay">>}]);
|
[{<<"xmlns">>, <<"urn:xmpp:delay">>}]);
|
||||||
@ -1926,6 +1992,14 @@ encode({carbons_received, _} = Received) ->
|
|||||||
encode({carbons_sent, _} = Sent) ->
|
encode({carbons_sent, _} = Sent) ->
|
||||||
encode_carbons_sent(Sent,
|
encode_carbons_sent(Sent,
|
||||||
[{<<"xmlns">>, <<"urn:xmpp:carbons:2">>}]);
|
[{<<"xmlns">>, <<"urn:xmpp:carbons:2">>}]);
|
||||||
|
encode({feature_csi, _} = Csi) ->
|
||||||
|
encode_feature_csi(Csi, []);
|
||||||
|
encode({csi_active} = Active) ->
|
||||||
|
encode_csi_active(Active,
|
||||||
|
[{<<"xmlns">>, <<"urn:xmpp:csi:0">>}]);
|
||||||
|
encode({csi_inactive} = Inactive) ->
|
||||||
|
encode_csi_inactive(Inactive,
|
||||||
|
[{<<"xmlns">>, <<"urn:xmpp:csi:0">>}]);
|
||||||
encode({feature_sm, _} = Sm) ->
|
encode({feature_sm, _} = Sm) ->
|
||||||
encode_feature_sm(Sm, []);
|
encode_feature_sm(Sm, []);
|
||||||
encode({sm_enable, _, _, _} = Enable) ->
|
encode({sm_enable, _, _, _} = Enable) ->
|
||||||
@ -2084,6 +2158,16 @@ get_ns({pubsub, _, _, _, _, _, _, _, _}) ->
|
|||||||
<<"http://jabber.org/protocol/pubsub">>;
|
<<"http://jabber.org/protocol/pubsub">>;
|
||||||
get_ns({shim, _}) ->
|
get_ns({shim, _}) ->
|
||||||
<<"http://jabber.org/protocol/shim">>;
|
<<"http://jabber.org/protocol/shim">>;
|
||||||
|
get_ns({chatstate_active}) ->
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>;
|
||||||
|
get_ns({chatstate_composing}) ->
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>;
|
||||||
|
get_ns({chatstate_gone}) ->
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>;
|
||||||
|
get_ns({chatstate_inactive}) ->
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>;
|
||||||
|
get_ns({chatstate_paused}) ->
|
||||||
|
<<"http://jabber.org/protocol/chatstates">>;
|
||||||
get_ns({delay, _, _}) -> <<"urn:xmpp:delay">>;
|
get_ns({delay, _, _}) -> <<"urn:xmpp:delay">>;
|
||||||
get_ns({legacy_delay, _, _}) -> <<"jabber:x:delay">>;
|
get_ns({legacy_delay, _, _}) -> <<"jabber:x:delay">>;
|
||||||
get_ns({streamhost, _, _, _}) ->
|
get_ns({streamhost, _, _, _}) ->
|
||||||
@ -2115,6 +2199,9 @@ get_ns({carbons_private}) -> <<"urn:xmpp:carbons:2">>;
|
|||||||
get_ns({carbons_received, _}) ->
|
get_ns({carbons_received, _}) ->
|
||||||
<<"urn:xmpp:carbons:2">>;
|
<<"urn:xmpp:carbons:2">>;
|
||||||
get_ns({carbons_sent, _}) -> <<"urn:xmpp:carbons:2">>;
|
get_ns({carbons_sent, _}) -> <<"urn:xmpp:carbons:2">>;
|
||||||
|
get_ns({feature_csi, _}) -> <<"urn:xmpp:csi:0">>;
|
||||||
|
get_ns({csi_active}) -> <<"urn:xmpp:csi:0">>;
|
||||||
|
get_ns({csi_inactive}) -> <<"urn:xmpp:csi:0">>;
|
||||||
get_ns(_) -> <<>>.
|
get_ns(_) -> <<>>.
|
||||||
|
|
||||||
dec_int(Val) -> dec_int(Val, infinity, infinity).
|
dec_int(Val) -> dec_int(Val, infinity, infinity).
|
||||||
@ -2272,6 +2359,11 @@ pp(pubsub, 8) ->
|
|||||||
[subscriptions, affiliations, publish, subscribe,
|
[subscriptions, affiliations, publish, subscribe,
|
||||||
unsubscribe, options, items, retract];
|
unsubscribe, options, items, retract];
|
||||||
pp(shim, 1) -> [headers];
|
pp(shim, 1) -> [headers];
|
||||||
|
pp(chatstate_active, 0) -> [];
|
||||||
|
pp(chatstate_composing, 0) -> [];
|
||||||
|
pp(chatstate_gone, 0) -> [];
|
||||||
|
pp(chatstate_inactive, 0) -> [];
|
||||||
|
pp(chatstate_paused, 0) -> [];
|
||||||
pp(delay, 2) -> [stamp, from];
|
pp(delay, 2) -> [stamp, from];
|
||||||
pp(legacy_delay, 2) -> [stamp, from];
|
pp(legacy_delay, 2) -> [stamp, from];
|
||||||
pp(streamhost, 3) -> [jid, host, port];
|
pp(streamhost, 3) -> [jid, host, port];
|
||||||
@ -2298,6 +2390,9 @@ pp(carbons_enable, 0) -> [];
|
|||||||
pp(carbons_private, 0) -> [];
|
pp(carbons_private, 0) -> [];
|
||||||
pp(carbons_received, 1) -> [forwarded];
|
pp(carbons_received, 1) -> [forwarded];
|
||||||
pp(carbons_sent, 1) -> [forwarded];
|
pp(carbons_sent, 1) -> [forwarded];
|
||||||
|
pp(feature_csi, 1) -> [xmlns];
|
||||||
|
pp(csi_active, 0) -> [];
|
||||||
|
pp(csi_inactive, 0) -> [];
|
||||||
pp(feature_sm, 1) -> [xmlns];
|
pp(feature_sm, 1) -> [xmlns];
|
||||||
pp(sm_enable, 3) -> [max, resume, xmlns];
|
pp(sm_enable, 3) -> [max, resume, xmlns];
|
||||||
pp(sm_enabled, 5) -> [id, location, max, resume, xmlns];
|
pp(sm_enabled, 5) -> [id, location, max, resume, xmlns];
|
||||||
@ -3268,6 +3363,54 @@ encode_feature_sm_attr_xmlns(undefined, _acc) -> _acc;
|
|||||||
encode_feature_sm_attr_xmlns(_val, _acc) ->
|
encode_feature_sm_attr_xmlns(_val, _acc) ->
|
||||||
[{<<"xmlns">>, _val} | _acc].
|
[{<<"xmlns">>, _val} | _acc].
|
||||||
|
|
||||||
|
decode_csi_inactive(__TopXMLNS, __IgnoreEls,
|
||||||
|
{xmlel, <<"inactive">>, _attrs, _els}) ->
|
||||||
|
{csi_inactive}.
|
||||||
|
|
||||||
|
encode_csi_inactive({csi_inactive}, _xmlns_attrs) ->
|
||||||
|
_els = [],
|
||||||
|
_attrs = _xmlns_attrs,
|
||||||
|
{xmlel, <<"inactive">>, _attrs, _els}.
|
||||||
|
|
||||||
|
decode_csi_active(__TopXMLNS, __IgnoreEls,
|
||||||
|
{xmlel, <<"active">>, _attrs, _els}) ->
|
||||||
|
{csi_active}.
|
||||||
|
|
||||||
|
encode_csi_active({csi_active}, _xmlns_attrs) ->
|
||||||
|
_els = [],
|
||||||
|
_attrs = _xmlns_attrs,
|
||||||
|
{xmlel, <<"active">>, _attrs, _els}.
|
||||||
|
|
||||||
|
decode_feature_csi(__TopXMLNS, __IgnoreEls,
|
||||||
|
{xmlel, <<"csi">>, _attrs, _els}) ->
|
||||||
|
Xmlns = decode_feature_csi_attrs(__TopXMLNS, _attrs,
|
||||||
|
undefined),
|
||||||
|
{feature_csi, Xmlns}.
|
||||||
|
|
||||||
|
decode_feature_csi_attrs(__TopXMLNS,
|
||||||
|
[{<<"xmlns">>, _val} | _attrs], _Xmlns) ->
|
||||||
|
decode_feature_csi_attrs(__TopXMLNS, _attrs, _val);
|
||||||
|
decode_feature_csi_attrs(__TopXMLNS, [_ | _attrs],
|
||||||
|
Xmlns) ->
|
||||||
|
decode_feature_csi_attrs(__TopXMLNS, _attrs, Xmlns);
|
||||||
|
decode_feature_csi_attrs(__TopXMLNS, [], Xmlns) ->
|
||||||
|
decode_feature_csi_attr_xmlns(__TopXMLNS, Xmlns).
|
||||||
|
|
||||||
|
encode_feature_csi({feature_csi, Xmlns},
|
||||||
|
_xmlns_attrs) ->
|
||||||
|
_els = [],
|
||||||
|
_attrs = encode_feature_csi_attr_xmlns(Xmlns,
|
||||||
|
_xmlns_attrs),
|
||||||
|
{xmlel, <<"csi">>, _attrs, _els}.
|
||||||
|
|
||||||
|
decode_feature_csi_attr_xmlns(__TopXMLNS, undefined) ->
|
||||||
|
undefined;
|
||||||
|
decode_feature_csi_attr_xmlns(__TopXMLNS, _val) -> _val.
|
||||||
|
|
||||||
|
encode_feature_csi_attr_xmlns(undefined, _acc) -> _acc;
|
||||||
|
encode_feature_csi_attr_xmlns(_val, _acc) ->
|
||||||
|
[{<<"xmlns">>, _val} | _acc].
|
||||||
|
|
||||||
decode_carbons_sent(__TopXMLNS, __IgnoreEls,
|
decode_carbons_sent(__TopXMLNS, __IgnoreEls,
|
||||||
{xmlel, <<"sent">>, _attrs, _els}) ->
|
{xmlel, <<"sent">>, _attrs, _els}) ->
|
||||||
Forwarded = decode_carbons_sent_els(__TopXMLNS,
|
Forwarded = decode_carbons_sent_els(__TopXMLNS,
|
||||||
@ -5355,6 +5498,55 @@ encode_delay_attr_from(undefined, _acc) -> _acc;
|
|||||||
encode_delay_attr_from(_val, _acc) ->
|
encode_delay_attr_from(_val, _acc) ->
|
||||||
[{<<"from">>, enc_jid(_val)} | _acc].
|
[{<<"from">>, enc_jid(_val)} | _acc].
|
||||||
|
|
||||||
|
decode_chatstate_paused(__TopXMLNS, __IgnoreEls,
|
||||||
|
{xmlel, <<"paused">>, _attrs, _els}) ->
|
||||||
|
{chatstate_paused}.
|
||||||
|
|
||||||
|
encode_chatstate_paused({chatstate_paused},
|
||||||
|
_xmlns_attrs) ->
|
||||||
|
_els = [],
|
||||||
|
_attrs = _xmlns_attrs,
|
||||||
|
{xmlel, <<"paused">>, _attrs, _els}.
|
||||||
|
|
||||||
|
decode_chatstate_inactive(__TopXMLNS, __IgnoreEls,
|
||||||
|
{xmlel, <<"inactive">>, _attrs, _els}) ->
|
||||||
|
{chatstate_inactive}.
|
||||||
|
|
||||||
|
encode_chatstate_inactive({chatstate_inactive},
|
||||||
|
_xmlns_attrs) ->
|
||||||
|
_els = [],
|
||||||
|
_attrs = _xmlns_attrs,
|
||||||
|
{xmlel, <<"inactive">>, _attrs, _els}.
|
||||||
|
|
||||||
|
decode_chatstate_gone(__TopXMLNS, __IgnoreEls,
|
||||||
|
{xmlel, <<"gone">>, _attrs, _els}) ->
|
||||||
|
{chatstate_gone}.
|
||||||
|
|
||||||
|
encode_chatstate_gone({chatstate_gone}, _xmlns_attrs) ->
|
||||||
|
_els = [],
|
||||||
|
_attrs = _xmlns_attrs,
|
||||||
|
{xmlel, <<"gone">>, _attrs, _els}.
|
||||||
|
|
||||||
|
decode_chatstate_composing(__TopXMLNS, __IgnoreEls,
|
||||||
|
{xmlel, <<"composing">>, _attrs, _els}) ->
|
||||||
|
{chatstate_composing}.
|
||||||
|
|
||||||
|
encode_chatstate_composing({chatstate_composing},
|
||||||
|
_xmlns_attrs) ->
|
||||||
|
_els = [],
|
||||||
|
_attrs = _xmlns_attrs,
|
||||||
|
{xmlel, <<"composing">>, _attrs, _els}.
|
||||||
|
|
||||||
|
decode_chatstate_active(__TopXMLNS, __IgnoreEls,
|
||||||
|
{xmlel, <<"active">>, _attrs, _els}) ->
|
||||||
|
{chatstate_active}.
|
||||||
|
|
||||||
|
encode_chatstate_active({chatstate_active},
|
||||||
|
_xmlns_attrs) ->
|
||||||
|
_els = [],
|
||||||
|
_attrs = _xmlns_attrs,
|
||||||
|
{xmlel, <<"active">>, _attrs, _els}.
|
||||||
|
|
||||||
decode_shim_headers(__TopXMLNS, __IgnoreEls,
|
decode_shim_headers(__TopXMLNS, __IgnoreEls,
|
||||||
{xmlel, <<"headers">>, _attrs, _els}) ->
|
{xmlel, <<"headers">>, _attrs, _els}) ->
|
||||||
Headers = decode_shim_headers_els(__TopXMLNS,
|
Headers = decode_shim_headers_els(__TopXMLNS,
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
-record(text, {lang :: binary(),
|
-record(text, {lang :: binary(),
|
||||||
data :: binary()}).
|
data :: binary()}).
|
||||||
|
|
||||||
|
-record(chatstate_paused, {}).
|
||||||
|
|
||||||
-record(streamhost, {jid :: any(),
|
-record(streamhost, {jid :: any(),
|
||||||
host :: binary(),
|
host :: binary(),
|
||||||
port = 1080 :: non_neg_integer()}).
|
port = 1080 :: non_neg_integer()}).
|
||||||
@ -24,6 +26,8 @@
|
|||||||
jid :: any(),
|
jid :: any(),
|
||||||
subid :: binary()}).
|
subid :: binary()}).
|
||||||
|
|
||||||
|
-record(csi_inactive, {}).
|
||||||
|
|
||||||
-record(ping, {}).
|
-record(ping, {}).
|
||||||
|
|
||||||
-record(delay, {stamp :: any(),
|
-record(delay, {stamp :: any(),
|
||||||
@ -57,6 +61,8 @@
|
|||||||
resume = false :: any(),
|
resume = false :: any(),
|
||||||
xmlns :: binary()}).
|
xmlns :: binary()}).
|
||||||
|
|
||||||
|
-record(chatstate_gone, {}).
|
||||||
|
|
||||||
-record(starttls_failure, {}).
|
-record(starttls_failure, {}).
|
||||||
|
|
||||||
-record(sasl_challenge, {text :: any()}).
|
-record(sasl_challenge, {text :: any()}).
|
||||||
@ -123,14 +129,20 @@
|
|||||||
|
|
||||||
-record(sasl_response, {text :: any()}).
|
-record(sasl_response, {text :: any()}).
|
||||||
|
|
||||||
|
-record(chatstate_inactive, {}).
|
||||||
|
|
||||||
-record(pubsub_subscribe, {node :: binary(),
|
-record(pubsub_subscribe, {node :: binary(),
|
||||||
jid :: any()}).
|
jid :: any()}).
|
||||||
|
|
||||||
|
-record(chatstate_composing, {}).
|
||||||
|
|
||||||
-record(sasl_auth, {mechanism :: binary(),
|
-record(sasl_auth, {mechanism :: binary(),
|
||||||
text :: any()}).
|
text :: any()}).
|
||||||
|
|
||||||
-record(p1_push, {}).
|
-record(p1_push, {}).
|
||||||
|
|
||||||
|
-record(feature_csi, {xmlns :: binary()}).
|
||||||
|
|
||||||
-record(legacy_delay, {stamp :: binary(),
|
-record(legacy_delay, {stamp :: binary(),
|
||||||
from :: any()}).
|
from :: any()}).
|
||||||
|
|
||||||
@ -220,6 +232,8 @@
|
|||||||
|
|
||||||
-record(block_list, {}).
|
-record(block_list, {}).
|
||||||
|
|
||||||
|
-record(csi_active, {}).
|
||||||
|
|
||||||
-record(xdata_field, {label :: binary(),
|
-record(xdata_field, {label :: binary(),
|
||||||
type :: 'boolean' | 'fixed' | 'hidden' | 'jid-multi' | 'jid-single' | 'list-multi' | 'list-single' | 'text-multi' | 'text-private' | 'text-single',
|
type :: 'boolean' | 'fixed' | 'hidden' | 'jid-multi' | 'jid-single' | 'list-multi' | 'list-single' | 'text-multi' | 'text-private' | 'text-single',
|
||||||
var :: binary(),
|
var :: binary(),
|
||||||
@ -255,6 +269,8 @@
|
|||||||
mode = tcp :: 'tcp' | 'udp',
|
mode = tcp :: 'tcp' | 'udp',
|
||||||
sid :: binary()}).
|
sid :: binary()}).
|
||||||
|
|
||||||
|
-record(chatstate_active, {}).
|
||||||
|
|
||||||
-record(vcard_org, {name :: binary(),
|
-record(vcard_org, {name :: binary(),
|
||||||
units = [] :: [binary()]}).
|
units = [] :: [binary()]}).
|
||||||
|
|
||||||
@ -475,7 +491,7 @@
|
|||||||
-record(time, {tzo :: any(),
|
-record(time, {tzo :: any(),
|
||||||
utc :: any()}).
|
utc :: any()}).
|
||||||
|
|
||||||
-type xmpp_element() :: #session{} |
|
-type xmpp_codec_type() :: #session{} |
|
||||||
#compression{} |
|
#compression{} |
|
||||||
#pubsub_subscription{} |
|
#pubsub_subscription{} |
|
||||||
#version{} |
|
#version{} |
|
||||||
@ -502,21 +518,15 @@
|
|||||||
#pubsub_event_item{} |
|
#pubsub_event_item{} |
|
||||||
#muc_item{} |
|
#muc_item{} |
|
||||||
#shim{} |
|
#shim{} |
|
||||||
#caps{} |
|
|
||||||
#muc{} |
|
|
||||||
#stream_features{} |
|
|
||||||
#stats{} |
|
|
||||||
#pubsub_items{} |
|
|
||||||
#pubsub_event_items{} |
|
#pubsub_event_items{} |
|
||||||
#disco_items{} |
|
#disco_items{} |
|
||||||
#pubsub_options{} |
|
#pubsub_options{} |
|
||||||
#starttls{} |
|
|
||||||
#sasl_mechanisms{} |
|
|
||||||
#sasl_success{} |
|
#sasl_success{} |
|
||||||
#compress{} |
|
#compress{} |
|
||||||
#bytestreams{} |
|
#bytestreams{} |
|
||||||
#vcard_key{} |
|
#vcard_key{} |
|
||||||
#identity{} |
|
#identity{} |
|
||||||
|
#feature_csi{} |
|
||||||
#legacy_delay{} |
|
#legacy_delay{} |
|
||||||
#muc_user_destroy{} |
|
#muc_user_destroy{} |
|
||||||
#muc_owner_destroy{} |
|
#muc_owner_destroy{} |
|
||||||
@ -527,8 +537,8 @@
|
|||||||
#vcard_email{} |
|
#vcard_email{} |
|
||||||
#vcard_label{} |
|
#vcard_label{} |
|
||||||
#vcard_tel{} |
|
#vcard_tel{} |
|
||||||
#disco_info{} |
|
|
||||||
#vcard_logo{} |
|
#vcard_logo{} |
|
||||||
|
#disco_info{} |
|
||||||
#vcard_geo{} |
|
#vcard_geo{} |
|
||||||
#vcard_photo{} |
|
#vcard_photo{} |
|
||||||
#muc_owner{} |
|
#muc_owner{} |
|
||||||
@ -542,15 +552,19 @@
|
|||||||
#muc_user{} |
|
#muc_user{} |
|
||||||
#vcard_adr{} |
|
#vcard_adr{} |
|
||||||
#register{} |
|
#register{} |
|
||||||
|
#csi_active{} |
|
||||||
#muc_invite{} |
|
#muc_invite{} |
|
||||||
#carbons_disable{} |
|
#carbons_disable{} |
|
||||||
|
#chatstate_active{} |
|
||||||
#bookmark_conference{} |
|
#bookmark_conference{} |
|
||||||
#time{} |
|
#time{} |
|
||||||
#sasl_response{} |
|
#sasl_response{} |
|
||||||
|
#chatstate_inactive{} |
|
||||||
#pubsub_subscribe{} |
|
#pubsub_subscribe{} |
|
||||||
#presence{} |
|
#presence{} |
|
||||||
#message{} |
|
#message{} |
|
||||||
#sm_enable{} |
|
#sm_enable{} |
|
||||||
|
#chatstate_gone{} |
|
||||||
#starttls_failure{} |
|
#starttls_failure{} |
|
||||||
#sasl_challenge{} |
|
#sasl_challenge{} |
|
||||||
#gone{} |
|
#gone{} |
|
||||||
@ -563,6 +577,7 @@
|
|||||||
#carbons_enable{} |
|
#carbons_enable{} |
|
||||||
#carbons_private{} |
|
#carbons_private{} |
|
||||||
#pubsub_unsubscribe{} |
|
#pubsub_unsubscribe{} |
|
||||||
|
#csi_inactive{} |
|
||||||
#muc_decline{} |
|
#muc_decline{} |
|
||||||
#sasl_auth{} |
|
#sasl_auth{} |
|
||||||
#p1_push{} |
|
#p1_push{} |
|
||||||
@ -577,12 +592,21 @@
|
|||||||
#sm_enabled{} |
|
#sm_enabled{} |
|
||||||
#pubsub_event{} |
|
#pubsub_event{} |
|
||||||
#vcard_sound{} |
|
#vcard_sound{} |
|
||||||
|
#chatstate_paused{} |
|
||||||
#streamhost{} |
|
#streamhost{} |
|
||||||
#stat{} |
|
#stat{} |
|
||||||
#xdata_field{} |
|
#xdata_field{} |
|
||||||
#bind{} |
|
#bind{} |
|
||||||
#sm_failed{} |
|
#sm_failed{} |
|
||||||
#vcard{} |
|
#vcard{} |
|
||||||
|
#chatstate_composing{} |
|
||||||
#ping{} |
|
#ping{} |
|
||||||
#disco_item{} |
|
#disco_item{} |
|
||||||
#privacy_item{}.
|
#privacy_item{} |
|
||||||
|
#caps{} |
|
||||||
|
#muc{} |
|
||||||
|
#stream_features{} |
|
||||||
|
#stats{} |
|
||||||
|
#pubsub_items{} |
|
||||||
|
#starttls{} |
|
||||||
|
#sasl_mechanisms{}.
|
||||||
|
@ -1757,6 +1757,31 @@
|
|||||||
result = {shim, '$headers'},
|
result = {shim, '$headers'},
|
||||||
refs = [#ref{name = shim_header, label = '$headers'}]}).
|
refs = [#ref{name = shim_header, label = '$headers'}]}).
|
||||||
|
|
||||||
|
-xml(chatstate_active,
|
||||||
|
#elem{name = <<"active">>,
|
||||||
|
xmlns = <<"http://jabber.org/protocol/chatstates">>,
|
||||||
|
result = {chatstate_active}}).
|
||||||
|
|
||||||
|
-xml(chatstate_composing,
|
||||||
|
#elem{name = <<"composing">>,
|
||||||
|
xmlns = <<"http://jabber.org/protocol/chatstates">>,
|
||||||
|
result = {chatstate_composing}}).
|
||||||
|
|
||||||
|
-xml(chatstate_gone,
|
||||||
|
#elem{name = <<"gone">>,
|
||||||
|
xmlns = <<"http://jabber.org/protocol/chatstates">>,
|
||||||
|
result = {chatstate_gone}}).
|
||||||
|
|
||||||
|
-xml(chatstate_inactive,
|
||||||
|
#elem{name = <<"inactive">>,
|
||||||
|
xmlns = <<"http://jabber.org/protocol/chatstates">>,
|
||||||
|
result = {chatstate_inactive}}).
|
||||||
|
|
||||||
|
-xml(chatstate_paused,
|
||||||
|
#elem{name = <<"paused">>,
|
||||||
|
xmlns = <<"http://jabber.org/protocol/chatstates">>,
|
||||||
|
result = {chatstate_paused}}).
|
||||||
|
|
||||||
-xml(delay,
|
-xml(delay,
|
||||||
#elem{name = <<"delay">>,
|
#elem{name = <<"delay">>,
|
||||||
xmlns = <<"urn:xmpp:delay">>,
|
xmlns = <<"urn:xmpp:delay">>,
|
||||||
@ -2070,6 +2095,22 @@
|
|||||||
refs = [#ref{name = forwarded, min = 1,
|
refs = [#ref{name = forwarded, min = 1,
|
||||||
max = 1, label = '$forwarded'}]}).
|
max = 1, label = '$forwarded'}]}).
|
||||||
|
|
||||||
|
-xml(feature_csi,
|
||||||
|
#elem{name = <<"csi">>,
|
||||||
|
xmlns = <<"urn:xmpp:csi:0">>,
|
||||||
|
result = {feature_csi, '$xmlns'},
|
||||||
|
attrs = [#attr{name = <<"xmlns">>}]}).
|
||||||
|
|
||||||
|
-xml(csi_active,
|
||||||
|
#elem{name = <<"active">>,
|
||||||
|
xmlns = <<"urn:xmpp:csi:0">>,
|
||||||
|
result = {csi_active}}).
|
||||||
|
|
||||||
|
-xml(csi_inactive,
|
||||||
|
#elem{name = <<"inactive">>,
|
||||||
|
xmlns = <<"urn:xmpp:csi:0">>,
|
||||||
|
result = {csi_inactive}}).
|
||||||
|
|
||||||
-xml(feature_sm,
|
-xml(feature_sm,
|
||||||
#elem{name = <<"sm">>,
|
#elem{name = <<"sm">>,
|
||||||
xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>],
|
xmlns = [<<"urn:xmpp:sm:2">>, <<"urn:xmpp:sm:3">>],
|
||||||
|
Loading…
Reference in New Issue
Block a user