def check_tube_in_presence(presence, initiator): tubes_nodes = xpath.queryForNodes('/presence/tubes[@xmlns="%s"]' % ns.TUBES, presence) assert tubes_nodes is not None assert len(tubes_nodes) == 1 tube_nodes = xpath.queryForNodes('/tubes/tube', tubes_nodes[0]) assert tube_nodes is not None assert len(tube_nodes) == 1 for tube in tube_nodes: tube['type'] = 'dbus' assert tube['initiator'] == initiator assert tube['service'] == 'com.example.TestCase' dbus_stream_id = tube['stream-id'] my_bus_name = tube['dbus-name'] dbus_tube_id = tube['id'] params = {} parameter_nodes = xpath.queryForNodes('/tube/parameters/parameter', tube) for node in parameter_nodes: assert node['name'] not in params params[node['name']] = (node['type'], str(node)) assert params == {'ay': ('bytes', 'aGVsbG8='), 's': ('str', 'hello'), 'i': ('int', '-123'), 'u': ('uint', '123'), } return dbus_stream_id, my_bus_name, dbus_tube_id
def expect_socks5_reply(q): event = q.expect('stream-iq', iq_type='result') iq = event.stanza query = xpath.queryForNodes('/iq/query', iq)[0] assert query.uri == ns.BYTESTREAMS streamhost_used = xpath.queryForNodes('/query/streamhost-used', query)[0] return streamhost_used
def test_invisible_on_connect(q, bus, conn, stream): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn, ['invisible']) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) stream.send_privacy_list(get_list.stanza, [elem('item', action='deny', order='1')(elem('presence-out'))]) set_active = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', set_active.query)[0] assertEquals('invisible', active['name']) acknowledge_iq(stream, set_active.stanza) q.unforbid_events([presence_event_pattern]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
def parse_form(stanza): fields = xpath.queryForNodes('/iq/query/x/field', stanza) form = {} for field in fields: values = xpath.queryForNodes('/field/value', field) form[field['var']] = [str(v) for v in values] return form
def extract_disco_parts(stanza): identity_nodes = xpath.queryForNodes('/iq/query/identity', stanza) assertLength(1, identity_nodes) identity_node = identity_nodes[0] assertEquals('client', identity_node['category']) assertDoesNotContain('xml:lang', identity_node.attributes) identity = 'client/%s//%s' % (identity_node['type'], identity_node['name']) features = [] for feature in xpath.queryForNodes('/iq/query/feature', stanza): features.append(feature['var']) # a quick and ugly data form extractor x_nodes = xpath.queryForNodes('/iq/query/x', stanza) or [] dataforms = {} for form in x_nodes: name = None fields = {} for field in xpath.queryForNodes('/x/field', form): if field['var'] == 'FORM_TYPE': name = str(field.firstChildElement()) else: values = [str(x) for x in xpath.queryForNodes('/field/value', field)] fields[field['var']] = values if name is not None: dataforms[name] = fields return ([identity], features, dataforms)
def itemsReceived(self, item_event): """Gather items, convert to html and send the files to there proper location. """ for item in item_event.items: if item.name != 'item': # TODO - handle retract and other events continue item_id = item.getAttribute('id', str(time.time())) date_pub = xpath.queryForNodes("/item/entry/published", item) published = datetime.datetime.now() if date_pub: published = str(date_pub[0]) blog_id = None blog_ids = xpath.queryForNodes("/item/entry/id", item) if blog_ids: blog_id = str(blog_ids[0]) if item.entry.id is None: log.msg('Wrong format for entry') continue # create entry args = self.blog.atom2hash(item.entry) self.blog.updateEntry(blog_id, args) last_items = self.blog.archiveItems() # update index self.blog.updateIndex(blog_id, last_items) # update atom self.blog.updateAtom(blog_id, last_items)
def test_invisible_on_connect_fail(q, bus, conn, stream): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn) create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') # Check its name assertNotEquals([], xpath.queryForNodes('/query/list/item/presence-out', create_list.query)) acknowledge_iq(stream, create_list.stanza) set_active = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', set_active.query)[0] assertEquals('invisible', active['name']) send_error_reply(stream, set_active.stanza) q.unforbid_events([presence_event_pattern]) # Darn! At least we should have our presence set to DND. q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (6, 'dnd', '')}]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]))
def test_create_invisible_list(q, bus, conn, stream): conn.SimplePresence.SetPresence("away", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) error = domish.Element((None, 'error')) error['type'] = 'cancel' error.addElement((ns.STANZA, 'item-not-found')) send_error_reply (stream, get_list.stanza, error) create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') list_node = xpath.queryForNodes('//list', create_list.query)[0] assertEquals('invisible', list_node['name']) assertNotEquals([], xpath.queryForNodes('/query/list/item/presence-out', create_list.query)) acknowledge_iq(stream, create_list.stanza) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) assertContains("hidden", conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses"))
def test(q, bus, conn, stream): # This sidecar sends a stanza, and waits for a reply, before being # created. pattern = EventPattern('stream-iq', to='sidecar.example.com', query_ns='http://example.com/sidecar') call_async(q, conn.Sidecars1, 'EnsureSidecar', TEST_PLUGIN_IFACE + ".IQ") e = q.expect_many(pattern)[0] # The server said yes, so we should get a sidecar back! acknowledge_iq(stream, e.stanza) q.expect('dbus-return', method='EnsureSidecar') identities = ["test/app-list//Test"] features = ["com.example.test1", "com.example.test2"] ver = compute_caps_hash(identities, features, {}) iq = IQ(stream, "get") query = iq.addElement((ns.DISCO_INFO, 'query')) query['node'] = ns.GABBLE_CAPS + '#' + ver stream.send(iq) e = q.expect('stream-iq', query_ns='http://jabber.org/protocol/disco#info') returned_features = [feature['var'] for feature in xpath.queryForNodes('/iq/query/feature', e.stanza)] assertEquals(features, returned_features) returned_identities = [identity['category'] + "/" + identity['type']+"//" + identity['name'] for identity in xpath.queryForNodes('/iq/query/identity', e.stanza)] assertEquals(identities, returned_identities) new_ver = compute_caps_hash(returned_identities, returned_features, {}) assertEquals(new_ver, ver)
def _cb_disco_iq(self, iq): nodes = xpath.queryForNodes("/iq/query", iq) query = nodes[0] if query.getAttribute('node') is None: return node = query.attributes['node'] ver = node.replace("http://telepathy.freedesktop.org/caps#", "") if iq.getAttribute('type') == 'result': if FileTransferTest.caps_identities is None or \ FileTransferTest.caps_features is None: identity_nodes = xpath.queryForNodes('/iq/query/identity', iq) assertLength(1, identity_nodes) identity_node = identity_nodes[0] identity_category = identity_node['category'] identity_type = identity_node['type'] identity_name = identity_node['name'] identity = '%s/%s//%s' % (identity_category, identity_type, identity_name) FileTransferTest.caps_identities = [identity] FileTransferTest.caps_features = [] for feature in xpath.queryForNodes('/iq/query/feature', iq): FileTransferTest.caps_features.append(feature['var']) # Check if the hash matches the announced capabilities assertEquals(compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features, {}), ver) if ver == FileTransferTest.caps_ft: caps_share = compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features + \ [ns.GOOGLE_FEAT_SHARE], {}) n = query.attributes['node'].replace(ver, caps_share) query.attributes['node'] = n for feature in xpath.queryForNodes('/iq/query/feature', iq): query.children.remove(feature) for f in FileTransferTest.caps_features + [ns.GOOGLE_FEAT_SHARE]: el = domish.Element((None, 'feature')) el['var'] = f query.addChild(el) elif iq.getAttribute('type') == 'get': caps_share = compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features + \ [ns.GOOGLE_FEAT_SHARE], {}) if ver == caps_share: n = query.attributes['node'].replace(ver, FileTransferTest.caps_ft) query.attributes['node'] = n
def test_privacy_list_push_conflict(q, bus, conn, stream): test_invisible_on_connect(q, bus, conn, stream) set_id = stream.send_privacy_list_push_iq("invisible") _, req_list = q.expect_many( EventPattern('stream-iq', iq_type='result', predicate=lambda event: \ event.stanza['id'] == set_id), EventPattern('stream-iq', query_ns=ns.PRIVACY, iq_type="get")) stream.send_privacy_list(req_list.stanza, [elem('item', type='jid', value='*****@*****.**', action='allow', order='1')(elem('presence-out')), elem('item', action='deny', order='2')(elem('presence-out'))]) create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') created = xpath.queryForNodes('//list', create_list.stanza)[0] assertEquals(created["name"], 'invisible-gabble') acknowledge_iq(stream, create_list.stanza) set_active = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', set_active.query)[0] assertEquals('invisible-gabble', active['name']) acknowledge_iq(stream, set_active.stanza)
def validate_session_initiate(self, query): contents = xpath.queryForNodes( '/jingle[@xmlns="%s"]/content' % ns.JINGLE, query) audio, video = [], [] for c in contents: descs = xpath.queryForNodes( '/content/description[@xmlns="%s"]' % ns.JINGLE_RTP, c) assert len(descs) == 1, c.toXml() d = descs[0] if d['media'] == 'audio': audio.append(c['name']) elif d['media'] == 'video': video.append(c['name']) else: assert False, c.toXml() assert len(audio) + len(video) > 0, query.toXml() return (self._extract_session_id(query), audio, video)
def validate_session_initiate(self, query): contents = xpath.queryForNodes( '/jingle[@xmlns="%s"]/content' % ns.JINGLE_015, query) audio, video = [], [] for c in contents: a_desc = xpath.queryForNodes( '/content/description[@xmlns="%s"]' % ns.JINGLE_015_AUDIO, c) v_desc = xpath.queryForNodes( '/content/description[@xmlns="%s"]' % ns.JINGLE_015_VIDEO, c) if a_desc is not None: assert len(a_desc) == 1, c.toXml() assert v_desc is None audio.append(c['name']) elif v_desc is not None: assert len(v_desc) == 1, c.toXml() assert a_desc is None video.append(c['name']) else: assert False, c.toXml() assert len(audio) + len(video) > 0, query.toXml() return (self._extract_session_id(query), audio, video)
def _check_oob_iq(self, iq_event): assert iq_event.iq_type == 'set' assert iq_event.connection == self.incoming self.iq = iq_event.stanza assert self.iq['to'] == self.contact_name query = self.iq.firstChildElement() assert query.uri == 'jabber:iq:oob' url_node = xpath.queryForNodes("/iq/query/url", self.iq)[0] assert url_node['type'] == 'file' assert url_node['size'] == str(self.file.size) assert url_node['mimeType'] == self.file.content_type self.url = url_node.children[0] _, self.host, self.filename, _, _, _ = urlparse.urlparse(self.url) urllib.unquote(self.filename) == self.file.name desc_node = xpath.queryForNodes("/iq/query/desc", self.iq)[0] self.desc = desc_node.children[0] assert self.desc == self.file.description # Metadata forms forms = extract_data_forms(xpath.queryForNodes('/iq/query/x', self.iq)) if self.service_name: assertEquals({'ServiceName': [self.service_name]}, forms[ns.TP_FT_METADATA_SERVICE]) else: assert ns.TP_FT_METADATA_SERVICE not in forms if self.metadata: assertEquals(self.metadata, forms[ns.TP_FT_METADATA]) else: assert ns.TP_FT_METADATA not in forms
def disco_caps(q, stream, presence): c_nodes = xpath.queryForNodes('/presence/c', presence.stanza) assert c_nodes is not None assert len(c_nodes) == 1 hash = c_nodes[0].attributes['hash'] ver = c_nodes[0].attributes['ver'] node = c_nodes[0].attributes['node'] assert hash == 'sha-1' # ask caps request = """ <iq from='[email protected]/resource' id='disco1' to='[email protected]/resource' type='get'> <query xmlns='""" + ns.DISCO_INFO + """' node='""" + node + '#' + ver + """'/> </iq> """ stream.send(request) # receive caps event = q.expect('stream-iq', query_ns=ns.DISCO_INFO) features = [] for feature in xpath.queryForNodes('/iq/query/feature', event.stanza): features.append(feature['var']) # Check if the hash matches the announced capabilities assert ver == compute_caps_hash(['client/pc//%s' % PACKAGE_STRING], features, {}) return (event, features)
def check_si_reply(self, iq): si = xpath.queryForNodes('/iq/si[@xmlns="%s"]' % ns.SI, iq)[0] value = xpath.queryForNodes('/si/feature/x/field/value', si) assert len(value) == 1 proto = value[0] assert str(proto) == self.get_ns()
def test(q, bus, conn, stream): muc_handle = request_muc_handle(q, conn, stream, '*****@*****.**') call_async(q, conn, 'RequestChannel', cs.CHANNEL_TYPE_TEXT, cs.HT_ROOM, muc_handle, True) q.expect('stream-presence', to='[email protected]/test') # Send presence for own membership of room. stream.send( make_muc_presence('owner', 'moderator', '*****@*****.**', 'test')) iq, ret = q.expect_many( EventPattern('stream-iq', to='*****@*****.**', iq_type='get', query_ns=ns.MUC_OWNER), EventPattern('dbus-return', method='RequestChannel')) handle_muc_get_iq(stream, iq.stanza) text_chan = wrap_channel( bus.get_object(conn.bus_name, ret.value[0]), 'Text') props = dict([(name, id) for id, name, sig, flags in text_chan.TpProperties.ListProperties()]) call_async(q, text_chan.TpProperties, 'SetProperties', [(props['password'], 'foo'), (props['password-required'], True)]) event = q.expect('stream-iq', to='*****@*****.**', iq_type='get', query_ns=ns.MUC_OWNER) handle_muc_get_iq(stream, event.stanza) event = q.expect('stream-iq', to='*****@*****.**', iq_type='set', query_ns=ns.MUC_OWNER) fields = xpath.queryForNodes('/iq/query/x/field', event.stanza) form = {} for field in fields: values = xpath.queryForNodes('/field/value', field) form[field['var']] = [str(v) for v in values] assert form == {'password': ['foo'], 'password_protected': ['1'], 'muc#roomconfig_presencebroadcast' : ['moderator', 'participant', 'visitor']} acknowledge_iq(stream, event.stanza) event = q.expect('dbus-signal', signal='PropertiesChanged') assert event.args == [[(props['password'], 'foo'), (props['password-required'], True)]] q.expect('dbus-return', method='SetProperties', value=()) call_async(q, text_chan.TpProperties, 'SetProperties', [(31337, 'foo'), (props['password-required'], True)]) q.expect('dbus-error', name=cs.INVALID_ARGUMENT) call_async(q, text_chan.TpProperties, 'SetProperties', [(props['password'], True), (props['password-required'], 'foo')]) q.expect('dbus-error', name=cs.NOT_AVAILABLE) call_async(q, text_chan.TpProperties, 'SetProperties', [(props['subject-contact'], 42)]) q.expect('dbus-error', name=cs.PERMISSION_DENIED)
def test(q, bus, conn, stream): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) # send diso request m = domish.Element((None, 'iq')) m['from'] = '*****@*****.**' m['id'] = '1' m['type'] = 'get' query = m.addElement('query') query['xmlns'] = ns.DISCO_INFO stream.send(m) # wait for disco response event = q.expect('stream-iq', iq_type='result', query_ns=ns.DISCO_INFO, to='*****@*****.**') features = set([str(f['var']) for f in xpath.queryForNodes('/iq/query/feature', event.stanza)]) # OLPC NS aren't announced assert len(olpc_features.intersection(features)) == 0 # Use OLPC interface buddy_info_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo') call_async(q, buddy_info_iface, 'SetProperties', {'color': '#ff0000,#0000ff'}) # wait for <presence> stanza event = q.expect('stream-presence') c_nodes = xpath.queryForNodes('/presence/c', event.stanza) assert c_nodes is not None assert len(c_nodes) == 1 # send diso request m = domish.Element((None, 'iq')) m['from'] = '*****@*****.**' m['id'] = '2' m['type'] = 'get' query = m.addElement('query') query['xmlns'] = ns.DISCO_INFO stream.send(m) # wait for disco response event = q.expect('stream-iq', iq_type='result', query_ns=ns.DISCO_INFO, to='*****@*****.**') assert event.stanza['id'] == '2' # OLPC NS are now announced features = set([str(f['var']) for f in xpath.queryForNodes('/iq/query/feature', event.stanza)]) assert olpc_features.issubset(features)
def test(q, bus, conn, stream): self_presence = q.expect('stream-presence') c = xpath.queryForNodes('/presence/c', self_presence.stanza)[0] jid = '[email protected]/omg' # Gabble shouldn't send any disco requests to our contact during this test. q.forbid_events([ EventPattern('stream-iq', to=jid, iq_type='get', query_ns=ns.DISCO_INFO), ]) # Check that Gabble doesn't disco other clients with the same caps hash. p = make_presence(jid, caps={'node': c['node'], 'hash': c['hash'], 'ver': c['ver'], }) stream.send(p) sync_stream(q, stream) # Check that Gabble doesn't disco its own ext='' bundles (well, its own # bundles as advertised by Gabbles that don't do hashed caps) p = make_presence(jid, caps={'node': c['node'], 'ver': c['ver'], # omitting hash='' so Gabble doesn't ignore ext='' 'ext': 'voice-v1 video-v1', }) stream.send(p) sync_stream(q, stream) # Advertise some different capabilities, to change our own caps hash. add = [(cs.CHANNEL_TYPE_STREAMED_MEDIA, 2L**32-1), (cs.CHANNEL_TYPE_STREAM_TUBE, 2L**32-1), (cs.CHANNEL_TYPE_STREAM_TUBE, 2L**32-1)] remove = [] caps = conn.Capabilities.AdvertiseCapabilities(add, remove) self_presence = q.expect('stream-presence') c_ = xpath.queryForNodes('/presence/c', self_presence.stanza)[0] assertNotEquals(c['ver'], c_['ver']) # But then someone asks us for our old caps iq = IQ(stream, 'get') iq['from'] = jid query = iq.addElement((ns.DISCO_INFO, 'query')) query['node'] = c['node'] + '#' + c['ver'] stream.send(iq) # Gabble should still know what they are, and reply. This is actually quite # important: there's a bug in iChat where if you return an error to a disco # query, it just asks again, and again, and again... reply = q.expect('stream-iq', to=jid) assertEquals('result', reply.iq_type)
def _wait_activation_iq(self): e = self.q.expect('stream-iq', iq_type='set', to='proxy.localhost', query_ns=ns.BYTESTREAMS) query = xpath.queryForNodes('/iq/query', e.stanza)[0] assert query['sid'] == self.stream_id activate = xpath.queryForNodes('/iq/query/activate', e.stanza)[0] assert str(activate) == self.target self._reply_activation_iq(e.stanza)
def processMessageGC(self, elem): """Process a stanza element that is from a chatroom""" # Ignore all messages that are x-stamp (delayed / room history) # <delay xmlns='urn:xmpp:delay' stamp='2016-05-06T20:04:17.513Z' # from='[email protected]/twisted_words'/> if xpath.queryForNodes("/message/delay[@xmlns='urn:xmpp:delay']", elem): return _from = jid.JID(elem["from"]) room = _from.user res = _from.resource body = xpath.queryForString('/message/body', elem) if body is not None and len(body) >= 4 and body[:4] == "ping": self.send_groupchat(room, "%s: %s" % (res, self.get_fortune())) # Look for bot commands if re.match(r"^%s:" % (self.name,), body): self.process_groupchat_cmd(room, res, body[7:].strip()) # In order for the message to be logged, it needs to be from iembot # and have a channels attribute if res is None or res != 'iembot': return a = xpath.queryForNodes("/message/x[@xmlns='nwschat:nwsbot']", elem) if a is None or len(a) == 0: return if room not in CHATLOG: CHATLOG[room] = {'seqnum': [-1]*40, 'timestamps': [0]*40, 'log': ['']*40, 'author': ['']*40, 'product_id': ['']*40, 'txtlog': ['']*40} ts = datetime.datetime.utcnow() product_id = '' if elem.x and elem.x.hasAttribute("product_id"): product_id = elem.x['product_id'] html = xpath.queryForNodes('/message/html/body', elem) logEntry = body if html is not None: logEntry = html[0].toXml() CHATLOG[room]['seqnum'] = (CHATLOG[room]['seqnum'][1:] + [self.next_seqnum(), ]) CHATLOG[room]['timestamps'] = (CHATLOG[room]['timestamps'][1:] + [ts.strftime("%Y%m%d%H%M%S"), ]) CHATLOG[room]['author'] = CHATLOG[room]['author'][1:] + [res, ] CHATLOG[room]['product_id'] = (CHATLOG[room]['product_id'][1:] + [product_id, ]) CHATLOG[room]['log'] = CHATLOG[room]['log'][1:] + [logEntry, ] CHATLOG[room]['txtlog'] = CHATLOG[room]['txtlog'][1:] + [body, ]
def _store_shared_statuses(self, iq): _status = xpath.queryForNodes('//status', iq)[0] _show = xpath.queryForNodes('//show', iq)[0] _invisible = xpath.queryForNodes('//invisible', iq)[0] self.shared_status = (str(_status), str(_show), _invisible.getAttribute('value')) _status_lists = xpath.queryForNodes('//status-list', iq) self.shared_status_lists = {} for s in _status_lists: self.shared_status_lists[s.getAttribute('show')] = \ [str(e) for e in xpath.queryForNodes('//status', s)]
def _expect_socks5_init(self): event = self.q.expect('stream-iq', iq_type='set') iq = event.stanza query = xpath.queryForNodes('/iq/query', iq)[0] assert query.uri == ns.BYTESTREAMS mode = query['mode'] sid = query['sid'] hosts = [] for streamhost in xpath.queryForNodes('/query/streamhost', query): hosts.append((streamhost['jid'], streamhost['host'], int(streamhost['port']))) return iq['id'], mode, sid, hosts
def test_invisible_on_connect_fail_invalid_list(q, bus, conn, stream, really_invalid=False): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn, ['invisible']) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) if really_invalid: # At one point Gabble would crash if the reply was of type 'result' but # wasn't well-formed. acknowledge_iq(stream, get_list.stanza) else: stream.send_privacy_list(get_list.stanza, [elem('item', type='jid', value='*****@*****.**', action='allow', order='1')(elem('presence-out')), elem('item', action='deny', order='2')(elem('presence-out'))]) create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') created = xpath.queryForNodes('//list', create_list.stanza)[0] assertEquals(created["name"], 'invisible-gabble') acknowledge_iq (stream, create_list.stanza) q.unforbid_events([presence_event_pattern]) activate_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', activate_list.stanza)[0] assertEquals(active["name"], 'invisible-gabble') acknowledge_iq (stream, activate_list.stanza) q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (5, 'hidden', '')}]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])) # 'hidden' should not be an available status. assertContains("hidden", conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses"))
def test(q, bus, conn, stream): statuses = conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, 'Statuses') # testbusy and testaway are provided by test plugin assertContains('testbusy', statuses) assertContains('testaway', statuses) assertEquals(statuses['testbusy'][0], cs.PRESENCE_BUSY) assertEquals(statuses['testaway'][0], cs.PRESENCE_AWAY) conn.SimplePresence.SetPresence('testbusy', '') conn.Connect() # ... gabble asks for all the available lists on the server ... stream.handle_get_all_privacy_lists(q, bus, conn, lists=["foo-list", "test-busy-list", "bar-list"]) # ... gabble checks whether there's usable invisible list on the server ... get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) error = domish.Element((None, 'error')) error['type'] = 'cancel' error.addElement((ns.STANZA, 'item-not-found')) send_error_reply (stream, get_list.stanza, error) # ... since there is none, Gabble creates it ... create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') list_node = xpath.queryForNodes('//list', create_list.query)[0] assertEquals('invisible', list_node['name']) assertNotEquals([], xpath.queryForNodes('/query/list/item/presence-out', create_list.query)) acknowledge_iq(stream, create_list.stanza) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') list_node = xpath.queryForNodes('//active', get_list.query)[0] # ... and then activates the one linked with the requested status # Note: testbusy status is linked to test-busy-list by test plugin assertEquals('test-busy-list', list_node['name']) acknowledge_iq(stream, get_list.stanza) q.expect('dbus-signal', signal='PresencesChanged', args=[{1L: (cs.PRESENCE_BUSY, u'testbusy', '')}])
def test_with_xep0186(q, bus, conn, stream): statuses = conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, 'Statuses') # testbusy and testaway are provided by test plugin assertContains('testbusy', statuses) assertContains('testaway', statuses) assertEquals(statuses['testbusy'][0], cs.PRESENCE_BUSY) assertEquals(statuses['testaway'][0], cs.PRESENCE_AWAY) conn.SimplePresence.SetPresence('testbusy', '') conn.Connect() # ... gabble asks for all the available lists on the server ... stream.handle_get_all_privacy_lists(q, bus, conn, lists=["foo-list", "test-busy-list", "bar-list"]) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') list_node = xpath.queryForNodes('//active', get_list.query)[0] # ... and then activates the one linked with the requested status # Note: testbusy status is linked to test-busy-list by test plugin assertEquals('test-busy-list', list_node['name']) acknowledge_iq(stream, get_list.stanza) q.expect('dbus-signal', signal='PresencesChanged', args=[{1L: (cs.PRESENCE_BUSY, u'testbusy', '')}])
def messageReceived(self, element): body = unicode(xpath.queryForNodes('/message/body', element)[0]) words = body.split() command = words.pop(0).encode('ascii', 'replace') func = getattr(self, 'interact_' + command, None) if func: d = defer.maybeDeferred(func, element, words) else: d = defer.succeed(u"Unknown command '%s'. Try 'help'." % (command,)) def on_error(failure): log.error("Error in interactive handler '%s':\n%s", command, failure.getTraceback()) return (u'An internal error occurred in the rMake server. ' u'Please check the server logs for more information.') def on_reply(reply): if reply is None: reply = 'OK' msg = toResponse(element, 'chat') msg.addElement('body', content=unicode(reply)) threads = xpath.queryForNodes('/message/thread', element) if threads: msg.addChild(threads[0]) self.send(msg) d.addErrback(on_error) d.addCallback(on_reply)
def try_to_join_muc(q, bus, conn, stream, muc, request=None): """ Ask Gabble to join a MUC, and expect it to send <presence/> Returns: the stream-presence Event object. """ if request is None: request = { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: muc, } call_async(q, conn.Requests, 'CreateChannel', dbus.Dictionary(request, signature='sv')) join_event = q.expect('stream-presence', to='%s/test' % muc) # XEP-0045 §7.1.2 sez: # “MUC clients SHOULD signal their ability to speak the MUC protocol by # including in the initial presence stanza an empty <x/> element # qualified by the 'http://jabber.org/protocol/muc' namespace.” x_muc_nodes = xpath.queryForNodes('/presence/x[@xmlns="%s"]' % ns.MUC, join_event.stanza) assertLength(1, x_muc_nodes) return join_event
def accept_file(self): self.address = self.ft_channel.AcceptFile(self.address_type, self.access_control, self.access_control_param, self.file.offset, byte_arrays=True) state_event, iq_event = self.q.expect_many( EventPattern('dbus-signal', signal='FileTransferStateChanged'), EventPattern('stream-iq', iq_type='result')) state, reason = state_event.args assert state == cs.FT_STATE_ACCEPTED assert reason == cs.FT_STATE_CHANGE_REASON_REQUESTED # Got SI reply self.bytestream.check_si_reply(iq_event.stanza) if self.file.offset != 0: range = xpath.queryForNodes('/iq/si/file/range', iq_event.stanza)[0] assert range['offset'] == str(self.file.offset) _, events = self.bytestream.open_bytestream([], [ EventPattern('dbus-signal', signal='InitialOffsetDefined'), EventPattern('dbus-signal', signal='FileTransferStateChanged')]) offset_event, state_event = events offset = offset_event.args[0] assert offset == self.file.offset state, reason = state_event.args assert state == cs.FT_STATE_OPEN assert reason == cs.FT_STATE_CHANGE_REASON_NONE # send the beginning of the file (client didn't connect to socket yet) self.bytestream.send_data(self.file.data[self.file.offset:self.file.offset + 2])
def test_invisible_on_connect_fail_no_list(q, bus, conn, stream): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) send_error_reply(stream, get_list.stanza) q.unforbid_events([presence_event_pattern]) # Darn! At least we should have our presence set to DND. q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (6, 'dnd', '')}]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])) # 'hidden' should not be an available status. assertDoesNotContain("hidden", conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses"))
def run_incoming_test(q, bus, conn, stream, bob_leaves_room = False): jp = JingleProtocol031 () jt = JingleTest2(jp, conn, q, stream, 'test@localhost', muc + "/bob") jt.prepare() forbidden = [ no_muji_presences (muc) ] self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") _, _, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, muc) presence = make_muc_presence('owner', 'moderator', muc, 'bob') muji = ('muji', ns.MUJI, {}, [('content', ns.MUJI, { "name": "Voice" }, [( 'description', ns.JINGLE_RTP, {"media": "audio"}, jt.generate_payloads(jt.audio_codecs))])]) presence.addChild(jp._simple_xml(muji)) stream.send(presence) e = q.expect ('dbus-signal', signal='NewChannels', predicate=lambda e: \ e.args[0][0][1][cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_CALL ) (path, props) = e.args[0][0] assertContains((cs.CHANNEL_TYPE_CALL + '.InitialAudio', True), props.items()) assertContains((cs.CHANNEL_TYPE_CALL + '.InitialVideo', False), props.items()) general_tests (jp, q, bus, conn, stream, path, props) channel = bus.get_object (conn.bus_name, path) props = channel.GetAll (cs.CHANNEL_TYPE_CALL, dbus_interface = dbus.PROPERTIES_IFACE) content = bus.get_object (conn.bus_name, props['Contents'][0]) check_state (q, channel, cs.CALL_STATE_PENDING_RECEIVER) md = jt.get_call_audio_md_dbus() check_and_accept_offer (q, bus, conn, self_handle, content, md) channel.Accept (dbus_interface=cs.CHANNEL_TYPE_CALL) # Preparing stanza e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Codecs stanza e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Gabble shouldn't send new presences for a while q.forbid_events(forbidden) e = q.expect ('dbus-signal', signal = 'StreamsAdded') cstream = bus.get_object (conn.bus_name, e.args[0][0]) cstream.SetCredentials(jt.ufrag, jt.pwd, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) candidates = jt.get_call_remote_transports_dbus () cstream.AddCandidates (candidates, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) jt.parse_session_initiate (e.query) jt.accept() # Bob adds a Video content presence = make_muc_presence('owner', 'moderator', muc, 'bob') presence.addElement ((ns.MUJI, 'muji')).addElement('preparing') stream.send(presence) presence = make_muc_presence('owner', 'moderator', muc, 'bob') muji = ('muji', ns.MUJI, {}, [('content', ns.MUJI, { "name": "Voice" }, [( 'description', ns.JINGLE_RTP, {"media": "audio"}, jt.generate_payloads(jt.audio_codecs))]), ('content', ns.MUJI, { "name": "Camera" }, [( 'description', ns.JINGLE_RTP, {"media": "video"}, jt.generate_payloads(jt.video_codecs))]), ]) presence.addChild(jp._simple_xml(muji)) stream.send(presence) # Gabble noticed bob added a content e = q.expect('dbus-signal', signal = 'ContentAdded') q.unforbid_events (forbidden) content = bus.get_object (conn.bus_name, e.args[0]) check_and_accept_offer (q, bus, conn, self_handle, content, jt.get_call_video_codecs_dbus(), check_codecs_changed = False) # Gabble sends a presence to prepare e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Gabble sends a presence with the video codecs e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Gabble adds a content to the jingle session and thus a stream is added e = q.expect ('dbus-signal', signal = 'StreamsAdded') cstream = bus.get_object (conn.bus_name, e.args[0][0]) candidates = jt.get_call_remote_transports_dbus () cstream.AddCandidates (candidates, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) # And now the content-add on the jingle streams e = q.expect('stream-iq', to = muc + "/bob", predicate = lambda x: \ xpath.queryForNodes("/iq/jingle[@action='content-add']", x.stanza)) # Bob leaves the call, bye bob if bob_leaves_room: presence = make_muc_presence('owner', 'moderator', muc, 'bob') presence['type'] = 'unavailable' else: presence = make_muc_presence('owner', 'moderator', muc, 'bob') stream.send(presence) (cmembers, _, _) = q.expect_many( EventPattern ('dbus-signal', signal = 'CallMembersChanged'), # Audio and video stream EventPattern ('dbus-signal', signal = 'StreamsRemoved'), EventPattern ('dbus-signal', signal = 'StreamsRemoved')) # Just bob left assertLength (1, cmembers.args[1])
def test_hash(q, bus, conn, stream, contact, contact_handle, client): presence = make_presence(contact, status='hello') stream.send(presence) q.expect('dbus-signal', signal='PresencesChanged', args=[{ contact_handle: (2, u'available', 'hello') }]) # no special capabilities for rcc in get_contacts_capabilities_sync( conn, [contact_handle])[contact_handle]: assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE)) # send updated presence with Jingle caps info presence = make_presence(contact, status='hello', caps={ 'node': client, 'ver': '0.1', }) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + '0.1' # send good reply send_disco_reply(stream, event.stanza, [], jingle_av_features) # we can now do audio calls cc, = q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), ) assert_rccs_callable(cc.args[0][contact_handle]) assertEquals(cc.args[0], get_contacts_capabilities_sync(conn, [contact_handle])) # Send presence without any capabilities. XEP-0115 §8.4 Caps Optimization # says “receivers of presence notifications MUST NOT expect an annotation # on every presence notification they receive”, so the contact should still # be media-capable afterwards. stream.send(make_presence(contact, status='very capable')) q.expect('dbus-signal', signal='PresencesChanged', args=[{ contact_handle: (2, u'available', 'very capable') }]) # still exactly the same capabilities assertEquals(cc.args[0], get_contacts_capabilities_sync(conn, [contact_handle])) # send bogus presence caps = { 'node': client, 'ver': 'ceci=nest=pas=un=hash', 'hash': 'sha-1', } presence = make_presence(contact, status='hello', caps=caps) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + caps['ver'] # send bogus reply send_disco_reply(stream, event.stanza, [], ['http://jabber.org/protocol/bogus-feature']) # don't receive any D-Bus signal forbidden = [ EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), ] q.forbid_events(forbidden) sync_dbus(bus, q, conn) sync_stream(q, stream) # send presence with empty caps presence = make_presence(contact, status='hello', caps={ 'node': client, 'ver': '0.0', }) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + '0.0' # still don't receive any D-Bus signal sync_dbus(bus, q, conn) # send good reply q.unforbid_events(forbidden) result = make_result_iq(stream, event.stanza) query = result.firstChildElement() stream.send(result) # we can now do nothing cc, = q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), ) for rcc in cc.args[0][contact_handle]: assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE)) assert_rccs_not_callable(cc.args[0][contact_handle]) assertEquals(cc.args[0], get_contacts_capabilities_sync(conn, [contact_handle])) # send correct presence ver = compute_caps_hash(some_identities, jingle_av_features, fake_client_dataforms) caps = { 'node': client, 'ver': ver, 'hash': 'sha-1', } presence = make_presence(contact, status='hello', caps=caps) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + caps['ver'] # don't receive any D-Bus signal q.forbid_events(forbidden) sync_dbus(bus, q, conn) q.unforbid_events(forbidden) # send good reply send_disco_reply(stream, event.stanza, some_identities, jingle_av_features, fake_client_dataforms) # we can now do audio calls cc, = q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), ) assert_rccs_callable(cc.args[0][contact_handle]) assertEquals(cc.args[0], get_contacts_capabilities_sync(conn, [contact_handle]))
def receive_caps(q, bus, conn, service, contact, contact_handle, features, expected_caps, expect_disco=True, expect_ccc=True): ver = compute_caps_hash([], features, {}) txt_record = { "txtvers": "1", "status": "avail", "node": client, "ver": ver, "hash": "sha-1" } listener, port = setup_stream_listener(q, contact) AvahiAnnouncer(contact, "_presence._tcp", port, txt_record) if expect_disco: # Salut looks up our capabilities e = q.expect('incoming-connection', listener=listener) stream = e.connection event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO, connection=stream) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver # send good reply result = make_result_iq(event.stanza) query = result.firstChildElement() query['node'] = client + '#' + ver for f in features: feature = query.addElement('feature') feature['var'] = f stream.send(result) if expect_ccc: event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') announced_ccs, = event.args assertSameElements(expected_caps, announced_ccs[contact_handle]) else: if expect_disco: # Make sure Salut's got the caps sync_stream(q, stream) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(expected_caps, caps_via_contacts_iface) # close the connection and expect a new one to be opened by Salut # the next time we need some discoing doing if expect_disco: stream.send('</stream:stream>') stream.transport.loseConnection() # pass some time so Salut knows the connection is lost and # won't try and send stuff down a closed connection on the # next test. sync_dbus(bus, q, conn)
class ReceiveFileTest(FileTransferTest): def __init__(self, bytestream_cls, file, address_type, access_control, access_control_param): FileTransferTest.__init__(self, bytestream_cls, file, address_type, access_control, access_control_param) self._actions = [ self.connect, self.announce_contact, self.send_ft_offer_iq, self.check_new_channel, self.create_ft_channel, self.set_uri, self.accept_file, self.receive_file, self.close_channel, self.done ] def send_ft_offer_iq(self): self.bytestream = self.bytestream_cls(self.stream, self.q, 'alpha', self.contact_full_jid, 'test@localhost/Resource', True) iq, si = self.bytestream.create_si_offer(ns.FILE_TRANSFER) file_node = si.addElement((ns.FILE_TRANSFER, 'file')) file_node['name'] = self.file.name file_node['size'] = str(self.file.size) file_node['mime-type'] = self.file.content_type file_node['hash'] = self.file.hash date = datetime.datetime.utcfromtimestamp( self.file.date).strftime('%FT%H:%M:%SZ') file_node['date'] = date file_node.addElement('desc', content=self.file.description) # we support range transfer file_node.addElement('range') # Metadata if self.service_name: service_form = { ns.TP_FT_METADATA_SERVICE: { 'ServiceName': [self.service_name] } } add_data_forms(file_node, service_form) if self.metadata: metadata_form = {ns.TP_FT_METADATA: self.metadata} add_data_forms(file_node, metadata_form) # so... lunch? iq.send() 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 set_uri(self): ft_props = dbus.Interface(self.ft_channel, cs.PROPERTIES_IFACE) # URI is not set yet uri = ft_props.Get(cs.CHANNEL_TYPE_FILE_TRANSFER, 'URI') assertEquals('', uri) # Setting URI call_async(self.q, ft_props, 'Set', cs.CHANNEL_TYPE_FILE_TRANSFER, 'URI', self.file.uri) self.q.expect('dbus-signal', signal='URIDefined', args=[self.file.uri]) self.q.expect('dbus-return', method='Set') # Check it has the right value now uri = ft_props.Get(cs.CHANNEL_TYPE_FILE_TRANSFER, 'URI') assertEquals(self.file.uri, uri) # We can't change it once it has been set call_async(self.q, ft_props, 'Set', cs.CHANNEL_TYPE_FILE_TRANSFER, 'URI', 'badger://snake') self.q.expect('dbus-error', method='Set', name=cs.INVALID_ARGUMENT) def accept_file(self): try: self.address = self.ft_channel.AcceptFile( self.address_type, self.access_control, self.access_control_param, self.file.offset, byte_arrays=True) except dbus.DBusException, e: if self.address_type == cs.SOCKET_ADDRESS_TYPE_IPV6 and \ e.get_dbus_name() == cs.NOT_AVAILABLE and \ e.get_dbus_message() == "Could not set up local socket": print "Ignoring error for ipv6 address space" return True else: raise e state_event, iq_event = self.q.expect_many( EventPattern('dbus-signal', signal='FileTransferStateChanged'), EventPattern('stream-iq', iq_type='result')) state, reason = state_event.args assert state == cs.FT_STATE_ACCEPTED assert reason == cs.FT_STATE_CHANGE_REASON_REQUESTED # Got SI reply self.bytestream.check_si_reply(iq_event.stanza) if self.file.offset != 0: range = xpath.queryForNodes('/iq/si/file/range', iq_event.stanza)[0] assert range['offset'] == str(self.file.offset) _, events = self.bytestream.open_bytestream([], [ EventPattern('dbus-signal', signal='InitialOffsetDefined'), EventPattern('dbus-signal', signal='FileTransferStateChanged') ]) offset_event, state_event = events offset = offset_event.args[0] assert offset == self.file.offset state, reason = state_event.args assert state == cs.FT_STATE_OPEN assert reason == cs.FT_STATE_CHANGE_REASON_NONE # send the beginning of the file (client didn't connect to socket yet) self.bytestream.send_data( self.file.data[self.file.offset:self.file.offset + 2])
def test_ft_caps_from_contact(q, bus, conn, stream, contact, contact_handle, client): conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACT_CAPS) conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS) # send presence with no FT cap presence = make_presence(contact, status='hello') c = presence.addElement((ns.CAPS, 'c')) c['node'] = client c['ver'] = compute_caps_hash([], [], {}) c['hash'] = 'sha-1' stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + c['ver'] # send good reply result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query['node'] = client + '#' + c['ver'] stream.send(result) # no change in ContactCapabilities, so no signal ContactCapabilitiesChanged sync_stream(q, stream) # no special capabilities basic_caps = dbus.Dictionary({contact_handle: [(text_fixed_properties, text_allowed_properties)]}) caps = get_contacts_capabilities_sync(conn, [contact_handle]) assert caps == basic_caps, caps # test again, to check GetContactCapabilities does not have side effect caps = get_contacts_capabilities_sync(conn, [contact_handle]) assert caps == basic_caps, caps # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assert caps_via_contacts_iface == caps[contact_handle], \ caps_via_contacts_iface # send presence with ft capa presence = make_presence(contact, status='hello') c = presence.addElement((ns.CAPS, 'c')) c['node'] = client c['ver'] = compute_caps_hash([], [ns.FILE_TRANSFER], {}) c['hash'] = 'sha-1' stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + c['ver'] # send good reply result = make_result_iq(stream, event.stanza) query = result.firstChildElement() query['node'] = client + '#' + c['ver'] feature = query.addElement('feature') feature['var'] = ns.FILE_TRANSFER stream.send(result) generic_tubes_caps = dbus.Dictionary({contact_handle: [(text_fixed_properties, text_allowed_properties), (ft_fixed_properties, ft_allowed_properties)]}) event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') assert len(event.args) == 1 assert event.args[0] == generic_tubes_caps caps = get_contacts_capabilities_sync(conn, [contact_handle]) assert caps == generic_tubes_caps, caps # test again, to check GetContactCapabilities does not have side effect caps = get_contacts_capabilities_sync(conn, [contact_handle]) assert caps == generic_tubes_caps, caps # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assert caps_via_contacts_iface == caps[contact_handle], \ caps_via_contacts_iface
def 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 test2(q, bus, connections, streams): conn1, conn2 = connections stream1, stream2 = streams conn1_handle = conn1.Properties.Get(cs.CONN, 'SelfHandle') conn1_jid = conn1.inspect_contact_sync(conn1_handle) conn2_handle = conn2.Properties.Get(cs.CONN, 'SelfHandle') conn2_jid = conn2.inspect_contact_sync(conn2_handle) handle1 = conn2.get_contact_handle_sync(conn1_jid) handle2 = conn1.get_contact_handle_sync(conn2_jid) q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', path=conn1.object.object_path), EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', path=conn2.object.object_path)) check_contact_caps(conn1, handle2, False) check_contact_caps(conn2, handle1, False) caps_iface = dbus.Interface(conn1, cs.CONN_IFACE_CONTACT_CAPS) caps_iface.UpdateCapabilities([("self", [ft_fixed_properties], dbus.Array([], signature="s"))]) _, presence, disco, _ = \ q.expect_many(EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', path=conn1.object.object_path, args=[{conn1_handle:generic_ft_caps}]), EventPattern('stream-presence', stream=stream1), EventPattern('stream-iq', stream=stream1, query_ns=ns.DISCO_INFO, iq_type = 'result'), EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', path=conn2.object.object_path, args=[{handle1:generic_ft_caps}])) presence_c = xpath.queryForNodes('/presence/c', presence.stanza)[0] assert "share-v1" in presence_c.attributes['ext'] conn1_ver = presence_c.attributes['ver'] found_share = False for feature in xpath.queryForNodes('/iq/query/feature', disco.stanza): if feature.attributes['var'] == ns.GOOGLE_FEAT_SHARE: found_share = True assert found_share check_contact_caps(conn2, handle1, True) caps_iface = dbus.Interface(conn2, cs.CONN_IFACE_CONTACT_CAPS) caps_iface.UpdateCapabilities([("self", [ft_fixed_properties], dbus.Array([], signature="s"))]) _, presence, _ = \ q.expect_many(EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', path=conn2.object.object_path, args=[{conn2_handle:generic_ft_caps}]), EventPattern('stream-presence', stream=stream2), EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', path=conn1.object.object_path, args=[{handle2:generic_ft_caps}])) presence_c = xpath.queryForNodes('/presence/c', presence.stanza)[0] assert "share-v1" in presence_c.attributes['ext'] # We will have the same capabilities on both sides, so we can't check for # a cap disco since the hash will be the same, so we need to make sure the # hash is indeed the same assert presence_c.attributes['ver'] == conn1_ver found_share = False for feature in xpath.queryForNodes('/iq/query/feature', disco.stanza): if feature.attributes['var'] == ns.GOOGLE_FEAT_SHARE: found_share = True assert found_share check_contact_caps(conn1, handle2, True)
def test(q, bus, conn, stream): # Initial vCard request. Respond only after we call SetAliases(). vcard_get_event = q.expect('stream-iq', iq_type='get', to=None, query_ns=ns.VCARD_TEMP, query_name='vCard') sync_stream(q, stream) handle = conn.Properties.Get(cs.CONN, "SelfHandle") call_async(q, conn.Aliasing, 'SetAliases', {handle: 'Robert the Bruce'}) sync_dbus(bus, q, conn) acknowledge_iq(stream, vcard_get_event.stanza) # Gabble sets a new vCard with our nickname. vcard_set_event = q.expect('stream-iq', iq_type='set', query_ns=ns.VCARD_TEMP, query_name='vCard') assertEquals( 'Robert the Bruce', xpath.queryForString('/iq/vCard/NICKNAME', vcard_set_event.stanza)) assertEquals( None, xpath.queryForNodes('/iq/vCard/PHOTO', vcard_set_event.stanza)) assertEquals(None, xpath.queryForNodes('/iq/vCard/FN', vcard_set_event.stanza)) assertEquals(None, xpath.queryForNodes('/iq/vCard/N', vcard_set_event.stanza)) # Before the server replies, the user sets their avatar call_async(q, conn.Avatars, 'SetAvatar', 'hello', 'image/png') sync_dbus(bus, q, conn) # This acknowledgement is for the nickname acknowledge_iq(stream, vcard_set_event.stanza) hello_binval = base64.b64encode('hello') # This sets the avatar vcard_set_event = q.expect('stream-iq', iq_type='set', query_ns=ns.VCARD_TEMP, query_name='vCard') assertEquals( 'Robert the Bruce', xpath.queryForString('/iq/vCard/NICKNAME', vcard_set_event.stanza)) assertLength( 1, xpath.queryForNodes('/iq/vCard/PHOTO', vcard_set_event.stanza)) assertEquals( 'image/png', xpath.queryForString('/iq/vCard/PHOTO/TYPE', vcard_set_event.stanza)) binval = xpath.queryForString('/iq/vCard/PHOTO/BINVAL', vcard_set_event.stanza) assertEquals(hello_binval, binval.strip()) assertEquals(None, xpath.queryForNodes('/iq/vCard/FN', vcard_set_event.stanza)) assertEquals(None, xpath.queryForNodes('/iq/vCard/N', vcard_set_event.stanza)) # Before the server replies, the user sets their ContactInfo call_async(q, conn.ContactInfo, 'SetContactInfo', [(u'fn', [], [u'King Robert I']), (u'n', [], [u'de Brus', u'Robert', u'', u'King', u'']), (u'nickname', [], [u'Bob'])]) sync_dbus(bus, q, conn) # This acknowledgement is for the avatar; SetAvatar won't happen # until this has acknowledge_iq(stream, vcard_set_event.stanza) # This sets the ContactInfo vcard_set_event, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_ns=ns.VCARD_TEMP, query_name='vCard'), EventPattern('dbus-return', method='SetAvatar')) assertEquals( 'Bob', xpath.queryForString('/iq/vCard/NICKNAME', vcard_set_event.stanza)) assertLength( 1, xpath.queryForNodes('/iq/vCard/PHOTO', vcard_set_event.stanza)) assertEquals( 'image/png', xpath.queryForString('/iq/vCard/PHOTO/TYPE', vcard_set_event.stanza)) binval = xpath.queryForString('/iq/vCard/PHOTO/BINVAL', vcard_set_event.stanza) assertEquals(hello_binval, binval.strip()) assertLength(1, xpath.queryForNodes('/iq/vCard/N', vcard_set_event.stanza)) assertEquals( 'Robert', xpath.queryForString('/iq/vCard/N/GIVEN', vcard_set_event.stanza)) assertEquals( 'de Brus', xpath.queryForString('/iq/vCard/N/FAMILY', vcard_set_event.stanza)) assertEquals( 'King', xpath.queryForString('/iq/vCard/N/PREFIX', vcard_set_event.stanza)) assertEquals('King Robert I', xpath.queryForString('/iq/vCard/FN', vcard_set_event.stanza)) # Before the server replies, the user unsets their avatar call_async(q, conn.Avatars, 'SetAvatar', '', '') sync_dbus(bus, q, conn) # This acknowledgement is for the ContactInfo; SetContactInfo won't happen # until this has acknowledge_iq(stream, vcard_set_event.stanza) vcard_set_event, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_ns=ns.VCARD_TEMP, query_name='vCard'), EventPattern('dbus-return', method='SetContactInfo')) assertEquals( 'Bob', xpath.queryForString('/iq/vCard/NICKNAME', vcard_set_event.stanza)) assertEquals( None, xpath.queryForNodes('/iq/vCard/PHOTO', vcard_set_event.stanza)) assertLength(1, xpath.queryForNodes('/iq/vCard/N', vcard_set_event.stanza)) assertEquals( 'Robert', xpath.queryForString('/iq/vCard/N/GIVEN', vcard_set_event.stanza)) assertEquals( 'de Brus', xpath.queryForString('/iq/vCard/N/FAMILY', vcard_set_event.stanza)) assertEquals( 'King', xpath.queryForString('/iq/vCard/N/PREFIX', vcard_set_event.stanza)) assertEquals('King Robert I', xpath.queryForString('/iq/vCard/FN', vcard_set_event.stanza)) # This acknowledgement is for the avatar; SetAvatar won't finish # until this is received acknowledge_iq(stream, vcard_set_event.stanza) q.expect('dbus-return', method='SetAvatar') # Now Gabble gets disconnected. sync_stream(q, stream) disconnect_conn(q, conn, stream)
def test(q, bus, conn, stream): self_presence = q.expect('stream-presence') c = xpath.queryForNodes('/presence/c', self_presence.stanza)[0] jid = '[email protected]/omg' # Gabble shouldn't send any disco requests to our contact during this test. q.forbid_events([ EventPattern('stream-iq', to=jid, iq_type='get', query_ns=ns.DISCO_INFO), ]) # Check that Gabble doesn't disco other clients with the same caps hash. p = make_presence(jid, caps={ 'node': c['node'], 'hash': c['hash'], 'ver': c['ver'], }) stream.send(p) sync_stream(q, stream) # Check that Gabble doesn't disco its own ext='' bundles (well, its own # bundles as advertised by Gabbles that don't do hashed caps) p = make_presence( jid, caps={ 'node': c['node'], 'ver': c['ver'], # omitting hash='' so Gabble doesn't ignore ext='' 'ext': 'voice-v1 video-v1', }) stream.send(p) sync_stream(q, stream) conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.AbiWord', [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.STREAM_TUBE_SERVICE: 'x-abiword' }, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.STREAM_TUBE_SERVICE: 'x-abiword' }, ], []), (cs.CLIENT + '.KCall', [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL }, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.CALL_INITIAL_AUDIO: True }, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.CALL_INITIAL_VIDEO: True }, ], [ cs.CHANNEL_TYPE_CALL + '/gtalk-p2p', cs.CHANNEL_TYPE_CALL + '/ice-udp', cs.CHANNEL_TYPE_CALL + '/video/h264', ]), ]) self_presence = q.expect('stream-presence') c_ = xpath.queryForNodes('/presence/c', self_presence.stanza)[0] assertNotEquals(c['ver'], c_['ver']) for suffix in [ c['ver'], 'voice-v1', 'video-v1', 'camera-v1', 'share-v1', 'pmuc-v1' ] + list(c_['ext'].split()): # But then someone asks us for our old caps iq = IQ(stream, 'get') iq['from'] = jid query = iq.addElement((ns.DISCO_INFO, 'query')) query['node'] = c['node'] + '#' + suffix stream.send(iq) # Gabble should still know what they are, and reply. This is # actually quite important: there's a bug in iChat where if you # return an error to a disco query, it just asks again, and again, # and again... reply = q.expect('stream-iq', to=jid) assertEquals('result', reply.iq_type)
def test(q, bus, conn): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L]) basic_txt = { "txtvers": "1", "status": "avail" } contact_name = "test-text-channel@" + get_host_name() listener, port = setup_stream_listener(q, contact_name) announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt) handle = wait_for_contact_in_publish(q, bus, conn, contact_name) t = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: handle})[0] text_channel = make_channel_proxy(conn, t, "Channel.Type.Text") text_channel.Send(cs.MT_NORMAL, INCOMING_MESSAGE) e = q.expect('incoming-connection', listener = listener) incoming = e.connection e = q.expect('stream-message', connection = incoming) assert e.message_type == "chat" body = xpath.queryForNodes("/message/body", e.stanza ) assert map(str, body) == [ INCOMING_MESSAGE ] # drop the connection incoming.transport.loseConnection() # Now send a message to salut self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") self_handle_name = conn.Properties.Get(cs.CONN, "SelfID") AvahiListener(q).listen_for_service("_presence._tcp") e = q.expect('service-added', name = self_handle_name, protocol = avahi.PROTO_INET) service = e.service service.resolve() e = q.expect('service-resolved', service = service) outbound = connect_to_stream(q, contact_name, self_handle_name, str(e.pt), e.port) e = q.expect('connection-result') assert e.succeeded, e.reason e = q.expect('stream-opened', connection = outbound) # connected to salut, now send a message message = domish.Element(('', 'message')) message['type'] = "chat" message.addElement('body', content=OUTGOING_MESSAGE) e.connection.send(message) e = q.expect('dbus-signal', signal='Received') assert e.args[2] == handle assert e.args[3] == cs.MT_NORMAL assert e.args[5] == OUTGOING_MESSAGE
def test_ft_caps_from_contact(q, bus, conn, client): conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACT_CAPS) conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS) # send presence with FT capa ver = compute_caps_hash([], [ns.IQ_OOB], {}) txt_record = { "txtvers": "1", "status": "avail", "node": client, "ver": ver, "hash": "sha-1" } contact_name = "test-caps-ft@" + get_host_name() listener, port = setup_stream_listener(q, contact_name) announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, txt_record) # this is the first presence, Salut connects to the contact e = q.expect('incoming-connection', listener=listener) incoming = e.connection # Salut looks up our capabilities event = q.expect('stream-iq', connection=incoming, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver, (query_node.attributes['node'], client, ver) contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0] # send good reply result = make_result_iq(event.stanza) query = result.firstChildElement() query['node'] = client + '#' + ver feature = query.addElement('feature') feature['var'] = ns.IQ_OOB incoming.send(result) # FT capa is announced e = q.expect('dbus-signal', signal='ContactCapabilitiesChanged', predicate=lambda e: contact_handle in e.args[0]) caps = e.args[0][contact_handle] assertContains(ft_caps, caps) # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities'] assert caps_via_contacts_iface == caps, caps_via_contacts_iface # check if Salut announces the OOB capa self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") self_handle_name = conn.Properties.Get(cs.CONN, "SelfID") AvahiListener(q).listen_for_service("_presence._tcp") e = q.expect('service-added', name=self_handle_name, protocol=avahi.PROTO_INET) service = e.service service.resolve() receive_presence_and_ask_caps(q, incoming, service, contact_name) # capa announced without FT ver = compute_caps_hash([], ["http://telepathy.freedesktop.org/xmpp/pony"], {}) txt_record = { "txtvers": "1", "status": "avail", "node": client, "ver": ver, "hash": "sha-1" } contact_name = "test-caps-ft2@" + get_host_name() listener, port = setup_stream_listener(q, contact_name) announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, txt_record) # this is the first presence, Salut connects to the contact e = q.expect('incoming-connection', listener=listener) incoming = e.connection # Salut looks up our capabilities event = q.expect('stream-iq', connection=incoming, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver, (query_node.attributes['node'], client, ver) contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0] # send good reply result = make_result_iq(event.stanza) query = result.firstChildElement() query['node'] = client + '#' + ver feature = query.addElement('feature') feature['var'] = "http://telepathy.freedesktop.org/xmpp/pony" incoming.send(result) # the FT capability is not announced e = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') caps = e.args[0][contact_handle] assertDoesNotContain(ft_caps, caps) # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities'] assert caps_via_contacts_iface == caps, caps_via_contacts_iface # no capabilites announced (assume FT is supported to insure interop) txt_record = {"txtvers": "1", "status": "avail"} contact_name = "test-caps-ft-no-capa2@" + get_host_name() contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0] listener, port = setup_stream_listener(q, contact_name) announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, txt_record) # FT capa is announced e = q.expect('dbus-signal', signal='ContactCapabilitiesChanged', predicate=lambda e: contact_handle in e.args[0].keys()) caps = e.args[0][contact_handle] assertContains(ft_caps, caps) # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities'] assert caps_via_contacts_iface == caps, caps_via_contacts_iface
def test(q, bus, conn, stream, bytestream_cls, address_type, access_control, access_control_param): address1 = t.set_up_echo(q, address_type, True, streamfile='stream') address2 = t.set_up_echo(q, address_type, True, streamfile='stream2') t.check_conn_properties(q, conn) vcard_event, roster_event = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', query_ns=ns.ROSTER)) self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") acknowledge_iq(stream, vcard_event.stanza) roster = roster_event.stanza roster['type'] = 'result' item = roster_event.query.addElement('item') item['jid'] = 'bob@localhost' # Bob can do tubes item['subscription'] = 'both' stream.send(roster) bob_full_jid = 'bob@localhost/Bob' self_full_jid = 'test@localhost/Resource' # Send Bob presence and his tube caps presence = domish.Element(('jabber:client', 'presence')) presence['from'] = bob_full_jid presence['to'] = self_full_jid c = presence.addElement('c') c['xmlns'] = 'http://jabber.org/protocol/caps' c['node'] = 'http://example.com/ICantBelieveItsNotTelepathy' c['ver'] = '1.2.3' stream.send(presence) event = q.expect('stream-iq', iq_type='get', query_ns='http://jabber.org/protocol/disco#info', to=bob_full_jid) assert event.query['node'] == \ 'http://example.com/ICantBelieveItsNotTelepathy#1.2.3' result = make_result_iq(stream, event.stanza) query = result.firstChildElement() feature = query.addElement('feature') feature['var'] = ns.TUBES stream.send(result) # A tube request can be done only if the contact has tube capabilities # Ensure that Bob's caps have been received sync_stream(q, stream) # Also ensure that all the new contact list channels have been announced, # so that the NewChannel(s) signals we look for after calling # RequestChannel are the ones we wanted. sync_dbus(bus, q, conn) # Test tubes with Bob. Bob has tube capabilities. bob_handle = conn.get_contact_handle_sync('bob@localhost') # Try CreateChannel with correct properties # Gabble must succeed call_async( q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: bob_handle, cs.STREAM_TUBE_SERVICE: "newecho", }) def find_stream_tube(channels): for path, props in channels: if props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE: return path, props return None, None def new_chan_predicate(e): path, _ = find_stream_tube(e.args[0]) return path is not None ret, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels', predicate=new_chan_predicate), ) new_chan_path = ret.value[0] new_chan_prop_asv = ret.value[1] # State and Parameters are mutables so not announced assert cs.TUBE_STATE not in new_chan_prop_asv assert cs.TUBE_PARAMETERS not in new_chan_prop_asv assert new_chan_path.find("StreamTube") != -1, new_chan_path assert new_chan_path.find("SITubesChannel") == -1, new_chan_path new_tube_chan = bus.get_object(conn.bus_name, new_chan_path) new_tube_iface = dbus.Interface(new_tube_chan, cs.CHANNEL_TYPE_STREAM_TUBE) # check State and Parameters new_tube_props = new_tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE) # the tube created using the new API is in the "not offered" state assert new_tube_props['State'] == cs.TUBE_CHANNEL_STATE_NOT_OFFERED _, stream_tube_channel_properties = find_stream_tube(new_sig.args[0]) assert cs.TUBE_STATE not in stream_tube_channel_properties assert cs.TUBE_PARAMETERS not in stream_tube_channel_properties # Offer the first tube created call_async(q, new_tube_iface, 'Offer', address_type, address2, access_control, new_sample_parameters) msg_event, state_event = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='TubeChannelStateChanged')) assert state_event.args[0] == cs.TUBE_CHANNEL_STATE_REMOTE_PENDING message = msg_event.stanza assert message['to'] == bob_full_jid tube_nodes = xpath.queryForNodes('/message/tube[@xmlns="%s"]' % ns.TUBES, message) assert tube_nodes is not None assert len(tube_nodes) == 1 tube = tube_nodes[0] assert tube['service'] == 'newecho' assert tube['type'] == 'stream' assert not tube.hasAttribute('initiator') stream_tube_id = long(tube['id']) params = {} parameter_nodes = xpath.queryForNodes('/tube/parameters/parameter', tube) for node in parameter_nodes: assert node['name'] not in params params[node['name']] = (node['type'], str(node)) assert params == { 'ay': ('bytes', 'bmV3aGVsbG8='), 's': ('str', 'newhello'), 'i': ('int', '-123'), 'u': ('uint', '123'), } # The new tube has been offered, the parameters cannot be changed anymore # We need to use call_async to check the error tube_prop_iface = dbus.Interface(new_tube_chan, cs.PROPERTIES_IFACE) call_async(q, tube_prop_iface, 'Set', cs.CHANNEL_IFACE_TUBE, 'Parameters', dbus.Dictionary({dbus.String(u'foo2'): dbus.String(u'bar2')}, signature=dbus.Signature('sv')), dbus_interface=cs.PROPERTIES_IFACE) set_error = q.expect('dbus-error') # check it is *not* correctly changed new_tube_props = new_tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) assert new_tube_props.get("Parameters") == new_sample_parameters, \ new_tube_props.get("Parameters") # The CM is the server, so fake a client wanting to talk to it # Old API tube bytestream1 = bytestream_cls(stream, q, 'alpha', bob_full_jid, self_full_jid, True) iq, si = bytestream1.create_si_offer(ns.TUBES) stream_node = si.addElement((ns.TUBES, 'stream')) stream_node['tube'] = str(stream_tube_id) stream.send(iq) si_reply_event, _, new_conn_event, socket_event = q.expect_many( EventPattern('stream-iq', iq_type='result'), EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[cs.TUBE_STATE_OPEN]), EventPattern('dbus-signal', signal='NewRemoteConnection'), EventPattern('socket-connected')) bytestream1.check_si_reply(si_reply_event.stanza) tube = xpath.queryForNodes('/iq/si/tube[@xmlns="%s"]' % ns.TUBES, si_reply_event.stanza) assert len(tube) == 1 handle, access, id = new_conn_event.args assert handle == bob_handle protocol = socket_event.protocol # we don't want to echo the access control byte protocol.echoed = False # start to read from the transport so we can read the control byte protocol.transport.startReading() t.check_new_connection_access(q, access_control, access, protocol) protocol.echoed = True # The CM is the server, so fake a client wanting to talk to it # New API tube bytestream2 = bytestream_cls(stream, q, 'beta', bob_full_jid, self_full_jid, True) iq, si = bytestream2.create_si_offer(ns.TUBES) stream_node = si.addElement((ns.TUBES, 'stream')) stream_node['tube'] = str(stream_tube_id) stream.send(iq) si_reply_event, new_conn_event, socket_event = q.expect_many( EventPattern('stream-iq', iq_type='result'), EventPattern('dbus-signal', signal='NewRemoteConnection'), EventPattern('socket-connected')) bytestream2.check_si_reply(si_reply_event.stanza) tube = xpath.queryForNodes('/iq/si/tube[@xmlns="%s"]' % ns.TUBES, si_reply_event.stanza) assert len(tube) == 1 handle, access, conn_id = new_conn_event.args assert handle == bob_handle protocol = socket_event.protocol # we don't want to echo the access control byte protocol.echoed = False # start to read from the transport so we can read the control byte protocol.transport.startReading() t.check_new_connection_access(q, access_control, access, protocol) protocol.echoed = True # have the fake client open the stream bytestream1.open_bytestream() # have the fake client send us some data data = 'hello, world' bytestream1.send_data(data) binary = bytestream1.get_data(len(data)) assert binary == data, binary # have the fake client open the stream bytestream2.open_bytestream() # have the fake client send us some data data = 'hello, new world' bytestream2.send_data(data) binary = bytestream2.get_data(len(data)) assert binary == data, binary # peer closes the bytestream bytestream2.close() e = q.expect('dbus-signal', signal='ConnectionClosed') assertEquals(conn_id, e.args[0]) assertEquals(cs.CONNECTION_LOST, e.args[1]) t.cleanup()
def run_outgoing_test(q, bus, conn, stream, close_channel=False): jp = JingleProtocol031 () jt = JingleTest2(jp, conn, q, stream, 'test@localhost', muc + '/bob') jt.prepare() self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") # Not allowed to have muji related presences before we accept the channel forbidden = [ no_muji_presences (muc) ] (path, props) = create_muji_channel (q, conn, stream, muc) q.forbid_events(forbidden) general_tests (jp, q, bus, conn, stream, path, props) channel = bus.get_object (conn.bus_name, path) props = channel.GetAll (cs.CHANNEL_TYPE_CALL, dbus_interface = dbus.PROPERTIES_IFACE) content = bus.get_object (conn.bus_name, props['Contents'][0]) md = jt.get_call_audio_md_dbus() check_and_accept_offer (q, bus, conn, content, md) # Accept the channel, which means we can get muji presences q.unforbid_events (forbidden) channel.Accept() e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') mujinode = xpath.queryForNodes("/presence/muji", e.stanza) assertLength (1, mujinode) # The one with the codecs e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Gabble shouldn't send new presences for a while q.forbid_events(forbidden) presence = make_muc_presence('owner', 'moderator', muc, 'bob') presence.addElement ((ns.MUJI, 'muji')).addElement('preparing') stream.send(presence) presence = make_muc_presence('owner', 'moderator', muc, 'bob') muji = ('muji', ns.MUJI, {}, [('content', ns.MUJI, { "name": "Audio" }, [( 'description', ns.JINGLE_RTP, {"media": "audio"}, jt.generate_payloads(jt.audio_codecs))])]) presence.addChild(jp._simple_xml(muji)) stream.send(presence) q.expect('dbus-signal', signal = 'CallStateChanged') # Bob appears and starts a session right afterwards q.expect('dbus-signal', signal = 'CallMembersChanged') q.unforbid_events(forbidden) e = q.expect('dbus-signal', signal = 'NewMediaDescriptionOffer') offer = bus.get_object (conn.bus_name, e.args[0]) offer.Accept(md, dbus_interface=cs.CALL_CONTENT_MEDIA_DESCRIPTION) jt.incoming_call(audio = "Audio") e = q.expect ('dbus-signal', signal = 'StreamsAdded') cstream = bus.get_object (conn.bus_name, e.args[0][0]) candidates = jt.get_call_remote_transports_dbus () cstream.AddCandidates (candidates, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) # Fake our endpoint being connected endpoints = cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, "Endpoints", dbus_interface=dbus.PROPERTIES_IFACE) assertLength (1, endpoints) endpoint = bus.get_object (conn.bus_name, endpoints[0]) endpoint.SetEndpointState (1, cs.MEDIA_STREAM_STATE_CONNECTED, dbus_interface=cs.CALL_STREAM_ENDPOINT) endpoint.SetEndpointState (2, cs.MEDIA_STREAM_STATE_CONNECTED, dbus_interface=cs.CALL_STREAM_ENDPOINT) e = q.expect ('stream-iq', predicate = jp.action_predicate ('session-accept')) stream.send(jp.xml(jp.ResultIq(jt.peer, e.stanza, []))) # But we want video as well ! c = channel.AddContent ("Camera!", cs.MEDIA_STREAM_TYPE_VIDEO, dbus_interface=cs.CHANNEL_TYPE_CALL) q.expect('dbus-signal', signal = 'ContentAdded') content = bus.get_object (conn.bus_name, c) # wait for the CodecOffer and Accept it q.expect('dbus-signal', signal = 'NewMediaDescriptionOffer') md = jt.get_call_video_md_dbus() check_and_accept_offer (q, bus, conn, content, md) # preparing e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') #codecs e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Bob would like to join our video party presence = make_muc_presence('owner', 'moderator', muc, 'bob') muji = ('muji', ns.MUJI, {}, [('content', ns.MUJI, { "name": "Audio" }, [( 'description', ns.JINGLE_RTP, {"media": "audio"}, jt.generate_payloads(jt.audio_codecs))]), ('content', ns.MUJI, { "name": "Camera!" }, [( 'description', ns.JINGLE_RTP, {"media": "video"}, jt.generate_payloads(jt.video_codecs))]), ]) presence.addChild(jp._simple_xml(muji)) stream.send(presence) # new codec offer as bob threw in some codecs q.expect('dbus-signal', signal='NewMediaDescriptionOffer') check_and_accept_offer (q, bus, conn, self_handle, content, codecs, check_codecs_changed = False) # Bob sends a content node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.peer, 'content-add', [ jp.Content('videostream', 'initiator', 'both', jp.Description('video', [ jp.PayloadType(name, str(rate), str(id), parameters) for (name, id, rate, parameters) in jt.video_codecs ]), jp.TransportGoogleP2P()) ]) ]) stream.send(jp.xml(node)) # We get a new stream q.expect('dbus-signal', signal = 'StreamsAdded') # Sync up the stream to ensure we sent out all the xmpp traffic that was # the result of a stream being added sync_stream (q, stream) # happiness.. Now let's hang up if close_channel: channel.Close() hangup_event = EventPattern ('dbus-signal', signal = "Closed", path = path) else: channel.Hangup (0, "", "", dbus_interface=cs.CHANNEL_TYPE_CALL) hangup_event = EventPattern ('dbus-signal', signal='CallStateChanged') # Should change the call state to ended, send a session-terminate to our # only peer and send a muc presence without any mention of muji q.forbid_events(forbidden) q.expect_many (EventPattern ('stream-presence', to = muc + "/test"), EventPattern ('stream-iq', predicate=jp.action_predicate ('session-terminate')), hangup_event) if not close_channel: channel.Close() q.expect ('dbus-signal', signal="Closed", path = path) try: channel.Close() raise AssertionError ("Channel didn't actually close") except DBusException: pass
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 _cb_disco_iq(self, iq): nodes = xpath.queryForNodes("/iq/query", iq) query = nodes[0] if query.getAttribute('node') is None: return node = query.attributes['node'] ver = node.replace("http://telepathy.freedesktop.org/caps#", "") if iq.getAttribute('type') == 'result': if FileTransferTest.caps_identities is None or \ FileTransferTest.caps_features is None or \ FileTransferTest.caps_dataforms is None: # create our own identity identity_nodes = xpath.queryForNodes('/iq/query/identity', iq) assertLength(1, identity_nodes) identity_node = identity_nodes[0] identity_category = identity_node['category'] identity_type = identity_node['type'] identity_name = identity_node['name'] identity = '%s/%s//%s' % (identity_category, identity_type, identity_name) _, features, dataforms = extract_disco_parts(iq) FileTransferTest.caps_identities = [identity] FileTransferTest.caps_features = features FileTransferTest.caps_dataforms = dataforms # Check if the hash matches the announced capabilities assertEquals( compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features, FileTransferTest.caps_dataforms), ver) if ver == FileTransferTest.caps_ft: caps_share = compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features + \ [ns.GOOGLE_FEAT_SHARE], FileTransferTest.caps_dataforms) n = query.attributes['node'].replace(ver, caps_share) query.attributes['node'] = n for feature in xpath.queryForNodes('/iq/query/feature', iq): query.children.remove(feature) for f in FileTransferTest.caps_features + [ ns.GOOGLE_FEAT_SHARE ]: el = domish.Element((None, 'feature')) el['var'] = f query.addChild(el) elif iq.getAttribute('type') == 'get': caps_share = compute_caps_hash(FileTransferTest.caps_identities, FileTransferTest.caps_features + \ [ns.GOOGLE_FEAT_SHARE], FileTransferTest.caps_dataforms) if ver == caps_share: n = query.attributes['node'].replace(ver, FileTransferTest.caps_ft) query.attributes['node'] = n
def test(q, bus, conn, stream): room = '*****@*****.**' chan, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, room) # Exercise basic Channel Properties from spec 0.17.7 channel_props = chan.Properties.GetAll(cs.CHANNEL) assertEquals(cs.HT_ROOM, channel_props.get('TargetHandleType')) assertEquals(cs.CHANNEL_TYPE_TEXT, channel_props.get('ChannelType')) interfaces = channel_props.get('Interfaces') assertContains(cs.CHANNEL_IFACE_GROUP, interfaces) assertContains(cs.CHANNEL_IFACE_PASSWORD, interfaces) assertContains(cs.CHANNEL_IFACE_CHAT_STATE, interfaces) assertContains(cs.CHANNEL_IFACE_MESSAGES, interfaces) assert channel_props['TargetID'] == '*****@*****.**', channel_props assert channel_props['Requested'] == True assert channel_props['InitiatorID'] == 'test@localhost' assert channel_props['InitiatorHandle'] == conn.Properties.Get( cs.CONN, "SelfHandle") # Exercise Group Properties from spec 0.17.6 (in a basic way) group_props = chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP) assert 'HandleOwners' in group_props, group_props assert 'Members' in group_props, group_props assert 'LocalPendingMembers' in group_props, group_props assert 'RemotePendingMembers' in group_props, group_props assert 'GroupFlags' in group_props, group_props # Test receiving a message from Bob in the MUC message = domish.Element((None, 'message')) message['from'] = '[email protected]/bob' message['type'] = 'groupchat' body = message.addElement('body', content='hello') stream.send(message) message_received = q.expect('dbus-signal', signal='MessageReceived') # Check Channel.Interface.Messages.MessageReceived: message = message_received.args[0] # message should have two parts: the header and one content part assert len(message) == 2, message header, body = message assert header['message-sender'] == bob_handle, header # the spec says that message-type "SHOULD be omitted for normal chat # messages." assert 'message-type' not in header, header assert body['content-type'] == 'text/plain', body assert body['content'] == 'hello', body # Remove the message from the pending message queue, and check that # PendingMessagesRemoved fires. message_id = header['pending-message-id'] chan.Text.AcknowledgePendingMessages([message_id]) removed = q.expect('dbus-signal', signal='PendingMessagesRemoved') removed_ids = removed.args[0] assert len(removed_ids) == 1, removed_ids assert removed_ids[0] == message_id, (removed_ids, message_id) # Send an action using the Messages API greeting = [ dbus.Dictionary( { 'message-type': 1, # Action }, signature='sv'), { 'content-type': 'text/plain', 'content': u"peers through a gap in the curtains", } ] # We ask for delivery reports (which MUCs provide) and read reports (which # MUCs do not provide). sent_token = chan.Messages.SendMessage( greeting, cs.MSG_SENDING_FLAGS_REPORT_DELIVERY | cs.MSG_SENDING_FLAGS_REPORT_READ) assert sent_token stream_message, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='MessageSent'), ) sent_message, flags, token = message_sent.args assert len(sent_message) == 2, sent_message header = sent_message[0] assert header['message-type'] == 1, header # Action assertEquals(test_handle, header['message-sender']) assertEquals('[email protected]/test', header['message-sender-id']) body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'peers through a gap in the curtains', body # Of the flags passed to SendMessage, Gabble should only report the # DELIVERY flag, since the other is not supported. assertEquals(cs.MSG_SENDING_FLAGS_REPORT_DELIVERY, flags) assertEquals(sent_token, token) assert message_sent.args[2] == sent_token elem = stream_message.stanza assert elem.name == 'message' assert elem['type'] == 'groupchat', repr(elem) assert elem['id'] == sent_token, repr(elem) assert elem['to'] == '*****@*****.**', repr(elem) for sub_elem in stream_message.stanza.elements(): if sub_elem.name == 'body': found_body = True assert sub_elem.children[ 0] == u'/me peers through a gap in the curtains' break assert found_body # reflect the sent message back to the MUC elem['from'] = '[email protected]/test' stream.send(elem) # Check that we got the corresponding delivery report report = q.expect('dbus-signal', signal='MessageReceived') assert len(report.args) == 1, report.args parts = report.args[0] # The delivery report should just be a header, no body. assert len(parts) == 1, parts part = parts[0] # The intended recipient was the MUC, so there's no contact handle # suitable for being 'message-sender'. assert 'message-sender' not in part or part['message-sender'] == 0, part assert part['message-type'] == 4, part # Message_Type_Delivery_Report assert part['delivery-status'] == 1, part # Delivery_Status_Delivered assert part['delivery-token'] == sent_token, part assert 'delivery-error' not in part, part assert 'delivery-echo' in part, part # Check that the included echo is from us, and matches all the keys in the # message we sent. echo = part['delivery-echo'] assert len(echo) == len(greeting), (echo, greeting) assert echo[0]['message-sender'] == test_handle, echo[0] assert echo[0]['message-token'] == sent_token, echo[0] for i in range(0, len(echo)): for key in greeting[i]: assert key in echo[i], (i, key, echo) assert echo[i][key] == greeting[i][key], (i, key, echo, greeting) # Send a normal message using the Channel.Type.Text API chan.send_msg_sync('goodbye') event, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='MessageSent'), ) sent_message, flags, _ = message_sent.args assert len(sent_message) == 2, sent_message header = sent_message[0] body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'goodbye', body # The caller didn't ask for delivery reports (how could they? they're using # the old API), but the server's going to send us an echo anyway, so # Gabble's within its rights to pretend that the caller asked. assert flags in [0, cs.MSG_SENDING_FLAGS_REPORT_DELIVERY], flags sent_token = message_sent.args[2] elem = event.stanza assert elem.name == 'message' assert elem['type'] == 'groupchat' assert elem['id'] == message_sent.args[2] body = list(event.stanza.elements())[0] assert body.name == 'body' assert body.children[0] == u'goodbye' # reflect the sent message back to the MUC elem['from'] = '[email protected]/test' stream.send(elem) # TODO: check for a delivery report. # test that presence changes are sent via the MUC conn.SimplePresence.SetPresence('away', 'hurrah') event = q.expect('stream-presence', to='[email protected]/test') elem = event.stanza show = [e for e in elem.elements() if e.name == 'show'][0] assert show assert show.children[0] == u'away' status = [e for e in elem.elements() if e.name == 'status'][0] assert status assert status.children[0] == u'hurrah' # Check that there's no <x xmlns='.../muc'/> element in the <presence> # stanza when we're just updating our presence, as opposed to joining the # MUC in the first place. This is a regression test for # <https://bugs.freedesktop.org/show_bug.cgi?id=29147>. XEP-0045 §7.4 shows # that you do not need to include this element in presence updates; if we # erroneously include it, some implementations take this to mean that we're # trying to join the MUC again and helpfully send us all the scrollback # again. x_muc_nodes = xpath.queryForNodes('/presence/x[@xmlns="%s"]' % ns.MUC, elem) assert x_muc_nodes is None, elem.toXml() # test that leaving the channel results in an unavailable message chan.Group.RemoveMembers( [chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, "SelfHandle")], 'booo') event = q.expect('stream-presence', to='[email protected]/test') elem = event.stanza assert elem['type'] == 'unavailable' status = [e for e in elem.elements() if e.name == 'status'] assertLength(1, status) assertEquals(status[0].children[0], u'booo')
def test(q, bus, conn, stream): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) # send diso request m = domish.Element((None, 'iq')) m['from'] = '*****@*****.**' m['id'] = '1' m['type'] = 'get' query = m.addElement('query') query['xmlns'] = ns.DISCO_INFO stream.send(m) # wait for disco response event = q.expect('stream-iq', iq_type='result', query_ns=ns.DISCO_INFO, to='*****@*****.**') features = set([ str(f['var']) for f in xpath.queryForNodes('/iq/query/feature', event.stanza) ]) # OLPC NS aren't announced assert len(olpc_features.intersection(features)) == 0 # Use OLPC interface buddy_info_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo') call_async(q, buddy_info_iface, 'SetProperties', {'color': '#ff0000,#0000ff'}) # wait for <presence> stanza event = q.expect('stream-presence') c_nodes = xpath.queryForNodes('/presence/c', event.stanza) assert c_nodes is not None assert len(c_nodes) == 1 # send diso request m = domish.Element((None, 'iq')) m['from'] = '*****@*****.**' m['id'] = '2' m['type'] = 'get' query = m.addElement('query') query['xmlns'] = ns.DISCO_INFO stream.send(m) # wait for disco response event = q.expect('stream-iq', iq_type='result', query_ns=ns.DISCO_INFO, to='*****@*****.**') assert event.stanza['id'] == '2' # OLPC NS are now announced features = set([ str(f['var']) for f in xpath.queryForNodes('/iq/query/feature', event.stanza) ]) assert olpc_features.issubset(features)
def _test_local_status(q, conn, stream, msg, show, expected_show=None): expected_show = expected_show or show away = expected_show in ('away', 'xa') self = conn.Properties.Get(cs.CONN, "SelfHandle") prev_presence = get_contacts_presences_sync(conn, [self])[self] was_away = prev_presence[0] in (cs.PRESENCE_AWAY, cs.PRESENCE_EXTENDED_AWAY) was_invisible = (prev_presence[0] == cs.PRESENCE_HIDDEN) if away: # Away and extended away are mapped to idle, that is per connection. # This means we use <presence/> instead of shared presence... if not was_invisible: # ... so in normal cases we don't expect the shared presence # stuff, but ... wrong_presence_pattern = EventPattern( 'stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='set') else: # ... when switching from invisible we have to leave invisible # first and then go away. wrong_presence_pattern = None elif was_away: # Non-away status, but we were away previously. Considering that we # went away using <presence/>, we need to also leave it using # <presence/> plus the shared status. wrong_presence_pattern = None else: # Normal case without away involvement; we just expect the use of # shared status. wrong_presence_pattern = EventPattern('stream-presence') if wrong_presence_pattern: q.forbid_events([wrong_presence_pattern]) conn.SimplePresence.SetPresence(show, msg) max_status_message_length = int(stream.max_status_message_length) if not away or (away and was_invisible): event = q.expect('stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='set') shared_show, shared_invisible = _show_to_shared_status_show( expected_show) _status = xpath.queryForNodes('//status', event.query)[0] assertEquals(msg[:max_status_message_length], _status.children[0]) _show = xpath.queryForNodes('//show', event.query)[0] assertEquals(shared_show, _show.children[0]) _invisible = xpath.queryForNodes('//invisible', event.query)[0] assertEquals(shared_invisible, _invisible.getAttribute('value')) if was_away or (away and was_invisible): q.expect('stream-presence') else: q.expect('stream-presence') q.expect('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{ 1: (presence_types[expected_show], expected_show, msg[:max_status_message_length]) }]) if wrong_presence_pattern: q.unforbid_events([wrong_presence_pattern])
def no_muji_presences(muc): return EventPattern( 'stream-presence', to=muc + "/test", predicate=lambda x: xpath.queryForNodes("/presence/muji", x.stanza))
def send_privacy_list(self, req_iq, list_items): req_list = xpath.queryForNodes('//list', req_iq)[0] iq = elem_iq(self, "result", id=req_iq["id"])(elem( ns.PRIVACY, 'query')(elem('list', name=req_list["name"])(*list_items))) self.send(iq)
def test(q, bus, conn): call_async(q, conn.Future, 'EnsureSidecar', ycs.STATUS_IFACE) conn.Connect() # Now we're connected, the call we made earlier should return. e = q.expect('dbus-return', method='EnsureSidecar') path, props = e.value assertEquals({}, props) status = ProxyWrapper(bus.get_object(conn.bus_name, path), ycs.STATUS_IFACE, {}) discovered = status.Get(ycs.STATUS_IFACE, 'DiscoveredServices', dbus_interface=dbus.PROPERTIES_IFACE) assertEquals({}, discovered) # announce a contact with the right caps ver = compute_caps_hash([], [], banshee) txt_record = { "txtvers": "1", "status": "avail", "node": CLIENT_NAME, "ver": ver, "hash": "sha-1" } contact_name = "test-service@" + get_host_name() listener, port = setup_stream_listener(q, contact_name) announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, txt_record) handle = wait_for_contact_in_publish(q, bus, conn, contact_name) # this is the first presence, Salut connects to the contact e = q.expect('incoming-connection', listener=listener) incoming = e.connection # Salut looks up its capabilities event = q.expect('stream-iq', connection=incoming, query_ns=ns.DISCO_INFO) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assertEquals(CLIENT_NAME + '#' + ver, query_node.attributes['node']) contact_handle = conn.RequestHandles(cs.HT_CONTACT, [contact_name])[0] # send good reply result = make_result_iq(event.stanza) query = result.firstChildElement() query['node'] = CLIENT_NAME + '#' + ver x = query.addElement((ns.X_DATA, 'x')) x['type'] = 'result' # FORM_TYPE field = x.addElement((None, 'field')) field['var'] = 'FORM_TYPE' field['type'] = 'hidden' field.addElement((None, 'value'), content='urn:ytstenut:capabilities#org.gnome.Banshee') # type field = x.addElement((None, 'field')) field['var'] = 'type' field.addElement((None, 'value'), content='application') # name field = x.addElement((None, 'field')) field['var'] = 'name' field.addElement((None, 'value'), content='en_GB/Banshee Media Player') field.addElement((None, 'value'), content='fr/Banshee Lecteur de Musique') # capabilities field = x.addElement((None, 'field')) field['var'] = 'capabilities' field.addElement((None, 'value'), content='urn:ytstenut:capabilities:yts-caps-audio') field.addElement((None, 'value'), content='urn:ytstenut:data:jingle:rtp') incoming.send(result) # this will be fired as text channel caps will be fired _, e = q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', predicate=lambda e: contact_handle in e.args[0]), EventPattern('dbus-signal', signal='ServiceAdded')) contact_id, service_name, details = e.args assertEquals(contact_name, contact_id) assertEquals('org.gnome.Banshee', service_name) type, name_map, caps = details assertEquals('application', type) assertEquals( { 'en_GB': 'Banshee Media Player', 'fr': 'Banshee Lecteur de Musique' }, name_map) assertSameSets([ 'urn:ytstenut:capabilities:yts-caps-audio', 'urn:ytstenut:data:jingle:rtp' ], caps) discovered = status.Get(ycs.STATUS_IFACE, 'DiscoveredServices', dbus_interface=dbus.PROPERTIES_IFACE) assertEquals( { contact_name: { 'org.gnome.Banshee': ('application', { 'en_GB': 'Banshee Media Player', 'fr': 'Banshee Lecteur de Musique' }, [ 'urn:ytstenut:capabilities:yts-caps-audio', 'urn:ytstenut:data:jingle:rtp' ]) }, }, discovered) # add evince tmp = banshee.copy() tmp.update(evince) ver = compute_caps_hash([], [], tmp) txt_record['ver'] = ver announcer.update(txt_record) # Salut looks up our capabilities event = q.expect('stream-iq', connection=incoming, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ CLIENT_NAME + '#' + txt_record['ver'] # send good reply result['id'] = event.stanza['id'] query['node'] = CLIENT_NAME + '#' + ver x = query.addElement((ns.X_DATA, 'x')) x['type'] = 'result' # FORM_TYPE field = x.addElement((None, 'field')) field['var'] = 'FORM_TYPE' field['type'] = 'hidden' field.addElement((None, 'value'), content='urn:ytstenut:capabilities#org.gnome.Evince') # type field = x.addElement((None, 'field')) field['var'] = 'type' field.addElement((None, 'value'), content='application') # name field = x.addElement((None, 'field')) field['var'] = 'name' field.addElement((None, 'value'), content='en_GB/Evince Picture Viewer') field.addElement((None, 'value'), content='fr/Evince uh, ow do you say') # capabilities field = x.addElement((None, 'field')) field['var'] = 'capabilities' field.addElement((None, 'value'), content='urn:ytstenut:capabilities:pics') incoming.send(result) e = q.expect('dbus-signal', signal='ServiceAdded') contact_id, service_name, details = e.args assertEquals(contact_name, contact_id) assertEquals('org.gnome.Evince', service_name) type, name_map, caps = details assertEquals('application', type) assertEquals( { 'en_GB': 'Evince Picture Viewer', 'fr': 'Evince uh, ow do you say' }, name_map) assertSameSets(['urn:ytstenut:capabilities:pics'], caps) discovered = status.Get(ycs.STATUS_IFACE, 'DiscoveredServices', dbus_interface=dbus.PROPERTIES_IFACE) assertEquals( { contact_name: { 'org.gnome.Banshee': ('application', { 'en_GB': 'Banshee Media Player', 'fr': 'Banshee Lecteur de Musique' }, [ 'urn:ytstenut:capabilities:yts-caps-audio', 'urn:ytstenut:data:jingle:rtp' ]), 'org.gnome.Evince': ('application', { 'en_GB': 'Evince Picture Viewer', 'fr': 'Evince uh, ow do you say' }, ['urn:ytstenut:capabilities:pics']) } }, discovered) # remove evince ver = compute_caps_hash([], [], banshee) txt_record['ver'] = ver forbidden = [ EventPattern('dbus-signal', signal='stream-iq', connection=incoming) ] q.forbid_events(forbidden) announcer.update(txt_record) e = q.expect('dbus-signal', signal='ServiceRemoved') contact_id, service_name = e.args assertEquals(contact_name, contact_id) assertEquals('org.gnome.Evince', service_name) discovered = status.Get(ycs.STATUS_IFACE, 'DiscoveredServices', dbus_interface=dbus.PROPERTIES_IFACE) assertEquals( { contact_name: { 'org.gnome.Banshee': ('application', { 'en_GB': 'Banshee Media Player', 'fr': 'Banshee Lecteur de Musique' }, [ 'urn:ytstenut:capabilities:yts-caps-audio', 'urn:ytstenut:data:jingle:rtp' ]) }, }, discovered) sync_stream(q, incoming) q.unforbid_events(forbidden) # now just evince ver = compute_caps_hash([], [], evince) txt_record['ver'] = ver announcer.update(txt_record) # Salut looks up our capabilities event = q.expect('stream-iq', connection=incoming, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ CLIENT_NAME + '#' + txt_record['ver'] # send good reply result = make_result_iq(event.stanza) query = result.firstChildElement() query['node'] = CLIENT_NAME + '#' + ver x = query.addElement((ns.X_DATA, 'x')) x['type'] = 'result' # FORM_TYPE field = x.addElement((None, 'field')) field['var'] = 'FORM_TYPE' field['type'] = 'hidden' field.addElement((None, 'value'), content='urn:ytstenut:capabilities#org.gnome.Evince') # type field = x.addElement((None, 'field')) field['var'] = 'type' field.addElement((None, 'value'), content='application') # name field = x.addElement((None, 'field')) field['var'] = 'name' field.addElement((None, 'value'), content='en_GB/Evince Picture Viewer') field.addElement((None, 'value'), content='fr/Evince uh, ow do you say') # capabilities field = x.addElement((None, 'field')) field['var'] = 'capabilities' field.addElement((None, 'value'), content='urn:ytstenut:capabilities:pics') incoming.send(result) sa, sr = q.expect_many( EventPattern('dbus-signal', signal='ServiceAdded'), EventPattern('dbus-signal', signal='ServiceRemoved')) contact_id, service_name, details = sa.args assertEquals(contact_name, contact_id) assertEquals('org.gnome.Evince', service_name) type, name_map, caps = details assertEquals('application', type) assertEquals( { 'en_GB': 'Evince Picture Viewer', 'fr': 'Evince uh, ow do you say' }, name_map) assertSameSets(['urn:ytstenut:capabilities:pics'], caps) contact_id, service_name = sr.args assertEquals(contact_name, contact_id) assertEquals('org.gnome.Banshee', service_name) discovered = status.Get(ycs.STATUS_IFACE, 'DiscoveredServices', dbus_interface=dbus.PROPERTIES_IFACE) assertEquals( { contact_name: { 'org.gnome.Evince': ('application', { 'en_GB': 'Evince Picture Viewer', 'fr': 'Evince uh, ow do you say' }, ['urn:ytstenut:capabilities:pics']) } }, discovered) # just banshee again ver = compute_caps_hash([], [], banshee) txt_record['ver'] = ver forbidden = [ EventPattern('dbus-signal', signal='stream-iq', connection=incoming) ] q.forbid_events(forbidden) announcer.update(txt_record) sr, sa = q.expect_many( EventPattern('dbus-signal', signal='ServiceRemoved'), EventPattern('dbus-signal', signal='ServiceAdded')) contact_id, service_name = sr.args assertEquals(contact_name, contact_id) assertEquals('org.gnome.Evince', service_name) contact_id, service_name, details = sa.args assertEquals(contact_name, contact_id) assertEquals('org.gnome.Banshee', service_name) type, name_map, caps = details assertEquals('application', type) assertEquals( { 'en_GB': 'Banshee Media Player', 'fr': 'Banshee Lecteur de Musique' }, name_map) assertSameSets([ 'urn:ytstenut:capabilities:yts-caps-audio', 'urn:ytstenut:data:jingle:rtp' ], caps) discovered = status.Get(ycs.STATUS_IFACE, 'DiscoveredServices', dbus_interface=dbus.PROPERTIES_IFACE) assertEquals( { contact_name: { 'org.gnome.Banshee': ('application', { 'en_GB': 'Banshee Media Player', 'fr': 'Banshee Lecteur de Musique' }, [ 'urn:ytstenut:capabilities:yts-caps-audio', 'urn:ytstenut:data:jingle:rtp' ]) } }, discovered) sync_stream(q, incoming) q.unforbid_events(forbidden) # both again ver = compute_caps_hash([], [], tmp) txt_record['ver'] = ver announcer.update(txt_record) sa = q.expect('dbus-signal', signal='ServiceAdded') contact_id, service_name, details = sa.args assertEquals(contact_name, contact_id) assertEquals('org.gnome.Evince', service_name) type, name_map, caps = details assertEquals('application', type) assertEquals( { 'en_GB': 'Evince Picture Viewer', 'fr': 'Evince uh, ow do you say' }, name_map) assertSameSets(['urn:ytstenut:capabilities:pics'], caps) discovered = status.Get(ycs.STATUS_IFACE, 'DiscoveredServices', dbus_interface=dbus.PROPERTIES_IFACE) assertEquals( { contact_name: { 'org.gnome.Banshee': ('application', { 'en_GB': 'Banshee Media Player', 'fr': 'Banshee Lecteur de Musique' }, [ 'urn:ytstenut:capabilities:yts-caps-audio', 'urn:ytstenut:data:jingle:rtp' ]), 'org.gnome.Evince': ('application', { 'en_GB': 'Evince Picture Viewer', 'fr': 'Evince uh, ow do you say' }, ['urn:ytstenut:capabilities:pics']) } }, discovered) sync_stream(q, incoming) # and finally, nothing ver = compute_caps_hash([], [], {}) txt_record['ver'] = ver announcer.update(txt_record) # Salut looks up our capabilities event = q.expect('stream-iq', connection=incoming, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ CLIENT_NAME + '#' + txt_record['ver'] # send good reply result = make_result_iq(event.stanza) query = result.firstChildElement() query['node'] = CLIENT_NAME + '#' + ver incoming.send(result) q.expect_many( EventPattern('dbus-signal', signal='ServiceRemoved', args=[contact_name, 'org.gnome.Banshee']), EventPattern('dbus-signal', signal='ServiceRemoved', args=[contact_name, 'org.gnome.Evince'])) discovered = status.Get(ycs.STATUS_IFACE, 'DiscoveredServices', dbus_interface=dbus.PROPERTIES_IFACE) assertEquals({}, discovered)
def test(q, bus, conn, stream): handles = {} handles['bob'] = conn.get_contact_handle_sync('bob@localhost') buddy_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo') act_prop_iface = dbus.Interface(conn, 'org.laptop.Telepathy.ActivityProperties') call_async(q, buddy_iface, 'GetActivities', handles['bob']) event = q.expect('stream-iq', iq_type='get', to='bob@localhost') # Bob has no activities event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'bob@localhost' stream.send(event.stanza) event = q.expect('dbus-return', method='GetActivities') # initially, Bob has no activities assert event.value == ([], ) # Bob sends an activity properties message message = domish.Element(('jabber:client', 'message')) message['from'] = 'bob@localhost' message['to'] = 'test@localhost' properties = message.addElement((ns.OLPC_ACTIVITY_PROPS, 'properties')) properties['room'] = '*****@*****.**' properties['activity'] = 'foo_id' property = properties.addElement((None, 'property')) property['type'] = 'str' property['name'] = 'color' property.addContent('#ffff00,#00ffff') property = properties.addElement((None, 'property')) property['type'] = 'bool' property['name'] = 'private' property.addContent('1') stream.send(message) event = q.expect('dbus-signal', signal='ActivityPropertiesChanged') handles['chat'], props = event.args assert props == {'color': '#ffff00,#00ffff', 'private': True} event = q.expect('dbus-signal', signal='ActivitiesChanged') assert event.args[0] == handles['bob'] acts = event.args[1] assert len(acts) == 1 assert acts[0] == ('foo_id', handles['chat']) props = act_prop_iface.GetProperties(handles['chat']) assert props == {'color': '#ffff00,#00ffff', 'private': True} # Bobs invites us to the activity message = domish.Element((None, 'message')) message['from'] = '*****@*****.**' message['to'] = 'test@localhost' x = message.addElement((ns.MUC_USER, 'x')) invite = x.addElement((None, 'invite')) invite['from'] = 'bob@localhost' reason = invite.addElement((None, 'reason')) reason.addContent('No good reason') stream.send(message) event = q.expect('dbus-signal', signal='NewChannel') assert event.args[1] == cs.CHANNEL_TYPE_TEXT assert event.args[2] == 2 # handle type assert event.args[3] == handles['chat'] # handle text_chan = wrap_channel(bus.get_object(conn.bus_name, event.args[0]), 'Text') group_iface = text_chan.Group members = group_iface.GetAllMembers()[0] local_pending = group_iface.GetAllMembers()[1] remote_pending = group_iface.GetAllMembers()[2] assert len(members) == 1 assert conn.inspect_contact_sync(members[0]) == 'bob@localhost' assert len(local_pending) == 1 # FIXME: the username-part-is-nickname assumption assert conn.inspect_contact_sync(local_pending[0]) == \ '[email protected]/test' assert len(remote_pending) == 0 handles['chat_self'] = text_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, "SelfHandle") assert handles['chat_self'] == local_pending[0] # by now, we should have picked up the extra activity properties call_async(q, buddy_iface, 'GetActivities', handles['bob']) event = q.expect('stream-iq', iq_type='get', to='bob@localhost') # Bob still has no (public) activities event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'bob@localhost' stream.send(event.stanza) event = q.expect('dbus-return', method='GetActivities') assert event.value == ([('foo_id', handles['chat'])], ) # OK, now accept the invitation call_async(q, group_iface, 'AddMembers', [handles['chat_self']], 'Oh, OK then') _, event, _ = q.expect_many( EventPattern('stream-presence', to='[email protected]/test'), EventPattern('dbus-signal', signal='MembersChanged'), EventPattern('dbus-return', method='AddMembers')) assert event.args == [ '', [], [handles['bob']], [], [handles['chat_self']], 0, cs.GC_REASON_INVITED ] # Send presence for own membership of room. stream.send( make_muc_presence('owner', 'moderator', '*****@*****.**', 'test')) event = q.expect('dbus-signal', signal='MembersChanged') assert event.args == ['', [handles['chat_self']], [], [], [], 0, 0] call_async(q, buddy_iface, 'SetActivities', [('foo_id', handles['chat'])]) event = q.expect('stream-iq', iq_type='set') event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) q.expect('dbus-return', method='SetActivities') call_async(q, act_prop_iface, 'SetProperties', handles['chat'], { 'color': '#ffff00,#00ffff', 'private': True }) event = q.expect('dbus-signal', signal='ActivityPropertiesChanged') chat_handle, props = event.args assert chat_handle == handles['chat'] assert props == {'color': '#ffff00,#00ffff', 'private': True} q.expect('dbus-return', method='SetProperties') # Test sending an invitation handles['alice'] = conn.get_contact_handle_sync('alice@localhost') call_async(q, group_iface, 'AddMembers', [handles['alice']], 'I want to test invitations') event = q.expect('stream-message', to='alice@localhost') message = event.stanza properties = xpath.queryForNodes('/message/properties', message) assert (properties is not None and len(properties) == 1), repr(properties) assert properties[0].uri == ns.OLPC_ACTIVITY_PROPS assert properties[0]['room'] == '*****@*****.**' assert properties[0]['activity'] == 'foo_id' property = xpath.queryForNodes('/properties/property', properties[0]) assert (property is not None and len(property) == 2), repr(property) seen = set() for p in property: seen.add(p['name']) if p['name'] == 'color': assert p['type'] == 'str' assert str(p) == '#ffff00,#00ffff' elif p['name'] == 'private': assert p['type'] == 'bool' assert str(p) == '1' else: assert False, 'Unexpected property %s' % p['name'] assert 'color' in seen, seen assert 'private' in seen, seen event = q.expect('stream-message', to='*****@*****.**') message = event.stanza x = xpath.queryForNodes('/message/x', message) assert (x is not None and len(x) == 1), repr(x) assert x[0].uri == ns.MUC_USER invites = xpath.queryForNodes('/x/invite', x[0]) assert (invites is not None and len(invites) == 1), repr(invites) assert invites[0]['to'] == 'alice@localhost' reasons = xpath.queryForNodes('/invite/reason', invites[0]) assert (reasons is not None and len(reasons) == 1), repr(reasons) assert str(reasons[0]) == 'I want to test invitations' call_async(q, act_prop_iface, 'SetProperties', handles['chat'], { 'color': '#f00baa,#f00baa', 'private': True }) event, apc_event, _ = q.expect_many( EventPattern('stream-message', to='alice@localhost'), EventPattern('dbus-signal', signal='ActivityPropertiesChanged'), EventPattern('dbus-return', method='SetProperties'), ) message = event.stanza properties = xpath.queryForNodes('/message/properties', message) assert (properties is not None and len(properties) == 1), repr(properties) assert properties[0].uri == ns.OLPC_ACTIVITY_PROPS assert properties[0]['room'] == '*****@*****.**' assert properties[0]['activity'] == 'foo_id' property = xpath.queryForNodes('/properties/property', properties[0]) assert (property is not None and len(property) == 2), repr(property) seen = set() for p in property: seen.add(p['name']) if p['name'] == 'color': assert p['type'] == 'str' assert str(p) == '#f00baa,#f00baa' elif p['name'] == 'private': assert p['type'] == 'bool' assert str(p) == '1' else: assert False, 'Unexpected property %s' % p['name'] assert 'color' in seen, seen assert 'private' in seen, seen chat_handle, props = apc_event.args assert chat_handle == handles['chat'] assert props == {'color': '#f00baa,#f00baa', 'private': True}
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, bytestream_cls, access_control): global last_tube_id t.check_conn_properties(q, conn) vcard_event, roster_event = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', query_ns=ns.ROSTER)) self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") acknowledge_iq(stream, vcard_event.stanza) roster = roster_event.stanza roster['type'] = 'result' item = roster_event.query.addElement('item') item['jid'] = 'bob@localhost' # Bob can do tubes item['subscription'] = 'both' stream.send(roster) bob_full_jid = 'bob@localhost/Bob' self_full_jid = 'test@localhost/Resource' # Send Bob presence and his tube caps presence = domish.Element(('jabber:client', 'presence')) presence['from'] = bob_full_jid presence['to'] = self_full_jid c = presence.addElement('c') c['xmlns'] = 'http://jabber.org/protocol/caps' c['node'] = 'http://example.com/ICantBelieveItsNotTelepathy' c['ver'] = '1.2.3' stream.send(presence) event = q.expect('stream-iq', iq_type='get', query_ns='http://jabber.org/protocol/disco#info', to=bob_full_jid) result = event.stanza result['type'] = 'result' assert event.query['node'] == \ 'http://example.com/ICantBelieveItsNotTelepathy#1.2.3' feature = event.query.addElement('feature') feature['var'] = ns.TUBES stream.send(result) # A tube request can be done only if the contact has tube capabilities # Ensure that Bob's caps have been received sync_stream(q, stream) # Also ensure that all the new contact list channels have been announced, # so that the NewChannel(s) signals we look for after calling # RequestChannel are the ones we wanted. sync_dbus(bus, q, conn) bob_handle = conn.get_contact_handle_sync('bob@localhost') # let's try to accept a D-Bus tube using the new API bytestream = bytestream_cls(stream, q, 'gamma', bob_full_jid, self_full_jid, True) last_tube_id += 1 contact_offer_dbus_tube(bytestream, last_tube_id) def new_chan_predicate(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE e = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) channels = e.args[0] assert len(channels) == 1 path, props = channels[0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE assert props[cs.INITIATOR_HANDLE] == bob_handle assert props[cs.INITIATOR_ID] == 'bob@localhost' assert props[cs.INTERFACES] == [cs.CHANNEL_IFACE_TUBE] assert props[cs.REQUESTED] == False assert props[cs.TARGET_HANDLE] == bob_handle assert props[cs.TARGET_ID] == 'bob@localhost' assert props[cs.DBUS_TUBE_SERVICE_NAME] == 'com.example.TestCase2' assert props[cs.TUBE_PARAMETERS] == {'login': '******'} assert props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS] == [ cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST ] assert cs.TUBE_STATE not in props tube_chan = bus.get_object(conn.bus_name, path) tube_chan_iface = dbus.Interface(tube_chan, cs.CHANNEL) dbus_tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_DBUS_TUBE) status = tube_chan.Get(cs.CHANNEL_IFACE_TUBE, 'State', dbus_interface=cs.PROPERTIES_IFACE) assert status == cs.TUBE_STATE_LOCAL_PENDING # try to accept using a wrong access control try: dbus_tube_iface.Accept(cs.SOCKET_ACCESS_CONTROL_PORT) except dbus.DBusException as e: assertEquals(e.get_dbus_name(), cs.INVALID_ARGUMENT) else: assert False # accept the tube (new API) call_async(q, dbus_tube_iface, 'Accept', access_control) events = q.expect_many( EventPattern('stream-iq', iq_type='result', query_ns=ns.SI), EventPattern('dbus-return', method='Accept')) iq_event = events[0] bytestream.check_si_reply(iq_event.stanza) tube = xpath.queryForNodes('/iq/si/tube[@xmlns="%s"]' % ns.TUBES, iq_event.stanza) assert len(tube) == 1 return_event = events[1] addr = return_event.value[0] assert len(addr) > 0 # Open the bytestream _, state_event = bytestream.open_bytestream([], [ EventPattern( 'dbus-signal', signal='TubeChannelStateChanged', path=path) ]) state_event = state_event[0] assert state_event.args[0] == cs.TUBE_STATE_OPEN # close the tube tube_chan_iface.Close() q.expect_many(EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'))
conn.Location, 'SetLocation', { 'lat': dbus.Double(0.0, variant_level=1), 'lon': 0.0, 'language': 'en', 'timestamp': date, 'country': 'Congo', 'accuracy': 1.4, # Gabble silently ignores unknown keys 'badger': 'mushroom' }) geoloc_iq_set_event = EventPattern( 'stream-iq', predicate=lambda x: xpath.queryForNodes( "/iq/pubsub/publish/item/geoloc", x.stanza)) event = q.expect_many(geoloc_iq_set_event)[0] geoloc = xpath.queryForNodes("/iq/pubsub/publish/item/geoloc", event.stanza)[0] assertEquals(geoloc.getAttribute((ns.XML, 'lang')), 'en') lon = xpath.queryForNodes('/geoloc/lon', geoloc)[0] assertEquals(float(str(lon)), 0.0) lat = xpath.queryForNodes('/geoloc/lat', geoloc)[0] assertEquals(float(str(lat)), 0.0) timestamp = xpath.queryForNodes('/geoloc/timestamp', geoloc)[0] assertEquals(str(timestamp), date_str) country = xpath.queryForNodes('/geoloc/country', geoloc)[0] assertEquals(str(country), 'Congo') lat = xpath.queryForNodes('/geoloc/accuracy', geoloc)[0] assertEquals(float(str(lat)), 1.4)
def process_site(mcursor, nwsli, network): """Do our processing work""" url = ("http://water.weather.gov/ahps2/hydrograph_to_xml.php?" "gage=%s&output=xml") % (nwsli, ) elementStream = domish.elementStream() roots = [] results = [] elementStream.DocumentStartEvent = roots.append elementStream.ElementEvent = lambda elem: roots[0].addChild(elem) elementStream.DocumentEndEvent = lambda: results.append(roots[0]) try: req = requests.get(url, timeout=30) xml = req.content if xml.strip() == 'No results found for this gage.': print('No results for %s' % (nwsli, )) return except Exception as exp: print("DOWNLOAD ERROR") print(url) print(exp) return try: elementStream.parse(xml) except Exception as exp: print("XML ERROR") print(url) print(exp) return elem = results[0] nodes = xpath.queryForNodes('/site/sigstages', elem) if nodes is None: print("No data found for %s" % (nwsli, )) return sigstages = nodes[0] data = { 'id': nwsli, 'network': network, 'sigstage_low': None, 'sigstage_action': None, 'sigstage_bankfull': None, 'sigstage_flood': None, 'sigstage_moderate': None, 'sigstage_major': None, 'sigstage_record': None } for s in sigstages.children: val = str(s) if val == '': continue data['sigstage_%s' % (s.name, )] = float(val) if 'sigstage_low' not in data: print('No Data %s %s' % (nwsli, network)) return print(("%s %5.1f %5.1f %5.1f %5.1f %5.1f %5.1f %5.1f") % (data['id'], data['sigstage_low'] or -99, data['sigstage_action'] or -99, data['sigstage_bankfull'] or -99, data['sigstage_flood'] or -99, data['sigstage_moderate'] or -99, data['sigstage_major'] or -99, data['sigstage_record'] or -99)) mcursor.execute( """ UPDATE stations SET sigstage_low = %(sigstage_low)s, sigstage_action = %(sigstage_action)s, sigstage_bankfull = %(sigstage_bankfull)s, sigstage_flood = %(sigstage_flood)s, sigstage_moderate = %(sigstage_moderate)s, sigstage_major = %(sigstage_major)s, sigstage_record = %(sigstage_record)s WHERE id = %(id)s and network = %(network)s """, data)
def test_two_clients(q, bus, conn, stream, contact1, contact2, contact_handle1, contact_handle2, client, broken_hash): presence = make_presence(contact1, status='hello') stream.send(presence) q.expect('dbus-signal', signal='PresencesChanged', args=[{ contact_handle1: (2, u'available', 'hello') }]) presence = make_presence(contact2, status='hello') stream.send(presence) q.expect('dbus-signal', signal='PresencesChanged', args=[{ contact_handle2: (2, u'available', 'hello') }]) # no special capabilities for h in (contact_handle1, contact_handle2): for rcc in get_contacts_capabilities_sync(conn, [h])[h]: assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE)) # send updated presence with Jingle caps info ver = compute_caps_hash(some_identities, jingle_av_features, {}) caps = { 'node': client, 'ver': ver, 'hash': 'sha-1', } presence = make_presence(contact1, status='hello', caps=caps) stream.send(presence) presence = make_presence(contact2, status='hello', caps=caps) stream.send(presence) # Gabble looks up our capabilities event = q.expect('stream-iq', to=contact1, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver # don't receive any D-Bus signal forbidden = [ EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), ] q.forbid_events(forbidden) sync_dbus(bus, q, conn) q.unforbid_events(forbidden) result = make_caps_disco_reply(stream, event.stanza, some_identities, jingle_av_features) if broken_hash: # make the hash break! query = result.firstChildElement() query.addElement( 'feature')['var'] = 'http://example.com/another-feature' stream.send(result) if broken_hash: # Gabble looks up our capabilities again because the first contact # failed to provide a valid hash event = q.expect('stream-iq', to=contact2, query_ns='http://jabber.org/protocol/disco#info') query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver # don't receive any D-Bus signal q.forbid_events(forbidden) sync_dbus(bus, q, conn) q.unforbid_events(forbidden) # send good reply send_disco_reply(stream, event.stanza, some_identities, jingle_av_features) # we can now do audio calls cc, = q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', predicate=lambda e: contact_handle2 in e.args[0]), ) assert_rccs_callable(cc.args[0][contact_handle2]) if not broken_hash: # if the first contact failed to provide a good hash, it does not # deserve its capabilities to be understood by Gabble! cc, = q.expect_many( EventPattern('dbus-signal', signal='ContactCapabilitiesChanged', predicate=lambda e: contact_handle1 in e.args[0]), ) assert_rccs_callable(cc.args[0][contact_handle1]) # don't receive any further signals q.forbid_events(forbidden) sync_dbus(bus, q, conn) q.unforbid_events(forbidden)