diff options
author | Marco Barisione <marco@barisione.org> | 2011-05-05 15:01:59 +0100 |
---|---|---|
committer | Marco Barisione <marco@barisione.org> | 2011-05-05 15:02:16 +0100 |
commit | 5d2927d9155d2388145406c189fdf9d9f8dfcac6 (patch) | |
tree | a0855929ac9e1ff8e7159388af6c14e1a1ba744f | |
parent | cacd8147f3d0c0f4b0b851731b76dcacb4a9fb35 (diff) | |
parent | e4ae0fe527dbbffe403026b25eb4c44b3b325715 (diff) |
Merge branch 'shared-presence' into telepathy-gabble-0.12
Reviewed-by: Will Thompson <will.thompson@collabora.co.uk>
Fixes: <https://bugs.freedesktop.org/show_bug.cgi?id=36058>
-rw-r--r-- | src/conn-presence.c | 142 | ||||
-rw-r--r-- | tests/twisted/presence/invisible_helper.py | 2 | ||||
-rw-r--r-- | tests/twisted/presence/shared-status.py | 116 |
3 files changed, 199 insertions, 61 deletions
diff --git a/src/conn-presence.c b/src/conn-presence.c index 0d28d6030..871ee9f4f 100644 --- a/src/conn-presence.c +++ b/src/conn-presence.c @@ -326,16 +326,19 @@ set_shared_status_cb (GObject *source_object, GabbleConnection *self = GABBLE_CONNECTION (source_object); GError *error = NULL; - if (!conn_util_send_iq_finish (self, res, NULL, &error) || - !conn_presence_signal_own_presence (self, NULL, &error)) + if (!conn_util_send_iq_finish (self, res, NULL, &error)) { g_simple_async_result_set_error (result, CONN_PRESENCE_ERROR, CONN_PRESENCE_ERROR_SET_SHARED_STATUS, "error setting Google shared status: %s", error->message); } + else + { + gabble_muc_factory_broadcast_presence (self->muc_factory); + } - g_simple_async_result_complete (result); - g_object_unref (result); + g_simple_async_result_complete (result); + g_object_unref (result); if (error != NULL) g_error_free (error); @@ -380,23 +383,50 @@ set_shared_status (GabbleConnection *self, { GabbleConnectionPresencePrivate *priv = self->presence_priv; GabblePresence *presence = self->self_presence; - WockyStanza *iq; g_object_ref (result); - DEBUG ("shared status invisibility is %savailable", - priv->shared_status_compat ? "" : "un"); + if (presence->status != GABBLE_PRESENCE_AWAY && + presence->status != GABBLE_PRESENCE_XA) + { + WockyStanza *iq; - if (presence->status == GABBLE_PRESENCE_HIDDEN && !priv->shared_status_compat) - presence->status = GABBLE_PRESENCE_DND; + DEBUG ("shared status invisibility is %savailable", + priv->shared_status_compat ? "" : "un"); - insert_presence_to_shared_statuses (self); + if (presence->status == GABBLE_PRESENCE_HIDDEN && !priv->shared_status_compat) + presence->status = GABBLE_PRESENCE_DND; - iq = build_shared_status_stanza (self); + insert_presence_to_shared_statuses (self); - conn_util_send_iq_async (self, iq, NULL, set_shared_status_cb, result); + iq = build_shared_status_stanza (self); - g_object_unref (iq); + conn_util_send_iq_async (self, iq, NULL, set_shared_status_cb, result); + + g_object_unref (iq); + } + else + { + gboolean retval; + GError *error = NULL; + + /* Away is treated like idleness in GTalk, so it's per connection and + * not global. To set the presence as away we use the normal + * <presence/> method. */ + DEBUG ("not updating shared status as it's not supported for away"); + + retval = conn_presence_signal_own_presence (self, NULL, &error); + if (!retval) + { + g_simple_async_result_set_from_error (result, error); + g_error_free (error); + } + + emit_presences_changed_for_self (self); + + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); + } } static void @@ -1003,6 +1033,10 @@ get_shared_status_async (GabbleConnection *self, conn_util_send_iq_async (self, iq, NULL, get_shared_status_cb, result); + /* We cannot use the chat status with GTalk's shared status. */ + if (self->self_presence->status == GABBLE_PRESENCE_CHAT) + self->self_presence->status = GABBLE_PRESENCE_AVAILABLE; + g_object_unref (iq); } @@ -1364,6 +1398,38 @@ privacy_lists_loaded_cb (GObject *source_object, } static void +shared_status_toggle_initial_presence_visibility_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GabbleConnection *self = GABBLE_CONNECTION (source_object); + GSimpleAsyncResult *external_result = G_SIMPLE_ASYNC_RESULT (user_data); + GError *error = NULL; + + if (!toggle_presence_visibility_finish (self, result, &error)) + { + g_simple_async_result_set_from_error (external_result, error); + g_clear_error (&error); + } + else if (self->self_presence->status != GABBLE_PRESENCE_AWAY && + self->self_presence->status != GABBLE_PRESENCE_XA) + { + /* With shared status we send the normal <presence/> only with away and + * extended away, but for initial status we need to send <presence/> as + * it also contains the caps. */ + if (!conn_presence_signal_own_presence (self, NULL, &error)) + { + g_simple_async_result_set_from_error (external_result, error); + g_error_free (error); + } + } + + g_simple_async_result_complete_in_idle (external_result); + + g_object_unref (external_result); +} + +static void shared_status_setup_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) @@ -1393,7 +1459,8 @@ shared_status_setup_cb (GObject *source_object, g_error_free (error); } - get_existing_privacy_lists_async (self, privacy_lists_loaded_cb, user_data); + toggle_presence_visibility_async (self, + shared_status_toggle_initial_presence_visibility_cb, user_data); } void @@ -1790,6 +1857,12 @@ status_available_cb (GObject *obj, guint status) TpConnectionPresenceType presence_type = gabble_statuses[status].presence_type; + if (base->status != TP_CONNECTION_STATUS_CONNECTED) + { + /* we just don't know yet */ + return TRUE; + } + /* This relies on the fact the first entries in the statuses table * are from gabble_base_statuses. If index to the statuses table is outside * the gabble_base_statuses table, the status is provided by a plugin. */ @@ -1799,36 +1872,35 @@ status_available_cb (GObject *obj, guint status) * lists, so any extra status should be backed by one. If it's not * (or if privacy lists are not supported by the server at all) * by the time we're connected, it's not available. */ - - if (base->status == TP_CONNECTION_STATUS_CONNECTED) + if (priv->privacy_statuses != NULL && + g_hash_table_lookup (priv->privacy_statuses, + gabble_statuses[status].name)) { - if (priv->privacy_statuses != NULL && - g_hash_table_lookup (priv->privacy_statuses, - gabble_statuses[status].name)) - { - return TRUE; - } - else - { - return FALSE; - } + return TRUE; } else { - /* we just don't know yet */ - return TRUE; + return FALSE; } } - /* If we've gone online and found that the server doesn't support invisible, - * reject it. - */ - if (base->status == TP_CONNECTION_STATUS_CONNECTED && - presence_type == TP_CONNECTION_PRESENCE_TYPE_HIDDEN && + if (presence_type == TP_CONNECTION_PRESENCE_TYPE_HIDDEN && priv->invisibility_method == INVISIBILITY_METHOD_NONE) - return FALSE; + { + /* If we've gone online and found that the server doesn't support + * invisible, reject it. */ + return FALSE; + } + else if (status == GABBLE_PRESENCE_CHAT && + priv->shared_statuses != NULL) + { + /* We cannot use the chat status with GTalk's shared status. */ + return FALSE; + } else - return TRUE; + { + return TRUE; + } } GabblePresenceId diff --git a/tests/twisted/presence/invisible_helper.py b/tests/twisted/presence/invisible_helper.py index 1e345dddf..29659b04b 100644 --- a/tests/twisted/presence/invisible_helper.py +++ b/tests/twisted/presence/invisible_helper.py @@ -95,6 +95,8 @@ class Xep0186AndManualPrivacyListStream(ManualPrivacyListStream): class SharedStatusStream(XmppXmlStream): disco_features = [ns.GOOGLE_SHARED_STATUS] + handle_privacy_lists = False + def __init__(self, event_func, authenticator): XmppXmlStream.__init__(self, event_func, authenticator) self.addObserver("/iq/query[@xmlns='%s']" % ns.GOOGLE_SHARED_STATUS, diff --git a/tests/twisted/presence/shared-status.py b/tests/twisted/presence/shared-status.py index e0db603cf..63af7500b 100644 --- a/tests/twisted/presence/shared-status.py +++ b/tests/twisted/presence/shared-status.py @@ -12,16 +12,22 @@ from servicetest import ( assertDoesNotContain ) import ns +import copy import constants as cs +import dbus from twisted.words.xish import xpath, domish from invisible_helper import SharedStatusStream presence_types = {'available' : cs.PRESENCE_AVAILABLE, 'away' : cs.PRESENCE_AWAY, + 'xa' : cs.PRESENCE_EXTENDED_AWAY, 'hidden' : cs.PRESENCE_HIDDEN, 'dnd' : cs.PRESENCE_BUSY} def _show_to_shared_status_show(show): + # Away and extended away don't use shared status. + assert show not in ('away', 'xa') + shared_show = 'default' if show == 'dnd': shared_show = 'dnd' @@ -48,22 +54,36 @@ def _test_remote_status(q, stream, msg, show, list_attrs): def _test_local_status(q, conn, stream, msg, show, expected_show=None): expected_show = expected_show or show - - shared_show, shared_invisible = _show_to_shared_status_show(expected_show) + away = expected_show in ('away', 'xa') + + # Away and extended away are mapped to idle, that is per connection. + # This means we use <presence/> instead of shared presence. + if not away: + wrong_presence_pattern = EventPattern('stream-presence') + else: + wrong_presence_pattern = EventPattern('stream-iq', + query_ns=ns.GOOGLE_SHARED_STATUS, + iq_type='set') + q.forbid_events([wrong_presence_pattern]) conn.SimplePresence.SetPresence(show, msg) - event = q.expect('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, - iq_type='set') - max_status_message_length = int(stream.max_status_message_length) - _status = xpath.queryForNodes('//status', event.query)[0] - assertEquals(msg[:max_status_message_length], _status.children[0]) - _show = xpath.queryForNodes('//show', event.query)[0] - assertEquals(shared_show, _show.children[0]) - _invisible = xpath.queryForNodes('//invisible', event.query)[0] - assertEquals(shared_invisible, _invisible.getAttribute('value')) + if not away: + event = q.expect('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, + iq_type='set') + + shared_show, shared_invisible = _show_to_shared_status_show(expected_show) + + _status = xpath.queryForNodes('//status', event.query)[0] + assertEquals(msg[:max_status_message_length], _status.children[0]) + _show = xpath.queryForNodes('//show', event.query)[0] + assertEquals(shared_show, _show.children[0]) + _invisible = xpath.queryForNodes('//invisible', event.query)[0] + assertEquals(shared_invisible, _invisible.getAttribute('value')) + else: + q.expect('stream-presence') q.expect_many( EventPattern('dbus-signal', signal='PresenceUpdate', @@ -75,12 +95,14 @@ def _test_local_status(q, conn, stream, msg, show, expected_show=None): args=[{1: (presence_types[expected_show], expected_show, msg[:max_status_message_length])}])) + q.unforbid_events([wrong_presence_pattern]) def test(q, bus, conn, stream): q.expect_many(EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='get'), EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, - iq_type='set')) + iq_type='set'), + EventPattern('stream-presence')) # Set shared status to dnd. _test_local_status(q, conn, stream, "Don't disturb, buddy.", "dnd") @@ -94,10 +116,14 @@ def test(q, bus, conn, stream): # Set shared status to default, local status to away. _test_local_status(q, conn, stream, "I'm away right now", "away") + _test_local_status(q, conn, stream, "cd" * max_status_message_length, "away") - # Status changes from another client - _test_remote_status(q, stream, "This is me, set from another client.", - "away", {"show" : "default"}) + # Test interaction with another client. + _test_local_status(q, conn, stream, "Don't disturb, buddy.", "dnd") + _test_remote_status(q, stream, "This is me busy, set from another client.", + "dnd", {"show" : "dnd"}) + _test_remote_status(q, stream, "This is me available, set from another client.", + "available", {"show" : "default"}) # Change min version stream.set_shared_status_lists(min_version="1") @@ -125,12 +151,15 @@ def test(q, bus, conn, stream): interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (cs.PRESENCE_BUSY, 'dnd', "Peekabo")}])) -def _test_on_connect(q, bus, conn, stream, shared_status, show, msg): +def _test_on_connect(q, bus, conn, stream, shared_status, show, msg, expected_show=None): + expected_show = expected_show or show _status, _show, _invisible = shared_status stream.shared_status = shared_status - presence_event_pattern = EventPattern('stream-presence') - q.forbid_events([presence_event_pattern]) + forbidden_even_patterns = [EventPattern('stream-presence'), + EventPattern('stream-iq', query_ns=ns.PRIVACY, + iq_type='get')] + q.forbid_events(forbidden_even_patterns) conn.SimplePresence.SetPresence(show, msg) conn.Connect() @@ -149,15 +178,16 @@ def _test_on_connect(q, bus, conn, stream, shared_status, show, msg): _invisible = xpath.queryForNodes('//invisible', event.query)[0] assertEquals(shared_invisible, _invisible.getAttribute('value')) - q.unforbid_events([presence_event_pattern]) + q.unforbid_events(forbidden_even_patterns) q.expect_many( EventPattern('dbus-signal', signal='PresenceUpdate', interface=cs.CONN_IFACE_PRESENCE, - args=[{1: (0, {show: {'message': msg}})}]), + args=[{1: (0, {expected_show: {'message': msg}})}]), EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, - args=[{1: (presence_types[show], show, msg)}]), + args=[{1: (presence_types[expected_show], + expected_show, msg)}]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])) @@ -165,6 +195,10 @@ def test_connect_available(q, bus, conn, stream): _test_on_connect(q, bus, conn, stream, ("I'm busy, buddy.", 'dnd', 'false'), 'available', "I'm here, baby.") +def test_connect_chat(q, bus, conn, stream): + _test_on_connect(q, bus, conn, stream, ("I'm busy, buddy.", 'dnd', 'false'), + 'chat', "Do you want to chat?", 'available') + def test_connect_dnd(q, bus, conn, stream): _test_on_connect(q, bus, conn, stream, ("Chat with me.", 'default', 'false'), 'dnd', "I'm busy, buddy.") @@ -223,17 +257,15 @@ def test_shared_status_list(q, bus, conn, stream): "available" : ['I am twiddling my thumbs', 'Please chat me up', 'I am here for you', - 'Message me already!'], - "away" : ['I am not around', - 'Don\'t bother...', - 'I am awaaaaaaaay']} + 'Message me already!']} max_statuses = int(stream.max_statuses) q.expect_many(EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='get'), EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, - iq_type='set')) + iq_type='set'), + EventPattern('stream-presence')) for show, statuses in test_statuses.items(): shared_show, _ = _show_to_shared_status_show(show) @@ -243,10 +275,42 @@ def test_shared_status_list(q, bus, conn, stream): expected_list = [status] + expected_list[:max_statuses - 1] assertEquals(expected_list, stream.shared_status_lists[shared_show]) +def test_shared_status_away(q, bus, conn, stream): + '''Test the shared status lists with away statuses''' + q.expect_many(EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, + iq_type='get'), + EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, + iq_type='set'), + EventPattern('stream-presence')) + + expected_list = copy.deepcopy(stream.shared_status_lists) + for show in ('away', 'xa'): + for status in ('not going to', 'be actually set'): + _test_local_status(q, conn, stream, status, show) + assertEquals(expected_list, stream.shared_status_lists) + +def test_shared_status_chat(q, bus, conn, stream): + '''Test that 'chat' is not supported with shared status''' + q.expect_many(EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, + iq_type='get'), + EventPattern('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, + iq_type='set'), + EventPattern('stream-presence')) + + try: + conn.SimplePresence.SetPresence('chat', 'This is not going to work') + except dbus.DBusException, e: + assert e.get_dbus_name() == cs.NOT_AVAILABLE + else: + assert False + if __name__ == '__main__': exec_test(test, protocol=SharedStatusStream) exec_test(test_connect_available, protocol=SharedStatusStream, do_connect=False) + exec_test(test_connect_chat, protocol=SharedStatusStream, do_connect=False) exec_test(test_connect_dnd, protocol=SharedStatusStream, do_connect=False) exec_test(test_connect_hidden, protocol=SharedStatusStream, do_connect=False) exec_test(test_connect_hidden_not_available, protocol=SharedStatusStream, do_connect=False) exec_test(test_shared_status_list, protocol=SharedStatusStream) + exec_test(test_shared_status_away, protocol=SharedStatusStream) + exec_test(test_shared_status_chat, protocol=SharedStatusStream) |