summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarco Barisione <marco@barisione.org>2011-05-05 15:01:59 +0100
committerMarco Barisione <marco@barisione.org>2011-05-05 15:02:16 +0100
commit5d2927d9155d2388145406c189fdf9d9f8dfcac6 (patch)
treea0855929ac9e1ff8e7159388af6c14e1a1ba744f
parentcacd8147f3d0c0f4b0b851731b76dcacb4a9fb35 (diff)
parente4ae0fe527dbbffe403026b25eb4c44b3b325715 (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.c142
-rw-r--r--tests/twisted/presence/invisible_helper.py2
-rw-r--r--tests/twisted/presence/shared-status.py116
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)