def test(q, bus, conn, stream):
    conn.Connect()
    _, event = q.expect_many(
        EventPattern('dbus-signal', signal='StatusChanged',
            args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]),
        EventPattern('stream-iq', query_ns=ns.ROSTER),
        )

    amy_handle = conn.RequestHandles(1, ['*****@*****.**'])[0]

    event.stanza['type'] = 'result'

    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'both'

    stream.send(event.stanza)
    stream.send(make_presence('*****@*****.**', show='away', status='At the pub'))

    q.expect('dbus-signal', signal='PresencesChanged',
        args=[{amy_handle: (cs.PRESENCE_AWAY, 'away', 'At the pub')}])

    stream.send(make_presence(
        '*****@*****.**', show='chat', status='I may have been drinking'))

    q.expect('dbus-signal', signal='PresencesChanged',
        args=[{amy_handle:
            (cs.PRESENCE_AVAILABLE, 'chat', 'I may have been drinking')}])
Exemple #2
0
def test(q, bus, conn, stream):
    event = q.expect('stream-iq', query_ns=ns.ROSTER)

    amy_handle = conn.get_contact_handle_sync('*****@*****.**')

    event.stanza['type'] = 'result'

    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'both'

    stream.send(event.stanza)
    stream.send(make_presence('*****@*****.**', show='away', status='At the pub'))

    q.expect('dbus-signal', signal='PresencesChanged',
        args=[{amy_handle: (cs.PRESENCE_AWAY, 'away', 'At the pub')}])

    stream.send(make_presence(
        '*****@*****.**', show='chat', status='I may have been drinking'))

    e = q.expect('dbus-signal', signal='PresencesChanged',
        args=[{amy_handle:
            (cs.PRESENCE_AVAILABLE, 'chat', 'I may have been drinking')}])

    amy_handle, asv = conn.Contacts.GetContactByID('*****@*****.**',
            [cs.CONN_IFACE_SIMPLE_PRESENCE])
    assertEquals(e.args[0][amy_handle], asv.get(cs.ATTR_PRESENCE))

    bob_handle, asv = conn.Contacts.GetContactByID('*****@*****.**',
            [cs.CONN_IFACE_SIMPLE_PRESENCE])
    assertEquals((cs.PRESENCE_UNKNOWN, 'unknown', ''),
            asv.get(cs.ATTR_PRESENCE))
def send_presence(q, conn, stream, contact, caps, initial=True, show=None):
    h = conn.RequestHandles(cs.HT_CONTACT, [contact])[0]

    if initial:
        stream.send(make_presence(contact, status='hello'))

        q.expect_many(
            EventPattern('dbus-signal',
                         signal='PresenceUpdate',
                         args=[{
                             h: (0L, {
                                 u'available': {
                                     'message': 'hello'
                                 }
                             })
                         }]),
            EventPattern('dbus-signal',
                         signal='PresencesChanged',
                         args=[{
                             h: (2, u'available', 'hello')
                         }]))

        # no special capabilities
        assertEquals([(h, cs.CHANNEL_TYPE_TEXT, 3, 0)],
                     conn.Capabilities.GetCapabilities([h]))

    # send updated presence with caps info
    stream.send(make_presence(contact, show=show, status='hello', caps=caps))

    return h
def test(q, bus, conn, stream):
    event = q.expect('stream-iq', query_ns=ns.ROSTER)

    amy, bob, che, dre, eve = conn.RequestHandles(cs.HT_CONTACT,
        ['*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**',
         '*****@*****.**'])
    assertEquals({amy: UNKNOWN,
                  bob: UNKNOWN,
                  che: UNKNOWN,
                  dre: UNKNOWN,
                  eve: UNKNOWN,
                 },
        conn.SimplePresence.GetPresences([amy, bob, che, dre, eve]))

    # Before the server sends Gabble the roster, it relays an 'unavailable'
    # presence for one of the contacts we're subscribed to. This seems to
    # happen in practice when using Prosody with a shared roster: the presence
    # probes start coming back negatively before the shared roster is retrieved
    # and returned to the client.
    stream.send(make_presence('*****@*****.**', type='unavailable'))

    # Dre's presence is still unknown, since we don't have the roster. This
    # isn't a change per se---we checked above, and Dre's presence was
    # unknown---so it shouldn't be signalled.
    q.forbid_events([EventPattern('dbus-signal', signal='PresencesChanged',
        args=[{dre: UNKNOWN}])])

    # We also receive an available presence from Eve before the roster arrives:
    # this presence should behave normally.
    stream.send(make_presence('*****@*****.**'))
    q.expect('dbus-signal', signal='PresencesChanged', args=[{eve: AVAILABLE}])

    event.stanza['type'] = 'result'
    event.query.addChild(make_roster_item('*****@*****.**', 'both'))
    event.query.addChild(make_roster_item('*****@*****.**', 'from'))
    event.query.addChild(make_roster_item('*****@*****.**', 'to'))
    event.query.addChild(make_roster_item('*****@*****.**', 'both'))
    event.query.addChild(make_roster_item('*****@*****.**', 'both'))
    stream.send(event.stanza)

    # The presence for contacts on the roster whose subscription is 'to' or
    # 'both' but for whom we haven't already received presence should change
    # from 'unknown' (as checked above) to 'offline'.
    e = q.expect('dbus-signal', signal='PresencesChanged')
    changed_presences, = e.args
    assertEquals(
        {amy: OFFLINE,
         che: OFFLINE,
         dre: OFFLINE,
        },
        changed_presences)

    assertEquals({amy: OFFLINE,
                  bob: UNKNOWN,
                  che: OFFLINE,
                  dre: OFFLINE,
                  eve: AVAILABLE,
                 },
        conn.SimplePresence.GetPresences([amy, bob, che, dre, eve]))
Exemple #5
0
def test(q, bus, conn, stream):
    jids = ['*****@*****.**',
            '*****@*****.**',
           ]
    gregory, hawk = jids
    gregory_handle, hawk_handle = conn.get_contact_handles_sync(jids)

    event = q.expect('stream-iq', query_ns=ns.ROSTER)
    event.stanza['type'] = 'result'
    for jid in jids:
        item = event.query.addElement('item')
        item['jid'] = jid
        item['subscription'] = 'both'

    stream.send(event.stanza)
    q.expect('dbus-signal', signal='PresencesChanged',
        args=[{gregory_handle: (cs.PRESENCE_OFFLINE, 'offline', ''),
               hawk_handle:    (cs.PRESENCE_OFFLINE, 'offline', ''),
              }
             ])

    # Our server can't resolve unreachable.example.com so it sends us an error
    # presence for Gregory. (This is what Prosody actually does.)
    presence = make_presence(gregory, type='error')
    error_text = u'Connection failed: DNS resolution failed'
    presence.addChild(
        elem('error', type='cancel')(
          elem(ns.STANZA, 'remote-server-not-found'),
          elem(ns.STANZA, 'text')(
            error_text
          )
        ))

    stream.send(presence)

    e = q.expect('dbus-signal', signal='PresencesChanged')
    presences, = e.args
    type_, status, message = presences[gregory_handle]
    assertEquals(cs.PRESENCE_ERROR, type_)
    assertEquals('error', status)
    assertEquals(error_text, message)

    # How about maybe the hawk's server is busted?
    presence = make_presence(hawk, type='error')
    presence.addChild(
        elem('error', type='cancel')(
          elem(ns.STANZA, 'internal-server-error'),
        ))
    stream.send(presence)

    e = q.expect('dbus-signal', signal='PresencesChanged')
    presences, = e.args
    type_, status, message = presences[hawk_handle]
    assertEquals(cs.PRESENCE_ERROR, type_)
    assertEquals('error', status)
    # FIXME: It might be less user-hostile to give some kind of readable
    # description of the error in future.
    assertEquals('internal-server-error', message)
def test(q, bus, conn, stream):
    self_presence = q.expect('stream-presence')

    c = xpath.queryForNodes('/presence/c', self_presence.stanza)[0]

    jid = '[email protected]/omg'

    # Gabble shouldn't send any disco requests to our contact during this test.
    q.forbid_events([
        EventPattern('stream-iq', to=jid, iq_type='get',
            query_ns=ns.DISCO_INFO),
    ])

    # Check that Gabble doesn't disco other clients with the same caps hash.
    p = make_presence(jid,
        caps={'node': c['node'],
              'hash': c['hash'],
              'ver':  c['ver'],
             })
    stream.send(p)
    sync_stream(q, stream)

    # Check that Gabble doesn't disco its own ext='' bundles (well, its own
    # bundles as advertised by Gabbles that don't do hashed caps)
    p = make_presence(jid,
        caps={'node': c['node'],
              'ver':  c['ver'],
              # omitting hash='' so Gabble doesn't ignore ext=''
              'ext':  'voice-v1 video-v1',
            })
    stream.send(p)
    sync_stream(q, stream)

    # Advertise some different capabilities, to change our own caps hash.
    add = [(cs.CHANNEL_TYPE_STREAMED_MEDIA, 2L**32-1),
           (cs.CHANNEL_TYPE_STREAM_TUBE, 2L**32-1),
           (cs.CHANNEL_TYPE_STREAM_TUBE, 2L**32-1)]
    remove = []
    caps = conn.Capabilities.AdvertiseCapabilities(add, remove)

    self_presence = q.expect('stream-presence')
    c_ = xpath.queryForNodes('/presence/c', self_presence.stanza)[0]
    assertNotEquals(c['ver'], c_['ver'])

    # But then someone asks us for our old caps
    iq = IQ(stream, 'get')
    iq['from'] = jid
    query = iq.addElement((ns.DISCO_INFO, 'query'))
    query['node'] = c['node'] + '#' + c['ver']
    stream.send(iq)

    # Gabble should still know what they are, and reply. This is actually quite
    # important: there's a bug in iChat where if you return an error to a disco
    # query, it just asks again, and again, and again...
    reply = q.expect('stream-iq', to=jid)
    assertEquals('result', reply.iq_type)
Exemple #7
0
def test(q, bus, conn, stream):
    jids = [
        '*****@*****.**',
        '*****@*****.**',
    ]
    gregory, hawk = jids
    gregory_handle, hawk_handle = conn.get_contact_handles_sync(jids)

    event = q.expect('stream-iq', query_ns=ns.ROSTER)
    event.stanza['type'] = 'result'
    for jid in jids:
        item = event.query.addElement('item')
        item['jid'] = jid
        item['subscription'] = 'both'

    stream.send(event.stanza)
    q.expect('dbus-signal',
             signal='PresencesChanged',
             args=[{
                 gregory_handle: (cs.PRESENCE_OFFLINE, 'offline', ''),
                 hawk_handle: (cs.PRESENCE_OFFLINE, 'offline', ''),
             }])

    # Our server can't resolve unreachable.example.com so it sends us an error
    # presence for Gregory. (This is what Prosody actually does.)
    presence = make_presence(gregory, type='error')
    error_text = u'Connection failed: DNS resolution failed'
    presence.addChild(
        elem('error', type='cancel')(elem(ns.STANZA,
                                          'remote-server-not-found'),
                                     elem(ns.STANZA, 'text')(error_text)))

    stream.send(presence)

    e = q.expect('dbus-signal', signal='PresencesChanged')
    presences, = e.args
    type_, status, message = presences[gregory_handle]
    assertEquals(cs.PRESENCE_ERROR, type_)
    assertEquals('error', status)
    assertEquals(error_text, message)

    # How about maybe the hawk's server is busted?
    presence = make_presence(hawk, type='error')
    presence.addChild(
        elem('error', type='cancel')(elem(ns.STANZA,
                                          'internal-server-error'), ))
    stream.send(presence)

    e = q.expect('dbus-signal', signal='PresencesChanged')
    presences, = e.args
    type_, status, message = presences[hawk_handle]
    assertEquals(cs.PRESENCE_ERROR, type_)
    assertEquals('error', status)
    # FIXME: It might be less user-hostile to give some kind of readable
    # description of the error in future.
    assertEquals('internal-server-error', message)
def test(q, bus, conn, stream, bytestream_cls, access_control):
    disco_event = q.expect('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS)

    announce_socks5_proxy(q, stream, disco_event.stanza)

    t.check_conn_properties(q, conn)

    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
    alice_handle = conn.get_contact_handle_sync('alice@localhost')

    # send Alice's presence
    caps =  { 'ext': '', 'ver': '0.0.0',
        'node': 'http://example.com/fake-client0' }
    presence = make_presence('alice@localhost/Test', caps=caps)
    stream.send(presence)

    _, disco_event = q.expect_many(
        EventPattern('dbus-signal', signal='PresencesChanged',
            args = [{alice_handle: (2L, u'available', u'')}]),
        EventPattern('stream-iq', to='alice@localhost/Test',
            query_ns=ns.DISCO_INFO),
        )

    # reply to disco query
    send_disco_reply(stream, disco_event.stanza, [], [ns.TUBES])

    sync_stream(q, stream)

    offer_new_dbus_tube(q, bus, conn, stream, self_handle, alice_handle, bytestream_cls, access_control)
Exemple #9
0
def test_39464(q, bus, conn, stream):
    """
    Regression test for an issue where a form with no type='' attribute on the
    <x/> node would crash Gabble.
    """
    client = 'fake:qutim'
    hash = 'blahblah'
    contact = '[email protected]/foo'
    caps = {
        'node': client,
        'ver': hash,
        'hash': 'sha-1',
        }
    presence = make_presence(contact, status='hello', caps=caps)
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO)

    # Send a reply with a form without a type=''
    result = make_result_iq(stream, event.stanza, add_query_node=False)
    result.addChild(
        elem(ns.DISCO_INFO, 'query', node='%s#%s' % (client, hash))(
          # NB. no type='' attribute
          elem(ns.X_DATA, 'x')
        )
      )
    stream.send(result)
    # We don't really care what Gabble does, as long as it doesn't crash.
    sync_stream(q, stream)
Exemple #10
0
def test(q, bus, conn, stream):
    contact = '[email protected]/delicious'
    presence = make_presence(contact,
                             status='eat me!',
                             caps={
                                 'node': 'oh:hai',
                                 'ver': 'thar',
                             })
    thar_disco = EventPattern('stream-iq',
                              to=contact,
                              query_ns=ns.DISCO_INFO,
                              query_node='oh:hai#thar')

    stream.send(presence)
    q.expect_many(thar_disco)

    # Okay, all good so far. But if we get the same caps node again from the
    # same contact, we shouldn't disco it again: we won't get any more trust
    # that way. This matters in practice, because Google's clients send a whole
    # bunch of presence stanzas in quick succession when they sign on.
    q.forbid_events([thar_disco])

    stream.send(presence)
    sync_stream(q, stream)

    # If we get a presence update from this contact with some new ext=''
    # bundles, we should disco those, but not the nodes we're already querying.
    presence = make_presence(contact,
                             status='eat me!',
                             caps={
                                 'node': 'oh:hai',
                                 'ver': 'thar',
                                 'ext': 'good-sir',
                             })
    good_sir_disco = EventPattern('stream-iq',
                                  to=contact,
                                  query_ns=ns.DISCO_INFO,
                                  query_node='oh:hai#good-sir')
    stream.send(presence)

    q.expect_many(good_sir_disco)
    sync_stream(q, stream)

    # We should only disco ext='' attributes once per jid, too.
    q.forbid_events([good_sir_disco])
    stream.send(presence)
    sync_stream(q, stream)
def send_presence(q, conn, stream, contact, caps, initial=True, show=None):
    h = conn.get_contact_handle_sync(contact)

    if initial:
        stream.send(make_presence(contact, status='hello'))

        q.expect('dbus-signal', signal='PresencesChanged',
                args=[{h:
                   (2, u'available', 'hello')}])

        # no special capabilities
        for rcc in get_contacts_capabilities_sync(conn, [h])[h]:
            assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE))

    # send updated presence with caps info
    stream.send(make_presence(contact, show=show, status='hello', caps=caps))

    return h
