コード例 #1
0
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)
コード例 #2
0
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()
コード例 #3
0
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
コード例 #4
0
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])
コード例 #5
0
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()
コード例 #6
0
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)
コード例 #7
0
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)
コード例 #8
0
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)
コード例 #9
0
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
コード例 #10
0
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)
コード例 #11
0
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)
コード例 #12
0
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)
コード例 #13
0
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()
コード例 #14
0
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
コード例 #15
0
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'))
コード例 #16
0
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)
コード例 #18
0
ファイル: console.py プロジェクト: mlundblad/telepathy-gabble
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)
コード例 #19
0
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)
コード例 #20
0
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)
コード例 #21
0
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")
コード例 #22
0
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)
コード例 #23
0
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)
コード例 #24
0
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)
コード例 #25
0
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)
コード例 #26
0
    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()
コード例 #27
0
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)
コード例 #28
0
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)
コード例 #29
0
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)
コード例 #30
0
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)
コード例 #31
0
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()
コード例 #33
0
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
コード例 #34
0
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()
コード例 #35
0
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'))
コード例 #36
0
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'])
コード例 #38
0
    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'])
コード例 #39
0
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')
コード例 #40
0
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)
コード例 #41
0
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)
コード例 #42
0
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)
コード例 #43
0
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)
コード例 #44
0
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)
コード例 #45
0
ファイル: test-debug.py プロジェクト: jku/telepathy-gabble
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)
コード例 #46
0
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()
コード例 #48
0
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)
コード例 #49
0
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
コード例 #50
0
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
    ])
コード例 #52
0
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)
コード例 #53
0
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)
コード例 #54
0
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)
コード例 #55
0
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='')
コード例 #56
0
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: '*****@*****.**' }, {}]),
                )