def test(q, bus, conn, stream, incoming=True, too_slow=None): jt = jingletest.JingleTest(stream, 'test@localhost', '[email protected]/Foo') # If we need to override remote caps, feats, codecs or caps, # this is a good time to do it # Connecting conn.Connect() ji_event = q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]), EventPattern('stream-authenticated'), EventPattern('dbus-signal', signal='PresenceUpdate', args=[{1L: (0L, {u'available': {}})}]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]), # See: http://code.google.com/apis/talk/jep_extensions/jingleinfo.html EventPattern('stream-iq', query_ns='google:jingleinfo', to='test@localhost'), )[-1] listen_port = listen_http(q, 0) jingleinfo = make_result_iq(stream, ji_event.stanza) stun = jingleinfo.firstChildElement().addElement('stun') server = stun.addElement('server') server['host'] = 'resolves-to-1.2.3.4' server['udp'] = '12345' expected_stun_server = '1.2.3.4' expected_stun_port = 12345 # This bit is undocumented... but it has the same format as what we get # from Google Talk servers: # <iq to="censored" from="censored" id="73930208084" type="result"> # <query xmlns="google:jingleinfo"> # <stun> # <server host="stun.l.google.com" udp="19302"/> # <server host="stun4.l.google.com" udp="19302"/> # <server host="stun3.l.google.com" udp="19302"/> # <server host="stun1.l.google.com" udp="19302"/> # <server host="stun2.l.google.com" udp="19302"/> # </stun> # <relay> # <token>censored</token> # <server host="relay.google.com" udp="19295" tcp="19294" # tcpssl="443"/> # </relay> # </query> # </iq> relay = jingleinfo.firstChildElement().addElement('relay') relay.addElement('token', content='jingle all the way') server = relay.addElement('server') server['host'] = '127.0.0.1' server['udp'] = '11111' server['tcp'] = '22222' server['tcpssl'] = '443' # The special regression-test build of Gabble parses this attribute, # because we can't listen on port 80 server['gabble-test-http-port'] = str(listen_port.getHost().port) stream.send(jingleinfo) # We need remote end's presence for capabilities jt.send_remote_presence() # Gabble doesn't trust it, so makes a disco event = q.expect('stream-iq', query_ns='http://jabber.org/protocol/disco#info', to='[email protected]/Foo') jt.send_remote_disco_reply(event.stanza) # Force Gabble to process the capabilities sync_stream(q, stream) remote_handle = conn.RequestHandles(cs.HT_CONTACT, ["[email protected]/Foo"])[0] self_handle = conn.GetSelfHandle() req_pattern = EventPattern('http-request', method='GET', path='/create_session') if incoming: # Remote end calls us jt.incoming_call() # FIXME: these signals are not observable by real clients, since they # happen before NewChannels. # The caller is in members # We're pending because of remote_handle mc, _, e, req1, req2 = q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=[u'', [remote_handle], [], [], [], 0, 0]), EventPattern('dbus-signal', signal='MembersChanged', args=[u'', [], [], [self_handle], [], remote_handle, cs.GC_REASON_INVITED]), EventPattern('dbus-signal', signal='NewSessionHandler'), req_pattern, req_pattern) media_chan = make_channel_proxy(conn, mc.path, 'Channel.Interface.Group') media_iface = make_channel_proxy(conn, mc.path, 'Channel.Type.StreamedMedia') else: call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: remote_handle, }) ret, old_sig, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) path = ret.value[0] media_chan = make_channel_proxy(conn, path, 'Channel.Interface.Group') media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') call_async(q, media_iface, 'RequestStreams', remote_handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) e, req1, req2 = q.expect_many( EventPattern('dbus-signal', signal='NewSessionHandler'), req_pattern, req_pattern) # S-E gets notified about new session handler, and calls Ready on it assert e.args[1] == 'rtp' if too_slow is not None: test_too_slow(q, bus, conn, stream, req1, req2, media_chan, too_slow) return if incoming: assertLength(0, media_iface.ListStreams()) # Accept the call. media_chan.AddMembers([self_handle], '') # In response to the streams call, we now have two HTTP requests # (for RTP and RTCP) handle_request(req1.request, 0) handle_request(req2.request, 1) if incoming: # We accepted the call, and it should get a new, bidirectional stream # now that the relay info request has finished. This tests against a # regression of bug #24023. q.expect('dbus-signal', signal='StreamAdded', args=[1, remote_handle, cs.MEDIA_STREAM_TYPE_AUDIO]) q.expect('dbus-signal', signal='StreamDirectionChanged', args=[1, cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, 0]) else: # Now that we have the relay info, RequestStreams can return q.expect('dbus-return', method='RequestStreams') session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') # Exercise channel properties channel_props = media_chan.GetAll( cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assert channel_props['TargetHandle'] == remote_handle assert channel_props['TargetHandleType'] == cs.HT_CONTACT assert channel_props['TargetID'] == '*****@*****.**' assert channel_props['Requested'] == (not incoming) # The new API for STUN servers etc. sh_props = stream_handler.GetAll( cs.STREAM_HANDLER, dbus_interface=dbus.PROPERTIES_IFACE) assert sh_props['NATTraversal'] == 'gtalk-p2p' assert sh_props['CreatedLocally'] == (not incoming) assert sh_props['STUNServers'] == \ [(expected_stun_server, expected_stun_port)], \ sh_props['STUNServers'] credentials_used = {} credentials = {} for relay in sh_props['RelayInfo']: assert relay['ip'] == '127.0.0.1', sh_props['RelayInfo'] assert relay['type'] in ('udp', 'tcp', 'tls') assert relay['component'] in (1, 2) if relay['type'] == 'udp': assert relay['port'] == 11111, sh_props['RelayInfo'] elif relay['type'] == 'tcp': assert relay['port'] == 22222, sh_props['RelayInfo'] elif relay['type'] == 'tls': assert relay['port'] == 443, sh_props['RelayInfo'] assert relay['username'][:8] == 'UUUUUUUU', sh_props['RelayInfo'] assert relay['password'][:8] == 'PPPPPPPP', sh_props['RelayInfo'] assert relay['password'][8:] == relay['username'][8:], \ sh_props['RelayInfo'] assert (relay['password'][8:], relay['type']) not in credentials_used credentials_used[(relay['password'][8:], relay['type'])] = 1 credentials[(relay['component'], relay['type'])] = relay['password'][8:] assert (1, 'udp') in credentials assert (1, 'tcp') in credentials assert (1, 'tls') in credentials assert (2, 'udp') in credentials assert (2, 'tcp') in credentials assert (2, 'tls') in credentials assert ('0', 'udp') in credentials_used assert ('0', 'tcp') in credentials_used assert ('0', 'tls') in credentials_used assert ('1', 'udp') in credentials_used assert ('1', 'tcp') in credentials_used assert ('1', 'tls') in credentials_used # consistency check, since we currently reimplement Get separately for k in sh_props: assert sh_props[k] == stream_handler.Get( 'org.freedesktop.Telepathy.Media.StreamHandler', k, dbus_interface=dbus.PROPERTIES_IFACE), k media_chan.RemoveMembers([self_handle], '') if incoming: q.expect_many( EventPattern('stream-iq', predicate=lambda e: e.query is not None and e.query.name == 'jingle' and e.query['action'] == 'session-terminate'), EventPattern('dbus-signal', signal='Closed'), ) else: # We haven't sent a session-initiate, so we shouldn't expect to send a # session-terminate. q.expect('dbus-signal', signal='Closed')
def prepare(self): events = self.q.expect_many( EventPattern('stream-iq', query_ns=ns.GOOGLE_JINGLE_INFO), EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', query_ns=ns.ROSTER), ) CallTest.prepare(self, events=events) ji_event = events[0] listen_port = listen_http(self.q, 0) jingleinfo = make_result_iq(self.stream, ji_event.stanza) stun = jingleinfo.firstChildElement().addElement('stun') server = stun.addElement('server') server['host'] = 'resolves-to-1.2.3.4' server['udp'] = '12345' self.expected_stun_server = '1.2.3.4' self.expected_stun_port = 12345 # This bit is undocumented... but it has the same format as what we get # from Google Talk servers: # <iq to="censored" from="censored" id="73930208084" type="result"> # <query xmlns="google:jingleinfo"> # <stun> # <server host="stun.l.google.com" udp="19302"/> # <server host="stun4.l.google.com" udp="19302"/> # <server host="stun3.l.google.com" udp="19302"/> # <server host="stun1.l.google.com" udp="19302"/> # <server host="stun2.l.google.com" udp="19302"/> # </stun> # <relay> # <token>censored</token> # <server host="relay.google.com" udp="19295" tcp="19294" # tcpssl="443"/> # </relay> # </query> # </iq> relay = jingleinfo.firstChildElement().addElement('relay') relay.addElement('token', content='jingle all the way') server = relay.addElement('server') server['host'] = '127.0.0.1' server['udp'] = '11111' server['tcp'] = '22222' server['tcpssl'] = '443' # The special regression-test build of Gabble parses this attribute, # because we can't listen on port 80 server['gabble-test-http-port'] = str(listen_port.getHost().port) self.stream.send(jingleinfo) jingleinfo = None # Spoof some jingle info. This is a regression test for # <https://bugs.freedesktop.org/show_bug.cgi?id=34048>. We assert that # Gabble has ignored this stuff later. iq = IQ(self.stream, 'set') iq['from'] = "*****@*****.**" query = iq.addElement((ns.GOOGLE_JINGLE_INFO, "query")) stun = query.addElement('stun') server = stun.addElement('server') server['host'] = '6.6.6.6' server['udp'] = '6666' relay = query.addElement('relay') relay.addElement('token', content='mwohahahahaha') server = relay.addElement('server') server['host'] = '127.0.0.1' server['udp'] = '666' server['tcp'] = '999' server['tcpssl'] = '666' self.stream.send(iq) # Force Gabble to process the capabilities sync_stream(self.q, self.stream)
def test(q, bus, conn, stream, incoming=True, too_slow=None, use_call=False): jt = jingletest.JingleTest(stream, 'test@localhost', '[email protected]/Foo') if use_call: # wjt only updated just about enough of this test for Call to check for # one specific crash, not to verify that it all works... assert incoming assert too_slow in [TOO_SLOW_CLOSE, TOO_SLOW_DISCONNECT] # Tell Gabble we want to use Call. conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + ".CallHandler", [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.CALL_INITIAL_AUDIO: True}, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.CALL_INITIAL_VIDEO: True}, ], [ cs.CHANNEL_TYPE_CALL + '/gtalk-p2p', cs.CHANNEL_TYPE_CALL + '/ice-udp', cs.CHANNEL_TYPE_CALL + '/video/h264', ]), ]) # See: http://code.google.com/apis/talk/jep_extensions/jingleinfo.html ji_event = q.expect('stream-iq', query_ns='google:jingleinfo', to='test@localhost') # Regression test for a bug where Gabble would crash if it disconnected # before receiving a reply to the google:jingleinfo query. if too_slow == TOO_SLOW_DISCONNECT_IMMEDIATELY: disconnect_conn(q, conn, stream, []) return listen_port = listen_http(q, 0) jingleinfo = make_result_iq(stream, ji_event.stanza) stun = jingleinfo.firstChildElement().addElement('stun') server = stun.addElement('server') server['host'] = 'resolves-to-1.2.3.4' server['udp'] = '12345' expected_stun_server = '1.2.3.4' expected_stun_port = 12345 # This bit is undocumented... but it has the same format as what we get # from Google Talk servers: # <iq to="censored" from="censored" id="73930208084" type="result"> # <query xmlns="google:jingleinfo"> # <stun> # <server host="stun.l.google.com" udp="19302"/> # <server host="stun4.l.google.com" udp="19302"/> # <server host="stun3.l.google.com" udp="19302"/> # <server host="stun1.l.google.com" udp="19302"/> # <server host="stun2.l.google.com" udp="19302"/> # </stun> # <relay> # <token>censored</token> # <server host="relay.google.com" udp="19295" tcp="19294" # tcpssl="443"/> # </relay> # </query> # </iq> relay = jingleinfo.firstChildElement().addElement('relay') relay.addElement('token', content='jingle all the way') server = relay.addElement('server') server['host'] = '127.0.0.1' server['udp'] = '11111' server['tcp'] = '22222' server['tcpssl'] = '443' # The special regression-test build of Gabble parses this attribute, # because we can't listen on port 80 server['gabble-test-http-port'] = str(listen_port.getHost().port) stream.send(jingleinfo) jingleinfo = None # Spoof some jingle info. This is a regression test for # <https://bugs.freedesktop.org/show_bug.cgi?id=34048>. We assert that # Gabble has ignored this stuff later. iq = IQ(stream, 'set') iq['from'] = "*****@*****.**" query = iq.addElement((ns.GOOGLE_JINGLE_INFO, "query")) stun = query.addElement('stun') server = stun.addElement('server') server['host'] = '6.6.6.6' server['udp'] = '6666' relay = query.addElement('relay') relay.addElement('token', content='mwohahahahaha') server = relay.addElement('server') server['host'] = '127.0.0.1' server['udp'] = '666' server['tcp'] = '999' server['tcpssl'] = '666' stream.send(iq) # We need remote end's presence for capabilities jt.send_remote_presence() # Gabble doesn't trust it, so makes a disco event = q.expect('stream-iq', query_ns='http://jabber.org/protocol/disco#info', to='[email protected]/Foo') jt.send_remote_disco_reply(event.stanza) # Force Gabble to process the capabilities sync_stream(q, stream) remote_handle = conn.RequestHandles(cs.HT_CONTACT, ["[email protected]/Foo"])[0] self_handle = conn.GetSelfHandle() req_pattern = EventPattern('http-request', method='GET', path='/create_session') if incoming: # Remote end calls us jt.incoming_call() if use_call: def looks_like_a_call_to_me(event): channels, = event.args path, props = channels[0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_CALL new_channels = q.expect('dbus-signal', signal='NewChannels', predicate=looks_like_a_call_to_me) path = new_channels.args[0][0][0] media_chan = bus.get_object(conn.bus_name, path) else: # FIXME: these signals are not observable by real clients, since they # happen before NewChannels. # The caller is in members # We're pending because of remote_handle mc, _, e = q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=[u'', [remote_handle], [], [], [], 0, 0]), EventPattern('dbus-signal', signal='MembersChanged', args=[u'', [], [], [self_handle], [], remote_handle, cs.GC_REASON_INVITED]), EventPattern('dbus-signal', signal='NewSessionHandler')) media_chan = make_channel_proxy(conn, mc.path, 'Channel.Interface.Group') media_iface = make_channel_proxy(conn, mc.path, 'Channel.Type.StreamedMedia') else: call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: remote_handle, }) ret, old_sig, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewChannels', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args[0][0][1].values()), ) path = ret.value[0] media_chan = make_channel_proxy(conn, path, 'Channel.Interface.Group') media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') call_async(q, media_iface, 'RequestStreams', remote_handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) e = q.expect('dbus-signal', signal='NewSessionHandler') req1 = q.expect('http-request', method='GET', path='/create_session') req2 = q.expect('http-request', method='GET', path='/create_session') if too_slow is not None: test_too_slow(q, bus, conn, stream, req1, req2, media_chan, too_slow) return if incoming: assertLength(0, media_iface.ListStreams()) # Accept the call. media_chan.AddMembers([self_handle], '') # In response to the streams call, we now have two HTTP requests # (for RTP and RTCP) handle_request(req1.request, 0) handle_request(req2.request, 1) if incoming: # We accepted the call, and it should get a new, bidirectional stream # now that the relay info request has finished. This tests against a # regression of bug #24023. q.expect('dbus-signal', signal='StreamAdded', args=[1, remote_handle, cs.MEDIA_STREAM_TYPE_AUDIO]) q.expect('dbus-signal', signal='StreamDirectionChanged', args=[1, cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, 0]) else: # Now that we have the relay info, RequestStreams can return q.expect('dbus-return', method='RequestStreams') session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') # Exercise channel properties channel_props = media_chan.GetAll( cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assert channel_props['TargetHandle'] == remote_handle assert channel_props['TargetHandleType'] == cs.HT_CONTACT assert channel_props['TargetID'] == '*****@*****.**' assert channel_props['Requested'] == (not incoming) # The new API for STUN servers etc. sh_props = stream_handler.GetAll( cs.STREAM_HANDLER, dbus_interface=dbus.PROPERTIES_IFACE) assert sh_props['NATTraversal'] == 'gtalk-p2p' assert sh_props['CreatedLocally'] == (not incoming) # If Gabble has erroneously paid attention to the contact [email protected] who # sent us a google:jingleinfo stanza, this assertion will fail. assertEquals([(expected_stun_server, expected_stun_port)], sh_props['STUNServers']) credentials_used = {} credentials = {} for relay in sh_props['RelayInfo']: assert relay['ip'] == '127.0.0.1', sh_props['RelayInfo'] assert relay['type'] in ('udp', 'tcp', 'tls') assert relay['component'] in (1, 2) if relay['type'] == 'udp': assert relay['port'] == 11111, sh_props['RelayInfo'] elif relay['type'] == 'tcp': assert relay['port'] == 22222, sh_props['RelayInfo'] elif relay['type'] == 'tls': assert relay['port'] == 443, sh_props['RelayInfo'] assert relay['username'][:8] == 'UUUUUUUU', sh_props['RelayInfo'] assert relay['password'][:8] == 'PPPPPPPP', sh_props['RelayInfo'] assert relay['password'][8:] == relay['username'][8:], \ sh_props['RelayInfo'] assert (relay['password'][8:], relay['type']) not in credentials_used credentials_used[(relay['password'][8:], relay['type'])] = 1 credentials[(relay['component'], relay['type'])] = relay['password'][8:] assert (1, 'udp') in credentials assert (1, 'tcp') in credentials assert (1, 'tls') in credentials assert (2, 'udp') in credentials assert (2, 'tcp') in credentials assert (2, 'tls') in credentials assert ('0', 'udp') in credentials_used assert ('0', 'tcp') in credentials_used assert ('0', 'tls') in credentials_used assert ('1', 'udp') in credentials_used assert ('1', 'tcp') in credentials_used assert ('1', 'tls') in credentials_used # consistency check, since we currently reimplement Get separately for k in sh_props: assert sh_props[k] == stream_handler.Get( 'org.freedesktop.Telepathy.Media.StreamHandler', k, dbus_interface=dbus.PROPERTIES_IFACE), k media_chan.RemoveMembers([self_handle], '') if incoming: q.expect_many( EventPattern('stream-iq', predicate=lambda e: e.query is not None and e.query.name == 'jingle' and e.query['action'] == 'session-terminate'), EventPattern('dbus-signal', signal='Closed'), ) else: # We haven't sent a session-initiate, so we shouldn't expect to send a # session-terminate. q.expect('dbus-signal', signal='Closed')