def send_presence(q, conn, stream, contact, caps, initial=True, show=None):
    h = conn.RequestHandles(cs.HT_CONTACT, [contact])[0]

    if initial:
        stream.send(make_presence(contact, status='hello'))

        q.expect('dbus-signal', signal='PresencesChanged',
                args=[{h:
                   (2, u'available', 'hello')}])

        # no special capabilities
        assertEquals([(h, cs.CHANNEL_TYPE_TEXT, 3, 0)],
            conn.Capabilities.GetCapabilities([h]))

    # send updated presence with caps info
    stream.send(make_presence(contact, show=show, status='hello', caps=caps))

    return h
def test(q, bus, conn, stream):
    conn.Connect()
    q.expect('dbus-signal', signal='StatusChanged',
            args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])

    contact = '[email protected]/delicious'
    presence = make_presence(contact, type='available', status='eat me!',
        caps={ 'node': 'oh:hai',
               'ver':  'thar',
             })
    thar_disco = EventPattern('stream-iq', to=contact,
        query_ns=ns.DISCO_INFO, query_node='oh:hai#thar')

    stream.send(presence)
    q.expect_many(thar_disco)

    # Okay, all good so far. But if we get the same caps node again from the
    # same contact, we shouldn't disco it again: we won't get any more trust
    # that way. This matters in practice, because Google's clients send a whole
    # bunch of presence stanzas in quick succession when they sign on.
    q.forbid_events([thar_disco])

    stream.send(presence)
    sync_stream(q, stream)

    # If we get a presence update from this contact with some new ext=''
    # bundles, we should disco those, but not the nodes we're already querying.
    presence = make_presence(contact, type='available', status='eat me!',
        caps={ 'node': 'oh:hai',
               'ver':  'thar',
               'ext':  'good-sir',
             })
    good_sir_disco = EventPattern('stream-iq', to=contact,
        query_ns=ns.DISCO_INFO, query_node='oh:hai#good-sir')
    stream.send(presence)

    q.expect_many(good_sir_disco)
    sync_stream(q, stream)

    # We should only disco ext='' attributes once per jid, too.
    q.forbid_events([good_sir_disco])
    stream.send(presence)
    sync_stream(q, stream)
def send_presence(q, stream, contact_jid, identity):
    ver = compute_caps_hash([identity], features, {})
    stream.send(
        make_presence(contact_jid,
                      status='Hello',
                      caps={
                          'node': client,
                          'hash': 'sha-1',
                          'ver': ver
                      }))
def receive_caps(q,
                 conn,
                 stream,
                 contact,
                 contact_handle,
                 features,
                 expected_caps,
                 expect_disco=True,
                 expect_ccc=True):
    presence = make_presence(contact, status='hello')
    c = presence.addElement((ns.CAPS, 'c'))
    c['node'] = client
    c['ver'] = compute_caps_hash([], features if features is not None else [],
                                 {})
    c['hash'] = 'sha-1'
    stream.send(presence)

    if expect_disco:
        # Gabble looks up our capabilities
        event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO)
        query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
        assert query_node.attributes['node'] == \
            client + '#' + c['ver']

        # send good reply
        result = make_result_iq(stream, event.stanza)
        query = result.firstChildElement()
        query['node'] = client + '#' + c['ver']

        for f in features:
            feature = query.addElement('feature')
            feature['var'] = f

        stream.send(result)

    if expect_ccc:
        event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
        announced_ccs, = event.args
        assertSameElements(expected_caps, announced_ccs)
    else:
        # Make sure Gabble's got the caps
        sync_stream(q, stream)

    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assertSameElements(expected_caps, caps)

    # test again, to check GetContactCapabilities does not have side effect
    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assertSameElements(expected_caps, caps)

    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn.Contacts.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.ATTR_CONTACT_CAPABILITIES]
    assertSameElements(caps[contact_handle], caps_via_contacts_iface)
def test2(q, bus, conn, stream):
    marco_pidgin = '[email protected]/Pidgin'
    marco_phone = '[email protected]/N900'
    handle = conn.get_contact_handle_sync(marco_pidgin)

    # pidgin comes online
    contact_online(q, conn, stream, marco_pidgin, PC)

    types = get_client_types(conn, handle)
    assertSameSets(['pc'], types)

    # phone comes online
    contact_online(q, conn, stream, marco_phone, PHONE, initial=False)

    types = get_client_types(conn, handle)
    assertSameSets(['pc'], types)

    sync_stream(q, stream)

    # pidgin goes offline
    stream.send(make_presence(marco_pidgin, type='unavailable'))

    # no presence signal

    q.expect('dbus-signal',
             signal='ClientTypesUpdated',
             args=[handle, ['phone']])

    # pidgin comes back online
    caps, _, _ = build_stuff(PC)
    stream.send(make_presence(marco_pidgin, status='hello', caps=caps))

    q.expect('dbus-signal', signal='ClientTypesUpdated', args=[handle, ['pc']])

    attrs = conn.Contacts.GetContactAttributes([handle],
                                               [cs.CONN_IFACE_CLIENT_TYPES],
                                               False)
    assertContains(handle, attrs)
    attr = cs.CONN_IFACE_CLIENT_TYPES + '/client-types'
    assertContains(attr, attrs[handle])
    assertEquals(['pc'], attrs[handle][attr])
def test2(q, bus, conn, stream):
    marco_pidgin = '[email protected]/Pidgin'
    marco_phone = '[email protected]/N900'
    handle = conn.get_contact_handle_sync(marco_pidgin)

    # pidgin comes online
    contact_online(q, conn, stream, marco_pidgin, PC)

    types = get_client_types(conn, handle)
    assertSameSets(['pc'], types)

    # phone comes online
    contact_online(q, conn, stream, marco_phone, PHONE, initial=False)

    types = get_client_types(conn, handle)
    assertSameSets(['pc'], types)

    sync_stream(q, stream)

    # pidgin goes offline
    stream.send(make_presence(marco_pidgin, type='unavailable'))

    # no presence signal

    q.expect('dbus-signal', signal='ClientTypesUpdated',
             args=[handle, ['phone']])

    # pidgin comes back online
    caps, _, _ = build_stuff(PC)
    stream.send(make_presence(marco_pidgin, status='hello', caps=caps))

    q.expect('dbus-signal', signal='ClientTypesUpdated',
             args=[handle, ['pc']])

    attrs = conn.Contacts.GetContactAttributes([handle],
        [cs.CONN_IFACE_CLIENT_TYPES], False)
    assertContains(handle, attrs)
    attr = cs.CONN_IFACE_CLIENT_TYPES + '/client-types'
    assertContains(attr, attrs[handle])
    assertEquals(['pc'], attrs[handle][attr])
Exemple #18
0
def test(q, bus, conn, stream):
    event = q.expect('stream-iq', query_ns=ns.ROSTER)

    amy_handle = conn.get_contact_handle_sync('*****@*****.**')

    event.stanza['type'] = 'result'

    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'both'

    stream.send(event.stanza)
    stream.send(make_presence('*****@*****.**', show='away', status='At the pub'))

    q.expect('dbus-signal',
             signal='PresencesChanged',
             args=[{
                 amy_handle: (cs.PRESENCE_AWAY, 'away', 'At the pub')
             }])

    stream.send(
        make_presence('*****@*****.**',
                      show='chat',
                      status='I may have been drinking'))

    e = q.expect('dbus-signal',
                 signal='PresencesChanged',
                 args=[{
                     amy_handle: (cs.PRESENCE_AVAILABLE, 'chat',
                                  'I may have been drinking')
                 }])

    amy_handle, asv = conn.Contacts.GetContactByID(
        '*****@*****.**', [cs.CONN_IFACE_SIMPLE_PRESENCE])
    assertEquals(e.args[0][amy_handle], asv.get(cs.ATTR_PRESENCE))

    bob_handle, asv = conn.Contacts.GetContactByID(
        '*****@*****.**', [cs.CONN_IFACE_SIMPLE_PRESENCE])
    assertEquals((cs.PRESENCE_UNKNOWN, 'unknown', ''),
                 asv.get(cs.ATTR_PRESENCE))
Exemple #19
0
def test_success(q, gateways_iface, stream):
    call_async(q, gateways_iface, 'Register',
            'talkd.example.com', '1970', 's3kr1t')
    e = q.expect('stream-iq', iq_type='set', query_name='query',
            query_ns=ns.REGISTER, to='talkd.example.com')
    assertEquals('1970', xpath.queryForString('/query/username', e.query))
    assertEquals('s3kr1t', xpath.queryForString('/query/password', e.query))
    acknowledge_iq(stream, e.stanza)
    q.expect_many(
            EventPattern('dbus-return', method='Register'),
            EventPattern('stream-presence', presence_type='subscribe',
                to='talkd.example.com'),
            )
    stream.send(make_presence('talkd.example.com', type='subscribed'))
Exemple #20
0
def worker(q, bus, conn, stream, should_decloak):
    decloak_automatically = conn.Get(cs.CONN_IFACE_GABBLE_DECLOAK,
            'DecloakAutomatically', dbus_interface=cs.PROPERTIES_IFACE)
    assertEquals(should_decloak, decloak_automatically)

    amy_handle = conn.get_contact_handle_sync('*****@*****.**')

    # Amy directs presence to us

    presence = make_presence('[email protected]/panopticon')
    decloak = presence.addElement((ns.TEMPPRES, 'temppres'))
    decloak['reason'] = 'media'
    stream.send(presence)

    events = [
            EventPattern('dbus-signal', signal='PresencesChanged',
                args=[{amy_handle: (cs.PRESENCE_AVAILABLE, 'available', '')}]),
            EventPattern('dbus-signal', signal='DecloakRequested',
                args=[amy_handle, 'media', should_decloak]),
            ]
    forbidden = []

    if should_decloak:
        events.append(EventPattern('stream-presence',
            to='[email protected]/panopticon'))
    else:
        forbidden = [EventPattern('stream-presence')]

    q.forbid_events(forbidden)
    q.expect_many(*events)

    presence = make_presence('[email protected]/panopticon', type='unavailable')
    stream.send(presence)
    q.expect('dbus-signal', signal='PresencesChanged',
                args=[{amy_handle: (cs.PRESENCE_OFFLINE, 'offline', '')}])

    q.unforbid_events(forbidden)
def receive_caps(q, conn, stream, contact, contact_handle, features,
                 expected_caps, expect_disco=True, expect_ccc=True):
    presence = make_presence(contact, status='hello')
    c = presence.addElement((ns.CAPS, 'c'))
    c['node'] = client
    c['ver'] = compute_caps_hash([],
        features if features is not None else [],
        {})
    c['hash'] = 'sha-1'
    stream.send(presence)

    if expect_disco:
        # Gabble looks up our capabilities
        event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO)
        query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
        assert query_node.attributes['node'] == \
            client + '#' + c['ver']

        # send good reply
        result = make_result_iq(stream, event.stanza)
        query = result.firstChildElement()
        query['node'] = client + '#' + c['ver']

        for f in features:
            feature = query.addElement('feature')
            feature['var'] = f

        stream.send(result)

    if expect_ccc:
        event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
        announced_ccs, = event.args
        assertSameElements(expected_caps, announced_ccs)
    else:
        # Make sure Gabble's got the caps
        sync_stream(q, stream)

    caps = conn.ContactCapabilities.GetContactCapabilities([contact_handle])
    assertSameElements(expected_caps, caps)

    # test again, to check GetContactCapabilities does not have side effect
    caps = conn.ContactCapabilities.GetContactCapabilities([contact_handle])
    assertSameElements(expected_caps, caps)

    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn.Contacts.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.ATTR_CONTACT_CAPABILITIES]
    assertSameElements(caps[contact_handle], caps_via_contacts_iface)
def connect_and_announce_alice(q, bus, conn, stream):
    q.forbid_events(proxy_query_events)

    # Send Alice's presence
    caps =  { 'ext': '', 'ver': '0.0.0',
        'node': 'http://example.com/fake-client0' }
    presence = make_presence('alice@localhost/Test', caps=caps)
    stream.send(presence)

    disco_event = q.expect('stream-iq', to='alice@localhost/Test',
        query_ns=ns.DISCO_INFO)

    send_disco_reply(
        stream, disco_event.stanza, [], [ns.TUBES, ns.FILE_TRANSFER])
    sync_stream(q, stream)

    q.unforbid_events(proxy_query_events)
def test_success(q, gateways_iface, stream):
    call_async(q, gateways_iface, 'Register', 'talkd.example.com', '1970',
               's3kr1t')
    e = q.expect('stream-iq',
                 iq_type='set',
                 query_name='query',
                 query_ns=ns.REGISTER,
                 to='talkd.example.com')
    assertEquals('1970', xpath.queryForString('/query/username', e.query))
    assertEquals('s3kr1t', xpath.queryForString('/query/password', e.query))
    acknowledge_iq(stream, e.stanza)
    q.expect_many(
        EventPattern('dbus-return', method='Register'),
        EventPattern('stream-presence',
                     presence_type='subscribe',
                     to='talkd.example.com'),
    )
    stream.send(make_presence('talkd.example.com', type='subscribed'))
Exemple #24
0
def connect_and_announce_alice(q, bus, conn, stream):
    q.forbid_events(proxy_query_events)

    # Send Alice's presence
    caps = {
        'ext': '',
        'ver': '0.0.0',
        'node': 'http://example.com/fake-client0'
    }
    presence = make_presence('alice@localhost/Test', caps=caps)
    stream.send(presence)

    disco_event = q.expect('stream-iq',
                           to='alice@localhost/Test',
                           query_ns=ns.DISCO_INFO)

    send_disco_reply(stream, disco_event.stanza, [],
                     [ns.TUBES, ns.FILE_TRANSFER])
    sync_stream(q, stream)

    q.unforbid_events(proxy_query_events)
def test(q, bus, conn, stream, bytestream_cls, access_control):
    disco_event = q.expect('stream-iq',
                           to='localhost',
                           query_ns=ns.DISCO_ITEMS)

    announce_socks5_proxy(q, stream, disco_event.stanza)

    t.check_conn_properties(q, conn)

    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
    alice_handle = conn.get_contact_handle_sync('alice@localhost')

    # send Alice's presence
    caps = {
        'ext': '',
        'ver': '0.0.0',
        'node': 'http://example.com/fake-client0'
    }
    presence = make_presence('alice@localhost/Test', caps=caps)
    stream.send(presence)

    _, disco_event = q.expect_many(
        EventPattern('dbus-signal',
                     signal='PresencesChanged',
                     args=[{
                         alice_handle: (2L, u'available', u'')
                     }]),
        EventPattern('stream-iq',
                     to='alice@localhost/Test',
                     query_ns=ns.DISCO_INFO),
    )

    # reply to disco query
    send_disco_reply(stream, disco_event.stanza, [], [ns.TUBES])

    sync_stream(q, stream)

    offer_new_dbus_tube(q, bus, conn, stream, self_handle, alice_handle,
                        bytestream_cls, access_control)
