def advertise_caps(q, bus, conn, stream, filters, expected_features, unexpected_features, expected_caps): # make sure nothing from a previous update is still running sync_dbus(bus, q, conn) self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") ret_caps = conn.ContactCapabilities.UpdateCapabilities( [(cs.CLIENT + '.Foo', filters, [])]) # Expect Gabble to reply with the correct caps event, namespaces, _, signaled_caps = receive_presence_and_ask_caps(q, stream) assertSameElements(expected_caps, signaled_caps[self_handle]) assertContains(ns.TP_FT_METADATA, namespaces) for var in expected_features: assertContains(var, namespaces) for var in unexpected_features: assertDoesNotContain(var, namespaces) # Check our own caps caps = get_contacts_capabilities_sync(conn, [self_handle]) assertSameElements(expected_caps, caps[self_handle]) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(caps[self_handle], caps_via_contacts_iface)
def tube_no_text(q, bus, conn, stream): jid = '*****@*****.**' # create a stream tube. # this will need a MUC channel to be opened, but we want to make # sure it doesn't get signalled. request_stream_tube(q, bus, conn, 'CreateChannel', jid) send_muc_presence(q, stream, jid) ret, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels')) q.forbid_events([EventPattern('dbus-signal', signal='NewChannels')]) tube_path, tube_props = ret.value assertEquals(cs.CHANNEL_TYPE_STREAM_TUBE, tube_props[cs.CHANNEL_TYPE]) channels = new_sig.args[0] assertEquals(1, len(channels)) path, props = channels[0] assertEquals(tube_path, path) assertEquals(tube_props, props) sync_dbus(bus, q, conn) q.unforbid_all()
def signal_channel_expect_query(q, bus, account, conn, empathy, kopete): # This target is special-cased in test-plugin.c target = '*****@*****.**' channel_properties = dbus.Dictionary(text_fixed_properties, signature='sv') channel_properties[cs.CHANNEL + '.TargetID'] = target channel_properties[cs.CHANNEL + '.TargetHandle'] = \ conn.ensure_handle(cs.HT_CONTACT, target) channel_properties[cs.CHANNEL + '.InitiatorID'] = target channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \ conn.ensure_handle(cs.HT_CONTACT, target) channel_properties[cs.CHANNEL + '.Requested'] = False channel_properties[cs.CHANNEL + '.Interfaces'] = \ dbus.Array([cs.CHANNEL_IFACE_DESTROYABLE, cs.CHANNEL_IFACE_GROUP, ],signature='s') chan = SimulatedChannel(conn, channel_properties, group=True) chan.announce() e = q.expect('dbus-signal', path=cs.CD_PATH, interface=cs.CD_IFACE_OP_LIST, signal='NewDispatchOperation') cdo_path = e.args[0] cdo_properties = e.args[1] assert cdo_properties[cs.CDO + '.Account'] == account.object_path assert cdo_properties[cs.CDO + '.Connection'] == conn.object_path assert cs.CDO + '.Interfaces' in cdo_properties handlers = cdo_properties[cs.CDO + '.PossibleHandlers'][:] handlers.sort() assert handlers == [cs.tp_name_prefix + '.Client.Empathy', cs.tp_name_prefix + '.Client.Kopete'], handlers e, k = q.expect_many( EventPattern('dbus-method-call', path=empathy.object_path, interface=cs.OBSERVER, method='ObserveChannels', handled=False), EventPattern('dbus-method-call', path=kopete.object_path, interface=cs.OBSERVER, method='ObserveChannels', handled=False), ) # What does the policy service think? permission = q.expect('dbus-method-call', path='/com/example/Policy', interface='com.example.Policy', method='RequestPermission') # Think about it for a bit sync_dbus(bus, q, account) # Both Observers indicate that they are ready to proceed q.dbus_return(k.message, signature='') q.dbus_return(e.message, signature='') # Let the test code decide how to reply return permission, chan, cdo_path
def test(q, bus, conn, stream): conn.Connect() # Initial vCard request. Respond only after we call SetAliases(). vcard_get_event = q.expect('stream-iq', iq_type='get', to=None, query_ns=ns.VCARD_TEMP, query_name='vCard') sync_stream(q, stream) handle = conn.GetSelfHandle() call_async(q, conn.Aliasing, 'SetAliases', {handle: 'Some Guy'}) sync_dbus(bus, q, conn) acknowledge_iq(stream, vcard_get_event.stanza) # Gabble sets a new vCard with our nickname. vcard_set_event = q.expect('stream-iq', iq_type='set', query_ns=ns.VCARD_TEMP, query_name='vCard') # Before the server replies, the user sets their avatar. call_async(q, conn.Avatars, 'SetAvatar', 'hello', 'image/png') sync_dbus(bus, q, conn) acknowledge_iq(stream, vcard_set_event.stanza) vcard_set_event = q.expect('stream-iq', iq_type='set', query_ns=ns.VCARD_TEMP, query_name='vCard') acknowledge_iq(stream, vcard_set_event.stanza) q.expect('dbus-return', method='SetAvatar') # And then crashes. sync_stream(q, stream) conn.Disconnect() q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
def test(q, bus, conn, stream): conn.Connect() roster_event = q.expect('stream-iq', query_ns=ns.ROSTER) roster_event.stanza['type'] = 'result' call_async(q, conn, "RequestHandles", cs.HT_GROUP, ['test']) event = q.expect('dbus-return', method='RequestHandles') test_handle = event.value[0][0] # send an empty roster stream.send(roster_event.stanza) sync_stream(q, stream) sync_dbus(bus, q, conn) call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST, cs.TARGET_HANDLE_TYPE: cs.HT_GROUP, cs.TARGET_HANDLE: test_handle, }) event = q.expect('dbus-return', method='CreateChannel') ret_path, ret_props = event.value event = q.expect('dbus-signal', signal='NewChannels') path, props = event.args[0][0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_CONTACT_LIST, props assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_GROUP, props assert props[cs.TARGET_HANDLE] == test_handle, props assert props[cs.TARGET_ID] == 'test', props assert ret_path == path, (ret_path, path) assert ret_props == props, (ret_props, props)
def test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) handle = conn.get_contact_handle_sync('*****@*****.**') call_async(q, conn.Aliasing, 'RequestAliases', [handle]) # First, Gabble sends a PEP query event = q.expect('stream-iq', to='*****@*****.**', iq_type='get', query_ns='http://jabber.org/protocol/pubsub', query_name='pubsub') # We disconnect too soon to get a reply disconnect_conn(q, conn, stream) # fd.o #31412 was that while the request pipeline was shutting down, # it would give the PEP query an error; the aliasing code would # respond by falling back to vCard via the request pipeline, which # was no longer there, *crash*. # check that Gabble hasn't crashed sync_dbus(bus, q, conn)
def tube_remains_text_closes(q, bus, conn): jid = 'test-muc' connect(q, bus, conn) text_chan, text_path, _ = text_channel(q, bus, conn, 'CreateChannel', jid) tube_chan, tube_path, _ = stream_tube(q, bus, conn, 'CreateChannel', jid) # now let's try and close the text channel # this should happen sucessfully but the tube channel # should stick around forbidden = [EventPattern('dbus-signal', signal='ChannelClosed', args=[tube_path])] q.forbid_events(forbidden) assert_on_bus(q, tube_chan) assert_on_bus(q, text_chan) text_chan.Close() expect_close(q, text_path) sync_dbus(bus, q, conn) assert_on_bus(q, tube_chan) assert_not_on_bus(q, text_chan) q.unforbid_events(forbidden)
def test(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0, 1]) # join a chat room with the same name as our nick call_async( q, conn.Requests, 'CreateChannel', { CHANNEL_TYPE: CHANNEL_TYPE_TEXT, TARGET_HANDLE_TYPE: HT_ROOM, TARGET_ID: CHANNEL }) # wait for the join to finish ret = q.expect('dbus-return', method='CreateChannel') muc_path = ret.value chan = bus.get_object(conn.bus_name, ret.value[0]) group_text_chan = dbus.Interface(chan, CHANNEL_TYPE_TEXT) group_text_chan.connect_to_signal('Received', group_received_cb) q.expect('dbus-signal', signal='MembersChanged') stream.sendMessage('PRIVMSG', NICK, ':PRIVATE', prefix=REMOTEUSER) event = q.expect('dbus-signal', signal='Received') # this seems a bit fragile, but I'm not entirely sure how else to ensure # that the message is not delivered to the MUC channel assert event.path not in muc_path # verify that we didn't receive a 'Received' D-Bus signal on the group text # channel global group_received_flag sync_dbus(bus, q, conn) assert group_received_flag == False call_async(q, conn, 'Disconnect') return True
def text_remains_after_tube(q, bus, conn, stream): jid = '*****@*****.**' tube_chan, tube_path, _ = stream_tube(q, bus, conn, stream, 'CreateChannel', jid) text_chan, text_path, _ = text_channel(q, bus, conn, stream, 'CreateChannel', jid, presence=False) sync_dbus(bus, q, conn) tube_chan.Close() expect_close(q, tube_path) assert_not_on_bus(q, tube_chan) assert_on_bus(q, text_chan) call_async(q, text_chan.Properties, 'GetAll', cs.CHANNEL_TYPE_TEXT) q.expect('dbus-return', method='GetAll') text_chan.Close() expect_close(q, text_path, stream, jid) assert_not_on_bus(q, tube_chan) assert_not_on_bus(q, text_chan)
def test(q, bus, conn, stream, is_google): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') result = make_result_iq(stream, iq_event.stanza) # Testing reveals that Google's vCard server does not actually support # NICKNAME (or indeed any fields beside FN, N and PHOTO): if you set a # vCard including it, it accepts the request but strips out the unsupported # fields. So if the server looks like Google, it's a redundant set # operation on FN that we want to avoid. if is_google: vcard = result.firstChildElement() vcard.addElement('FN', content='oh hello there') else: vcard = result.firstChildElement() vcard.addElement('NICKNAME', content='oh hello there') stream.send(result) q.forbid_events([ EventPattern('stream-iq', iq_type='set', query_ns='vcard-temp', query_name='vCard') ]) sync_stream(q, stream) sync_dbus(bus, q, conn)
def advertise_caps(q, bus, conn, stream, filters, expected_features, unexpected_features, expected_caps): # make sure nothing from a previous update is still running sync_dbus(bus, q, conn) self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") ret_caps = conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.Foo', filters, []) ]) # Expect Gabble to reply with the correct caps event, namespaces, _, signaled_caps = receive_presence_and_ask_caps( q, stream) assertSameElements(expected_caps, signaled_caps[self_handle]) assertContains(ns.TP_FT_METADATA, namespaces) for var in expected_features: assertContains(var, namespaces) for var in unexpected_features: assertDoesNotContain(var, namespaces) # Check our own caps caps = get_contacts_capabilities_sync(conn, [self_handle]) assertSameElements(expected_caps, caps[self_handle]) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(caps[self_handle], caps_via_contacts_iface)
def tube_remains_text_closes(q, bus, conn, stream): jid = '*****@*****.**' text_chan, text_path, _ = text_channel(q, bus, conn, stream, 'CreateChannel', jid) tube_chan, tube_path, _ = stream_tube(q, bus, conn, stream, 'CreateChannel', jid, presence=False) # now let's try and close the text channel # this should happen sucessfully but the tube channel # should stick around q.forbid_events([ EventPattern('dbus-signal', signal='ChannelClosed', args=[tube_path]) ]) text_chan.Close() expect_close(q, text_path) sync_dbus(bus, q, conn) assert_on_bus(q, tube_chan) assert_not_on_bus(q, text_chan) q.unforbid_all()
def test(q, bus, conn, stream): roster_event = q.expect('stream-iq', query_ns=ns.ROSTER) roster_event.stanza['type'] = 'result' call_async(q, conn, "RequestHandles", cs.HT_GROUP, ['test']) event = q.expect('dbus-return', method='RequestHandles') test_handle = event.value[0][0] call_async(q, conn, 'RequestChannel', cs.CHANNEL_TYPE_CONTACT_LIST, cs.HT_GROUP, test_handle, True) # A previous incarnation of this test --- written with the intention that # RequestChannel would be called before the roster was received, to expose # a bug in Gabble triggered by that ordering --- was racy: if the D-Bus # daemon happened to be particularly busy, the call to RequestChannel # reached Gabble after the roster stanza. (The race was discovered when # that reversed order triggered a newly-introduced instance of the # opposite bug to the one the test was targetting!) So we sync the XMPP # stream and D-Bus queue here. sync_stream(q, stream) sync_dbus(bus, q, conn) # send an empty roster stream.send(roster_event.stanza) event = q.expect('dbus-return', method='RequestChannel') path = event.value[0] while True: event = q.expect('dbus-signal', signal='NewChannel') assert event.args[0] == path, (event.args, path) _, type, handle_type, handle, suppress_handler = event.args if handle_type == cs.HT_GROUP and handle == test_handle: break
def test(q, bus, conn, stream): room = '*****@*****.**' chan, path, props, disco = join_muc(q, bus, conn, stream, room, also_capture=[ EventPattern( 'stream-iq', iq_type='get', query_name='query', query_ns=ns.DISCO_INFO, to=room) ]) sync_dbus(bus, q, conn) # we call Close... call_async(q, chan, 'Close') q.expect('dbus-return', method='Close') # ...so gabble announces our unavailable presence to the MUC. event = q.expect('stream-presence', to=room + '/test') elem = event.stanza assertEquals('unavailable', elem['type']) # while we wait for the conference server to echo our unavailable # presence, we try and create the same channel again... call_async( q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: room }) # ...which should fail because the channel hasn't closed yet. q.expect('dbus-error', method='CreateChannel', name=cs.NOT_AVAILABLE) # the conference server finally gets around to echoing our # unavailable presence... echo_muc_presence(q, stream, elem, 'none', 'participant') # ...and only now is the channel closed. q.expect_many(EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) # now that the channel has finally closed, let's try and request # it again which should succeed! chan, _, _ = join_muc(q, bus, conn, stream, room) # let's clear up though. chan.Close() event = q.expect('stream-presence', to=room + '/test') echo_muc_presence(q, stream, event.stanza, 'none', 'participant') q.expect_many(EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'))
def test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) handle = conn.RequestHandles(1, ['*****@*****.**'])[0] call_async(q, conn.ContactInfo, 'RefreshContactInfo', [handle]) event = q.expect('stream-iq', to='*****@*****.**', query_ns='vcard-temp', query_name='vCard') result = make_result_iq(stream, event.stanza) result.firstChildElement().addElement('FN', content='Bob') n = result.firstChildElement().addElement('N') n.addElement('GIVEN', content='Bob') result.firstChildElement().addElement('NICKNAME', content=r'bob,bob1\,,bob2,bob3\,bob4') label = result.firstChildElement().addElement('LABEL') label.addElement('LINE', content='42 West Wallaby Street') label.addElement('LINE', content="Bishop's Stortford\n") label.addElement('LINE', content='Huntingdon') org = result.firstChildElement().addElement('ORG') # ORG is a sequence of decreasingly large org.units, starting # with the organisation name itself (but here we've moved the org name # to the end, to make sure that works.) org.addElement('ORGUNIT', content='Dept. of Examples') org.addElement('ORGUNIT', content='Exemplary Team') org.addElement('ORGNAME', content='Collabora Ltd.') stream.send(result) q.expect('dbus-signal', signal='ContactInfoChanged', args=[handle, [(u'fn', [], [u'Bob']), (u'n', [], [u'', u'Bob', u'', u'', u'']), (u'nickname', [], [r'bob,bob1\,,bob2,bob3\,bob4']), # LABEL comes out as a single blob of text (u'label', [], ['42 West Wallaby Street\n' "Bishop's Stortford\n" 'Huntingdon\n']), # ORG is a sequence of decreasingly large org.units, starting # with the organisation (u'org', [], [u'Collabora Ltd.', u'Dept. of Examples', u'Exemplary Team']), ]]) # ContactInfoChanged should not be signalled again forbidden = [EventPattern('dbus-signal', signal='ContactInfoChanged')] q.forbid_events(forbidden) # Refresh the contact info again; gabble should contact the server again call_async(q, conn.ContactInfo, 'RefreshContactInfo', [handle]) event = q.expect('stream-iq', to='*****@*****.**', query_ns='vcard-temp', query_name='vCard') sync_dbus(bus, q, conn) q.unforbid_events(forbidden)
def receive_caps(q, bus, conn, service, contact, contact_handle, features, expected_caps, expect_disco=True, expect_ccc=True): ver = compute_caps_hash([], features, {}) txt_record = { "txtvers": "1", "status": "avail", "node": client, "ver": ver, "hash": "sha-1"} listener, port = setup_stream_listener(q, contact) AvahiAnnouncer(contact, "_presence._tcp", port, txt_record) if expect_disco: # Salut looks up our capabilities e = q.expect('incoming-connection', listener=listener) stream = e.connection event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO, connection=stream) query_node = xpath.queryForNodes('/iq/query', event.stanza)[0] assert query_node.attributes['node'] == \ client + '#' + ver # send good reply result = make_result_iq(event.stanza) query = result.firstChildElement() query['node'] = client + '#' + ver for f in features: feature = query.addElement('feature') feature['var'] = f stream.send(result) if expect_ccc: event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged') announced_ccs, = event.args assertSameElements(expected_caps, announced_ccs[contact_handle]) else: if expect_disco: # Make sure Salut's got the caps sync_stream(q, stream) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(expected_caps, caps_via_contacts_iface) # close the connection and expect a new one to be opened by Salut # the next time we need some discoing doing if expect_disco: stream.send('</stream:stream>') stream.transport.loseConnection() # pass some time so Salut knows the connection is lost and # won't try and send stuff down a closed connection on the # next test. sync_dbus(bus, q, conn)
def test(q, bus, conn, stream): path, _ = conn.Future.EnsureSidecar(CONSOLE_PLUGIN_IFACE) console = ProxyWrapper(bus.get_object(conn.bus_name, path), CONSOLE_PLUGIN_IFACE) assert not console.Properties.Get(CONSOLE_PLUGIN_IFACE, 'SpewStanzas') es = [ EventPattern('dbus-signal', signal='StanzaReceived'), EventPattern('dbus-signal', signal='StanzaSent'), ] q.forbid_events(es) call_async(q, console, 'SendIQ', 'get', STACY, '<coffee xmlns="urn:unimaginative"/>') e = q.expect('stream-iq', iq_type='get', query_ns='urn:unimaginative', query_name='coffee') acknowledge_iq(stream, e.stanza) e = q.expect('dbus-return', method='SendIQ') type_, body = e.value assertEquals('result', type_) # We just assume the body works. # Turn on signalling incoming and outgoing stanzas console.Properties.Set(CONSOLE_PLUGIN_IFACE, 'SpewStanzas', True) sync_dbus(bus, q, conn) q.unforbid_events(es) send_unrecognised_get(q, stream) e = q.expect('dbus-signal', signal='StanzaReceived') xml, = e.args assertContains('<iq', xml) assertContains('<dont-handle-me-bro', xml) signal = q.expect('dbus-signal', signal='StanzaSent') assertContains('service-unavailable', signal.args[0]) # Turn off spewing out stanzas; check it works. console.Properties.Set(CONSOLE_PLUGIN_IFACE, 'SpewStanzas', False) q.forbid_events(es) send_unrecognised_get(q, stream) sync_dbus(bus, q, conn) # Try sending just any old stanza console.SendStanza(''' <message to='%(stacy)s' type='headline'> <body> Hi sis. </body> </message>''' % { 'stacy': STACY }) e = q.expect('stream-message', to=STACY, message_type='headline') # Wocky fills in xmlns='' for us if we don't specify a namespace... great. # So this means <message/> gets sent as <message xmlns=''/> and the server # kicks us off. assertNotEquals('', e.stanza.uri)
def test(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0, 1]) messages = [] def new_message(timestamp, domain, level, string): messages.append((timestamp, domain, level, string)) debug = bus.get_object(conn.bus_name, cs.DEBUG_PATH) debug_iface = dbus.Interface(debug, cs.DEBUG_IFACE) debug_iface.connect_to_signal('NewDebugMessage', new_message) props_iface = dbus.Interface(debug, cs.PROPERTIES_IFACE) assert len(debug_iface.GetMessages()) > 0 # Turn signalling on and generate some messages. assert len(messages) == 0 assert props_iface.Get(cs.DEBUG_IFACE, 'Enabled') == False props_iface.Set(cs.DEBUG_IFACE, 'Enabled', True) self_handle = conn.Get(cs.CONN, 'SelfHandle', dbus_interface=cs.PROPERTIES_IFACE) channel_path, _ = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: self_handle }) q.expect('dbus-signal', signal='NewChannels') if DEBUGGING: assert len(messages) > 0 else: assertEquals([], messages) # Turn signalling off and check we don't get any more messages. props_iface.Set(cs.DEBUG_IFACE, 'Enabled', False) sync_dbus(bus, q, conn) snapshot = list(messages) channel = bus.get_object(conn.bus_name, channel_path) channel.Close(dbus_interface=cs.CHANNEL) q.expect('dbus-signal', signal='Closed') channel_path, _ = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: self_handle }) q.expect('dbus-signal', signal='NewChannels') assertEquals (snapshot, messages)
def text_can_automatically_close(q, bus, conn, stream): jid = '*****@*****.**' tube_chan, tube_path, _ = stream_tube(q, bus, conn, stream, 'CreateChannel', jid) sync_dbus(bus, q, conn) tube_chan.Close() expect_close(q, tube_path, stream, jid) assert_not_on_bus(q, tube_chan)
def run_test(q, bus, conn, stream, jt, request_before_presence): """ Requests streams on a media channel to jt.remote_jid, either before their presence is received (if request_before_presence is True) or after their presence is received but before we've got a disco response for their capabilities (otherwise). """ # We intentionally DON'T set remote presence yet. Since Gabble is still # unsure whether to treat contact as offline for this purpose, it # will tentatively allow channel creation and contact handle addition request = dbus.Dictionary( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jt.remote_jid, }, signature="sv", ) path, props = conn.CreateChannel(request, dbus_interface=cs.CONN_IFACE_REQUESTS) media_iface = make_channel_proxy(conn, path, "Channel.Type.StreamedMedia") handle = props[cs.TARGET_HANDLE] sync_dbus(bus, q, conn) def call_request_streams(): call_async(q, media_iface, "RequestStreams", handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) def send_presence(): jt.send_remote_presence() return q.expect("stream-iq", query_ns=ns.DISCO_INFO, to=jt.remote_jid) if request_before_presence: # Request streams before either <presence> or caps have arrived. Gabble # should wait for both to arrive before returning from RequestStreams. call_request_streams() # Ensure Gabble's received the method call. sync_dbus(bus, q, conn) # Now send the presence. info_event = send_presence() else: info_event = send_presence() # Now call RequestStreams; it should wait for the disco reply. call_request_streams() jt.send_remote_disco_reply(info_event.stanza) # RequestStreams should now happily complete q.expect("dbus-return", method="RequestStreams")
def test(q, bus, conn, stream): conn.Connect() expect_and_handle_get_vcard(q, stream) # Ensure that Gabble's actually got the initial vCard reply; if it hasn't # processed it by the time we call SetAliases, the latter will wait for it # to reply and then set immediately. sync_stream(q, stream) handle = conn.GetSelfHandle() call_async(q, conn.Aliasing, 'SetAliases', {handle: 'Some Guy'}) # SetAliases requests vCard v1 get_vcard_event = q.expect('stream-iq', query_ns=ns.VCARD_TEMP, query_name='vCard', iq_type='get') iq = get_vcard_event.stanza vcard = iq.firstChildElement() assert vcard.name == 'vCard', vcard.toXml() call_async(q, conn.Avatars, 'SetAvatar', 'hello', 'image/png') # We don't expect Gabble to send a second vCard request, since there's one # outstanding. But we want to ensure that SetAvatar reaches Gabble before # the empty vCard does. sync_dbus(bus, q, conn) # Send back current empty vCard result = make_result_iq(stream, iq) # result already includes the <vCard/> from the query, which is all we need stream.send(result) def has_nickname_and_photo(vcard): nicknames = xpath.queryForNodes('/vCard/NICKNAME', vcard) assert nicknames is not None assert len(nicknames) == 1 assert str(nicknames[0]) == 'Some Guy' photos = xpath.queryForNodes('/vCard/PHOTO', vcard) assert photos is not None and len(photos) == 1, repr(photos) types = xpath.queryForNodes('/PHOTO/TYPE', photos[0]) binvals = xpath.queryForNodes('/PHOTO/BINVAL', photos[0]) assert types is not None and len(types) == 1, repr(types) assert binvals is not None and len(binvals) == 1, repr(binvals) assert str(types[0]) == 'image/png' got = str(binvals[0]) exp = base64.b64encode('hello') assert got == exp, (got, exp) # Now Gabble should set a new vCard with both of the above changes. expect_and_handle_set_vcard(q, stream, has_nickname_and_photo)
def test(q, bus, conn, stream): def send_own_message(to, text): iq = elem_iq(stream, 'set', from_='chat.facebook.com')(elem(NS_FACEBOOK_MESSAGES, 'own-message', to=to, self='false')( elem('body')(text))) stream.send(iq) q.expect('stream-iq', iq_type='result', iq_id=iq['id']) # First, test receiving an own-message stanza for a message sent to a # contact we have an open channel for. jid = '*****@*****.**' _, path, props = conn.Requests.EnsureChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, }) channel = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['Messages']) handle = props[cs.TARGET_HANDLE] text = u'omg omg its ur birthdayy <3 <3 xoxoxoxo' send_own_message(to=jid, text=text) e = q.expect('dbus-signal', signal='MessageReceived') message, = e.args assertLength(1, message) header = message[0] assertEquals(handle, header['message-sender']) assertEquals(cs.MT_DELIVERY_REPORT, header['message-type']) assertEquals(cs.DELIVERY_STATUS_ACCEPTED, header['delivery-status']) assertContains('delivery-echo', header) echo = header['delivery-echo'] echo_header, echo_body = echo assertEquals(conn.Properties.Get(cs.CONN, "SelfHandle"), echo_header['message-sender']) assertEquals('text/plain', echo_body['content-type']) assertEquals(text, echo_body['content']) channel.Text.AcknowledgePendingMessages([header['pending-message-id']]) channel.Close() # Now test receiving an own-message stanza for a message sent to a contact # we don't have a channel open for. It should be ignored (but acked). This # is consistent with delivery failure reports. q.forbid_events([EventPattern('dbus-signal', signal='MessageReceived')]) send_own_message(to='*****@*****.**', text=u'please ignore this message') sync_dbus(bus, q, conn)
def test(q, bus, conn, stream): expect_and_handle_get_vcard(q, stream) # Ensure that Gabble's actually got the initial vCard reply; if it hasn't # processed it by the time we call SetAliases, the latter will wait for it # to reply and then set immediately. sync_stream(q, stream) handle = conn.Properties.Get(cs.CONN, "SelfHandle") call_async(q, conn.Aliasing, 'SetAliases', {handle: 'Some Guy'}) # SetAliases requests vCard v1 get_vcard_event = q.expect('stream-iq', query_ns=ns.VCARD_TEMP, query_name='vCard', iq_type='get') iq = get_vcard_event.stanza vcard = iq.firstChildElement() assert vcard.name == 'vCard', vcard.toXml() call_async(q, conn.Avatars, 'SetAvatar', 'hello', 'image/png') # We don't expect Gabble to send a second vCard request, since there's one # outstanding. But we want to ensure that SetAvatar reaches Gabble before # the empty vCard does. sync_dbus(bus, q, conn) # Send back current empty vCard result = make_result_iq(stream, iq) # result already includes the <vCard/> from the query, which is all we need stream.send(result) def has_nickname_and_photo(vcard): nicknames = xpath.queryForNodes('/vCard/NICKNAME', vcard) assert nicknames is not None assert len(nicknames) == 1 assert str(nicknames[0]) == 'Some Guy' photos = xpath.queryForNodes('/vCard/PHOTO', vcard) assert photos is not None and len(photos) == 1, repr(photos) types = xpath.queryForNodes('/PHOTO/TYPE', photos[0]) binvals = xpath.queryForNodes('/PHOTO/BINVAL', photos[0]) assert types is not None and len(types) == 1, repr(types) assert binvals is not None and len(binvals) == 1, repr(binvals) assert str(types[0]) == 'image/png' got = str(binvals[0]).strip() exp = base64.b64encode('hello') assertEquals(exp, got) # Now Gabble should set a new vCard with both of the above changes. expect_and_handle_set_vcard(q, stream, has_nickname_and_photo)
def announce_contact(self, name=CONTACT_NAME, metadata=True): client = 'http://telepathy.freedesktop.org/fake-client' features = [ns.IQ_OOB] if metadata: features += [ns.TP_FT_METADATA] ver = compute_caps_hash([], features, {}) txt_record = { "txtvers": "1", "status": "avail", "node": client, "ver": ver, "hash": "sha-1" } suffix = '@%s' % get_host_name() name += ('-' + os.path.splitext(os.path.basename(sys.argv[0]))[0]) self.contact_name = name + suffix if len(self.contact_name) > 63: allowed = 63 - len(suffix) self.contact_name = name[:allowed] + suffix self.listener, port = setup_stream_listener(self.q, self.contact_name) self.contact_service = AvahiAnnouncer(self.contact_name, "_presence._tcp", port, txt_record) self.handle = wait_for_contact_in_publish(self.q, self.bus, self.conn, self.contact_name) # expect salut to disco our caps e = self.q.expect('incoming-connection', listener=self.listener) stream = e.connection e = self.q.expect('stream-iq', to=self.contact_name, query_ns=ns.DISCO_INFO, connection=stream) assertEquals(client + '#' + ver, e.query['node']) send_disco_reply(stream, e.stanza, [], features) # lose the connection here to ensure connections are created # where necessary; I just wanted salut to know my caps. stream.send('</stream:stream>') # spend a bit of time in the main loop to ensure the last two # stanzas are actually received by salut before closing the # connection. sync_dbus(self.bus, self.q, self.conn) stream.transport.loseConnection()
def text_can_automatically_close(q, bus, conn): jid = 'test-muc' connect(q, bus, conn) tube_chan, tube_path, _ = stream_tube(q, bus, conn, 'CreateChannel', jid) sync_dbus(bus, q, conn) tube_chan.Close() expect_close(q, tube_path) assert_not_on_bus(q, tube_chan)
def test(q, bus, conn, stream): def send_own_message(to, text): iq = elem_iq(stream, 'set', from_='chat.facebook.com')( elem(NS_FACEBOOK_MESSAGES, 'own-message', to=to, self='false')( elem('body')(text) ) ) stream.send(iq) q.expect('stream-iq', iq_type='result', iq_id=iq['id']) # First, test receiving an own-message stanza for a message sent to a # contact we have an open channel for. jid = '*****@*****.**' _, path, props = conn.Requests.EnsureChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, }) channel = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['Messages']) handle = props[cs.TARGET_HANDLE] text = u'omg omg its ur birthdayy <3 <3 xoxoxoxo' send_own_message(to=jid, text=text) e = q.expect('dbus-signal', signal='MessageReceived') message, = e.args assertLength(1, message) header = message[0] assertEquals(handle, header['message-sender']) assertEquals(cs.MT_DELIVERY_REPORT, header['message-type']) assertEquals(cs.DELIVERY_STATUS_ACCEPTED, header['delivery-status']) assertContains('delivery-echo', header) echo = header['delivery-echo'] echo_header, echo_body = echo assertEquals(conn.GetSelfHandle(), echo_header['message-sender']) assertEquals('text/plain', echo_body['content-type']) assertEquals(text, echo_body['content']) channel.Text.AcknowledgePendingMessages([header['pending-message-id']]) channel.Close() # Now test receiving an own-message stanza for a message sent to a contact # we don't have a channel open for. It should be ignored (but acked). This # is consistent with delivery failure reports. q.forbid_events([EventPattern('dbus-signal', signal='MessageReceived')]) send_own_message(to='*****@*****.**', text=u'please ignore this message') sync_dbus(bus, q, conn)
def test(q, bus, conn, stream): messages = [] def new_message(timestamp, domain, level, string): messages.append((timestamp, domain, level, string)) debug = ProxyWrapper(bus.get_object(conn.bus_name, cs.DEBUG_PATH), cs.DEBUG_IFACE) debug.connect_to_signal('NewDebugMessage', new_message) assert len(debug.GetMessages()) > 0 # Turn signalling on and generate some messages. assert len(messages) == 0 assert debug.Properties.Get(cs.DEBUG_IFACE, 'Enabled') == False debug.Properties.Set(cs.DEBUG_IFACE, 'Enabled', True) channel_path, props = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: conn.Properties.Get(cs.CONN, "SelfHandle") }) q.expect('dbus-signal', signal='NewDebugMessage') assert len(messages) > 0 # Turn signalling off and check we don't get any more messages. debug.Properties.Set(cs.DEBUG_IFACE, 'Enabled', False) sync_dbus(bus, q, conn) snapshot = list(messages) channel = bus.get_object(conn.bus_name, channel_path) channel.Close(dbus_interface=cs.CHANNEL) q.expect('dbus-signal', signal='Closed') channel_path, props = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: conn.Properties.Get(cs.CONN, "SelfHandle") }) q.expect('dbus-signal', signal='NewChannels') assertEquals(snapshot, messages)
def _test_remote_status_away(q, bus, conn, stream, msg, show, list_attrs): events = [EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (presence_types[show], show, msg)}])] q.forbid_events(events) list_attrs['status'] = list_attrs.get('status', msg) stream.set_shared_status_lists(**list_attrs) q.expect('stream-iq', iq_type='result') sync_dbus(bus, q, conn) q.unforbid_events(events)
def test(q, bus, conn, stream): conn.Connect() q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]), EventPattern('irc-connected')) e = q.expect('dbus-signal', signal='NewChannels') conn.Disconnect() q.expect_many( EventPattern('dbus-signal', signal='StatusChanged'), EventPattern('irc-disconnected'), ) # Idle would now crash in an idle callback; so let's see if it's alive. sync_dbus(bus, q, conn)
def advertise_caps(q, bus, conn, service, filters, expected_features, unexpected_features, expected_caps): # make sure nothing from a previous update is still running sync_dbus(bus, q, conn) self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") self_handle_name = conn.Properties.Get(cs.CONN, "SelfID") ret_caps = conn.ContactCapabilities.UpdateCapabilities( [(cs.CLIENT + '.Foo', filters, [])]) presence, event_dbus = q.expect_many( EventPattern('service-resolved', service=service), EventPattern('dbus-signal', signal='ContactCapabilitiesChanged') ) assertLength(1, event_dbus.args) signaled_caps = event_dbus.args[0] outbound = connect_to_stream(q, 'test@foobar', self_handle_name, str(presence.pt), presence.port) e = q.expect('connection-result') assert e.succeeded, e.reason e = q.expect('stream-opened', connection=outbound) # Expect Salut to reply with the correct caps event, namespaces = disco_caps(q, outbound, presence.txt) assertSameElements(expected_caps, signaled_caps[self_handle]) assertContains(ns.TP_FT_METADATA, namespaces) for var in expected_features: assertContains(var, namespaces) for var in unexpected_features: assertDoesNotContain(var, namespaces) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(expected_caps, caps_via_contacts_iface) # close things... outbound.send('</stream:stream>') sync_dbus(bus, q, conn) outbound.transport.loseConnection()
def test(q, bus, conn, stream, channel_type): jt = jingletest.JingleTest(stream, 'test@localhost', '[email protected]/Foo') # We intentionally DON'T set remote presence yet. Since Gabble is still # unsure whether to treat contact as offline for this purpose, it # will tentatively allow channel creation and contact handle addition handle = conn.RequestHandles(cs.HT_CONTACT, [jt.remote_jid])[0] if channel_type == cs.CHANNEL_TYPE_STREAMED_MEDIA: path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') # So it turns out that the calls to RequestStreams and Disconnect could be # reordered while the first waits on the result of introspecting the # channel's object which is kicked off by making a proxy object for it, # whereas the connection proxy is long ago introspected. Isn't dbus-python # great? Syncing here forces that introspection to finish so we can rely on # the ordering of RequestStreams and Disconnect. Yay. sync_dbus(bus, q, conn) # Now we request streams before either <presence> or caps have arrived if channel_type == cs.CHANNEL_TYPE_STREAMED_MEDIA: call_async(q, media_iface, 'RequestStreams', handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) before_events, after_events = disconnect_conn(q, conn, stream, [EventPattern('dbus-error', method='RequestStreams')]) # RequestStreams should now return NotAvailable assert before_events[0].error.get_dbus_name() == cs.NOT_AVAILABLE, \ before_events[0].error else: call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: channel_type, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jt.remote_jid, cs.CALL_INITIAL_AUDIO: True }) before_events, after_events = disconnect_conn(q, conn, stream, [EventPattern('dbus-error', method='CreateChannel')]) # CreateChannel should now return Disconnected assert before_events[0].error.get_dbus_name() == cs.DISCONNECTED, \ before_events[0].error
def test(q, bus, mc): service = SimulatedSession(q, bus, STATUS_IDLE) account1, conn1 = _create_and_enable( q, bus, mc, "*****@*****.**", True, [ EventPattern( 'dbus-method-call', method='SetPowerSaving', args=[True]) ]) account2, conn2 = _create_and_enable(q, bus, mc, "*****@*****.**", False) # Second account does not support PowerSaving interface, don't call SetPowerSaving forbid_no_iface = [ EventPattern('dbus-method-call', method='SetPowerSaving', path=conn2.object_path) ] q.forbid_events(forbid_no_iface) for status in [STATUS_AVAILABLE, STATUS_IDLE, STATUS_BUSY]: service.StatusChanged(status) q.expect('dbus-method-call', method='SetPowerSaving', args=[status == STATUS_IDLE], interface=cs.CONN_IFACE_POWER_SAVING, path=conn1.object_path) _disable_account(q, bus, mc, account1, conn1) _disable_account(q, bus, mc, account2, conn2) q.unforbid_events(forbid_no_iface) # Make sure we don't call SetPowerSaving on a disconnected connection. forbid_when_disconnected = [ EventPattern('dbus-method-call', method='SetPowerSaving') ] q.forbid_events(forbid_when_disconnected) service.StatusChanged(STATUS_IDLE) sync_dbus(bus, q, account1) q.unforbid_events(forbid_when_disconnected) service.release_name()
def test(q, bus, conn, stream): room = '*****@*****.**' chan, path, props, disco = join_muc(q, bus, conn, stream, room, also_capture=[EventPattern('stream-iq', iq_type='get', query_name='query', query_ns=ns.DISCO_INFO, to=room)]) sync_dbus(bus, q, conn) # we call Close... call_async(q, chan, 'Close') q.expect('dbus-return', method='Close') # ...so gabble announces our unavailable presence to the MUC. event = q.expect('stream-presence', to=room + '/test') elem = event.stanza assertEquals('unavailable', elem['type']) # while we wait for the conference server to echo our unavailable # presence, we try and create the same channel again... call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: room }) # ...which should fail because the channel hasn't closed yet. q.expect('dbus-error', method='CreateChannel', name=cs.NOT_AVAILABLE) # the conference server finally gets around to echoing our # unavailable presence... echo_muc_presence(q, stream, elem, 'none', 'participant') # ...and only now is the channel closed. q.expect_many(EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) # now that the channel has finally closed, let's try and request # it again which should succeed! chan, _, _ = join_muc(q, bus, conn, stream, room) # let's clear up though. chan.Close() event = q.expect('stream-presence', to=room + '/test') echo_muc_presence(q, stream, event.stanza, 'none', 'participant') q.expect_many(EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'))
def advertise_caps(q, bus, conn, service, filters, expected_features, unexpected_features, expected_caps): # make sure nothing from a previous update is still running sync_dbus(bus, q, conn) self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") self_handle_name = conn.Properties.Get(cs.CONN, "SelfID") ret_caps = conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.Foo', filters, []) ]) presence, event_dbus = q.expect_many( EventPattern('service-resolved', service=service), EventPattern('dbus-signal', signal='ContactCapabilitiesChanged')) assertLength(1, event_dbus.args) signaled_caps = event_dbus.args[0] outbound = connect_to_stream(q, 'test@foobar', self_handle_name, str(presence.pt), presence.port) e = q.expect('connection-result') assert e.succeeded, e.reason e = q.expect('stream-opened', connection=outbound) # Expect Salut to reply with the correct caps event, namespaces = disco_caps(q, outbound, presence.txt) assertSameElements(expected_caps, signaled_caps[self_handle]) assertContains(ns.TP_FT_METADATA, namespaces) for var in expected_features: assertContains(var, namespaces) for var in unexpected_features: assertDoesNotContain(var, namespaces) # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(expected_caps, caps_via_contacts_iface) # close things... outbound.send('</stream:stream>') sync_dbus(bus, q, conn) outbound.transport.loseConnection()
def running_check(self): self.context.options_ping(self.q) sync_dbus(self.bus, self.q, self.conn) for c in self.contents: props = c.stream.Properties.GetAll(cs.CALL_STREAM) assertEquals(cs.CALL_SENDING_STATE_SENDING, props['LocalSendingState']) assertEquals({self.remote_handle: cs.CALL_SENDING_STATE_SENDING}, props['RemoteMembers']) props = c.stream.Properties.GetAll(cs.CALL_STREAM_IFACE_MEDIA) assertEquals(cs.CALL_STREAM_FLOW_STATE_STARTED, props['SendingState']) assertEquals(cs.CALL_STREAM_FLOW_STATE_STARTED, props['ReceivingState'])
def test_then_disconnect(q, bus, conn, stream): room = '*****@*****.**' chan, path, props, disco = join_muc(q, bus, conn, stream, room, also_capture=[ EventPattern( 'stream-iq', iq_type='get', query_name='query', query_ns=ns.DISCO_INFO, to=room) ]) sync_dbus(bus, q, conn) # we call Close... call_async(q, chan, 'Close') q.expect('dbus-return', method='Close') # ...so gabble announces our unavailable presence to the MUC. event = q.expect('stream-presence', to=room + '/test') elem = event.stanza assertEquals('unavailable', elem['type']) # oh no, but now we want to disconnect. call_async(q, conn, 'Disconnect') # the muc factory is told to close everything, so it does so # without announcing it to the channel because it does it # forcibly, so the channels disappear. q.expect_many(EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) # now echo the unavailable presence; this shouldn't be handled # because the channel has already closed. echo_muc_presence(q, stream, elem, 'none', 'participant') # send the stream footer so that the connection thinks it's # property disconnected now. stream.sendFooter() # finally, Disconnect returns q.expect('dbus-return', method='Disconnect')
def test(q, bus, conn, stream): conn.Connect() _, iq_event = q.expect_many( EventPattern("dbus-signal", signal="StatusChanged", args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]), EventPattern("stream-iq", to=None, query_ns="vcard-temp", query_name="vCard"), ) acknowledge_iq(stream, iq_event.stanza) call_async(q, conn.Avatars, "SetAvatar", "Guy.brush", "image/x-mighty-pirate") iq_event = q.expect("stream-iq", iq_type="set", query_ns="vcard-temp", query_name="vCard") call_async(q, conn.Avatars, "SetAvatar", "LeChuck.brush", "image/x-ghost-pirate") conn.Disconnect() q.expect("dbus-signal", signal="StatusChanged", args=[cs.CONN_STATUS_DISCONNECTED, cs.CSR_REQUESTED]), q.expect("dbus-error", method="SetAvatar", name=cs.NOT_AVAILABLE) q.expect("dbus-error", method="SetAvatar", name=cs.NOT_AVAILABLE) sync_dbus(bus, q, conn)
def test(q, bus, conn, stream): expect_and_handle_get_vcard(q, stream) sync_stream(q, stream) call_async( q, conn.Avatars, 'SetAvatar', b'Guy.brush', 'image/x-mighty-pirate') expect_and_handle_get_vcard(q, stream) iq_event = q.expect( 'stream-iq', iq_type='set', query_ns='vcard-temp', query_name='vCard') call_async( q, conn.Avatars, 'SetAvatar', b'LeChuck.brush', 'image/x-ghost-pirate') disconnect_conn(q, conn, stream) q.expect('dbus-error', method='SetAvatar', name=cs.NOT_AVAILABLE) q.expect('dbus-error', method='SetAvatar', name=cs.NOT_AVAILABLE) sync_dbus(bus, q, conn)
def test(q, bus, conn, stream): expect_and_handle_get_vcard(q, stream) sync_stream(q, stream) call_async( q, conn.Avatars, 'SetAvatar', 'Guy.brush', 'image/x-mighty-pirate') expect_and_handle_get_vcard(q, stream) iq_event = q.expect( 'stream-iq', iq_type='set', query_ns='vcard-temp', query_name='vCard') call_async( q, conn.Avatars, 'SetAvatar', 'LeChuck.brush', 'image/x-ghost-pirate') disconnect_conn(q, conn, stream) q.expect('dbus-error', method='SetAvatar', name=cs.NOT_AVAILABLE) q.expect('dbus-error', method='SetAvatar', name=cs.NOT_AVAILABLE) sync_dbus(bus, q, conn)
def test(q, bus, conn, stream): # Gabble asks for the roster; the server sends back an empty roster. event = q.expect('stream-iq', query_ns=ns.ROSTER) acknowledge_iq(stream, event.stanza) pairs = expect_contact_list_signals(q, bus, conn, ['stored']) stored = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, 'stored', []) # The server sends us a roster push without an id=''. WTF! iq = make_roster_push(stream, jid, 'both') del iq['id'] stream.send(iq) h = conn.RequestHandles(cs.HT_CONTACT, [jid])[0] q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=['', [h], [], [], [], 0, 0], path=stored.object_path), EventPattern('dbus-signal', signal='ContactsChanged', args=[{ h: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_YES, ''), }, []], ), ) # Verify that Gabble didn't crash while trying to ack the push. sync_stream(q, stream) # Just for completeness, let's repeat this test with a malicious roster # push from a contact (rather than from our server). Our server's *really* # broken if it allows this. Nonetheless... iq = make_roster_push(stream, '*****@*****.**', 'both') del iq['id'] iq['from'] = '*****@*****.**' stream.send(iq) q.forbid_events( [ EventPattern('dbus-signal', signal='MembersChanged', path=stored.object_path), EventPattern('dbus-signal', signal='ContactsChanged'), ]) # Make sure Gabble's got the evil push... sync_stream(q, stream) # ...and make sure it's not emitted anything. sync_dbus(bus, q, conn)
def test(q, bus, conn, stream): messages = [] def new_message(timestamp, domain, level, string): messages.append((timestamp, domain, level, string)) debug = ProxyWrapper(bus.get_object(conn.bus_name, cs.DEBUG_PATH), cs.DEBUG_IFACE) debug.connect_to_signal('NewDebugMessage', new_message) assert len(debug.GetMessages()) > 0 # Turn signalling on and generate some messages. assert len(messages) == 0 assert debug.Properties.Get(cs.DEBUG_IFACE, 'Enabled') == False debug.Properties.Set(cs.DEBUG_IFACE, 'Enabled', True) channel_path, props = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: conn.Properties.Get(cs.CONN, "SelfHandle") }) q.expect('dbus-signal', signal = 'NewDebugMessage') assert len(messages) > 0 # Turn signalling off and check we don't get any more messages. debug.Properties.Set(cs.DEBUG_IFACE, 'Enabled', False) sync_dbus(bus, q, conn) snapshot = list(messages) channel = bus.get_object(conn.bus_name, channel_path) channel.Close(dbus_interface=cs.CHANNEL) q.expect('dbus-signal', signal='Closed') channel_path, props = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: conn.Properties.Get(cs.CONN, "SelfHandle") }) q.expect('dbus-signal', signal='NewChannels') assertEquals (snapshot, messages)
def test(q, bus, conn, stream): messages = [] def new_message(timestamp, domain, level, string): messages.append((timestamp, domain, level, string)) debug = ProxyWrapper(bus.get_object(conn.bus_name, path), iface) debug.connect_to_signal('NewDebugMessage', new_message) if not DEBUGGING: # If we're built with --disable-debug, check that the Debug object # isn't present. call_async(q, debug, 'GetMessages') q.expect('dbus-error', method='GetMessages') return assert len(debug.GetMessages()) > 0 # Turn signalling on and generate some messages. assert len(messages) == 0 assert debug.Properties.Get(iface, 'Enabled') == False debug.Properties.Set(iface, 'Enabled', True) channel_path = conn.RequestChannel( cs.CHANNEL_TYPE_TEXT, cs.HT_CONTACT, conn.GetSelfHandle(), True) q.expect('dbus-signal', signal='NewChannel') assert len(messages) > 0 # Turn signalling off and check we don't get any more messages. debug.Properties.Set(iface, 'Enabled', False) sync_dbus(bus, q, conn) snapshot = list(messages) channel = bus.get_object(conn.bus_name, channel_path) channel.Close(dbus_interface=cs.CHANNEL) q.expect('dbus-signal', signal='Closed') conn.RequestChannel( cs.CHANNEL_TYPE_TEXT, cs.HT_CONTACT, conn.GetSelfHandle(), True) q.expect('dbus-signal', signal='NewChannel') assertEquals (snapshot, messages)
def test(q, bus, conn, stream): # Gabble asks for the roster; the server sends back an empty roster. event = q.expect('stream-iq', query_ns=ns.ROSTER) acknowledge_iq(stream, event.stanza) q.expect('dbus-signal', signal='ContactListStateChanged', args=[cs.CONTACT_LIST_STATE_SUCCESS]) # The server sends us a roster push without an id=''. WTF! iq = make_roster_push(stream, jid, 'both') del iq['id'] stream.send(iq) h = conn.get_contact_handle_sync(jid) q.expect_many( EventPattern( 'dbus-signal', signal='ContactsChangedWithID', args=[{ h: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_YES, ''), }, { h: jid }, {}], ), ) # Verify that Gabble didn't crash while trying to ack the push. sync_stream(q, stream) # Just for completeness, let's repeat this test with a malicious roster # push from a contact (rather than from our server). Our server's *really* # broken if it allows this. Nonetheless... iq = make_roster_push(stream, '*****@*****.**', 'both') del iq['id'] iq['from'] = '*****@*****.**' stream.send(iq) q.forbid_events([ EventPattern('dbus-signal', signal='ContactsChangedWithID'), ]) # Make sure Gabble's got the evil push... sync_stream(q, stream) # ...and make sure it's not emitted anything. sync_dbus(bus, q, conn)
def announce_contact(self, name=CONTACT_NAME, metadata=True): client = 'http://telepathy.freedesktop.org/fake-client' features = [ns.IQ_OOB] if metadata: features += [ns.TP_FT_METADATA] ver = compute_caps_hash([], features, {}) txt_record = { "txtvers": "1", "status": "avail", "node": client, "ver": ver, "hash": "sha-1"} suffix = '@%s' % get_host_name() name += ('-' + os.path.splitext(os.path.basename(sys.argv[0]))[0]) self.contact_name = name + suffix if len(self.contact_name) > 63: allowed = 63 - len(suffix) self.contact_name = name[:allowed] + suffix self.listener, port = setup_stream_listener(self.q, self.contact_name) self.contact_service = AvahiAnnouncer(self.contact_name, "_presence._tcp", port, txt_record) self.handle = wait_for_contact_in_publish(self.q, self.bus, self.conn, self.contact_name) # expect salut to disco our caps e = self.q.expect('incoming-connection', listener=self.listener) stream = e.connection e = self.q.expect('stream-iq', to=self.contact_name, query_ns=ns.DISCO_INFO, connection=stream) assertEquals(client + '#' + ver, e.query['node']) send_disco_reply(stream, e.stanza, [], features) # lose the connection here to ensure connections are created # where necessary; I just wanted salut to know my caps. stream.send('</stream:stream>') # spend a bit of time in the main loop to ensure the last two # stanzas are actually received by salut before closing the # connection. sync_dbus(self.bus, self.q, self.conn) stream.transport.loseConnection()
def _test_remote_status_away(q, bus, conn, stream, msg, show, list_attrs): events = [ EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{ 1: (presence_types[show], show, msg) }]) ] q.forbid_events(events) list_attrs['status'] = list_attrs.get('status', msg) stream.set_shared_status_lists(**list_attrs) q.expect('stream-iq', iq_type='result') sync_dbus(bus, q, conn) q.unforbid_events(events)
def test(q, bus, conn, stream): jt = jingletest.JingleTest(stream, 'test@localhost', '[email protected]/Foo') conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) q.expect('stream-authenticated') q.expect('dbus-signal', signal='PresenceUpdate', args=[{1L: (0L, {u'available': {}})}]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) # We intentionally DON'T set remote presence yet. Since Gabble is still # unsure whether to treat contact as offline for this purpose, it # will tentatively allow channel creation and contact handle addition handle = conn.RequestHandles(cs.HT_CONTACT, [jt.remote_jid])[0] path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') # So it turns out that the calls to RequestStreams and Disconnect could be # reordered while the first waits on the result of introspecting the # channel's object which is kicked off by making a proxy object for it, # whereas the connection proxy is long ago introspected. Isn't dbus-python # great? Syncing here forces that introspection to finish so we can rely on # the ordering of RequestStreams and Disconnect. Yay. sync_dbus(bus, q, conn) # Now we request streams before either <presence> or caps have arrived call_async(q, media_iface, 'RequestStreams', handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) event = disconnect_conn(q, conn, stream, [EventPattern('dbus-error', method='RequestStreams')])[0] # RequestStreams should now return NotAvailable assert event.error.get_dbus_name() == cs.NOT_AVAILABLE, event.error
def tube_remains_text_closes(q, bus, conn, stream): jid = '*****@*****.**' text_chan, text_path, _ = text_channel(q, bus, conn, stream, 'CreateChannel', jid) tube_chan, tube_path, _ = stream_tube(q, bus, conn, stream, 'CreateChannel', jid, presence=False) # now let's try and close the text channel # this should happen sucessfully but the tube channel # should stick around q.forbid_events([EventPattern('dbus-signal', signal='ChannelClosed', args=[tube_path])]) text_chan.Close() expect_close(q, text_path) sync_dbus(bus, q, conn) assert_on_bus(q, tube_chan) assert_not_on_bus(q, text_chan) q.unforbid_all()
def test(q, bus, conn, stream): event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, event.stanza) sync_stream(q, stream) sync_dbus(bus, q, conn) # A presence from a contact stream.send( make_presence('contact1@localhost/client', 'SHA1SUM-FOR-CONTACT1')) event = q.expect('dbus-signal', signal='AvatarUpdated') assert event.args[0] == 2, event.args assert event.args[1] == "SHA1SUM-FOR-CONTACT1", event.args AvatarRetrieved_event = EventPattern('dbus-signal', signal='AvatarRetrieved') AvatarUpdated_event = EventPattern('dbus-signal', signal='AvatarUpdated') StreamPresence_event = EventPattern('stream-presence') StreamIqVcard_event = EventPattern('stream-iq', query_ns='vcard-temp') # A presence from myself on another resource stream.send( make_presence('test@localhost/resource1', 'SHA1SUM-FOR-MYSELF-RES1')) q.forbid_events([AvatarRetrieved_event, AvatarUpdated_event]) stream_presence, stream_iq = q.expect_many( EventPattern('stream-presence'), EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard')) sync_dbus(bus, q, conn) q.unforbid_events([AvatarRetrieved_event, AvatarUpdated_event]) # If the server wrongly send a presence stanza with our resource, # AvatarUpdated must not be emitted q.forbid_events([ StreamPresence_event, StreamIqVcard_event, AvatarRetrieved_event, AvatarUpdated_event ]) stream.send(make_presence('test@localhost/Resource', 'SHA1SUM-FOR-MYSELF')) sync_dbus(bus, q, conn) sync_stream(q, stream) q.unforbid_events([ StreamPresence_event, StreamIqVcard_event, AvatarRetrieved_event, AvatarUpdated_event ])
def test(q, bus, conn, stream, 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, e: assertEquals(e.get_dbus_name(), cs.INVALID_ARGUMENT)
def test(q, bus, mc): params = dbus.Dictionary( { "account": "*****@*****.**", "password": "******" }, signature='sv') (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) # Ensure that it's enabled but has offline RP and doesn't connect # automatically call_async(q, account_props, 'Set', cs.ACCOUNT, 'RequestedPresence', (dbus.UInt32(cs.PRESENCE_OFFLINE), 'offline', '')) q.expect('dbus-return', method='Set') call_async( q, account_props, 'Set', cs.ACCOUNT, 'AutomaticPresence', (dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Testing automatic presence')) q.expect('dbus-return', method='Set') q.expect( 'dbus-signal', signal='AccountPropertyChanged', predicate=lambda e: e.args[0].get('AutomaticPresence', (None, None, None))[1] == 'busy') call_async(q, account_props, 'Set', cs.ACCOUNT, 'ConnectAutomatically', False) q.expect('dbus-return', method='Set') call_async(q, account_props, 'Set', cs.ACCOUNT, 'Enabled', True) q.expect('dbus-return', method='Set') q.expect('dbus-signal', signal='AccountPropertyChanged', predicate=lambda e: e.args[0].get('Enabled')) # Requesting a channel will put us online user_action_time = dbus.Int64(1238582606) cd = bus.get_object(cs.CD, cs.CD_PATH) request = dbus.Dictionary( { cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT, cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT, cs.CHANNEL + '.TargetID': 'juliet', }, signature='sv') call_async(q, cd, 'CreateChannel', account.object_path, request, user_action_time, "", dbus_interface=cs.CD) ret = q.expect('dbus-return', method='CreateChannel') request_path = ret.value[0] cr = bus.get_object(cs.AM, request_path) request_props = cr.GetAll(cs.CR, dbus_interface=cs.PROPERTIES_IFACE) assert request_props['Account'] == account.object_path assert request_props['Requests'] == [request] assert request_props['UserActionTime'] == user_action_time assert request_props['PreferredHandler'] == "" assert request_props['Interfaces'] == [] # make sure RequestConnection doesn't get called until we Proceed events = [EventPattern('dbus-method-call', method='RequestConnection')] q.forbid_events(events) sync_dbus(bus, q, mc) q.unforbid_events(events) cr.Proceed(dbus_interface=cs.CR) e = q.expect('dbus-method-call', method='RequestConnection', args=['fakeprotocol', params], destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', path=cs.tp_path_prefix + '/ConnectionManager/fakecm', interface=cs.tp_name_prefix + '.ConnectionManager', handled=False) conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', '_', 'myself', has_presence=True) q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) conn.presence = dbus.Struct((cs.PRESENCE_AVAILABLE, 'available', ''), signature='uss') _, cm_request_call = q.expect_many( EventPattern('dbus-method-call', path=conn.object_path, interface=cs.CONN_IFACE_SIMPLE_PRESENCE, method='SetPresence', args=['busy', 'Testing automatic presence'], handled=True), EventPattern('dbus-method-call', path=conn.object_path, interface=cs.CONN_IFACE_REQUESTS, method='CreateChannel', args=[request], handled=False), ) q.dbus_emit(conn.object_path, cs.CONN_IFACE_SIMPLE_PRESENCE, 'PresencesChanged', { conn.self_handle: (dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Testing automatic presence') }, signature='a{u(uss)}') # Time passes. A channel is returned. channel_immutable = dbus.Dictionary(request) channel_immutable[cs.CHANNEL + '.InitiatorID'] = conn.self_ident channel_immutable[cs.CHANNEL + '.InitiatorHandle'] = conn.self_handle channel_immutable[cs.CHANNEL + '.Requested'] = True channel_immutable[cs.CHANNEL + '.Interfaces'] = \ dbus.Array([], signature='s') channel_immutable[cs.CHANNEL + '.TargetHandle'] = \ conn.ensure_handle(cs.HT_CONTACT, 'juliet') channel = SimulatedChannel(conn, channel_immutable) # this order of events is guaranteed by telepathy-spec (since 0.17.14) q.dbus_return(cm_request_call.message, channel.object_path, channel.immutable, signature='oa{sv}') channel.announce() # there's no handler, so it gets shot down q.expect('dbus-method-call', path=channel.object_path, method='Close', handled=True)
def test(q, bus, mc): params = dbus.Dictionary({ "account": "brucewayne", "password": "******" }, signature='sv') (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) call_async(q, account_props, 'Set', cs.ACCOUNT, 'Nickname', "BruceWayne") q.expect_many( EventPattern('dbus-signal', path=account.object_path, signal='AccountPropertyChanged', interface=cs.ACCOUNT, args=[{ 'Nickname': "BruceWayne" }]), EventPattern('dbus-return', method='Set'), ) assertEquals("BruceWayne", account_props.Get(cs.ACCOUNT, 'Nickname')) expect_after_connect = [ EventPattern('dbus-method-call', interface=cs.CONN_IFACE_CONTACTS, predicate=(lambda e: e.method in ('GetContactAttributes', 'GetContactByID' ) and cs.CONN_IFACE_ALIASING in e.args[1]), handled=True), EventPattern('dbus-method-call', interface=cs.CONN_IFACE_ALIASING, method='SetAliases', handled=False), EventPattern('dbus-signal', interface=cs.ACCOUNT, predicate=lambda e: e.args[0].get('CurrentPresence') == (cs.PRESENCE_UNSET, '', '')), ] conn, get_aliases, set_aliases, _ = enable_fakecm_account( q, bus, mc, account, params, has_aliasing=True, expect_after_connect=expect_after_connect, self_ident=params['account']) assert get_aliases.args[0] == [conn.self_handle] assert set_aliases.args[0] == {conn.self_handle: 'BruceWayne'} q.dbus_return(set_aliases.message, signature='') # FIXME: fd.o #55666 in telepathy-glib breaks the rest of this test. # Reinstate it when we depend on a version that has that fixed. return # Another client changes our alias remotely, but because this is IRC, # that manifests itself as a handle change conn.change_self_ident('thebatman') conn.change_self_alias('TheBatman') get_aliases, _ = q.expect_many( EventPattern('dbus-method-call', interface=cs.CONN_IFACE_CONTACTS, predicate=(lambda e: e.method in ('GetContactAttributes', 'GetContactByID' ) and cs.CONN_IFACE_ALIASING in e.args[1]), handled=True), EventPattern( 'dbus-signal', path=account.object_path, signal='AccountPropertyChanged', interface=cs.ACCOUNT, predicate=( lambda e: e.args[0].get('NormalizedName') == 'thebatman')), ) assert get_aliases.args[0] in ([conn.self_handle], conn.self_id) q.expect('dbus-signal', path=account.object_path, signal='AccountPropertyChanged', interface=cs.ACCOUNT, args=[{ 'Nickname': 'TheBatman' }]) # We change our nickname back call_async(q, account_props, 'Set', cs.ACCOUNT, 'Nickname', 'BruceWayne') _, _, e = q.expect_many( EventPattern( 'dbus-signal', path=account.object_path, signal='AccountPropertyChanged', interface=cs.ACCOUNT, predicate=(lambda e: e.args[0].get('Nickname') == 'BruceWayne')), EventPattern('dbus-return', method='Set'), EventPattern('dbus-method-call', interface=cs.CONN_IFACE_ALIASING, method='SetAliases', args=[{ conn.self_handle: 'BruceWayne', }], handled=False)) assertEquals('BruceWayne', account_props.Get(cs.ACCOUNT, 'Nickname')) conn.change_self_ident('brucewayne') conn.change_self_alias('BruceWayne') q.dbus_return(e.message, signature='') # In response to the self-handle change, we check our nickname again get_aliases, _ = q.expect_many( EventPattern('dbus-method-call', interface=cs.CONN_IFACE_CONTACTS, predicate=(lambda e: e.method in ('GetContactAttributes', 'GetContactByID' ) and cs.CONN_IFACE_ALIASING in e.args[1]), handled=True), EventPattern( 'dbus-signal', path=account.object_path, signal='AccountPropertyChanged', interface=cs.ACCOUNT, predicate=( lambda e: e.args[0].get('NormalizedName') == 'brucewayne')), ) assert get_aliases.args[0] in ([conn.self_handle], conn.self_id) forbidden = [ EventPattern('dbus-signal', signal='AccountPropertyChanged', predicate=lambda e: 'Nickname' in e.args[0]) ] q.forbid_events(forbidden) sync_dbus(bus, q, mc)
def test(q, bus, mc, nickname): params = dbus.Dictionary( { "account": "*****@*****.**", "password": "******" }, signature='sv') (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) call_async(q, account_props, 'Set', cs.ACCOUNT, 'Nickname', nickname) if nickname == '': q.expect('dbus-return', method='Set') else: q.expect_many( EventPattern('dbus-signal', path=account.object_path, signal='AccountPropertyChanged', interface=cs.ACCOUNT, args=[{ 'Nickname': nickname }]), EventPattern('dbus-return', method='Set'), ) assertEquals(nickname, account_props.Get(cs.ACCOUNT, 'Nickname')) # OK, let's go online expect_after_connect = [ EventPattern('dbus-method-call', interface=cs.CONN_IFACE_CONTACTS, predicate=(lambda e: e.method in ('GetContactAttributes', 'GetContactByID' ) and cs.CONN_IFACE_ALIASING in e.args[1]), handled=True), ] forbidden = [] if nickname == params['account'] or nickname == '': forbidden.append(EventPattern('dbus-method-call', method='SetAliases')) q.forbid_events(forbidden) if nickname == '': expect_after_connect.append( EventPattern('dbus-signal', path=account.object_path, signal='AccountPropertyChanged', interface=cs.ACCOUNT, predicate=(lambda e: e.args[0].get('Nickname') == params['account']))) else: expect_after_connect.append( EventPattern('dbus-method-call', interface=cs.CONN_IFACE_ALIASING, method='SetAliases', handled=False)) results = enable_fakecm_account(q, bus, mc, account, params, has_aliasing=True, expect_after_connect=expect_after_connect, self_ident=params['account']) conn = results[0] get_aliases = results[1] assert get_aliases.args[0] == [conn.self_handle] if nickname == params['account']: assertLength(2, results) elif nickname == '': assertLength(3, results) else: assertLength(3, results) set_aliases = results[2] assert set_aliases.args[0] == {conn.self_handle: nickname} q.dbus_return(set_aliases.message, signature='') if forbidden: sync_dbus(bus, q, mc) q.unforbid_events(forbidden) # Change alias after going online call_async(q, account_props, 'Set', cs.ACCOUNT, 'Nickname', 'Will Thomspon') e = q.expect('dbus-method-call', interface=cs.CONN_IFACE_ALIASING, method='SetAliases', args=[{ conn.self_handle: 'Will Thomspon' }], handled=False) # Set returns immediately; the change happens asynchronously q.expect('dbus-return', method='Set') q.dbus_return(e.message, signature='') someone_else = conn.ensure_handle(cs.HT_CONTACT, '*****@*****.**') # Another client changes our alias remotely q.dbus_emit(conn.object_path, cs.CONN_IFACE_ALIASING, 'AliasesChanged', dbus.Array([(conn.self_handle, 'wjt'), (someone_else, 'mardy')], signature='(us)'), signature='a(us)') q.expect('dbus-signal', path=account.object_path, signal='AccountPropertyChanged', interface=cs.ACCOUNT, args=[{ 'Nickname': 'wjt' }]) # If we set a trivial nickname while connected, MC does use it nickname = params['account'] call_async(q, account_props, 'Set', cs.ACCOUNT, 'Nickname', params['account']) _, _, e = q.expect_many( EventPattern('dbus-signal', path=account.object_path, signal='AccountPropertyChanged', interface=cs.ACCOUNT, args=[{ 'Nickname': params['account'] }]), EventPattern('dbus-return', method='Set'), EventPattern('dbus-method-call', interface=cs.CONN_IFACE_ALIASING, method='SetAliases', args=[{ conn.self_handle: params['account'] }], handled=False)) assertEquals(nickname, account_props.Get(cs.ACCOUNT, 'Nickname')) q.dbus_return(e.message, signature='') # Set the nickname back to something else nickname = 'wjt' call_async(q, account_props, 'Set', cs.ACCOUNT, 'Nickname', nickname) _, _, e = q.expect_many( EventPattern('dbus-signal', path=account.object_path, signal='AccountPropertyChanged', interface=cs.ACCOUNT, args=[{ 'Nickname': nickname }]), EventPattern('dbus-return', method='Set'), EventPattern('dbus-method-call', interface=cs.CONN_IFACE_ALIASING, method='SetAliases', args=[{ conn.self_handle: nickname }], handled=False)) assertEquals(nickname, account_props.Get(cs.ACCOUNT, 'Nickname')) q.dbus_return(e.message, signature='') # If we set an empty nickname while connected, MC uses our normalized # name (identifier) instead. call_async(q, account_props, 'Set', cs.ACCOUNT, 'Nickname', '') _, _, e = q.expect_many( EventPattern('dbus-signal', path=account.object_path, signal='AccountPropertyChanged', interface=cs.ACCOUNT, args=[{ 'Nickname': params['account'] }]), EventPattern('dbus-return', method='Set'), EventPattern('dbus-method-call', interface=cs.CONN_IFACE_ALIASING, method='SetAliases', args=[{ conn.self_handle: params['account'] }], handled=False)) assertEquals(params['account'], account_props.Get(cs.ACCOUNT, 'Nickname')) q.dbus_return(e.message, signature='')
def test(q, bus, conn, stream, remove=False): call_async(q, conn.ContactList, 'GetContactListAttributes', [], False) q.expect('dbus-error', method='GetContactListAttributes', name=cs.NOT_YET) event = q.expect('stream-iq', query_ns=ns.ROSTER) item = event.query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'both' event.stanza['type'] = 'result' stream.send(event.stanza) holly, dave, arnold, kristine, cat = conn.get_contact_handles_sync( ['*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**']) # slight implementation detail: TpBaseContactList emits ContactsChangedWithID # before it announces its channels s = q.expect('dbus-signal', signal='ContactsChangedWithID', interface=cs.CONN_IFACE_CONTACT_LIST, path=conn.object_path) assertEquals([{ holly: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_YES, ''), }, { holly: '*****@*****.**' }, {}], s.args) # this is emitted last, so clients can tell when the initial state dump # has finished q.expect('dbus-signal', signal='ContactListStateChanged', args=[cs.CONTACT_LIST_STATE_SUCCESS]) call_async(q, conn.ContactList, 'GetContactListAttributes', [], False) r = q.expect('dbus-return', method='GetContactListAttributes') assertEquals(({ holly: { cs.CONN_IFACE_CONTACT_LIST + '/publish': cs.SUBSCRIPTION_STATE_YES, cs.CONN_IFACE_CONTACT_LIST + '/subscribe': cs.SUBSCRIPTION_STATE_YES, cs.CONN + '/contact-id': '*****@*****.**', } },), r.value) # publication authorized for Dave, Holly (the former is pre-authorization, # the latter is a no-op) call_async(q, conn.ContactList, 'AuthorizePublication', [dave, holly]) event = q.expect('dbus-return', method='AuthorizePublication') # Receive authorization requests from the contacts # We pre-authorized Dave, so this is automatically approved presence = domish.Element(('jabber:client', 'presence')) presence['type'] = 'subscribe' presence['from'] = '*****@*****.**' stream.send(presence) q.expect_many( EventPattern('dbus-signal', signal='ContactsChangedWithID', args=[{dave: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_ASK, '')}, { dave: '*****@*****.**' }, {}]), EventPattern('stream-presence', presence_type='subscribed', to='*****@*****.**'), ) # Our server responds to Dave being authorized send_roster_push(stream, '*****@*****.**', 'from') q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id='push'), EventPattern('dbus-signal', signal='ContactsChangedWithID', args=[{dave: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_YES, '')}, { dave: '*****@*****.**' }, {}]), ) # The request from Kristine needs authorization (below) presence['from'] = '*****@*****.**' stream.send(presence) q.expect('dbus-signal', signal='ContactsChangedWithID', args=[{kristine: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_ASK, '')}, { kristine: '*****@*****.**' }, {}]) # This request from Arnold is dealt with below presence['from'] = '*****@*****.**' stream.send(presence) q.expect('dbus-signal', signal='ContactsChangedWithID', args=[{arnold: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_ASK, '')}, { arnold: '*****@*****.**' }, {}]) returning_method = 'AuthorizePublication' call_async(q, conn.ContactList, 'AuthorizePublication', [kristine, holly]) q.expect_many( EventPattern('dbus-return', method=returning_method), EventPattern('stream-presence', presence_type='subscribed', to='*****@*****.**'), ) # Our server acknowledges that we authorized Kristine. Holly's state # does not change. send_roster_push(stream, '*****@*****.**', 'from') q.expect_many( EventPattern('dbus-signal', signal='ContactsChangedWithID', args=[{kristine: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_YES, '')}, { kristine: '*****@*****.**' }, {}]), EventPattern('stream-iq', iq_type='result', iq_id='push'), ) # Arnold gives up waiting for us, and cancels his request presence['from'] = '*****@*****.**' presence['type'] = 'unsubscribe' stream.send(presence) q.expect_many( EventPattern('dbus-signal', signal='ContactsChangedWithID', args=[{arnold: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_REMOVED_REMOTELY, '')}, { arnold: '*****@*****.**' }, {}]), EventPattern('stream-presence', presence_type='unsubscribed', to='*****@*****.**'), ) # We can acknowledge that with RemoveContacts or with Unpublish. # The old Chan.T.ContactList API can't acknowledge RemovedRemotely, # because it sees it as "not there at all" and the group logic drops # the "redundant" request. if remove: returning_method = 'RemoveContacts' call_async(q, conn.ContactList, 'RemoveContacts', [arnold]) else: returning_method = 'Unpublish' call_async(q, conn.ContactList, 'Unpublish', [arnold]) # Even if we Unpublish() here, Arnold was never on our XMPP roster, # so setting his publish state to SUBSCRIPTION_STATE_NO should result # in his removal. q.expect_many( EventPattern('dbus-return', method=returning_method), EventPattern('dbus-signal', signal='ContactsChangedWithID', args=[{}, {}, {arnold: '*****@*****.**' }]), ) # Rejecting an authorization request also works presence = domish.Element(('jabber:client', 'presence')) presence['type'] = 'subscribe' presence['from'] = '*****@*****.**' stream.send(presence) q.expect('dbus-signal', signal='ContactsChangedWithID', args=[{cat: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_ASK, '')}, { cat: '*****@*****.**' }, {}]) if remove: returning_method = 'RemoveContacts' call_async(q, conn.ContactList, 'RemoveContacts', [cat]) else: returning_method = 'Unpublish' call_async(q, conn.ContactList, 'Unpublish', [cat]) # As above, the only reason the Cat is on our contact list is the pending # publish request, so Unpublish really results in removal. q.expect_many( EventPattern('dbus-return', method=returning_method), EventPattern('dbus-signal', signal='ContactsChangedWithID', args=[{}, {}, { cat: '*****@*****.**' }]), ) # Redundant API calls (removing an absent contact, etc.) cause no network # traffic, and succeed. forbidden = [EventPattern('stream-iq', query_ns=ns.ROSTER), EventPattern('stream-presence')] sync_stream(q, stream) sync_dbus(bus, q, conn) q.forbid_events(forbidden) call_async(q, conn.ContactList, 'AuthorizePublication', [kristine, holly, dave]) call_async(q, conn.ContactList, 'Unpublish', [arnold, cat]) call_async(q, conn.ContactList, 'RemoveContacts', [arnold, cat]) q.expect_many( EventPattern('dbus-return', method='AuthorizePublication'), EventPattern('dbus-return', method='Unpublish'), EventPattern('dbus-return', method='RemoveContacts'), ) sync_stream(q, stream) sync_dbus(bus, q, conn) q.unforbid_events(forbidden) # There's one more case: revoking the publish permission of someone who is # genuinely on the roster. if remove: returning_method = 'RemoveContacts' call_async(q, conn.ContactList, 'RemoveContacts', [holly]) else: returning_method = 'Unpublish' call_async(q, conn.ContactList, 'Unpublish', [holly]) if remove: iq = q.expect('stream-iq', iq_type='set', query_ns=ns.ROSTER, query_name='query') acknowledge_iq(stream, iq.stanza) q.expect('dbus-return', method='RemoveContacts') # FIXME: when we depend on a new enough tp-glib, expect RemoveMembers # to return here too send_roster_push(stream, '*****@*****.**', 'remove') q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id='push'), EventPattern('dbus-signal', signal='ContactsChangedWithID', args=[{}, {}, { holly: '*****@*****.**' }]), ) else: q.expect_many( EventPattern('dbus-return', method=returning_method), EventPattern('stream-presence', presence_type='unsubscribed', to='*****@*****.**'), ) send_roster_push(stream, '*****@*****.**', 'to') q.expect_many( EventPattern('stream-iq', iq_type='result', iq_id='push'), EventPattern('dbus-signal', signal='ContactsChangedWithID', args=[{holly: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_NO, ''), }, { holly: '*****@*****.**' }, {}]), )