From 12ea43016a73e853a1a6ad8227f2a6f109474abf Mon Sep 17 00:00:00 2001 From: Michael Scherer Date: Mon, 27 Jun 2011 18:41:35 +0100 Subject: [PATCH 2/3] Support XEP-0012: Last Activity Patch somewhat rewritten by Will, since Gabble has changed a lot in the two years since he rejected it on (in retrospect, incorrect and unnecessarily hostile) grounds. Fixes: Signed-off-by: Will Thompson --- src/connection.c | 59 ++++++++++++++++++++++++++++++++++++++++ src/connection.h | 1 + src/message-util.c | 1 + src/namespaces.h | 1 + tests/twisted/Makefile.am | 1 + tests/twisted/last-activity.py | 49 +++++++++++++++++++++++++++++++++ tests/twisted/ns.py | 1 + 7 files changed, 113 insertions(+), 0 deletions(-) create mode 100644 tests/twisted/last-activity.py diff --git a/src/connection.c b/src/connection.c index 4af9767..dcbe523 100644 --- a/src/connection.c +++ b/src/connection.c @@ -22,6 +22,7 @@ #include "connection.h" #include "gabble.h" +#include #include #define DBUS_API_SUBJECT_TO_CHANGE @@ -236,6 +237,10 @@ struct _GabbleConnectionPrivate /* serial number of current advertised caps */ guint caps_serial; + /* Last activity time for XEP-0012 purposes, where "activity" is defined to + * mean "sending a message". + */ + time_t last_activity_time; /* capabilities from various sources: */ /* subscriptions on behalf of the Connection, like PEP "+notify" @@ -518,6 +523,7 @@ gabble_connection_init (GabbleConnection *self) self->lmconn = lm_connection_new (); priv->caps_serial = 1; + priv->last_activity_time = time (NULL); priv->port = 5222; gabble_capabilities_init (self); @@ -1379,6 +1385,18 @@ _gabble_connection_send (GabbleConnection *conn, LmMessage *msg, GError **error) return TRUE; } +void +gabble_connection_update_last_use (GabbleConnection *conn) +{ + conn->priv->last_activity_time = time (NULL); +} + +static gdouble +gabble_connection_get_last_use (GabbleConnection *conn) +{ + return difftime (time (NULL), conn->priv->last_activity_time); +} + typedef struct { GabbleConnectionMsgReplyFunc reply_func; @@ -1970,6 +1988,41 @@ connector_register_cb (GObject *source, g_free (jid); } +static gboolean +connection_iq_last_cb ( + WockyPorter *porter, + WockyStanza *stanza, + gpointer user_data) +{ + GabbleConnection *self = GABBLE_CONNECTION (user_data); + const gchar *from = wocky_stanza_get_from (stanza); + /* Aside from 21 being an appropriate number, 2 ^ 64 is 20 digits long. */ + char seconds[21]; + + /* Check if the peer, if any, is authorized to receive our presence. */ + if (from != NULL) + { + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( + (TpBaseConnection *) self, TP_HANDLE_TYPE_CONTACT); + TpHandle handle = tp_handle_lookup (contact_repo, from, NULL, NULL); + + /* If there's no handle for them, they're certainly not on the roster. */ + if (handle == 0 || + !gabble_roster_handle_gets_presence_from_us (self->roster, handle)) + { + wocky_porter_send_iq_error (porter, stanza, + WOCKY_XMPP_ERROR_FORBIDDEN, NULL); + return TRUE; + } + } + + sprintf (seconds, "%.0f", gabble_connection_get_last_use (self)); + wocky_porter_acknowledge_iq (porter, stanza, + '(', "query", ':', NS_LAST, '@', "seconds", seconds, ')', + NULL); + return TRUE; +} + static void connect_iq_callbacks (GabbleConnection *conn) { @@ -1987,6 +2040,12 @@ connect_iq_callbacks (GabbleConnection *conn) iq_version_cb, conn, '(', "query", ':', NS_VERSION, ')', NULL); + wocky_porter_register_handler_from_anyone (priv->porter, + WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, + WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, + connection_iq_last_cb, conn, + '(', "query", ':', NS_LAST, ')', NULL); + /* FIXME: the porter should do this for us. */ wocky_porter_register_handler_from_anyone (priv->porter, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE, diff --git a/src/connection.h b/src/connection.h index 2b425e9..109f2d4 100644 --- a/src/connection.h +++ b/src/connection.h @@ -218,6 +218,7 @@ void _gabble_connection_acknowledge_set_iq (GabbleConnection *conn, LmMessage *iq); void _gabble_connection_send_iq_error (GabbleConnection *conn, LmMessage *message, GabbleXmppError error, const gchar *errmsg); +void gabble_connection_update_last_use (GabbleConnection *conn); const char *_gabble_connection_find_conference_server (GabbleConnection *); gchar *gabble_connection_get_canonical_room_name (GabbleConnection *conn, diff --git a/src/message-util.c b/src/message-util.c index 1e7f2a0..c0f5a69 100644 --- a/src/message-util.c +++ b/src/message-util.c @@ -180,6 +180,7 @@ gabble_message_util_build_stanza (TpMessage *message, else g_free (id); + gabble_connection_update_last_use (conn); return stanza; } diff --git a/src/namespaces.h b/src/namespaces.h index 08b00c6..d39853d 100644 --- a/src/namespaces.h +++ b/src/namespaces.h @@ -84,6 +84,7 @@ /* Jingle ICE-UDP transport */ #define NS_JINGLE_TRANSPORT_ICEUDP "urn:xmpp:jingle:transports:ice-udp:1" +#define NS_LAST "jabber:iq:last" #define NS_MUC "http://jabber.org/protocol/muc" #define NS_MUC_BYTESTREAM "http://telepathy.freedesktop.org/xmpp/protocol/muc-bytestream" #define NS_MUC_USER "http://jabber.org/protocol/muc#user" diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am index 0603d6a..3bb3a24 100644 --- a/tests/twisted/Makefile.am +++ b/tests/twisted/Makefile.am @@ -20,6 +20,7 @@ TWISTED_TESTS = \ client-types.py \ cm/protocol.py \ gateways.py \ + last-activity.py \ muc/avatars.py \ muc/banned.py \ muc/conference.py \ diff --git a/tests/twisted/last-activity.py b/tests/twisted/last-activity.py new file mode 100644 index 0000000..fd3da5a --- /dev/null +++ b/tests/twisted/last-activity.py @@ -0,0 +1,49 @@ +""" +Trivial smoke-test for XEP-0012 support. +""" +from servicetest import assertEquals, assertContains +from gabbletest import exec_test, elem, elem_iq +import ns + +def test(q, bus, conn, stream): + e = q.expect('stream-iq', iq_type='get', query_ns=ns.ROSTER) + e.stanza['type'] = 'result' + e.query.addChild( + elem('item', jid='romeo@montague.lit', subscription='both')) + stream.send(e.stanza) + + # Romeo's on the roster. + stream.send( + elem_iq(stream, 'get', from_='romeo@montague.lit')( + elem(ns.LAST, 'query') + ) + ) + e = q.expect('stream-iq', iq_type='result', + query_ns=ns.LAST, query_name='query') + # No real assertions about the number of seconds; this is just a smoke + # test. + seconds = e.query['seconds'] + assert seconds >= 0 + + # Juliet is not. + stream.send( + elem_iq(stream, 'get', from_='juliet@capulet.lit')( + elem(ns.LAST, 'query') + ) + ) + e = q.expect('stream-iq', iq_type='error', + query_ns=ns.LAST, query_name='query') + # Yuck. + assertEquals('forbidden', e.stanza.children[1].children[0].name) + + # If the server asks, Gabble had better not crash. + stream.send( + elem_iq(stream, 'get')( + elem(ns.LAST, 'query') + ) + ) + e = q.expect('stream-iq', iq_type='result', + query_ns=ns.LAST, query_name='query') + +if __name__ == '__main__': + exec_test(test) diff --git a/tests/twisted/ns.py b/tests/twisted/ns.py index 66d2ecb..629f5b4 100644 --- a/tests/twisted/ns.py +++ b/tests/twisted/ns.py @@ -32,6 +32,7 @@ JINGLE_RTP_ERRORS = "urn:xmpp:jingle:apps:rtp:errors:1" JINGLE_RTP_INFO_1 = "urn:xmpp:jingle:apps:rtp:info:1" JINGLE_TRANSPORT_ICEUDP = "urn:xmpp:jingle:transports:ice-udp:1" JINGLE_TRANSPORT_RAWUDP = "urn:xmpp:jingle:transports:raw-udp:1" +LAST = "jabber:iq:last" MUC = 'http://jabber.org/protocol/muc' MUC_BYTESTREAM = 'http://telepathy.freedesktop.org/xmpp/protocol/muc-bytestream' MUC_OWNER = '%s#owner' % MUC -- 1.7.5.4