def test(q, bus, conn, stream):
    conn.Connect()
    q.expect('dbus-signal', signal='StatusChanged',
            args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])

    self_handle = conn.GetSelfHandle()

    jid = '*****@*****.**'
    foo_handle = conn.RequestHandles(cs.HT_CONTACT, [jid])[0]

    path = conn.Requests.CreateChannel(
            { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
              cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
              cs.TARGET_HANDLE: foo_handle,
              })[0]
    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
        ['ChatState', 'Destroyable'])

    presence = make_presence('[email protected]/Foo', status='hello',
        caps={
            'node': 'http://telepathy.freedesktop.org/homeopathy',
            'ver' : '0.1',
        })
    stream.send(presence)

    version_event = q.expect('stream-iq', to='[email protected]/Foo',
        query_ns='http://jabber.org/protocol/disco#info',
        query_node='http://telepathy.freedesktop.org/homeopathy#0.1')

    result = make_result_iq(stream, version_event.stanza)
    query = result.firstChildElement()
    feature = query.addElement('feature')
    feature['var'] = 'http://jabber.org/protocol/chatstates'
    stream.send(result)

    sync_stream(q, stream)

    # Receiving chat states:

    # Composing...
    m = domish.Element((None, 'message'))
    m['from'] = '[email protected]/Foo'
    m['type'] = 'chat'
    m.addElement((ns.CHAT_STATES, 'composing'))
    stream.send(m)

    changed = q.expect('dbus-signal', signal='ChatStateChanged')
    handle, state = changed.args
    assertEquals(foo_handle, handle)
    assertEquals(cs.CHAT_STATE_COMPOSING, state)

    # Message!

    m = domish.Element((None, 'message'))
    m['from'] = '[email protected]/Foo'
    m['type'] = 'chat'
    m.addElement((ns.CHAT_STATES, 'active'))
    m.addElement('body', content='hello')
    stream.send(m)

    changed = q.expect('dbus-signal', signal='ChatStateChanged')
    handle, state = changed.args
    assertEquals(foo_handle, handle)
    assertEquals(cs.CHAT_STATE_ACTIVE, state)

    # Sending chat states:

    # Composing...
    call_async(q, chan.ChatState, 'SetChatState', cs.CHAT_STATE_COMPOSING)

    stream_message = q.expect('stream-message')
    check_state_notification(stream_message.stanza, 'composing')

    # XEP 0085:
    #   every content message SHOULD contain an <active/> notification.
    call_async(q, chan.Text, 'Send', 0, 'hi.')

    stream_message = q.expect('stream-message')
    elem = stream_message.stanza
    assert elem.name == 'message'
    assert elem['type'] == 'chat', elem['type']

    def is_body(e):
        if e.name == 'body':
            assert e.children[0] == u'hi.', e.toXml()
            return True
        return False

    def is_active(e):
        if e.uri == ns.CHAT_STATES:
            assert e.name == 'active', e.toXml()
            return True
        return False

    children = list(elem.elements())

    assert len(filter(is_body,   children)) == 1, elem.toXml()
    assert len(filter(is_active, children)) == 1, elem.toXml()

    # Close the channel without acking the received message. The peer should
    # get a <gone/> notification, and the channel should respawn.
    chan.Close()

    gone, _, _ = q.expect_many(
        EventPattern('stream-message'),
        EventPattern('dbus-signal', signal='Closed'),
        EventPattern('dbus-signal', signal='NewChannel'),
        )
    check_state_notification(gone.stanza, 'gone')

    # Reusing the proxy object because we happen to know it'll be at the same
    # path...

    # Destroy the channel. The peer shouldn't get a <gone/> notification, since
    # we already said we were gone and haven't sent them any messages to the
    # contrary.
    es = [EventPattern('stream-message')]
    q.forbid_events(es)

    chan.Destroyable.Destroy()
    sync_stream(q, stream)

    # Make the channel anew.
    path = conn.Requests.CreateChannel(
            { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
              cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
              cs.TARGET_HANDLE: foo_handle,
              })[0]
    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
        ['ChatState', 'Destroyable'])

    # Close it immediately; the peer should again not get a <gone/>
    # notification, since we haven't sent any notifications on that channel.
    chan.Close()
    sync_stream(q, stream)
def test(q, bus, conn, stream):
    event = q.expect('stream-iq', query_ns=ns.ROSTER)

    amy, bob, che, dre, eve = conn.get_contact_handles_sync(
        ['*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**',
         '*****@*****.**'])

    assertEquals({amy: UNKNOWN,
                  bob: UNKNOWN,
                  che: UNKNOWN,
                  dre: UNKNOWN,
                  eve: UNKNOWN,
                 },
        get_contacts_presences_sync(conn, [amy, bob, che, dre, eve]))

    # Before the server sends Gabble the roster, it relays an 'unavailable'
    # presence for one of the contacts we're subscribed to. This seems to
    # happen in practice when using Prosody with a shared roster: the presence
    # probes start coming back negatively before the shared roster is retrieved
    # and returned to the client.
    stream.send(make_presence('*****@*****.**', type='unavailable'))

    # Dre's presence is still unknown, since we don't have the roster. This
    # isn't a change per se---we checked above, and Dre's presence was
    # unknown---so it shouldn't be signalled.
    q.forbid_events([EventPattern('dbus-signal', signal='PresencesChanged',
        args=[{dre: UNKNOWN}])])

    # We also receive an available presence from Eve before the roster arrives:
    # this presence should behave normally.
    stream.send(make_presence('*****@*****.**'))
    q.expect('dbus-signal', signal='PresencesChanged', args=[{eve: AVAILABLE}])

    # We also get a message from a contact before we get the roster (presumably
    # they sent this while we were offline?). This shouldn't affect the contact
    # being reported as offline when we finally do get the roster, but it used
    # to: <https://bugs.freedesktop.org/show_bug.cgi?id=41743>.
    stream.send(
        elem('message', from_='*****@*****.**', type='chat')(
          elem('body')(u'why are you never online?')
        ))
    q.expect('dbus-signal', signal='MessageReceived')

    event.stanza['type'] = 'result'
    event.query.addChild(make_roster_item('*****@*****.**', 'both'))
    event.query.addChild(make_roster_item('*****@*****.**', 'from'))
    event.query.addChild(make_roster_item('*****@*****.**', 'to'))
    event.query.addChild(make_roster_item('*****@*****.**', 'both'))
    event.query.addChild(make_roster_item('*****@*****.**', 'both'))
    stream.send(event.stanza)

    # The presence for contacts on the roster whose subscription is 'to' or
    # 'both' but for whom we haven't already received presence should change
    # from 'unknown' (as checked above) to 'offline'.
    e = q.expect('dbus-signal', signal='PresencesChanged')
    changed_presences, = e.args
    assertEquals(
        {amy: OFFLINE,
         che: OFFLINE,
         dre: OFFLINE,
        },
        changed_presences)

    assertEquals({amy: OFFLINE,
                  bob: UNKNOWN,
                  che: OFFLINE,
                  dre: OFFLINE,
                  eve: AVAILABLE,
                 },
        get_contacts_presences_sync(conn, [amy, bob, che, dre, eve]))
def send_presence(q, stream, contact_jid, identity):
    ver = compute_caps_hash([identity], features, {})
    stream.send(make_presence(contact_jid, status='Hello',
        caps={'node': client, 'hash': 'sha-1', 'ver': ver}))
def test(q, bus, conn, stream):
    # check all these types appear as they should
    contact_online(q, conn, stream, '[email protected]/lol', BOT)
    contact_online(q, conn, stream, '[email protected]/lol', CONSOLE)
    contact_online(q, conn, stream, '[email protected]/lol', GAME)
    contact_online(q, conn, stream, '[email protected]/lol', HANDHELD)
    contact_online(q, conn, stream, '[email protected]/lol', PC)
    contact_online(q, conn, stream, '[email protected]/lol', PHONE)
    contact_online(q, conn, stream, '[email protected]/lol', WEB)
    contact_online(q, conn, stream, '[email protected]/lol', SMS)

    meredith_one = '[email protected]/One'
    meredith_two = '[email protected]/Two'
    meredith_three = '[email protected]/Three'
    meredith_handle = conn.get_contact_handle_sync(meredith_one)

    # Meredith signs in from one resource
    contact_online(q, conn, stream, meredith_one, PC, show='chat')

    # * One: chat: pc
    # ClientTypes should be: ['pc']

    # Meredith signs in from another resource
    contact_online(q,
                   conn,
                   stream,
                   meredith_two,
                   PHONE,
                   show='dnd',
                   initial=False)

    # * One: chat: pc
    # * Two: dnd: phone
    # ClientTypes should be: ['pc']

    # check we're still a PC
    types = get_client_types(conn, meredith_handle)

    assertLength(1, types)
    assertEquals('pc', types[0])

    types = conn.RequestClientTypes(meredith_handle,
                                    dbus_interface=cs.CONN_IFACE_CLIENT_TYPES)

    assertLength(1, types)
    assertEquals('pc', types[0])

    # Two now becomes more available
    stream.send(make_presence(meredith_two, show='chat'))

    # * One: chat: pc
    # * Two: chat: phone
    # ClientTypes should be: ['pc']

    types = get_client_types(conn, meredith_handle)
    assertEquals('pc', types[0])

    # One now becomes less available
    stream.send(make_presence(meredith_one, show='away'))

    # * One: away: pc
    # * Two: chat: phone
    # ClientTypes should be: ['phone']

    # wait for the presence change
    q.expect('dbus-signal',
             signal='PresencesChanged',
             args=[{
                 meredith_handle: (cs.PRESENCE_AVAILABLE, 'chat', '')
             }])

    # now wait for the change in client type
    event = q.expect('dbus-signal', signal='ClientTypesUpdated')
    assertEquals([meredith_handle, ['phone']], event.args)

    # make One more available again
    stream.send(make_presence(meredith_one, show='chat', status='lawl'))

    # * One: chat: pc
    # * Two: chat: phone
    # ClientTypes should be: ['pc']

    # wait for the presence change
    q.expect('dbus-signal',
             signal='PresencesChanged',
             args=[{
                 meredith_handle: (cs.PRESENCE_AVAILABLE, 'chat', 'lawl')
             }])

    # now wait for the change in client type
    event = q.expect('dbus-signal', signal='ClientTypesUpdated')
    assertEquals([meredith_handle, ['pc']], event.args)

    # both One and Two go away
    stream.send(make_presence(meredith_one, show='away'))

    # * One: away: pc
    # * Two: chat: phone
    # ClientTypes should be: ['phone']

    stream.send(make_presence(meredith_two, show='away'))

    # * One: away: pc
    # * Two: away: phone
    # ClientTypes should be: ['pc']

    # wait for the presence change
    q.expect('dbus-signal',
             signal='PresencesChanged',
             args=[{
                 meredith_handle: (cs.PRESENCE_AWAY, 'away', '')
             }])

    # check it still thinks we're a PC
    types = get_client_types(conn, meredith_handle)
    assertEquals('pc', types[0])

    # Three, with multiple identities, signs in
    identities = [PHONE[0], CONSOLE[0], HANDHELD[0], BOT[0]]
    contact_online(q,
                   conn,
                   stream,
                   meredith_three,
                   identities,
                   show='chat',
                   initial=False)

    # * One: away: pc
    # * Two: away: phone
    # * Three: chat: phone, console, handheld, bot
    # ClientTypes should be: ['phone', 'console', 'handheld', 'bot'] in some order

    # wait for the presence change
    q.expect('dbus-signal',
             signal='PresencesChanged',
             args=[{
                 meredith_handle: (cs.PRESENCE_AVAILABLE, 'chat', 'hello')
             }])

    # now wait for the change in client type
    event = q.expect('dbus-signal', signal='ClientTypesUpdated')
    assertEquals(meredith_handle, event.args[0])
    assertEquals(['bot', 'console', 'handheld', 'phone'],
                 sorted(event.args[1]))

    # that'll do
    #
    # ...
    #
    # wait wait! no it won't! Here's a regression test for
    # <https://bugs.freedesktop.org/show_bug.cgi?id=31772>.
    (caps, client, types) = build_stuff(TRANSIENT_PHONE)
    contact = '[email protected]/hai'
    send_presence(q, conn, stream, contact, caps)
    stanza = expect_disco(q, contact, client, caps)
    stream.send(make_presence(contact, type='unavailable'))
    send_disco_reply(stream, stanza, TRANSIENT_PHONE, [])

    # Gabble used to crash upon receiving a disco reply from a contact who's no
    # longer in the presence cache. So we sync here to check if it's died.
    sync_stream(q, stream)
def test(q, bus, conn, stream):
    event = q.expect('stream-iq', query_ns=ns.ROSTER)

    amy, bob, che, dre, eve = conn.get_contact_handles_sync([
        '*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**',
        '*****@*****.**'
    ])

    assertEquals(
        {
            amy: UNKNOWN,
            bob: UNKNOWN,
            che: UNKNOWN,
            dre: UNKNOWN,
            eve: UNKNOWN,
        }, get_contacts_presences_sync(conn, [amy, bob, che, dre, eve]))

    # Before the server sends Gabble the roster, it relays an 'unavailable'
    # presence for one of the contacts we're subscribed to. This seems to
    # happen in practice when using Prosody with a shared roster: the presence
    # probes start coming back negatively before the shared roster is retrieved
    # and returned to the client.
    stream.send(make_presence('*****@*****.**', type='unavailable'))

    # Dre's presence is still unknown, since we don't have the roster. This
    # isn't a change per se---we checked above, and Dre's presence was
    # unknown---so it shouldn't be signalled.
    q.forbid_events([
        EventPattern('dbus-signal',
                     signal='PresencesChanged',
                     args=[{
                         dre: UNKNOWN
                     }])
    ])

    # We also receive an available presence from Eve before the roster arrives:
    # this presence should behave normally.
    stream.send(make_presence('*****@*****.**'))
    q.expect('dbus-signal', signal='PresencesChanged', args=[{eve: AVAILABLE}])

    # We also get a message from a contact before we get the roster (presumably
    # they sent this while we were offline?). This shouldn't affect the contact
    # being reported as offline when we finally do get the roster, but it used
    # to: <https://bugs.freedesktop.org/show_bug.cgi?id=41743>.
    stream.send(
        elem('message', from_='*****@*****.**',
             type='chat')(elem('body')(u'why are you never online?')))
    q.expect('dbus-signal', signal='MessageReceived')

    event.stanza['type'] = 'result'
    event.query.addChild(make_roster_item('*****@*****.**', 'both'))
    event.query.addChild(make_roster_item('*****@*****.**', 'from'))
    event.query.addChild(make_roster_item('*****@*****.**', 'to'))
    event.query.addChild(make_roster_item('*****@*****.**', 'both'))
    event.query.addChild(make_roster_item('*****@*****.**', 'both'))
    stream.send(event.stanza)

    # The presence for contacts on the roster whose subscription is 'to' or
    # 'both' but for whom we haven't already received presence should change
    # from 'unknown' (as checked above) to 'offline'.
    e = q.expect('dbus-signal', signal='PresencesChanged')
    changed_presences, = e.args
    assertEquals({
        amy: OFFLINE,
        che: OFFLINE,
        dre: OFFLINE,
    }, changed_presences)

    assertEquals(
        {
            amy: OFFLINE,
            bob: UNKNOWN,
            che: OFFLINE,
            dre: OFFLINE,
            eve: AVAILABLE,
        }, get_contacts_presences_sync(conn, [amy, bob, che, dre, eve]))
 def send_remote_presence(self):
     presence = make_presence(self.remote_jid, self.local_jid,
         caps=self.remote_caps)
     self.stream.send(presence.toXml())
def test_two_clients(q, bus, conn, stream, contact1, contact2,
        contact_handle1, contact_handle2, client, broken_hash):
    global caps_changed_flag

    presence = make_presence(contact1, status='hello')
    stream.send(presence)

    event = q.expect_many(
        EventPattern('dbus-signal', signal='PresenceUpdate',
            args=[{contact_handle1:
                (0L, {u'available': {'message': 'hello'}})}]),
        EventPattern('dbus-signal', signal='PresencesChanged',
            args=[{contact_handle1:
                (2, u'available', 'hello')}]))

    presence = make_presence(contact2, status='hello')
    stream.send(presence)

    event = q.expect_many(
        EventPattern('dbus-signal', signal='PresenceUpdate',
            args=[{contact_handle2:
                (0L, {u'available': {'message': 'hello'}})}]),
        EventPattern('dbus-signal', signal='PresencesChanged',
            args=[{contact_handle2:
                (2, u'available', 'hello')}]))

    # no special capabilities
    basic_caps = [(contact_handle1, cs.CHANNEL_TYPE_TEXT, 3, 0)]
    assert conn.Capabilities.GetCapabilities([contact_handle1]) == basic_caps
    basic_caps = [(contact_handle2, cs.CHANNEL_TYPE_TEXT, 3, 0)]
    assert conn.Capabilities.GetCapabilities([contact_handle2]) == basic_caps

    # send updated presence with Jingle caps info
    ver = compute_caps_hash([], jingle_av_features, {})
    caps = {
        'node': client,
        'ver': ver,
        'hash': 'sha-1',
        }
    presence = make_presence(contact1, status='hello', caps=caps)
    stream.send(presence)
    presence = make_presence(contact2, status='hello', caps=caps)
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact1,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + ver

    # don't receive any D-Bus signal
    sync_dbus(bus, q, conn)
    assert caps_changed_flag == False

    result = make_caps_disco_reply(stream, event.stanza, jingle_av_features)

    if broken_hash:
        # make the hash break!
        query = result.firstChildElement()
        query.addElement('feature')['var'] = 'http://example.com/another-feature'

    stream.send(result)

    if broken_hash:
        # Gabble looks up our capabilities again because the first contact
        # failed to provide a valid hash
        event = q.expect('stream-iq', to=contact2,
            query_ns='http://jabber.org/protocol/disco#info')
        query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
        assert query_node.attributes['node'] == \
            client + '#' + ver

        # don't receive any D-Bus signal
        sync_dbus(bus, q, conn)
        assert caps_changed_flag == False

        # send good reply
        result = make_caps_disco_reply(stream, event.stanza, jingle_av_features)
        stream.send(result)

    # we can now do audio calls with both contacts
    event = q.expect('dbus-signal', signal='CapabilitiesChanged',
        args=[[(contact_handle2, cs.CHANNEL_TYPE_STREAMED_MEDIA, 0, 3, 0,
            cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO)]])
    if not broken_hash:
        # if the first contact failed to provide a good hash, it does not
        # deserve its capabilities to be understood by Gabble!
        event = q.expect('dbus-signal', signal='CapabilitiesChanged',
            args=[[(contact_handle1, cs.CHANNEL_TYPE_STREAMED_MEDIA, 0, 3, 0,
                cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO)]])

    caps_changed_flag = False

    # don't receive any D-Bus signal
    sync_dbus(bus, q, conn)
    assert caps_changed_flag == False
