summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWill Thompson <will.thompson@collabora.co.uk>2011-06-16 17:07:55 +0100
committerWill Thompson <will.thompson@collabora.co.uk>2011-06-16 17:07:57 +0100
commit052ca8131c8e145714372db249efd9bd04c70f94 (patch)
treeb60274ba51c9901724c038a30b1ac714f0cf45f1
parente8e2d23490846ebabb7c640bb905b7549883b3af (diff)
parenta88dd5529d038f32b3c01b9eb6109eaa10e88604 (diff)
Merge branch 'gtalk-jingle-workarounds' into telepathy-gabble-0.12
These aren't perfect, but they'll do for now. https://bugs.freedesktop.org/show_bug.cgi?id=38352
-rw-r--r--src/jingle-content.c16
-rw-r--r--src/jingle-media-rtp.c4
-rw-r--r--src/jingle-session.c27
-rw-r--r--src/jingle-session.h3
-rw-r--r--src/jingle-transport-iface.c2
-rw-r--r--src/media-channel.c4
-rw-r--r--src/presence-cache.c1
-rw-r--r--tests/twisted/Makefile.am1
-rw-r--r--tests/twisted/jingle/incoming-gmail-modern-jingle.py179
9 files changed, 223 insertions, 14 deletions
diff --git a/src/jingle-content.c b/src/jingle-content.c
index 25167d32f..b8ebefd60 100644
--- a/src/jingle-content.c
+++ b/src/jingle-content.c
@@ -507,6 +507,8 @@ gabble_jingle_content_parse_add (GabbleJingleContent *c,
GabbleJingleTransportIface *trans = NULL;
JingleDialect dialect = gabble_jingle_session_get_dialect (c->session);
+ priv->created_by_us = FALSE;
+
desc_node = lm_message_node_get_child_any_ns (content_node, "description");
trans_node = lm_message_node_get_child_any_ns (content_node, "transport");
creator = lm_message_node_get_attribute (content_node, "creator");
@@ -549,6 +551,19 @@ gabble_jingle_content_parse_add (GabbleJingleContent *c,
}
else
{
+ if (creator == NULL &&
+ gabble_jingle_session_peer_has_quirk (c->session,
+ QUIRK_GOOGLE_WEBMAIL_CLIENT))
+ {
+ if (gabble_jingle_content_creator_is_initiator (c))
+ creator = "initiator";
+ else
+ creator = "responder";
+
+ DEBUG ("Working around GMail omitting creator=''; assuming '%s'",
+ creator);
+ }
+
if ((trans_node == NULL) || (creator == NULL) || (name == NULL))
{
SET_BAD_REQ ("missing required content attributes or elements");
@@ -577,7 +592,6 @@ gabble_jingle_content_parse_add (GabbleJingleContent *c,
priv->transport_ns = g_strdup (ns);
}
- priv->created_by_us = FALSE;
if (senders == NULL)
priv->senders = get_default_senders (c);
else
diff --git a/src/jingle-media-rtp.c b/src/jingle-media-rtp.c
index 0e21a4fc0..5b1a9775b 100644
--- a/src/jingle-media-rtp.c
+++ b/src/jingle-media-rtp.c
@@ -337,7 +337,9 @@ static void transport_created (GabbleJingleContent *content,
dialect = gabble_jingle_session_get_dialect (content->session);
if (priv->media_type == JINGLE_MEDIA_TYPE_VIDEO &&
- JINGLE_IS_GOOGLE_DIALECT (dialect))
+ (JINGLE_IS_GOOGLE_DIALECT (dialect) ||
+ gabble_jingle_session_peer_has_quirk (content->session,
+ QUIRK_GOOGLE_WEBMAIL_CLIENT)))
{
jingle_transport_google_set_component_name (gtrans, "video_rtp", 1);
jingle_transport_google_set_component_name (gtrans, "video_rtcp", 2);
diff --git a/src/jingle-session.c b/src/jingle-session.c
index 539c0d162..f20c2d631 100644
--- a/src/jingle-session.c
+++ b/src/jingle-session.c
@@ -631,6 +631,21 @@ static void set_state (GabbleJingleSession *sess,
JingleState state, JingleReason termination_reason, const gchar *text);
static GabbleJingleContent *_get_any_content (GabbleJingleSession *session);
+gboolean
+gabble_jingle_session_peer_has_quirk (
+ GabbleJingleSession *self,
+ const gchar *quirk)
+{
+ GabbleJingleSessionPrivate *priv = self->priv;
+ GabblePresence *presence = gabble_presence_cache_get (
+ priv->conn->presence_cache, self->peer);
+
+ return (presence != NULL &&
+ priv->peer_resource != NULL &&
+ gabble_presence_resource_has_caps (presence, priv->peer_resource,
+ gabble_capability_set_predicate_has, quirk));
+}
+
static gboolean
lookup_content (GabbleJingleSession *sess,
const gchar *name,
@@ -668,13 +683,8 @@ lookup_content (GabbleJingleSession *sess,
* of the moon, and get kind of confused in the process), and we try to
* pick globally-unique content names.
*/
- GabblePresence *presence = gabble_presence_cache_get (
- priv->conn->presence_cache, sess->peer);
-
- if (creator == NULL && presence != NULL &&
- priv->peer_resource != NULL &&
- gabble_presence_resource_has_caps (presence, priv->peer_resource,
- gabble_capability_set_predicate_has,
+ if (creator == NULL &&
+ gabble_jingle_session_peer_has_quirk (sess,
QUIRK_OMITS_CONTENT_CREATORS))
{
DEBUG ("working around missing 'creator' attribute");
@@ -2355,7 +2365,8 @@ gabble_jingle_session_get_remote_ringing (GabbleJingleSession *sess)
gboolean
gabble_jingle_session_can_modify_contents (GabbleJingleSession *sess)
{
- return !JINGLE_IS_GOOGLE_DIALECT (sess->priv->dialect);
+ return !JINGLE_IS_GOOGLE_DIALECT (sess->priv->dialect) &&
+ !gabble_jingle_session_peer_has_quirk (sess, QUIRK_GOOGLE_WEBMAIL_CLIENT);
}
JingleDialect
diff --git a/src/jingle-session.h b/src/jingle-session.h
index f2a1fb83a..5c80b6c7a 100644
--- a/src/jingle-session.h
+++ b/src/jingle-session.h
@@ -107,6 +107,9 @@ const gchar *gabble_jingle_session_get_sid (GabbleJingleSession *sess);
JingleDialect gabble_jingle_session_get_dialect (GabbleJingleSession *sess);
gboolean gabble_jingle_session_can_modify_contents (GabbleJingleSession *sess);
+gboolean gabble_jingle_session_peer_has_quirk (
+ GabbleJingleSession *self,
+ const gchar *quirk);
typedef void (*JingleReplyHandler) (GObject *, gboolean success,
LmMessage *reply);
diff --git a/src/jingle-transport-iface.c b/src/jingle-transport-iface.c
index 76dd29d15..8c9861ec5 100644
--- a/src/jingle-transport-iface.c
+++ b/src/jingle-transport-iface.c
@@ -107,7 +107,7 @@ gabble_jingle_transport_iface_can_accept (GabbleJingleTransportIface *self)
if (state != JINGLE_TRANSPORT_STATE_CONNECTED)
return FALSE;
- /* Only Raw UDP *needs* contents in order to accept. */
+ /* Only Raw UDP *needs* candidates in order to accept. */
if (m != NULL)
return m (self);
else
diff --git a/src/media-channel.c b/src/media-channel.c
index 4e2341a57..136c785c5 100644
--- a/src/media-channel.c
+++ b/src/media-channel.c
@@ -450,9 +450,7 @@ gabble_media_channel_constructor (GType type, guint n_props,
/* If this is a Google session, let's set ImmutableStreams */
if (priv->session != NULL)
{
- JingleDialect d = gabble_jingle_session_get_dialect (priv->session);
-
- priv->immutable_streams = JINGLE_IS_GOOGLE_DIALECT (d);
+ priv->immutable_streams = !gabble_jingle_session_can_modify_contents (priv->session);
}
/* If there's no session yet, but we know who the peer will be, and we have
* presence for them, we can set ImmutableStreams using the same algorithm as
diff --git a/src/presence-cache.c b/src/presence-cache.c
index b8d818c1d..6132ed68d 100644
--- a/src/presence-cache.c
+++ b/src/presence-cache.c
@@ -1007,6 +1007,7 @@ _parse_node (GabblePresence *presence,
DEBUG ("Client is Google Web Client");
gabble_capability_set_add (cap_set, QUIRK_GOOGLE_WEBMAIL_CLIENT);
+ gabble_capability_set_add (cap_set, QUIRK_OMITS_CONTENT_CREATORS);
gabble_presence_set_capabilities (presence, resource, cap_set, serial);
gabble_capability_set_free (cap_set);
}
diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am
index 65c110c78..311deb77d 100644
--- a/tests/twisted/Makefile.am
+++ b/tests/twisted/Makefile.am
@@ -159,6 +159,7 @@ TWISTED_JINGLE_TESTS = \
jingle/hold-av.py \
jingle/incoming-basics.py \
jingle/incoming-call-stream-error.py \
+ jingle/incoming-gmail-modern-jingle.py \
jingle/initial-audio-video.py \
jingle/misuse.py \
jingle/stun-server.py \
diff --git a/tests/twisted/jingle/incoming-gmail-modern-jingle.py b/tests/twisted/jingle/incoming-gmail-modern-jingle.py
new file mode 100644
index 000000000..6ff285880
--- /dev/null
+++ b/tests/twisted/jingle/incoming-gmail-modern-jingle.py
@@ -0,0 +1,179 @@
+"""
+Tests workarounds for calls with the GMail client, which supports a (currently
+quirky) variation on the theme of modern Jingle.
+"""
+
+from servicetest import EventPattern, wrap_channel, make_channel_proxy, assertEquals
+from gabbletest import elem, elem_iq, exec_test
+from jingletest2 import JingleTest2, JingleProtocol031
+import ns
+import constants as cs
+from twisted.words.xish import xpath
+
+class GMail(JingleTest2):
+ remote_caps = {
+ 'ext': 'pmuc-v1 sms-v1 camera-v1 video-v1 voice-v1',
+ 'ver': '1.1',
+ 'node': 'http://mail.google.com/xmpp/client/caps',
+ }
+
+def test(q, bus, conn, stream):
+ peer = 'foo@gmail.com/gmail.7E1F07D0'
+ self = 'test@localhost/test'
+ jp = JingleProtocol031()
+ jt = GMail(jp, conn, q, stream, 'test@localhost', peer)
+ jt.prepare(send_roster=False)
+
+ sid = 'c1025763497'
+ si = elem_iq(stream, 'set', from_=peer, to=self)(
+ elem(ns.JINGLE, 'jingle', action='session-initiate', sid=sid, initiator=peer)(
+ elem('content', name='video')(
+ elem(ns.JINGLE_RTP, 'description', media='video')(
+ elem('payload-type', id='99', name='H264-SVC')(
+ elem('parameter', name='width', value='320'),
+ elem('parameter', name='height', value='200'),
+ elem('parameter', name='framerate', value='30'),
+ ),
+ # ... other codecs elided ...
+ elem('encryption'),
+ ),
+ elem(ns.GOOGLE_P2P, 'transport'),
+ ),
+ elem('content', name='audio')(
+ elem(ns.JINGLE_RTP, 'description', media='audio')(
+ elem('payload-type', id='103', name='ISAC', clockrate='16000')(
+ elem('parameter', name='bitrate', value='32000'),
+ ),
+ # ... other codecs elided ...
+ elem('encryption'),
+ ),
+ elem(ns.GOOGLE_P2P, 'transport'),
+ )
+ ),
+ elem(ns.GOOGLE_SESSION, 'session', action='initiate', sid='c1025763497', initiator=peer)(
+ elem(ns.GOOGLE_SESSION_VIDEO, 'description')(
+ elem('payload-type', id='99', name='H264-SVC', width='320', height='200', framerate='30'),
+ # ... other codecs elided ...
+ elem(ns.JINGLE_RTP, 'encryption')(
+ elem(ns.GOOGLE_SESSION_VIDEO, 'usage'),
+ ),
+ elem(ns.GOOGLE_SESSION_PHONE, 'payload-type', id='103', name='ISAC', bitrate='32000', clockrate='16000'),
+ # ... other codecs elided ...
+ elem(ns.JINGLE_RTP, 'encryption')(
+ elem(ns.GOOGLE_SESSION_PHONE, 'usage'),
+ ),
+ ),
+ ),
+ )
+ stream.send(si)
+
+ nc, nsh = q.expect_many(
+ EventPattern('dbus-signal', signal='NewChannels'),
+ EventPattern('dbus-signal', signal='NewSessionHandler'),
+ )
+
+ path, properties = nc.args[0][0]
+
+ # It's an audio+video call
+ assert properties[cs.INITIAL_AUDIO]
+ assert properties[cs.INITIAL_VIDEO]
+ # Google can't add and remove streams on the fly. We special-case GMail.
+ assert properties[cs.IMMUTABLE_STREAMS]
+
+ chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia')
+ session_handler = make_channel_proxy(conn, nsh.args[0], 'Media.SessionHandler')
+ session_handler.Ready()
+
+ path, _, _, _ = q.expect('dbus-signal', signal='NewStreamHandler').args
+ stream1 = make_channel_proxy(conn, path, 'Media.StreamHandler')
+ path, _, _, _ = q.expect('dbus-signal', signal='NewStreamHandler').args
+ stream2 = make_channel_proxy(conn, path, 'Media.StreamHandler')
+
+ stream1.Ready([])
+ stream2.Ready([])
+
+ # Audio rtcp
+ stream.send(
+ elem_iq(stream, from_=peer, to=self, type='set')(
+ elem(ns.JINGLE, 'jingle', action='transport-info', sid=sid)(
+ elem('content', name='audio')(
+ elem(ns.GOOGLE_P2P, 'transport')(
+ elem('candidate', address='172.22.64.192', port='54335',
+ name='rtcp', username='+wJqkmRVYotCz+Rd',
+ password='POWPzg5Pks4+ywAz', preference='1', protocol='udp',
+ generation='0', network='1', type='local')
+ )
+ )
+ )
+ )
+ )
+ q.expect('dbus-signal', signal='AddRemoteCandidate', path=stream1.object_path)
+
+ # audio rtp
+ stream.send(
+ elem_iq(stream, from_=peer, to=self, type='set')(
+ elem(ns.JINGLE, 'jingle', action='transport-info', sid=sid)(
+ elem('content', name='audio')(
+ elem(ns.GOOGLE_P2P, 'transport')(
+ elem('candidate', address='172.22.64.192', port='54337',
+ name='rtp', username='F7rgdWcCgH3Q/HgE',
+ password='ioh2IDwd3iZEZHzM', preference='1', protocol='udp',
+ generation='0', network='1', type='local')
+ )
+ )
+ )
+ )
+ )
+ q.expect('dbus-signal', signal='AddRemoteCandidate', path=stream1.object_path)
+
+ # video rtcp: note the weird name='' field which Gabble has to work around
+ stream.send(
+ elem_iq(stream, from_=peer, to=self, type='set')(
+ elem(ns.JINGLE, 'jingle', action='transport-info', sid=sid)(
+ elem('content', name='video')(
+ elem(ns.GOOGLE_P2P, 'transport')(
+ elem('candidate', address='172.22.64.192', port='54339',
+ name='video_rtcp', username='fnLduEIu6VHsSOqh',
+ password='IYeNu/HWzMpx2zrS', preference='1', protocol='udp',
+ generation='0', network='1', type='local')
+ )
+ )
+ )
+ )
+ )
+ q.expect('dbus-signal', signal='AddRemoteCandidate', path=stream2.object_path)
+
+ # video rtp: ditto
+ stream.send(
+ elem_iq(stream, from_=peer, to=self, type='set')(
+ elem(ns.JINGLE, 'jingle', action='transport-info', sid=sid)(
+ elem('content', name='video')(
+ elem(ns.GOOGLE_P2P, 'transport')(
+ elem('candidate', address='172.22.64.192', port='54341',
+ name='video_rtp', username='mZVBFdQ2LyAP6oyE',
+ password='3uoyCHP8zwE+/Ylw', preference='1', protocol='udp',
+ generation='0', network='1', type='local')
+ )
+ )
+ )
+ )
+ )
+ q.expect('dbus-signal', signal='AddRemoteCandidate', path=stream2.object_path)
+
+ # Test that we're sending with name='video_rtp' as well, but only for the video stream.
+ stream1.NewNativeCandidate("fake", jt.get_remote_transports_dbus())
+ e = q.expect('stream-iq', predicate=jp.action_predicate('transport-info'))
+ candidate = xpath.queryForNodes(
+ '/iq/jingle/content[@name="audio"]/transport/candidate',
+ e.stanza)[0]
+ assertEquals('rtp', candidate['name'])
+
+ stream2.NewNativeCandidate("fake", jt.get_remote_transports_dbus())
+ e = q.expect('stream-iq', predicate=jp.action_predicate('transport-info'))
+ candidate = xpath.queryForNodes(
+ '/iq/jingle/content[@name="video"]/transport/candidate',
+ e.stanza)[0]
+ assertEquals('video_rtp', candidate['name'])
+
+if __name__ == '__main__':
+ exec_test(test)