def noop_presence_update(q, stream): # At the moment Gabble does not optimize away presence updates that # have no effect. When it does, we can forbid those events here. #events = [EventPattern('stream-presence')] #q.forbid_events(events) sync_stream(q, stream)
def test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) # Force Gabble to process the vCard before calling any methods. sync_stream(q, stream) # Request our alias and avatar, expect them to be resolved from cache. handle = conn.Properties.Get(cs.CONN, "SelfHandle") call_async(q, conn.Avatars, 'RequestAvatars', [handle]) call_async(q, conn.Aliasing, 'RequestAliases', [handle]) # FIXME - find out why RequestAliases returns before RequestAvatar even # though everything's cached. Probably due to queueing, which means # conn-avatars don't look into the cache before making a request. Prolly # should make vcard_request look into the cache itself, and return # immediately. Or not, if it's g_idle()'d. So it's better if conn-aliasing # look into the cache itself. r1, r2 = q.expect_many( EventPattern('dbus-return', method='RequestAliases'), EventPattern('dbus-return', method='RequestAvatars')) # Default alias is our jid assert r1.value[0] == ['test@localhost']
def test(q, bus, conn, stream): conn.Connect() _, event = q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]), EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard')) acknowledge_iq(stream, event.stanza) # Force Gabble to process the vCard before calling any methods. sync_stream(q, stream) handle = conn.GetSelfHandle() call_async(q, conn.Avatars, 'SetAvatar', 'william shatner', 'image/x-actor-name') event = q.expect('stream-iq', iq_type='get', to=None, query_ns='vcard-temp', query_name='vCard') reply = make_result_iq(stream, event.stanza) reply['type'] = 'error' reply.addChild(elem('error')( elem(ns.STANZA, 'forbidden')(), elem(ns.STANZA, 'text')(u'zomg whoops'))) stream.send(reply) event = q.expect('dbus-error', method='SetAvatar', name=cs.NOT_AVAILABLE)
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 announce_contact(self, name=CONTACT_NAME): self.contact_name = name self.contact_full_jid = '%s/Telepathy' % name self.handle = self.conn.RequestHandles(cs.HT_CONTACT, [name])[0] presence = domish.Element(('jabber:client', 'presence')) presence['from'] = self.contact_full_jid presence['to'] = 'test@localhost/Resource' c = presence.addElement('c') c['xmlns'] = 'http://jabber.org/protocol/caps' c['node'] = 'http://example.com/ISupportFT' c['ver'] = '1.0' self.stream.send(presence) disco_event, presence_event = self.q.expect_many( EventPattern('stream-iq', iq_type='get', query_ns='http://jabber.org/protocol/disco#info', to=self.contact_full_jid), EventPattern('dbus-signal', signal='PresencesChanged', args=[ {self.handle: (cs.PRESENCE_AVAILABLE, u'available', u'')}])) assert disco_event.query['node'] == \ 'http://example.com/ISupportFT#1.0' result = make_result_iq(self.stream, disco_event.stanza) query = result.firstChildElement() feature = query.addElement('feature') feature['var'] = ns.FILE_TRANSFER self.stream.send(result) sync_stream(self.q, self.stream)
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 test(q, bus, conn, stream, is_google): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') result = make_result_iq(stream, iq_event.stanza) # Testing reveals that Google's vCard server does not actually support # NICKNAME (or indeed any fields beside FN, N and PHOTO): if you set a # vCard including it, it accepts the request but strips out the unsupported # fields. So if the server looks like Google, it's a redundant set # operation on FN that we want to avoid. if is_google: vcard = result.firstChildElement() vcard.addElement('FN', content='oh hello there') else: vcard = result.firstChildElement() vcard.addElement('NICKNAME', content='oh hello there') stream.send(result) q.forbid_events([ EventPattern('stream-iq', iq_type='set', query_ns='vcard-temp', query_name='vCard') ]) sync_stream(q, stream) sync_dbus(bus, q, conn)
def test(q, bus, conn, stream): roster_event = q.expect('stream-iq', query_ns=ns.ROSTER) roster_event.stanza['type'] = 'result' call_async(q, conn, "RequestHandles", cs.HT_GROUP, ['test']) event = q.expect('dbus-return', method='RequestHandles') test_handle = event.value[0][0] call_async(q, conn, 'RequestChannel', cs.CHANNEL_TYPE_CONTACT_LIST, cs.HT_GROUP, test_handle, True) # A previous incarnation of this test --- written with the intention that # RequestChannel would be called before the roster was received, to expose # a bug in Gabble triggered by that ordering --- was racy: if the D-Bus # daemon happened to be particularly busy, the call to RequestChannel # reached Gabble after the roster stanza. (The race was discovered when # that reversed order triggered a newly-introduced instance of the # opposite bug to the one the test was targetting!) So we sync the XMPP # stream and D-Bus queue here. sync_stream(q, stream) sync_dbus(bus, q, conn) # send an empty roster stream.send(roster_event.stanza) event = q.expect('dbus-return', method='RequestChannel') path = event.value[0] while True: event = q.expect('dbus-signal', signal='NewChannel') assert event.args[0] == path, (event.args, path) _, type, handle_type, handle, suppress_handler = event.args if handle_type == cs.HT_GROUP and handle == test_handle: break
def test(q, bus, conn, stream): conn.Connect() roster_event = q.expect('stream-iq', query_ns=ns.ROSTER) roster_event.stanza['type'] = 'result' call_async(q, conn, "RequestHandles", cs.HT_GROUP, ['test']) event = q.expect('dbus-return', method='RequestHandles') test_handle = event.value[0][0] # send an empty roster stream.send(roster_event.stanza) sync_stream(q, stream) sync_dbus(bus, q, conn) call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST, cs.TARGET_HANDLE_TYPE: cs.HT_GROUP, cs.TARGET_HANDLE: test_handle, }) event = q.expect('dbus-return', method='CreateChannel') ret_path, ret_props = event.value event = q.expect('dbus-signal', signal='NewChannels') path, props = event.args[0][0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_CONTACT_LIST, props assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_GROUP, props assert props[cs.TARGET_HANDLE] == test_handle, props assert props[cs.TARGET_ID] == 'test', props assert ret_path == path, (ret_path, path) assert ret_props == props, (ret_props, props)
def test(q, bus, conn, stream): conn.Connect() # Initial vCard request. Respond only after we call SetAliases(). vcard_get_event = q.expect('stream-iq', iq_type='get', to=None, query_ns=ns.VCARD_TEMP, query_name='vCard') sync_stream(q, stream) handle = conn.GetSelfHandle() call_async(q, conn.Aliasing, 'SetAliases', {handle: 'Some Guy'}) sync_dbus(bus, q, conn) acknowledge_iq(stream, vcard_get_event.stanza) # Gabble sets a new vCard with our nickname. vcard_set_event = q.expect('stream-iq', iq_type='set', query_ns=ns.VCARD_TEMP, query_name='vCard') # Before the server replies, the user sets their avatar. call_async(q, conn.Avatars, 'SetAvatar', 'hello', 'image/png') sync_dbus(bus, q, conn) acknowledge_iq(stream, vcard_set_event.stanza) vcard_set_event = q.expect('stream-iq', iq_type='set', query_ns=ns.VCARD_TEMP, query_name='vCard') acknowledge_iq(stream, vcard_set_event.stanza) q.expect('dbus-return', method='SetAvatar') # And then crashes. sync_stream(q, stream) conn.Disconnect() q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
def test_39464(q, bus, conn, stream): """ Regression test for an issue where a form with no type='' attribute on the <x/> node would crash Gabble. """ client = 'fake:qutim' hash = 'blahblah' contact = '[email protected]/foo' caps = { 'node': client, 'ver': 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=ns.DISCO_INFO) # Send a reply with a form without a type='' result = make_result_iq(stream, event.stanza, add_query_node=False) result.addChild( elem(ns.DISCO_INFO, 'query', node='%s#%s' % (client, hash))( # NB. no type='' attribute elem(ns.X_DATA, 'x') ) ) stream.send(result) # We don't really care what Gabble does, as long as it doesn't crash. sync_stream(q, stream)
def test(q, bus, conn, stream): conn.Connect() expect_and_handle_get_vcard(q, stream) sync_stream(q, stream) call_async(q, conn.Avatars, 'SetAvatar', 'william shatner', 'image/x-actor-name') # Gabble request the last version of the vCard before changing it expect_and_handle_get_vcard(q, stream) set_vcard_event = q.expect('stream-iq', query_ns=ns.VCARD_TEMP, query_name='vCard', iq_type='set') iq = set_vcard_event.stanza error = domish.Element((None, 'error')) error['code'] = '400' error['type'] = 'modify' error.addElement((ns.STANZA, 'bad-request')) send_error_reply(stream, iq, error) event = q.expect('dbus-error', method='SetAvatar') assert event.error.get_dbus_name() == cs.INVALID_ARGUMENT, \ event.error.get_dbus_name()
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 proxy_error(q, bus, conn, stream): # Test if another proxy is queried if a query failed connect_and_announce_alice(q, bus, conn, stream) send_file_to_alice(q, conn) return_event, e1, e2, e3 = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS), EventPattern('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS), EventPattern('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS)) # Return errors for all the requests; the bugged proxies shouldn't be queried again q.forbid_events([EventPattern('stream-iq', to=e1.stanza['to'], iq_type='get', query_ns=ns.BYTESTREAMS)]) send_error_reply(stream, e1.stanza) # the fourth proxy is queried q.expect('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS) q.forbid_events([EventPattern('stream-iq', to=e2.stanza['to'], iq_type='get', query_ns=ns.BYTESTREAMS)]) send_error_reply(stream, e2.stanza) sync_stream(q, stream) q.forbid_events([EventPattern('stream-iq', to=e3.stanza['to'], iq_type='get', query_ns=ns.BYTESTREAMS)]) send_error_reply(stream, e3.stanza) sync_stream(q, stream)
def test_deny_unblock_remove(q, bus, conn, stream, stored, deny): """ Test unblocking a contact, and, while that request is pending, deleting them. """ self_handle = conn.GetSelfHandle() # This contact was on our roster, blocked and subscribed, when we started. contact = '*****@*****.**' handle = conn.RequestHandles(cs.HT_CONTACT, [contact])[0] # They're blocked, and we have a bidi subscription, so they should be on # deny and stored. (We already checked this earlier, but we've been messing # with the roster so let's be sure the preconditions are okay...) assertContains(handle, deny.Properties.Get(cs.CHANNEL_IFACE_GROUP, "Members")) assertContains(handle, stored.Properties.Get(cs.CHANNEL_IFACE_GROUP, "Members")) # Unblock them. call_async(q, deny.Group, 'RemoveMembers', [handle], "") roster_event = q.expect('stream-iq', query_ns=ns.ROSTER) item = roster_event.query.firstChildElement() assertEquals(contact, item['jid']) assertDoesNotContain((ns.GOOGLE_ROSTER, 't'), item.attributes) # If we now remove them from stored, the edit shouldn't be sent until the # unblock event has had a reply. q.forbid_events(remove_events) call_async(q, stored.Group, 'RemoveMembers', [handle], "") # Make sure if the remove is sent prematurely, we catch it. sync_stream(q, stream) q.unforbid_events(remove_events) # So now we send a roster push and reply for the unblock request. stream.send(make_set_roster_iq(stream, 'test@localhost/Resource', contact, 'both', False, attrs={})) acknowledge_iq(stream, roster_event.stanza) # And on receiving the push and reply, Gabble should show them being # removed from deny, and send a remove. _, roster_event = q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=['', [], [handle], [], [], self_handle, cs.GC_REASON_NONE], predicate=is_deny), remove_events[0], ) item = roster_event.query.firstChildElement() assertEquals(contact, item['jid']) stream.send(make_set_roster_iq(stream, 'test@localhost/Resource', contact, 'remove', False, attrs={})) acknowledge_iq(stream, roster_event.stanza) q.expect('dbus-signal', signal='MembersChanged', args=['', [], [handle], [], [], 0, cs.GC_REASON_NONE], predicate=is_stored)
def test(q, bus, conn, stream): # event node without NS message = elem('message', from_='*****@*****.**')(elem('event')(elem( 'items', node=ns.GEOLOC)(elem('item', id='12345')(elem( ns.GEOLOC, 'geoloc')(elem('country')(u'France')))))) stream.send(message) # event node with a wrong NS message = elem('message', from_='*****@*****.**')(elem('badger', 'event')( elem('items', node=ns.GEOLOC)(elem('item', id='12345')(elem( ns.GEOLOC, 'geoloc')(elem('country')(u'France')))))) stream.send(message) # event node without 'from' message = elem('message')(elem( (ns.PUBSUB_EVENT), 'event')(elem('items', node=ns.GEOLOC)(elem('item', id='12345')(elem( ns.GEOLOC, 'geoloc')(elem('country')(u'France')))))) stream.send(message) # event node with an invalid 'from' message = elem('message', from_='aaaa')(elem( (ns.PUBSUB_EVENT), 'event')(elem('items', node=ns.GEOLOC)(elem('item', id='12345')(elem( ns.GEOLOC, 'geoloc')(elem('country')(u'France')))))) stream.send(message) # no items node message = elem('message', from_='*****@*****.**')(elem((ns.PUBSUB_EVENT), 'event')()) stream.send(message) # no item node message = elem('message', from_='*****@*****.**')(elem( (ns.PUBSUB_EVENT), 'event')(elem('items', node=ns.GEOLOC)())) stream.send(message) # item node doesn't have any child message = elem('message', from_='*****@*****.**')(elem( (ns.PUBSUB_EVENT), 'event')(elem('items', node=ns.GEOLOC)(elem('item', id='12345')()))) stream.send(message) # the child of the item node doesn't have a NS message = elem('message', from_='*****@*****.**')(elem( (ns.PUBSUB_EVENT), 'event')(elem('items', node=ns.GEOLOC)(elem( 'item', id='12345')(elem('geoloc')(elem('country')(u'France')))))) stream.send(message) # valid but unknown pubsub notification message = elem('message', from_='*****@*****.**')(elem( (ns.PUBSUB_EVENT), 'event')(elem('items', node='http://www.badger.com')(elem( 'item', id='12345')(elem('http://www.badger.com', 'badger')(elem('mushroom')(u'snake')))))) stream.send(message) sync_stream(q, stream)
def test_deny_overlap_two(q, bus, conn, stream, subscribe, publish, stored, deny): """ Here's another tricky case: editing a contact (setting an alias, say), and then while that edit's in flight, blocking and remove the contact. """ # This contact was on our roster when we started. contact = '*****@*****.**' handle = conn.RequestHandles(cs.HT_CONTACT, [contact])[0] assertContains(handle, stored.Properties.Get(cs.CHANNEL_IFACE_GROUP, "Members")) assertContains(handle, subscribe.Properties.Get(cs.CHANNEL_IFACE_GROUP, "Members")) assertContains(handle, publish.Properties.Get(cs.CHANNEL_IFACE_GROUP, "Members")) # Once again, at no point in this test should anyone be removed outright. q.forbid_events(remove_events) # First up, we edit the contact's alias, triggering a roster update from # the client. conn.Aliasing.SetAliases({handle: 'oh! the huge manatee!'}) event = q.expect('stream-iq', query_ns=ns.ROSTER) item = event.query.firstChildElement() assertEquals(contact, item['jid']) assertEquals('oh! the huge manatee!', item['name']) # Before the server responds, we block and remove the contact. The edits # should be queued... patterns = [ EventPattern('stream-iq', query_ns=ns.ROSTER), EventPattern('stream-presence', presence_type='unsubscribed'), EventPattern('stream-presence', presence_type='unsubscribe'), ] q.forbid_events(patterns) call_async(q, deny.Group, 'AddMembers', [handle], "") call_async(q, stored.Group, 'RemoveMembers', [handle], "") # Make sure if the edits are sent prematurely, we've got them. sync_stream(q, stream) q.unforbid_events(patterns) # Okay, now we respond to the alias update. At this point we expect an # update to gr:t=B, leaving subscription=both intact, and subscription # cancellations. acknowledge_iq(stream, event.stanza) roster_event, _, _ = q.expect_many(*patterns) item = roster_event.query.firstChildElement() assertEquals(contact, item['jid']) assertEquals('B', item[(ns.GOOGLE_ROSTER, 't')]) # And we're done. Clean up. q.unforbid_events(remove_events)
def test(q, bus, conn, stream): self_presence = q.expect('stream-presence') c = xpath.queryForNodes('/presence/c', self_presence.stanza)[0] jid = '[email protected]/omg' # Gabble shouldn't send any disco requests to our contact during this test. q.forbid_events([ EventPattern('stream-iq', to=jid, iq_type='get', query_ns=ns.DISCO_INFO), ]) # Check that Gabble doesn't disco other clients with the same caps hash. p = make_presence(jid, caps={'node': c['node'], 'hash': c['hash'], 'ver': c['ver'], }) stream.send(p) sync_stream(q, stream) # Check that Gabble doesn't disco its own ext='' bundles (well, its own # bundles as advertised by Gabbles that don't do hashed caps) p = make_presence(jid, caps={'node': c['node'], 'ver': c['ver'], # omitting hash='' so Gabble doesn't ignore ext='' 'ext': 'voice-v1 video-v1', }) stream.send(p) sync_stream(q, stream) # Advertise some different capabilities, to change our own caps hash. add = [(cs.CHANNEL_TYPE_STREAMED_MEDIA, 2L**32-1), (cs.CHANNEL_TYPE_STREAM_TUBE, 2L**32-1), (cs.CHANNEL_TYPE_STREAM_TUBE, 2L**32-1)] remove = [] caps = conn.Capabilities.AdvertiseCapabilities(add, remove) self_presence = q.expect('stream-presence') c_ = xpath.queryForNodes('/presence/c', self_presence.stanza)[0] assertNotEquals(c['ver'], c_['ver']) # But then someone asks us for our old caps iq = IQ(stream, 'get') iq['from'] = jid query = iq.addElement((ns.DISCO_INFO, 'query')) query['node'] = c['node'] + '#' + c['ver'] stream.send(iq) # Gabble should still know what they are, and reply. This is actually quite # important: there's a bug in iChat where if you return an error to a disco # query, it just asks again, and again, and again... reply = q.expect('stream-iq', to=jid) assertEquals('result', reply.iq_type)
def test(q, bus, conn, stream): jid = '[email protected]/hi' caps = { 'node': 'oh:hi', 'ver': "dere", } h = caps_helper.send_presence(q, conn, stream, jid, caps, initial=False) request = caps_helper.expect_disco(q, jid, caps['node'], caps) result = make_result_iq(stream, request, add_query_node=False) stream.send(result) sync_stream(q, stream)
def test_deny_unblock_remove(q, bus, conn, stream): """ Test unblocking a contact, and, while that request is pending, deleting them. """ # This contact was on our roster, blocked and subscribed, when we started. contact = '*****@*****.**' handle = conn.get_contact_handle_sync(contact) check_contact_roster(conn, contact, [], cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_YES) # They're blocked, and we have a bidi subscription, so they should be on # deny and stored. (We already checked this earlier, but we've been messing # with the roster so let's be sure the preconditions are okay...) assertContains(handle, conn.ContactBlocking.RequestBlockedContacts().keys()) # Unblock them. call_async(q, conn.ContactBlocking, 'UnblockContacts', [handle]) roster_event = q.expect('stream-iq', query_ns=ns.ROSTER) item = roster_event.query.firstChildElement() assertEquals(contact, item['jid']) assertDoesNotContain((ns.GOOGLE_ROSTER, 't'), item.attributes) # If we now remove them from stored, the edit shouldn't be sent until the # unblock event has had a reply. q.forbid_events(remove_events) call_async(q, conn.ContactList, 'RemoveContacts', [handle]) # Make sure if the remove is sent prematurely, we catch it. sync_stream(q, stream) q.unforbid_events(remove_events) # So now we send a roster push and reply for the unblock request. stream.send(make_set_roster_iq(stream, 'test@localhost/Resource', contact, 'both', False, attrs={})) acknowledge_iq(stream, roster_event.stanza) # And on receiving the push and reply, Gabble should show them being # removed from deny, and send a remove. _, roster_event = q.expect_many( EventPattern('dbus-signal', signal='BlockedContactsChanged', predicate=lambda e: blocked_contacts_changed_predicate(e, [], [contact])), remove_events[0], ) item = roster_event.query.firstChildElement() assertEquals(contact, item['jid']) stream.send(make_set_roster_iq(stream, 'test@localhost/Resource', contact, 'remove', False, attrs={})) acknowledge_iq(stream, roster_event.stanza)
def test_get_roster(q, bus, conn, stream): # DownloadAtConnection = True, so gabble should get the roster # automatically q.expect('stream-iq', query_ns=ns.ROSTER) # but calling ContactList.Download should not try and get the # roster again q.forbid_events(forbidden) conn.ContactList.Download() sync_stream(q, stream) q.unforbid_events(forbidden)
def normally_close_text(q, bus, conn, stream): jid = '*****@*****.**' text_chan, text_path, _ = text_channel(q, bus, conn, stream, 'CreateChannel', jid) sync_stream(q, stream) text_chan.Close() expect_close(q, text_path, stream, jid) assert_not_on_bus(q, text_chan)
def test(q, bus, conn, stream): conn.Connect() expect_and_handle_get_vcard(q, stream) # Ensure that Gabble's actually got the initial vCard reply; if it hasn't # processed it by the time we call SetAliases, the latter will wait for it # to reply and then set immediately. sync_stream(q, stream) handle = conn.GetSelfHandle() call_async(q, conn.Aliasing, 'SetAliases', {handle: 'Some Guy'}) # SetAliases requests vCard v1 get_vcard_event = q.expect('stream-iq', query_ns=ns.VCARD_TEMP, query_name='vCard', iq_type='get') iq = get_vcard_event.stanza vcard = iq.firstChildElement() assert vcard.name == 'vCard', vcard.toXml() call_async(q, conn.Avatars, 'SetAvatar', 'hello', 'image/png') # We don't expect Gabble to send a second vCard request, since there's one # outstanding. But we want to ensure that SetAvatar reaches Gabble before # the empty vCard does. sync_dbus(bus, q, conn) # Send back current empty vCard result = make_result_iq(stream, iq) # result already includes the <vCard/> from the query, which is all we need stream.send(result) def has_nickname_and_photo(vcard): nicknames = xpath.queryForNodes('/vCard/NICKNAME', vcard) assert nicknames is not None assert len(nicknames) == 1 assert str(nicknames[0]) == 'Some Guy' photos = xpath.queryForNodes('/vCard/PHOTO', vcard) assert photos is not None and len(photos) == 1, repr(photos) types = xpath.queryForNodes('/PHOTO/TYPE', photos[0]) binvals = xpath.queryForNodes('/PHOTO/BINVAL', photos[0]) assert types is not None and len(types) == 1, repr(types) assert binvals is not None and len(binvals) == 1, repr(binvals) assert str(types[0]) == 'image/png' got = str(binvals[0]) exp = base64.b64encode('hello') assert got == exp, (got, exp) # Now Gabble should set a new vCard with both of the above changes. expect_and_handle_set_vcard(q, stream, has_nickname_and_photo)
def test_deny_overlap_two(q, bus, conn, stream): """ Here's another tricky case: editing a contact (setting an alias, say), and then while that edit's in flight, blocking and remove the contact. """ # This contact was on our roster when we started. contact = '*****@*****.**' handle = conn.get_contact_handle_sync(contact) check_contact_roster(conn, contact, [], cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_YES) # Once again, at no point in this test should anyone be removed outright. q.forbid_events(remove_events) # First up, we edit the contact's alias, triggering a roster update from # the client. conn.Aliasing.SetAliases({handle: 'oh! the huge manatee!'}) event = q.expect('stream-iq', query_ns=ns.ROSTER) item = event.query.firstChildElement() assertEquals(contact, item['jid']) assertEquals('oh! the huge manatee!', item['name']) # Before the server responds, we block and remove the contact. The edits # should be queued... patterns = [ EventPattern('stream-iq', query_ns=ns.ROSTER), EventPattern('stream-presence', presence_type='unsubscribed'), EventPattern('stream-presence', presence_type='unsubscribe'), ] q.forbid_events(patterns) call_async(q, conn.ContactBlocking, 'BlockContacts', [handle], "") call_async(q, conn.ContactList, 'RemoveContacts', [handle]) # Make sure if the edits are sent prematurely, we've got them. sync_stream(q, stream) q.unforbid_events(patterns) # Okay, now we respond to the alias update. At this point we expect an # update to gr:t=B, leaving subscription=both intact, and subscription # cancellations. acknowledge_iq(stream, event.stanza) roster_event, _, _ = q.expect_many(*patterns) item = roster_event.query.firstChildElement() assertEquals(contact, item['jid']) assertEquals('B', item[(ns.GOOGLE_ROSTER, 't')]) # And we're done. Clean up. q.unforbid_events(remove_events)
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 test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) sync_stream(q, stream) sync_dbus(bus, q, conn) # A presence from a contact stream.send( make_presence('contact1@localhost/client', 'SHA1SUM-FOR-CONTACT1')) event = q.expect('dbus-signal', signal='AvatarUpdated') assert event.args[0] == 2, event.args assert event.args[1] == "SHA1SUM-FOR-CONTACT1", event.args AvatarRetrieved_event = EventPattern('dbus-signal', signal='AvatarRetrieved') AvatarUpdated_event = EventPattern('dbus-signal', signal='AvatarUpdated') StreamPresence_event = EventPattern('stream-presence') StreamIqVcard_event = EventPattern('stream-iq', query_ns='vcard-temp') # A presence from myself on another resource stream.send( make_presence('test@localhost/resource1', 'SHA1SUM-FOR-MYSELF-RES1')) q.forbid_events([AvatarRetrieved_event, AvatarUpdated_event]) stream_presence, stream_iq = q.expect_many( EventPattern('stream-presence'), EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard')) sync_dbus(bus, q, conn) q.unforbid_events([AvatarRetrieved_event, AvatarUpdated_event]) # If the server wrongly send a presence stanza with our resource, # AvatarUpdated must not be emitted q.forbid_events([ StreamPresence_event, StreamIqVcard_event, AvatarRetrieved_event, AvatarUpdated_event ]) stream.send(make_presence('test@localhost/Resource', 'SHA1SUM-FOR-MYSELF')) sync_dbus(bus, q, conn) sync_stream(q, stream) q.unforbid_events([ StreamPresence_event, StreamIqVcard_event, AvatarRetrieved_event, AvatarUpdated_event ])
def test(q, bus, conn, stream): expect_get_and_send_item_not_found(q, stream) sync_stream(q, stream) call_async( q, conn.Avatars, 'SetAvatar', 'Guy.brush', 'image/x-mighty-pirate') # Gabble checks again, but we still don't have a vCard expect_get_and_send_item_not_found(q, stream) # Never mind! It creates a new one. expect_and_handle_set_vcard(q, stream) q.expect('dbus-return', method='SetAvatar')
def send_presence_and_caps(self): # We need remote end's presence for capabilities self.stream.send(self.jp.xml( self.jp.Presence(self.peer, self.jid, self.remote_caps))) # Gabble doesn't trust it, so makes a disco event = self.q.expect('stream-iq', query_ns=ns.DISCO_INFO, to=self.peer) # jt.send_remote_disco_reply(event.stanza) self.stream.send(self.jp.xml(self.jp.ResultIq(self.jid, event.stanza, [ self.jp.Query(None, ns.DISCO_INFO, [ self.jp.Feature(x) for x in self.jp.features ]) ]) )) # Force Gabble to process the caps before doing any more Jingling sync_stream(self.q, self.stream)
def setup_tests(q, bus, conn, stream, announce=False): bare_jid = "*****@*****.**" full_jid = bare_jid + "/HotHotResource" if announce: presence_and_disco(q, conn, stream, full_jid, True, client, caps, features, identity, {}, True, None) sync_stream(q, stream) handle = conn.RequestHandles(cs.HT_CONTACT, [full_jid])[0] return handle, bare_jid, full_jid
def test_dont_get_roster(q, bus, conn, stream): # DownloadAtConnection = False, so let's make sure the roster # isn't fetched automatically q.forbid_events(forbidden) conn.Connect() q.expect_many(EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])) sync_stream(q, stream) q.unforbid_events(forbidden) # seems fine, now calling Download should try and get the roster # successfully. call_async(q, conn.ContactList, 'Download') q.expect_many(EventPattern('stream-iq', query_ns=ns.ROSTER), EventPattern('dbus-return', method='Download'))
def test(q, bus, conn, stream): expect_and_handle_get_vcard(q, stream) sync_stream(q, stream) call_async( q, conn.Avatars, 'SetAvatar', b'Guy.brush', 'image/x-mighty-pirate') expect_and_handle_get_vcard(q, stream) iq_event = q.expect( 'stream-iq', iq_type='set', query_ns='vcard-temp', query_name='vCard') call_async( q, conn.Avatars, 'SetAvatar', b'LeChuck.brush', 'image/x-ghost-pirate') disconnect_conn(q, conn, stream) q.expect('dbus-error', method='SetAvatar', name=cs.NOT_AVAILABLE) q.expect('dbus-error', method='SetAvatar', name=cs.NOT_AVAILABLE) sync_dbus(bus, q, conn)
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 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): # Gabble asks for the roster; the server sends back an empty roster. event = q.expect('stream-iq', query_ns=ns.ROSTER) acknowledge_iq(stream, event.stanza) q.expect('dbus-signal', signal='ContactListStateChanged', args=[cs.CONTACT_LIST_STATE_SUCCESS]) # The server sends us a roster push without an id=''. WTF! iq = make_roster_push(stream, jid, 'both') del iq['id'] stream.send(iq) h = conn.get_contact_handle_sync(jid) q.expect_many( EventPattern( 'dbus-signal', signal='ContactsChangedWithID', args=[{ h: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_YES, ''), }, { h: jid }, {}], ), ) # Verify that Gabble didn't crash while trying to ack the push. sync_stream(q, stream) # Just for completeness, let's repeat this test with a malicious roster # push from a contact (rather than from our server). Our server's *really* # broken if it allows this. Nonetheless... iq = make_roster_push(stream, '*****@*****.**', 'both') del iq['id'] iq['from'] = '*****@*****.**' stream.send(iq) q.forbid_events([ EventPattern('dbus-signal', signal='ContactsChangedWithID'), ]) # Make sure Gabble's got the evil push... sync_stream(q, stream) # ...and make sure it's not emitted anything. sync_dbus(bus, q, conn)
def test_dont_get_roster(q, bus, conn, stream): # DownloadAtConnection = False, so let's make sure the roster # isn't fetched automatically q.forbid_events(forbidden) conn.Connect() q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])) sync_stream(q, stream) q.unforbid_events(forbidden) # seems fine, now calling Download should try and get the roster # successfully. call_async(q, conn.ContactList, 'Download') q.expect_many(EventPattern('stream-iq', query_ns=ns.ROSTER), EventPattern('dbus-return', method='Download'))
def proxy_error(q, bus, conn, stream): # Test if another proxy is queried if a query failed connect_and_announce_alice(q, bus, conn, stream) send_file_to_alice(q, conn) return_event, e1, e2, e3 = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS), EventPattern('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS), EventPattern('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS)) # Return errors for all the requests; the bugged proxies shouldn't be queried again q.forbid_events([ EventPattern('stream-iq', to=e1.stanza['to'], iq_type='get', query_ns=ns.BYTESTREAMS) ]) send_error_reply(stream, e1.stanza) # the fourth proxy is queried q.expect('stream-iq', iq_type='get', query_ns=ns.BYTESTREAMS) q.forbid_events([ EventPattern('stream-iq', to=e2.stanza['to'], iq_type='get', query_ns=ns.BYTESTREAMS) ]) send_error_reply(stream, e2.stanza) sync_stream(q, stream) q.forbid_events([ EventPattern('stream-iq', to=e3.stanza['to'], iq_type='get', query_ns=ns.BYTESTREAMS) ]) send_error_reply(stream, e3.stanza) sync_stream(q, stream)
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)