def test_ft_caps_from_contact(q, bus, conn, stream, contact, contact_handle,
                              client):

    conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACT_CAPS)
    conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS)

    # send presence with no FT cap
    presence = make_presence(contact, status='hello')
    c = presence.addElement((ns.CAPS, 'c'))
    c['node'] = client
    c['ver'] = compute_caps_hash([], [], {})
    c['hash'] = 'sha-1'
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO)
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + c['ver']

    # send good reply
    result = make_result_iq(stream, event.stanza)
    query = result.firstChildElement()
    query['node'] = client + '#' + c['ver']
    stream.send(result)

    # no change in ContactCapabilities, so no signal ContactCapabilitiesChanged
    sync_stream(q, stream)

    # no special capabilities
    basic_caps = dbus.Dictionary(
        {contact_handle: [(text_fixed_properties, text_allowed_properties)]})
    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assert caps == basic_caps, caps
    # test again, to check GetContactCapabilities does not have side effect
    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assert caps == basic_caps, caps
    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.ATTR_CONTACT_CAPABILITIES]
    assert caps_via_contacts_iface == caps[contact_handle], \
                                    caps_via_contacts_iface

    # send presence with ft capa
    presence = make_presence(contact, status='hello')
    c = presence.addElement((ns.CAPS, 'c'))
    c['node'] = client
    c['ver'] = compute_caps_hash([], [ns.FILE_TRANSFER], {})
    c['hash'] = 'sha-1'
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO)
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + c['ver']

    # send good reply
    result = make_result_iq(stream, event.stanza)
    query = result.firstChildElement()
    query['node'] = client + '#' + c['ver']
    feature = query.addElement('feature')
    feature['var'] = ns.FILE_TRANSFER
    stream.send(result)

    generic_tubes_caps = dbus.Dictionary({
        contact_handle: [(text_fixed_properties, text_allowed_properties),
                         (ft_fixed_properties, ft_allowed_properties)]
    })

    event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
    assert len(event.args) == 1
    assert event.args[0] == generic_tubes_caps

    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assert caps == generic_tubes_caps, caps
    # test again, to check GetContactCapabilities does not have side effect
    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assert caps == generic_tubes_caps, caps
    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.ATTR_CONTACT_CAPABILITIES]
    assert caps_via_contacts_iface == caps[contact_handle], \
                                    caps_via_contacts_iface
def test(q, bus, conn, stream):
    self_presence = q.expect('stream-presence')

    c = xpath.queryForNodes('/presence/c', self_presence.stanza)[0]

    jid = '[email protected]/omg'

    # Gabble shouldn't send any disco requests to our contact during this test.
    q.forbid_events([
        EventPattern('stream-iq',
                     to=jid,
                     iq_type='get',
                     query_ns=ns.DISCO_INFO),
    ])

    # Check that Gabble doesn't disco other clients with the same caps hash.
    p = make_presence(jid,
                      caps={
                          'node': c['node'],
                          'hash': c['hash'],
                          'ver': c['ver'],
                      })
    stream.send(p)
    sync_stream(q, stream)

    # Check that Gabble doesn't disco its own ext='' bundles (well, its own
    # bundles as advertised by Gabbles that don't do hashed caps)
    p = make_presence(
        jid,
        caps={
            'node': c['node'],
            'ver': c['ver'],
            # omitting hash='' so Gabble doesn't ignore ext=''
            'ext': 'voice-v1 video-v1',
        })
    stream.send(p)
    sync_stream(q, stream)

    conn.ContactCapabilities.UpdateCapabilities([
        (cs.CLIENT + '.AbiWord', [
            {
                cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE,
                cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
                cs.STREAM_TUBE_SERVICE: 'x-abiword'
            },
            {
                cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE,
                cs.TARGET_HANDLE_TYPE: cs.HT_ROOM,
                cs.STREAM_TUBE_SERVICE: 'x-abiword'
            },
        ], []),
        (cs.CLIENT + '.KCall', [
            {
                cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL
            },
            {
                cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL,
                cs.CALL_INITIAL_AUDIO: True
            },
            {
                cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL,
                cs.CALL_INITIAL_VIDEO: True
            },
        ], [
            cs.CHANNEL_TYPE_CALL + '/gtalk-p2p',
            cs.CHANNEL_TYPE_CALL + '/ice-udp',
            cs.CHANNEL_TYPE_CALL + '/video/h264',
        ]),
    ])

    self_presence = q.expect('stream-presence')
    c_ = xpath.queryForNodes('/presence/c', self_presence.stanza)[0]
    assertNotEquals(c['ver'], c_['ver'])

    for suffix in [
            c['ver'], 'voice-v1', 'video-v1', 'camera-v1', 'share-v1',
            'pmuc-v1'
    ] + list(c_['ext'].split()):
        # But then someone asks us for our old caps
        iq = IQ(stream, 'get')
        iq['from'] = jid
        query = iq.addElement((ns.DISCO_INFO, 'query'))
        query['node'] = c['node'] + '#' + suffix
        stream.send(iq)

        # Gabble should still know what they are, and reply. This is
        # actually quite important: there's a bug in iChat where if you
        # return an error to a disco query, it just asks again, and again,
        # and again...
        reply = q.expect('stream-iq', to=jid)
        assertEquals('result', reply.iq_type)
Exemple #35
0
def test(q, bus, conn, stream):
    caps = {
        'node': client,
        'ver':  '0.1',
        }

    update_contact_caps(q, conn, stream, '[email protected]/Foo', caps)
    update_contact_caps(q, conn, stream, '[email protected]/Foo', caps)

    # Meredith signs in from one resource.
    update_contact_caps(q, conn, stream, '[email protected]/One', caps)
    # Meredith signs in from another resource with the same client. We don't
    # need to disco her, even though we don't trust this caps node in general
    # yet, because she's already told us what it means.
    meredith_two = '[email protected]/Two'
    q.forbid_events([
        EventPattern('stream-iq', to=meredith_two, query_ns=ns.DISCO_INFO)
        ])
    stream.send(make_presence(meredith_two, 'hello', caps=caps))
    sync_stream(q, stream)

    # Jens signs in from one resource, which is slow to answer the disco query.
    jens_one = '[email protected]/One'
    j = send_presence(q, conn, stream, jens_one, caps)
    j_stanza = expect_disco(q, jens_one, client, caps)

    # Jens now signs in elsewhere with the same client; we disco it (maybe
    # it'll reply sooner? Maybe his first client's network connection went away
    # and the server hasn't noticed yet?) and it replies immediately.
    update_contact_caps (q, conn, stream, '[email protected]/Two', caps,
        initial=False)

    # Jens' first client replies. We don't expect any caps changes here, and
    # this shouldn't count as a second point towards the five we need to trust
    # this caps node.
    send_disco_reply(stream, j_stanza, [], features)
    check_caps (conn, j)

    update_contact_caps (q, conn, stream, '[email protected]/Foo', caps)

    # Now five distinct contacts have told us what this caps node means, we
    # trust it.
    update_contact_caps (q, conn, stream, '[email protected]/Foo', caps,
        disco = False)
    update_contact_caps (q, conn, stream, '[email protected]/Foo', caps,
        disco = False)

    caps = {
        'node': client,
        'ver':  compute_caps_hash([], features, fake_client_dataforms),
        'hash': 'sha-1',
        }

    update_contact_caps(q, conn, stream, '[email protected]/Foo',
       caps,  dataforms = fake_client_dataforms)
    # We can verify the reply for these caps against the hash, and thus never
    # need to disco it again.
    update_contact_caps(q, conn, stream, '[email protected]/Foo', caps,
        disco = False, dataforms = fake_client_dataforms)
    update_contact_caps(q, conn, stream, '[email protected]/Foo', caps,
        disco = False, dataforms = fake_client_dataforms)
def test(q, bus, conn, stream):
    self_handle = conn.GetSelfHandle()

    jid = '*****@*****.**'
    full_jid = '[email protected]/Foo'
    foo_handle = conn.RequestHandles(cs.HT_CONTACT, [jid])[0]

    path = conn.Requests.CreateChannel(
            { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
              cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
              cs.TARGET_HANDLE: foo_handle,
              })[0]
    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
        ['ChatState', 'Destroyable'])

    presence = make_presence(full_jid, status='hello',
        caps={
            'node': 'http://telepathy.freedesktop.org/homeopathy',
            'ver' : '0.1',
        })
    stream.send(presence)

    version_event = q.expect('stream-iq', to=full_jid,
        query_ns=ns.DISCO_INFO,
        query_node='http://telepathy.freedesktop.org/homeopathy#0.1')

    result = make_result_iq(stream, version_event.stanza)
    query = result.firstChildElement()
    feature = query.addElement('feature')
    feature['var'] = ns.CHAT_STATES
    stream.send(result)

    sync_stream(q, stream)

    # Receiving chat states:

    # Composing...
    stream.send(make_message(full_jid, state='composing'))

    changed = q.expect('dbus-signal', signal='ChatStateChanged')
    handle, state = changed.args
    assertEquals(foo_handle, handle)
    assertEquals(cs.CHAT_STATE_COMPOSING, state)

    # Message!
    stream.send(make_message(full_jid, body='hello', state='active'))

    changed = q.expect('dbus-signal', signal='ChatStateChanged')
    handle, state = changed.args
    assertEquals(foo_handle, handle)
    assertEquals(cs.CHAT_STATE_ACTIVE, state)

    # Sending chat states:

    # Composing...
    chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING)

    stream_message = q.expect('stream-message')
    check_state_notification(stream_message.stanza, 'composing')

    # XEP 0085:
    #   every content message SHOULD contain an <active/> notification.
    chan.Text.Send(0, 'hi.')

    stream_message = q.expect('stream-message')
    elem = stream_message.stanza
    assertEquals('chat', elem['type'])

    check_state_notification(elem, 'active', allow_body=True)

    def is_body(e):
        if e.name == 'body':
            assert e.children[0] == u'hi.', e.toXml()
            return True
        return False

    assert len([x for x in elem.elements() if is_body(x)]) == 1, elem.toXml()

    # Close the channel without acking the received message. The peer should
    # get a <gone/> notification, and the channel should respawn.
    chan.Close()

    gone, _, _ = q.expect_many(
        EventPattern('stream-message'),
        EventPattern('dbus-signal', signal='Closed'),
        EventPattern('dbus-signal', signal='NewChannel'),
        )
    check_state_notification(gone.stanza, 'gone')

    # Reusing the proxy object because we happen to know it'll be at the same
    # path...

    # Destroy the channel. The peer shouldn't get a <gone/> notification, since
    # we already said we were gone and haven't sent them any messages to the
    # contrary.
    es = [EventPattern('stream-message')]
    q.forbid_events(es)

    chan.Destroyable.Destroy()
    sync_stream(q, stream)

    # Make the channel anew.
    path = conn.Requests.CreateChannel(
            { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
              cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
              cs.TARGET_HANDLE: foo_handle,
              })[0]
    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
        ['ChatState', 'Destroyable'])

    # Close it immediately; the peer should again not get a <gone/>
    # notification, since we haven't sent any notifications on that channel.
    chan.Close()
    sync_stream(q, stream)
    q.unforbid_events(es)

    # XEP-0085 §5.1 defines how to negotiate support for chat states with a
    # contact in the absence of capabilities. This is useful when talking to
    # invisible contacts, for example.

    # First, if we receive a message from a contact, containing an <active/>
    # notification, they support chat states, so we should send them.

    jid = '*****@*****.**'
    full_jid = jid + '/GTalk'

    path = conn.Requests.CreateChannel(
            { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
              cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
              cs.TARGET_ID: jid,
              })[0]
    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
        ['ChatState'])

    stream.send(make_message(full_jid, body='i am invisible', state='active'))

    changed = q.expect('dbus-signal', signal='ChatStateChanged')
    assertEquals(cs.CHAT_STATE_ACTIVE, changed.args[1])

    # We've seen them send a chat state notification, so we should send them
    # notifications when the UI tells us to.
    chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING)
    stream_message = q.expect('stream-message', to=full_jid)
    check_state_notification(stream_message.stanza, 'composing')

    changed = q.expect('dbus-signal', signal='ChatStateChanged')
    handle, state = changed.args
    assertEquals(cs.CHAT_STATE_COMPOSING, state)
    assertEquals(self_handle, handle)

    chan.Text.Send(0, 'very convincing')
    stream_message = q.expect('stream-message', to=full_jid)
    check_state_notification(stream_message.stanza, 'active', allow_body=True)

    # Now, test the case where we start the negotiation, and the contact
    # turns out to support chat state notifications.

    jid = '*****@*****.**'
    full_jid = jid + '/GTalk'
    path = conn.Requests.CreateChannel(
            { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
              cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
              cs.TARGET_ID: jid,
              })[0]
    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
        ['ChatState'])

    # We shouldn't send any notifications until we actually send a message.
    e = EventPattern('stream-message', to=jid)
    q.forbid_events([e])
    for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE,
              cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]:
        chan.ChatState.SetChatState(i)
    sync_stream(q, stream)
    q.unforbid_events([e])

    # When we send a message, say we're active.
    chan.Text.Send(0, 'is anyone there?')
    stream_message = q.expect('stream-message', to=jid)
    check_state_notification(stream_message.stanza, 'active', allow_body=True)

    # We get a notification back from our contact.
    stream.send(make_message(full_jid, state='composing'))

    # Wait until gabble tells us the chat-state of the remote party has
    # changed so we know gabble knows chat state notification are supported
    changed = q.expect('dbus-signal', signal='ChatStateChanged')
    handle, state = changed.args
    assertEquals(cs.CHAT_STATE_COMPOSING, state)
    assertNotEquals(self_handle, handle)

    # So now we know they support notification, so should send notifications.
    chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING)

    # This doesn't check whether we're sending to the bare jid, or the
    # jid+resource. In fact, the notification is sent to the bare jid, because
    # we only update which jid we send to when we actually receive a message,
    # not when we receive a notification. wjt thinks this is less surprising
    # than the alternative:
    #
    #  • I'm talking to you on my N900, and signed in on my laptop;
    #  • I enter one character in a tab to you on my laptop, and then delete
    #    it;
    #  • Now your messages to me appear on my laptop (until I send you another
    #    one from my N900)!
    stream_message = q.expect('stream-message')
    check_state_notification(stream_message.stanza, 'composing')

    # But! Now they start messaging us from a different client, which *doesn't*
    # support notifications.
    other_jid = jid + '/Library'
    stream.send(make_message(other_jid, body='grr, library computers'))
    q.expect('dbus-signal', signal='Received')

    # Okay, we should stop sending typing notifications.
    e = EventPattern('stream-message', to=other_jid)
    q.forbid_events([e])
    for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE,
              cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]:
        chan.ChatState.SetChatState(i)
    sync_stream(q, stream)
    q.unforbid_events([e])

    # Now, test the case where we start the negotiation, and the contact
    # does not support chat state notifications

    jid = '*****@*****.**'
    full_jid = jid + '/Nonsense'
    path = conn.Requests.CreateChannel(
            { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
              cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
              cs.TARGET_ID: jid,
              })[0]
    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
        ['ChatState'])

    # We shouldn't send any notifications until we actually send a message.
    e = EventPattern('stream-message', to=jid)
    q.forbid_events([e])
    for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE,
              cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]:
        chan.ChatState.SetChatState(i)
    sync_stream(q, stream)
    q.unforbid_events([e])

    # When we send a message, say we're active.
    chan.Text.Send(0, '#n900 #maemo #zomg #woo #yay http://bit.ly/n900')
    stream_message = q.expect('stream-message', to=jid)
    check_state_notification(stream_message.stanza, 'active', allow_body=True)

    # They reply without a chat state.
    stream.send(make_message(full_jid, body="posted."))
    q.expect('dbus-signal', signal='Received')

    # Okay, we shouldn't send any more.
    e = EventPattern('stream-message', to=other_jid)
    q.forbid_events([e])
    for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE,
              cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]:
        chan.ChatState.SetChatState(i)
    sync_stream(q, stream)
    q.unforbid_events([e])

    chan.Text.Send(0, '@stephenfry simmer down')
    message = q.expect('stream-message')
    states = [x for x in message.stanza.elements() if x.uri == ns.CHAT_STATES]
    assertLength(0, states)
