def test(q, bus, conn, stream, bytestream_cls, access_control): disco_event = q.expect('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS) announce_socks5_proxy(q, stream, disco_event.stanza) t.check_conn_properties(q, conn) self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") alice_handle = conn.get_contact_handle_sync('alice@localhost') # send Alice's presence caps = { 'ext': '', 'ver': '0.0.0', 'node': 'http://example.com/fake-client0' } presence = make_presence('alice@localhost/Test', caps=caps) stream.send(presence) _, disco_event = q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', args = [{alice_handle: (2L, u'available', u'')}]), EventPattern('stream-iq', to='alice@localhost/Test', query_ns=ns.DISCO_INFO), ) # reply to disco query send_disco_reply(stream, disco_event.stanza, [], [ns.TUBES]) sync_stream(q, stream) offer_new_dbus_tube(q, bus, conn, stream, self_handle, alice_handle, bytestream_cls, access_control)
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 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 connect_and_announce_alice(q, bus, conn, stream): q.forbid_events(proxy_query_events) # Send Alice's presence caps = { 'ext': '', 'ver': '0.0.0', 'node': 'http://example.com/fake-client0' } presence = make_presence('alice@localhost/Test', caps=caps) stream.send(presence) disco_event = q.expect('stream-iq', to='alice@localhost/Test', query_ns=ns.DISCO_INFO) send_disco_reply( stream, disco_event.stanza, [], [ns.TUBES, ns.FILE_TRANSFER]) sync_stream(q, stream) q.unforbid_events(proxy_query_events)
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 connect_and_announce_alice(q, bus, conn, stream): q.forbid_events(proxy_query_events) # Send Alice's presence caps = { 'ext': '', 'ver': '0.0.0', 'node': 'http://example.com/fake-client0' } presence = make_presence('alice@localhost/Test', caps=caps) stream.send(presence) disco_event = q.expect('stream-iq', to='alice@localhost/Test', query_ns=ns.DISCO_INFO) send_disco_reply(stream, disco_event.stanza, [], [ns.TUBES, ns.FILE_TRANSFER]) sync_stream(q, stream) q.unforbid_events(proxy_query_events)
def test(q, bus, conn, stream, bytestream_cls, access_control): disco_event = q.expect('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS) announce_socks5_proxy(q, stream, disco_event.stanza) t.check_conn_properties(q, conn) self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") alice_handle = conn.get_contact_handle_sync('alice@localhost') # send Alice's presence caps = { 'ext': '', 'ver': '0.0.0', 'node': 'http://example.com/fake-client0' } presence = make_presence('alice@localhost/Test', caps=caps) stream.send(presence) _, disco_event = q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', args=[{ alice_handle: (2L, u'available', u'') }]), EventPattern('stream-iq', to='alice@localhost/Test', query_ns=ns.DISCO_INFO), ) # reply to disco query send_disco_reply(stream, disco_event.stanza, [], [ns.TUBES]) sync_stream(q, stream) offer_new_dbus_tube(q, bus, conn, stream, self_handle, alice_handle, bytestream_cls, access_control)
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(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(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): # check all these types appear as they should contact_online(q, conn, stream, '[email protected]/lol', BOT) contact_online(q, conn, stream, '[email protected]/lol', CONSOLE) contact_online(q, conn, stream, '[email protected]/lol', GAME) contact_online(q, conn, stream, '[email protected]/lol', HANDHELD) contact_online(q, conn, stream, '[email protected]/lol', PC) contact_online(q, conn, stream, '[email protected]/lol', PHONE) contact_online(q, conn, stream, '[email protected]/lol', WEB) contact_online(q, conn, stream, '[email protected]/lol', SMS) meredith_one = '[email protected]/One' meredith_two = '[email protected]/Two' meredith_three = '[email protected]/Three' meredith_handle = conn.get_contact_handle_sync(meredith_one) # Meredith signs in from one resource contact_online(q, conn, stream, meredith_one, PC, show='chat') # * One: chat: pc # ClientTypes should be: ['pc'] # Meredith signs in from another resource contact_online(q, conn, stream, meredith_two, PHONE, show='dnd', initial=False) # * One: chat: pc # * Two: dnd: phone # ClientTypes should be: ['pc'] # check we're still a PC types = get_client_types(conn, meredith_handle) assertLength(1, types) assertEquals('pc', types[0]) types = conn.RequestClientTypes(meredith_handle, dbus_interface=cs.CONN_IFACE_CLIENT_TYPES) assertLength(1, types) assertEquals('pc', types[0]) # Two now becomes more available stream.send(make_presence(meredith_two, show='chat')) # * One: chat: pc # * Two: chat: phone # ClientTypes should be: ['pc'] types = get_client_types(conn, meredith_handle) assertEquals('pc', types[0]) # One now becomes less available stream.send(make_presence(meredith_one, show='away')) # * One: away: pc # * Two: chat: phone # ClientTypes should be: ['phone'] # wait for the presence change q.expect('dbus-signal', signal='PresencesChanged', args=[{ meredith_handle: (cs.PRESENCE_AVAILABLE, 'chat', '') }]) # now wait for the change in client type event = q.expect('dbus-signal', signal='ClientTypesUpdated') assertEquals([meredith_handle, ['phone']], event.args) # make One more available again stream.send(make_presence(meredith_one, show='chat', status='lawl')) # * One: chat: pc # * Two: chat: phone # ClientTypes should be: ['pc'] # wait for the presence change q.expect('dbus-signal', signal='PresencesChanged', args=[{ meredith_handle: (cs.PRESENCE_AVAILABLE, 'chat', 'lawl') }]) # now wait for the change in client type event = q.expect('dbus-signal', signal='ClientTypesUpdated') assertEquals([meredith_handle, ['pc']], event.args) # both One and Two go away stream.send(make_presence(meredith_one, show='away')) # * One: away: pc # * Two: chat: phone # ClientTypes should be: ['phone'] stream.send(make_presence(meredith_two, show='away')) # * One: away: pc # * Two: away: phone # ClientTypes should be: ['pc'] # wait for the presence change q.expect('dbus-signal', signal='PresencesChanged', args=[{ meredith_handle: (cs.PRESENCE_AWAY, 'away', '') }]) # check it still thinks we're a PC types = get_client_types(conn, meredith_handle) assertEquals('pc', types[0]) # Three, with multiple identities, signs in identities = [PHONE[0], CONSOLE[0], HANDHELD[0], BOT[0]] contact_online(q, conn, stream, meredith_three, identities, show='chat', initial=False) # * One: away: pc # * Two: away: phone # * Three: chat: phone, console, handheld, bot # ClientTypes should be: ['phone', 'console', 'handheld', 'bot'] in some order # wait for the presence change q.expect('dbus-signal', signal='PresencesChanged', args=[{ meredith_handle: (cs.PRESENCE_AVAILABLE, 'chat', 'hello') }]) # now wait for the change in client type event = q.expect('dbus-signal', signal='ClientTypesUpdated') assertEquals(meredith_handle, event.args[0]) assertEquals(['bot', 'console', 'handheld', 'phone'], sorted(event.args[1])) # that'll do # # ... # # wait wait! no it won't! Here's a regression test for # <https://bugs.freedesktop.org/show_bug.cgi?id=31772>. (caps, client, types) = build_stuff(TRANSIENT_PHONE) contact = '[email protected]/hai' send_presence(q, conn, stream, contact, caps) stanza = expect_disco(q, contact, client, caps) stream.send(make_presence(contact, type='unavailable')) send_disco_reply(stream, stanza, TRANSIENT_PHONE, []) # Gabble used to crash upon receiving a disco reply from a contact who's no # longer in the presence cache. So we sync here to check if it's died. sync_stream(q, stream)
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(q, bus, conn, stream): # check all these types appear as they should contact_online(q, conn, stream, '[email protected]/lol', BOT) contact_online(q, conn, stream, '[email protected]/lol', CONSOLE) contact_online(q, conn, stream, '[email protected]/lol', GAME) contact_online(q, conn, stream, '[email protected]/lol', HANDHELD) contact_online(q, conn, stream, '[email protected]/lol', PC) contact_online(q, conn, stream, '[email protected]/lol', PHONE) contact_online(q, conn, stream, '[email protected]/lol', WEB) contact_online(q, conn, stream, '[email protected]/lol', SMS) meredith_one = '[email protected]/One' meredith_two = '[email protected]/Two' meredith_three = '[email protected]/Three' meredith_handle = conn.get_contact_handle_sync(meredith_one) # Meredith signs in from one resource contact_online(q, conn, stream, meredith_one, PC, show='chat') # * One: chat: pc # ClientTypes should be: ['pc'] # Meredith signs in from another resource contact_online(q, conn, stream, meredith_two, PHONE, show='dnd', initial=False) # * One: chat: pc # * Two: dnd: phone # ClientTypes should be: ['pc'] # check we're still a PC types = get_client_types(conn, meredith_handle) assertLength(1, types) assertEquals('pc', types[0]) types = conn.RequestClientTypes(meredith_handle, dbus_interface=cs.CONN_IFACE_CLIENT_TYPES) assertLength(1, types) assertEquals('pc', types[0]) # Two now becomes more available stream.send(make_presence(meredith_two, show='chat')) # * One: chat: pc # * Two: chat: phone # ClientTypes should be: ['pc'] types = get_client_types(conn, meredith_handle) assertEquals('pc', types[0]) # One now becomes less available stream.send(make_presence(meredith_one, show='away')) # * One: away: pc # * Two: chat: phone # ClientTypes should be: ['phone'] # wait for the presence change q.expect('dbus-signal', signal='PresencesChanged', args=[{meredith_handle: (cs.PRESENCE_AVAILABLE, 'chat', '')}]) # now wait for the change in client type event = q.expect('dbus-signal', signal='ClientTypesUpdated') assertEquals([meredith_handle, ['phone']], event.args) # make One more available again stream.send(make_presence(meredith_one, show='chat', status='lawl')) # * One: chat: pc # * Two: chat: phone # ClientTypes should be: ['pc'] # wait for the presence change q.expect('dbus-signal', signal='PresencesChanged', args=[{meredith_handle: (cs.PRESENCE_AVAILABLE, 'chat', 'lawl')}]) # now wait for the change in client type event = q.expect('dbus-signal', signal='ClientTypesUpdated') assertEquals([meredith_handle, ['pc']], event.args) # both One and Two go away stream.send(make_presence(meredith_one, show='away')) # * One: away: pc # * Two: chat: phone # ClientTypes should be: ['phone'] stream.send(make_presence(meredith_two, show='away')) # * One: away: pc # * Two: away: phone # ClientTypes should be: ['pc'] # wait for the presence change q.expect('dbus-signal', signal='PresencesChanged', args=[{meredith_handle: (cs.PRESENCE_AWAY, 'away', '')}]) # check it still thinks we're a PC types = get_client_types(conn, meredith_handle) assertEquals('pc', types[0]) # Three, with multiple identities, signs in identities = [PHONE[0], CONSOLE[0], HANDHELD[0], BOT[0]] contact_online(q, conn, stream, meredith_three, identities, show='chat', initial=False) # * One: away: pc # * Two: away: phone # * Three: chat: phone, console, handheld, bot # ClientTypes should be: ['phone', 'console', 'handheld', 'bot'] in some order # wait for the presence change q.expect('dbus-signal', signal='PresencesChanged', args=[{meredith_handle: (cs.PRESENCE_AVAILABLE, 'chat', 'hello')}]) # now wait for the change in client type event = q.expect('dbus-signal', signal='ClientTypesUpdated') assertEquals(meredith_handle, event.args[0]) assertEquals(['bot', 'console', 'handheld', 'phone'], sorted(event.args[1])) # that'll do # # ... # # wait wait! no it won't! Here's a regression test for # <https://bugs.freedesktop.org/show_bug.cgi?id=31772>. (caps, client, types) = build_stuff(TRANSIENT_PHONE) contact = '[email protected]/hai' send_presence(q, conn, stream, contact, caps) stanza = expect_disco(q, contact, client, caps) stream.send(make_presence(contact, type='unavailable')) send_disco_reply(stream, stanza, TRANSIENT_PHONE, []) # Gabble used to crash upon receiving a disco reply from a contact who's no # longer in the presence cache. So we sync here to check if it's died. sync_stream(q, stream)
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
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) 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 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(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 sync_dbus(bus, q, conn) assert caps_changed_flag == False 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 sync_dbus(bus, q, conn) assert caps_changed_flag == False # send good reply send_disco_reply(stream, event.stanza, some_identities, jingle_av_features) # 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 send_remote_disco_reply(self, stanza): send_disco_reply(self.stream, stanza, [], self.remote_feats)
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_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)