def test(q, bus, conn, stream): # This sidecar sends a stanza, and waits for a reply, before being # created. pattern = EventPattern('stream-iq', to='sidecar.example.com', query_ns='http://example.com/sidecar') call_async(q, conn.Sidecars1, 'EnsureSidecar', TEST_PLUGIN_IFACE + ".IQ") e = q.expect_many(pattern)[0] # The server said yes, so we should get a sidecar back! acknowledge_iq(stream, e.stanza) q.expect('dbus-return', method='EnsureSidecar') identities = ["test/app-list//Test"] features = ["com.example.test1", "com.example.test2"] ver = compute_caps_hash(identities, features, {}) iq = IQ(stream, "get") query = iq.addElement((ns.DISCO_INFO, 'query')) query['node'] = ns.GABBLE_CAPS + '#' + ver stream.send(iq) e = q.expect('stream-iq', query_ns='http://jabber.org/protocol/disco#info') returned_features = [feature['var'] for feature in xpath.queryForNodes('/iq/query/feature', e.stanza)] assertEquals(features, returned_features) returned_identities = [identity['category'] + "/" + identity['type']+"//" + identity['name'] for identity in xpath.queryForNodes('/iq/query/identity', e.stanza)] assertEquals(identities, returned_identities) new_ver = compute_caps_hash(returned_identities, returned_features, {}) assertEquals(new_ver, ver)
def _cb_disco_iq(self, iq): nodes = xpath.queryForNodes("/iq/query", iq) query = nodes[0] if query.getAttribute('node') is None: return node = query.attributes['node'] ver = node.replace("http://telepathy.freedesktop.org/caps#", "") if iq.getAttribute('type') == 'result': if FileTransferTest.caps_identities is None or \ FileTransferTest.caps_features is None: identity_nodes = xpath.queryForNodes('/iq/query/identity', iq) assertLength(1, identity_nodes) identity_node = identity_nodes[0] identity_category = identity_node['category'] identity_type = identity_node['type'] identity_name = identity_node['name'] identity = '%s/%s//%s' % (identity_category, identity_type, identity_name) FileTransferTest.caps_identities = [identity] FileTransferTest.caps_features = [] for feature in xpath.queryForNodes('/iq/query/feature', iq): FileTransferTest.caps_features.append(feature['var']) # Check if the hash matches the announced capabilities assertEquals(compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features, {}), ver) if ver == FileTransferTest.caps_ft: caps_share = compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features + \ [ns.GOOGLE_FEAT_SHARE], {}) n = query.attributes['node'].replace(ver, caps_share) query.attributes['node'] = n for feature in xpath.queryForNodes('/iq/query/feature', iq): query.children.remove(feature) for f in FileTransferTest.caps_features + [ns.GOOGLE_FEAT_SHARE]: el = domish.Element((None, 'feature')) el['var'] = f query.addChild(el) elif iq.getAttribute('type') == 'get': caps_share = compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features + \ [ns.GOOGLE_FEAT_SHARE], {}) if ver == caps_share: n = query.attributes['node'].replace(ver, FileTransferTest.caps_ft) query.attributes['node'] = n
def test_caps(q, conn, stream, contact, features, audio, video, google=False): caps['ver'] = compute_caps_hash([], features, {}) h = presence_and_disco(q, conn, stream, contact, True, client, caps, features) cflags = 0 call_expected_media_caps = [] if audio: cflags |= cs.MEDIA_CAP_AUDIO call_expected_media_caps.append(cs.CALL_INITIAL_AUDIO) call_expected_media_caps.append(cs.CALL_INITIAL_AUDIO_NAME) if video: cflags |= cs.MEDIA_CAP_VIDEO call_expected_media_caps.append(cs.CALL_INITIAL_VIDEO) call_expected_media_caps.append(cs.CALL_INITIAL_VIDEO_NAME) # If the contact can only do one of audio or video, or uses a Google # client, they'll have the ImmutableStreams cap. if cflags < (cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO) or google: cflags |= cs.MEDIA_CAP_IMMUTABLE_STREAMS else: call_expected_media_caps.append(cs.CALL_MUTABLE_CONTENTS) event, = q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged')) # Check Contact capabilities assertEquals(len(event.args), 1) assertEquals(event.args[0], get_contacts_capabilities_sync(conn, [h])) check_contact_caps(event.args[0][h], cs.CHANNEL_TYPE_CALL, call_expected_media_caps)
def test(q, bus, conn): # last value of the "ver" key we resolved. We use it to be sure that the # modified caps has already be announced. old_ver = None conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0, 0]) self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") self_handle_name = conn.Properties.Get(cs.CONN, "SelfID") AvahiListener(q).listen_for_service("_presence._tcp") e = q.expect('service-added', name = self_handle_name, protocol = avahi.PROTO_INET) service = e.service service.resolve() e = q.expect('service-resolved', service = service) ver = txt_get_key(e.txt, "ver") while ver == old_ver: # be sure that the announced caps actually changes e = q.expect('service-resolved', service=service) ver = txt_get_key(e.txt, "ver") old_ver = ver caps = compute_caps_hash(['client/pc//%s' % PACKAGE_STRING], fixed_features, {}) assertEquals(caps, ver) test_ft_caps_from_contact(q, bus, conn, service, 'yo@momma') test_ft_caps_to_contact(q, bus, conn, service)
def test(q, bus, conn): # last value of the "ver" key we resolved. We use it to be sure that the # modified caps has already be announced. old_ver = None conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0, 0]) self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") self_handle_name = conn.Properties.Get(cs.CONN, "SelfID") AvahiListener(q).listen_for_service("_presence._tcp") e = q.expect('service-added', name=self_handle_name, protocol=avahi.PROTO_INET) service = e.service service.resolve() e = q.expect('service-resolved', service=service) ver = txt_get_key(e.txt, "ver") while ver == old_ver: # be sure that the announced caps actually changes e = q.expect('service-resolved', service=service) ver = txt_get_key(e.txt, "ver") old_ver = ver caps = compute_caps_hash(['client/pc//%s' % PACKAGE_STRING], fixed_features, {}) assertEquals(caps, ver) test_ft_caps_from_contact(q, bus, conn, service, 'yo@momma') test_ft_caps_to_contact(q, bus, conn, service)
def _cb_presence_iq(self, stanza): nodes = xpath.queryForNodes("/presence/c", stanza) c = nodes[0] if 'share-v1' in c.getAttribute('ext'): assert FileTransferTest.caps_identities is not None and \ FileTransferTest.caps_features is not None and \ FileTransferTest.caps_dataforms is not None new_hash = compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features + \ [ns.GOOGLE_FEAT_SHARE], FileTransferTest.caps_dataforms) # Replace ver hash from one with file-transfer ns to one without FileTransferTest.caps_ft = c.attributes['ver'] c.attributes['ver'] = new_hash else: node = c.attributes['node'] ver = c.attributes['ver'] # ask for raw caps request = elem_iq(self.stream, 'get', from_='[email protected]/resource')(elem( ns.DISCO_INFO, 'query', node=(node + '#' + ver))) self.stream.send(request)
def receive_caps(q, conn, stream, contact, contact_handle, features, expected_caps, expect_disco=True, expect_ccc=True): caps = {'node': client, 'ver': compute_caps_hash([], features, {}), 'hash': 'sha-1'} presence_and_disco(q, conn, stream, contact, expect_disco, client, caps, features, initial=False) if expect_ccc: event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') announced_ccs, = event.args assertSameElements(expected_caps, announced_ccs[contact_handle]) else: # Make sure Gabble's got the caps sync_stream(q, stream) caps = get_contacts_capabilities_sync(conn, [contact_handle]) assertSameElements(expected_caps, caps[contact_handle]) # test again, to check GetContactCapabilities does not have side effect caps = get_contacts_capabilities_sync(conn, [contact_handle]) assertSameElements(expected_caps, caps[contact_handle]) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(caps[contact_handle], caps_via_contacts_iface)
def sign_in_a_cat(jid, identities, show=None): caps['ver'] = compute_caps_hash(identities, features, {}) presence_and_disco(q, conn, stream, jid, expect_disco, client, caps, features, identities=identities, initial=False, show=show) # Make sure Gabble's got the caps sync_stream(q, stream)
def sending_request_to_cappy_contact(q, bus, conn, stream, chan): """ Test that Gabble requests a receipt from a contact whom we know supports this extension, but only if asked. """ # Convince Gabble that Guybrush supports this extension caps = { 'node': 'http://whatever', 'ver': caps_helper.compute_caps_hash([], [ns.RECEIPTS], {}), 'hash': 'sha-1', } caps_helper.presence_and_disco(q, conn, stream, GUYBRUSH_FULL_JID, disco=True, client=caps['node'], caps=caps, features=[ns.RECEIPTS]) sync_stream(q, stream) # Don't ask, don't tell — even if we know Guybrush does support this. not_sending_request_to_contact(q, bus, conn, stream, chan) # Ask, tell. message = [ { 'message-type': cs.MT_NORMAL, }, { 'content-type': 'text/plain', 'content': 'Ulysses?', }] chan.Messages.SendMessage(message, cs.MSG_SENDING_FLAGS_REPORT_DELIVERY) e = q.expect('stream-message', to=GUYBRUSH) assertLength(1, list(e.stanza.elements(uri=ns.RECEIPTS, name='request')))
def handle_disco(q, stream, contact_jid, identity): # Gabble tries to resolve a caps hash. ver = compute_caps_hash([identity], features, {}) event = q.expect('stream-iq', to=contact_jid, query_ns=ns.DISCO_INFO) assertEquals(client + '#' + ver, event.query.attributes['node']) # The bare jid replies. send_disco_reply(stream, event.stanza, [identity], features)
def test(q, bus, conn): # last value of the "ver" key we resolved. We use it to be sure that the # modified caps has already be announced. old_ver = None conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L]) self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") self_handle_name = conn.Properties.Get(cs.CONN, "SelfID") AvahiListener(q).listen_for_service("_presence._tcp") e = q.expect('service-added', name=self_handle_name, protocol=avahi.PROTO_INET) service = e.service service.resolve() e = q.expect('service-resolved', service=service) ver = txt_get_key(e.txt, "ver") while ver == old_ver: # be sure that the announced caps actually changes e = q.expect('service-resolved', service=service) ver = txt_get_key(e.txt, "ver") old_ver = ver # We support OOB file transfer caps = compute_caps_hash(['client/pc//%s' % PACKAGE_STRING], fixed_features, {}) check_caps_txt(e.txt, caps) conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACT_CAPS) # Advertise nothing conn_caps_iface.UpdateCapabilities([]) service.resolve() e = q.expect('service-resolved', service=service) # Announced capa didn't change caps = compute_caps_hash(['client/pc//%s' % PACKAGE_STRING], fixed_features, {}) check_caps_txt(e.txt, caps)
def test(q, bus, conn): # last value of the "ver" key we resolved. We use it to be sure that the # modified caps has already be announced. old_ver = None conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L]) self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") self_handle_name = conn.Properties.Get(cs.CONN, "SelfID") AvahiListener(q).listen_for_service("_presence._tcp") e = q.expect('service-added', name = self_handle_name, protocol = avahi.PROTO_INET) service = e.service service.resolve() e = q.expect('service-resolved', service = service) ver = txt_get_key(e.txt, "ver") while ver == old_ver: # be sure that the announced caps actually changes e = q.expect('service-resolved', service=service) ver = txt_get_key(e.txt, "ver") old_ver = ver # We support OOB file transfer caps = compute_caps_hash(['client/pc//%s' % PACKAGE_STRING], fixed_features, {}) check_caps_txt(e.txt, caps) conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACT_CAPS) # Advertise nothing conn_caps_iface.UpdateCapabilities([]) service.resolve() e = q.expect('service-resolved', service = service) # Announced capa didn't change caps = compute_caps_hash(['client/pc//%s' % PACKAGE_STRING], fixed_features, {}) check_caps_txt(e.txt, caps)
def receive_caps(q, bus, conn, service, contact, contact_handle, features, expected_caps, expect_disco=True, expect_ccc=True): ver = compute_caps_hash([], features, {}) txt_record = { "txtvers": "1", "status": "avail", "node": client, "ver": ver, "hash": "sha-1"} listener, port = setup_stream_listener(q, contact) AvahiAnnouncer(contact, "_presence._tcp", port, txt_record) if expect_disco: # Salut looks up our capabilities e = q.expect('incoming-connection', listener=listener) stream = e.connection event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO, connection=stream) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver # send good reply result = make_result_iq(event.stanza) query = result.firstChildElement() query['node'] = client + '#' + ver for f in features: feature = query.addElement('feature') feature['var'] = f stream.send(result) if expect_ccc: event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') announced_ccs, = event.args assertSameElements(expected_caps, announced_ccs[contact_handle]) else: if expect_disco: # Make sure Salut's got the caps sync_stream(q, stream) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(expected_caps, caps_via_contacts_iface) # close the connection and expect a new one to be opened by Salut # the next time we need some discoing doing if expect_disco: stream.send('</stream:stream>') stream.transport.loseConnection() # pass some time so Salut knows the connection is lost and # won't try and send stuff down a closed connection on the # next test. sync_dbus(bus, q, conn)
def send_presence(q, stream, contact_jid, identity): ver = compute_caps_hash([identity], features, {}) stream.send( make_presence(contact_jid, status='Hello', caps={ 'node': client, 'hash': 'sha-1', 'ver': ver }))
def receive_caps(q, conn, stream, contact, contact_handle, features, expected_caps, expect_disco=True, expect_ccc=True): presence = make_presence(contact, status='hello') c = presence.addElement((ns.CAPS, 'c')) c['node'] = client c['ver'] = compute_caps_hash([], features if features is not None else [], {}) c['hash'] = 'sha-1' stream.send(presence) if expect_disco: # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + c['ver'] # send good reply result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query['node'] = client + '#' + c['ver'] for f in features: feature = query.addElement('feature') feature['var'] = f stream.send(result) if expect_ccc: event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') announced_ccs, = event.args assertSameElements(expected_caps, announced_ccs) else: # Make sure Gabble's got the caps sync_stream(q, stream) caps = get_contacts_capabilities_sync(conn, [contact_handle]) assertSameElements(expected_caps, caps) # test again, to check GetContactCapabilities does not have side effect caps = get_contacts_capabilities_sync(conn, [contact_handle]) assertSameElements(expected_caps, caps) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(caps[contact_handle], caps_via_contacts_iface)
def test_caps(q, conn, stream, contact, features, audio, video, google=False): caps['ver'] = compute_caps_hash ([], features, {}) h = presence_and_disco(q, conn, stream, contact, True, client, caps, features) cflags = 0 stream_expected_media_caps = [] call_expected_media_caps = [] if audio: cflags |= cs.MEDIA_CAP_AUDIO stream_expected_media_caps.append (cs.INITIAL_AUDIO) call_expected_media_caps.append (cs.CALL_INITIAL_AUDIO) call_expected_media_caps.append (cs.CALL_INITIAL_AUDIO_NAME) if video: cflags |= cs.MEDIA_CAP_VIDEO stream_expected_media_caps.append (cs.INITIAL_VIDEO) call_expected_media_caps.append (cs.CALL_INITIAL_VIDEO) call_expected_media_caps.append (cs.CALL_INITIAL_VIDEO_NAME) # If the contact can only do one of audio or video, or uses a Google # client, they'll have the ImmutableStreams cap. if cflags < (cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO) or google: cflags |= cs.MEDIA_CAP_IMMUTABLE_STREAMS stream_expected_media_caps.append(cs.IMMUTABLE_STREAMS) else: call_expected_media_caps.append(cs.CALL_MUTABLE_CONTENTS) _, event = q.expect_many( EventPattern('dbus-signal', signal='CapabilitiesChanged', args = [[ ( h, cs.CHANNEL_TYPE_STREAMED_MEDIA, 0, # old generic 3, # new generic (can create and receive these) 0, # old specific cflags ) ]] # new specific ), EventPattern('dbus-signal', signal='ContactCapabilitiesChanged') ) assertContains((h, cs.CHANNEL_TYPE_STREAMED_MEDIA, 3, cflags), conn.Capabilities.GetCapabilities([h])) # Check Contact capabilities for streamed media assertEquals(len(event.args), 1) assertEquals (event.args[0], conn.ContactCapabilities.GetContactCapabilities([h])) check_contact_caps (event.args[0][h], cs.CHANNEL_TYPE_STREAMED_MEDIA, stream_expected_media_caps) check_contact_caps (event.args[0][h], cs.CHANNEL_TYPE_CALL, call_expected_media_caps)
def announce_contact(self, name=CONTACT_NAME, metadata=True): client = 'http://telepathy.freedesktop.org/fake-client' features = [ns.IQ_OOB] if metadata: features += [ns.TP_FT_METADATA] ver = compute_caps_hash([], features, {}) txt_record = { "txtvers": "1", "status": "avail", "node": client, "ver": ver, "hash": "sha-1" } suffix = '@%s' % get_host_name() name += ('-' + os.path.splitext(os.path.basename(sys.argv[0]))[0]) self.contact_name = name + suffix if len(self.contact_name) > 63: allowed = 63 - len(suffix) self.contact_name = name[:allowed] + suffix self.listener, port = setup_stream_listener(self.q, self.contact_name) self.contact_service = AvahiAnnouncer(self.contact_name, "_presence._tcp", port, txt_record) self.handle = wait_for_contact_in_publish(self.q, self.bus, self.conn, self.contact_name) # expect salut to disco our caps e = self.q.expect('incoming-connection', listener=self.listener) stream = e.connection e = self.q.expect('stream-iq', to=self.contact_name, query_ns=ns.DISCO_INFO, connection=stream) assertEquals(client + '#' + ver, e.query['node']) send_disco_reply(stream, e.stanza, [], features) # lose the connection here to ensure connections are created # where necessary; I just wanted salut to know my caps. stream.send('</stream:stream>') # spend a bit of time in the main loop to ensure the last two # stanzas are actually received by salut before closing the # connection. sync_dbus(self.bus, self.q, self.conn) stream.transport.loseConnection()
def receive_caps(q, conn, stream, contact, contact_handle, features, expected_caps, expect_disco=True, expect_ccc=True): presence = make_presence(contact, status='hello') c = presence.addElement((ns.CAPS, 'c')) c['node'] = client c['ver'] = compute_caps_hash([], features if features is not None else [], {}) c['hash'] = 'sha-1' stream.send(presence) if expect_disco: # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + c['ver'] # send good reply result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query['node'] = client + '#' + c['ver'] for f in features: feature = query.addElement('feature') feature['var'] = f stream.send(result) if expect_ccc: event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') announced_ccs, = event.args assertSameElements(expected_caps, announced_ccs) else: # Make sure Gabble's got the caps sync_stream(q, stream) caps = conn.ContactCapabilities.GetContactCapabilities([contact_handle]) assertSameElements(expected_caps, caps) # test again, to check GetContactCapabilities does not have side effect caps = conn.ContactCapabilities.GetContactCapabilities([contact_handle]) assertSameElements(expected_caps, caps) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(caps[contact_handle], caps_via_contacts_iface)
def receive_caps(q, conn, stream, contact, contact_handle, features, expected_caps, expect_disco=True, expect_ccc=True): caps = { 'node': client, 'ver': compute_caps_hash([], features, {}), 'hash': 'sha-1' } presence_and_disco(q, conn, stream, contact, expect_disco, client, caps, features, initial=False) if expect_ccc: event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') announced_ccs, = event.args assertSameElements(expected_caps, announced_ccs[contact_handle]) else: # Make sure Gabble's got the caps sync_stream(q, stream) caps = get_contacts_capabilities_sync(conn, [contact_handle]) assertSameElements(expected_caps, caps[contact_handle]) # test again, to check GetContactCapabilities does not have side effect caps = get_contacts_capabilities_sync(conn, [contact_handle]) assertSameElements(expected_caps, caps[contact_handle]) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(caps[contact_handle], caps_via_contacts_iface)
def receive_presence_and_ask_caps(q, stream, service, contact_name): global old_ver event_avahi = q.expect('service-resolved', service=service) ver = txt_get_key(event_avahi.txt, "ver") while ver == old_ver: # be sure that the announced caps actually changes event_avahi = q.expect('service-resolved', service=service) ver = txt_get_key(event_avahi.txt, "ver") old_ver = ver hash = txt_get_key(event_avahi.txt, "hash") node = txt_get_key(event_avahi.txt, "node") assert hash == 'sha-1' # ask caps # FIXME: this only works because Salut ignores @to request = """ <iq from='""" + contact_name + """' id='receive-presence-and-ask-caps' to='[email protected]/resource' type='get'> <query xmlns='http://jabber.org/protocol/disco#info' node='""" + node + '#' + ver + """'/> </iq> """ stream.send(request) # receive caps event = q.expect('stream-iq', iq_type='result', to=contact_name, iq_id='receive-presence-and-ask-caps', query_ns='http://jabber.org/protocol/disco#info') caps_str = str(xpath.queryForNodes('/iq/query/feature', event.stanza)) features = [] for feature in xpath.queryForNodes('/iq/query/feature', event.stanza): features.append(feature['var']) # Check if the hash matches the announced capabilities assert ver == compute_caps_hash(['client/pc//%s' % PACKAGE_STRING], features, {}) assert ns.X_OOB in features assert ns.IQ_OOB in features
def announce_contact(self, name=CONTACT_NAME, metadata=True): client = 'http://telepathy.freedesktop.org/fake-client' features = [ns.IQ_OOB] if metadata: features += [ns.TP_FT_METADATA] ver = compute_caps_hash([], features, {}) txt_record = { "txtvers": "1", "status": "avail", "node": client, "ver": ver, "hash": "sha-1"} suffix = '@%s' % get_host_name() name += ('-' + os.path.splitext(os.path.basename(sys.argv[0]))[0]) self.contact_name = name + suffix if len(self.contact_name) > 63: allowed = 63 - len(suffix) self.contact_name = name[:allowed] + suffix self.listener, port = setup_stream_listener(self.q, self.contact_name) self.contact_service = AvahiAnnouncer(self.contact_name, "_presence._tcp", port, txt_record) self.handle = wait_for_contact_in_publish(self.q, self.bus, self.conn, self.contact_name) # expect salut to disco our caps e = self.q.expect('incoming-connection', listener=self.listener) stream = e.connection e = self.q.expect('stream-iq', to=self.contact_name, query_ns=ns.DISCO_INFO, connection=stream) assertEquals(client + '#' + ver, e.query['node']) send_disco_reply(stream, e.stanza, [], features) # lose the connection here to ensure connections are created # where necessary; I just wanted salut to know my caps. stream.send('</stream:stream>') # spend a bit of time in the main loop to ensure the last two # stanzas are actually received by salut before closing the # connection. sync_dbus(self.bus, self.q, self.conn) stream.transport.loseConnection()
def _cb_presence_iq(self, stanza): nodes = xpath.queryForNodes("/presence/c", stanza) c = nodes[0] if 'share-v1' in c.getAttribute('ext'): assert FileTransferTest.caps_identities is not None and \ FileTransferTest.caps_features is not None new_hash = compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features + \ [ns.GOOGLE_FEAT_SHARE], {}) # Replace ver hash from one with file-transfer ns to one without FileTransferTest.caps_ft = c.attributes['ver'] c.attributes['ver'] = new_hash else: node = c.attributes['node'] ver = c.attributes['ver'] # ask for raw caps request = elem_iq(self.stream, 'get', from_='[email protected]/resource')( elem(ns.DISCO_INFO, 'query', node=(node + '#' + ver))) self.stream.send(request)
def two_contacts_with_the_same_hash(q, bus, conn, stream, bare_jids): contact1 = '*****@*****.**' contact2 = '*****@*****.**' if not bare_jids: contact1 += '/lol' contact2 += '/whut' h1, h2 = conn.get_contact_handles_sync([contact1, contact2]) ver = compute_caps_hash(BANANAPHONE, features, {}) caps = { # Uniquify slightly with a stringified boolean ;-) 'node': '%s%s' % (client_base, bare_jids), 'ver': ver, 'hash': 'sha-1', } send_presence(q, conn, stream, contact1, caps) stanza = expect_disco(q, contact1, caps['node'], caps) send_presence(q, conn, stream, contact2, caps) q.forbid_events([ EventPattern('stream-iq', to=contact2, query_ns=ns.DISCO_INFO), ]) sync_stream(q, stream) send_disco_reply(stream, stanza, BANANAPHONE, features, {}) q.expect_many( EventPattern('dbus-signal', signal='ClientTypesUpdated', args=[h1, ['phone']]), # Gabble previously did not emit ClientTypesUpdated for anyone beside # the contact we sent the disco request to; so this second event would # never arrive. EventPattern('dbus-signal', signal='ClientTypesUpdated', args=[h2, ['phone']]), )
def test_caps(q, conn, stream, contact, features, audio, video, google=False): caps['ver'] = compute_caps_hash ([], features, {}) h = presence_and_disco(q, conn, stream, contact, True, client, caps, features) cflags = 0 call_expected_media_caps = [] if audio: cflags |= cs.MEDIA_CAP_AUDIO call_expected_media_caps.append (cs.CALL_INITIAL_AUDIO) call_expected_media_caps.append (cs.CALL_INITIAL_AUDIO_NAME) if video: cflags |= cs.MEDIA_CAP_VIDEO call_expected_media_caps.append (cs.CALL_INITIAL_VIDEO) call_expected_media_caps.append (cs.CALL_INITIAL_VIDEO_NAME) # If the contact can only do one of audio or video, or uses a Google # client, they'll have the ImmutableStreams cap. if cflags < (cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO) or google: cflags |= cs.MEDIA_CAP_IMMUTABLE_STREAMS else: call_expected_media_caps.append(cs.CALL_MUTABLE_CONTENTS) event, = q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged') ) # Check Contact capabilities assertEquals(len(event.args), 1) assertEquals (event.args[0], get_contacts_capabilities_sync(conn, [h])) check_contact_caps (event.args[0][h], cs.CHANNEL_TYPE_CALL, call_expected_media_caps)
def test_ft_caps_from_contact(q, bus, conn, client): conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACT_CAPS) conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS) # send presence with FT capa ver = compute_caps_hash([], [ns.IQ_OOB], {}) txt_record = { "txtvers": "1", "status": "avail", "node": client, "ver": ver, "hash": "sha-1"} contact_name = "test-caps-ft@" + get_host_name() listener, port = setup_stream_listener(q, contact_name) announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, txt_record) # this is the first presence, Salut connects to the contact e = q.expect('incoming-connection', listener = listener) incoming = e.connection # Salut looks up our capabilities event = q.expect('stream-iq', connection = incoming, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver, (query_node.attributes['node'], client, ver) contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0] # send good reply result = make_result_iq(event.stanza) query = result.firstChildElement() query['node'] = client + '#' + ver feature = query.addElement('feature') feature['var'] = ns.IQ_OOB incoming.send(result) # FT capa is announced e = q.expect('dbus-signal', signal='ContactCapabilitiesChanged', predicate=lambda e: contact_handle in e.args[0]) caps = e.args[0][contact_handle] assertContains(ft_caps, caps) # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities'] assert caps_via_contacts_iface == caps, caps_via_contacts_iface # check if Salut announces the OOB capa self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") self_handle_name = conn.Properties.Get(cs.CONN, "SelfID") AvahiListener(q).listen_for_service("_presence._tcp") e = q.expect('service-added', name = self_handle_name, protocol = avahi.PROTO_INET) service = e.service service.resolve() receive_presence_and_ask_caps(q, incoming, service, contact_name) # capa announced without FT ver = compute_caps_hash([], ["http://telepathy.freedesktop.org/xmpp/pony"], {}) txt_record = { "txtvers": "1", "status": "avail", "node": client, "ver": ver, "hash": "sha-1"} contact_name = "test-caps-ft2@" + get_host_name() listener, port = setup_stream_listener(q, contact_name) announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, txt_record) # this is the first presence, Salut connects to the contact e = q.expect('incoming-connection', listener = listener) incoming = e.connection # Salut looks up our capabilities event = q.expect('stream-iq', connection = incoming, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver, (query_node.attributes['node'], client, ver) contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0] # send good reply result = make_result_iq(event.stanza) query = result.firstChildElement() query['node'] = client + '#' + ver feature = query.addElement('feature') feature['var'] = "http://telepathy.freedesktop.org/xmpp/pony" incoming.send(result) # the FT capability is not announced e = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') caps = e.args[0][contact_handle] assertDoesNotContain(ft_caps, caps) # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities'] assert caps_via_contacts_iface == caps, caps_via_contacts_iface # no capabilites announced (assume FT is supported to insure interop) txt_record = { "txtvers": "1", "status": "avail"} contact_name = "test-caps-ft-no-capa2@" + get_host_name() contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0] listener, port = setup_stream_listener(q, contact_name) announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, txt_record) # FT capa is announced e = q.expect('dbus-signal', signal='ContactCapabilitiesChanged', predicate=lambda e: contact_handle in e.args[0].keys()) caps = e.args[0][contact_handle] assertContains(ft_caps, caps) # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities'] assert caps_via_contacts_iface == caps, caps_via_contacts_iface
def test_ft_caps_from_contact(q, bus, conn, stream, contact, contact_handle, client): conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACT_CAPS) conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS) # send presence with no FT cap presence = make_presence(contact, status='hello') c = presence.addElement((ns.CAPS, 'c')) c['node'] = client c['ver'] = compute_caps_hash([], [], {}) c['hash'] = 'sha-1' stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + c['ver'] # send good reply result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query['node'] = client + '#' + c['ver'] stream.send(result) # no change in ContactCapabilities, so no signal ContactCapabilitiesChanged sync_stream(q, stream) # no special capabilities basic_caps = dbus.Dictionary( {contact_handle: [(text_fixed_properties, text_allowed_properties)]}) caps = get_contacts_capabilities_sync(conn, [contact_handle]) assert caps == basic_caps, caps # test again, to check GetContactCapabilities does not have side effect caps = get_contacts_capabilities_sync(conn, [contact_handle]) assert caps == basic_caps, caps # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assert caps_via_contacts_iface == caps[contact_handle], \ caps_via_contacts_iface # send presence with ft capa presence = make_presence(contact, status='hello') c = presence.addElement((ns.CAPS, 'c')) c['node'] = client c['ver'] = compute_caps_hash([], [ns.FILE_TRANSFER], {}) c['hash'] = 'sha-1' stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + c['ver'] # send good reply result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query['node'] = client + '#' + c['ver'] feature = query.addElement('feature') feature['var'] = ns.FILE_TRANSFER stream.send(result) generic_tubes_caps = dbus.Dictionary({ contact_handle: [(text_fixed_properties, text_allowed_properties), (ft_fixed_properties, ft_allowed_properties)] }) event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') assert len(event.args) == 1 assert event.args[0] == generic_tubes_caps caps = get_contacts_capabilities_sync(conn, [contact_handle]) assert caps == generic_tubes_caps, caps # test again, to check GetContactCapabilities does not have side effect caps = get_contacts_capabilities_sync(conn, [contact_handle]) assert caps == generic_tubes_caps, caps # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assert caps_via_contacts_iface == caps[contact_handle], \ caps_via_contacts_iface
def test(q, bus, conn, stream): caps = { 'node': client, 'ver': '0.1', } update_contact_caps(q, conn, stream, '[email protected]/Foo', caps) update_contact_caps(q, conn, stream, '[email protected]/Foo', caps) # Meredith signs in from one resource. update_contact_caps(q, conn, stream, '[email protected]/One', caps) # Meredith signs in from another resource with the same client. We don't # need to disco her, even though we don't trust this caps node in general # yet, because she's already told us what it means. meredith_two = '[email protected]/Two' q.forbid_events([ EventPattern('stream-iq', to=meredith_two, query_ns=ns.DISCO_INFO) ]) stream.send(make_presence(meredith_two, 'hello', caps=caps)) sync_stream(q, stream) # Jens signs in from one resource, which is slow to answer the disco query. jens_one = '[email protected]/One' j = send_presence(q, conn, stream, jens_one, caps) j_stanza = expect_disco(q, jens_one, client, caps) # Jens now signs in elsewhere with the same client; we disco it (maybe # it'll reply sooner? Maybe his first client's network connection went away # and the server hasn't noticed yet?) and it replies immediately. update_contact_caps (q, conn, stream, '[email protected]/Two', caps, initial=False) # Jens' first client replies. We don't expect any caps changes here, and # this shouldn't count as a second point towards the five we need to trust # this caps node. send_disco_reply(stream, j_stanza, [], features) check_caps (conn, j) update_contact_caps (q, conn, stream, '[email protected]/Foo', caps) # Now five distinct contacts have told us what this caps node means, we # trust it. update_contact_caps (q, conn, stream, '[email protected]/Foo', caps, disco = False) update_contact_caps (q, conn, stream, '[email protected]/Foo', caps, disco = False) caps = { 'node': client, 'ver': compute_caps_hash([], features, fake_client_dataforms), 'hash': 'sha-1', } update_contact_caps(q, conn, stream, '[email protected]/Foo', caps, dataforms = fake_client_dataforms) # We can verify the reply for these caps against the hash, and thus never # need to disco it again. update_contact_caps(q, conn, stream, '[email protected]/Foo', caps, disco = False, dataforms = fake_client_dataforms) update_contact_caps(q, conn, stream, '[email protected]/Foo', caps, disco = False, dataforms = fake_client_dataforms)
def test(q, bus, conn, stream): client = 'http://example.com/perverse-client' contact_bare_jid = '*****@*****.**' contact_with_resource = '[email protected]/hi' contact_handle = conn.get_contact_handle_sync(contact_bare_jid) # Gabble gets a presence stanza from a bare JID, which is a tad surprising. features = [ ns.JINGLE_015, ns.JINGLE_015_AUDIO, ns.JINGLE_015_VIDEO, ns.GOOGLE_P2P, ] caps = { 'node': client, 'hash': 'sha-1', 'ver': compute_caps_hash([], features, {}), } p = make_presence(contact_bare_jid, status='Hello', caps=caps) stream.send(p) # Gabble looks up the hash event = q.expect('stream-iq', to=contact_bare_jid, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assertEquals(client + '#' + caps['ver'], query_node.attributes['node']) # The bare jid replies send_disco_reply(stream, event.stanza, [], features) # Gabble lets us know their caps have changed. (Gabble used to ignore the # reply.) cc, = q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), ) assert_rccs_callable(cc.args[0][contact_handle]) # Gabble gets another presence stanza from the bare JID, with different # caps. features.append(ns.TUBES) caps = { 'node': client, 'hash': 'sha-1', 'ver': compute_caps_hash([], features, {}), } p = make_presence(contact_bare_jid, status='Get out the abacus', caps=caps) stream.send(p) # Gabble looks up the new hash disco2 = q.expect('stream-iq', to=contact_bare_jid, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', disco2.stanza)[0] assertEquals(client + '#' + caps['ver'], query_node.attributes['node']) # This time, before the bare JID replies, Gabble gets a presence from the # resourceful jid. features_ = features + [ns.CHAT_STATES] caps = { 'node': client, 'hash': 'sha-1', 'ver': compute_caps_hash([], features_, {}), } p = make_presence(contact_with_resource, status='Count this', caps=caps) stream.send(p) # Gabble throws away presence from the bare JID when it gets presence from # a resource (and vice versa), so it should now say the contact is # incapable. Gabble also looks up the resourceful JID's hash. cc, disco3 = q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), EventPattern('stream-iq', to=contact_with_resource, query_ns='http://jabber.org/protocol/disco#info'), ) assert_rccs_not_callable(cc.args[0][contact_handle]) query_node = xpath.queryForNodes('/iq/query', disco3.stanza)[0] assertEquals(client + '#' + caps['ver'], query_node.attributes['node']) # The bare jid replies! Getting a disco reply from a bare JID when we've # got presence from resources used to crash Gabble, but now it just ignores # it. send_disco_reply(stream, disco2.stanza, [], features) # Now the resourceful JID replies: send_disco_reply(stream, disco3.stanza, [], features_) # Gabble should announce that the contact has acquired some caps. cc, = q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), ) assert_rccs_callable(cc.args[0][contact_handle])
def test_hash(q, bus, conn, stream, contact, contact_handle, client): global caps_changed_flag presence = make_presence(contact, status='hello') stream.send(presence) q.expect_many( EventPattern('dbus-signal', signal='PresenceUpdate', args=[{contact_handle: (0L, {u'available': {'message': 'hello'}})}]), EventPattern('dbus-signal', signal='PresencesChanged', args=[{contact_handle: (2, u'available', 'hello')}])) # no special capabilities basic_caps = [(contact_handle, cs.CHANNEL_TYPE_TEXT, 3, 0)] assert conn.Capabilities.GetCapabilities([contact_handle]) == basic_caps # send updated presence with Jingle caps info presence = make_presence(contact, status='hello', caps={'node': client, 'ver': '0.1', }) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + '0.1' # send good reply stream.send(make_caps_disco_reply(stream, event.stanza, jingle_av_features)) # we can now do audio calls event = q.expect('dbus-signal', signal='CapabilitiesChanged') caps_changed_flag = False # send bogus presence caps = { 'node': client, 'ver': 'ceci=nest=pas=un=hash', 'hash': 'sha-1', } presence = make_presence(contact, status='hello', caps=caps) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + caps['ver'] # send bogus reply stream.send(make_caps_disco_reply(stream, event.stanza, ['http://jabber.org/protocol/bogus-feature'])) # don't receive any D-Bus signal sync_dbus(bus, q, conn) sync_stream(q, stream) assert caps_changed_flag == False # send presence with empty caps presence = make_presence(contact, status='hello', caps={'node': client, 'ver': '0.0', }) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + '0.0' # still don't receive any D-Bus signal sync_dbus(bus, q, conn) assert caps_changed_flag == False # send good reply result = make_result_iq(stream, event.stanza) query = result.firstChildElement() stream.send(result) # we can now do nothing event = q.expect('dbus-signal', signal='CapabilitiesChanged') assert caps_changed_flag == True caps_changed_flag = False # send correct presence ver = compute_caps_hash([], jingle_av_features, fake_client_dataforms) caps = { 'node': client, 'ver': ver, 'hash': 'sha-1', } presence = make_presence(contact, status='hello', caps=caps) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + caps['ver'] # don't receive any D-Bus signal sync_dbus(bus, q, conn) assert caps_changed_flag == False # send good reply result = make_caps_disco_reply(stream, event.stanza, jingle_av_features, fake_client_dataforms) stream.send(result) # we can now do audio calls event = q.expect('dbus-signal', signal='CapabilitiesChanged', ) assert caps_changed_flag == True caps_changed_flag = False
def test_hash(q, bus, conn, stream, contact, contact_handle, client): presence = make_presence(contact, status='hello') stream.send(presence) q.expect('dbus-signal', signal='PresencesChanged', args=[{contact_handle: (2, u'available', 'hello')}]) # no special capabilities for rcc in get_contacts_capabilities_sync(conn, [contact_handle])[contact_handle]: assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE)) # send updated presence with Jingle caps info presence = make_presence(contact, status='hello', caps={'node': client, 'ver': '0.1', }) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + '0.1' # send good reply send_disco_reply(stream, event.stanza, [], jingle_av_features) # we can now do audio calls cc, = q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), ) assert_rccs_callable(cc.args[0][contact_handle]) assertEquals(cc.args[0], get_contacts_capabilities_sync(conn, [contact_handle])) # Send presence without any capabilities. XEP-0115 §8.4 Caps Optimization # says “receivers of presence notifications MUST NOT expect an annotation # on every presence notification they receive”, so the contact should still # be media-capable afterwards. stream.send(make_presence(contact, status='very capable')) q.expect('dbus-signal', signal='PresencesChanged', args=[{contact_handle: (2, u'available', 'very capable')}]) # still exactly the same capabilities assertEquals(cc.args[0], get_contacts_capabilities_sync(conn, [contact_handle])) # send bogus presence caps = { 'node': client, 'ver': 'ceci=nest=pas=un=hash', 'hash': 'sha-1', } presence = make_presence(contact, status='hello', caps=caps) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + caps['ver'] # send bogus reply send_disco_reply(stream, event.stanza, [], ['http://jabber.org/protocol/bogus-feature']) # don't receive any D-Bus signal forbidden = [ EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), ] q.forbid_events(forbidden) sync_dbus(bus, q, conn) sync_stream(q, stream) # send presence with empty caps presence = make_presence(contact, status='hello', caps={'node': client, 'ver': '0.0', }) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + '0.0' # still don't receive any D-Bus signal sync_dbus(bus, q, conn) # send good reply q.unforbid_events(forbidden) result = make_result_iq(stream, event.stanza) query = result.firstChildElement() stream.send(result) # we can now do nothing cc, = q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), ) for rcc in cc.args[0][contact_handle]: assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE)) assert_rccs_not_callable(cc.args[0][contact_handle]) assertEquals(cc.args[0], get_contacts_capabilities_sync(conn, [contact_handle])) # send correct presence ver = compute_caps_hash(some_identities, jingle_av_features, fake_client_dataforms) caps = { 'node': client, 'ver': ver, 'hash': 'sha-1', } presence = make_presence(contact, status='hello', caps=caps) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + caps['ver'] # don't receive any D-Bus signal q.forbid_events(forbidden) sync_dbus(bus, q, conn) q.unforbid_events(forbidden) # send good reply send_disco_reply( stream, event.stanza, some_identities, jingle_av_features, fake_client_dataforms) # we can now do audio calls cc, = q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), ) assert_rccs_callable(cc.args[0][contact_handle]) assertEquals(cc.args[0], get_contacts_capabilities_sync(conn, [contact_handle]))
def test_two_clients(q, bus, conn, stream, contact1, contact2, contact_handle1, contact_handle2, client, broken_hash): presence = make_presence(contact1, status='hello') stream.send(presence) q.expect('dbus-signal', signal='PresencesChanged', args=[{contact_handle1: (2, u'available', 'hello')}]) presence = make_presence(contact2, status='hello') stream.send(presence) q.expect('dbus-signal', signal='PresencesChanged', args=[{contact_handle2: (2, u'available', 'hello')}]) # no special capabilities for h in (contact_handle1, contact_handle2): for rcc in get_contacts_capabilities_sync(conn, [h])[h]: assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE)) # send updated presence with Jingle caps info ver = compute_caps_hash(some_identities, jingle_av_features, {}) caps = { 'node': client, 'ver': ver, 'hash': 'sha-1', } presence = make_presence(contact1, status='hello', caps=caps) stream.send(presence) presence = make_presence(contact2, status='hello', caps=caps) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact1, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver # don't receive any D-Bus signal forbidden = [ EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), ] q.forbid_events(forbidden) sync_dbus(bus, q, conn) q.unforbid_events(forbidden) result = make_caps_disco_reply( stream, event.stanza, some_identities, jingle_av_features) if broken_hash: # make the hash break! query = result.firstChildElement() query.addElement('feature')['var'] = 'http://example.com/another-feature' stream.send(result) if broken_hash: # Gabble looks up our capabilities again because the first contact # failed to provide a valid hash event = q.expect('stream-iq', to=contact2, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver # don't receive any D-Bus signal q.forbid_events(forbidden) sync_dbus(bus, q, conn) q.unforbid_events(forbidden) # send good reply send_disco_reply(stream, event.stanza, some_identities, jingle_av_features) # we can now do audio calls cc, = q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', predicate=lambda e: contact_handle2 in e.args[0]), ) assert_rccs_callable(cc.args[0][contact_handle2]) if not broken_hash: # if the first contact failed to provide a good hash, it does not # deserve its capabilities to be understood by Gabble! cc, = q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', predicate=lambda e: contact_handle1 in e.args[0]), ) assert_rccs_callable(cc.args[0][contact_handle1]) # don't receive any further signals q.forbid_events(forbidden) sync_dbus(bus, q, conn) q.unforbid_events(forbidden)
def receive_caps(q, bus, conn, service, contact, contact_handle, features, expected_caps, expect_disco=True, expect_ccc=True): ver = compute_caps_hash([], features, {}) txt_record = { "txtvers": "1", "status": "avail", "node": client, "ver": ver, "hash": "sha-1" } listener, port = setup_stream_listener(q, contact) AvahiAnnouncer(contact, "_presence._tcp", port, txt_record) if expect_disco: # Salut looks up our capabilities e = q.expect('incoming-connection', listener=listener) stream = e.connection event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO, connection=stream) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver # send good reply result = make_result_iq(event.stanza) query = result.firstChildElement() query['node'] = client + '#' + ver for f in features: feature = query.addElement('feature') feature['var'] = f stream.send(result) if expect_ccc: event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') announced_ccs, = event.args assertSameElements(expected_caps, announced_ccs[contact_handle]) else: if expect_disco: # Make sure Salut's got the caps sync_stream(q, stream) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(expected_caps, caps_via_contacts_iface) # close the connection and expect a new one to be opened by Salut # the next time we need some discoing doing if expect_disco: stream.send('</stream:stream>') stream.transport.loseConnection() # pass some time so Salut knows the connection is lost and # won't try and send stuff down a closed connection on the # next test. sync_dbus(bus, q, conn)
def test_ft_caps_from_contact(q, bus, conn, stream, contact, contact_handle, client): global run run += 1 conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACT_CAPS) conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS) # send presence with no FT cap presence = make_presence(contact, status='hello') c = presence.addElement((ns.CAPS, 'c')) c['node'] = client c['ver'] = compute_caps_hash(['client/pc//jingleshareutils-%d' % run], [], {}) c['ext'] = "" stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + c['ver'] # send good reply result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query['node'] = client + '#' + c['ver'] stream.send(result) # no change in ContactCapabilities, so no signal ContactCapabilitiesChanged sync_stream(q, stream) # no special capabilities basic_caps = dbus.Dictionary({contact_handle: [(text_fixed_properties, text_allowed_properties)]}) caps = conn_caps_iface.GetContactCapabilities([contact_handle]) assert caps == basic_caps, caps # test again, to check GetContactCapabilities does not have side effect caps = conn_caps_iface.GetContactCapabilities([contact_handle]) assert caps == basic_caps, caps # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assert caps_via_contacts_iface == caps[contact_handle], \ caps_via_contacts_iface # send presence with ft capa presence = make_presence(contact, status='hello') c = presence.addElement((ns.CAPS, 'c')) c['node'] = client c['ext'] = "share-v1" c['ver'] = compute_caps_hash([], [], {}) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + c['ext'] # send good reply result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query['node'] = client + '#' + c['ext'] feature = query.addElement('feature') feature['var'] = ns.GOOGLE_FEAT_SHARE stream.send(result) generic_ft_caps = dbus.Dictionary({contact_handle: [(text_fixed_properties, text_allowed_properties), (ft_fixed_properties, ft_allowed_properties)]}) event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') assert len(event.args) == 1 assert event.args[0] == generic_ft_caps caps = conn_caps_iface.GetContactCapabilities([contact_handle]) assert caps == generic_ft_caps, caps # test again, to check GetContactCapabilities does not have side effect caps = conn_caps_iface.GetContactCapabilities([contact_handle]) assert caps == generic_ft_caps, caps # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assert caps_via_contacts_iface == caps[contact_handle], \ caps_via_contacts_iface
def send_presence(q, stream, contact_jid, identity): ver = compute_caps_hash([identity], features, {}) stream.send(make_presence(contact_jid, status='Hello', caps={'node': client, 'hash': 'sha-1', 'ver': ver}))
def test_ft_caps_from_contact(q, bus, conn, client): conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACT_CAPS) conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS) # send presence with FT capa ver = compute_caps_hash([], [ns.IQ_OOB], {}) txt_record = { "txtvers": "1", "status": "avail", "node": client, "ver": ver, "hash": "sha-1" } contact_name = "test-caps-ft@" + get_host_name() listener, port = setup_stream_listener(q, contact_name) announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, txt_record) # this is the first presence, Salut connects to the contact e = q.expect('incoming-connection', listener=listener) incoming = e.connection # Salut looks up our capabilities event = q.expect('stream-iq', connection=incoming, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver, (query_node.attributes['node'], client, ver) contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0] # send good reply result = make_result_iq(event.stanza) query = result.firstChildElement() query['node'] = client + '#' + ver feature = query.addElement('feature') feature['var'] = ns.IQ_OOB incoming.send(result) # FT capa is announced e = q.expect('dbus-signal', signal='ContactCapabilitiesChanged', predicate=lambda e: contact_handle in e.args[0]) caps = e.args[0][contact_handle] assertContains(ft_caps, caps) # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities'] assert caps_via_contacts_iface == caps, caps_via_contacts_iface # check if Salut announces the OOB capa self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") self_handle_name = conn.Properties.Get(cs.CONN, "SelfID") AvahiListener(q).listen_for_service("_presence._tcp") e = q.expect('service-added', name=self_handle_name, protocol=avahi.PROTO_INET) service = e.service service.resolve() receive_presence_and_ask_caps(q, incoming, service, contact_name) # capa announced without FT ver = compute_caps_hash([], ["http://telepathy.freedesktop.org/xmpp/pony"], {}) txt_record = { "txtvers": "1", "status": "avail", "node": client, "ver": ver, "hash": "sha-1" } contact_name = "test-caps-ft2@" + get_host_name() listener, port = setup_stream_listener(q, contact_name) announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, txt_record) # this is the first presence, Salut connects to the contact e = q.expect('incoming-connection', listener=listener) incoming = e.connection # Salut looks up our capabilities event = q.expect('stream-iq', connection=incoming, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver, (query_node.attributes['node'], client, ver) contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0] # send good reply result = make_result_iq(event.stanza) query = result.firstChildElement() query['node'] = client + '#' + ver feature = query.addElement('feature') feature['var'] = "http://telepathy.freedesktop.org/xmpp/pony" incoming.send(result) # the FT capability is not announced e = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') caps = e.args[0][contact_handle] assertDoesNotContain(ft_caps, caps) # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities'] assert caps_via_contacts_iface == caps, caps_via_contacts_iface # no capabilites announced (assume FT is supported to insure interop) txt_record = {"txtvers": "1", "status": "avail"} contact_name = "test-caps-ft-no-capa2@" + get_host_name() contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0] listener, port = setup_stream_listener(q, contact_name) announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, txt_record) # FT capa is announced e = q.expect('dbus-signal', signal='ContactCapabilitiesChanged', predicate=lambda e: contact_handle in e.args[0].keys()) caps = e.args[0][contact_handle] assertContains(ft_caps, caps) # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities'] assert caps_via_contacts_iface == caps, caps_via_contacts_iface
def _cb_disco_iq(self, iq): nodes = xpath.queryForNodes("/iq/query", iq) query = nodes[0] if query.getAttribute('node') is None: return node = query.attributes['node'] ver = node.replace("http://telepathy.freedesktop.org/caps#", "") if iq.getAttribute('type') == 'result': if FileTransferTest.caps_identities is None or \ FileTransferTest.caps_features is None or \ FileTransferTest.caps_dataforms is None: # create our own identity identity_nodes = xpath.queryForNodes('/iq/query/identity', iq) assertLength(1, identity_nodes) identity_node = identity_nodes[0] identity_category = identity_node['category'] identity_type = identity_node['type'] identity_name = identity_node['name'] identity = '%s/%s//%s' % (identity_category, identity_type, identity_name) _, features, dataforms = extract_disco_parts(iq) FileTransferTest.caps_identities = [identity] FileTransferTest.caps_features = features FileTransferTest.caps_dataforms = dataforms # Check if the hash matches the announced capabilities assertEquals(compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features, FileTransferTest.caps_dataforms), ver) if ver == FileTransferTest.caps_ft: caps_share = compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features + \ [ns.GOOGLE_FEAT_SHARE], FileTransferTest.caps_dataforms) n = query.attributes['node'].replace(ver, caps_share) query.attributes['node'] = n for feature in xpath.queryForNodes('/iq/query/feature', iq): query.children.remove(feature) for f in FileTransferTest.caps_features + [ns.GOOGLE_FEAT_SHARE]: el = domish.Element((None, 'feature')) el['var'] = f query.addChild(el) elif iq.getAttribute('type') == 'get': caps_share = compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features + \ [ns.GOOGLE_FEAT_SHARE], FileTransferTest.caps_dataforms) if ver == caps_share: n = query.attributes['node'].replace(ver, FileTransferTest.caps_ft) query.attributes['node'] = n
def test_two_clients(q, bus, conn, stream, contact1, contact2, contact_handle1, contact_handle2, client, broken_hash): global caps_changed_flag presence = make_presence(contact1, status='hello') stream.send(presence) event = q.expect_many( EventPattern('dbus-signal', signal='PresenceUpdate', args=[{contact_handle1: (0L, {u'available': {'message': 'hello'}})}]), EventPattern('dbus-signal', signal='PresencesChanged', args=[{contact_handle1: (2, u'available', 'hello')}])) presence = make_presence(contact2, status='hello') stream.send(presence) event = q.expect_many( EventPattern('dbus-signal', signal='PresenceUpdate', args=[{contact_handle2: (0L, {u'available': {'message': 'hello'}})}]), EventPattern('dbus-signal', signal='PresencesChanged', args=[{contact_handle2: (2, u'available', 'hello')}])) # no special capabilities basic_caps = [(contact_handle1, cs.CHANNEL_TYPE_TEXT, 3, 0)] assert conn.Capabilities.GetCapabilities([contact_handle1]) == basic_caps basic_caps = [(contact_handle2, cs.CHANNEL_TYPE_TEXT, 3, 0)] assert conn.Capabilities.GetCapabilities([contact_handle2]) == basic_caps # send updated presence with Jingle caps info ver = compute_caps_hash([], jingle_av_features, {}) caps = { 'node': client, 'ver': ver, 'hash': 'sha-1', } presence = make_presence(contact1, status='hello', caps=caps) stream.send(presence) presence = make_presence(contact2, status='hello', caps=caps) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact1, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver # don't receive any D-Bus signal sync_dbus(bus, q, conn) assert caps_changed_flag == False result = make_caps_disco_reply(stream, event.stanza, jingle_av_features) if broken_hash: # make the hash break! query = result.firstChildElement() query.addElement('feature')['var'] = 'http://example.com/another-feature' stream.send(result) if broken_hash: # Gabble looks up our capabilities again because the first contact # failed to provide a valid hash event = q.expect('stream-iq', to=contact2, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver # don't receive any D-Bus signal sync_dbus(bus, q, conn) assert caps_changed_flag == False # send good reply result = make_caps_disco_reply(stream, event.stanza, jingle_av_features) stream.send(result) # we can now do audio calls with both contacts event = q.expect('dbus-signal', signal='CapabilitiesChanged', args=[[(contact_handle2, cs.CHANNEL_TYPE_STREAMED_MEDIA, 0, 3, 0, cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO)]]) if not broken_hash: # if the first contact failed to provide a good hash, it does not # deserve its capabilities to be understood by Gabble! event = q.expect('dbus-signal', signal='CapabilitiesChanged', args=[[(contact_handle1, cs.CHANNEL_TYPE_STREAMED_MEDIA, 0, 3, 0, cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO)]]) caps_changed_flag = False # don't receive any D-Bus signal sync_dbus(bus, q, conn) assert caps_changed_flag == False
def test(q, bus, conn, stream): client = 'http://example.com/perverse-client' contact_bare_jid = '*****@*****.**' contact_with_resource = '[email protected]/hi' contact_handle = conn.RequestHandles(cs.HT_CONTACT, [contact_bare_jid])[0] # Gabble gets a presence stanza from a bare JID, which is a tad surprising. features = [ ns.JINGLE_015, ns.JINGLE_015_AUDIO, ns.JINGLE_015_VIDEO, ns.GOOGLE_P2P, ] caps = {'node': client, 'hash': 'sha-1', 'ver': compute_caps_hash([], features, {}), } p = make_presence(contact_bare_jid, status='Hello', caps=caps) stream.send(p) # Gabble looks up the hash event = q.expect('stream-iq', to=contact_bare_jid, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assertEquals(client + '#' + caps['ver'], query_node.attributes['node']) # The bare jid replies send_disco_reply(stream, event.stanza, [], features) # Gabble lets us know their caps have changed. (Gabble used to ignore the # reply.) streamed_media_caps = (contact_handle, cs.CHANNEL_TYPE_STREAMED_MEDIA, 0, 3, 0, cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO) e = q.expect('dbus-signal', signal='CapabilitiesChanged') assertContains(streamed_media_caps, e.args[0]) # Gabble gets another presence stanza from the bare JID, with different # caps. features.append(ns.TUBES) caps = {'node': client, 'hash': 'sha-1', 'ver': compute_caps_hash([], features, {}), } p = make_presence(contact_bare_jid, status='Get out the abacus', caps=caps) stream.send(p) # Gabble looks up the new hash disco2 = q.expect('stream-iq', to=contact_bare_jid, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', disco2.stanza)[0] assertEquals(client + '#' + caps['ver'], query_node.attributes['node']) # This time, before the bare JID replies, Gabble gets a presence from the # resourceful jid. features_ = features + [ns.CHAT_STATES] caps = {'node': client, 'hash': 'sha-1', 'ver': compute_caps_hash([], features_, {}), } p = make_presence(contact_with_resource, status='Count this', caps=caps) stream.send(p) # Gabble throws away presence from the bare JID when it gets presence from # a resource (and vice versa), so it should now say the contact is # incapable. Gabble also looks up the resourceful JID's hash. cc, disco3 = q.expect_many( EventPattern('dbus-signal', signal='CapabilitiesChanged'), EventPattern('stream-iq', to=contact_with_resource, query_ns='http://jabber.org/protocol/disco#info'), ) assertDoesNotContain(streamed_media_caps, cc.args[0]) query_node = xpath.queryForNodes('/iq/query', disco3.stanza)[0] assertEquals(client + '#' + caps['ver'], query_node.attributes['node']) # The bare jid replies! Getting a disco reply from a bare JID when we've # got presence from resources used to crash Gabble, but now it just ignores # it. send_disco_reply(stream, disco2.stanza, [], features) # Now the resourceful JID replies: send_disco_reply(stream, disco3.stanza, [], features_) # Gabble should announce that the contact has acquired some caps. e = q.expect('dbus-signal', signal='CapabilitiesChanged') assertContains(streamed_media_caps, e.args[0])
def test_hash(q, bus, conn, stream, contact, contact_handle, client): global caps_changed_flag presence = make_presence(contact, status='hello') stream.send(presence) q.expect('dbus-signal', signal='PresencesChanged', args=[{contact_handle: (2, u'available', 'hello')}]) # no special capabilities basic_caps = [(contact_handle, cs.CHANNEL_TYPE_TEXT, 3, 0)] assert conn.Capabilities.GetCapabilities([contact_handle]) == basic_caps # send updated presence with Jingle caps info presence = make_presence(contact, status='hello', caps={'node': client, 'ver': '0.1', }) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + '0.1' # send good reply send_disco_reply(stream, event.stanza, [], jingle_av_features) # we can now do audio calls event = q.expect('dbus-signal', signal='CapabilitiesChanged') caps_diff = event.args[0] media_diff = [c for c in caps_diff if c[1] == cs.CHANNEL_TYPE_STREAMED_MEDIA][0] assert media_diff[5] & cs.MEDIA_CAP_AUDIO, media_diff[5] caps_changed_flag = False # Send presence without any capabilities. XEP-0115 §8.4 Caps Optimization # says “receivers of presence notifications MUST NOT expect an annotation # on every presence notification they receive”, so the contact should still # be media-capable afterwards. stream.send(make_presence(contact, status='very capable')) q.expect('dbus-signal', signal='PresencesChanged', args=[{contact_handle: (2, u'available', 'very capable')}]) ye_olde_caps = conn.Capabilities.GetCapabilities([contact_handle]) assertLength(1, [c for c in ye_olde_caps if c[1] == cs.CHANNEL_TYPE_STREAMED_MEDIA and c[3] & cs.MEDIA_CAP_AUDIO]) # send bogus presence caps = { 'node': client, 'ver': 'ceci=nest=pas=un=hash', 'hash': 'sha-1', } presence = make_presence(contact, status='hello', caps=caps) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + caps['ver'] # send bogus reply send_disco_reply(stream, event.stanza, [], ['http://jabber.org/protocol/bogus-feature']) # don't receive any D-Bus signal sync_dbus(bus, q, conn) sync_stream(q, stream) assert caps_changed_flag == False # send presence with empty caps presence = make_presence(contact, status='hello', caps={'node': client, 'ver': '0.0', }) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + '0.0' # still don't receive any D-Bus signal sync_dbus(bus, q, conn) assert caps_changed_flag == False # send good reply result = make_result_iq(stream, event.stanza) query = result.firstChildElement() stream.send(result) # we can now do nothing event = q.expect('dbus-signal', signal='CapabilitiesChanged') assert caps_changed_flag == True caps_changed_flag = False # send correct presence ver = compute_caps_hash(some_identities, jingle_av_features, fake_client_dataforms) caps = { 'node': client, 'ver': ver, 'hash': 'sha-1', } presence = make_presence(contact, status='hello', caps=caps) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + caps['ver'] # don't receive any D-Bus signal sync_dbus(bus, q, conn) assert caps_changed_flag == False # send good reply send_disco_reply( stream, event.stanza, some_identities, jingle_av_features, fake_client_dataforms) # we can now do audio calls event = q.expect('dbus-signal', signal='CapabilitiesChanged', ) assert caps_changed_flag == True caps_changed_flag = False