def test_hash(q, bus, conn, stream, contact, contact_handle, client):
    global caps_changed_flag

    presence = make_presence(contact, status='hello')
    stream.send(presence)

    q.expect_many(
        EventPattern('dbus-signal', signal='PresenceUpdate',
            args=[{contact_handle:
                (0L, {u'available': {'message': 'hello'}})}]),
        EventPattern('dbus-signal', signal='PresencesChanged',
            args=[{contact_handle:
                (2, u'available', 'hello')}]))

    # no special capabilities
    basic_caps = [(contact_handle, cs.CHANNEL_TYPE_TEXT, 3, 0)]
    assert conn.Capabilities.GetCapabilities([contact_handle]) == basic_caps

    # send updated presence with Jingle caps info
    presence = make_presence(contact, status='hello',
        caps={'node': client,
              'ver':  '0.1',
             })
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + '0.1'

    # send good reply
    stream.send(make_caps_disco_reply(stream, event.stanza,
        jingle_av_features))

    # we can now do audio calls
    event = q.expect('dbus-signal', signal='CapabilitiesChanged')
    caps_changed_flag = False

    # send bogus presence
    caps = {
        'node': client,
        'ver':  'ceci=nest=pas=un=hash',
        'hash': 'sha-1',
        }
    presence = make_presence(contact, status='hello', caps=caps)
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + caps['ver']

    # send bogus reply
    stream.send(make_caps_disco_reply(stream, event.stanza,
        ['http://jabber.org/protocol/bogus-feature']))

    # don't receive any D-Bus signal
    sync_dbus(bus, q, conn)
    sync_stream(q, stream)
    assert caps_changed_flag == False


    # send presence with empty caps
    presence = make_presence(contact, status='hello',
        caps={'node': client,
              'ver':  '0.0',
             })
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + '0.0'

    # still don't receive any D-Bus signal
    sync_dbus(bus, q, conn)
    assert caps_changed_flag == False

    # send good reply
    result = make_result_iq(stream, event.stanza)
    query = result.firstChildElement()
    stream.send(result)

    # we can now do nothing
    event = q.expect('dbus-signal', signal='CapabilitiesChanged')
    assert caps_changed_flag == True
    caps_changed_flag = False


    # send correct presence
    ver = compute_caps_hash([], jingle_av_features, fake_client_dataforms)
    caps = {
        'node': client,
        'ver':  ver,
        'hash': 'sha-1',
        }
    presence = make_presence(contact, status='hello', caps=caps)
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + caps['ver']

    # don't receive any D-Bus signal
    sync_dbus(bus, q, conn)
    assert caps_changed_flag == False

    # send good reply
    result = make_caps_disco_reply(stream, event.stanza, jingle_av_features,
        fake_client_dataforms)
    stream.send(result)

    # we can now do audio calls
    event = q.expect('dbus-signal', signal='CapabilitiesChanged',
    )
    assert caps_changed_flag == True
    caps_changed_flag = False
def test(q, bus, conn, stream):
    client = 'http://example.com/perverse-client'
    contact_bare_jid = '*****@*****.**'
    contact_with_resource = '[email protected]/hi'
    contact_handle = conn.get_contact_handle_sync(contact_bare_jid)

    # Gabble gets a presence stanza from a bare JID, which is a tad surprising.
    features = [
        ns.JINGLE_015,
        ns.JINGLE_015_AUDIO,
        ns.JINGLE_015_VIDEO,
        ns.GOOGLE_P2P,
    ]
    caps = {
        'node': client,
        'hash': 'sha-1',
        'ver': compute_caps_hash([], features, {}),
    }
    p = make_presence(contact_bare_jid, status='Hello', caps=caps)
    stream.send(p)

    # Gabble looks up the hash
    event = q.expect('stream-iq',
                     to=contact_bare_jid,
                     query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assertEquals(client + '#' + caps['ver'], query_node.attributes['node'])

    # The bare jid replies
    send_disco_reply(stream, event.stanza, [], features)

    # Gabble lets us know their caps have changed. (Gabble used to ignore the
    # reply.)
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), )
    assert_rccs_callable(cc.args[0][contact_handle])

    # Gabble gets another presence stanza from the bare JID, with different
    # caps.
    features.append(ns.TUBES)
    caps = {
        'node': client,
        'hash': 'sha-1',
        'ver': compute_caps_hash([], features, {}),
    }
    p = make_presence(contact_bare_jid, status='Get out the abacus', caps=caps)
    stream.send(p)

    # Gabble looks up the new hash
    disco2 = q.expect('stream-iq',
                      to=contact_bare_jid,
                      query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', disco2.stanza)[0]
    assertEquals(client + '#' + caps['ver'], query_node.attributes['node'])

    # This time, before the bare JID replies, Gabble gets a presence from the
    # resourceful jid.
    features_ = features + [ns.CHAT_STATES]
    caps = {
        'node': client,
        'hash': 'sha-1',
        'ver': compute_caps_hash([], features_, {}),
    }
    p = make_presence(contact_with_resource, status='Count this', caps=caps)
    stream.send(p)

    # Gabble throws away presence from the bare JID when it gets presence from
    # a resource (and vice versa), so it should now say the contact is
    # incapable.  Gabble also looks up the resourceful JID's hash.
    cc, disco3 = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
        EventPattern('stream-iq',
                     to=contact_with_resource,
                     query_ns='http://jabber.org/protocol/disco#info'),
    )

    assert_rccs_not_callable(cc.args[0][contact_handle])

    query_node = xpath.queryForNodes('/iq/query', disco3.stanza)[0]
    assertEquals(client + '#' + caps['ver'], query_node.attributes['node'])

    # The bare jid replies! Getting a disco reply from a bare JID when we've
    # got presence from resources used to crash Gabble, but now it just ignores
    # it.
    send_disco_reply(stream, disco2.stanza, [], features)

    # Now the resourceful JID replies:
    send_disco_reply(stream, disco3.stanza, [], features_)

    # Gabble should announce that the contact has acquired some caps.
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), )
    assert_rccs_callable(cc.args[0][contact_handle])
def test(q, bus, conn, stream):
    bob = conn.get_contact_handle_sync('*****@*****.**')

    presence = make_presence('[email protected]/Foo', status='hello')
    stream.send(presence)

    q.expect('dbus-signal', signal='PresencesChanged',
           args=[{bob: (cs.PRESENCE_AVAILABLE, u'available', 'hello')}])

    basic_caps = [(bob, cs.CHANNEL_TYPE_TEXT, 3, 0)]

    # only Text
    for rcc in get_contacts_capabilities_sync(conn, [bob])[bob]:
        assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE))

    # holding the handle here: see below
    assertEquals(
            { bob: {
                cs.ATTR_CONTACT_CAPABILITIES:
                    get_contacts_capabilities_sync(conn, [bob])[bob],
                cs.CONN + '/contact-id': '*****@*****.**',
                },
            },
            conn.Contacts.GetContactAttributes([bob], [cs.CONN_IFACE_CONTACT_CAPS], True))

    # send updated presence with Jingle audio/video caps info. we turn on both
    # audio and video at the same time to test that all of the capabilities are
    # discovered before any capabilities change signal is emitted
    presence = make_presence('[email protected]/Foo', status='hello',
        caps={
            'node': 'http://telepathy.freedesktop.org/fake-client',
            'ver' : '0.1',
            'ext' : 'video',
        })
    stream.send(presence)

    # Gabble looks up both the version and the video bundles, in any order
    (version_event, video_event) = q.expect_many(
        EventPattern('stream-iq', to='[email protected]/Foo',
            query_ns='http://jabber.org/protocol/disco#info',
            query_node='http://telepathy.freedesktop.org/fake-client#0.1'),
        EventPattern('stream-iq', to='[email protected]/Foo',
            query_ns='http://jabber.org/protocol/disco#info',
            query_node='http://telepathy.freedesktop.org/fake-client#video'))

    # reply to the video bundle query first - this capability alone is not
    # sufficient to make us callable
    result = make_result_iq(stream, video_event.stanza)
    query = result.firstChildElement()
    feature = query.addElement('feature')
    feature['var'] = 'http://jabber.org/protocol/jingle/description/video'
    stream.send(result)

    # reply to the version bundle query, which should make us audio and
    # video callable
    result = make_result_iq(stream, version_event.stanza)
    query = result.firstChildElement()
    feature = query.addElement('feature')
    feature['var'] = 'http://jabber.org/protocol/jingle'
    feature = query.addElement('feature')
    feature['var'] = 'http://jabber.org/protocol/jingle/description/audio'
    feature = query.addElement('feature')
    feature['var'] = 'http://www.google.com/transport/p2p'
    stream.send(result)

    # we can now do audio and video calls
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged',
            predicate=lambda e: check_rccs_callable(e.args[0][bob])),
        )
    assert_rccs_callable(cc.args[0][bob], require_video=True,
            mutable_contents=True)

    assertEquals(
            { bob: {
                cs.ATTR_CONTACT_CAPABILITIES:
                    cc.args[0][bob],
                cs.CONN + '/contact-id': '*****@*****.**',
                },
            },
            conn.Contacts.GetContactAttributes([bob],
                [cs.CONN_IFACE_CONTACT_CAPS], True))

    # send updated presence without video support
    presence = make_presence('[email protected]/Foo', status='hello',
        caps={
            'node': 'http://telepathy.freedesktop.org/fake-client',
            'ver' : '0.1',
        })
    stream.send(presence)

    # we can now do only audio calls (and as a result have the ImmutableStreams
    # cap)
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
        )
    assert_rccs_callable(cc.args[0][bob])
    assert_rccs_not_callable(cc.args[0][bob], require_audio=False,
            require_video=True, mutable_contents=False)

    assertEquals(
            { bob: {
                cs.ATTR_CONTACT_CAPABILITIES:
                    cc.args[0][bob],
                cs.CONN + '/contact-id': '*****@*****.**',
                },
            },
            conn.Contacts.GetContactAttributes([bob],
                [cs.CONN_IFACE_CONTACT_CAPS], True))

    # go offline
    presence = make_presence('[email protected]/Foo', type='unavailable')
    stream.send(presence)

    # can't do audio calls any more
    q.expect_many(
            EventPattern('dbus-signal', signal='PresencesChanged',
                args=[{bob: (cs.PRESENCE_OFFLINE, 'offline', '')}]),
            EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
            )

    # Contact went offline. Previously, this test asserted that the handle
    # became invalid, but that's not guaranteed to happen immediately; so we
    # now hold the handle (above), to guarantee that it does *not* become
    # invalid.
    rccs = get_contacts_capabilities_sync(conn, [bob])[bob]
    for rcc in rccs:
        assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE))

    assertEquals(
            { bob: {
                cs.ATTR_CONTACT_CAPABILITIES: rccs,
                cs.CONN + '/contact-id': '*****@*****.**',
                },
            },
            conn.Contacts.GetContactAttributes([bob],
                [cs.CONN_IFACE_CONTACT_CAPS], True))

    # What about a handle that's not valid?
    assertEquals({}, conn.Contacts.GetContactAttributes(
        [31337], [cs.CONN_IFACE_CONTACT_CAPS], False))
