def test_new_channel(q, bus, conn, target_uri, initiator_uri, requested): event = q.expect('dbus-signal', signal='NewChannels') path, props = event.args[0][0] assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_CONTACT, props[cs.TARGET_HANDLE_TYPE]) handle = props[cs.TARGET_HANDLE] obj = bus.get_object(conn._named_service, path) initiator_handle = conn.get_contact_handle_sync(initiator_uri) text_props = obj.GetAll(cs.CHANNEL, dbus_interface='org.freedesktop.DBus.Properties') assert text_props['ChannelType'] == cs.CHANNEL_TYPE_TEXT, text_props assert 'Interfaces' in text_props, text_props assertSameSets((cs.CHANNEL_IFACE_MESSAGES, cs.CHANNEL_IFACE_DESTROYABLE), text_props['Interfaces']) assert 'TargetHandle' in text_props, text_props assert text_props['TargetHandle'] == handle, \ (text_props, handle) assert 'TargetHandleType' in text_props, text_props assert text_props['TargetHandleType'] == 1, text_props assert text_props['TargetID'] == target_uri, text_props assert text_props['InitiatorHandle'] == initiator_handle, \ (text_props, initiator_handle) assert text_props['InitiatorID'] == initiator_uri, \ (text_props, initiator_uri) assert 'Requested' in text_props, text_props assert text_props['Requested'] == requested, text_props return obj, handle
def test_channel_reference_identity_with_extra_multiple(q, bus, conn, stream): props = connect_and_get_tls_properties (q, bus, conn) reference_identities = props["ReferenceIdentities"] assertSameSets(reference_identities, [ "example.org", "hypnotoad.example.org", "localhost", "other.local" ]) assertEquals(props["Hostname"], "example.org")
def test_channel_reference_identity_with_extra(q, bus, conn, stream): props = connect_and_get_tls_properties(q, bus, conn) reference_identities = props["ReferenceIdentities"] assertSameSets(reference_identities, ["example.org", "hypnotoad.example.org", "localhost"]) assertEquals(props["Hostname"], "example.org")
def test_jabber_pass_success(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) e = q.expect('auth-initial-iq') authenticator = e.authenticator authenticator.respondToInitialIq(e.iq) chan, props = expect_sasl_channel(q, bus, conn) assertSameSets(['X-TELEPATHY-PASSWORD'], props.get(cs.SASL_AVAILABLE_MECHANISMS)) chan.SASLAuthentication.StartMechanismWithData('X-TELEPATHY-PASSWORD', PASSWORD) e = q.expect('auth-second-iq') authenticator.respondToSecondIq(e.iq) q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) chan.SASLAuthentication.AcceptSASL() q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SUCCEEDED, '', {}]) e = q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
def check_contact_roster(conn, contact, groups=None, subscribe=None, publish=None): h = conn.get_contact_handle_sync(contact) attrs = conn.Contacts.GetContactAttributes([h], [cs.CONN_IFACE_CONTACT_LIST, cs.CONN_IFACE_CONTACT_GROUPS], True)[h] if groups is not None: assertSameSets(groups, attrs[cs.ATTR_GROUPS]) if subscribe is not None: assertEquals(subscribe, attrs[cs.ATTR_SUBSCRIBE]) if publish is not None: assertEquals(publish, attrs[cs.ATTR_PUBLISH])
def request_ft_channel(self, uri=True): request = { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: self.handle, cs.FT_CONTENT_TYPE: self.file.content_type, cs.FT_FILENAME: self.file.name, cs.FT_SIZE: self.file.size, cs.FT_CONTENT_HASH_TYPE: self.file.hash_type, cs.FT_CONTENT_HASH: self.file.hash, cs.FT_DESCRIPTION: self.file.description, cs.FT_DATE: self.file.date, cs.FT_INITIAL_OFFSET: 0, cs.FT_SERVICE_NAME: self.service_name, cs.FT_METADATA: dbus.Dictionary(self.metadata, signature='sas') } if uri: request[cs.FT_URI] = self.file.uri self.ft_path, props = self.conn.Requests.CreateChannel(request) # Channel D-Bus properties assertEquals(cs.CHANNEL_TYPE_FILE_TRANSFER, props[cs.CHANNEL_TYPE]) assertSameSets([ cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA, ], props[cs.INTERFACES]) assertEquals(self.handle, props[cs.TARGET_HANDLE]) assertEquals(self.contact_name, props[cs.TARGET_ID]) assertEquals(cs.HT_CONTACT, props[cs.TARGET_HANDLE_TYPE]) assert props[cs.REQUESTED] assertEquals(self.self_handle, props[cs.INITIATOR_HANDLE]) assertEquals(self.self_handle_name, props[cs.INITIATOR_ID]) # Channel.Type.FileTransfer D-Bus properties assertEquals(cs.FT_STATE_PENDING, props[cs.FT_STATE]) assertEquals(self.file.content_type, props[cs.FT_CONTENT_TYPE]) assertEquals(self.file.name, props[cs.FT_FILENAME]) assertEquals(self.file.size, props[cs.FT_SIZE]) assertEquals(self.file.hash_type, props[cs.FT_CONTENT_HASH_TYPE]) assertEquals(self.file.hash, props[cs.FT_CONTENT_HASH]) assertEquals(self.file.description, props[cs.FT_DESCRIPTION]) assertEquals(self.file.date, props[cs.FT_DATE]) assertEquals(0, props[cs.FT_TRANSFERRED_BYTES]) assertEquals(0, props[cs.FT_INITIAL_OFFSET]) assertEquals(self.service_name, props[cs.FT_SERVICE_NAME]) assertEquals(self.metadata, props[cs.FT_METADATA]) if uri: assertEquals(self.file.uri, props[cs.FT_URI]) else: assertEquals('', props[cs.FT_URI]) self.check_platform_socket_types(props[cs.FT_AVAILABLE_SOCKET_TYPES])
def check_new_channel(self): def is_ft_channel_event(event): channels, = event.args if len(channels) > 1: return False path, props = channels[0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER e = self.q.expect('dbus-signal', signal='NewChannels', path=self.conn.object.object_path, predicate=is_ft_channel_event) channels, = e.args path, props = channels[0] # check channel properties # org.freedesktop.Telepathy.Channel D-Bus properties assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER, props assertSameSets([ cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA, ], props[cs.INTERFACES]) assert props[cs.TARGET_HANDLE] == self.handle, props assert props[cs.TARGET_ID] == self.target, props assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT, props assert props[cs.REQUESTED] == False, props assert props[cs.INITIATOR_HANDLE] == self.handle, props assert props[cs.INITIATOR_ID] == self.target, props # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties assert props[cs.FT_STATE] == cs.FT_STATE_PENDING, props assert props[cs.FT_CONTENT_TYPE] == '', props assert props[cs.FT_FILENAME].encode('utf-8') == self.file.name, props assert props[cs.FT_SIZE] == self.file.size, props # FT's protocol doesn't allow us the send the hash info assert props[cs.FT_CONTENT_HASH_TYPE] == cs.FILE_HASH_TYPE_NONE, props assert props[cs.FT_CONTENT_HASH] == '', props assert props[cs.FT_DESCRIPTION] == '', props assert props[cs.FT_DATE] == 0, props assert props[cs.FT_AVAILABLE_SOCKET_TYPES] == \ {cs.SOCKET_ADDRESS_TYPE_UNIX: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV4: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV6: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST]}, \ props[cs.FT_AVAILABLE_SOCKET_TYPES] assert props[cs.FT_TRANSFERRED_BYTES] == 0, props assert props[cs.FT_INITIAL_OFFSET] == 0, props self.ft_path = path self.create_ft_channel()
def check_new_channel(self): def is_ft_channel_event(event): channels, = event.args if len(channels) > 1: return False path, props = channels[0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER e = self.q.expect('dbus-signal', signal='NewChannels', path=self.conn.object.object_path, predicate=is_ft_channel_event) channels, = e.args path, props = channels[0] # check channel properties # org.freedesktop.Telepathy.Channel D-Bus properties assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER, props assertSameSets( [ cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA, ], props[cs.INTERFACES]) assert props[cs.TARGET_HANDLE] == self.handle, props assert props[cs.TARGET_ID] == self.target, props assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT, props assert props[cs.REQUESTED] == False, props assert props[cs.INITIATOR_HANDLE] == self.handle, props assert props[cs.INITIATOR_ID] == self.target, props # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties assert props[cs.FT_STATE] == cs.FT_STATE_PENDING, props assert props[cs.FT_CONTENT_TYPE] == '', props assert props[cs.FT_FILENAME].encode('utf-8') == self.file.name, props assert props[cs.FT_SIZE] == self.file.size, props # FT's protocol doesn't allow us the send the hash info assert props[cs.FT_CONTENT_HASH_TYPE] == cs.FILE_HASH_TYPE_NONE, props assert props[cs.FT_CONTENT_HASH] == '', props assert props[cs.FT_DESCRIPTION] == '', props assert props[cs.FT_DATE] == 0, props assert props[cs.FT_AVAILABLE_SOCKET_TYPES] == \ {cs.SOCKET_ADDRESS_TYPE_UNIX: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV4: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV6: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST]}, \ props[cs.FT_AVAILABLE_SOCKET_TYPES] assert props[cs.FT_TRANSFERRED_BYTES] == 0, props assert props[cs.FT_INITIAL_OFFSET] == 0, props self.ft_path = path self.create_ft_channel()
def check_new_channel(self): def is_ft_channel_event(event): channels, = event.args if len(channels) > 1: return False path, props = channels[0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER e = self.q.expect('dbus-signal', signal='NewChannels', path=self.conn.object.object_path, predicate=is_ft_channel_event) channels = e.args[0] assert len(channels) == 1 path, props = channels[0] # check channel properties # Channel D-Bus properties assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER assertSameSets([ cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA, ], props[cs.INTERFACES]) assert props[cs.TARGET_HANDLE] == self.handle assert props[cs.TARGET_ID] == self.contact_name assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT assert props[cs.REQUESTED] == False assert props[cs.INITIATOR_HANDLE] == self.handle assert props[cs.INITIATOR_ID] == self.contact_name # Channel.Type.FileTransfer D-Bus properties assert props[cs.FT_STATE] == cs.FT_STATE_PENDING assert props[cs.FT_CONTENT_TYPE] == self.file.content_type assert props[cs.FT_FILENAME] == self.file.name assert props[cs.FT_SIZE] == self.file.size # FT's protocol doesn't allow us the send the hash info assert props[cs.FT_CONTENT_HASH_TYPE] == cs.FILE_HASH_TYPE_MD5 assert props[cs.FT_CONTENT_HASH] == self.file.hash assert props[cs.FT_DESCRIPTION] == self.file.description assert props[cs.FT_DATE] == self.file.date assert props[cs.FT_TRANSFERRED_BYTES] == 0 assert props[cs.FT_INITIAL_OFFSET] == 0 self.check_platform_socket_types(props[cs.FT_AVAILABLE_SOCKET_TYPES]) assertEquals(self.service_name, props[cs.FT_SERVICE_NAME]) assertEquals(self.metadata, props[cs.FT_METADATA]) self.ft_path = path
def request_ft_channel(self, uri=True): request = { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: self.handle, cs.FT_CONTENT_TYPE: self.file.content_type, cs.FT_FILENAME: self.file.name, cs.FT_SIZE: self.file.size, cs.FT_CONTENT_HASH_TYPE: self.file.hash_type, cs.FT_CONTENT_HASH: self.file.hash, cs.FT_DESCRIPTION: self.file.description, cs.FT_DATE: self.file.date, cs.FT_INITIAL_OFFSET: 0, cs.FT_SERVICE_NAME: self.service_name, cs.FT_METADATA: dbus.Dictionary(self.metadata, signature='sas')} if uri: request[cs.FT_URI] = self.file.uri self.ft_path, props = self.conn.Requests.CreateChannel(request) # Channel D-Bus properties assertEquals(cs.CHANNEL_TYPE_FILE_TRANSFER, props[cs.CHANNEL_TYPE]) assertSameSets( [ cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA, ], props[cs.INTERFACES]) assertEquals(self.handle, props[cs.TARGET_HANDLE]) assertEquals(self.contact_name, props[cs.TARGET_ID]) assertEquals(cs.HT_CONTACT, props[cs.TARGET_HANDLE_TYPE]) assert props[cs.REQUESTED] assertEquals(self.self_handle, props[cs.INITIATOR_HANDLE]) assertEquals(self.self_handle_name, props[cs.INITIATOR_ID]) # Channel.Type.FileTransfer D-Bus properties assertEquals(cs.FT_STATE_PENDING, props[cs.FT_STATE]) assertEquals(self.file.content_type, props[cs.FT_CONTENT_TYPE]) assertEquals(self.file.name, props[cs.FT_FILENAME]) assertEquals(self.file.size, props[cs.FT_SIZE]) assertEquals(self.file.hash_type, props[cs.FT_CONTENT_HASH_TYPE]) assertEquals(self.file.hash, props[cs.FT_CONTENT_HASH]) assertEquals(self.file.description, props[cs.FT_DESCRIPTION]) assertEquals(self.file.date, props[cs.FT_DATE]) assertEquals(0, props[cs.FT_TRANSFERRED_BYTES]) assertEquals(0, props[cs.FT_INITIAL_OFFSET]) assertEquals(self.service_name, props[cs.FT_SERVICE_NAME]) assertEquals(self.metadata, props[cs.FT_METADATA]) if uri: assertEquals(self.file.uri, props[cs.FT_URI]) else: assertEquals('', props[cs.FT_URI]) self.check_platform_socket_types(props[cs.FT_AVAILABLE_SOCKET_TYPES])
def check_new_channel(self): def is_ft_channel_event(event): channels, = event.args if len(channels) > 1: return False path, props = channels[0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER e = self.q.expect('dbus-signal', signal='NewChannels', path=self.conn.object.object_path, predicate=is_ft_channel_event) channels = e.args[0] assert len(channels) == 1 path, props = channels[0] # check channel properties # Channel D-Bus properties assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER assertSameSets( [ cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA, ], props[cs.INTERFACES]) assert props[cs.TARGET_HANDLE] == self.handle assert props[cs.TARGET_ID] == self.contact_name assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT assert props[cs.REQUESTED] == False assert props[cs.INITIATOR_HANDLE] == self.handle assert props[cs.INITIATOR_ID] == self.contact_name # Channel.Type.FileTransfer D-Bus properties assert props[cs.FT_STATE] == cs.FT_STATE_PENDING assert props[cs.FT_CONTENT_TYPE] == self.file.content_type assert props[cs.FT_FILENAME] == self.file.name assert props[cs.FT_SIZE] == self.file.size # FT's protocol doesn't allow us the send the hash info assert props[cs.FT_CONTENT_HASH_TYPE] == cs.FILE_HASH_TYPE_MD5 assert props[cs.FT_CONTENT_HASH] == self.file.hash assert props[cs.FT_DESCRIPTION] == self.file.description assert props[cs.FT_DATE] == self.file.date assert props[cs.FT_TRANSFERRED_BYTES] == 0 assert props[cs.FT_INITIAL_OFFSET] == 0 self.check_platform_socket_types(props[cs.FT_AVAILABLE_SOCKET_TYPES]) assertEquals(self.service_name, props[cs.FT_SERVICE_NAME]) assertEquals(self.metadata, props[cs.FT_METADATA]) self.ft_path = path
def check_contact_roster(conn, contact, groups=None, subscribe=None, publish=None): h = conn.get_contact_handle_sync(contact) attrs = conn.Contacts.GetContactAttributes( [h], [cs.CONN_IFACE_CONTACT_LIST, cs.CONN_IFACE_CONTACT_GROUPS], True)[h] if groups is not None: assertSameSets(groups, attrs[cs.ATTR_GROUPS]) if subscribe is not None: assertEquals(subscribe, attrs[cs.ATTR_SUBSCRIBE]) if publish is not None: assertEquals(publish, attrs[cs.ATTR_PUBLISH])
def rccs(q, bus, conn, stream): """ Tests that the connection's RequestableChannelClasses for StreamedMedia are sane. """ conn.Connect() a = q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) a = q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) rccs = conn.Properties.Get(cs.CONN_IFACE_REQUESTS, 'RequestableChannelClasses') # Test Channel.Type.StreamedMedia media_classes = [ rcc for rcc in rccs if rcc[0][cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_CALL ] assertLength(2, media_classes) for media_class in media_classes: fixed, allowed = media_class assertEquals(cs.HT_CONTACT, fixed[cs.TARGET_HANDLE_TYPE]) assert fixed.has_key(cs.CALL_INITIAL_AUDIO) or fixed.has_key( cs.CALL_INITIAL_VIDEO) expected_allowed = [ cs.TARGET_ID, cs.TARGET_HANDLE, cs.CALL_INITIAL_VIDEO, cs.CALL_INITIAL_AUDIO, cs.CALL_INITIAL_VIDEO_NAME, cs.CALL_INITIAL_AUDIO_NAME, cs.CALL_INITIAL_TRANSPORT, cs.DTMF_INITIAL_TONES, ] allowed.sort() expected_allowed.sort() assertSameSets(expected_allowed, allowed)
def test_jabber_pass_fail(q, bus, conn, stream, which): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) e = q.expect('auth-initial-iq') authenticator = e.authenticator authenticator.respondToInitialIq(e.iq) chan, props = expect_sasl_channel(q, bus, conn) assertSameSets(['X-TELEPATHY-PASSWORD'], props.get(cs.SASL_AVAILABLE_MECHANISMS)) chan.SASLAuthentication.StartMechanismWithData('X-TELEPATHY-PASSWORD', PASSWORD) e = q.expect('auth-second-iq') result = IQ(stream, 'error') result['id'] = e.id error = result.addElement('error') error['code'] = str(CODES[which]) error['type'] = TYPES[which] error.addElement((ns.STANZA, which)) stream.send(result) e = q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, predicate=lambda e: e.args[0] == cs.SASL_STATUS_SERVER_FAILED) assertEquals(ERRORS[which], e.args[1]) assertContains('debug-message', e.args[2]) e = q.expect('dbus-signal', signal='ConnectionError') assertEquals(ERRORS[which], e.args[0]) assertContains('debug-message', e.args[1]) e = q.expect('dbus-signal', signal='StatusChanged') assertEquals(cs.CONN_STATUS_DISCONNECTED, e.args[0]) assertEquals(CSRS[which], e.args[1])
def complete_search2(q, bus, conn, stream): # uses other, dataform specific, fields fields = [('given', 'text-single', 'Name', []), ('family', 'text-single', 'Family Name', []), ('nickname', 'text-single', 'Nickname', [])] expected_search_keys = ['nickname', 'x-n-family', 'x-n-given'] terms = { 'x-n-family': 'Threepwood' } g_results = { 'jid': g_jid, 'given': 'Guybrush', 'family': 'Threepwood', 'nickname': 'Fancy Pants', 'email': g_jid } f_results = { 'jid': f_jid, 'given': 'Frederick', 'family': 'Threepwood', 'nickname': 'Freddie', 'email': f_jid } results = { g_jid: g_results, f_jid: f_results } search_fields, chan, c_search, c_props = do_one_search (q, bus, conn, stream, fields, expected_search_keys, terms, results.values()) assert len(search_fields) == 1 assert ('family', 'Threepwood') in search_fields, search_fields e = q.expect('dbus-signal', signal='SearchResultReceived') infos = e.args[0] assertSameSets(results.keys(), infos.keys()) for id in results.keys(): i = infos[id] r = results[id] i_ = pformat(unwrap(i)) assert ("n", [], [r['family'], r['given'], "", "", ""]) in i, i_ assert ("nickname", [], [r['nickname']]) in i, i_ assert ("email", [], [r['email']]) in i, i_ assert ("x-n-family", [], [r['family']]) in i, i_ assert ("x-n-given", [], [r['given']]) in i, i_ assert len(i) == 5, i_ search_done(q, chan, c_search, c_props)
def test2(q, bus, conn, stream): marco_pidgin = '[email protected]/Pidgin' marco_phone = '[email protected]/N900' handle = conn.get_contact_handle_sync(marco_pidgin) # pidgin comes online contact_online(q, conn, stream, marco_pidgin, PC) types = get_client_types(conn, handle) assertSameSets(['pc'], types) # phone comes online contact_online(q, conn, stream, marco_phone, PHONE, initial=False) types = get_client_types(conn, handle) assertSameSets(['pc'], types) sync_stream(q, stream) # pidgin goes offline stream.send(make_presence(marco_pidgin, type='unavailable')) # no presence signal q.expect('dbus-signal', signal='ClientTypesUpdated', args=[handle, ['phone']]) # pidgin comes back online caps, _, _ = build_stuff(PC) stream.send(make_presence(marco_pidgin, status='hello', caps=caps)) q.expect('dbus-signal', signal='ClientTypesUpdated', args=[handle, ['pc']]) attrs = conn.Contacts.GetContactAttributes([handle], [cs.CONN_IFACE_CLIENT_TYPES], False) assertContains(handle, attrs) attr = cs.CONN_IFACE_CLIENT_TYPES + '/client-types' assertContains(attr, attrs[handle]) assertEquals(['pc'], attrs[handle][attr])
def test(q, bus, conn, stream): conn.Connect() # bob is offline jid = '*****@*****.**' event = q.expect('stream-iq', query_ns=ns.ROSTER) event.stanza['type'] = 'result' item = event.query.addElement('item') item['jid'] = jid item['subscription'] = 'from' stream.send(event.stanza) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]), bob_handle = conn.RequestHandles(cs.HT_CONTACT, [jid])[0] # new ContactCapabilities ccaps_map = conn.ContactCapabilities.GetContactCapabilities([bob_handle]) assertLength(1, ccaps_map) assertLength(1, ccaps_map[bob_handle]) fixed, allowed = ccaps_map[bob_handle][0] assertEquals({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT}, fixed) assertSameSets([cs.TARGET_HANDLE], allowed) # old Capabilities all_caps = conn.Capabilities.GetCapabilities([bob_handle]) assertLength(1, all_caps) caps = all_caps[0] assertEquals((bob_handle, cs.CHANNEL_TYPE_TEXT, 3, 0), caps)
def test(q, bus, conn, stream): conn.Connect() # bob is offline jid = '*****@*****.**' event = q.expect('stream-iq', query_ns=ns.ROSTER) event.stanza['type'] = 'result' item = event.query.addElement('item') item['jid'] = jid item['subscription'] = 'from' stream.send(event.stanza) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]), bob_handle = conn.get_contact_handle_sync(jid) # new ContactCapabilities ccaps_map = get_contacts_capabilities_sync(conn, [bob_handle]) assertLength(1, ccaps_map) assertLength(1, ccaps_map[bob_handle]) fixed, allowed = ccaps_map[bob_handle][0] assertEquals( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT }, fixed) assertSameSets([cs.TARGET_HANDLE], allowed)
def openfire_search(q, bus, conn, stream): # Openfire only supports one text field and a bunch of checkboxes fields = [('search', 'text-single', 'Search', []), ('Username', 'boolean', 'Username', []), ('Name', 'boolean', 'Name', []), ('Email', 'boolean', 'Email', [])] expected_search_keys = [''] terms = { '': '*badger*' } jid = '*****@*****.**' results = {jid : { 'jid': jid, 'Name': 'Badger Badger', 'Email': jid, 'Username': '******'}} search_fields, chan, c_search, c_props = do_one_search (q, bus, conn, stream, fields, expected_search_keys, terms, results.values()) assert len(search_fields) == 4 assert ('search', '*badger*') in search_fields, search_fields assert ('Username', '1') in search_fields, search_fields assert ('Name', '1') in search_fields, search_fields assert ('Email', '1') in search_fields, search_fields r = q.expect('dbus-signal', signal='SearchResultReceived') infos = r.args[0] assertSameSets(results.keys(), infos.keys()) for id in results.keys(): i = infos[id] r = results[id] i_ = pformat(unwrap(i)) assert ("fn", [], [r['Name']]) in i, i_ assert ("email", [], [r['Email']]) in i, i_ assert len(i) == 2
def test(q, bus, conn, stream): conn.Connect() # This test can't be exactly like Gabble's because libpurple doesn't # signal that it's connected until it receives a roster; as a result, # the publish and subscribe channels already exist on startup. q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]), EventPattern('stream-authenticated'), ) event = q.expect('stream-iq', query_ns=ns.ROSTER) event.stanza['type'] = 'result' item = event.query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'both' group = item.addElement('group', content='3 letter names') item = event.query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'from' group = item.addElement('group', content='3 letter names') item = event.query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'to' stream.send(event.stanza) _, s, _ = q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]), EventPattern('dbus-signal', signal='ContactsChanged', interface=cs.CONN_IFACE_CONTACT_LIST, path=conn.object_path), EventPattern('dbus-signal', signal='ContactListStateChanged', args=[cs.CONTACT_LIST_STATE_SUCCESS]), ) amy, bob, chris = conn.get_contact_handles_sync( ['*****@*****.**', '*****@*****.**', '*****@*****.**']) # Amy, Bob and Chris are all stored on our server-side roster. # # Everyone on our roster is (falsely!) alleged to have subscribe=YES # (in fact this ought to be just Amy and Chris, because we're publishing # presence to Bob without being subscribed to his presence, but libpurple # apparently can't represent this). # # The publish value is unknown, because libpurple doesn't have # state-recovery. assertEquals([{ amy: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_UNKNOWN, ''), bob: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_UNKNOWN, ''), chris: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_UNKNOWN, ''), }, []], s.args) # the XMPP prpl puts people into some sort of group, probably called # Buddies groups = conn.Properties.Get(cs.CONN_IFACE_CONTACT_GROUPS, 'Groups') default_group = None for group in groups: if group == '3 letter names': continue if default_group is not None: raise AssertionError('Two unexplained groups: %s, %s' % (group, default_group)) default_group = group call_async(q, conn.ContactList, 'GetContactListAttributes', [cs.CONN_IFACE_CONTACT_GROUPS], False) r = q.expect('dbus-return', method='GetContactListAttributes') assertEquals(cs.SUBSCRIPTION_STATE_YES, r.value[0][amy][cs.ATTR_SUBSCRIBE]) assertEquals(cs.SUBSCRIPTION_STATE_YES, r.value[0][bob][cs.ATTR_SUBSCRIBE]) assertEquals(cs.SUBSCRIPTION_STATE_YES, r.value[0][chris][cs.ATTR_SUBSCRIBE]) assertEquals(cs.SUBSCRIPTION_STATE_UNKNOWN, r.value[0][amy][cs.ATTR_PUBLISH]) assertEquals(cs.SUBSCRIPTION_STATE_UNKNOWN, r.value[0][bob][cs.ATTR_PUBLISH]) assertEquals(cs.SUBSCRIPTION_STATE_UNKNOWN, r.value[0][chris][cs.ATTR_PUBLISH]) assertSameSets(['3 letter names'], r.value[0][amy][cs.ATTR_GROUPS]) assertSameSets(['3 letter names'], r.value[0][bob][cs.ATTR_GROUPS]) assertSameSets([default_group], r.value[0][chris][cs.ATTR_GROUPS])
def test(q, bus, conn, stream): event = q.expect('stream-iq', query_ns=ns.ROSTER) event.stanza['type'] = 'result' item = event.query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'both' item.addElement('group', content='women') item.addElement('group', content='affected-by-fdo-12791') # This is a broken roster - Amy appears twice. This should only happen # if the server is somehow buggy. This was my initial attempt at # reproducing fd.o #12791 - I doubt it's very realistic, but we shouldn't # assert, regardless of what input we get! item = event.query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'both' item.addElement('group', content='women') item = event.query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'from' item.addElement('group', content='men') # This is what was *actually* strange about the #12791 submitter's roster - # Bob appears, fully subscribed, but also there's an attempt to subscribe # to one of Bob's resources. We now ignore such items item = event.query.addElement('item') item['jid'] = '[email protected]/Resource' item['subscription'] = 'none' item['ask'] = 'subscribe' item = event.query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'to' item.addElement('group', content='men') stream.send(event.stanza) contacts = [ ('*****@*****.**', cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_YES, ''), ('*****@*****.**', cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_YES, ''), ('*****@*****.**', cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_NO, ''), ] q.expect_many( EventPattern( 'dbus-signal', signal='ContactsChangedWithID', predicate=lambda e: contacts_changed_predicate(e, conn, contacts)), EventPattern('dbus-signal', signal='GroupsCreated', predicate=lambda e: groups_created_predicate( e, ['women', 'men', 'affected-by-fdo-12791'])), ) contacts = conn.ContactList.GetContactListAttributes( [cs.CONN_IFACE_CONTACT_GROUPS], False) assertLength(3, contacts) check_contact_roster(conn, '*****@*****.**', ['women']) check_contact_roster(conn, '*****@*****.**', ['men']) check_contact_roster(conn, '*****@*****.**', ['men']) groups = conn.Properties.Get(cs.CONN_IFACE_CONTACT_GROUPS, 'Groups') assertSameSets(['men', 'women', 'affected-by-fdo-12791'], groups)
def test(q, bus, conn, stream): client = 'http://telepathy.freedesktop.org/fake-client' contact = '[email protected]/Resource' files = [("file", "File.txt", 12345, False), ("file", "Image.txt", 54321, True), ("folder", "Folder", 123, False), ("folder", "Folder no size", None, True)] test_ft_caps_from_contact(q, bus, conn, stream, contact, 2L, client) self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") jid = conn.inspect_contact_sync(self_handle) iq = IQ(stream, "set") iq['to'] = jid iq['from'] = contact session = iq.addElement("session", "http://www.google.com/session") session['type'] = "initiate" session['id'] = "2156517633" session['initiator'] = contact session.addElement("transport", "http://www.google.com/transport/p2p") description = session.addElement("description", "http://www.google.com/session/share") manifest = description.addElement("manifest") for f in files: type, name, size, image = f file = manifest.addElement(type) if size is not None: file['size'] = str(size) file.addElement("name", None, name) if image: image = file.addElement("image") image['width'] = '1200' image['height'] = '1024' protocol = description.addElement("protocol") http = protocol.addElement("http") url = http.addElement("url", None, "/temporary/ade15194140cf7b7bceafe/") url['name'] = 'source-path' url = http.addElement("url", None, "/temporary/578d715be25ddc28870d3f/") url['name'] = 'preview-path' stream.send(iq) patterns = [] found = {} def get_predicate(name, found, i): # This needs to be a function so that name, found, i # are part of a closure. # /!\ This predicate has side-effects: it writes to 'found' def predicate(e): path, props = e.args[0][0] if props[cs.CHANNEL_TYPE] != cs.CHANNEL_TYPE_FILE_TRANSFER: return False if props[cs.FT_FILENAME] == name: found[i] = (path, props) return True return predicate for i, f in enumerate(files): type, name, size, image = f if type == "folder": name = "%s.tar" % name return False patterns.append( EventPattern('dbus-signal', signal='NewChannels', predicate=get_predicate(name, found, i))) # Make sure every file transfer has a channel associated with it file_collection = None q.expect_many(*patterns) assertLength(len(files), found) channels = [] for i in found: assert found[i] is not None path, props = found[i] channels.append((path, props)) # Get the FileCollection and make sure it exists if file_collection is None: file_collection = props[cs.FT_FILE_COLLECTION] assert file_collection != '' assert file_collection is not None # FileCollection must be the same for every channel assert props[cs.FT_FILE_COLLECTION] == file_collection, props type, name, size, image = files[i] if size is None: size = 0 assertEquals(size, props[cs.FT_SIZE]) assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER, props assertSameSets([ cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA, ], props[cs.INTERFACES]) assert props[cs.TARGET_HANDLE] == 2L, props assert props[cs.TARGET_ID] == contact.replace("/Resource", ""), props assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT, props assert props[cs.REQUESTED] == False, props assert props[cs.INITIATOR_HANDLE] == 2L, props assert props[cs.INITIATOR_ID] == contact.replace("/Resource", ""), props assert props[cs.FT_STATE] == cs.FT_STATE_PENDING, props assert props[cs.FT_CONTENT_TYPE] == '', props # FT's protocol doesn't allow us the send the hash info assert props[cs.FT_CONTENT_HASH_TYPE] == cs.FILE_HASH_TYPE_NONE, props assert props[cs.FT_CONTENT_HASH] == '', props assert props[cs.FT_DESCRIPTION] == '', props assert props[cs.FT_DATE] == 0, props assert props[cs.FT_AVAILABLE_SOCKET_TYPES] == \ {cs.SOCKET_ADDRESS_TYPE_UNIX: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV4: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV6: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST]}, \ props[cs.FT_AVAILABLE_SOCKET_TYPES] assert props[cs.FT_TRANSFERRED_BYTES] == 0, props assert props[cs.FT_INITIAL_OFFSET] == 0, props event = q.expect('stream-iq', to=contact, iq_type='set', query_name='session') session_node = event.query assert session_node.attributes['type'] == 'transport-accept' # Close all but one of the channels, and make sure Gabble doesn't cancel # the multi-FT yet. terminate_pattern = EventPattern( 'stream-iq', to=contact, iq_type='set', query_name='session', predicate=lambda event: event.query['type'] == 'terminate') q.forbid_events([terminate_pattern]) for path, props in channels[:-1]: ft_chan = bus.get_object(conn.object.bus_name, path) channel = dbus.Interface(ft_chan, cs.CHANNEL) channel.Close() q.expect('dbus-signal', signal='Closed', path=path) sync_stream(q, stream) q.unforbid_all() # Now close the final channel, and make sure Gabble terminates the session. last_path, props = channels[-1] ft_chan = bus.get_object(conn.object.bus_name, last_path) channel = dbus.Interface(ft_chan, cs.CHANNEL) channel.Close() q.expect_many(terminate_pattern)
def test_some_stuff(q, bus, conn, stream): text_chan, _, _, disco_iq, owner_iq, _ = join_muc(q, bus, conn, stream, '*****@*****.**', role='moderator', affiliation='owner', also_capture=[ EventPattern('stream-iq', to='*****@*****.**', iq_type='get', query_ns=ns.DISCO_INFO), EventPattern('stream-iq', to='*****@*****.**', iq_type='get', query_ns=ns.MUC_OWNER), # We discovered that we're an owner. Emitting a signal seems # acceptable, although technically this happens before the channel # request finishes so the channel could just as well not be on the bus. EventPattern('dbus-signal', signal='PropertiesChanged', args=[cs.CHANNEL_IFACE_ROOM_CONFIG, {'CanUpdateConfiguration': True}, [] ]), ]) # This tells Gabble that the MUC is well-behaved and lets owners modify the # room description. Technically we could also pull the description out of # here, but as an implementation detail we only read configuration out of # the disco reply. handle_muc_owner_get_iq(stream, owner_iq.stanza) pc = q.expect('dbus-signal', signal='PropertiesChanged', predicate=lambda e: e.args[0] == cs.CHANNEL_IFACE_ROOM_CONFIG) _, changed, invalidated = pc.args assertEquals(['MutableProperties'], changed.keys()) assertContains('Description', changed['MutableProperties']) handle_disco_info_iq(stream, disco_iq.stanza) pc = q.expect('dbus-signal', signal='PropertiesChanged', predicate=lambda e: e.args[0] == cs.CHANNEL_IFACE_ROOM_CONFIG) q.expect('dbus-signal', signal='PropertiesChanged', args=[cs.CHANNEL_IFACE_ROOM_CONFIG, {'ConfigurationRetrieved': True}, [] ]) _, changed, invalidated = pc.args assertEquals( { 'Anonymous': True, 'Moderated': True, 'Title': ROOM_NAME, 'Description': ROOM_DESCRIPTION, 'Private': True, }, changed) assertEquals([], invalidated) config = text_chan.Properties.GetAll(cs.CHANNEL_IFACE_ROOM_CONFIG) # Verify that all of the config properties (besides the password ones) # correspond to the flags set in handle_disco_info_iq(). assertEquals(True, config['Anonymous']) assertEquals(False, config['InviteOnly']) assertEquals(0, config['Limit']) assertEquals(True, config['Moderated']) assertEquals(ROOM_NAME, config['Title']) assertEquals(ROOM_DESCRIPTION, config['Description']) assertEquals(False, config['Persistent']) assertEquals(True, config['Private']) # This is affirmed to be false both by the disco reply and by the muc#owner # reply. assertEquals(False, config['PasswordProtected']) # This comes from the muc#owner reply. assertEquals('', config['Password']) # We're a room owner, so we should be able to modify the room configuration assertEquals(True, config['CanUpdateConfiguration']) assertSameSets( ['Anonymous', 'InviteOnly', # TODO: when we understand member limit fields, add Limit 'Moderated', 'Title', 'Description', 'Persistent', 'Private', 'PasswordProtected', 'Password', ], config['MutableProperties']) props = dbus.Dictionary( { 'Password': '******', 'PasswordProtected': True, }, signature='sv') call_async(q, text_chan.RoomConfig1, 'UpdateConfiguration', props) event = q.expect('stream-iq', to='*****@*****.**', iq_type='get', query_ns=ns.MUC_OWNER) handle_muc_owner_get_iq(stream, event.stanza) event = q.expect('stream-iq', to='*****@*****.**', iq_type='set', query_ns=ns.MUC_OWNER) handle_muc_owner_set_iq(stream, event.stanza, {'password': ['foo'], 'password_protected': ['1'], }) pc, _ = q.expect_many( EventPattern('dbus-signal', signal='PropertiesChanged', predicate=lambda e: e.args[0] == cs.CHANNEL_IFACE_ROOM_CONFIG), EventPattern('dbus-return', method='UpdateConfiguration'), ) _, changed, invalidated = pc.args assertEquals(props, changed) assertEquals([], invalidated) config = text_chan.Properties.GetAll(cs.CHANNEL_IFACE_ROOM_CONFIG) assertEquals(True, config['PasswordProtected']) assertEquals('foo', config['Password']) # Check unknown fields are rejected. props = dbus.Dictionary( { 'PasswordProtected': True, 'Riding on a donkey': True, }, signature='sv') call_async(q, text_chan.RoomConfig1, 'UpdateConfiguration', props) q.expect('dbus-error', name=cs.INVALID_ARGUMENT) # Check that mis-typed fields are rejected. props = dbus.Dictionary( { 'PasswordProtected': 'foo', 'Password': True, }, signature='sv') call_async(q, text_chan.RoomConfig1, 'UpdateConfiguration', props) q.expect('dbus-error', name=cs.INVALID_ARGUMENT) # Updating no fields should be a no-op, and not wait on any network # traffic. text_chan.RoomConfig1.UpdateConfiguration({})
def test_complex_success(q, bus, conn, stream, with_extra_data=True, accept_early=False): chan, props = connect_and_get_sasl_channel(q, bus, conn) assertSameSets(MECHANISMS + ['X-TELEPATHY-PASSWORD'], props.get(cs.SASL_AVAILABLE_MECHANISMS)) call_async(q, chan.SASLAuthentication, 'StartMechanismWithData', "FOO", "") q.expect('dbus-error', method='StartMechanismWithData', name=cs.NOT_IMPLEMENTED) if with_extra_data: chan.SASLAuthentication.StartMechanismWithData("SCOTTISH-PLAY", INITIAL_RESPONSE) e = q.expect('sasl-auth', initial_response=INITIAL_RESPONSE) else: chan.SASLAuthentication.StartMechanism("SCOTTISH-PLAY") e = q.expect('sasl-auth', has_initial_response=False) authenticator = e.authenticator q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_IN_PROGRESS, '', {}]) if not with_extra_data: # send the stage directions in-band instead authenticator.challenge('') e = q.expect('dbus-signal', signal='NewChallenge', interface=cs.CHANNEL_IFACE_SASL_AUTH) # this ought to be '' but dbus-python has fd.o #28131 assert e.args in ([''], ['None']) chan.SASLAuthentication.Respond(INITIAL_RESPONSE) q.expect('sasl-response', response=INITIAL_RESPONSE) for challenge, response in CR_PAIRS: authenticator.challenge(challenge) q.expect('dbus-signal', signal='NewChallenge', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[challenge]) chan.SASLAuthentication.Respond(response) q.expect('sasl-response', response=response) if with_extra_data: authenticator.success(SUCCESS_DATA) else: # The success data is sent in-band as a challenge authenticator.challenge(SUCCESS_DATA) q.expect('dbus-signal', signal='NewChallenge', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[SUCCESS_DATA]) if accept_early: # the UI can tell that this challenge isn't actually a challenge, # it's a success in disguise chan.SASLAuthentication.AcceptSASL() q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_CLIENT_ACCEPTED, '', {}]) else: chan.SASLAuthentication.Respond(dbus.ByteArray('')) if with_extra_data: # Wocky removes the distinction between a challenge containing # success data followed by a plain success, and a success # containing initial data, so we won't get to Server_Succeeded # til we "respond" to the "challenge". However, at the XMPP level, # we shouldn't get a response to a success. q.forbid_events([EventPattern('sasl-response')]) else: q.expect('sasl-response', response='') authenticator.success(None) if not accept_early: # *now* we accept q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) # We're willing to accept this SASL transaction chan.SASLAuthentication.AcceptSASL() q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SUCCEEDED, '', {}]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) chan.Close() # ... and check that the Connection is still OK conn.Properties.Get(cs.CONN, "SelfHandle")
def test(q, bus, conn): contact1_name, conn2, contact2_name, contact2_handle_on_conn1,\ contact1_handle_on_conn2 = t.connect_two_accounts(q, bus, conn) conn1_self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") conn2_self_handle = conn2.Properties.Get(cs.CONN, "SelfHandle") # first connection: join muc muc_handle1, group1 = t.join_muc(q, conn, muc_name) # Can we request muc D-Bus tubes? properties = conn.GetAll(cs.CONN_IFACE_REQUESTS, dbus_interface=cs.PROPERTIES_IFACE) assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM}, [cs.TARGET_HANDLE, cs.TARGET_ID, cs.DBUS_TUBE_SERVICE_NAME] ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] # request a stream tube channel (new API) requestotron = dbus.Interface(conn, cs.CONN_IFACE_REQUESTS) requestotron.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: muc_name, cs.DBUS_TUBE_SERVICE_NAME: 'com.example.TestCase' }) e = q.expect('dbus-signal', signal='NewChannels') channels = e.args[0] assert len(channels) == 1 # get the list of all channels to check that newly announced ones are in it all_channels = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) path, props = channels[0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE assert props[cs.REQUESTED] == True assertSameSets([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE], props[cs.INTERFACES]) assert props[cs.DBUS_TUBE_SERVICE_NAME] == 'com.example.TestCase' assert props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS] == [ cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST ] contact1_tube = wrap_channel(bus.get_object(conn.bus_name, path), 'DBusTube') tube1_path = path assert (path, props) in all_channels, (path, props) state = contact1_tube.Properties.Get(cs.CHANNEL_IFACE_TUBE, 'State') assert state == cs.TUBE_CHANNEL_STATE_NOT_OFFERED call_async(q, contact1_tube.DBusTube, 'Offer', sample_parameters, cs.SOCKET_ACCESS_CONTROL_CREDENTIALS) _, e = q.expect_many( EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[cs.TUBE_CHANNEL_STATE_OPEN]), EventPattern('dbus-return', method='Offer')) tube_addr1 = e.value[0] state = contact1_tube.Properties.Get(cs.CHANNEL_IFACE_TUBE, 'State') assert state == cs.TUBE_CHANNEL_STATE_OPEN check_dbus_names(contact1_tube, [conn1_self_handle]) t.invite_to_muc(q, group1, conn2, contact2_handle_on_conn1, contact1_handle_on_conn2) # tubes channel is created e, dbus_names_e = q.expect_many( EventPattern('dbus-signal', signal='NewChannels'), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE)) channels = e.args[0] assert len(channels) == 1 # get the list of all channels to check that newly announced ones are in it all_channels = conn2.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) path, props = channels[0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE assert props[cs.REQUESTED] == False assertSameSets([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE], props[cs.INTERFACES]) assert props[cs.TUBE_PARAMETERS] == sample_parameters assert props[cs.DBUS_TUBE_SERVICE_NAME] == 'com.example.TestCase' assert props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS] == [ cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST ] contact2_tube = wrap_channel(bus.get_object(conn.bus_name, path), 'DBusTube') tube2_path = path assert (path, props) in all_channels, (path, props) # second connection: check DBusNamesChanged signal assert dbus_names_e.path == tube2_path added, removed = dbus_names_e.args assert added.keys() == [contact1_handle_on_conn2] assert removed == [] state = contact2_tube.Properties.Get(cs.CHANNEL_IFACE_TUBE, 'State') assert state == cs.TUBE_CHANNEL_STATE_LOCAL_PENDING # first connection: contact2 is not in the tube yet check_dbus_names(contact1_tube, [conn1_self_handle]) # second connection: accept the tube (new API) tube_addr2 = unix_socket_adr = contact2_tube.DBusTube.Accept( cs.SOCKET_ACCESS_CONTROL_CREDENTIALS) state = contact2_tube.Properties.Get(cs.CHANNEL_IFACE_TUBE, 'State') assert state == cs.TUBE_CHANNEL_STATE_OPEN e, dbus_names_e = q.expect_many( EventPattern('dbus-signal', signal='TubeChannelStateChanged', path=tube2_path, args=[cs.TUBE_CHANNEL_STATE_OPEN]), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE, path=tube1_path)) added, removed = dbus_names_e.args assert added.keys() == [contact2_handle_on_conn1] assert removed == [] check_dbus_names(contact1_tube, [conn1_self_handle, contact2_handle_on_conn1]) check_dbus_names(contact2_tube, [conn2_self_handle, contact1_handle_on_conn2]) tube2_names = contact2_tube.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) tube_conn1 = dbus.connection.Connection(tube_addr1) tube_conn2 = dbus.connection.Connection(tube_addr2) obj1 = Test(tube_conn1, q) # fire 'MySig' signal on the tube def my_sig_cb(arg, sender=None): assert tube2_names[contact1_handle_on_conn2] == sender q.append(Event('tube-dbus-signal', signal='MySig', args=[arg])) tube_conn2.add_signal_receiver(my_sig_cb, 'MySig', IFACE, path=PATH, sender_keyword='sender') obj1.MySig('hello') q.expect('tube-dbus-signal', signal='MySig', args=['hello']) # call remote method def my_method_cb(result): q.append(Event('tube-dbus-return', method='MyMethod', value=[result])) def my_method_error(e): assert False, e tube_conn2.get_object(tube2_names[contact1_handle_on_conn2], PATH).MyMethod(42, dbus_interface=IFACE, reply_handler=my_method_cb, error_handler=my_method_error) q.expect('tube-dbus-call', method='MyMethod', args=[42]) q.expect('tube-dbus-return', method='MyMethod', value=[420]) call_async(q, contact1_tube, 'Close') _, _, _, dbus_names_e = q.expect_many( EventPattern('dbus-return', method='Close'), EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE, path=tube2_path)) # Contact1 is removed from the tube added, removed = dbus_names_e.args assert added == {} assert removed == [contact1_handle_on_conn2] check_dbus_names(contact2_tube, [conn2_self_handle]) call_async(q, contact2_tube, 'Close') q.expect_many(EventPattern('dbus-return', method='Close'), EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) conn.Disconnect() conn2.Disconnect()
def test(q, bus, mc): params = dbus.Dictionary ({"account": "*****@*****.**", "password": "******"}, signature='sv') (simulated_cm, account) = create_fakecm_account (q, bus, mc, params) account_iface = dbus.Interface (account, cs.ACCOUNT) account_props = dbus.Interface (account, cs.PROPERTIES_IFACE) address_iface = dbus.Interface (account, cs.ACCOUNT_IFACE_ADDRESSING) uri_schemes = get_schemes (account_props) # initial URI scheme list is empty assertEquals (uri_schemes, []) # remove URI from empty list: address_iface.SetURISchemeAssociation ('mailto', False) uri_schemes = get_schemes (account_props) assertEquals (uri_schemes, []) # add association to empty list address_iface.SetURISchemeAssociation ('mailto', True) uri_schemes = get_schemes (account_props) assertEquals (uri_schemes, ['mailto']) # add association to list where it already resides address_iface.SetURISchemeAssociation ('mailto', True) uri_schemes = get_schemes (account_props) assertEquals (uri_schemes, ['mailto']) q.expect('dbus-signal', signal='PropertiesChanged', predicate=(lambda e: e.args[0] == cs.ACCOUNT_IFACE_ADDRESSING and set(e.args[1]['URISchemes']) == set(['mailto']))) # add a second association address_iface.SetURISchemeAssociation ('telnet', True) uri_schemes = get_schemes (account_props) assertSameSets (['mailto','telnet',], uri_schemes) q.expect('dbus-signal', signal='PropertiesChanged', predicate=(lambda e: e.args[0] == cs.ACCOUNT_IFACE_ADDRESSING and set(e.args[1]['URISchemes']) == set(['telnet', 'mailto']))) # remove associations to produce empty list address_iface.SetURISchemeAssociation ('mailto', False) address_iface.SetURISchemeAssociation ('telnet', False) uri_schemes = get_schemes (account_props) assertEquals (uri_schemes, []) # extend list to 3 schemes, with some redundant additions: address_iface.SetURISchemeAssociation ('scheme-a', True) address_iface.SetURISchemeAssociation ('scheme-b', True) address_iface.SetURISchemeAssociation ('scheme-c', True) address_iface.SetURISchemeAssociation ('scheme-a', True) address_iface.SetURISchemeAssociation ('scheme-c', True) uri_schemes = get_schemes (account_props) assertSameSets (['scheme-a','scheme-b','scheme-c'], uri_schemes) # remove a scheme that's not there from a non-empty list address_iface.SetURISchemeAssociation ('scheme-d', False) uri_schemes = get_schemes (account_props) assertSameSets (['scheme-a','scheme-b','scheme-c'], uri_schemes) # remove one that is there: address_iface.SetURISchemeAssociation ('scheme-b', False) uri_schemes = get_schemes (account_props) assertSameSets (['scheme-a','scheme-c'], uri_schemes)
def test_gtalk_weirdness(q, bus, conn, stream, room_jid): """ There's a strange bug in the Google Talk MUC server where it sends the <conflict/> stanza twice. This has been reported to their server team; but in any case it triggered a crazy bug in Gabble, so here's a regression test. """ # Implementation detail: Gabble uses the first part of your jid (if you # don't have an alias) as your room nickname, and appends an underscore a # few times before giving up. jids = ['%s/test%s' % (room_jid, x) for x in ['', '_', '__']] member, member_, member__ = jids # Gabble should never get as far as trying to join as 'test__' since # joining as 'test_' will succeed. q.forbid_events([ EventPattern('stream-presence', to=member__) ]) call_async(q, conn.Requests, 'CreateChannel', dbus.Dictionary({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: room_jid, }, signature='sv')) # Gabble first tries to join as test q.expect('stream-presence', to=member) # Google Talk says no from 'test', twice. presence = elem('presence', from_=member, type='error')( elem(ns.MUC, 'x'), elem('error', type='cancel')( elem(ns.STANZA, 'conflict'), )) stream.send(presence) stream.send(presence) # Gabble should try to join again as test_ q.expect('stream-presence', to=member_) # Since 'test_' is not in use in the MUC, joining should succeed. According # to XEP-0045 §7.1.3 <http://xmpp.org/extensions/xep-0045.html#enter-pres>: # The service MUST first send the complete list of the existing occupants # to the new occupant and only then send the new occupant's own presence # to the new occupant # but groupchat.google.com cheerfully violates this. stream.send(make_muc_presence('none', 'participant', room_jid, 'test_')) # Here's some other random person, who owns the MUC. stream.send(make_muc_presence('owner', 'moderator', room_jid, 'foobar_gmail.com')) # And here's our hypothetical other self. stream.send(make_muc_presence('none', 'participant', room_jid, 'test')) # The Gabble bug makes this time out: because Gabble thinks it's joining as # test__ it ignores the presence for test_, since it's not flagged with # code='210' to say “this is you”. (This is acceptable behaviour by the # server: it only needs to include code='210' if it's assigned the client a # name other than the one it asked for. # # The forbidden stream-presence event above doesn't blow up here because # servicetest doesn't process events on the 'stream-*' queue at all when # we're not waiting for one. But during disconnection in the test clean-up, # the forbidden event is encountered and correctly flagged up. event = q.expect('dbus-return', method='CreateChannel') path, _ = event.value text_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') # As far as Gabble's concerned, the two other participants joined # immediately after we did. We can't request handles for them before we # try to join the MUC, because until we do so, Gabble doesn't know that # room_jid is a MUC, and so considers these three JIDs to be different # resources of the same contact. There is no race between this method # returning and MembersChangedDetailed firing, because libdbus reorders # messages when you make blocking calls. handle, handle_, handle__, foobar_handle = conn.RequestHandles( cs.HT_CONTACT, jids + ['%s/foobar_gmail.com' % room_jid]) q.expect('dbus-signal', signal='MembersChangedDetailed', predicate=lambda e: e.args[0:4] == [[foobar_handle], [], [], []]) q.expect('dbus-signal', signal='MembersChangedDetailed', predicate=lambda e: e.args[0:4] == [[handle], [], [], []]) group_props = text_chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP) assertEquals(handle_, group_props['SelfHandle']) assertSameSets([handle, handle_, foobar_handle], group_props['Members'])
def test_connection(q, bus, conn, stream): event = q.expect('stream-iq', query_ns=ns.ROSTER) event.stanza['type'] = 'result' normalized_buddies = ['*****@*****.**', '*****@*****.**', '*****@*****.**'] buddies = ['*****@*****.**', '*****@*****.**', '[email protected]/resource'] for buddy in normalized_buddies: item = event.query.addElement('item') item['jid'] = buddy item['subscription'] = 'both' stream.send(event.stanza) requested, attributes = conn.Addressing.GetContactsByVCardField( "X-JABBER", buddies[:2] + ['bad!jid'] + buddies[2:], []) addresses = [] assertEquals(3, len(attributes)) assertEquals(3, len(requested)) for attr in attributes.values(): assertContains(cs.CONN_IFACE_ADDRESSING + '/addresses', attr.keys()) assertContains('x-jabber', attr[cs.CONN_IFACE_ADDRESSING + '/addresses'].keys()) addresses.append(attr[cs.CONN_IFACE_ADDRESSING + '/addresses']['x-jabber']) assertSameSets(normalized_buddies, addresses) assertSameSets(buddies, requested.keys()); normalized_buddies = ['12345', '54321'] buddies = ['12345', '54321'] bad_jid_buddies = ['-12345!CHAT.facebook.com', '*****@*****.**'] for buddy in buddies: item = event.query.addElement('item') item['jid'] = buddy item['subscription'] = 'both' stream.send(event.stanza) requested, attributes = conn.Addressing.GetContactsByVCardField( "X-FACEBOOK-ID", buddies + bad_jid_buddies, []) addresses = [] for attr in attributes.values(): assertContains(cs.CONN_IFACE_ADDRESSING + '/addresses', attr.keys()) assertContains('x-facebook-id', attr[cs.CONN_IFACE_ADDRESSING + '/addresses'].keys()) addr = attr[cs.CONN_IFACE_ADDRESSING + '/addresses']['x-facebook-id'] addresses.append(addr) assertEquals(attr[cs.CONN + '/contact-id'], "-" + addr + "@chat.facebook.com") assertSameSets(normalized_buddies, addresses) assertSameSets(buddies, requested.keys()); normalized_buddies = ['*****@*****.**', '*****@*****.**', '*****@*****.**'] buddies = ['[email protected]', '*****@*****.**', '[email protected]/resource'] normalized_schemes = ["xmpp", "xmpp", "http"] schemes = ["xmpp", "XMPP", "http"] valid_schemes = ["xmpp", "XMPP"] request_uris = [a + ":" + b for a, b in zip(schemes, buddies)] valid_request_uris = [a + ":" + b for a, b in zip(valid_schemes, buddies)] normalized_request_uris = [a + ":" + b for a, b in zip(normalized_schemes, normalized_buddies)] requested, attributes = conn.Addressing.GetContactsByURI(request_uris, []) assertEquals(2, len(attributes)) assertEquals(2, len(requested)) for attr in attributes.values(): assertContains(attr[cs.CONN_IFACE_ADDRESSING + '/uris'][0], normalized_request_uris) assertContains(cs.CONN_IFACE_ADDRESSING + '/uris', attr.keys()) assertSameSets(valid_request_uris, requested.keys())
def test_gtalk_weirdness(q, bus, conn, stream, room_jid): """ There's a strange bug in the Google Talk MUC server where it sends the <conflict/> stanza twice. This has been reported to their server team; but in any case it triggered a crazy bug in Gabble, so here's a regression test. """ # Implementation detail: Gabble uses the first part of your jid (if you # don't have an alias) as your room nickname, and appends an underscore a # few times before giving up. jids = ['%s/test%s' % (room_jid, x) for x in ['', '_', '__']] member, member_, member__ = jids # Gabble should never get as far as trying to join as 'test__' since # joining as 'test_' will succeed. q.forbid_events([EventPattern('stream-presence', to=member__)]) call_async( q, conn.Requests, 'CreateChannel', dbus.Dictionary( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: room_jid, }, signature='sv')) # Gabble first tries to join as test q.expect('stream-presence', to=member) # Google Talk says no from 'test', twice. presence = elem('presence', from_=member, type='error')(elem(ns.MUC, 'x'), elem('error', type='cancel')(elem( ns.STANZA, 'conflict'), )) stream.send(presence) stream.send(presence) # Gabble should try to join again as test_ q.expect('stream-presence', to=member_) # Since 'test_' is not in use in the MUC, joining should succeed. According # to XEP-0045 §7.1.3 <http://xmpp.org/extensions/xep-0045.html#enter-pres>: # The service MUST first send the complete list of the existing occupants # to the new occupant and only then send the new occupant's own presence # to the new occupant # but groupchat.google.com cheerfully violates this. stream.send(make_muc_presence('none', 'participant', room_jid, 'test_')) # Here's some other random person, who owns the MUC. stream.send( make_muc_presence('owner', 'moderator', room_jid, 'foobar_gmail.com')) # And here's our hypothetical other self. stream.send(make_muc_presence('none', 'participant', room_jid, 'test')) # The Gabble bug makes this time out: because Gabble thinks it's joining as # test__ it ignores the presence for test_, since it's not flagged with # code='210' to say “this is you”. (This is acceptable behaviour by the # server: it only needs to include code='210' if it's assigned the client a # name other than the one it asked for. # # The forbidden stream-presence event above doesn't blow up here because # servicetest doesn't process events on the 'stream-*' queue at all when # we're not waiting for one. But during disconnection in the test clean-up, # the forbidden event is encountered and correctly flagged up. event = q.expect('dbus-return', method='CreateChannel') path, _ = event.value text_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') # As far as Gabble's concerned, the two other participants joined # immediately after we did. We can't request handles for them before we # try to join the MUC, because until we do so, Gabble doesn't know that # room_jid is a MUC, and so considers these three JIDs to be different # resources of the same contact. There is no race between this method # returning and MembersChangedDetailed firing, because libdbus reorders # messages when you make blocking calls. handle, handle_, handle__, foobar_handle = conn.get_contact_handles_sync( jids + ['%s/foobar_gmail.com' % room_jid]) q.expect('dbus-signal', signal='MembersChangedDetailed', predicate=lambda e: e.args[0:4] == [[foobar_handle], [], [], []]) q.expect('dbus-signal', signal='MembersChangedDetailed', predicate=lambda e: e.args[0:4] == [[handle], [], [], []]) group_props = text_chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP) assertEquals(handle_, group_props['SelfHandle']) assertSameSets([handle, handle_, foobar_handle], group_props['Members'])
def request_ft_channel(self): self.ft_path, props = self.conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: self.handle, cs.FT_CONTENT_TYPE: self.file.content_type, cs.FT_FILENAME: self.file.name, cs.FT_SIZE: self.file.size, cs.FT_CONTENT_HASH_TYPE: self.file.hash_type, cs.FT_CONTENT_HASH: self.file.hash, cs.FT_DESCRIPTION: self.file.description, cs.FT_DATE: self.file.date, cs.FT_INITIAL_OFFSET: 0, }) # org.freedesktop.Telepathy.Channel D-Bus properties assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER assertSameSets( [ cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA, ], props[cs.INTERFACES]) assert props[cs.TARGET_HANDLE] == self.handle assert props[cs.TARGET_ID] == self.target assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT assert props[cs.REQUESTED] == True assert props[cs.INITIATOR_HANDLE] == self.self_handle assert props[cs.INITIATOR_ID] == self.self_handle_name # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties assert props[cs.FT_STATE] == cs.FT_STATE_PENDING assert props[cs.FT_CONTENT_TYPE] == self.file.content_type assert props[cs.FT_FILENAME].encode('utf-8') == self.file.name, props assert props[cs.FT_SIZE] == self.file.size assert props[cs.FT_CONTENT_HASH_TYPE] == self.file.hash_type assert props[cs.FT_CONTENT_HASH] == self.file.hash assert props[cs.FT_DESCRIPTION] == self.file.description assert props[cs.FT_DATE] == self.file.date assert props[cs.FT_AVAILABLE_SOCKET_TYPES] == \ {cs.SOCKET_ADDRESS_TYPE_UNIX: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV4: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV6: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST]}, \ props[cs.FT_AVAILABLE_SOCKET_TYPES] assert props[cs.FT_TRANSFERRED_BYTES] == 0 assert props[cs.FT_INITIAL_OFFSET] == 0 self.create_ft_channel() self.open = False self.offset_defined = False def initial_offset_defined_cb(offset): self.offset_defined = True assert offset == 0, offset self.ft_channel.connect_to_signal('InitialOffsetDefined', initial_offset_defined_cb) # Make sure the file transfer is of type jingle-share event = self.q.expect('stream-iq', stream=self.stream, query_name = 'session', query_ns = ns.GOOGLE_SESSION) description_node = xpath.queryForNodes('/iq/session/description', event.stanza)[0] assert description_node.uri == ns.GOOGLE_SESSION_SHARE, \ description_node.uri
def request_ft_channel(self): self.ft_path, props = self.conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: self.handle, cs.FT_CONTENT_TYPE: self.file.content_type, cs.FT_FILENAME: self.file.name, cs.FT_SIZE: self.file.size, cs.FT_CONTENT_HASH_TYPE: self.file.hash_type, cs.FT_CONTENT_HASH: self.file.hash, cs.FT_DESCRIPTION: self.file.description, cs.FT_DATE: self.file.date, cs.FT_INITIAL_OFFSET: 0, }) # org.freedesktop.Telepathy.Channel D-Bus properties assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER assertSameSets( [ cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA, ], props[cs.INTERFACES]) assert props[cs.TARGET_HANDLE] == self.handle assert props[cs.TARGET_ID] == self.target assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT assert props[cs.REQUESTED] == True assert props[cs.INITIATOR_HANDLE] == self.self_handle assert props[cs.INITIATOR_ID] == self.self_handle_name # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties assert props[cs.FT_STATE] == cs.FT_STATE_PENDING assert props[cs.FT_CONTENT_TYPE] == self.file.content_type assert props[cs.FT_FILENAME] == self.file.name, props assert props[cs.FT_SIZE] == self.file.size assert props[cs.FT_CONTENT_HASH_TYPE] == self.file.hash_type assert props[cs.FT_CONTENT_HASH] == self.file.hash assert props[cs.FT_DESCRIPTION] == self.file.description assert props[cs.FT_DATE] == self.file.date assert props[cs.FT_AVAILABLE_SOCKET_TYPES] == \ {cs.SOCKET_ADDRESS_TYPE_UNIX: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV4: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV6: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST]}, \ props[cs.FT_AVAILABLE_SOCKET_TYPES] assert props[cs.FT_TRANSFERRED_BYTES] == 0 assert props[cs.FT_INITIAL_OFFSET] == 0 self.create_ft_channel() self.open = False self.offset_defined = False def initial_offset_defined_cb(offset): self.offset_defined = True assert offset == 0, offset self.ft_channel.connect_to_signal('InitialOffsetDefined', initial_offset_defined_cb) # Make sure the file transfer is of type jingle-share event = self.q.expect('stream-iq', stream=self.stream, query_name = 'session', query_ns = ns.GOOGLE_SESSION) description_node = xpath.queryForNodes('/iq/session/description', event.stanza)[0] assert description_node.uri == ns.GOOGLE_SESSION_SHARE, \ description_node.uri
def test(q, bus, conn, stream): self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") romeo, juliet, duncan = conn.get_contact_handles_sync( ['*****@*****.**', '*****@*****.**', '*****@*****.**']) # receive some roster pushes for the "initial" state iq = IQ(stream, 'set') iq['id'] = 'roster-push' query = iq.addElement(('jabber:iq:roster', 'query')) item = query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'both' group = item.addElement('group', content='Still alive') group = item.addElement('group', content='Capulets') stream.send(iq) iq = IQ(stream, 'set') iq['id'] = 'roster-push' query = iq.addElement(('jabber:iq:roster', 'query')) item = query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'both' group = item.addElement('group', content='Still alive') stream.send(iq) iq = IQ(stream, 'set') iq['id'] = 'roster-push' query = iq.addElement(('jabber:iq:roster', 'query')) item = query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'both' stream.send(iq) sync_dbus(bus, q, conn) sync_stream(q, stream) # the XMPP prpl puts people into some sort of group, probably called # Buddies groups = conn.Properties.Get(cs.CONN_IFACE_CONTACT_GROUPS, 'Groups') default_group = None for group in groups: if group in ('Capulets', 'Still alive'): continue if default_group is not None: raise AssertionError('Two unexplained groups: %s, %s' % (group, default_group)) default_group = group call_async(q, conn.ContactList, 'GetContactListAttributes', [cs.CONN_IFACE_CONTACT_GROUPS], False) r = q.expect('dbus-return', method='GetContactListAttributes') assertSameSets(['Still alive'], r.value[0][romeo][cs.ATTR_GROUPS]) assertSameSets(['Still alive', 'Capulets'], r.value[0][juliet][cs.ATTR_GROUPS]) assertSameSets([default_group], r.value[0][duncan][cs.ATTR_GROUPS]) # We can't remove Duncan from the default group, because it's his only # group call_async(q, conn.ContactGroups, 'RemoveFromGroup', default_group, [duncan]) q.expect('dbus-error', method='RemoveFromGroup', name=cs.NOT_AVAILABLE) call_async(q, conn.ContactGroups, 'SetGroupMembers', default_group, []) q.expect('dbus-error', method='SetGroupMembers', name=cs.NOT_AVAILABLE) # SetContactGroups just doesn't do anything in this situation call_async(q, conn.ContactGroups, 'SetContactGroups', duncan, []) q.expect('dbus-return', method='SetContactGroups') call_async(q, conn.ContactList, 'GetContactListAttributes', [cs.CONN_IFACE_CONTACT_GROUPS], False) r = q.expect('dbus-return', method='GetContactListAttributes') assertSameSets([default_group], r.value[0][duncan][cs.ATTR_GROUPS]) # Make a new group and add Duncan to it call_async(q, conn.ContactGroups, 'AddToGroup', 'Scots', [duncan]) iq, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('dbus-signal', signal='GroupsChanged', args=[[duncan], ['Scots'], []]), EventPattern('dbus-return', method='AddToGroup'), ) assertEquals('*****@*****.**', iq.stanza.query.item['jid']) groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza)]) assertLength(2, groups) assertContains(default_group, groups) assertContains('Scots', groups) # Now we can remove him from the default group. Much rejoicing. call_async(q, conn.ContactGroups, 'RemoveFromGroup', default_group, [duncan]) iq, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('dbus-signal', signal='GroupsChanged', args=[[duncan], [], [default_group]]), EventPattern('dbus-return', method='RemoveFromGroup'), ) assertEquals('*****@*****.**', iq.stanza.query.item['jid']) groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza)]) assertLength(1, groups) assertContains('Scots', groups) # Test SetContactGroups, which didn't previously have proper coverage call_async(q, conn.ContactGroups, 'SetContactGroups', duncan, ['Scottish former kings']) iq, _, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('dbus-signal', signal='GroupsChanged', args=[[duncan], ['Scottish former kings'], []]), EventPattern('dbus-signal', signal='GroupsChanged', args=[[duncan], [], ['Scots']]), EventPattern('dbus-return', method='SetContactGroups'), ) assertEquals('*****@*****.**', iq.stanza.query.item['jid']) groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza)]) assertLength(2, groups) assertContains('Scots', groups) assertContains('Scottish former kings', groups) iq, = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), ) assertEquals('*****@*****.**', iq.stanza.query.item['jid']) groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza)]) assertLength(1, groups) assertContains('Scottish former kings', groups) # Romeo dies. If he drops off the roster as a result, that would be # fd.o #21294. However, to fix that bug, Haze now puts him in the # default group. call_async(q, conn.ContactGroups, 'RemoveFromGroup', 'Still alive', [romeo]) iq1, iq2, _, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('dbus-signal', signal='GroupsChanged', args=[[romeo], [default_group], []]), EventPattern('dbus-signal', signal='GroupsChanged', args=[[romeo], [], ['Still alive']]), EventPattern('dbus-return', method='RemoveFromGroup'), ) assertEquals('*****@*****.**', iq1.stanza.query.item['jid']) groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq1.stanza)]) assertLength(2, groups) assertContains('Still alive', groups) assertContains(default_group, groups) assertEquals('*****@*****.**', iq2.stanza.query.item['jid']) groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq2.stanza)]) assertLength(1, groups) assertContains(default_group, groups) # Juliet dies. She's in another group already, so the workaround for # fd.o #21294 is not active. call_async(q, conn.ContactGroups, 'SetGroupMembers', 'Still alive', []) iq, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('dbus-signal', signal='GroupsChanged', args=[[juliet], [], ['Still alive']]), EventPattern('dbus-return', method='SetGroupMembers'), ) assertEquals('*****@*****.**', iq.stanza.query.item['jid']) groups = set([str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza)]) assertLength(1, groups) assertContains('Capulets', groups) # At the end of a tragedy, everyone dies, so there's no need for this # group. call_async(q, conn.ContactGroups, 'RemoveGroup', 'Still alive') q.expect('dbus-signal', signal='GroupsRemoved', args=[['Still alive']]) # Deleting a non-empty group is allowed. (It removes everyone.) call_async(q, conn.ContactGroups, 'RemoveGroup', 'Capulets') q.expect_many( EventPattern('dbus-signal', signal='GroupsRemoved', args=[['Capulets']]), EventPattern('dbus-signal', signal='GroupsChanged', args=[[juliet], [default_group], []]), EventPattern('dbus-signal', signal='GroupsChanged', args=[[juliet], [], ['Capulets']]), )
cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: muc_name, cs.STREAM_TUBE_SERVICE: 'test'}) e = q.expect('dbus-signal', signal='NewChannels') channels = e.args[0] assert len(channels) == 1 # get the list of all channels to check that newly announced ones are in it all_channels = conn.Properties.Get(cs.CONN_IFACE_REQUESTS, 'Channels', byte_arrays=True) path, props = channels[0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE assert props[cs.REQUESTED] == True assertSameSets([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE], props[cs.INTERFACES]) assert props[cs.STREAM_TUBE_SERVICE] == 'test' assert props[cs.INITIATOR_HANDLE] == conn1_self_handle assert props[cs.INITIATOR_ID] == contact1_name assert props[cs.TARGET_ID] == muc_name assert (path, props) in all_channels, (path, props) contact1_tube = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamTube') tube1_path = path state = contact1_tube.Properties.Get(CHANNEL_IFACE_TUBE, 'State') assert state == TUBE_CHANNEL_STATE_NOT_OFFERED call_async(q, contact1_tube.StreamTube, 'Offer', SOCKET_ADDRESS_TYPE_UNIX, dbus.ByteArray(server_socket_address),
def test(q, bus, conn, stream, bytestream_cls, address_type, access_control, access_control_param): if bytestream_cls in [BytestreamS5BRelay, BytestreamS5BRelayBugged]: # disable SOCKS5 relay tests because proxy can't be used with muc # contacts atm return if access_control == cs.SOCKET_ACCESS_CONTROL_CREDENTIALS: print("Skip Socket_Access_Control_Credentials (fdo #45445)") return iq_event, disco_event = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS)) acknowledge_iq(stream, iq_event.stanza) announce_socks5_proxy(q, stream, disco_event.stanza) # join the muc call_async( q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: '*****@*****.**' }) q.expect_many( EventPattern( 'dbus-signal', signal='MembersChangedDetailed', predicate=lambda e: e.args[0] == [] and # added e.args[1] == [] and # removed e.args[2] == [] and # local pending len(e.args[3]) == 1 and # remote pending e.args[4].get('actor', 0) == 0 and e.args[4].get( 'change-reason', 0) == 0 and e.args[4]['contact-ids'][e.args[ 3][0]] == '[email protected]/test'), EventPattern('stream-presence', to='[email protected]/test')) # Send presence for other member of room. stream.send( make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob')) # Send presence for own membership of room. stream.send( make_muc_presence('none', 'participant', '*****@*****.**', 'test')) event = q.expect( 'dbus-signal', signal='MembersChangedDetailed', predicate=lambda e: len(e.args[0]) == 2 and # added e.args[1] == [] and # removed e.args[2] == [] and # local pending e.args[3] == [] and # remote pending e.args[4].get('actor', 0) == 0 and e.args[4].get('change-reason', 0) == 0 and set([e.args[4]['contact-ids'][h] for h in e.args[0]]) == set( ['[email protected]/test', '[email protected]/bob'])) for h in event.args[0]: if event.args[4]['contact-ids'][h] == '[email protected]/bob': bob_handle = h event = q.expect('dbus-return', method='CreateChannel') # Bob offers a stream tube stream_tube_id = 666 presence = make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob') tubes = presence.addElement((ns.TUBES, 'tubes')) tube = tubes.addElement((None, 'tube')) tube['type'] = 'stream' tube['service'] = 'echo' tube['id'] = str(stream_tube_id) parameters = tube.addElement((None, 'parameters')) parameter = parameters.addElement((None, 'parameter')) parameter['name'] = 's' parameter['type'] = 'str' parameter.addContent('hello') parameter = parameters.addElement((None, 'parameter')) parameter['name'] = 'ay' parameter['type'] = 'bytes' parameter.addContent('aGVsbG8=') parameter = parameters.addElement((None, 'parameter')) parameter['name'] = 'u' parameter['type'] = 'uint' parameter.addContent('123') parameter = parameters.addElement((None, 'parameter')) parameter['name'] = 'i' parameter['type'] = 'int' parameter.addContent('-123') stream.send(presence) # text channel new_event = q.expect('dbus-signal', signal='NewChannels') channels = new_event.args[0] assert len(channels) == 1 path, props = channels[0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT def new_chan_predicate(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE # tube channel is announced new_event = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) channels = new_event.args[0] assert len(channels) == 1 path, props = channels[0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE assert props[cs.INITIATOR_HANDLE] == bob_handle assert props[cs.INITIATOR_ID] == '[email protected]/bob' assertSameSets([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE], props[cs.INTERFACES]) assert props[cs.REQUESTED] == False assert props[cs.TARGET_ID] == '*****@*****.**' assert props[cs.STREAM_TUBE_SERVICE] == 'echo' assert props[cs.TUBE_PARAMETERS] == { 's': 'hello', 'ay': b'hello', 'u': 123, 'i': -123 } assert access_control in \ props[cs.STREAM_TUBE_SUPPORTED_SOCKET_TYPES][address_type] tube_chan = bus.get_object(conn.bus_name, path) tube_props = tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_STREAM_TUBE) assert tube_props['Parameters'] == sample_parameters assert tube_props['State'] == cs.TUBE_CHANNEL_STATE_LOCAL_PENDING # Accept the tube call_async(q, tube_iface, 'Accept', address_type, access_control, access_control_param, byte_arrays=True) accept_return_event, _ = q.expect_many( EventPattern('dbus-return', method='Accept'), EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[2])) address = accept_return_event.value[0] if isinstance(address, bytes): address = address.decode() socket_event, si_event, conn_id = t.connect_to_cm_socket( q, '[email protected]/bob', address_type, address, access_control, access_control_param) protocol = socket_event.protocol protocol.sendData(b"hello initiator") def accept_tube_si_connection(): bytestream, profile = create_from_si_offer(stream, q, bytestream_cls, si_event.stanza, '[email protected]/test') assert profile == ns.TUBES muc_stream_node = xpath.queryForNodes( '/iq/si/muc-stream[@xmlns="%s"]' % ns.TUBES, si_event.stanza)[0] assert muc_stream_node is not None assert muc_stream_node['tube'] == str(stream_tube_id) # set the real jid of the target as 'to' because the XMPP server changes # it when delivering the IQ result, si = bytestream.create_si_reply(si_event.stanza, 'test@localhost/Resource') si.addElement((ns.TUBES, 'tube')) stream.send(result) bytestream.wait_bytestream_open() return bytestream bytestream = accept_tube_si_connection() binary = bytestream.get_data() assert binary == b'hello initiator' # reply on the socket bytestream.send_data(b'hi joiner!') q.expect('socket-data', protocol=protocol, data=b"hi joiner!") # peer closes the bytestream bytestream.close() e = q.expect('dbus-signal', signal='ConnectionClosed') assertEquals(conn_id, e.args[0]) assertEquals(cs.CONNECTION_LOST, e.args[1]) # establish another tube connection socket_event, si_event, conn_id = t.connect_to_cm_socket( q, '[email protected]/bob', address_type, address, access_control, access_control_param) # bytestream is refused send_error_reply(stream, si_event.stanza) e, _ = q.expect_many( EventPattern('dbus-signal', signal='ConnectionClosed'), EventPattern('socket-disconnected')) assertEquals(conn_id, e.args[0]) assertEquals(cs.CONNECTION_REFUSED, e.args[1]) # establish another tube connection socket_event, si_event, conn_id = t.connect_to_cm_socket( q, '[email protected]/bob', address_type, address, access_control, access_control_param) protocol = socket_event.protocol bytestream = accept_tube_si_connection() # disconnect local socket protocol.transport.loseConnection() e, _ = q.expect_many( EventPattern('dbus-signal', signal='ConnectionClosed'), EventPattern('socket-disconnected')) assertEquals(conn_id, e.args[0]) assertEquals(cs.CANCELLED, e.args[1]) # OK, we're done disconnect_conn(q, conn, stream)
def test_join(q, bus, conn, stream, room_jid, transient_conflict): """ Tells Gabble to join a MUC, but make the first nick it tries conflict with an existing member of the MUC. If transient_conflict is True, then when Gabble successfully joins with a different nick the originally conflicting user turns out not actually to be in the room (they left while we were retrying). """ # Implementation detail: Gabble uses the first part of your jid (if you # don't have an alias) as your room nickname, and appends an underscore a # few times before giving up. member, member_ = [room_jid + '/' + x for x in ['test', 'test_']] call_async(q, conn.Requests, 'CreateChannel', dbus.Dictionary({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: room_jid, }, signature='sv')) # Gabble first tries to join as test q.expect('stream-presence', to=member) # MUC says no: there's already someone called test in room_jid presence = elem('presence', from_=member, type='error')( elem(ns.MUC, 'x'), elem('error', type='cancel')( elem(ns.STANZA, 'conflict'), )) stream.send(presence) # Gabble tries again as test_ q.expect('stream-presence', to=member_) # MUC says yes! if not transient_conflict: # Send the other member of the room's presence. This is the nick we # originally wanted. stream.send(make_muc_presence('owner', 'moderator', room_jid, 'test')) # If gabble erroneously thinks the other user's presence is our own, it'll # think that it's got the whole userlist now. If so, syncing here will make # CreateChannel incorrectly return here. sync_stream(q, stream) sync_dbus(bus, q, conn) # Send presence for own membership of room. stream.send(make_muc_presence('none', 'participant', room_jid, 'test_')) # Only now should we have finished joining the room. event = q.expect('dbus-return', method='CreateChannel') path, props = event.value text_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') group_props = unwrap(text_chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP)) t, t_ = conn.RequestHandles(cs.HT_CONTACT, [member, member_]) # Check that Gabble think our nickname in the room is test_, not test muc_self_handle = group_props['SelfHandle'] assert muc_self_handle == t_, (muc_self_handle, t_, t) members = group_props['Members'] if transient_conflict: # The user we originally conflicted with isn't actually here; check # there's exactly one member (test_). assert members == [t_], (members, t_, t) else: # Check there are exactly two members (test and test_) assertSameSets([t, t_], members) # In either case, there should be no pending members. assert len(group_props['LocalPendingMembers']) == 0, group_props assert len(group_props['RemotePendingMembers']) == 0, group_props # Check that test_'s handle owner is us, and that test (if it's there) has # no owner. handle_owners = group_props['HandleOwners'] assertEquals (conn.GetSelfHandle(), handle_owners[t_]) if not transient_conflict: assertEquals (0, handle_owners[t]) # test that closing the channel results in an unavailable message to the # right jid text_chan.Close() event = q.expect('stream-presence', to=member_) assertEquals('unavailable', event.stanza['type'])
def test(q, bus, conn, stream, use_room=False): conn.Connect() q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]), EventPattern('irc-connected')) q.expect('dbus-signal', signal='SelfHandleChanged') q.expect('dbus-signal', signal='StatusChanged', args=[0, 1]) self_handle = conn.Get(cs.CONN, 'SelfHandle', dbus_interface=cs.PROPERTIES_IFACE) request = build_request(conn, '#idletest', use_room) call_async(q, conn.Requests, 'CreateChannel', request) # Idle should try to join the channel. q.expect('stream-JOIN') # Meanwhile, in another application... call_async(q, conn, 'EnsureChannel', request, dbus_interface=cs.CONN_IFACE_REQUESTS) sync_dbus(bus, q, conn) # Now the ircd responds: stream.sendJoin('#idletest') cc, ec = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-return', method='EnsureChannel'), ) nc = q.expect('dbus-signal', signal='NewChannels') path, props = cc.value assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT assertSameSets( [cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_PASSWORD, cs.CHANNEL_IFACE_MESSAGES, cs.CHANNEL_IFACE_ROOM, cs.CHANNEL_IFACE_SUBJECT, cs.CHANNEL_IFACE_ROOM_CONFIG, cs.CHANNEL_IFACE_DESTROYABLE, ], props[cs.INTERFACES]) assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM assert props[cs.TARGET_ID] == '#idletest' assertEquals('#idletest', props[cs.ROOM_NAME]) assertEquals('', props[cs.ROOM_SERVER]) assert props[cs.REQUESTED] assert props[cs.INITIATOR_HANDLE] == self_handle assert props[cs.INITIATOR_ID] == \ conn.inspect_contacts_sync([self_handle])[0] ec_yours, ec_path, ec_props = ec.value assert not ec_yours assert ec_path == path assert ec_props == props channels = nc.args[0] assert len(channels) == 1 nc_path, nc_props = channels[0] assert nc_path == path assert nc_props == props # And again? ec_ = conn.EnsureChannel(request, dbus_interface=cs.CONN_IFACE_REQUESTS) assert ec.value == ec_ chans = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE) assert len(chans) == 1 assert chans[0] == (path, props) chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['Destroyable', 'Messages']) # Put an unacknowledged message into the channel stream.sendMessage('PRIVMSG', '#idletest', ':oi oi', prefix='lol') q.expect('dbus-signal', signal='MessageReceived', path=path) # Make sure Close()ing the channel makes it respawn. This avoids the old # bug where empathy-chat crashing booted you out of all your channels. patterns = [EventPattern('stream-PART')] q.forbid_events(patterns) chan.Close() q.expect('dbus-signal', signal='Closed', path=chan.object_path) e = q.expect('dbus-signal', signal='NewChannels') path, props = e.args[0][0] assertEquals(chan.object_path, path) # We requested the channel originally, but we didn't request it popping # back up. assertEquals(0, props[cs.INITIATOR_HANDLE]) assert not props[cs.REQUESTED] # The unacknowledged message should still be there and be marked as rescued. messages = chan.Properties.Get(cs.CHANNEL_IFACE_MESSAGES, 'PendingMessages') assertLength(1, messages) assert messages[0][0]['rescued'], messages[0] # Check that ensuring a respawned channel does what you'd expect. ec_yours, ec_path, ec_props = conn.EnsureChannel(request, dbus_interface=cs.CONN_IFACE_REQUESTS) assert not ec_yours assertEquals(chan.object_path, ec_path) assertEquals(props, ec_props) sync_stream(q, stream) q.unforbid_events(patterns) chan.RemoveMembers([self_handle], "bye bye cruel\r\nworld", dbus_interface=cs.CHANNEL_IFACE_GROUP) part_event = q.expect('stream-PART') # This is a regression test for # <https://bugs.freedesktop.org/show_bug.cgi?id=34812>, where part messages # were not correctly colon-quoted. # # It is also a regression test for # <https://bugs.freedesktop.org/show_bug.cgi?id=34840>, where newlines # weren't stripped from part messages. We check that both \r and \n are # replaced by harmless spaces. assertEquals("bye bye cruel world", part_event.data[1]) stream.sendPart('#idletest', stream.nick) q.expect('dbus-signal', signal='Closed') chans = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE) assert len(chans) == 0
def test(q, bus, conn, stream): self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") romeo, juliet, duncan = conn.get_contact_handles_sync( ['*****@*****.**', '*****@*****.**', '*****@*****.**']) # receive some roster pushes for the "initial" state iq = IQ(stream, 'set') iq['id'] = 'roster-push' query = iq.addElement(('jabber:iq:roster', 'query')) item = query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'both' group = item.addElement('group', content='Still alive') group = item.addElement('group', content='Capulets') stream.send(iq) iq = IQ(stream, 'set') iq['id'] = 'roster-push' query = iq.addElement(('jabber:iq:roster', 'query')) item = query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'both' group = item.addElement('group', content='Still alive') stream.send(iq) iq = IQ(stream, 'set') iq['id'] = 'roster-push' query = iq.addElement(('jabber:iq:roster', 'query')) item = query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'both' stream.send(iq) sync_dbus(bus, q, conn) sync_stream(q, stream) # the XMPP prpl puts people into some sort of group, probably called # Buddies groups = conn.Properties.Get(cs.CONN_IFACE_CONTACT_GROUPS, 'Groups') default_group = None for group in groups: if group in ('Capulets', 'Still alive'): continue if default_group is not None: raise AssertionError('Two unexplained groups: %s, %s' % (group, default_group)) default_group = group call_async(q, conn.ContactList, 'GetContactListAttributes', [cs.CONN_IFACE_CONTACT_GROUPS], False) r = q.expect('dbus-return', method='GetContactListAttributes') assertSameSets(['Still alive'], r.value[0][romeo][cs.ATTR_GROUPS]) assertSameSets(['Still alive', 'Capulets'], r.value[0][juliet][cs.ATTR_GROUPS]) assertSameSets([default_group], r.value[0][duncan][cs.ATTR_GROUPS]) # We can't remove Duncan from the default group, because it's his only # group call_async(q, conn.ContactGroups, 'RemoveFromGroup', default_group, [duncan]) q.expect('dbus-error', method='RemoveFromGroup', name=cs.NOT_AVAILABLE) call_async(q, conn.ContactGroups, 'SetGroupMembers', default_group, []) q.expect('dbus-error', method='SetGroupMembers', name=cs.NOT_AVAILABLE) # SetContactGroups just doesn't do anything in this situation call_async(q, conn.ContactGroups, 'SetContactGroups', duncan, []) q.expect('dbus-return', method='SetContactGroups') call_async(q, conn.ContactList, 'GetContactListAttributes', [cs.CONN_IFACE_CONTACT_GROUPS], False) r = q.expect('dbus-return', method='GetContactListAttributes') assertSameSets([default_group], r.value[0][duncan][cs.ATTR_GROUPS]) # Make a new group and add Duncan to it call_async(q, conn.ContactGroups, 'AddToGroup', 'Scots', [duncan]) iq, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('dbus-signal', signal='GroupsChanged', args=[[duncan], ['Scots'], []]), EventPattern('dbus-return', method='AddToGroup'), ) assertEquals('*****@*****.**', iq.stanza.query.item['jid']) groups = set([ str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza) ]) assertLength(2, groups) assertContains(default_group, groups) assertContains('Scots', groups) # Now we can remove him from the default group. Much rejoicing. call_async(q, conn.ContactGroups, 'RemoveFromGroup', default_group, [duncan]) iq, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('dbus-signal', signal='GroupsChanged', args=[[duncan], [], [default_group]]), EventPattern('dbus-return', method='RemoveFromGroup'), ) assertEquals('*****@*****.**', iq.stanza.query.item['jid']) groups = set([ str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza) ]) assertLength(1, groups) assertContains('Scots', groups) # Test SetContactGroups, which didn't previously have proper coverage call_async(q, conn.ContactGroups, 'SetContactGroups', duncan, ['Scottish former kings']) iq, _, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('dbus-signal', signal='GroupsChanged', args=[[duncan], ['Scottish former kings'], []]), EventPattern('dbus-signal', signal='GroupsChanged', args=[[duncan], [], ['Scots']]), EventPattern('dbus-return', method='SetContactGroups'), ) assertEquals('*****@*****.**', iq.stanza.query.item['jid']) groups = set([ str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza) ]) assertLength(2, groups) assertContains('Scots', groups) assertContains('Scottish former kings', groups) iq, = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), ) assertEquals('*****@*****.**', iq.stanza.query.item['jid']) groups = set([ str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza) ]) assertLength(1, groups) assertContains('Scottish former kings', groups) # Romeo dies. If he drops off the roster as a result, that would be # fd.o #21294. However, to fix that bug, Haze now puts him in the # default group. call_async(q, conn.ContactGroups, 'RemoveFromGroup', 'Still alive', [romeo]) iq1, iq2, _, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('dbus-signal', signal='GroupsChanged', args=[[romeo], [default_group], []]), EventPattern('dbus-signal', signal='GroupsChanged', args=[[romeo], [], ['Still alive']]), EventPattern('dbus-return', method='RemoveFromGroup'), ) assertEquals('*****@*****.**', iq1.stanza.query.item['jid']) groups = set([ str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq1.stanza) ]) assertLength(2, groups) assertContains('Still alive', groups) assertContains(default_group, groups) assertEquals('*****@*****.**', iq2.stanza.query.item['jid']) groups = set([ str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq2.stanza) ]) assertLength(1, groups) assertContains(default_group, groups) # Juliet dies. She's in another group already, so the workaround for # fd.o #21294 is not active. call_async(q, conn.ContactGroups, 'SetGroupMembers', 'Still alive', []) iq, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('dbus-signal', signal='GroupsChanged', args=[[juliet], [], ['Still alive']]), EventPattern('dbus-return', method='SetGroupMembers'), ) assertEquals('*****@*****.**', iq.stanza.query.item['jid']) groups = set([ str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza) ]) assertLength(1, groups) assertContains('Capulets', groups) # At the end of a tragedy, everyone dies, so there's no need for this # group. call_async(q, conn.ContactGroups, 'RemoveGroup', 'Still alive') q.expect('dbus-signal', signal='GroupsRemoved', args=[['Still alive']]) # Deleting a non-empty group is allowed. (It removes everyone.) call_async(q, conn.ContactGroups, 'RemoveGroup', 'Capulets') q.expect_many( EventPattern('dbus-signal', signal='GroupsRemoved', args=[['Capulets']]), EventPattern('dbus-signal', signal='GroupsChanged', args=[[juliet], [default_group], []]), EventPattern('dbus-signal', signal='GroupsChanged', args=[[juliet], [], ['Capulets']]), )
def test(q, bus, conn, stream, use_room=False): conn.Connect() q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]), EventPattern('irc-connected')) q.expect('dbus-signal', signal='SelfHandleChanged') q.expect('dbus-signal', signal='StatusChanged', args=[0, 1]) self_handle = conn.Get(cs.CONN, 'SelfHandle', dbus_interface=cs.PROPERTIES_IFACE) request = build_request(conn, '#idletest', use_room) call_async(q, conn.Requests, 'CreateChannel', request) # Idle should try to join the channel. q.expect('stream-JOIN') # Meanwhile, in another application... call_async(q, conn, 'EnsureChannel', request, dbus_interface=cs.CONN_IFACE_REQUESTS) sync_dbus(bus, q, conn) # Now the ircd responds: stream.sendJoin('#idletest') cc, ec = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-return', method='EnsureChannel'), ) nc = q.expect('dbus-signal', signal='NewChannels') path, props = cc.value assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT assertSameSets([ cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_PASSWORD, cs.CHANNEL_IFACE_MESSAGES, cs.CHANNEL_IFACE_ROOM, cs.CHANNEL_IFACE_SUBJECT, cs.CHANNEL_IFACE_ROOM_CONFIG, cs.CHANNEL_IFACE_DESTROYABLE, ], props[cs.INTERFACES]) assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM assert props[cs.TARGET_ID] == '#idletest' assertEquals('#idletest', props[cs.ROOM_NAME]) assertEquals('', props[cs.ROOM_SERVER]) assert props[cs.REQUESTED] assert props[cs.INITIATOR_HANDLE] == self_handle assert props[cs.INITIATOR_ID] == \ conn.inspect_contacts_sync([self_handle])[0] ec_yours, ec_path, ec_props = ec.value assert not ec_yours assert ec_path == path assert ec_props == props channels = nc.args[0] assert len(channels) == 1 nc_path, nc_props = channels[0] assert nc_path == path assert nc_props == props # And again? ec_ = conn.EnsureChannel(request, dbus_interface=cs.CONN_IFACE_REQUESTS) assert ec.value == ec_ chans = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE) assert len(chans) == 1 assert chans[0] == (path, props) chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['Destroyable', 'Messages']) # Put an unacknowledged message into the channel stream.sendMessage('PRIVMSG', '#idletest', ':oi oi', prefix='lol') q.expect('dbus-signal', signal='MessageReceived', path=path) # Make sure Close()ing the channel makes it respawn. This avoids the old # bug where empathy-chat crashing booted you out of all your channels. patterns = [EventPattern('stream-PART')] q.forbid_events(patterns) chan.Close() q.expect('dbus-signal', signal='Closed', path=chan.object_path) e = q.expect('dbus-signal', signal='NewChannels') path, props = e.args[0][0] assertEquals(chan.object_path, path) # We requested the channel originally, but we didn't request it popping # back up. assertEquals(0, props[cs.INITIATOR_HANDLE]) assert not props[cs.REQUESTED] # The unacknowledged message should still be there and be marked as rescued. messages = chan.Properties.Get(cs.CHANNEL_IFACE_MESSAGES, 'PendingMessages') assertLength(1, messages) assert messages[0][0]['rescued'], messages[0] # Check that ensuring a respawned channel does what you'd expect. ec_yours, ec_path, ec_props = conn.EnsureChannel( request, dbus_interface=cs.CONN_IFACE_REQUESTS) assert not ec_yours assertEquals(chan.object_path, ec_path) assertEquals(props, ec_props) sync_stream(q, stream) q.unforbid_events(patterns) chan.RemoveMembers([self_handle], "bye bye cruel\r\nworld", dbus_interface=cs.CHANNEL_IFACE_GROUP) part_event = q.expect('stream-PART') # This is a regression test for # <https://bugs.freedesktop.org/show_bug.cgi?id=34812>, where part messages # were not correctly colon-quoted. # # It is also a regression test for # <https://bugs.freedesktop.org/show_bug.cgi?id=34840>, where newlines # weren't stripped from part messages. We check that both \r and \n are # replaced by harmless spaces. assertEquals("bye bye cruel world", part_event.data[1]) stream.sendPart('#idletest', stream.nick) q.expect('dbus-signal', signal='Closed') chans = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE) assert len(chans) == 0
def test_join(q, bus, conn, stream, room_jid, transient_conflict): """ Tells Gabble to join a MUC, but make the first nick it tries conflict with an existing member of the MUC. If transient_conflict is True, then when Gabble successfully joins with a different nick the originally conflicting user turns out not actually to be in the room (they left while we were retrying). """ # Implementation detail: Gabble uses the first part of your jid (if you # don't have an alias) as your room nickname, and appends an underscore a # few times before giving up. member, member_ = [room_jid + '/' + x for x in ['test', 'test_']] call_async( q, conn.Requests, 'CreateChannel', dbus.Dictionary( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: room_jid, }, signature='sv')) # Gabble first tries to join as test q.expect('stream-presence', to=member) # MUC says no: there's already someone called test in room_jid presence = elem('presence', from_=member, type='error')(elem(ns.MUC, 'x'), elem('error', type='cancel')(elem( ns.STANZA, 'conflict'), )) stream.send(presence) # Gabble tries again as test_ q.expect('stream-presence', to=member_) # MUC says yes! if not transient_conflict: # Send the other member of the room's presence. This is the nick we # originally wanted. stream.send(make_muc_presence('owner', 'moderator', room_jid, 'test')) # If gabble erroneously thinks the other user's presence is our own, it'll # think that it's got the whole userlist now. If so, syncing here will make # CreateChannel incorrectly return here. sync_stream(q, stream) sync_dbus(bus, q, conn) # Send presence for own membership of room. stream.send(make_muc_presence('none', 'participant', room_jid, 'test_')) # Only now should we have finished joining the room. event = q.expect('dbus-return', method='CreateChannel') path, props = event.value text_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') group_props = unwrap(text_chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP)) t, t_ = conn.get_contact_handles_sync([member, member_]) # Check that Gabble think our nickname in the room is test_, not test muc_self_handle = group_props['SelfHandle'] assert muc_self_handle == t_, (muc_self_handle, t_, t) members = group_props['Members'] if transient_conflict: # The user we originally conflicted with isn't actually here; check # there's exactly one member (test_). assert members == [t_], (members, t_, t) else: # Check there are exactly two members (test and test_) assertSameSets([t, t_], members) # In either case, there should be no pending members. assert len(group_props['LocalPendingMembers']) == 0, group_props assert len(group_props['RemotePendingMembers']) == 0, group_props # Check that test_'s handle owner is us, and that test (if it's there) has # no owner. handle_owners = group_props['HandleOwners'] assertEquals(conn.Properties.Get(cs.CONN, "SelfHandle"), handle_owners[t_]) if not transient_conflict: assertEquals(0, handle_owners[t]) # test that closing the channel results in an unavailable message to the # right jid text_chan.Close() event = q.expect('stream-presence', to=member_) assertEquals('unavailable', event.stanza['type'])
def complete_search(q, bus, conn, stream, server): call_create(q, conn, server) # the channel is not yet in conn.Requests.Channels as it's not ready yet channels = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE) for path, props in channels: assert props[cs.CHANNEL_TYPE] != cs.CHANNEL_TYPE_CONTACT_SEARCH ret, nc_sig = answer_field_query(q, stream, server) path, props = ret.value props = unwrap(props) expected_search_keys = ['email', 'nickname', 'x-n-family', 'x-n-given'] assert props[cs.CONTACT_SEARCH_SERVER] == server, pformat(props) assert sorted(props[cs.CONTACT_SEARCH_ASK]) == expected_search_keys, \ pformat(props) assert cs.CONTACT_SEARCH_STATE not in props, pformat(props) # check that channel is listed in conn.Requests.Channels channels = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE) assert (path, props) in channels c = make_channel_proxy(conn, path, 'Channel') c_props = dbus.Interface(c, cs.PROPERTIES_IFACE) c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH) state = c_props.Get(cs.CHANNEL_TYPE_CONTACT_SEARCH, 'SearchState') assert state == cs.SEARCH_NOT_STARTED, state # We make a search. iq = make_search(q, c_search, c_props, server, { 'x-n-family': 'Threepwood' }) query = iq.firstChildElement() i = 0 for field in query.elements(): assert field.name == 'last', field.toXml() assert field.children[0] == u'Threepwood', field.children[0] i += 1 assert i == 1, query # Server sends the results of the search. send_results(stream, iq, results.values()) r = q.expect('dbus-signal', signal='SearchResultReceived') infos = r.args[0] assertSameSets(results.keys(), infos.keys()) for id in results.keys(): i = infos[id] r = results[id] i_ = pformat(unwrap(i)) assert ("n", [], [r[2], r[1], "", "", ""]) in i, i_ assert ("nickname", [], [r[3]]) in i, i_ assert ("email", [], [r[0]]) in i, i_ assert ("x-n-family", [], [r[2]]) in i, i_ assert ("x-n-given", [], [r[1]]) in i, i_ assert len(i) == 5, i_ ssc = q.expect('dbus-signal', signal='SearchStateChanged') assert ssc.args[0] == cs.SEARCH_COMPLETED, ssc.args # We call Stop after the search has completed; it should succeed, but leave # the channel in state Completed rather than changing it to Failed for # reason Cancelled. call_async(q, c_search, 'Stop') event = q.expect('dbus-return', method='Stop') state = c_props.Get(cs.CHANNEL_TYPE_CONTACT_SEARCH, 'SearchState') assert state == cs.SEARCH_COMPLETED, (state, cs.SEARCH_COMPLETED) c.Close() q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'), )
def test_channel_reference_identity(q, bus, conn, stream): props = connect_and_get_tls_properties (q, bus, conn) reference_identities = props["ReferenceIdentities"] assertSameSets(reference_identities, [ "example.org", "localhost"]) assertEquals(props["Hostname"], "example.org")
def test(q, bus, conn, stream, access_control): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) muc = '*****@*****.**' _, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, muc) # Bob offers a stream tube bob_bus_name = ':2.Ym9i' presence = make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob') tubes = presence.addElement((ns.TUBES, 'tubes')) tube = tubes.addElement((None, 'tube')) tube['type'] = 'dbus' tube['initiator'] = '[email protected]/bob' tube['stream-id'] = '10' tube['id'] = '1' tube['service'] = 'com.example.Test' tube['dbus-name'] = bob_bus_name parameters = tube.addElement((None, 'parameters')) parameter = parameters.addElement((None, 'parameter')) parameter['type'] = 'str' parameter['name'] = 'foo' parameter.addContent('bar') stream.send(presence) # tube channel is created def new_chan_predicate(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE event = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) channels = event.args[0] path, props = channels[0] assertEquals(cs.CHANNEL_TYPE_DBUS_TUBE, props[cs.CHANNEL_TYPE]) assertEquals('[email protected]/bob', props[cs.INITIATOR_ID]) bob_handle = props[cs.INITIATOR_HANDLE] assertSameSets([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE], props[cs.INTERFACES]) assertEquals(False, props[cs.REQUESTED]) assertEquals('*****@*****.**', props[cs.TARGET_ID]) assertEquals('com.example.Test', props[cs.DBUS_TUBE_SERVICE_NAME]) assertEquals({'foo': 'bar'}, props[cs.TUBE_PARAMETERS]) assertEquals([cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST], props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS]) tube_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'DBusTube') # only Bob is in DBusNames dbus_names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assertEquals({bob_handle: bob_bus_name}, dbus_names) call_async(q, tube_chan.DBusTube, 'Accept', access_control) return_event, names_changed1, names_changed2, presence_event = q.expect_many( EventPattern('dbus-return', method='Accept'), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE), EventPattern('stream-presence', to='[email protected]/test', predicate=lambda e: t.presence_contains_tube(e))) tube_addr = return_event.value[0] assert len(tube_addr) > 0 # check presence stanza tube_node = xpath.queryForNodes('/presence/tubes/tube', presence_event.stanza)[0] assertEquals('[email protected]/bob', tube_node['initiator']) assertEquals('com.example.Test', tube_node['service']) assertEquals('10', tube_node['stream-id']) assertEquals('dbus', tube_node['type']) assertEquals('1', tube_node['id']) self_bus_name = tube_node['dbus-name'] tubes_self_handle = tube_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'SelfHandle') assertNotEquals(0, tubes_self_handle) # both of us are in DBusNames now dbus_names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assertEquals({bob_handle: bob_bus_name, tubes_self_handle: self_bus_name}, dbus_names) added, removed = names_changed1.args assertEquals({bob_handle: bob_bus_name}, added) assertEquals([], removed) added, removed = names_changed2.args assertEquals({tubes_self_handle: self_bus_name}, added) assertEquals([], removed) tube_chan.Channel.Close() q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'))
def test_connection(q, bus, conn, stream): event = q.expect('stream-iq', query_ns=ns.ROSTER) event.stanza['type'] = 'result' normalized_buddies = ['*****@*****.**', '*****@*****.**', '*****@*****.**'] buddies = ['*****@*****.**', '*****@*****.**', '[email protected]/resource'] for buddy in normalized_buddies: item = event.query.addElement('item') item['jid'] = buddy item['subscription'] = 'both' stream.send(event.stanza) requested, attributes = conn.Addressing.GetContactsByVCardField( "X-JABBER", buddies[:2] + ['bad!jid'] + buddies[2:], []) addresses = [] assertEquals(3, len(attributes)) assertEquals(3, len(requested)) for attr in attributes.values(): assertContains(cs.CONN_IFACE_ADDRESSING + '/addresses', attr.keys()) assertContains('x-jabber', attr[cs.CONN_IFACE_ADDRESSING + '/addresses'].keys()) addresses.append(attr[cs.CONN_IFACE_ADDRESSING + '/addresses']['x-jabber']) assertSameSets(normalized_buddies, addresses) assertSameSets(buddies, requested.keys()) normalized_buddies = ['12345', '54321'] buddies = ['12345', '54321'] bad_jid_buddies = ['-12345!CHAT.facebook.com', '*****@*****.**'] for buddy in buddies: item = event.query.addElement('item') item['jid'] = buddy item['subscription'] = 'both' stream.send(event.stanza) requested, attributes = conn.Addressing.GetContactsByVCardField( "X-FACEBOOK-ID", buddies + bad_jid_buddies, []) addresses = [] for attr in attributes.values(): assertContains(cs.CONN_IFACE_ADDRESSING + '/addresses', attr.keys()) assertContains('x-facebook-id', attr[cs.CONN_IFACE_ADDRESSING + '/addresses'].keys()) addr = attr[cs.CONN_IFACE_ADDRESSING + '/addresses']['x-facebook-id'] addresses.append(addr) assertEquals(attr[cs.CONN + '/contact-id'], "-" + addr + "@chat.facebook.com") assertSameSets(normalized_buddies, addresses) assertSameSets(buddies, requested.keys()) normalized_buddies = ['*****@*****.**', '*****@*****.**', '*****@*****.**'] buddies = ['[email protected]', '*****@*****.**', '[email protected]/resource'] normalized_schemes = ["xmpp", "xmpp", "http"] schemes = ["xmpp", "XMPP", "http"] valid_schemes = ["xmpp", "XMPP"] request_uris = [a + ":" + b for a, b in zip(schemes, buddies)] valid_request_uris = [a + ":" + b for a, b in zip(valid_schemes, buddies)] normalized_request_uris = [ a + ":" + b for a, b in zip(normalized_schemes, normalized_buddies) ] requested, attributes = conn.Addressing.GetContactsByURI(request_uris, []) assertEquals(2, len(attributes)) assertEquals(2, len(requested)) for attr in attributes.values(): assertContains(attr[cs.CONN_IFACE_ADDRESSING + '/uris'][0], normalized_request_uris) assertContains(cs.CONN_IFACE_ADDRESSING + '/uris', attr.keys()) assertSameSets(valid_request_uris, requested.keys())
def test_some_stuff(q, bus, conn, stream): text_chan, _, _, disco_iq, owner_iq, _ = join_muc( q, bus, conn, stream, '*****@*****.**', role='moderator', affiliation='owner', also_capture=[ EventPattern('stream-iq', to='*****@*****.**', iq_type='get', query_ns=ns.DISCO_INFO), EventPattern('stream-iq', to='*****@*****.**', iq_type='get', query_ns=ns.MUC_OWNER), # We discovered that we're an owner. Emitting a signal seems # acceptable, although technically this happens before the channel # request finishes so the channel could just as well not be on the bus. EventPattern('dbus-signal', signal='PropertiesChanged', args=[ cs.CHANNEL_IFACE_ROOM_CONFIG, { 'CanUpdateConfiguration': True }, [] ]), ]) # This tells Gabble that the MUC is well-behaved and lets owners modify the # room description. Technically we could also pull the description out of # here, but as an implementation detail we only read configuration out of # the disco reply. handle_muc_owner_get_iq(stream, owner_iq.stanza) pc = q.expect( 'dbus-signal', signal='PropertiesChanged', predicate=lambda e: e.args[0] == cs.CHANNEL_IFACE_ROOM_CONFIG) _, changed, invalidated = pc.args assertEquals(['MutableProperties'], changed.keys()) assertContains('Description', changed['MutableProperties']) handle_disco_info_iq(stream, disco_iq.stanza) pc = q.expect( 'dbus-signal', signal='PropertiesChanged', predicate=lambda e: e.args[0] == cs.CHANNEL_IFACE_ROOM_CONFIG) q.expect('dbus-signal', signal='PropertiesChanged', args=[ cs.CHANNEL_IFACE_ROOM_CONFIG, { 'ConfigurationRetrieved': True }, [] ]) _, changed, invalidated = pc.args assertEquals( { 'Anonymous': True, 'Moderated': True, 'Title': ROOM_NAME, 'Description': ROOM_DESCRIPTION, 'Private': True, }, changed) assertEquals([], invalidated) config = text_chan.Properties.GetAll(cs.CHANNEL_IFACE_ROOM_CONFIG) # Verify that all of the config properties (besides the password ones) # correspond to the flags set in handle_disco_info_iq(). assertEquals(True, config['Anonymous']) assertEquals(False, config['InviteOnly']) assertEquals(0, config['Limit']) assertEquals(True, config['Moderated']) assertEquals(ROOM_NAME, config['Title']) assertEquals(ROOM_DESCRIPTION, config['Description']) assertEquals(False, config['Persistent']) assertEquals(True, config['Private']) # This is affirmed to be false both by the disco reply and by the muc#owner # reply. assertEquals(False, config['PasswordProtected']) # This comes from the muc#owner reply. assertEquals('', config['Password']) # We're a room owner, so we should be able to modify the room configuration assertEquals(True, config['CanUpdateConfiguration']) assertSameSets( [ 'Anonymous', 'InviteOnly', # TODO: when we understand member limit fields, add Limit 'Moderated', 'Title', 'Description', 'Persistent', 'Private', 'PasswordProtected', 'Password', ], config['MutableProperties']) props = dbus.Dictionary({ 'Password': '******', 'PasswordProtected': True, }, signature='sv') call_async(q, text_chan.RoomConfig1, 'UpdateConfiguration', props) event = q.expect('stream-iq', to='*****@*****.**', iq_type='get', query_ns=ns.MUC_OWNER) handle_muc_owner_get_iq(stream, event.stanza) event = q.expect('stream-iq', to='*****@*****.**', iq_type='set', query_ns=ns.MUC_OWNER) handle_muc_owner_set_iq(stream, event.stanza, { 'password': ['foo'], 'password_protected': ['1'], }) pc, _ = q.expect_many( EventPattern( 'dbus-signal', signal='PropertiesChanged', predicate=lambda e: e.args[0] == cs.CHANNEL_IFACE_ROOM_CONFIG), EventPattern('dbus-return', method='UpdateConfiguration'), ) _, changed, invalidated = pc.args assertEquals(props, changed) assertEquals([], invalidated) config = text_chan.Properties.GetAll(cs.CHANNEL_IFACE_ROOM_CONFIG) assertEquals(True, config['PasswordProtected']) assertEquals('foo', config['Password']) # Check unknown fields are rejected. props = dbus.Dictionary( { 'PasswordProtected': True, 'Riding on a donkey': True, }, signature='sv') call_async(q, text_chan.RoomConfig1, 'UpdateConfiguration', props) q.expect('dbus-error', name=cs.INVALID_ARGUMENT) # Check that mis-typed fields are rejected. props = dbus.Dictionary({ 'PasswordProtected': 'foo', 'Password': True, }, signature='sv') call_async(q, text_chan.RoomConfig1, 'UpdateConfiguration', props) q.expect('dbus-error', name=cs.INVALID_ARGUMENT) # Updating no fields should be a no-op, and not wait on any network # traffic. text_chan.RoomConfig1.UpdateConfiguration({})
def test(q, bus, conn, stream): client = 'http://telepathy.freedesktop.org/fake-client' contact = '[email protected]/Resource' files = [("file", "File.txt", 12345, False), ("file", "Image.txt", 54321, True), ("folder", "Folder", 123, False), ("folder", "Folder no size", None, True)] test_ft_caps_from_contact(q, bus, conn, stream, contact, 2L, client) self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") jid = conn.inspect_contact_sync(self_handle) iq = IQ(stream, "set") iq['to'] = jid iq['from'] = contact session = iq.addElement("session", "http://www.google.com/session") session['type'] = "initiate" session['id'] = "2156517633" session['initiator'] = contact session.addElement("transport", "http://www.google.com/transport/p2p") description = session.addElement("description", "http://www.google.com/session/share") manifest = description.addElement("manifest") for f in files: type, name, size, image = f file = manifest.addElement(type) if size is not None: file['size'] = str(size) file.addElement("name", None, name) if image: image = file.addElement("image") image['width'] = '1200' image['height'] = '1024' protocol = description.addElement("protocol") http = protocol.addElement("http") url = http.addElement("url", None, "/temporary/ade15194140cf7b7bceafe/") url['name'] = 'source-path' url = http.addElement("url", None, "/temporary/578d715be25ddc28870d3f/") url['name'] = 'preview-path' stream.send(iq) patterns = [] found = {} def get_predicate(name, found, i): # This needs to be a function so that name, found, i # are part of a closure. # /!\ This predicate has side-effects: it writes to 'found' def predicate(e): path, props = e.args[0][0] if props[cs.CHANNEL_TYPE] != cs.CHANNEL_TYPE_FILE_TRANSFER: return False if props[cs.FT_FILENAME] == name: found[i] = (path, props) return True return predicate for i, f in enumerate(files): type, name, size, image = f if type == "folder": name = "%s.tar" % name return False patterns.append(EventPattern('dbus-signal', signal='NewChannels', predicate=get_predicate(name, found, i))) # Make sure every file transfer has a channel associated with it file_collection = None q.expect_many(*patterns) assertLength(len(files), found) channels = [] for i in found: assert found[i] is not None path, props = found[i] channels.append((path, props)) # Get the FileCollection and make sure it exists if file_collection is None: file_collection = props[cs.FT_FILE_COLLECTION] assert file_collection != '' assert file_collection is not None # FileCollection must be the same for every channel assert props[cs.FT_FILE_COLLECTION] == file_collection, props type, name, size, image = files[i] if size is None: size = 0 assertEquals(size, props[cs.FT_SIZE]) assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER, props assertSameSets( [ cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA, ], props[cs.INTERFACES]) assert props[cs.TARGET_HANDLE] == 2L, props assert props[cs.TARGET_ID] == contact.replace("/Resource", ""), props assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT, props assert props[cs.REQUESTED] == False, props assert props[cs.INITIATOR_HANDLE] == 2L, props assert props[cs.INITIATOR_ID] == contact.replace("/Resource", ""), props assert props[cs.FT_STATE] == cs.FT_STATE_PENDING, props assert props[cs.FT_CONTENT_TYPE] == '', props # FT's protocol doesn't allow us the send the hash info assert props[cs.FT_CONTENT_HASH_TYPE] == cs.FILE_HASH_TYPE_NONE, props assert props[cs.FT_CONTENT_HASH] == '', props assert props[cs.FT_DESCRIPTION] == '', props assert props[cs.FT_DATE] == 0, props assert props[cs.FT_AVAILABLE_SOCKET_TYPES] == \ {cs.SOCKET_ADDRESS_TYPE_UNIX: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV4: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST], cs.SOCKET_ADDRESS_TYPE_IPV6: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST]}, \ props[cs.FT_AVAILABLE_SOCKET_TYPES] assert props[cs.FT_TRANSFERRED_BYTES] == 0, props assert props[cs.FT_INITIAL_OFFSET] == 0, props event = q.expect('stream-iq', to=contact, iq_type='set', query_name='session') session_node = event.query assert session_node.attributes['type'] == 'transport-accept' # Close all but one of the channels, and make sure Gabble doesn't cancel # the multi-FT yet. terminate_pattern = EventPattern('stream-iq', to=contact, iq_type='set', query_name='session', predicate=lambda event: event.query['type'] == 'terminate') q.forbid_events([terminate_pattern]) for path, props in channels[:-1]: ft_chan = bus.get_object(conn.object.bus_name, path) channel = dbus.Interface(ft_chan, cs.CHANNEL) channel.Close() q.expect('dbus-signal', signal='Closed', path=path) sync_stream(q, stream) q.unforbid_all() # Now close the final channel, and make sure Gabble terminates the session. last_path, props = channels[-1] ft_chan = bus.get_object(conn.object.bus_name, last_path) channel = dbus.Interface(ft_chan, cs.CHANNEL) channel.Close() q.expect_many(terminate_pattern)
def test_connection(q, bus, conn, stream): event = q.expect("stream-iq", query_ns=ns.ROSTER) event.stanza["type"] = "result" normalized_buddies = ["*****@*****.**", "*****@*****.**", "*****@*****.**"] buddies = ["*****@*****.**", "*****@*****.**", "[email protected]/resource"] for buddy in normalized_buddies: item = event.query.addElement("item") item["jid"] = buddy item["subscription"] = "both" stream.send(event.stanza) requested, attributes = conn.Addressing.GetContactsByVCardField( "X-JABBER", buddies[:2] + ["bad!jid"] + buddies[2:], [] ) addresses = [] assertEquals(3, len(attributes)) assertEquals(3, len(requested)) for attr in attributes.values(): assertContains(cs.CONN_IFACE_ADDRESSING + "/addresses", attr.keys()) assertContains("x-jabber", attr[cs.CONN_IFACE_ADDRESSING + "/addresses"].keys()) addresses.append(attr[cs.CONN_IFACE_ADDRESSING + "/addresses"]["x-jabber"]) assertSameSets(normalized_buddies, addresses) assertSameSets(buddies, requested.keys()) normalized_buddies = ["12345", "54321"] buddies = ["12345", "54321"] bad_jid_buddies = ["-12345!CHAT.facebook.com", "*****@*****.**"] for buddy in buddies: item = event.query.addElement("item") item["jid"] = buddy item["subscription"] = "both" stream.send(event.stanza) requested, attributes = conn.Addressing.GetContactsByVCardField("X-FACEBOOK-ID", buddies + bad_jid_buddies, []) addresses = [] for attr in attributes.values(): assertContains(cs.CONN_IFACE_ADDRESSING + "/addresses", attr.keys()) assertContains("x-facebook-id", attr[cs.CONN_IFACE_ADDRESSING + "/addresses"].keys()) addr = attr[cs.CONN_IFACE_ADDRESSING + "/addresses"]["x-facebook-id"] addresses.append(addr) assertEquals(attr[cs.CONN + "/contact-id"], "-" + addr + "@chat.facebook.com") assertSameSets(normalized_buddies, addresses) assertSameSets(buddies, requested.keys()) normalized_buddies = ["*****@*****.**", "*****@*****.**", "*****@*****.**"] buddies = ["[email protected]", "*****@*****.**", "[email protected]/resource"] normalized_schemes = ["xmpp", "xmpp", "http"] schemes = ["xmpp", "XMPP", "http"] valid_schemes = ["xmpp", "XMPP"] request_uris = [a + ":" + b for a, b in zip(schemes, buddies)] valid_request_uris = [a + ":" + b for a, b in zip(valid_schemes, buddies)] normalized_request_uris = [a + ":" + b for a, b in zip(normalized_schemes, normalized_buddies)] requested, attributes = conn.Addressing.GetContactsByURI(request_uris, []) assertEquals(2, len(attributes)) assertEquals(2, len(requested)) for attr in attributes.values(): assertContains(attr[cs.CONN_IFACE_ADDRESSING + "/uris"][0], normalized_request_uris) assertContains(cs.CONN_IFACE_ADDRESSING + "/uris", attr.keys()) assertSameSets(valid_request_uris, requested.keys())
def test(q, bus, conn): contact1_name, conn2, contact2_name, contact2_handle_on_conn1,\ contact1_handle_on_conn2 = t.connect_two_accounts(q, bus, conn) conn1_self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") conn2_self_handle = conn2.Properties.Get(cs.CONN, "SelfHandle") # first connection: join muc muc_handle1, group1 = t.join_muc(q, conn, muc_name) # Can we request muc D-Bus tubes? properties = conn.GetAll(cs.CONN_IFACE_REQUESTS, dbus_interface=cs.PROPERTIES_IFACE) assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM}, [cs.TARGET_HANDLE, cs.TARGET_ID, cs.DBUS_TUBE_SERVICE_NAME] ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] # request a stream tube channel (new API) requestotron = dbus.Interface(conn, cs.CONN_IFACE_REQUESTS) requestotron.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: muc_name, cs.DBUS_TUBE_SERVICE_NAME: 'com.example.TestCase'}) e = q.expect('dbus-signal', signal='NewChannels') channels = e.args[0] assert len(channels) == 1 # get the list of all channels to check that newly announced ones are in it all_channels = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) path, props = channels[0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE assert props[cs.REQUESTED] == True assertSameSets([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE], props[cs.INTERFACES]) assert props[cs.DBUS_TUBE_SERVICE_NAME] == 'com.example.TestCase' assert props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS] == [ cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST] contact1_tube = wrap_channel(bus.get_object(conn.bus_name, path), 'DBusTube') tube1_path = path assert (path, props) in all_channels, (path, props) state = contact1_tube.Properties.Get(cs.CHANNEL_IFACE_TUBE, 'State') assert state == cs.TUBE_CHANNEL_STATE_NOT_OFFERED call_async(q, contact1_tube.DBusTube, 'Offer', sample_parameters, cs.SOCKET_ACCESS_CONTROL_CREDENTIALS) _, e = q.expect_many( EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[cs.TUBE_CHANNEL_STATE_OPEN]), EventPattern('dbus-return', method='Offer')) tube_addr1 = e.value[0] state = contact1_tube.Properties.Get(cs.CHANNEL_IFACE_TUBE, 'State') assert state == cs.TUBE_CHANNEL_STATE_OPEN check_dbus_names(contact1_tube, [conn1_self_handle]) t.invite_to_muc(q, group1, conn2, contact2_handle_on_conn1, contact1_handle_on_conn2) # tubes channel is created e, dbus_names_e = q.expect_many( EventPattern('dbus-signal', signal='NewChannels'), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE)) channels = e.args[0] assert len(channels) == 1 # get the list of all channels to check that newly announced ones are in it all_channels = conn2.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) path, props = channels[0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE assert props[cs.REQUESTED] == False assertSameSets([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE], props[cs.INTERFACES]) assert props[cs.TUBE_PARAMETERS] == sample_parameters assert props[cs.DBUS_TUBE_SERVICE_NAME] == 'com.example.TestCase' assert props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS] == [ cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST] contact2_tube = wrap_channel(bus.get_object(conn.bus_name, path), 'DBusTube') tube2_path = path assert (path, props) in all_channels, (path, props) # second connection: check DBusNamesChanged signal assert dbus_names_e.path == tube2_path added, removed = dbus_names_e.args assert added.keys() == [contact1_handle_on_conn2] assert removed == [] state = contact2_tube.Properties.Get(cs.CHANNEL_IFACE_TUBE, 'State') assert state == cs.TUBE_CHANNEL_STATE_LOCAL_PENDING # first connection: contact2 is not in the tube yet check_dbus_names(contact1_tube, [conn1_self_handle]) # second connection: accept the tube (new API) tube_addr2 = unix_socket_adr = contact2_tube.DBusTube.Accept(cs.SOCKET_ACCESS_CONTROL_CREDENTIALS) state = contact2_tube.Properties.Get(cs.CHANNEL_IFACE_TUBE, 'State') assert state == cs.TUBE_CHANNEL_STATE_OPEN e, dbus_names_e = q.expect_many( EventPattern('dbus-signal', signal='TubeChannelStateChanged', path=tube2_path, args=[cs.TUBE_CHANNEL_STATE_OPEN]), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE, path=tube1_path)) added, removed = dbus_names_e.args assert added.keys() == [contact2_handle_on_conn1] assert removed == [] check_dbus_names(contact1_tube, [conn1_self_handle, contact2_handle_on_conn1]) check_dbus_names(contact2_tube, [conn2_self_handle, contact1_handle_on_conn2]) tube2_names = contact2_tube.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) tube_conn1 = dbus.connection.Connection(tube_addr1) tube_conn2 = dbus.connection.Connection(tube_addr2) obj1 = Test(tube_conn1, q) # fire 'MySig' signal on the tube def my_sig_cb (arg, sender=None): assert tube2_names[contact1_handle_on_conn2] == sender q.append(Event('tube-dbus-signal', signal='MySig', args=[arg])) tube_conn2.add_signal_receiver(my_sig_cb, 'MySig', IFACE, path=PATH, sender_keyword='sender') obj1.MySig('hello') q.expect('tube-dbus-signal', signal='MySig', args=['hello']) # call remote method def my_method_cb(result): q.append(Event('tube-dbus-return', method='MyMethod', value=[result])) def my_method_error(e): assert False, e tube_conn2.get_object(tube2_names[contact1_handle_on_conn2], PATH).MyMethod( 42, dbus_interface=IFACE, reply_handler=my_method_cb, error_handler=my_method_error) q.expect('tube-dbus-call', method='MyMethod', args=[42]) q.expect('tube-dbus-return', method='MyMethod', value=[420]) call_async(q, contact1_tube, 'Close') _, _, _, dbus_names_e = q.expect_many( EventPattern('dbus-return', method='Close'), EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE, path=tube2_path)) # Contact1 is removed from the tube added, removed = dbus_names_e.args assert added == {} assert removed == [contact1_handle_on_conn2] check_dbus_names(contact2_tube, [conn2_self_handle]) call_async(q, contact2_tube, 'Close') q.expect_many( EventPattern('dbus-return', method='Close'), EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) conn.Disconnect() conn2.Disconnect()
def test(q, bus, conn, stream, access_control): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) muc = '*****@*****.**' _, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, muc) # Bob offers a stream tube bob_bus_name = ':2.Ym9i' presence = make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob') tubes = presence.addElement((ns.TUBES, 'tubes')) tube = tubes.addElement((None, 'tube')) tube['type'] = 'dbus' tube['initiator'] = '[email protected]/bob' tube['stream-id'] = '10' tube['id'] = '1' tube['service'] = 'com.example.Test' tube['dbus-name'] = bob_bus_name parameters = tube.addElement((None, 'parameters')) parameter = parameters.addElement((None, 'parameter')) parameter['type'] = 'str' parameter['name'] = 'foo' parameter.addContent('bar') stream.send(presence) # tube channel is created def new_chan_predicate(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE event = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) channels = event.args[0] path, props = channels[0] assertEquals(cs.CHANNEL_TYPE_DBUS_TUBE, props[cs.CHANNEL_TYPE]) assertEquals('[email protected]/bob', props[cs.INITIATOR_ID]) bob_handle = props[cs.INITIATOR_HANDLE] assertSameSets([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE], props[cs.INTERFACES]) assertEquals(False, props[cs.REQUESTED]) assertEquals('*****@*****.**', props[cs.TARGET_ID]) assertEquals('com.example.Test', props[cs.DBUS_TUBE_SERVICE_NAME]) assertEquals({'foo': 'bar'}, props[cs.TUBE_PARAMETERS]) assertEquals([ cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST ], props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS]) tube_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'DBusTube') # only Bob is in DBusNames dbus_names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assertEquals({bob_handle: bob_bus_name}, dbus_names) call_async(q, tube_chan.DBusTube, 'Accept', access_control) return_event, names_changed1, names_changed2, presence_event = q.expect_many( EventPattern('dbus-return', method='Accept'), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE), EventPattern('stream-presence', to='[email protected]/test', predicate=lambda e: t.presence_contains_tube(e))) tube_addr = return_event.value[0] assert len(tube_addr) > 0 # check presence stanza tube_node = xpath.queryForNodes('/presence/tubes/tube', presence_event.stanza)[0] assertEquals('[email protected]/bob', tube_node['initiator']) assertEquals('com.example.Test', tube_node['service']) assertEquals('10', tube_node['stream-id']) assertEquals('dbus', tube_node['type']) assertEquals('1', tube_node['id']) self_bus_name = tube_node['dbus-name'] tubes_self_handle = tube_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'SelfHandle') assertNotEquals(0, tubes_self_handle) # both of us are in DBusNames now dbus_names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assertEquals({ bob_handle: bob_bus_name, tubes_self_handle: self_bus_name }, dbus_names) added, removed = names_changed1.args assertEquals({bob_handle: bob_bus_name}, added) assertEquals([], removed) added, removed = names_changed2.args assertEquals({tubes_self_handle: self_bus_name}, added) assertEquals([], removed) tube_chan.Channel.Close() q.expect_many(EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'))
def test(q, bus, conn, stream): call_async(q, conn.ContactList, 'GetContactListAttributes', [cs.CONN_IFACE_CONTACT_GROUPS], False) r = q.expect('dbus-return', method='GetContactListAttributes') assertLength(0, r.value[0].keys()) # receive a subscription request alice = conn.get_contact_handle_sync('*****@*****.**') presence = domish.Element(('jabber:client', 'presence')) presence['from'] = '*****@*****.**' presence['type'] = 'subscribe' presence.addElement('status', content='friend me') stream.send(presence) # it seems either libpurple or haze doesn't pass the message through q.expect_many( EventPattern('dbus-signal', signal='ContactsChangedWithID', args=[{ alice: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_ASK, ''), }, {alice: '*****@*****.**'}, {}]), ) self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") # accept call_async(q, conn.ContactList, 'AuthorizePublication', [alice]) q.expect_many( EventPattern('stream-presence', presence_type='subscribed', to='*****@*****.**'), EventPattern('dbus-signal', signal='ContactsChangedWithID', args=[{ alice: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_YES, ''), }, {alice: '*****@*****.**'}, {}]), EventPattern('dbus-return', method='AuthorizePublication'), ) # the server sends us a roster push iq = IQ(stream, 'set') iq['id'] = 'roster-push' query = iq.addElement(('jabber:iq:roster', 'query')) item = query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'from' stream.send(iq) q.expect_many( EventPattern('stream-iq', iq_type='result', predicate=lambda e: e.stanza['id'] == 'roster-push'), # She's not really on our subscribe list, but this is the closest # we can guess from libpurple EventPattern('dbus-signal', signal='ContactsChangedWithID', args=[{ alice: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_YES, ''), }, {alice: '*****@*****.**'}, {}]), # the buddy needs a group, because libpurple EventPattern('dbus-signal', signal='GroupsChanged', predicate=lambda e: e.args[0] == [alice]), ) call_async(q, conn.ContactList, 'GetContactListAttributes', [cs.CONN_IFACE_CONTACT_GROUPS], False) r = q.expect('dbus-return', method='GetContactListAttributes') assertSameSets([alice], r.value[0].keys()) # receive another subscription request queen = conn.get_contact_handle_sync('*****@*****.**') presence = domish.Element(('jabber:client', 'presence')) presence['from'] = '*****@*****.**' presence['type'] = 'subscribe' presence.addElement('status', content='Off with her head!') stream.send(presence) # it seems either libpurple or haze doesn't pass the message through q.expect_many( EventPattern('dbus-signal', signal='ContactsChangedWithID', args=[{ queen: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_ASK, ''), }, {queen: '*****@*****.**'}, {}]), ) # the contact is temporarily on our roster call_async(q, conn.ContactList, 'GetContactListAttributes', [cs.CONN_IFACE_CONTACT_GROUPS], False) r = q.expect('dbus-return', method='GetContactListAttributes') assertSameSets([alice, queen], r.value[0].keys()) # decline call_async(q, conn.ContactList, 'RemoveContacts', [queen]) q.expect_many( EventPattern('stream-presence', presence_type='unsubscribed', to='*****@*****.**'), EventPattern('dbus-signal', signal='ContactsChangedWithID', args=[{ queen: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_NO, ''), }, {queen: '*****@*****.**'}, {}]), EventPattern('dbus-return', method='RemoveContacts'), ) sync_dbus(bus, q, conn) sync_stream(q, stream) # the declined contact isn't on our roster any more call_async(q, conn.ContactList, 'GetContactListAttributes', [cs.CONN_IFACE_CONTACT_GROUPS], False) r = q.expect('dbus-return', method='GetContactListAttributes') assertSameSets([alice], r.value[0].keys()) # she's persistent presence = domish.Element(('jabber:client', 'presence')) presence['from'] = '*****@*****.**' presence['type'] = 'subscribe' presence.addElement('status', content='How dare you?') stream.send(presence) q.expect_many( EventPattern('dbus-signal', signal='ContactsChangedWithID', args=[{ queen: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_ASK, ''), }, {queen: '*****@*****.**'}, {}]), ) # disconnect with the request outstanding, to make sure we don't crash conn.Disconnect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_REQUESTED]) # make sure Haze didn't crash sync_dbus(bus, q, conn)