def test(q, bus, conn, stream):
    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")

    jid = '*****@*****.**'
    full_jid = '[email protected]/Foo'
    foo_handle = conn.get_contact_handle_sync(jid)

    path = conn.Requests.CreateChannel({
        cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
        cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
        cs.TARGET_HANDLE: foo_handle,
    })[0]
    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text')

    presence = make_presence(full_jid,
                             status='hello',
                             caps={
                                 'node':
                                 'http://telepathy.freedesktop.org/homeopathy',
                                 'ver': '0.1',
                             })
    stream.send(presence)

    version_event = q.expect(
        'stream-iq',
        to=full_jid,
        query_ns=ns.DISCO_INFO,
        query_node='http://telepathy.freedesktop.org/homeopathy#0.1')

    result = make_result_iq(stream, version_event.stanza)
    query = result.firstChildElement()
    feature = query.addElement('feature')
    feature['var'] = ns.CHAT_STATES
    stream.send(result)

    sync_stream(q, stream)

    states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates')
    assertEquals(cs.CHAT_STATE_INACTIVE,
                 states.get(self_handle, cs.CHAT_STATE_INACTIVE))
    assertEquals(cs.CHAT_STATE_INACTIVE,
                 states.get(foo_handle, cs.CHAT_STATE_INACTIVE))

    # Receiving chat states:

    # Composing...
    stream.send(make_message(full_jid, state='composing'))

    changed = q.expect('dbus-signal',
                       signal='ChatStateChanged',
                       path=chan.object_path)
    handle, state = changed.args
    assertEquals(foo_handle, handle)
    assertEquals(cs.CHAT_STATE_COMPOSING, state)

    states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates')
    assertEquals(cs.CHAT_STATE_INACTIVE,
                 states.get(self_handle, cs.CHAT_STATE_INACTIVE))
    assertEquals(cs.CHAT_STATE_COMPOSING,
                 states.get(foo_handle, cs.CHAT_STATE_INACTIVE))

    # Message!
    stream.send(make_message(full_jid, body='hello', state='active'))

    changed = q.expect('dbus-signal',
                       signal='ChatStateChanged',
                       path=chan.object_path)
    handle, state = changed.args
    assertEquals(foo_handle, handle)
    assertEquals(cs.CHAT_STATE_ACTIVE, state)

    states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates')
    assertEquals(cs.CHAT_STATE_INACTIVE,
                 states.get(self_handle, cs.CHAT_STATE_INACTIVE))
    assertEquals(cs.CHAT_STATE_ACTIVE,
                 states.get(foo_handle, cs.CHAT_STATE_INACTIVE))

    # Assert that a redundant chat-state change doesn't emit a signal

    forbidden = [
        EventPattern('dbus-signal',
                     signal='ChatStateChanged',
                     args=[foo_handle, cs.CHAT_STATE_ACTIVE])
    ]
    q.forbid_events(forbidden)

    m = domish.Element((None, 'message'))
    m['from'] = '[email protected]/Foo'
    m['type'] = 'chat'
    m.addElement((ns.CHAT_STATES, 'active'))
    m.addElement('body', content='hello')
    stream.send(m)

    sync_dbus(bus, q, conn)
    sync_stream(q, stream)

    q.unforbid_events(forbidden)

    # Sending chat states:

    # Composing...
    chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING)

    stream_message = q.expect('stream-message')
    check_state_notification(stream_message.stanza, 'composing')

    states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates')
    assertEquals(cs.CHAT_STATE_COMPOSING,
                 states.get(self_handle, cs.CHAT_STATE_INACTIVE))
    assertEquals(cs.CHAT_STATE_ACTIVE,
                 states.get(foo_handle, cs.CHAT_STATE_INACTIVE))

    # XEP 0085:
    #   every content message SHOULD contain an <active/> notification.
    chan.send_msg_sync('hi.')

    stream_message = q.expect('stream-message')
    elem = stream_message.stanza
    assertEquals('chat', elem['type'])

    check_state_notification(elem, 'active', allow_body=True)

    states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates')
    assertEquals(cs.CHAT_STATE_ACTIVE,
                 states.get(self_handle, cs.CHAT_STATE_INACTIVE))
    assertEquals(cs.CHAT_STATE_ACTIVE,
                 states.get(foo_handle, cs.CHAT_STATE_INACTIVE))

    def is_body(e):
        if e.name == 'body':
            assert e.children[0] == u'hi.', e.toXml()
            return True
        return False

    assert len([x for x in elem.elements() if is_body(x)]) == 1, elem.toXml()

    # Close the channel without acking the received message. The peer should
    # get a <gone/> notification, and the channel should respawn.
    chan.Close()

    gone, _ = q.expect_many(
        EventPattern('stream-message'),
        EventPattern('dbus-signal', signal='Closed'),
    )
    check_state_notification(gone.stanza, 'gone')

    # Reusing the proxy object because we happen to know it'll be at the same
    # path...

    # Destroy the channel. The peer shouldn't get a <gone/> notification, since
    # we already said we were gone and haven't sent them any messages to the
    # contrary.
    es = [EventPattern('stream-message')]
    q.forbid_events(es)

    chan.Destroyable.Destroy()
    sync_stream(q, stream)

    # Make the channel anew.
    path = conn.Requests.CreateChannel({
        cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
        cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
        cs.TARGET_HANDLE: foo_handle,
    })[0]
    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text')

    # Close it immediately; the peer should again not get a <gone/>
    # notification, since we haven't sent any notifications on that channel.
    chan.Close()
    sync_stream(q, stream)
    q.unforbid_events(es)

    # XEP-0085 §5.1 defines how to negotiate support for chat states with a
    # contact in the absence of capabilities. This is useful when talking to
    # invisible contacts, for example.

    # First, if we receive a message from a contact, containing an <active/>
    # notification, they support chat states, so we should send them.

    jid = '*****@*****.**'
    full_jid = jid + '/GTalk'

    path = conn.Requests.CreateChannel({
        cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
        cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
        cs.TARGET_ID: jid,
    })[0]
    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text')

    stream.send(make_message(full_jid, body='i am invisible', state='active'))

    changed = q.expect('dbus-signal',
                       signal='ChatStateChanged',
                       path=chan.object_path)
    assertEquals(cs.CHAT_STATE_ACTIVE, changed.args[1])

    # We've seen them send a chat state notification, so we should send them
    # notifications when the UI tells us to.
    chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING)
    stream_message = q.expect('stream-message', to=full_jid)
    check_state_notification(stream_message.stanza, 'composing')

    changed = q.expect('dbus-signal',
                       signal='ChatStateChanged',
                       path=chan.object_path)
    handle, state = changed.args
    assertEquals(cs.CHAT_STATE_COMPOSING, state)
    assertEquals(self_handle, handle)

    chan.send_msg_sync('very convincing')
    stream_message = q.expect('stream-message', to=full_jid)
    check_state_notification(stream_message.stanza, 'active', allow_body=True)

    # Now, test the case where we start the negotiation, and the contact
    # turns out to support chat state notifications.

    jid = '*****@*****.**'
    full_jid = jid + '/GTalk'
    path = conn.Requests.CreateChannel({
        cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
        cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
        cs.TARGET_ID: jid,
    })[0]
    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text')

    # We shouldn't send any notifications until we actually send a message.
    # But ChatStateChanged is still emitted locally
    e = EventPattern('stream-message', to=jid)
    q.forbid_events([e])
    for i in [
            cs.CHAT_STATE_ACTIVE, cs.CHAT_STATE_COMPOSING,
            cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_INACTIVE
    ]:
        chan.ChatState.SetChatState(i)
        changed = q.expect('dbus-signal',
                           signal='ChatStateChanged',
                           path=chan.object_path)
        handle, state = changed.args
        assertEquals(i, state)
        assertEquals(self_handle, handle)

    sync_stream(q, stream)
    q.unforbid_events([e])

    # When we send a message, say we're active.
    chan.send_msg_sync('is anyone there?')
    stream_message = q.expect('stream-message', to=jid)
    check_state_notification(stream_message.stanza, 'active', allow_body=True)

    # The D-Bus property changes, too
    changed = q.expect('dbus-signal',
                       signal='ChatStateChanged',
                       path=chan.object_path)
    handle, state = changed.args
    assertEquals(cs.CHAT_STATE_ACTIVE, state)
    assertEquals(self_handle, handle)

    # We get a notification back from our contact.
    stream.send(make_message(full_jid, state='composing'))

    # Wait until gabble tells us the chat-state of the remote party has
    # changed so we know gabble knows chat state notification are supported
    changed = q.expect('dbus-signal',
                       signal='ChatStateChanged',
                       path=chan.object_path)
    handle, state = changed.args
    assertEquals(cs.CHAT_STATE_COMPOSING, state)
    assertNotEquals(foo_handle, handle)

    # So now we know they support notification, so should send notifications.
    chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING)

    # This doesn't check whether we're sending to the bare jid, or the
    # jid+resource. In fact, the notification is sent to the bare jid, because
    # we only update which jid we send to when we actually receive a message,
    # not when we receive a notification. wjt thinks this is less surprising
    # than the alternative:
    #
    #  • I'm talking to you on my N900, and signed in on my laptop;
    #  • I enter one character in a tab to you on my laptop, and then delete
    #    it;
    #  • Now your messages to me appear on my laptop (until I send you another
    #    one from my N900)!
    stream_message = q.expect('stream-message')
    check_state_notification(stream_message.stanza, 'composing')

    # The D-Bus property changes, too
    changed = q.expect('dbus-signal',
                       signal='ChatStateChanged',
                       path=chan.object_path)
    handle, state = changed.args
    assertEquals(cs.CHAT_STATE_COMPOSING, state)
    assertEquals(self_handle, handle)

    # But! Now they start messaging us from a different client, which *doesn't*
    # support notifications.
    other_jid = jid + '/Library'
    stream.send(make_message(other_jid, body='grr, library computers'))
    q.expect('dbus-signal', signal='MessageReceived')

    # Okay, we should stop sending typing notifications.
    e = EventPattern('stream-message', to=other_jid)
    q.forbid_events([e])
    for i in [
            cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE,
            cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE
    ]:
        chan.ChatState.SetChatState(i)
    sync_stream(q, stream)
    q.unforbid_events([e])

    # Now, test the case where we start the negotiation, and the contact
    # does not support chat state notifications

    jid = '*****@*****.**'
    full_jid = jid + '/Nonsense'
    path = conn.Requests.CreateChannel({
        cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
        cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
        cs.TARGET_ID: jid,
    })[0]
    chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text')

    # We shouldn't send any notifications until we actually send a message.
    e = EventPattern('stream-message', to=jid)
    q.forbid_events([e])
    for i in [
            cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE,
            cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE
    ]:
        chan.ChatState.SetChatState(i)
    sync_stream(q, stream)
    q.unforbid_events([e])

    # When we send a message, say we're active.
    chan.send_msg_sync('#n900 #maemo #zomg #woo #yay http://bit.ly/n900')
    stream_message = q.expect('stream-message', to=jid)
    check_state_notification(stream_message.stanza, 'active', allow_body=True)

    # They reply without a chat state.
    stream.send(make_message(full_jid, body="posted."))
    q.expect('dbus-signal', signal='MessageReceived')

    # Okay, we shouldn't send any more.
    e = EventPattern('stream-message', to=other_jid)
    q.forbid_events([e])
    for i in [
            cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE,
            cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE
    ]:
        chan.ChatState.SetChatState(i)
    sync_stream(q, stream)
    q.unforbid_events([e])

    chan.send_msg_sync('@stephenfry simmer down')
    message = q.expect('stream-message')
    states = [x for x in message.stanza.elements() if x.uri == ns.CHAT_STATES]
    assertLength(0, states)
def test(q, bus, conn, stream):
    iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp',
            query_name='vCard')

    # When we start, there is no avatar
    acknowledge_iq(stream, iq_event.stanza)
    self_handle = conn.GetSelfHandle()

    # Another resource confirms we have no avatar. We don't request our vCard
    # because we already know there is no avatar
    presence_stanza = make_presence('test@localhost/noavatar',
                                    to='test@localhost/Resource',
                                    show='away', status='At the pub',
                                    photo="")
    q.forbid_events([avatar_request_event, avatar_retrieved_event])
    # Gabble must resist temptation to send vCard requests even with several
    # presence stanza sent!
    stream.send(presence_stanza)
    stream.send(presence_stanza)
    sync_stream(q, stream) # Twice because the vCard request is done in
    sync_stream(q, stream) # g_idle_add
    q.unforbid_events([avatar_request_event, avatar_retrieved_event])

    # Request on the first contact. Test the cache.
    handle = conn.RequestHandles(cs.HT_CONTACT, ['*****@*****.**'])[0]
    test_get_avatar(q, bus, conn, stream, '*****@*****.**', handle,
            in_cache=False)
    test_get_avatar(q, bus, conn, stream, '*****@*****.**', handle,
            in_cache=True)

    # Request another vCard and get resource-constraint
    busy_contact = '*****@*****.**'
    busy_handle = conn.RequestHandles(cs.HT_CONTACT, [busy_contact])[0]
    conn.Avatars.RequestAvatars([busy_handle])

    iq_event = q.expect('stream-iq', to=busy_contact, query_ns='vcard-temp',
        query_name='vCard')
    iq = iq_event.stanza
    error = domish.Element((None, 'error'))
    error['code'] = '500'
    error['type'] = 'wait'
    error.addElement((ns.STANZA, 'resource-constraint'))

    q.forbid_events([avatar_retrieved_event, avatar_request_event])
    send_error_reply(stream, iq, error)

    # Request the same vCard again during the suspended delay
    # We should not get the avatar
    conn.Avatars.RequestAvatars([busy_handle])
    sync_stream(q, stream)
    sync_dbus(bus, q, conn)
    q.unforbid_events([avatar_retrieved_event, avatar_request_event])
    
    # Request on a different contact, on another server
    # We should get the avatar
    handle = conn.RequestHandles(cs.HT_CONTACT, ['*****@*****.**'])[0]
    test_get_avatar(q, bus, conn, stream, '*****@*****.**', handle)

    # Try again the contact on the busy server.
    # We should not get the avatar
    # Note: the timeout is 3 seconds for the test suites. We assume that
    # a few stanza with be processed fast enough to avoid the race.
    q.forbid_events([avatar_retrieved_event, avatar_request_event])
    conn.Avatars.RequestAvatars([busy_handle])
    sync_stream(q, stream)
    sync_dbus(bus, q, conn)
    q.unforbid_events([avatar_retrieved_event, avatar_request_event])

    # After 3 seconds, we receive a new vCard request on the busy server
    iq_event = q.expect('stream-iq', to=busy_contact, query_ns='vcard-temp',
        query_name='vCard')
    iq = make_result_iq(stream, iq_event.stanza)
    vcard = iq.firstChildElement()
    photo = vcard.addElement('PHOTO')
    photo.addElement('TYPE', content='image/png')
    photo.addElement('BINVAL', content=base64.b64encode('hello'))
    stream.send(iq)

    event = q.expect('dbus-signal', signal='AvatarRetrieved')
    assertEquals(busy_handle, event.args[0])
    assertEquals(hashlib.sha1('hello').hexdigest(), event.args[1])
    assertEquals('hello', event.args[2])
    assertEquals('image/png', event.args[3])

    # Test with our own avatar test@localhost/Resource2
    presence_stanza = make_presence('test@localhost/Resource2',
                                    to='test@localhost/Resource',
                                    show='away', status='At the pub',
                                    photo=hashlib.sha1(':-D').hexdigest())
    stream.send(presence_stanza)
    iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp',
        query_name='vCard')
    iq = make_result_iq(stream, iq_event.stanza)
    vcard = iq.firstChildElement()
    photo = vcard.addElement('PHOTO')
    photo.addElement('TYPE', content='image/png')
    photo.addElement('BINVAL', content=base64.b64encode(':-D'))

    # do not send the vCard reply now. First, send another presence.
    q.forbid_events([avatar_request_event])
    stream.send(presence_stanza)
    sync_stream(q, stream)

    # Now send the reply.
    stream.send(iq)

    # Which results in an AvatarUpdated signal
    event = q.expect('dbus-signal', signal='AvatarUpdated')
    assertEquals(self_handle, event.args[0])
    assertEquals(hashlib.sha1(':-D').hexdigest(), event.args[1])

    # So Gabble has the right hash, and no need to ask the vCard again
    stream.send(presence_stanza)
    sync_stream(q, stream)
    q.unforbid_events([avatar_request_event])

    # But if the hash is different, the vCard is asked again
    presence_stanza = make_presence('test@localhost/Resource2',
                                    to='test@localhost/Resource',
                                    show='away', status='At the pub',
                                    photo=hashlib.sha1('\o/').hexdigest())
    stream.send(presence_stanza)
    iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp',
        query_name='vCard')
    iq = make_result_iq(stream, iq_event.stanza)
    vcard = iq.firstChildElement()
    photo = vcard.addElement('PHOTO')
    photo.addElement('TYPE', content='image/png')
    photo.addElement('BINVAL', content=base64.b64encode('\o/'))
    stream.send(iq)

    event = q.expect('dbus-signal', signal='AvatarUpdated')
    assertEquals(self_handle, event.args[0])
    assertEquals(hashlib.sha1('\o/').hexdigest(), event.args[1])

    # Gabble must reply without asking the vCard to the server because the
    # avatar must be in the cache
    q.forbid_events([avatar_request_event])
    data, mime = conn.Avatars.RequestAvatar(self_handle, byte_arrays=True)
    assertEquals('\o/', data)
    data, mime = conn.Avatars.RequestAvatar(handle, byte_arrays=True)
    assertEquals('hello', data)
    q.unforbid_events([avatar_request_event])

    # First, ensure the pipeline is full
    contacts = ['*****@*****.**' % i for i in range(1, 100) ]
    handles = conn.RequestHandles(cs.HT_CONTACT, contacts)
    conn.Avatars.RequestAvatars(handles)
    # Then, request yet another avatar. The request will time out before
    # the IQ is sent, which used to trigger a crash in Gabble
    # (LP#445847). So, we assert that the error is NotAvailable (rather
    # than the error returned when the service crashes).
    try:
        conn.Avatars.RequestAvatar(handles[-1])
    except dbus.DBusException, e:
        assertEquals(cs.NOT_AVAILABLE, e.get_dbus_name())
Exemple #42
0
def test_two_clients(q, bus, conn, stream, contact1, contact2,
        contact_handle1, contact_handle2, client, broken_hash):

    presence = make_presence(contact1, status='hello')
    stream.send(presence)

    q.expect('dbus-signal', signal='PresencesChanged',
            args=[{contact_handle1:
                (2, u'available', 'hello')}])

    presence = make_presence(contact2, status='hello')
    stream.send(presence)

    q.expect('dbus-signal', signal='PresencesChanged',
            args=[{contact_handle2:
                (2, u'available', 'hello')}])

    # no special capabilities
    for h in (contact_handle1, contact_handle2):
        for rcc in get_contacts_capabilities_sync(conn, [h])[h]:
            assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE))

    # send updated presence with Jingle caps info
    ver = compute_caps_hash(some_identities, jingle_av_features, {})
    caps = {
        'node': client,
        'ver': ver,
        'hash': 'sha-1',
        }
    presence = make_presence(contact1, status='hello', caps=caps)
    stream.send(presence)
    presence = make_presence(contact2, status='hello', caps=caps)
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact1,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + ver

    # don't receive any D-Bus signal
    forbidden = [
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
        ]
    q.forbid_events(forbidden)
    sync_dbus(bus, q, conn)
    q.unforbid_events(forbidden)

    result = make_caps_disco_reply(
        stream, event.stanza, some_identities, jingle_av_features)

    if broken_hash:
        # make the hash break!
        query = result.firstChildElement()
        query.addElement('feature')['var'] = 'http://example.com/another-feature'

    stream.send(result)

    if broken_hash:
        # Gabble looks up our capabilities again because the first contact
        # failed to provide a valid hash
        event = q.expect('stream-iq', to=contact2,
            query_ns='http://jabber.org/protocol/disco#info')
        query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
        assert query_node.attributes['node'] == \
            client + '#' + ver

        # don't receive any D-Bus signal
        q.forbid_events(forbidden)
        sync_dbus(bus, q, conn)
        q.unforbid_events(forbidden)

        # send good reply
        send_disco_reply(stream, event.stanza, some_identities, jingle_av_features)

    # we can now do audio calls
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged',
            predicate=lambda e: contact_handle2 in e.args[0]),
        )
    assert_rccs_callable(cc.args[0][contact_handle2])

    if not broken_hash:
        # if the first contact failed to provide a good hash, it does not
        # deserve its capabilities to be understood by Gabble!
        cc, = q.expect_many(
            EventPattern('dbus-signal', signal='ContactCapabilitiesChanged',
                predicate=lambda e: contact_handle1 in e.args[0]),
            )
        assert_rccs_callable(cc.args[0][contact_handle1])

    # don't receive any further signals
    q.forbid_events(forbidden)
    sync_dbus(bus, q, conn)
    q.unforbid_events(forbidden)
def test_local_queueing(q, bus, conn, stream):
    assertContains(
        cs.CONN_IFACE_POWER_SAVING,
        conn.Get(cs.CONN, "Interfaces", dbus_interface=cs.PROPERTIES_IFACE))

    assertEquals(
        False,
        conn.Get(cs.CONN_IFACE_POWER_SAVING,
                 "PowerSavingActive",
                 dbus_interface=cs.PROPERTIES_IFACE))

    event = q.expect('stream-iq',
                     to=None,
                     query_ns='vcard-temp',
                     query_name='vCard')
    acknowledge_iq(stream, event.stanza)

    presence_update = [EventPattern('dbus-signal', signal='PresencesChanged')]
    q.forbid_events(presence_update)

    call_async(q, conn.PowerSaving, 'SetPowerSaving', True)

    q.expect_many(
        EventPattern('dbus-return', method='SetPowerSaving'),
        EventPattern('dbus-signal', signal='PowerSavingChanged', args=[True]))

    assertEquals(
        True,
        conn.Get(cs.CONN_IFACE_POWER_SAVING,
                 "PowerSavingActive",
                 dbus_interface=cs.PROPERTIES_IFACE))

    # These presence stanzas should be queued
    stream.send(make_presence('*****@*****.**', show='away', status='At the pub'))
    stream.send(
        make_presence('*****@*****.**',
                      show='xa',
                      status='Somewhere over the rainbow'))

    # Pep notifications too
    message = elem('message', from_='*****@*****.**')(elem(
        (ns.PUBSUB_EVENT),
        'event')(elem('items',
                      node=ns.NICK)(elem('item')(elem(ns.NICK,
                                                      'nick')(u'Robert')))))
    stream.send(message.toXml())

    sync_dbus(bus, q, conn)
    q.unforbid_events(presence_update)

    # Incoming important stanza will flush the queue
    m = domish.Element((None, 'message'))
    m['from'] = '[email protected]/Pidgin'
    m['id'] = '123'
    m['type'] = 'chat'
    m.addElement('body', content='important message')
    stream.send(m)

    # Presence updates should come in the original order ...
    p1 = q.expect('dbus-signal', signal='PresencesChanged')
    p2 = q.expect('dbus-signal', signal='PresencesChanged')

    assertEquals('away', p1.args[0].values()[0][1])
    assertEquals('xa', p2.args[0].values()[0][1])

    # .. followed by the result of PEP notification ..
    event = q.expect('dbus-signal', signal='AliasesChanged')

    # .. and finally the message that flushed the stanza queue
    q.expect('dbus-signal', signal='NewChannels')

    sync_stream(q, stream)

    q.forbid_events(presence_update)

    stream.send(make_presence('*****@*****.**', show='away', status='Home'))

    # Carl's presence update is queued
    sync_dbus(bus, q, conn)
    q.unforbid_events(presence_update)

    # Disable powersaving, flushing the queue
    conn.PowerSaving.SetPowerSaving(False)

    q.expect('dbus-signal', signal='PresencesChanged')
Exemple #44
0
def test(q, bus, conn, stream):
    iq_event = q.expect('stream-iq',
                        to=None,
                        query_ns='vcard-temp',
                        query_name='vCard')

    # When we start, there is no avatar
    acknowledge_iq(stream, iq_event.stanza)
    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")

    # Another resource confirms we have no avatar. We don't request our vCard
    # because we already know there is no avatar
    presence_stanza = make_presence('test@localhost/noavatar',
                                    to='test@localhost/Resource',
                                    show='away',
                                    status='At the pub',
                                    photo="")
    q.forbid_events([avatar_request_event, avatar_retrieved_event])
    # Gabble must resist temptation to send vCard requests even with several
    # presence stanza sent!
    stream.send(presence_stanza)
    stream.send(presence_stanza)
    sync_stream(q, stream)  # Twice because the vCard request is done in
    sync_stream(q, stream)  # g_idle_add
    q.unforbid_events([avatar_request_event, avatar_retrieved_event])

    # Request on the first contact. Test the cache.
    handle = conn.get_contact_handle_sync('*****@*****.**')
    test_get_avatar(q,
                    bus,
                    conn,
                    stream,
                    '*****@*****.**',
                    handle,
                    in_cache=False)
    test_get_avatar(q, bus, conn, stream, '*****@*****.**', handle, in_cache=True)

    # Request another vCard and get resource-constraint
    busy_contact = '*****@*****.**'
    busy_handle = conn.get_contact_handle_sync(busy_contact)
    conn.Avatars.RequestAvatars([busy_handle])

    iq_event = q.expect('stream-iq',
                        to=busy_contact,
                        query_ns='vcard-temp',
                        query_name='vCard')
    iq = iq_event.stanza
    error = domish.Element((None, 'error'))
    error['code'] = '500'
    error['type'] = 'wait'
    error.addElement((ns.STANZA, 'resource-constraint'))

    q.forbid_events([avatar_retrieved_event, avatar_request_event])
    send_error_reply(stream, iq, error)

    # Request the same vCard again during the suspended delay
    # We should not get the avatar
    conn.Avatars.RequestAvatars([busy_handle])
    sync_stream(q, stream)
    sync_dbus(bus, q, conn)
    q.unforbid_events([avatar_retrieved_event, avatar_request_event])

    # Request on a different contact, on another server
    # We should get the avatar
    handle = conn.get_contact_handle_sync('*****@*****.**')
    test_get_avatar(q, bus, conn, stream, '*****@*****.**', handle)

    # Try again the contact on the busy server.
    # We should not get the avatar
    # Note: the timeout is 3 seconds for the test suites. We assume that
    # a few stanza with be processed fast enough to avoid the race.
    q.forbid_events([avatar_retrieved_event, avatar_request_event])
    conn.Avatars.RequestAvatars([busy_handle])
    sync_stream(q, stream)
    sync_dbus(bus, q, conn)
    q.unforbid_events([avatar_retrieved_event, avatar_request_event])

    # After 3 seconds, we receive a new vCard request on the busy server
    iq_event = q.expect('stream-iq',
                        to=busy_contact,
                        query_ns='vcard-temp',
                        query_name='vCard')
    iq = make_result_iq(stream, iq_event.stanza)
    vcard = iq.firstChildElement()
    photo = vcard.addElement('PHOTO')
    photo.addElement('TYPE', content='image/png')
    photo.addElement('BINVAL', content=base64.b64encode(b'hello').decode())
    stream.send(iq)

    event = q.expect('dbus-signal', signal='AvatarRetrieved')
    assertEquals(busy_handle, event.args[0])
    assertEquals(hashlib.sha1(b'hello').hexdigest(), event.args[1])
    assertEquals(b'hello', event.args[2])
    assertEquals('image/png', event.args[3])

    # Test with our own avatar test@localhost/Resource2
    presence_stanza = make_presence('test@localhost/Resource2',
                                    to='test@localhost/Resource',
                                    show='away',
                                    status='At the pub',
                                    photo=hashlib.sha1(b':-D').hexdigest())
    stream.send(presence_stanza)
    iq_event = q.expect('stream-iq',
                        to=None,
                        query_ns='vcard-temp',
                        query_name='vCard')
    iq = make_result_iq(stream, iq_event.stanza)
    vcard = iq.firstChildElement()
    photo = vcard.addElement('PHOTO')
    photo.addElement('TYPE', content='image/png')
    photo.addElement('BINVAL', content=base64.b64encode(b':-D').decode())

    # do not send the vCard reply now. First, send another presence.
    q.forbid_events([avatar_request_event])
    stream.send(presence_stanza)
    sync_stream(q, stream)

    # Now send the reply.
    stream.send(iq)

    # Which results in an AvatarUpdated signal
    event = q.expect('dbus-signal', signal='AvatarUpdated')
    assertEquals(self_handle, event.args[0])
    assertEquals(hashlib.sha1(b':-D').hexdigest(), event.args[1])

    # So Gabble has the right hash, and no need to ask the vCard again
    stream.send(presence_stanza)
    sync_stream(q, stream)
    q.unforbid_events([avatar_request_event])

    # But if the hash is different, the vCard is asked again
    presence_stanza = make_presence('test@localhost/Resource2',
                                    to='test@localhost/Resource',
                                    show='away',
                                    status='At the pub',
                                    photo=hashlib.sha1(b'\o/').hexdigest())
    stream.send(presence_stanza)
    iq_event = q.expect('stream-iq',
                        to=None,
                        query_ns='vcard-temp',
                        query_name='vCard')
    iq = make_result_iq(stream, iq_event.stanza)
    vcard = iq.firstChildElement()
    photo = vcard.addElement('PHOTO')
    photo.addElement('TYPE', content='image/png')
    photo.addElement('BINVAL', content=base64.b64encode(b'\o/').decode())
    stream.send(iq)

    event = q.expect('dbus-signal', signal='AvatarUpdated')
    assertEquals(self_handle, event.args[0])
    assertEquals(hashlib.sha1(b'\o/').hexdigest(), event.args[1])

    # Gabble must reply without asking the vCard to the server because the
    # avatar must be in the cache
    q.forbid_events([avatar_request_event])
    conn.Avatars.RequestAvatars([self_handle])
    e = q.expect('dbus-signal', signal='AvatarRetrieved')
    assertEquals(b'\o/', e.args[2])
    conn.Avatars.RequestAvatars([handle])
    e = q.expect('dbus-signal', signal='AvatarRetrieved')
    assertEquals(b'hello', e.args[2])
    q.unforbid_events([avatar_request_event])

    # First, ensure the pipeline is full
    contacts = ['*****@*****.**' % i for i in range(1, 100)]
    handles = conn.get_contact_handles_sync(contacts)
    conn.Avatars.RequestAvatars(handles)
    # Then, request yet another avatar. The request will time out before
    # the IQ is sent, which used to trigger a crash in Gabble
    # (LP#445847).
    conn.Avatars.RequestAvatars([handles[-1]])
    sync_dbus(bus, q, conn)
def test_ft_caps_from_contact(q, bus, conn, stream, contact, contact_handle, client):
    global run
    run += 1

    conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACT_CAPS)
    conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS)

    # send presence with no FT cap
    presence = make_presence(contact, status='hello')
    c = presence.addElement((ns.CAPS, 'c'))
    c['node'] = client
    c['ver'] = compute_caps_hash(['client/pc//jingleshareutils-%d' % run], [], {})
    c['ext'] = ""
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO)
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + c['ver']

    # send good reply
    result = make_result_iq(stream, event.stanza)
    query = result.firstChildElement()
    query['node'] = client + '#' + c['ver']
    stream.send(result)

    # no change in ContactCapabilities, so no signal ContactCapabilitiesChanged
    sync_stream(q, stream)

    # no special capabilities
    basic_caps = dbus.Dictionary({contact_handle:
            [(text_fixed_properties, text_allowed_properties)]})
    caps = conn_caps_iface.GetContactCapabilities([contact_handle])
    assert caps == basic_caps, caps
    # test again, to check GetContactCapabilities does not have side effect
    caps = conn_caps_iface.GetContactCapabilities([contact_handle])
    assert caps == basic_caps, caps
    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.ATTR_CONTACT_CAPABILITIES]
    assert caps_via_contacts_iface == caps[contact_handle], \
                                    caps_via_contacts_iface

    # send presence with ft capa
    presence = make_presence(contact, status='hello')
    c = presence.addElement((ns.CAPS, 'c'))
    c['node'] = client
    c['ext'] = "share-v1"
    c['ver'] = compute_caps_hash([], [], {})
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact, query_ns=ns.DISCO_INFO)
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + c['ext']

    # send good reply
    result = make_result_iq(stream, event.stanza)
    query = result.firstChildElement()
    query['node'] = client + '#' + c['ext']
    feature = query.addElement('feature')
    feature['var'] = ns.GOOGLE_FEAT_SHARE
    stream.send(result)


    generic_ft_caps = dbus.Dictionary({contact_handle:
            [(text_fixed_properties, text_allowed_properties),
             (ft_fixed_properties, ft_allowed_properties)]})

    event = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
    assert len(event.args) == 1
    assert event.args[0] == generic_ft_caps

    caps = conn_caps_iface.GetContactCapabilities([contact_handle])
    assert caps == generic_ft_caps, caps
    # test again, to check GetContactCapabilities does not have side effect
    caps = conn_caps_iface.GetContactCapabilities([contact_handle])
    assert caps == generic_ft_caps, caps
    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.ATTR_CONTACT_CAPABILITIES]
    assert caps_via_contacts_iface == caps[contact_handle], \
                                    caps_via_contacts_iface
Exemple #46
0
def test_hash(q, bus, conn, stream, contact, contact_handle, client):
    presence = make_presence(contact, status='hello')
    stream.send(presence)

    q.expect('dbus-signal', signal='PresencesChanged',
            args=[{contact_handle:
                (2, u'available', 'hello')}])

    # no special capabilities
    for rcc in get_contacts_capabilities_sync(conn, [contact_handle])[contact_handle]:
        assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE))

    # send updated presence with Jingle caps info
    presence = make_presence(contact, status='hello',
        caps={'node': client,
              'ver':  '0.1',
             })
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + '0.1'

    # send good reply
    send_disco_reply(stream, event.stanza, [], jingle_av_features)

    # we can now do audio calls
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
        )

    assert_rccs_callable(cc.args[0][contact_handle])
    assertEquals(cc.args[0],
            get_contacts_capabilities_sync(conn, [contact_handle]))

    # Send presence without any capabilities. XEP-0115 §8.4 Caps Optimization
    # says “receivers of presence notifications MUST NOT expect an annotation
    # on every presence notification they receive”, so the contact should still
    # be media-capable afterwards.
    stream.send(make_presence(contact, status='very capable'))
    q.expect('dbus-signal', signal='PresencesChanged',
        args=[{contact_handle: (2, u'available', 'very capable')}])
    # still exactly the same capabilities
    assertEquals(cc.args[0],
            get_contacts_capabilities_sync(conn, [contact_handle]))

    # send bogus presence
    caps = {
        'node': client,
        'ver':  'ceci=nest=pas=un=hash',
        'hash': 'sha-1',
        }
    presence = make_presence(contact, status='hello', caps=caps)
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + caps['ver']

    # send bogus reply
    send_disco_reply(stream, event.stanza, [],
        ['http://jabber.org/protocol/bogus-feature'])

    # don't receive any D-Bus signal
    forbidden = [
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
        ]
    q.forbid_events(forbidden)
    sync_dbus(bus, q, conn)
    sync_stream(q, stream)

    # send presence with empty caps
    presence = make_presence(contact, status='hello',
        caps={'node': client,
              'ver':  '0.0',
             })
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + '0.0'

    # still don't receive any D-Bus signal
    sync_dbus(bus, q, conn)

    # send good reply
    q.unforbid_events(forbidden)
    result = make_result_iq(stream, event.stanza)
    query = result.firstChildElement()
    stream.send(result)

    # we can now do nothing
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
        )
    for rcc in cc.args[0][contact_handle]:
        assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE))
    assert_rccs_not_callable(cc.args[0][contact_handle])
    assertEquals(cc.args[0],
            get_contacts_capabilities_sync(conn, [contact_handle]))

    # send correct presence
    ver = compute_caps_hash(some_identities, jingle_av_features, fake_client_dataforms)
    caps = {
        'node': client,
        'ver':  ver,
        'hash': 'sha-1',
        }
    presence = make_presence(contact, status='hello', caps=caps)
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + caps['ver']

    # don't receive any D-Bus signal
    q.forbid_events(forbidden)
    sync_dbus(bus, q, conn)
    q.unforbid_events(forbidden)

    # send good reply
    send_disco_reply(
        stream, event.stanza, some_identities, jingle_av_features, fake_client_dataforms)

    # we can now do audio calls
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
        )
    assert_rccs_callable(cc.args[0][contact_handle])
    assertEquals(cc.args[0],
            get_contacts_capabilities_sync(conn, [contact_handle]))
Exemple #47
0
def test(q, bus, conn, stream):
    caps = {
        'node': client,
        'ver':  '0.1',
        }

    update_contact_caps(q, conn, stream, '[email protected]/Foo', caps)
    update_contact_caps(q, conn, stream, '[email protected]/Foo', caps)

    # Meredith signs in from one resource.
    update_contact_caps(q, conn, stream, '[email protected]/One', caps)
    # Meredith signs in from another resource with the same client. We don't
    # need to disco her, even though we don't trust this caps node in general
    # yet, because she's already told us what it means.
    meredith_two = '[email protected]/Two'
    q.forbid_events([
        EventPattern('stream-iq', to=meredith_two, query_ns=ns.DISCO_INFO)
        ])
    stream.send(make_presence(meredith_two, 'hello', caps=caps))
    sync_stream(q, stream)

    # Jens signs in from one resource, which is slow to answer the disco query.
    jens_one = '[email protected]/One'
    j = send_presence(q, conn, stream, jens_one, caps)
    j_stanza = expect_disco(q, jens_one, client, caps)

    # Jens now signs in elsewhere with the same client; we disco it (maybe
    # it'll reply sooner? Maybe his first client's network connection went away
    # and the server hasn't noticed yet?) and it replies immediately.
    update_contact_caps (q, conn, stream, '[email protected]/Two', caps,
        initial=False)

    # Jens' first client replies. We don't expect any caps changes here, and
    # this shouldn't count as a second point towards the five we need to trust
    # this caps node.
    send_disco_reply(stream, j_stanza, [], features)
    check_caps (conn, j)

    update_contact_caps (q, conn, stream, '[email protected]/Foo', caps)

    # Now five distinct contacts have told us what this caps node means, we
    # trust it.
    update_contact_caps (q, conn, stream, '[email protected]/Foo', caps,
        disco = False)
    update_contact_caps (q, conn, stream, '[email protected]/Foo', caps,
        disco = False)

    caps = {
        'node': client,
        'ver':  compute_caps_hash([], features, fake_client_dataforms),
        'hash': 'sha-1',
        }

    update_contact_caps(q, conn, stream, '[email protected]/Foo',
       caps,  dataforms = fake_client_dataforms)
    # We can verify the reply for these caps against the hash, and thus never
    # need to disco it again.
    update_contact_caps(q, conn, stream, '[email protected]/Foo', caps,
        disco = False, dataforms = fake_client_dataforms)
    update_contact_caps(q, conn, stream, '[email protected]/Foo', caps,
        disco = False, dataforms = fake_client_dataforms)
Exemple #48
0
def test_hash(q, bus, conn, stream, contact, contact_handle, client):
    global caps_changed_flag

    presence = make_presence(contact, status='hello')
    stream.send(presence)

    q.expect('dbus-signal', signal='PresencesChanged',
            args=[{contact_handle:
                (2, u'available', 'hello')}])

    # no special capabilities
    basic_caps = [(contact_handle, cs.CHANNEL_TYPE_TEXT, 3, 0)]
    assert conn.Capabilities.GetCapabilities([contact_handle]) == basic_caps

    # send updated presence with Jingle caps info
    presence = make_presence(contact, status='hello',
        caps={'node': client,
              'ver':  '0.1',
             })
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + '0.1'

    # send good reply
    send_disco_reply(stream, event.stanza, [], jingle_av_features)

    # we can now do audio calls
    event = q.expect('dbus-signal', signal='CapabilitiesChanged')
    caps_diff = event.args[0]
    media_diff = [c for c in caps_diff
                    if c[1] == cs.CHANNEL_TYPE_STREAMED_MEDIA][0]
    assert media_diff[5] & cs.MEDIA_CAP_AUDIO, media_diff[5]
    caps_changed_flag = False

    # Send presence without any capabilities. XEP-0115 §8.4 Caps Optimization
    # says “receivers of presence notifications MUST NOT expect an annotation
    # on every presence notification they receive”, so the contact should still
    # be media-capable afterwards.
    stream.send(make_presence(contact, status='very capable'))
    q.expect('dbus-signal', signal='PresencesChanged',
        args=[{contact_handle: (2, u'available', 'very capable')}])
    ye_olde_caps = conn.Capabilities.GetCapabilities([contact_handle])
    assertLength(1, [c for c in ye_olde_caps
                       if c[1] == cs.CHANNEL_TYPE_STREAMED_MEDIA and
                          c[3] & cs.MEDIA_CAP_AUDIO])

    # send bogus presence
    caps = {
        'node': client,
        'ver':  'ceci=nest=pas=un=hash',
        'hash': 'sha-1',
        }
    presence = make_presence(contact, status='hello', caps=caps)
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + caps['ver']

    # send bogus reply
    send_disco_reply(stream, event.stanza, [],
        ['http://jabber.org/protocol/bogus-feature'])

    # don't receive any D-Bus signal
    sync_dbus(bus, q, conn)
    sync_stream(q, stream)
    assert caps_changed_flag == False


    # send presence with empty caps
    presence = make_presence(contact, status='hello',
        caps={'node': client,
              'ver':  '0.0',
             })
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + '0.0'

    # still don't receive any D-Bus signal
    sync_dbus(bus, q, conn)
    assert caps_changed_flag == False

    # send good reply
    result = make_result_iq(stream, event.stanza)
    query = result.firstChildElement()
    stream.send(result)

    # we can now do nothing
    event = q.expect('dbus-signal', signal='CapabilitiesChanged')
    assert caps_changed_flag == True
    caps_changed_flag = False


    # send correct presence
    ver = compute_caps_hash(some_identities, jingle_av_features, fake_client_dataforms)
    caps = {
        'node': client,
        'ver':  ver,
        'hash': 'sha-1',
        }
    presence = make_presence(contact, status='hello', caps=caps)
    stream.send(presence)

    # Gabble looks up our capabilities
    event = q.expect('stream-iq', to=contact,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assert query_node.attributes['node'] == \
        client + '#' + caps['ver']

    # don't receive any D-Bus signal
    sync_dbus(bus, q, conn)
    assert caps_changed_flag == False

    # send good reply
    send_disco_reply(
        stream, event.stanza, some_identities, jingle_av_features, fake_client_dataforms)

    # we can now do audio calls
    event = q.expect('dbus-signal', signal='CapabilitiesChanged',
    )
    assert caps_changed_flag == True
    caps_changed_flag = False
def test(q, bus, conn, stream):
    client = 'http://example.com/perverse-client'
    contact_bare_jid = '*****@*****.**'
    contact_with_resource = '[email protected]/hi'
    contact_handle = conn.RequestHandles(cs.HT_CONTACT, [contact_bare_jid])[0]

    # Gabble gets a presence stanza from a bare JID, which is a tad surprising.
    features = [
        ns.JINGLE_015,
        ns.JINGLE_015_AUDIO,
        ns.JINGLE_015_VIDEO,
        ns.GOOGLE_P2P,
        ]
    caps = {'node': client,
            'hash': 'sha-1',
            'ver': compute_caps_hash([], features, {}),
           }
    p = make_presence(contact_bare_jid, status='Hello', caps=caps)
    stream.send(p)

    # Gabble looks up the hash
    event = q.expect('stream-iq', to=contact_bare_jid,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assertEquals(client + '#' + caps['ver'], query_node.attributes['node'])

    # The bare jid replies
    send_disco_reply(stream, event.stanza, [], features)

    # Gabble lets us know their caps have changed. (Gabble used to ignore the
    # reply.)
    streamed_media_caps = (contact_handle, cs.CHANNEL_TYPE_STREAMED_MEDIA,
        0, 3, 0, cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_VIDEO)
    e = q.expect('dbus-signal', signal='CapabilitiesChanged')
    assertContains(streamed_media_caps, e.args[0])

    # Gabble gets another presence stanza from the bare JID, with different
    # caps.
    features.append(ns.TUBES)
    caps = {'node': client,
            'hash': 'sha-1',
            'ver': compute_caps_hash([], features, {}),
           }
    p = make_presence(contact_bare_jid, status='Get out the abacus', caps=caps)
    stream.send(p)

    # Gabble looks up the new hash
    disco2 = q.expect('stream-iq', to=contact_bare_jid,
        query_ns='http://jabber.org/protocol/disco#info')
    query_node = xpath.queryForNodes('/iq/query', disco2.stanza)[0]
    assertEquals(client + '#' + caps['ver'], query_node.attributes['node'])

    # This time, before the bare JID replies, Gabble gets a presence from the
    # resourceful jid.
    features_ = features + [ns.CHAT_STATES]
    caps = {'node': client,
            'hash': 'sha-1',
            'ver': compute_caps_hash([], features_, {}),
           }
    p = make_presence(contact_with_resource, status='Count this', caps=caps)
    stream.send(p)

    # Gabble throws away presence from the bare JID when it gets presence from
    # a resource (and vice versa), so it should now say the contact is
    # incapable.  Gabble also looks up the resourceful JID's hash.
    cc, disco3 = q.expect_many(
        EventPattern('dbus-signal', signal='CapabilitiesChanged'),
        EventPattern('stream-iq', to=contact_with_resource,
            query_ns='http://jabber.org/protocol/disco#info'),
        )

    assertDoesNotContain(streamed_media_caps, cc.args[0])

    query_node = xpath.queryForNodes('/iq/query', disco3.stanza)[0]
    assertEquals(client + '#' + caps['ver'], query_node.attributes['node'])

    # The bare jid replies! Getting a disco reply from a bare JID when we've
    # got presence from resources used to crash Gabble, but now it just ignores
    # it.
    send_disco_reply(stream, disco2.stanza, [], features)

    # Now the resourceful JID replies:
    send_disco_reply(stream, disco3.stanza, [], features_)

    # Gabble should announce that the contact has acquired some caps.
    e = q.expect('dbus-signal', signal='CapabilitiesChanged')
    assertContains(streamed_media_caps, e.args[0])
def test(q, bus, conn, stream):
    bob = conn.get_contact_handle_sync('*****@*****.**')

    presence = make_presence('[email protected]/Foo', status='hello')
    stream.send(presence)

    q.expect('dbus-signal',
             signal='PresencesChanged',
             args=[{
                 bob: (cs.PRESENCE_AVAILABLE, u'available', 'hello')
             }])

    basic_caps = [(bob, cs.CHANNEL_TYPE_TEXT, 3, 0)]

    # only Text
    for rcc in get_contacts_capabilities_sync(conn, [bob])[bob]:
        assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE))

    # holding the handle here: see below
    assertEquals(
        {
            bob: {
                cs.ATTR_CONTACT_CAPABILITIES:
                get_contacts_capabilities_sync(conn, [bob])[bob],
                cs.CONN + '/contact-id':
                '*****@*****.**',
            },
        },
        conn.Contacts.GetContactAttributes([bob], [cs.CONN_IFACE_CONTACT_CAPS],
                                           True))

    # send updated presence with Jingle audio/video caps info. we turn on both
    # audio and video at the same time to test that all of the capabilities are
    # discovered before any capabilities change signal is emitted
    presence = make_presence(
        '[email protected]/Foo',
        status='hello',
        caps={
            'node': 'http://telepathy.freedesktop.org/fake-client',
            'ver': '0.1',
            'ext': 'video',
        })
    stream.send(presence)

    # Gabble looks up both the version and the video bundles, in any order
    (version_event, video_event) = q.expect_many(
        EventPattern(
            'stream-iq',
            to='[email protected]/Foo',
            query_ns='http://jabber.org/protocol/disco#info',
            query_node='http://telepathy.freedesktop.org/fake-client#0.1'),
        EventPattern(
            'stream-iq',
            to='[email protected]/Foo',
            query_ns='http://jabber.org/protocol/disco#info',
            query_node='http://telepathy.freedesktop.org/fake-client#video'))

    # reply to the video bundle query first - this capability alone is not
    # sufficient to make us callable
    result = make_result_iq(stream, video_event.stanza)
    query = result.firstChildElement()
    feature = query.addElement('feature')
    feature['var'] = 'http://jabber.org/protocol/jingle/description/video'
    stream.send(result)

    # reply to the version bundle query, which should make us audio and
    # video callable
    result = make_result_iq(stream, version_event.stanza)
    query = result.firstChildElement()
    feature = query.addElement('feature')
    feature['var'] = 'http://jabber.org/protocol/jingle'
    feature = query.addElement('feature')
    feature['var'] = 'http://jabber.org/protocol/jingle/description/audio'
    feature = query.addElement('feature')
    feature['var'] = 'http://www.google.com/transport/p2p'
    stream.send(result)

    # we can now do audio and video calls
    cc, = q.expect_many(
        EventPattern(
            'dbus-signal',
            signal='ContactCapabilitiesChanged',
            predicate=lambda e: check_rccs_callable(e.args[0][bob])), )
    assert_rccs_callable(cc.args[0][bob],
                         require_video=True,
                         mutable_contents=True)

    assertEquals(
        {
            bob: {
                cs.ATTR_CONTACT_CAPABILITIES: cc.args[0][bob],
                cs.CONN + '/contact-id': '*****@*****.**',
            },
        },
        conn.Contacts.GetContactAttributes([bob], [cs.CONN_IFACE_CONTACT_CAPS],
                                           True))

    # send updated presence without video support
    presence = make_presence(
        '[email protected]/Foo',
        status='hello',
        caps={
            'node': 'http://telepathy.freedesktop.org/fake-client',
            'ver': '0.1',
        })
    stream.send(presence)

    # we can now do only audio calls (and as a result have the ImmutableStreams
    # cap)
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), )
    assert_rccs_callable(cc.args[0][bob])
    assert_rccs_not_callable(cc.args[0][bob],
                             require_audio=False,
                             require_video=True,
                             mutable_contents=False)

    assertEquals(
        {
            bob: {
                cs.ATTR_CONTACT_CAPABILITIES: cc.args[0][bob],
                cs.CONN + '/contact-id': '*****@*****.**',
            },
        },
        conn.Contacts.GetContactAttributes([bob], [cs.CONN_IFACE_CONTACT_CAPS],
                                           True))

    # go offline
    presence = make_presence('[email protected]/Foo', type='unavailable')
    stream.send(presence)

    # can't do audio calls any more
    q.expect_many(
        EventPattern('dbus-signal',
                     signal='PresencesChanged',
                     args=[{
                         bob: (cs.PRESENCE_OFFLINE, 'offline', '')
                     }]),
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'),
    )

    # Contact went offline. Previously, this test asserted that the handle
    # became invalid, but that's not guaranteed to happen immediately; so we
    # now hold the handle (above), to guarantee that it does *not* become
    # invalid.
    rccs = get_contacts_capabilities_sync(conn, [bob])[bob]
    for rcc in rccs:
        assertEquals(cs.CHANNEL_TYPE_TEXT, rcc[0].get(cs.CHANNEL_TYPE))

    assertEquals(
        {
            bob: {
                cs.ATTR_CONTACT_CAPABILITIES: rccs,
                cs.CONN + '/contact-id': '*****@*****.**',
            },
        },
        conn.Contacts.GetContactAttributes([bob], [cs.CONN_IFACE_CONTACT_CAPS],
                                           True))

    # What about a handle that's not valid?
    assertEquals({},
                 conn.Contacts.GetContactAttributes(
                     [31337], [cs.CONN_IFACE_CONTACT_CAPS], False))