def check_tube_in_presence(presence, initiator):
    tubes_nodes = xpath.queryForNodes('/presence/tubes[@xmlns="%s"]'
        % ns.TUBES, presence)
    assert tubes_nodes is not None
    assert len(tubes_nodes) == 1

    tube_nodes = xpath.queryForNodes('/tubes/tube', tubes_nodes[0])
    assert tube_nodes is not None
    assert len(tube_nodes) == 1
    for tube in tube_nodes:
        tube['type'] = 'dbus'
        assert tube['initiator'] == initiator
        assert tube['service'] == 'com.example.TestCase'
        dbus_stream_id = tube['stream-id']
        my_bus_name = tube['dbus-name']
        dbus_tube_id = tube['id']

    params = {}
    parameter_nodes = xpath.queryForNodes('/tube/parameters/parameter', tube)
    for node in parameter_nodes:
        assert node['name'] not in params
        params[node['name']] = (node['type'], str(node))
    assert params == {'ay': ('bytes', 'aGVsbG8='),
                      's': ('str', 'hello'),
                      'i': ('int', '-123'),
                      'u': ('uint', '123'),
                     }

    return dbus_stream_id, my_bus_name, dbus_tube_id
def expect_socks5_reply(q):
    event = q.expect('stream-iq', iq_type='result')
    iq = event.stanza
    query = xpath.queryForNodes('/iq/query', iq)[0]
    assert query.uri == ns.BYTESTREAMS
    streamhost_used = xpath.queryForNodes('/query/streamhost-used', query)[0]
    return streamhost_used
def test_invisible_on_connect(q, bus, conn, stream):
    props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE)
    assertNotEquals({}, props['Statuses'])

    presence_event_pattern = EventPattern('stream-presence')

    q.forbid_events([presence_event_pattern])

    conn.SimplePresence.SetPresence("hidden", "")

    conn.Connect()

    stream.handle_get_all_privacy_lists(q, bus, conn, ['invisible'])

    get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get')
    list_node = xpath.queryForNodes('//list', get_list.query)[0]
    assertEquals('invisible', list_node['name'])

    stream.send_privacy_list(get_list.stanza,
        [elem('item', action='deny', order='1')(elem('presence-out'))])

    set_active = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set')
    active = xpath.queryForNodes('//active', set_active.query)[0]
    assertEquals('invisible', active['name'])
    acknowledge_iq(stream, set_active.stanza)

    q.unforbid_events([presence_event_pattern])

    q.expect('dbus-signal', signal='StatusChanged',
        args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
Example #4
0
def parse_form(stanza):
    fields = xpath.queryForNodes('/iq/query/x/field', stanza)
    form = {}
    for field in fields:
        values = xpath.queryForNodes('/field/value', field)
        form[field['var']] = [str(v) for v in values]
    return form
def extract_disco_parts(stanza):
    identity_nodes = xpath.queryForNodes('/iq/query/identity', stanza)
    assertLength(1, identity_nodes)
    identity_node = identity_nodes[0]

    assertEquals('client', identity_node['category'])
    assertDoesNotContain('xml:lang', identity_node.attributes)

    identity = 'client/%s//%s' % (identity_node['type'], identity_node['name'])

    features = []
    for feature in xpath.queryForNodes('/iq/query/feature', stanza):
        features.append(feature['var'])

    # a quick and ugly data form extractor
    x_nodes = xpath.queryForNodes('/iq/query/x', stanza) or []
    dataforms = {}
    for form in x_nodes:
        name = None
        fields = {}
        for field in xpath.queryForNodes('/x/field', form):
            if field['var'] == 'FORM_TYPE':
                name = str(field.firstChildElement())
            else:
                values = [str(x) for x in xpath.queryForNodes('/field/value', field)]

                fields[field['var']] = values

        if name is not None:
            dataforms[name] = fields

    return ([identity], features, dataforms)
Example #6
0
    def itemsReceived(self, item_event):
        """Gather items, convert to html and send the files to there proper location.
        """
        
        for item in item_event.items:
            if item.name != 'item': # TODO - handle retract and other events
                continue
            item_id = item.getAttribute('id', str(time.time()))

            date_pub = xpath.queryForNodes("/item/entry/published", item)
            published = datetime.datetime.now()
            if date_pub:
                published = str(date_pub[0])

            blog_id = None
            blog_ids = xpath.queryForNodes("/item/entry/id", item)
            if blog_ids:
                blog_id = str(blog_ids[0])

            if item.entry.id is None:
                log.msg('Wrong format for entry')
                continue
            # create entry
            args = self.blog.atom2hash(item.entry)
            self.blog.updateEntry(blog_id, args)
            
            last_items = self.blog.archiveItems()

            # update index
            self.blog.updateIndex(blog_id, last_items)
            # update atom
            self.blog.updateAtom(blog_id, last_items)
def test_invisible_on_connect_fail(q, bus, conn, stream):
    props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE)
    assertNotEquals({}, props['Statuses'])

    presence_event_pattern = EventPattern('stream-presence')

    q.forbid_events([presence_event_pattern])

    conn.SimplePresence.SetPresence("hidden", "")

    conn.Connect()

    stream.handle_get_all_privacy_lists(q, bus, conn)

    create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set')
    # Check its name
    assertNotEquals([],
        xpath.queryForNodes('/query/list/item/presence-out', create_list.query))
    acknowledge_iq(stream, create_list.stanza)

    set_active = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set')
    active = xpath.queryForNodes('//active', set_active.query)[0]
    assertEquals('invisible', active['name'])
    send_error_reply(stream, set_active.stanza)

    q.unforbid_events([presence_event_pattern])

    # Darn! At least we should have our presence set to DND.
    q.expect_many(
        EventPattern('dbus-signal', signal='PresencesChanged',
                     interface=cs.CONN_IFACE_SIMPLE_PRESENCE,
                     args=[{1: (6, 'dnd', '')}]),
        EventPattern('dbus-signal', signal='StatusChanged',
                     args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]))
def test_create_invisible_list(q, bus, conn, stream):
    conn.SimplePresence.SetPresence("away", "")

    conn.Connect()

    stream.handle_get_all_privacy_lists(q, bus, conn)

    get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get')
    list_node = xpath.queryForNodes('//list', get_list.query)[0]
    assertEquals('invisible', list_node['name'])

    error = domish.Element((None, 'error'))
    error['type'] = 'cancel'
    error.addElement((ns.STANZA, 'item-not-found'))
    send_error_reply (stream, get_list.stanza, error)

    create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set')
    list_node = xpath.queryForNodes('//list', create_list.query)[0]
    assertEquals('invisible', list_node['name'])
    assertNotEquals([],
        xpath.queryForNodes('/query/list/item/presence-out', create_list.query))
    acknowledge_iq(stream, create_list.stanza)

    q.expect('dbus-signal', signal='StatusChanged',
        args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])

    assertContains("hidden",
        conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses"))
def test(q, bus, conn, stream):
    # This sidecar sends a stanza, and waits for a reply, before being
    # created.
    pattern = EventPattern('stream-iq', to='sidecar.example.com',
        query_ns='http://example.com/sidecar')
    call_async(q, conn.Sidecars1, 'EnsureSidecar', TEST_PLUGIN_IFACE + ".IQ")
    e = q.expect_many(pattern)[0]

    # The server said yes, so we should get a sidecar back!
    acknowledge_iq(stream, e.stanza)
    q.expect('dbus-return', method='EnsureSidecar')

    identities = ["test/app-list//Test"]
    features = ["com.example.test1", "com.example.test2"]
    ver = compute_caps_hash(identities, features, {})

    iq = IQ(stream, "get")
    query = iq.addElement((ns.DISCO_INFO, 'query'))
    query['node'] = ns.GABBLE_CAPS + '#' + ver
    stream.send(iq)
    e = q.expect('stream-iq', query_ns='http://jabber.org/protocol/disco#info')

    returned_features = [feature['var']
        for feature in xpath.queryForNodes('/iq/query/feature', e.stanza)]
    assertEquals(features, returned_features)

    returned_identities = [identity['category'] + "/" + identity['type']+"//" + identity['name']
        for identity in xpath.queryForNodes('/iq/query/identity', e.stanza)]
    assertEquals(identities, returned_identities)

    new_ver = compute_caps_hash(returned_identities, returned_features, {})
    assertEquals(new_ver, ver)
    def _cb_disco_iq(self, iq):
        nodes = xpath.queryForNodes("/iq/query", iq)
        query = nodes[0]

        if query.getAttribute('node') is None:
            return

        node = query.attributes['node']
        ver = node.replace("http://telepathy.freedesktop.org/caps#", "")

        if iq.getAttribute('type') == 'result':

            if FileTransferTest.caps_identities is None or \
                    FileTransferTest.caps_features is None:
                identity_nodes = xpath.queryForNodes('/iq/query/identity', iq)
                assertLength(1, identity_nodes)
                identity_node = identity_nodes[0]

                identity_category = identity_node['category']
                identity_type = identity_node['type']
                identity_name = identity_node['name']
                identity = '%s/%s//%s' % (identity_category, identity_type,
                                          identity_name)
                FileTransferTest.caps_identities = [identity]

                FileTransferTest.caps_features = []
                for feature in xpath.queryForNodes('/iq/query/feature', iq):
                    FileTransferTest.caps_features.append(feature['var'])

                # Check if the hash matches the announced capabilities
                assertEquals(compute_caps_hash(FileTransferTest.caps_identities,
                                               FileTransferTest.caps_features,
                                               {}), ver)

            if ver == FileTransferTest.caps_ft:
                caps_share = compute_caps_hash(FileTransferTest.caps_identities,
                                               FileTransferTest.caps_features + \
                                                   [ns.GOOGLE_FEAT_SHARE],
                                               {})
                n = query.attributes['node'].replace(ver, caps_share)
                query.attributes['node'] = n

                for feature in xpath.queryForNodes('/iq/query/feature', iq):
                        query.children.remove(feature)

                for f in FileTransferTest.caps_features + [ns.GOOGLE_FEAT_SHARE]:
                    el = domish.Element((None, 'feature'))
                    el['var'] = f
                    query.addChild(el)

        elif iq.getAttribute('type') == 'get':
            caps_share = compute_caps_hash(FileTransferTest.caps_identities,
                                           FileTransferTest.caps_features + \
                                               [ns.GOOGLE_FEAT_SHARE],
                                           {})

            if ver == caps_share:
                n = query.attributes['node'].replace(ver,
                                                     FileTransferTest.caps_ft)
                query.attributes['node'] = n
def test_privacy_list_push_conflict(q, bus, conn, stream):
    test_invisible_on_connect(q, bus, conn, stream)

    set_id = stream.send_privacy_list_push_iq("invisible")

    _, req_list = q.expect_many(
        EventPattern('stream-iq', iq_type='result', predicate=lambda event: \
                         event.stanza['id'] == set_id),
        EventPattern('stream-iq', query_ns=ns.PRIVACY, iq_type="get"))

    stream.send_privacy_list(req_list.stanza,
        [elem('item', type='jid', value='*****@*****.**', action='allow',
              order='1')(elem('presence-out')),
         elem('item', action='deny', order='2')(elem('presence-out'))])

    create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set')
    created = xpath.queryForNodes('//list', create_list.stanza)[0]
    assertEquals(created["name"], 'invisible-gabble')

    acknowledge_iq(stream, create_list.stanza)

    set_active = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set')
    active = xpath.queryForNodes('//active', set_active.query)[0]
    assertEquals('invisible-gabble', active['name'])
    acknowledge_iq(stream, set_active.stanza)
Example #12
0
    def validate_session_initiate(self, query):
        contents = xpath.queryForNodes(
            '/jingle[@xmlns="%s"]/content' % ns.JINGLE,
            query)

        audio, video = [], []

        for c in contents:
            descs = xpath.queryForNodes(
                '/content/description[@xmlns="%s"]' % ns.JINGLE_RTP,
                c)

            assert len(descs) == 1, c.toXml()

            d = descs[0]

            if d['media'] == 'audio':
                audio.append(c['name'])
            elif d['media'] == 'video':
                video.append(c['name'])
            else:
                assert False, c.toXml()

        assert len(audio) + len(video) > 0, query.toXml()

        return (self._extract_session_id(query), audio, video)
Example #13
0
    def validate_session_initiate(self, query):
        contents = xpath.queryForNodes(
            '/jingle[@xmlns="%s"]/content' % ns.JINGLE_015,
            query)

        audio, video = [], []

        for c in contents:
            a_desc = xpath.queryForNodes(
                '/content/description[@xmlns="%s"]' % ns.JINGLE_015_AUDIO,
                c)
            v_desc = xpath.queryForNodes(
                '/content/description[@xmlns="%s"]' % ns.JINGLE_015_VIDEO,
                c)

            if a_desc is not None:
                assert len(a_desc) == 1, c.toXml()
                assert v_desc is None
                audio.append(c['name'])
            elif v_desc is not None:
                assert len(v_desc) == 1, c.toXml()
                assert a_desc is None
                video.append(c['name'])
            else:
                assert False, c.toXml()

        assert len(audio) + len(video) > 0, query.toXml()

        return (self._extract_session_id(query), audio, video)
    def _check_oob_iq(self, iq_event):
        assert iq_event.iq_type == 'set'
        assert iq_event.connection == self.incoming
        self.iq = iq_event.stanza
        assert self.iq['to'] == self.contact_name
        query = self.iq.firstChildElement()
        assert query.uri == 'jabber:iq:oob'
        url_node = xpath.queryForNodes("/iq/query/url", self.iq)[0]
        assert url_node['type'] == 'file'
        assert url_node['size'] == str(self.file.size)
        assert url_node['mimeType'] == self.file.content_type
        self.url = url_node.children[0]
        _, self.host, self.filename, _, _, _ = urlparse.urlparse(self.url)
        urllib.unquote(self.filename) == self.file.name
        desc_node = xpath.queryForNodes("/iq/query/desc", self.iq)[0]
        self.desc = desc_node.children[0]
        assert self.desc == self.file.description

        # Metadata forms
        forms = extract_data_forms(xpath.queryForNodes('/iq/query/x', self.iq))

        if self.service_name:
            assertEquals({'ServiceName': [self.service_name]},
                         forms[ns.TP_FT_METADATA_SERVICE])
        else:
            assert ns.TP_FT_METADATA_SERVICE not in forms

        if self.metadata:
            assertEquals(self.metadata, forms[ns.TP_FT_METADATA])
        else:
            assert ns.TP_FT_METADATA not in forms
def disco_caps(q, stream, presence):
    c_nodes = xpath.queryForNodes('/presence/c', presence.stanza)
    assert c_nodes is not None
    assert len(c_nodes) == 1
    hash = c_nodes[0].attributes['hash']
    ver = c_nodes[0].attributes['ver']
    node = c_nodes[0].attributes['node']
    assert hash == 'sha-1'

    # ask caps
    request = """
<iq from='[email protected]/resource' 
    id='disco1'
    to='[email protected]/resource' 
    type='get'>
  <query xmlns='""" + ns.DISCO_INFO + """'
         node='""" + node + '#' + ver + """'/>
</iq>
"""
    stream.send(request)

    # receive caps
    event = q.expect('stream-iq', query_ns=ns.DISCO_INFO)

    features = []
    for feature in xpath.queryForNodes('/iq/query/feature', event.stanza):
        features.append(feature['var'])

    # Check if the hash matches the announced capabilities
    assert ver == compute_caps_hash(['client/pc//%s' % PACKAGE_STRING], features, {})

    return (event, features)
 def check_si_reply(self, iq):
     si = xpath.queryForNodes('/iq/si[@xmlns="%s"]' % ns.SI,
             iq)[0]
     value = xpath.queryForNodes('/si/feature/x/field/value', si)
     assert len(value) == 1
     proto = value[0]
     assert str(proto) == self.get_ns()
def test(q, bus, conn, stream):
    muc_handle = request_muc_handle(q, conn, stream, '*****@*****.**')

    call_async(q, conn, 'RequestChannel', cs.CHANNEL_TYPE_TEXT, cs.HT_ROOM,
        muc_handle, True)

    q.expect('stream-presence', to='[email protected]/test')

    # Send presence for own membership of room.
    stream.send(
        make_muc_presence('owner', 'moderator', '*****@*****.**', 'test'))

    iq, ret = q.expect_many(
        EventPattern('stream-iq', to='*****@*****.**', iq_type='get',
            query_ns=ns.MUC_OWNER),
        EventPattern('dbus-return', method='RequestChannel'))
    handle_muc_get_iq(stream, iq.stanza)

    text_chan = wrap_channel(
        bus.get_object(conn.bus_name, ret.value[0]), 'Text')

    props = dict([(name, id)
        for id, name, sig, flags in text_chan.TpProperties.ListProperties()])
    call_async(q, text_chan.TpProperties, 'SetProperties',
        [(props['password'], 'foo'), (props['password-required'], True)])

    event = q.expect('stream-iq', to='*****@*****.**', iq_type='get',
        query_ns=ns.MUC_OWNER)
    handle_muc_get_iq(stream, event.stanza)

    event = q.expect('stream-iq', to='*****@*****.**', iq_type='set',
        query_ns=ns.MUC_OWNER)
    fields = xpath.queryForNodes('/iq/query/x/field', event.stanza)
    form = {}
    for field in fields:
        values = xpath.queryForNodes('/field/value', field)
        form[field['var']] = [str(v) for v in values]
    assert form == {'password': ['foo'], 'password_protected': ['1'],
            'muc#roomconfig_presencebroadcast' :
            ['moderator', 'participant', 'visitor']}
    acknowledge_iq(stream, event.stanza)

    event = q.expect('dbus-signal', signal='PropertiesChanged')
    assert event.args == [[(props['password'], 'foo'),
        (props['password-required'], True)]]

    q.expect('dbus-return', method='SetProperties', value=())

    call_async(q, text_chan.TpProperties, 'SetProperties',
        [(31337, 'foo'), (props['password-required'], True)])
    q.expect('dbus-error', name=cs.INVALID_ARGUMENT)

    call_async(q, text_chan.TpProperties, 'SetProperties',
        [(props['password'], True), (props['password-required'], 'foo')])
    q.expect('dbus-error', name=cs.NOT_AVAILABLE)

    call_async(q, text_chan.TpProperties, 'SetProperties',
        [(props['subject-contact'], 42)])
    q.expect('dbus-error', name=cs.PERMISSION_DENIED)
def test(q, bus, conn, stream):
    iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp',
            query_name='vCard')

    acknowledge_iq(stream, iq_event.stanza)

    # send diso request
    m = domish.Element((None, 'iq'))
    m['from'] = '*****@*****.**'
    m['id'] = '1'
    m['type'] = 'get'
    query = m.addElement('query')
    query['xmlns'] = ns.DISCO_INFO
    stream.send(m)

    # wait for disco response
    event = q.expect('stream-iq', iq_type='result',
            query_ns=ns.DISCO_INFO,
            to='*****@*****.**')

    features = set([str(f['var']) for f in xpath.queryForNodes('/iq/query/feature',
        event.stanza)])

    # OLPC NS aren't announced
    assert len(olpc_features.intersection(features)) == 0

    # Use OLPC interface
    buddy_info_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo')
    call_async(q, buddy_info_iface, 'SetProperties',
            {'color': '#ff0000,#0000ff'})

    # wait for <presence> stanza
    event = q.expect('stream-presence')
    c_nodes = xpath.queryForNodes('/presence/c', event.stanza)
    assert c_nodes is not None
    assert len(c_nodes) == 1

    # send diso request
    m = domish.Element((None, 'iq'))
    m['from'] = '*****@*****.**'
    m['id'] = '2'
    m['type'] = 'get'
    query = m.addElement('query')
    query['xmlns'] = ns.DISCO_INFO
    stream.send(m)

    # wait for disco response
    event = q.expect('stream-iq', iq_type='result',
        query_ns=ns.DISCO_INFO,
        to='*****@*****.**')
    assert event.stanza['id'] == '2'

    # OLPC NS are now announced
    features = set([str(f['var']) for f in xpath.queryForNodes('/iq/query/feature',
        event.stanza)])

    assert olpc_features.issubset(features)
Example #19
0
def test(q, bus, conn, stream):
    self_presence = q.expect('stream-presence')

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

    jid = '[email protected]/omg'

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

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

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

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

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

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

    # Gabble should still know what they are, and reply. This is actually quite
    # important: there's a bug in iChat where if you return an error to a disco
    # query, it just asks again, and again, and again...
    reply = q.expect('stream-iq', to=jid)
    assertEquals('result', reply.iq_type)
    def _wait_activation_iq(self):
        e = self.q.expect('stream-iq', iq_type='set', to='proxy.localhost',
            query_ns=ns.BYTESTREAMS)

        query = xpath.queryForNodes('/iq/query', e.stanza)[0]
        assert query['sid'] == self.stream_id
        activate = xpath.queryForNodes('/iq/query/activate', e.stanza)[0]
        assert str(activate) == self.target

        self._reply_activation_iq(e.stanza)
Example #21
0
    def processMessageGC(self, elem):
        """Process a stanza element that is from a chatroom"""
        # Ignore all messages that are x-stamp (delayed / room history)
        # <delay xmlns='urn:xmpp:delay' stamp='2016-05-06T20:04:17.513Z'
        #  from='[email protected]/twisted_words'/>
        if xpath.queryForNodes("/message/delay[@xmlns='urn:xmpp:delay']",
                               elem):
            return

        _from = jid.JID(elem["from"])
        room = _from.user
        res = _from.resource

        body = xpath.queryForString('/message/body', elem)
        if body is not None and len(body) >= 4 and body[:4] == "ping":
            self.send_groupchat(room, "%s: %s" % (res, self.get_fortune()))

        # Look for bot commands
        if re.match(r"^%s:" % (self.name,), body):
            self.process_groupchat_cmd(room, res, body[7:].strip())

        # In order for the message to be logged, it needs to be from iembot
        # and have a channels attribute
        if res is None or res != 'iembot':
            return

        a = xpath.queryForNodes("/message/x[@xmlns='nwschat:nwsbot']", elem)
        if a is None or len(a) == 0:
            return

        if room not in CHATLOG:
            CHATLOG[room] = {'seqnum': [-1]*40, 'timestamps': [0]*40,
                             'log': ['']*40, 'author': ['']*40,
                             'product_id': ['']*40, 'txtlog': ['']*40}
        ts = datetime.datetime.utcnow()

        product_id = ''
        if elem.x and elem.x.hasAttribute("product_id"):
            product_id = elem.x['product_id']

        html = xpath.queryForNodes('/message/html/body', elem)
        logEntry = body
        if html is not None:
            logEntry = html[0].toXml()

        CHATLOG[room]['seqnum'] = (CHATLOG[room]['seqnum'][1:] +
                                   [self.next_seqnum(), ])
        CHATLOG[room]['timestamps'] = (CHATLOG[room]['timestamps'][1:] +
                                       [ts.strftime("%Y%m%d%H%M%S"), ])
        CHATLOG[room]['author'] = CHATLOG[room]['author'][1:] + [res, ]
        CHATLOG[room]['product_id'] = (CHATLOG[room]['product_id'][1:] +
                                       [product_id, ])
        CHATLOG[room]['log'] = CHATLOG[room]['log'][1:] + [logEntry, ]
        CHATLOG[room]['txtlog'] = CHATLOG[room]['txtlog'][1:] + [body, ]
    def _store_shared_statuses(self, iq):
        _status = xpath.queryForNodes('//status', iq)[0]
        _show = xpath.queryForNodes('//show', iq)[0]
        _invisible = xpath.queryForNodes('//invisible', iq)[0]
        self.shared_status = (str(_status),
                              str(_show),
                              _invisible.getAttribute('value'))

        _status_lists = xpath.queryForNodes('//status-list', iq)
        self.shared_status_lists = {}
        for s in _status_lists:
            self.shared_status_lists[s.getAttribute('show')] = \
                [str(e) for e in xpath.queryForNodes('//status', s)]
    def _expect_socks5_init(self):
        event = self.q.expect('stream-iq', iq_type='set')
        iq = event.stanza
        query = xpath.queryForNodes('/iq/query', iq)[0]
        assert query.uri == ns.BYTESTREAMS

        mode = query['mode']
        sid = query['sid']
        hosts = []

        for streamhost in xpath.queryForNodes('/query/streamhost', query):
            hosts.append((streamhost['jid'], streamhost['host'], int(streamhost['port'])))
        return iq['id'], mode, sid, hosts
def test_invisible_on_connect_fail_invalid_list(q, bus, conn, stream,
                                                really_invalid=False):
    props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE)
    assertNotEquals({}, props['Statuses'])

    presence_event_pattern = EventPattern('stream-presence')

    q.forbid_events([presence_event_pattern])

    conn.SimplePresence.SetPresence("hidden", "")

    conn.Connect()

    stream.handle_get_all_privacy_lists(q, bus, conn, ['invisible'])

    get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get')
    list_node = xpath.queryForNodes('//list', get_list.query)[0]
    assertEquals('invisible', list_node['name'])

    if really_invalid:
        # At one point Gabble would crash if the reply was of type 'result' but
        # wasn't well-formed.
        acknowledge_iq(stream, get_list.stanza)
    else:
        stream.send_privacy_list(get_list.stanza,
            [elem('item', type='jid', value='*****@*****.**', action='allow',
                 order='1')(elem('presence-out')),
            elem('item', action='deny', order='2')(elem('presence-out'))])

    create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set')
    created = xpath.queryForNodes('//list', create_list.stanza)[0]
    assertEquals(created["name"], 'invisible-gabble')
    acknowledge_iq (stream, create_list.stanza)

    q.unforbid_events([presence_event_pattern])

    activate_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set')
    active = xpath.queryForNodes('//active', activate_list.stanza)[0]
    assertEquals(active["name"], 'invisible-gabble')
    acknowledge_iq (stream, activate_list.stanza)

    q.expect_many(
        EventPattern('dbus-signal', signal='PresencesChanged',
                     interface=cs.CONN_IFACE_SIMPLE_PRESENCE,
                     args=[{1: (5, 'hidden', '')}]),
        EventPattern('dbus-signal', signal='StatusChanged',
                     args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]))

    # 'hidden' should not be an available status.
    assertContains("hidden",
        conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses"))
Example #25
0
def test(q, bus, conn, stream):
    statuses = conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE,
        'Statuses')

    # testbusy and testaway are provided by test plugin
    assertContains('testbusy', statuses)
    assertContains('testaway', statuses)

    assertEquals(statuses['testbusy'][0], cs.PRESENCE_BUSY)
    assertEquals(statuses['testaway'][0], cs.PRESENCE_AWAY)

    conn.SimplePresence.SetPresence('testbusy', '')

    conn.Connect()

    # ... gabble asks for all the available lists on the server ...
    stream.handle_get_all_privacy_lists(q, bus, conn,
        lists=["foo-list", "test-busy-list", "bar-list"])

    # ... gabble checks whether there's usable invisible list on the server ...
    get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get')
    list_node = xpath.queryForNodes('//list', get_list.query)[0]
    assertEquals('invisible', list_node['name'])

    error = domish.Element((None, 'error'))
    error['type'] = 'cancel'
    error.addElement((ns.STANZA, 'item-not-found'))
    send_error_reply (stream, get_list.stanza, error)

    # ... since there is none, Gabble creates it ...
    create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set')
    list_node = xpath.queryForNodes('//list', create_list.query)[0]
    assertEquals('invisible', list_node['name'])
    assertNotEquals([],
        xpath.queryForNodes('/query/list/item/presence-out',
            create_list.query))
    acknowledge_iq(stream, create_list.stanza)

    get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set')
    list_node = xpath.queryForNodes('//active', get_list.query)[0]

    # ... and then activates the one linked with the requested status
    # Note: testbusy status is linked to test-busy-list by test plugin
    assertEquals('test-busy-list', list_node['name'])
    acknowledge_iq(stream, get_list.stanza)

    q.expect('dbus-signal', signal='PresencesChanged',
        args=[{1L: (cs.PRESENCE_BUSY, u'testbusy', '')}])
Example #26
0
def test_with_xep0186(q, bus, conn, stream):
    statuses = conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE,
        'Statuses')

    # testbusy and testaway are provided by test plugin
    assertContains('testbusy', statuses)
    assertContains('testaway', statuses)

    assertEquals(statuses['testbusy'][0], cs.PRESENCE_BUSY)
    assertEquals(statuses['testaway'][0], cs.PRESENCE_AWAY)

    conn.SimplePresence.SetPresence('testbusy', '')

    conn.Connect()

    # ... gabble asks for all the available lists on the server ...
    stream.handle_get_all_privacy_lists(q, bus, conn,
        lists=["foo-list", "test-busy-list", "bar-list"])

    get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set')
    list_node = xpath.queryForNodes('//active', get_list.query)[0]

    # ... and then activates the one linked with the requested status
    # Note: testbusy status is linked to test-busy-list by test plugin
    assertEquals('test-busy-list', list_node['name'])
    acknowledge_iq(stream, get_list.stanza)

    q.expect('dbus-signal', signal='PresencesChanged',
        args=[{1L: (cs.PRESENCE_BUSY, u'testbusy', '')}])
Example #27
0
    def messageReceived(self, element):
        body = unicode(xpath.queryForNodes('/message/body', element)[0])
        words = body.split()
        command = words.pop(0).encode('ascii', 'replace')

        func = getattr(self, 'interact_' + command, None)
        if func:
            d = defer.maybeDeferred(func, element, words)
        else:
            d = defer.succeed(u"Unknown command '%s'. Try 'help'."
                    % (command,))

        def on_error(failure):
            log.error("Error in interactive handler '%s':\n%s", command,
                    failure.getTraceback())
            return (u'An internal error occurred in the rMake server. '
                    u'Please check the server logs for more information.')

        def on_reply(reply):
            if reply is None:
                reply = 'OK'
            msg = toResponse(element, 'chat')
            msg.addElement('body', content=unicode(reply))
            threads = xpath.queryForNodes('/message/thread', element)
            if threads:
                msg.addChild(threads[0])
            self.send(msg)

        d.addErrback(on_error)
        d.addCallback(on_reply)
Example #28
0
def try_to_join_muc(q, bus, conn, stream, muc, request=None):
    """
    Ask Gabble to join a MUC, and expect it to send <presence/>

    Returns: the stream-presence Event object.
    """

    if request is None:
        request = {
            cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
            cs.TARGET_HANDLE_TYPE: cs.HT_ROOM,
            cs.TARGET_ID: muc,
        }

    call_async(q, conn.Requests, 'CreateChannel',
        dbus.Dictionary(request, signature='sv'))

    join_event = q.expect('stream-presence', to='%s/test' % muc)
    # XEP-0045 §7.1.2 sez:
    #   “MUC clients SHOULD signal their ability to speak the MUC protocol by
    #   including in the initial presence stanza an empty <x/> element
    #   qualified by the 'http://jabber.org/protocol/muc' namespace.”
    x_muc_nodes = xpath.queryForNodes('/presence/x[@xmlns="%s"]' % ns.MUC,
        join_event.stanza)
    assertLength(1, x_muc_nodes)

    return join_event
    def accept_file(self):
        self.address = self.ft_channel.AcceptFile(self.address_type,
                self.access_control, self.access_control_param, self.file.offset,
                byte_arrays=True)

        state_event, iq_event = self.q.expect_many(
            EventPattern('dbus-signal', signal='FileTransferStateChanged'),
            EventPattern('stream-iq', iq_type='result'))

        state, reason = state_event.args
        assert state == cs.FT_STATE_ACCEPTED
        assert reason == cs.FT_STATE_CHANGE_REASON_REQUESTED

        # Got SI reply
        self.bytestream.check_si_reply(iq_event.stanza)

        if self.file.offset != 0:
            range = xpath.queryForNodes('/iq/si/file/range', iq_event.stanza)[0]
            assert range['offset'] == str(self.file.offset)

        _, events = self.bytestream.open_bytestream([], [
            EventPattern('dbus-signal', signal='InitialOffsetDefined'),
            EventPattern('dbus-signal', signal='FileTransferStateChanged')])

        offset_event, state_event = events

        offset = offset_event.args[0]
        assert offset == self.file.offset

        state, reason = state_event.args
        assert state == cs.FT_STATE_OPEN
        assert reason == cs.FT_STATE_CHANGE_REASON_NONE

        # send the beginning of the file (client didn't connect to socket yet)
        self.bytestream.send_data(self.file.data[self.file.offset:self.file.offset + 2])
def test_invisible_on_connect_fail_no_list(q, bus, conn, stream):
    props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE)
    assertNotEquals({}, props['Statuses'])

    presence_event_pattern = EventPattern('stream-presence')

    q.forbid_events([presence_event_pattern])

    conn.SimplePresence.SetPresence("hidden", "")

    conn.Connect()

    stream.handle_get_all_privacy_lists(q, bus, conn)

    get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get')
    list_node = xpath.queryForNodes('//list', get_list.query)[0]
    assertEquals('invisible', list_node['name'])

    send_error_reply(stream, get_list.stanza)

    q.unforbid_events([presence_event_pattern])

    # Darn! At least we should have our presence set to DND.
    q.expect_many(
        EventPattern('dbus-signal', signal='PresencesChanged',
                     interface=cs.CONN_IFACE_SIMPLE_PRESENCE,
                     args=[{1: (6, 'dnd', '')}]),
        EventPattern('dbus-signal', signal='StatusChanged',
                     args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]))

    # 'hidden' should not be an available status.
    assertDoesNotContain("hidden",
        conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses"))
Example #31
0
def run_incoming_test(q, bus, conn, stream, bob_leaves_room = False):
    jp = JingleProtocol031 ()
    jt = JingleTest2(jp, conn, q, stream, 'test@localhost', muc + "/bob")
    jt.prepare()
    forbidden = [ no_muji_presences (muc) ]

    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")

    _, _, test_handle, bob_handle = \
        join_muc_and_check(q, bus, conn, stream, muc)

    presence = make_muc_presence('owner', 'moderator', muc, 'bob')
    muji =  ('muji', ns.MUJI, {},
        [('content', ns.MUJI, { "name": "Voice" },
            [( 'description', ns.JINGLE_RTP, {"media": "audio"},
            jt.generate_payloads(jt.audio_codecs))])])
    presence.addChild(jp._simple_xml(muji))

    stream.send(presence)

    e = q.expect ('dbus-signal',
        signal='NewChannels',
        predicate=lambda e: \
            e.args[0][0][1][cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_CALL )

    (path, props) = e.args[0][0]

    assertContains((cs.CHANNEL_TYPE_CALL + '.InitialAudio', True),
        props.items())
    assertContains((cs.CHANNEL_TYPE_CALL + '.InitialVideo', False),
        props.items())

    general_tests (jp, q, bus, conn, stream, path, props)

    channel = bus.get_object (conn.bus_name, path)
    props = channel.GetAll (cs.CHANNEL_TYPE_CALL,
        dbus_interface = dbus.PROPERTIES_IFACE)

    content = bus.get_object (conn.bus_name, props['Contents'][0])

    check_state (q, channel, cs.CALL_STATE_PENDING_RECEIVER)

    md = jt.get_call_audio_md_dbus()
    check_and_accept_offer (q, bus, conn, self_handle,
            content, md)
    channel.Accept (dbus_interface=cs.CHANNEL_TYPE_CALL)

    # Preparing stanza
    e = q.expect('stream-presence', to = muc + "/test")
    echo_muc_presence (q, stream, e.stanza, 'none', 'participant')

    # Codecs stanza
    e = q.expect('stream-presence', to = muc + "/test")
    echo_muc_presence (q, stream, e.stanza, 'none', 'participant')

    # Gabble shouldn't send new presences for a while
    q.forbid_events(forbidden)

    e = q.expect ('dbus-signal', signal = 'StreamsAdded')
    cstream = bus.get_object (conn.bus_name, e.args[0][0])

    cstream.SetCredentials(jt.ufrag, jt.pwd,
        dbus_interface=cs.CALL_STREAM_IFACE_MEDIA)

    candidates = jt.get_call_remote_transports_dbus ()
    cstream.AddCandidates (candidates,
        dbus_interface=cs.CALL_STREAM_IFACE_MEDIA)

    e = q.expect('stream-iq',
        predicate=jp.action_predicate('session-initiate'))
    jt.parse_session_initiate (e.query)

    jt.accept()

   # Bob adds a Video content
    presence = make_muc_presence('owner', 'moderator', muc, 'bob')
    presence.addElement ((ns.MUJI, 'muji')).addElement('preparing')
    stream.send(presence)

    presence = make_muc_presence('owner', 'moderator', muc, 'bob')
    muji =  ('muji', ns.MUJI, {},
        [('content', ns.MUJI, { "name": "Voice" },
            [( 'description', ns.JINGLE_RTP, {"media": "audio"},
            jt.generate_payloads(jt.audio_codecs))]),
         ('content', ns.MUJI, { "name": "Camera" },
            [( 'description', ns.JINGLE_RTP, {"media": "video"},
            jt.generate_payloads(jt.video_codecs))]),
        ])
    presence.addChild(jp._simple_xml(muji))
    stream.send(presence)

    # Gabble noticed bob added a content
    e = q.expect('dbus-signal', signal = 'ContentAdded')

    q.unforbid_events (forbidden)
    content = bus.get_object (conn.bus_name, e.args[0])
    check_and_accept_offer (q, bus, conn, self_handle,
            content, jt.get_call_video_codecs_dbus(),
            check_codecs_changed = False)

    # Gabble sends a presence to prepare
    e = q.expect('stream-presence', to = muc + "/test")
    echo_muc_presence (q, stream, e.stanza, 'none', 'participant')

    # Gabble sends a presence with the video codecs
    e = q.expect('stream-presence', to = muc + "/test")
    echo_muc_presence (q, stream, e.stanza, 'none', 'participant')

    # Gabble adds a content to the jingle session and thus a stream is added
    e = q.expect ('dbus-signal', signal = 'StreamsAdded')
    cstream = bus.get_object (conn.bus_name, e.args[0][0])

    candidates = jt.get_call_remote_transports_dbus ()
    cstream.AddCandidates (candidates,
        dbus_interface=cs.CALL_STREAM_IFACE_MEDIA)

    # And now the content-add on the jingle streams
    e = q.expect('stream-iq', to = muc + "/bob",
        predicate = lambda x: \
        xpath.queryForNodes("/iq/jingle[@action='content-add']", x.stanza))

    # Bob leaves the call, bye bob
    if bob_leaves_room:
        presence = make_muc_presence('owner', 'moderator', muc, 'bob')
        presence['type'] = 'unavailable'
    else:
        presence = make_muc_presence('owner', 'moderator', muc, 'bob')

    stream.send(presence)
    (cmembers, _, _) = q.expect_many(
        EventPattern ('dbus-signal', signal = 'CallMembersChanged'),
        # Audio and video stream
        EventPattern ('dbus-signal', signal = 'StreamsRemoved'),
        EventPattern ('dbus-signal', signal = 'StreamsRemoved'))


    # Just bob left
    assertLength (1, cmembers.args[1])
def test_hash(q, bus, conn, stream, contact, contact_handle, client):
    presence = make_presence(contact, status='hello')
    stream.send(presence)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    # we can now do audio calls
    cc, = q.expect_many(
        EventPattern('dbus-signal', signal='ContactCapabilitiesChanged'), )
    assert_rccs_callable(cc.args[0][contact_handle])
    assertEquals(cc.args[0],
                 get_contacts_capabilities_sync(conn, [contact_handle]))
def receive_caps(q,
                 bus,
                 conn,
                 service,
                 contact,
                 contact_handle,
                 features,
                 expected_caps,
                 expect_disco=True,
                 expect_ccc=True):

    ver = compute_caps_hash([], features, {})
    txt_record = {
        "txtvers": "1",
        "status": "avail",
        "node": client,
        "ver": ver,
        "hash": "sha-1"
    }

    listener, port = setup_stream_listener(q, contact)
    AvahiAnnouncer(contact, "_presence._tcp", port, txt_record)

    if expect_disco:
        # Salut looks up our capabilities
        e = q.expect('incoming-connection', listener=listener)
        stream = e.connection

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

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

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

        stream.send(result)

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

    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn.Contacts.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.ATTR_CONTACT_CAPABILITIES]
    assertSameElements(expected_caps, caps_via_contacts_iface)

    # close the connection and expect a new one to be opened by Salut
    # the next time we need some discoing doing
    if expect_disco:
        stream.send('</stream:stream>')
        stream.transport.loseConnection()
        # pass some time so Salut knows the connection is lost and
        # won't try and send stuff down a closed connection on the
        # next test.
        sync_dbus(bus, q, conn)
Example #34
0
class ReceiveFileTest(FileTransferTest):
    def __init__(self, bytestream_cls, file, address_type, access_control,
                 access_control_param):
        FileTransferTest.__init__(self, bytestream_cls, file, address_type,
                                  access_control, access_control_param)

        self._actions = [
            self.connect, self.announce_contact, self.send_ft_offer_iq,
            self.check_new_channel, self.create_ft_channel, self.set_uri,
            self.accept_file, self.receive_file, self.close_channel, self.done
        ]

    def send_ft_offer_iq(self):
        self.bytestream = self.bytestream_cls(self.stream, self.q, 'alpha',
                                              self.contact_full_jid,
                                              'test@localhost/Resource', True)

        iq, si = self.bytestream.create_si_offer(ns.FILE_TRANSFER)

        file_node = si.addElement((ns.FILE_TRANSFER, 'file'))
        file_node['name'] = self.file.name
        file_node['size'] = str(self.file.size)
        file_node['mime-type'] = self.file.content_type
        file_node['hash'] = self.file.hash
        date = datetime.datetime.utcfromtimestamp(
            self.file.date).strftime('%FT%H:%M:%SZ')
        file_node['date'] = date

        file_node.addElement('desc', content=self.file.description)
        # we support range transfer
        file_node.addElement('range')

        # Metadata
        if self.service_name:
            service_form = {
                ns.TP_FT_METADATA_SERVICE: {
                    'ServiceName': [self.service_name]
                }
            }
            add_data_forms(file_node, service_form)

        if self.metadata:
            metadata_form = {ns.TP_FT_METADATA: self.metadata}
            add_data_forms(file_node, metadata_form)

        # so... lunch?
        iq.send()

    def check_new_channel(self):
        def is_ft_channel_event(event):
            channels, = event.args

            if len(channels) > 1:
                return False

            path, props = channels[0]
            return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER

        e = self.q.expect('dbus-signal',
                          signal='NewChannels',
                          path=self.conn.object.object_path,
                          predicate=is_ft_channel_event)

        channels = e.args[0]
        assert len(channels) == 1
        path, props = channels[0]

        # check channel properties
        # Channel D-Bus properties
        assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER
        assertSameSets([
            cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA,
        ], props[cs.INTERFACES])
        assert props[cs.TARGET_HANDLE] == self.handle
        assert props[cs.TARGET_ID] == self.contact_name
        assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT
        assert props[cs.REQUESTED] == False
        assert props[cs.INITIATOR_HANDLE] == self.handle
        assert props[cs.INITIATOR_ID] == self.contact_name

        # Channel.Type.FileTransfer D-Bus properties
        assert props[cs.FT_STATE] == cs.FT_STATE_PENDING
        assert props[cs.FT_CONTENT_TYPE] == self.file.content_type
        assert props[cs.FT_FILENAME] == self.file.name
        assert props[cs.FT_SIZE] == self.file.size
        # FT's protocol doesn't allow us the send the hash info
        assert props[cs.FT_CONTENT_HASH_TYPE] == cs.FILE_HASH_TYPE_MD5
        assert props[cs.FT_CONTENT_HASH] == self.file.hash
        assert props[cs.FT_DESCRIPTION] == self.file.description
        assert props[cs.FT_DATE] == self.file.date
        assert props[cs.FT_TRANSFERRED_BYTES] == 0
        assert props[cs.FT_INITIAL_OFFSET] == 0

        self.check_platform_socket_types(props[cs.FT_AVAILABLE_SOCKET_TYPES])

        assertEquals(self.service_name, props[cs.FT_SERVICE_NAME])
        assertEquals(self.metadata, props[cs.FT_METADATA])

        self.ft_path = path

    def set_uri(self):
        ft_props = dbus.Interface(self.ft_channel, cs.PROPERTIES_IFACE)

        # URI is not set yet
        uri = ft_props.Get(cs.CHANNEL_TYPE_FILE_TRANSFER, 'URI')
        assertEquals('', uri)

        # Setting URI
        call_async(self.q, ft_props, 'Set', cs.CHANNEL_TYPE_FILE_TRANSFER,
                   'URI', self.file.uri)

        self.q.expect('dbus-signal', signal='URIDefined', args=[self.file.uri])

        self.q.expect('dbus-return', method='Set')

        # Check it has the right value now
        uri = ft_props.Get(cs.CHANNEL_TYPE_FILE_TRANSFER, 'URI')
        assertEquals(self.file.uri, uri)

        # We can't change it once it has been set
        call_async(self.q, ft_props, 'Set', cs.CHANNEL_TYPE_FILE_TRANSFER,
                   'URI', 'badger://snake')
        self.q.expect('dbus-error', method='Set', name=cs.INVALID_ARGUMENT)

    def accept_file(self):
        try:
            self.address = self.ft_channel.AcceptFile(
                self.address_type,
                self.access_control,
                self.access_control_param,
                self.file.offset,
                byte_arrays=True)
        except dbus.DBusException, e:
            if self.address_type == cs.SOCKET_ADDRESS_TYPE_IPV6 and \
                e.get_dbus_name() == cs.NOT_AVAILABLE and \
                e.get_dbus_message() == "Could not set up local socket":
                print "Ignoring error for ipv6 address space"
                return True
            else:
                raise e

        state_event, iq_event = self.q.expect_many(
            EventPattern('dbus-signal', signal='FileTransferStateChanged'),
            EventPattern('stream-iq', iq_type='result'))

        state, reason = state_event.args
        assert state == cs.FT_STATE_ACCEPTED
        assert reason == cs.FT_STATE_CHANGE_REASON_REQUESTED

        # Got SI reply
        self.bytestream.check_si_reply(iq_event.stanza)

        if self.file.offset != 0:
            range = xpath.queryForNodes('/iq/si/file/range',
                                        iq_event.stanza)[0]
            assert range['offset'] == str(self.file.offset)

        _, events = self.bytestream.open_bytestream([], [
            EventPattern('dbus-signal', signal='InitialOffsetDefined'),
            EventPattern('dbus-signal', signal='FileTransferStateChanged')
        ])

        offset_event, state_event = events

        offset = offset_event.args[0]
        assert offset == self.file.offset

        state, reason = state_event.args
        assert state == cs.FT_STATE_OPEN
        assert reason == cs.FT_STATE_CHANGE_REASON_NONE

        # send the beginning of the file (client didn't connect to socket yet)
        self.bytestream.send_data(
            self.file.data[self.file.offset:self.file.offset + 2])
def test_ft_caps_from_contact(q, bus, conn, stream, contact, contact_handle, client):

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

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

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

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

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

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

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

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

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

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

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

    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assert caps == generic_tubes_caps, caps
    # test again, to check GetContactCapabilities does not have side effect
    caps = get_contacts_capabilities_sync(conn, [contact_handle])
    assert caps == generic_tubes_caps, caps
    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.ATTR_CONTACT_CAPABILITIES]
    assert caps_via_contacts_iface == caps[contact_handle], \
                                    caps_via_contacts_iface
Example #36
0
    def request_ft_channel(self):
        self.ft_path, props = self.conn.Requests.CreateChannel({
            cs.CHANNEL_TYPE:
            cs.CHANNEL_TYPE_FILE_TRANSFER,
            cs.TARGET_HANDLE_TYPE:
            cs.HT_CONTACT,
            cs.TARGET_HANDLE:
            self.handle,
            cs.FT_CONTENT_TYPE:
            self.file.content_type,
            cs.FT_FILENAME:
            self.file.name,
            cs.FT_SIZE:
            self.file.size,
            cs.FT_CONTENT_HASH_TYPE:
            self.file.hash_type,
            cs.FT_CONTENT_HASH:
            self.file.hash,
            cs.FT_DESCRIPTION:
            self.file.description,
            cs.FT_DATE:
            self.file.date,
            cs.FT_INITIAL_OFFSET:
            0,
        })

        # org.freedesktop.Telepathy.Channel D-Bus properties
        assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER
        assertSameSets([
            cs.CHANNEL_IFACE_FILE_TRANSFER_METADATA,
        ], props[cs.INTERFACES])
        assert props[cs.TARGET_HANDLE] == self.handle
        assert props[cs.TARGET_ID] == self.target
        assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT
        assert props[cs.REQUESTED] == True
        assert props[cs.INITIATOR_HANDLE] == self.self_handle
        assert props[cs.INITIATOR_ID] == self.self_handle_name

        # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties
        assert props[cs.FT_STATE] == cs.FT_STATE_PENDING
        assert props[cs.FT_CONTENT_TYPE] == self.file.content_type
        assert props[cs.FT_FILENAME].encode('utf-8') == self.file.name, props
        assert props[cs.FT_SIZE] == self.file.size
        assert props[cs.FT_CONTENT_HASH_TYPE] == self.file.hash_type
        assert props[cs.FT_CONTENT_HASH] == self.file.hash
        assert props[cs.FT_DESCRIPTION] == self.file.description
        assert props[cs.FT_DATE] == self.file.date
        assert props[cs.FT_AVAILABLE_SOCKET_TYPES] == \
            {cs.SOCKET_ADDRESS_TYPE_UNIX: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST],
            cs.SOCKET_ADDRESS_TYPE_IPV4: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST],
            cs.SOCKET_ADDRESS_TYPE_IPV6: [cs.SOCKET_ACCESS_CONTROL_LOCALHOST]}, \
            props[cs.FT_AVAILABLE_SOCKET_TYPES]
        assert props[cs.FT_TRANSFERRED_BYTES] == 0
        assert props[cs.FT_INITIAL_OFFSET] == 0

        self.create_ft_channel()

        self.open = False
        self.offset_defined = False

        def initial_offset_defined_cb(offset):
            self.offset_defined = True
            assert offset == 0, offset

        self.ft_channel.connect_to_signal('InitialOffsetDefined',
                                          initial_offset_defined_cb)

        # Make sure the file transfer is of type jingle-share
        event = self.q.expect('stream-iq',
                              stream=self.stream,
                              query_name='session',
                              query_ns=ns.GOOGLE_SESSION)
        description_node = xpath.queryForNodes('/iq/session/description',
                                               event.stanza)[0]
        assert description_node.uri == ns.GOOGLE_SESSION_SHARE, \
            description_node.uri
def test2(q, bus, connections, streams):
    conn1, conn2 = connections
    stream1, stream2 = streams
    conn1_handle = conn1.Properties.Get(cs.CONN, 'SelfHandle')
    conn1_jid = conn1.inspect_contact_sync(conn1_handle)
    conn2_handle = conn2.Properties.Get(cs.CONN, 'SelfHandle')
    conn2_jid = conn2.inspect_contact_sync(conn2_handle)
    handle1 = conn2.get_contact_handle_sync(conn1_jid)
    handle2 = conn1.get_contact_handle_sync(conn2_jid)

    q.expect_many(
        EventPattern('dbus-signal',
                     signal='ContactCapabilitiesChanged',
                     path=conn1.object.object_path),
        EventPattern('dbus-signal',
                     signal='ContactCapabilitiesChanged',
                     path=conn2.object.object_path))

    check_contact_caps(conn1, handle2, False)
    check_contact_caps(conn2, handle1, False)

    caps_iface = dbus.Interface(conn1, cs.CONN_IFACE_CONTACT_CAPS)
    caps_iface.UpdateCapabilities([("self", [ft_fixed_properties],
                                    dbus.Array([], signature="s"))])

    _, presence, disco, _ = \
        q.expect_many(EventPattern('dbus-signal',
                                   signal='ContactCapabilitiesChanged',
                                   path=conn1.object.object_path,
                                   args=[{conn1_handle:generic_ft_caps}]),
                      EventPattern('stream-presence', stream=stream1),
                      EventPattern('stream-iq', stream=stream1,
                                   query_ns=ns.DISCO_INFO,
                                   iq_type = 'result'),
                      EventPattern('dbus-signal',
                                   signal='ContactCapabilitiesChanged',
                                   path=conn2.object.object_path,
                                   args=[{handle1:generic_ft_caps}]))

    presence_c = xpath.queryForNodes('/presence/c', presence.stanza)[0]
    assert "share-v1" in presence_c.attributes['ext']

    conn1_ver = presence_c.attributes['ver']

    found_share = False
    for feature in xpath.queryForNodes('/iq/query/feature', disco.stanza):
        if feature.attributes['var'] == ns.GOOGLE_FEAT_SHARE:
            found_share = True
    assert found_share

    check_contact_caps(conn2, handle1, True)

    caps_iface = dbus.Interface(conn2, cs.CONN_IFACE_CONTACT_CAPS)
    caps_iface.UpdateCapabilities([("self", [ft_fixed_properties],
                                    dbus.Array([], signature="s"))])

    _, presence, _ = \
        q.expect_many(EventPattern('dbus-signal',
                                   signal='ContactCapabilitiesChanged',
                                   path=conn2.object.object_path,
                                   args=[{conn2_handle:generic_ft_caps}]),
                      EventPattern('stream-presence', stream=stream2),
                      EventPattern('dbus-signal',
                                   signal='ContactCapabilitiesChanged',
                                   path=conn1.object.object_path,
                                   args=[{handle2:generic_ft_caps}]))

    presence_c = xpath.queryForNodes('/presence/c', presence.stanza)[0]
    assert "share-v1" in presence_c.attributes['ext']

    # We will have the same capabilities on both sides, so we can't check for
    # a cap disco since the hash will be the same, so we need to make sure the
    # hash is indeed the same
    assert presence_c.attributes['ver'] == conn1_ver

    found_share = False
    for feature in xpath.queryForNodes('/iq/query/feature', disco.stanza):
        if feature.attributes['var'] == ns.GOOGLE_FEAT_SHARE:
            found_share = True
    assert found_share

    check_contact_caps(conn1, handle2, True)
def test(q, bus, conn, stream):
    # Initial vCard request. Respond only after we call SetAliases().
    vcard_get_event = q.expect('stream-iq',
                               iq_type='get',
                               to=None,
                               query_ns=ns.VCARD_TEMP,
                               query_name='vCard')
    sync_stream(q, stream)

    handle = conn.Properties.Get(cs.CONN, "SelfHandle")
    call_async(q, conn.Aliasing, 'SetAliases', {handle: 'Robert the Bruce'})
    sync_dbus(bus, q, conn)
    acknowledge_iq(stream, vcard_get_event.stanza)

    # Gabble sets a new vCard with our nickname.
    vcard_set_event = q.expect('stream-iq',
                               iq_type='set',
                               query_ns=ns.VCARD_TEMP,
                               query_name='vCard')
    assertEquals(
        'Robert the Bruce',
        xpath.queryForString('/iq/vCard/NICKNAME', vcard_set_event.stanza))
    assertEquals(
        None, xpath.queryForNodes('/iq/vCard/PHOTO', vcard_set_event.stanza))
    assertEquals(None,
                 xpath.queryForNodes('/iq/vCard/FN', vcard_set_event.stanza))
    assertEquals(None,
                 xpath.queryForNodes('/iq/vCard/N', vcard_set_event.stanza))

    # Before the server replies, the user sets their avatar
    call_async(q, conn.Avatars, 'SetAvatar', 'hello', 'image/png')
    sync_dbus(bus, q, conn)
    # This acknowledgement is for the nickname
    acknowledge_iq(stream, vcard_set_event.stanza)

    hello_binval = base64.b64encode('hello')

    # This sets the avatar
    vcard_set_event = q.expect('stream-iq',
                               iq_type='set',
                               query_ns=ns.VCARD_TEMP,
                               query_name='vCard')
    assertEquals(
        'Robert the Bruce',
        xpath.queryForString('/iq/vCard/NICKNAME', vcard_set_event.stanza))
    assertLength(
        1, xpath.queryForNodes('/iq/vCard/PHOTO', vcard_set_event.stanza))
    assertEquals(
        'image/png',
        xpath.queryForString('/iq/vCard/PHOTO/TYPE', vcard_set_event.stanza))
    binval = xpath.queryForString('/iq/vCard/PHOTO/BINVAL',
                                  vcard_set_event.stanza)
    assertEquals(hello_binval, binval.strip())
    assertEquals(None,
                 xpath.queryForNodes('/iq/vCard/FN', vcard_set_event.stanza))
    assertEquals(None,
                 xpath.queryForNodes('/iq/vCard/N', vcard_set_event.stanza))

    # Before the server replies, the user sets their ContactInfo
    call_async(q, conn.ContactInfo, 'SetContactInfo',
               [(u'fn', [], [u'King Robert I']),
                (u'n', [], [u'de Brus', u'Robert', u'', u'King', u'']),
                (u'nickname', [], [u'Bob'])])
    sync_dbus(bus, q, conn)
    # This acknowledgement is for the avatar; SetAvatar won't happen
    # until this has
    acknowledge_iq(stream, vcard_set_event.stanza)

    # This sets the ContactInfo
    vcard_set_event, _ = q.expect_many(
        EventPattern('stream-iq',
                     iq_type='set',
                     query_ns=ns.VCARD_TEMP,
                     query_name='vCard'),
        EventPattern('dbus-return', method='SetAvatar'))

    assertEquals(
        'Bob',
        xpath.queryForString('/iq/vCard/NICKNAME', vcard_set_event.stanza))
    assertLength(
        1, xpath.queryForNodes('/iq/vCard/PHOTO', vcard_set_event.stanza))
    assertEquals(
        'image/png',
        xpath.queryForString('/iq/vCard/PHOTO/TYPE', vcard_set_event.stanza))
    binval = xpath.queryForString('/iq/vCard/PHOTO/BINVAL',
                                  vcard_set_event.stanza)
    assertEquals(hello_binval, binval.strip())
    assertLength(1, xpath.queryForNodes('/iq/vCard/N', vcard_set_event.stanza))
    assertEquals(
        'Robert',
        xpath.queryForString('/iq/vCard/N/GIVEN', vcard_set_event.stanza))
    assertEquals(
        'de Brus',
        xpath.queryForString('/iq/vCard/N/FAMILY', vcard_set_event.stanza))
    assertEquals(
        'King',
        xpath.queryForString('/iq/vCard/N/PREFIX', vcard_set_event.stanza))
    assertEquals('King Robert I',
                 xpath.queryForString('/iq/vCard/FN', vcard_set_event.stanza))

    # Before the server replies, the user unsets their avatar
    call_async(q, conn.Avatars, 'SetAvatar', '', '')
    sync_dbus(bus, q, conn)

    # This acknowledgement is for the ContactInfo; SetContactInfo won't happen
    # until this has
    acknowledge_iq(stream, vcard_set_event.stanza)

    vcard_set_event, _ = q.expect_many(
        EventPattern('stream-iq',
                     iq_type='set',
                     query_ns=ns.VCARD_TEMP,
                     query_name='vCard'),
        EventPattern('dbus-return', method='SetContactInfo'))
    assertEquals(
        'Bob',
        xpath.queryForString('/iq/vCard/NICKNAME', vcard_set_event.stanza))
    assertEquals(
        None, xpath.queryForNodes('/iq/vCard/PHOTO', vcard_set_event.stanza))
    assertLength(1, xpath.queryForNodes('/iq/vCard/N', vcard_set_event.stanza))
    assertEquals(
        'Robert',
        xpath.queryForString('/iq/vCard/N/GIVEN', vcard_set_event.stanza))
    assertEquals(
        'de Brus',
        xpath.queryForString('/iq/vCard/N/FAMILY', vcard_set_event.stanza))
    assertEquals(
        'King',
        xpath.queryForString('/iq/vCard/N/PREFIX', vcard_set_event.stanza))
    assertEquals('King Robert I',
                 xpath.queryForString('/iq/vCard/FN', vcard_set_event.stanza))

    # This acknowledgement is for the avatar; SetAvatar won't finish
    # until this is received
    acknowledge_iq(stream, vcard_set_event.stanza)
    q.expect('dbus-return', method='SetAvatar')

    # Now Gabble gets disconnected.
    sync_stream(q, stream)
    disconnect_conn(q, conn, stream)
def test(q, bus, conn, stream):
    self_presence = q.expect('stream-presence')

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

    jid = '[email protected]/omg'

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

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

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

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

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

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

        # Gabble should still know what they are, and reply. This is
        # actually quite important: there's a bug in iChat where if you
        # return an error to a disco query, it just asks again, and again,
        # and again...
        reply = q.expect('stream-iq', to=jid)
        assertEquals('result', reply.iq_type)
def test(q, bus, conn):
    conn.Connect()
    q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
    basic_txt = { "txtvers": "1", "status": "avail" }

    contact_name = "test-text-channel@" + get_host_name()
    listener, port = setup_stream_listener(q, contact_name)

    announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt)

    handle = wait_for_contact_in_publish(q, bus, conn, contact_name)

    t = conn.Requests.CreateChannel({
        cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
        cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
        cs.TARGET_HANDLE: handle})[0]
    text_channel = make_channel_proxy(conn, t, "Channel.Type.Text")
    text_channel.Send(cs.MT_NORMAL, INCOMING_MESSAGE)

    e = q.expect('incoming-connection', listener = listener)
    incoming = e.connection

    e = q.expect('stream-message', connection = incoming)
    assert e.message_type == "chat"
    body = xpath.queryForNodes("/message/body",  e.stanza )
    assert map(str, body) == [ INCOMING_MESSAGE ]

    # drop the connection
    incoming.transport.loseConnection()

    # Now send a message to salut
    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
    self_handle_name =  conn.Properties.Get(cs.CONN, "SelfID")


    AvahiListener(q).listen_for_service("_presence._tcp")
    e = q.expect('service-added', name = self_handle_name,
        protocol = avahi.PROTO_INET)
    service = e.service
    service.resolve()


    e = q.expect('service-resolved', service = service)

    outbound = connect_to_stream(q, contact_name,
        self_handle_name, str(e.pt), e.port)

    e = q.expect('connection-result')
    assert e.succeeded, e.reason

    e = q.expect('stream-opened', connection = outbound)

    # connected to salut, now send a message
    message = domish.Element(('', 'message'))
    message['type'] = "chat"
    message.addElement('body', content=OUTGOING_MESSAGE)

    e.connection.send(message)

    e = q.expect('dbus-signal', signal='Received')
    assert e.args[2] == handle
    assert e.args[3] == cs.MT_NORMAL
    assert e.args[5] == OUTGOING_MESSAGE
Example #41
0
def test_ft_caps_from_contact(q, bus, conn, client):

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

    # send presence with FT capa
    ver = compute_caps_hash([], [ns.IQ_OOB], {})
    txt_record = {
        "txtvers": "1",
        "status": "avail",
        "node": client,
        "ver": ver,
        "hash": "sha-1"
    }
    contact_name = "test-caps-ft@" + get_host_name()
    listener, port = setup_stream_listener(q, contact_name)
    announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port,
                               txt_record)

    # this is the first presence, Salut connects to the contact
    e = q.expect('incoming-connection', listener=listener)
    incoming = e.connection

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

    contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0]

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

    feature = query.addElement('feature')
    feature['var'] = ns.IQ_OOB
    incoming.send(result)

    # FT capa is announced
    e = q.expect('dbus-signal',
                 signal='ContactCapabilitiesChanged',
                 predicate=lambda e: contact_handle in e.args[0])
    caps = e.args[0][contact_handle]
    assertContains(ft_caps, caps)

    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities']
    assert caps_via_contacts_iface == caps, caps_via_contacts_iface

    # check if Salut announces the OOB capa
    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")
    self_handle_name = conn.Properties.Get(cs.CONN, "SelfID")

    AvahiListener(q).listen_for_service("_presence._tcp")
    e = q.expect('service-added',
                 name=self_handle_name,
                 protocol=avahi.PROTO_INET)
    service = e.service
    service.resolve()

    receive_presence_and_ask_caps(q, incoming, service, contact_name)

    # capa announced without FT
    ver = compute_caps_hash([], ["http://telepathy.freedesktop.org/xmpp/pony"],
                            {})
    txt_record = {
        "txtvers": "1",
        "status": "avail",
        "node": client,
        "ver": ver,
        "hash": "sha-1"
    }
    contact_name = "test-caps-ft2@" + get_host_name()
    listener, port = setup_stream_listener(q, contact_name)
    announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port,
                               txt_record)

    # this is the first presence, Salut connects to the contact
    e = q.expect('incoming-connection', listener=listener)
    incoming = e.connection

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

    contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0]

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

    feature = query.addElement('feature')
    feature['var'] = "http://telepathy.freedesktop.org/xmpp/pony"
    incoming.send(result)

    # the FT capability is not announced
    e = q.expect('dbus-signal', signal='ContactCapabilitiesChanged')
    caps = e.args[0][contact_handle]
    assertDoesNotContain(ft_caps, caps)

    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities']
    assert caps_via_contacts_iface == caps, caps_via_contacts_iface

    # no capabilites announced (assume FT is supported to insure interop)
    txt_record = {"txtvers": "1", "status": "avail"}
    contact_name = "test-caps-ft-no-capa2@" + get_host_name()
    contact_handle = conn_contacts_iface.GetContactByID(contact_name, [])[0]
    listener, port = setup_stream_listener(q, contact_name)
    announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port,
                               txt_record)

    # FT capa is announced
    e = q.expect('dbus-signal',
                 signal='ContactCapabilitiesChanged',
                 predicate=lambda e: contact_handle in e.args[0].keys())

    caps = e.args[0][contact_handle]
    assertContains(ft_caps, caps)

    # check the Contacts interface give the same caps
    caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
            [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \
            [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities']
    assert caps_via_contacts_iface == caps, caps_via_contacts_iface
def test(q, bus, conn, stream, bytestream_cls, address_type, access_control,
         access_control_param):
    address1 = t.set_up_echo(q, address_type, True, streamfile='stream')
    address2 = t.set_up_echo(q, address_type, True, streamfile='stream2')

    t.check_conn_properties(q, conn)

    vcard_event, roster_event = q.expect_many(
        EventPattern('stream-iq',
                     to=None,
                     query_ns='vcard-temp',
                     query_name='vCard'),
        EventPattern('stream-iq', query_ns=ns.ROSTER))

    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")

    acknowledge_iq(stream, vcard_event.stanza)

    roster = roster_event.stanza
    roster['type'] = 'result'
    item = roster_event.query.addElement('item')
    item['jid'] = 'bob@localhost'  # Bob can do tubes
    item['subscription'] = 'both'
    stream.send(roster)

    bob_full_jid = 'bob@localhost/Bob'
    self_full_jid = 'test@localhost/Resource'

    # Send Bob presence and his tube caps
    presence = domish.Element(('jabber:client', 'presence'))
    presence['from'] = bob_full_jid
    presence['to'] = self_full_jid
    c = presence.addElement('c')
    c['xmlns'] = 'http://jabber.org/protocol/caps'
    c['node'] = 'http://example.com/ICantBelieveItsNotTelepathy'
    c['ver'] = '1.2.3'
    stream.send(presence)

    event = q.expect('stream-iq',
                     iq_type='get',
                     query_ns='http://jabber.org/protocol/disco#info',
                     to=bob_full_jid)
    assert event.query['node'] == \
        'http://example.com/ICantBelieveItsNotTelepathy#1.2.3'
    result = make_result_iq(stream, event.stanza)
    query = result.firstChildElement()
    feature = query.addElement('feature')
    feature['var'] = ns.TUBES
    stream.send(result)

    # A tube request can be done only if the contact has tube capabilities
    # Ensure that Bob's caps have been received
    sync_stream(q, stream)

    # Also ensure that all the new contact list channels have been announced,
    # so that the NewChannel(s) signals we look for after calling
    # RequestChannel are the ones we wanted.
    sync_dbus(bus, q, conn)

    # Test tubes with Bob. Bob has tube capabilities.
    bob_handle = conn.get_contact_handle_sync('bob@localhost')

    # Try CreateChannel with correct properties
    # Gabble must succeed
    call_async(
        q, conn.Requests, 'CreateChannel', {
            cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE,
            cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
            cs.TARGET_HANDLE: bob_handle,
            cs.STREAM_TUBE_SERVICE: "newecho",
        })

    def find_stream_tube(channels):
        for path, props in channels:
            if props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE:
                return path, props

        return None, None

    def new_chan_predicate(e):
        path, _ = find_stream_tube(e.args[0])
        return path is not None

    ret, new_sig = q.expect_many(
        EventPattern('dbus-return', method='CreateChannel'),
        EventPattern('dbus-signal',
                     signal='NewChannels',
                     predicate=new_chan_predicate),
    )

    new_chan_path = ret.value[0]
    new_chan_prop_asv = ret.value[1]
    # State and Parameters are mutables so not announced
    assert cs.TUBE_STATE not in new_chan_prop_asv
    assert cs.TUBE_PARAMETERS not in new_chan_prop_asv
    assert new_chan_path.find("StreamTube") != -1, new_chan_path
    assert new_chan_path.find("SITubesChannel") == -1, new_chan_path

    new_tube_chan = bus.get_object(conn.bus_name, new_chan_path)
    new_tube_iface = dbus.Interface(new_tube_chan, cs.CHANNEL_TYPE_STREAM_TUBE)

    # check State and Parameters
    new_tube_props = new_tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE,
                                          dbus_interface=cs.PROPERTIES_IFACE)

    # the tube created using the new API is in the "not offered" state
    assert new_tube_props['State'] == cs.TUBE_CHANNEL_STATE_NOT_OFFERED

    _, stream_tube_channel_properties = find_stream_tube(new_sig.args[0])
    assert cs.TUBE_STATE not in stream_tube_channel_properties
    assert cs.TUBE_PARAMETERS not in stream_tube_channel_properties

    # Offer the first tube created
    call_async(q, new_tube_iface, 'Offer', address_type, address2,
               access_control, new_sample_parameters)

    msg_event, state_event = q.expect_many(
        EventPattern('stream-message'),
        EventPattern('dbus-signal', signal='TubeChannelStateChanged'))

    assert state_event.args[0] == cs.TUBE_CHANNEL_STATE_REMOTE_PENDING

    message = msg_event.stanza
    assert message['to'] == bob_full_jid
    tube_nodes = xpath.queryForNodes('/message/tube[@xmlns="%s"]' % ns.TUBES,
                                     message)
    assert tube_nodes is not None
    assert len(tube_nodes) == 1
    tube = tube_nodes[0]

    assert tube['service'] == 'newecho'
    assert tube['type'] == 'stream'
    assert not tube.hasAttribute('initiator')
    stream_tube_id = long(tube['id'])

    params = {}
    parameter_nodes = xpath.queryForNodes('/tube/parameters/parameter', tube)
    for node in parameter_nodes:
        assert node['name'] not in params
        params[node['name']] = (node['type'], str(node))
    assert params == {
        'ay': ('bytes', 'bmV3aGVsbG8='),
        's': ('str', 'newhello'),
        'i': ('int', '-123'),
        'u': ('uint', '123'),
    }

    # The new tube has been offered, the parameters cannot be changed anymore
    # We need to use call_async to check the error
    tube_prop_iface = dbus.Interface(new_tube_chan, cs.PROPERTIES_IFACE)
    call_async(q,
               tube_prop_iface,
               'Set',
               cs.CHANNEL_IFACE_TUBE,
               'Parameters',
               dbus.Dictionary({dbus.String(u'foo2'): dbus.String(u'bar2')},
                               signature=dbus.Signature('sv')),
               dbus_interface=cs.PROPERTIES_IFACE)
    set_error = q.expect('dbus-error')
    # check it is *not* correctly changed
    new_tube_props = new_tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE,
                                          dbus_interface=cs.PROPERTIES_IFACE,
                                          byte_arrays=True)
    assert new_tube_props.get("Parameters") == new_sample_parameters, \
            new_tube_props.get("Parameters")

    # The CM is the server, so fake a client wanting to talk to it
    # Old API tube
    bytestream1 = bytestream_cls(stream, q, 'alpha', bob_full_jid,
                                 self_full_jid, True)
    iq, si = bytestream1.create_si_offer(ns.TUBES)

    stream_node = si.addElement((ns.TUBES, 'stream'))
    stream_node['tube'] = str(stream_tube_id)
    stream.send(iq)

    si_reply_event, _, new_conn_event, socket_event = q.expect_many(
        EventPattern('stream-iq', iq_type='result'),
        EventPattern('dbus-signal',
                     signal='TubeChannelStateChanged',
                     args=[cs.TUBE_STATE_OPEN]),
        EventPattern('dbus-signal', signal='NewRemoteConnection'),
        EventPattern('socket-connected'))

    bytestream1.check_si_reply(si_reply_event.stanza)
    tube = xpath.queryForNodes('/iq/si/tube[@xmlns="%s"]' % ns.TUBES,
                               si_reply_event.stanza)
    assert len(tube) == 1

    handle, access, id = new_conn_event.args
    assert handle == bob_handle
    protocol = socket_event.protocol
    # we don't want to echo the access control byte
    protocol.echoed = False

    # start to read from the transport so we can read the control byte
    protocol.transport.startReading()
    t.check_new_connection_access(q, access_control, access, protocol)
    protocol.echoed = True

    # The CM is the server, so fake a client wanting to talk to it
    # New API tube
    bytestream2 = bytestream_cls(stream, q, 'beta', bob_full_jid,
                                 self_full_jid, True)
    iq, si = bytestream2.create_si_offer(ns.TUBES)

    stream_node = si.addElement((ns.TUBES, 'stream'))
    stream_node['tube'] = str(stream_tube_id)
    stream.send(iq)

    si_reply_event, new_conn_event, socket_event = q.expect_many(
        EventPattern('stream-iq', iq_type='result'),
        EventPattern('dbus-signal', signal='NewRemoteConnection'),
        EventPattern('socket-connected'))

    bytestream2.check_si_reply(si_reply_event.stanza)
    tube = xpath.queryForNodes('/iq/si/tube[@xmlns="%s"]' % ns.TUBES,
                               si_reply_event.stanza)
    assert len(tube) == 1

    handle, access, conn_id = new_conn_event.args
    assert handle == bob_handle
    protocol = socket_event.protocol
    # we don't want to echo the access control byte
    protocol.echoed = False

    # start to read from the transport so we can read the control byte
    protocol.transport.startReading()
    t.check_new_connection_access(q, access_control, access, protocol)
    protocol.echoed = True

    # have the fake client open the stream
    bytestream1.open_bytestream()

    # have the fake client send us some data
    data = 'hello, world'
    bytestream1.send_data(data)

    binary = bytestream1.get_data(len(data))
    assert binary == data, binary

    # have the fake client open the stream
    bytestream2.open_bytestream()

    # have the fake client send us some data
    data = 'hello, new world'
    bytestream2.send_data(data)

    binary = bytestream2.get_data(len(data))
    assert binary == data, binary

    # peer closes the bytestream
    bytestream2.close()
    e = q.expect('dbus-signal', signal='ConnectionClosed')
    assertEquals(conn_id, e.args[0])
    assertEquals(cs.CONNECTION_LOST, e.args[1])

    t.cleanup()
Example #43
0
def run_outgoing_test(q, bus, conn, stream, close_channel=False):
    jp = JingleProtocol031 ()
    jt = JingleTest2(jp, conn, q, stream, 'test@localhost', muc + '/bob')
    jt.prepare()

    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")

    # Not allowed to have muji related presences before we accept the channel
    forbidden = [ no_muji_presences (muc) ]

    (path, props) = create_muji_channel (q, conn, stream, muc)

    q.forbid_events(forbidden)
    general_tests (jp, q, bus, conn, stream, path, props)

    channel = bus.get_object (conn.bus_name, path)

    props = channel.GetAll (cs.CHANNEL_TYPE_CALL,
        dbus_interface = dbus.PROPERTIES_IFACE)

    content = bus.get_object (conn.bus_name, props['Contents'][0])


    md = jt.get_call_audio_md_dbus()
    check_and_accept_offer (q, bus, conn, content, md)

    # Accept the channel, which means we can get muji presences
    q.unforbid_events (forbidden)
    channel.Accept()

    e = q.expect('stream-presence', to = muc + "/test")
    echo_muc_presence (q, stream, e.stanza, 'none', 'participant')
    mujinode = xpath.queryForNodes("/presence/muji", e.stanza)
    assertLength (1, mujinode)

    # The one with the codecs
    e = q.expect('stream-presence', to = muc + "/test")
    echo_muc_presence (q, stream, e.stanza, 'none', 'participant')

    # Gabble shouldn't send new presences for a while
    q.forbid_events(forbidden)

    presence = make_muc_presence('owner', 'moderator', muc, 'bob')
    presence.addElement ((ns.MUJI, 'muji')).addElement('preparing')
    stream.send(presence)

    presence = make_muc_presence('owner', 'moderator', muc, 'bob')
    muji =  ('muji', ns.MUJI, {},
        [('content', ns.MUJI, { "name": "Audio" },
            [( 'description', ns.JINGLE_RTP, {"media": "audio"},
            jt.generate_payloads(jt.audio_codecs))])])
    presence.addChild(jp._simple_xml(muji))
    stream.send(presence)

    q.expect('dbus-signal', signal = 'CallStateChanged')

    # Bob appears and starts a session right afterwards
    q.expect('dbus-signal', signal = 'CallMembersChanged')

    q.unforbid_events(forbidden)

    e = q.expect('dbus-signal', signal = 'NewMediaDescriptionOffer')
    offer = bus.get_object (conn.bus_name, e.args[0])
    offer.Accept(md, dbus_interface=cs.CALL_CONTENT_MEDIA_DESCRIPTION)

    jt.incoming_call(audio = "Audio")

    e = q.expect ('dbus-signal', signal = 'StreamsAdded')
    cstream = bus.get_object (conn.bus_name, e.args[0][0])

    candidates = jt.get_call_remote_transports_dbus ()
    cstream.AddCandidates (candidates,
        dbus_interface=cs.CALL_STREAM_IFACE_MEDIA)

    # Fake our endpoint being connected
    endpoints = cstream.Get(cs.CALL_STREAM_IFACE_MEDIA,
        "Endpoints", dbus_interface=dbus.PROPERTIES_IFACE)
    assertLength (1, endpoints)

    endpoint = bus.get_object (conn.bus_name, endpoints[0])

    endpoint.SetEndpointState (1, cs.MEDIA_STREAM_STATE_CONNECTED,
                               dbus_interface=cs.CALL_STREAM_ENDPOINT)
    endpoint.SetEndpointState (2, cs.MEDIA_STREAM_STATE_CONNECTED,
                               dbus_interface=cs.CALL_STREAM_ENDPOINT)

    e = q.expect ('stream-iq',
        predicate = jp.action_predicate ('session-accept'))
    stream.send(jp.xml(jp.ResultIq(jt.peer, e.stanza, [])))

    # But we want video as well !
    c = channel.AddContent ("Camera!", cs.MEDIA_STREAM_TYPE_VIDEO,
        dbus_interface=cs.CHANNEL_TYPE_CALL)

    q.expect('dbus-signal', signal = 'ContentAdded')

    content = bus.get_object (conn.bus_name, c)

    # wait for the CodecOffer and Accept it
    q.expect('dbus-signal', signal = 'NewMediaDescriptionOffer')

    md = jt.get_call_video_md_dbus()
    check_and_accept_offer (q, bus, conn, content, md)
    
    # preparing
    e = q.expect('stream-presence', to = muc + "/test")
    echo_muc_presence (q, stream, e.stanza, 'none', 'participant')

    #codecs
    e = q.expect('stream-presence', to = muc + "/test")
    echo_muc_presence (q, stream, e.stanza, 'none', 'participant')

    # Bob would like to join our video party
    presence = make_muc_presence('owner', 'moderator', muc, 'bob')
    muji =  ('muji', ns.MUJI, {},
        [('content', ns.MUJI, { "name": "Audio" },
            [( 'description', ns.JINGLE_RTP, {"media": "audio"},
            jt.generate_payloads(jt.audio_codecs))]),
         ('content', ns.MUJI, { "name": "Camera!" },
            [( 'description', ns.JINGLE_RTP, {"media": "video"},
            jt.generate_payloads(jt.video_codecs))]),
        ])
    presence.addChild(jp._simple_xml(muji))
    stream.send(presence)

    # new codec offer as bob threw in some codecs
    q.expect('dbus-signal', signal='NewMediaDescriptionOffer')
    check_and_accept_offer (q, bus, conn, self_handle,
            content, codecs, check_codecs_changed = False)

    # Bob sends a content
    node = jp.SetIq(jt.peer, jt.jid, [
        jp.Jingle(jt.sid, jt.peer, 'content-add', [
            jp.Content('videostream', 'initiator', 'both',
                jp.Description('video', [
                    jp.PayloadType(name, str(rate), str(id), parameters) for
                        (name, id, rate, parameters) in jt.video_codecs ]),
            jp.TransportGoogleP2P()) ]) ])
    stream.send(jp.xml(node))

    # We get a new stream
    q.expect('dbus-signal', signal = 'StreamsAdded')

    # Sync up the stream to ensure we sent out all the xmpp traffic that was
    # the result of a stream being added
    sync_stream (q, stream)

    # happiness.. Now let's hang up
    if close_channel:
        channel.Close()
        hangup_event = EventPattern ('dbus-signal', signal = "Closed",
            path = path)
    else:
        channel.Hangup (0, "", "", dbus_interface=cs.CHANNEL_TYPE_CALL)
        hangup_event = EventPattern ('dbus-signal', signal='CallStateChanged')

    # Should change the call state to ended, send a session-terminate to our
    # only peer and send a muc presence without any mention of muji
    q.forbid_events(forbidden)
    q.expect_many (EventPattern ('stream-presence', to = muc + "/test"),
        EventPattern ('stream-iq',
                predicate=jp.action_predicate ('session-terminate')),
        hangup_event)

    if not close_channel:
        channel.Close()
        q.expect ('dbus-signal', signal="Closed", path = path)

    try:
        channel.Close()
        raise AssertionError ("Channel didn't actually close")
    except DBusException:
        pass
def test(q, bus, conn, stream, access_control):
    iq_event = q.expect('stream-iq',
                        to=None,
                        query_ns='vcard-temp',
                        query_name='vCard')

    acknowledge_iq(stream, iq_event.stanza)

    muc = '*****@*****.**'
    _, test_handle, bob_handle = \
        join_muc_and_check(q, bus, conn, stream, muc)

    # Bob offers a stream tube
    bob_bus_name = ':2.Ym9i'
    presence = make_muc_presence('owner', 'moderator', '*****@*****.**',
                                 'bob')
    tubes = presence.addElement((ns.TUBES, 'tubes'))
    tube = tubes.addElement((None, 'tube'))
    tube['type'] = 'dbus'
    tube['initiator'] = '[email protected]/bob'
    tube['stream-id'] = '10'
    tube['id'] = '1'
    tube['service'] = 'com.example.Test'
    tube['dbus-name'] = bob_bus_name
    parameters = tube.addElement((None, 'parameters'))
    parameter = parameters.addElement((None, 'parameter'))
    parameter['type'] = 'str'
    parameter['name'] = 'foo'
    parameter.addContent('bar')
    stream.send(presence)

    # tube channel is created
    def new_chan_predicate(e):
        path, props = e.args[0][0]
        return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE

    event = q.expect('dbus-signal',
                     signal='NewChannels',
                     predicate=new_chan_predicate)
    channels = event.args[0]
    path, props = channels[0]

    assertEquals(cs.CHANNEL_TYPE_DBUS_TUBE, props[cs.CHANNEL_TYPE])
    assertEquals('[email protected]/bob', props[cs.INITIATOR_ID])
    bob_handle = props[cs.INITIATOR_HANDLE]
    assertSameSets([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE],
                   props[cs.INTERFACES])
    assertEquals(False, props[cs.REQUESTED])
    assertEquals('*****@*****.**', props[cs.TARGET_ID])
    assertEquals('com.example.Test', props[cs.DBUS_TUBE_SERVICE_NAME])
    assertEquals({'foo': 'bar'}, props[cs.TUBE_PARAMETERS])
    assertEquals([
        cs.SOCKET_ACCESS_CONTROL_CREDENTIALS,
        cs.SOCKET_ACCESS_CONTROL_LOCALHOST
    ], props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS])

    tube_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'DBusTube')

    # only Bob is in DBusNames
    dbus_names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE,
                               'DBusNames',
                               dbus_interface=cs.PROPERTIES_IFACE)
    assertEquals({bob_handle: bob_bus_name}, dbus_names)

    call_async(q, tube_chan.DBusTube, 'Accept', access_control)

    return_event, names_changed1, names_changed2, presence_event = q.expect_many(
        EventPattern('dbus-return', method='Accept'),
        EventPattern('dbus-signal',
                     signal='DBusNamesChanged',
                     interface=cs.CHANNEL_TYPE_DBUS_TUBE),
        EventPattern('dbus-signal',
                     signal='DBusNamesChanged',
                     interface=cs.CHANNEL_TYPE_DBUS_TUBE),
        EventPattern('stream-presence',
                     to='[email protected]/test',
                     predicate=lambda e: t.presence_contains_tube(e)))

    tube_addr = return_event.value[0]
    assert len(tube_addr) > 0

    # check presence stanza
    tube_node = xpath.queryForNodes('/presence/tubes/tube',
                                    presence_event.stanza)[0]
    assertEquals('[email protected]/bob', tube_node['initiator'])
    assertEquals('com.example.Test', tube_node['service'])
    assertEquals('10', tube_node['stream-id'])
    assertEquals('dbus', tube_node['type'])
    assertEquals('1', tube_node['id'])
    self_bus_name = tube_node['dbus-name']

    tubes_self_handle = tube_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP,
                                                 'SelfHandle')
    assertNotEquals(0, tubes_self_handle)

    # both of us are in DBusNames now
    dbus_names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE,
                               'DBusNames',
                               dbus_interface=cs.PROPERTIES_IFACE)
    assertEquals({
        bob_handle: bob_bus_name,
        tubes_self_handle: self_bus_name
    }, dbus_names)

    added, removed = names_changed1.args
    assertEquals({bob_handle: bob_bus_name}, added)
    assertEquals([], removed)

    added, removed = names_changed2.args
    assertEquals({tubes_self_handle: self_bus_name}, added)
    assertEquals([], removed)

    tube_chan.Channel.Close()
    q.expect_many(EventPattern('dbus-signal', signal='Closed'),
                  EventPattern('dbus-signal', signal='ChannelClosed'))
Example #45
0
    def _cb_disco_iq(self, iq):
        nodes = xpath.queryForNodes("/iq/query", iq)
        query = nodes[0]

        if query.getAttribute('node') is None:
            return

        node = query.attributes['node']
        ver = node.replace("http://telepathy.freedesktop.org/caps#", "")

        if iq.getAttribute('type') == 'result':

            if FileTransferTest.caps_identities is None or \
                    FileTransferTest.caps_features is None or \
                    FileTransferTest.caps_dataforms is None:
                # create our own identity
                identity_nodes = xpath.queryForNodes('/iq/query/identity', iq)
                assertLength(1, identity_nodes)
                identity_node = identity_nodes[0]

                identity_category = identity_node['category']
                identity_type = identity_node['type']
                identity_name = identity_node['name']
                identity = '%s/%s//%s' % (identity_category, identity_type,
                                          identity_name)

                _, features, dataforms = extract_disco_parts(iq)

                FileTransferTest.caps_identities = [identity]
                FileTransferTest.caps_features = features
                FileTransferTest.caps_dataforms = dataforms

                # Check if the hash matches the announced capabilities
                assertEquals(
                    compute_caps_hash(FileTransferTest.caps_identities,
                                      FileTransferTest.caps_features,
                                      FileTransferTest.caps_dataforms), ver)

            if ver == FileTransferTest.caps_ft:
                caps_share = compute_caps_hash(FileTransferTest.caps_identities,
                                               FileTransferTest.caps_features + \
                                                   [ns.GOOGLE_FEAT_SHARE],
                                               FileTransferTest.caps_dataforms)
                n = query.attributes['node'].replace(ver, caps_share)
                query.attributes['node'] = n

                for feature in xpath.queryForNodes('/iq/query/feature', iq):
                    query.children.remove(feature)

                for f in FileTransferTest.caps_features + [
                        ns.GOOGLE_FEAT_SHARE
                ]:
                    el = domish.Element((None, 'feature'))
                    el['var'] = f
                    query.addChild(el)

        elif iq.getAttribute('type') == 'get':
            caps_share = compute_caps_hash(FileTransferTest.caps_identities,
                                           FileTransferTest.caps_features + \
                                               [ns.GOOGLE_FEAT_SHARE],
                                           FileTransferTest.caps_dataforms)

            if ver == caps_share:
                n = query.attributes['node'].replace(ver,
                                                     FileTransferTest.caps_ft)
                query.attributes['node'] = n
def test(q, bus, conn, stream):
    room = '*****@*****.**'
    chan, test_handle, bob_handle = \
        join_muc_and_check(q, bus, conn, stream, room)

    # Exercise basic Channel Properties from spec 0.17.7
    channel_props = chan.Properties.GetAll(cs.CHANNEL)
    assertEquals(cs.HT_ROOM, channel_props.get('TargetHandleType'))
    assertEquals(cs.CHANNEL_TYPE_TEXT, channel_props.get('ChannelType'))

    interfaces = channel_props.get('Interfaces')
    assertContains(cs.CHANNEL_IFACE_GROUP, interfaces)
    assertContains(cs.CHANNEL_IFACE_PASSWORD, interfaces)
    assertContains(cs.CHANNEL_IFACE_CHAT_STATE, interfaces)
    assertContains(cs.CHANNEL_IFACE_MESSAGES, interfaces)

    assert channel_props['TargetID'] == '*****@*****.**', channel_props
    assert channel_props['Requested'] == True
    assert channel_props['InitiatorID'] == 'test@localhost'
    assert channel_props['InitiatorHandle'] == conn.Properties.Get(
        cs.CONN, "SelfHandle")

    # Exercise Group Properties from spec 0.17.6 (in a basic way)
    group_props = chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP)
    assert 'HandleOwners' in group_props, group_props
    assert 'Members' in group_props, group_props
    assert 'LocalPendingMembers' in group_props, group_props
    assert 'RemotePendingMembers' in group_props, group_props
    assert 'GroupFlags' in group_props, group_props

    # Test receiving a message from Bob in the MUC
    message = domish.Element((None, 'message'))
    message['from'] = '[email protected]/bob'
    message['type'] = 'groupchat'
    body = message.addElement('body', content='hello')
    stream.send(message)

    message_received = q.expect('dbus-signal', signal='MessageReceived')

    # Check Channel.Interface.Messages.MessageReceived:
    message = message_received.args[0]

    # message should have two parts: the header and one content part
    assert len(message) == 2, message
    header, body = message

    assert header['message-sender'] == bob_handle, header
    # the spec says that message-type "SHOULD be omitted for normal chat
    # messages."
    assert 'message-type' not in header, header

    assert body['content-type'] == 'text/plain', body
    assert body['content'] == 'hello', body

    # Remove the message from the pending message queue, and check that
    # PendingMessagesRemoved fires.
    message_id = header['pending-message-id']

    chan.Text.AcknowledgePendingMessages([message_id])

    removed = q.expect('dbus-signal', signal='PendingMessagesRemoved')

    removed_ids = removed.args[0]
    assert len(removed_ids) == 1, removed_ids
    assert removed_ids[0] == message_id, (removed_ids, message_id)

    # Send an action using the Messages API
    greeting = [
        dbus.Dictionary(
            {
                'message-type': 1,  # Action
            },
            signature='sv'),
        {
            'content-type': 'text/plain',
            'content': u"peers through a gap in the curtains",
        }
    ]

    # We ask for delivery reports (which MUCs provide) and read reports (which
    # MUCs do not provide).
    sent_token = chan.Messages.SendMessage(
        greeting, cs.MSG_SENDING_FLAGS_REPORT_DELIVERY
        | cs.MSG_SENDING_FLAGS_REPORT_READ)

    assert sent_token

    stream_message, message_sent = q.expect_many(
        EventPattern('stream-message'),
        EventPattern('dbus-signal', signal='MessageSent'),
    )

    sent_message, flags, token = message_sent.args
    assert len(sent_message) == 2, sent_message
    header = sent_message[0]
    assert header['message-type'] == 1, header  # Action
    assertEquals(test_handle, header['message-sender'])
    assertEquals('[email protected]/test', header['message-sender-id'])
    body = sent_message[1]
    assert body['content-type'] == 'text/plain', body
    assert body['content'] == u'peers through a gap in the curtains', body

    # Of the flags passed to SendMessage, Gabble should only report the
    # DELIVERY flag, since the other is not supported.
    assertEquals(cs.MSG_SENDING_FLAGS_REPORT_DELIVERY, flags)
    assertEquals(sent_token, token)

    assert message_sent.args[2] == sent_token

    elem = stream_message.stanza
    assert elem.name == 'message'
    assert elem['type'] == 'groupchat', repr(elem)
    assert elem['id'] == sent_token, repr(elem)
    assert elem['to'] == '*****@*****.**', repr(elem)
    for sub_elem in stream_message.stanza.elements():
        if sub_elem.name == 'body':
            found_body = True
            assert sub_elem.children[
                0] == u'/me peers through a gap in the curtains'
            break
    assert found_body

    # reflect the sent message back to the MUC
    elem['from'] = '[email protected]/test'
    stream.send(elem)

    # Check that we got the corresponding delivery report
    report = q.expect('dbus-signal', signal='MessageReceived')

    assert len(report.args) == 1, report.args
    parts = report.args[0]
    # The delivery report should just be a header, no body.
    assert len(parts) == 1, parts
    part = parts[0]
    # The intended recipient was the MUC, so there's no contact handle
    # suitable for being 'message-sender'.
    assert 'message-sender' not in part or part['message-sender'] == 0, part
    assert part['message-type'] == 4, part  # Message_Type_Delivery_Report
    assert part['delivery-status'] == 1, part  # Delivery_Status_Delivered
    assert part['delivery-token'] == sent_token, part
    assert 'delivery-error' not in part, part
    assert 'delivery-echo' in part, part

    # Check that the included echo is from us, and matches all the keys in the
    # message we sent.
    echo = part['delivery-echo']
    assert len(echo) == len(greeting), (echo, greeting)
    assert echo[0]['message-sender'] == test_handle, echo[0]
    assert echo[0]['message-token'] == sent_token, echo[0]
    for i in range(0, len(echo)):
        for key in greeting[i]:
            assert key in echo[i], (i, key, echo)
            assert echo[i][key] == greeting[i][key], (i, key, echo, greeting)

    # Send a normal message using the Channel.Type.Text API
    chan.send_msg_sync('goodbye')

    event, message_sent = q.expect_many(
        EventPattern('stream-message'),
        EventPattern('dbus-signal', signal='MessageSent'),
    )

    sent_message, flags, _ = message_sent.args
    assert len(sent_message) == 2, sent_message
    header = sent_message[0]
    body = sent_message[1]
    assert body['content-type'] == 'text/plain', body
    assert body['content'] == u'goodbye', body

    # The caller didn't ask for delivery reports (how could they? they're using
    # the old API), but the server's going to send us an echo anyway, so
    # Gabble's within its rights to pretend that the caller asked.
    assert flags in [0, cs.MSG_SENDING_FLAGS_REPORT_DELIVERY], flags

    sent_token = message_sent.args[2]

    elem = event.stanza
    assert elem.name == 'message'
    assert elem['type'] == 'groupchat'
    assert elem['id'] == message_sent.args[2]
    body = list(event.stanza.elements())[0]
    assert body.name == 'body'
    assert body.children[0] == u'goodbye'

    # reflect the sent message back to the MUC
    elem['from'] = '[email protected]/test'
    stream.send(elem)

    # TODO: check for a delivery report.

    # test that presence changes are sent via the MUC
    conn.SimplePresence.SetPresence('away', 'hurrah')

    event = q.expect('stream-presence', to='[email protected]/test')
    elem = event.stanza
    show = [e for e in elem.elements() if e.name == 'show'][0]
    assert show
    assert show.children[0] == u'away'
    status = [e for e in elem.elements() if e.name == 'status'][0]
    assert status
    assert status.children[0] == u'hurrah'

    # Check that there's no <x xmlns='.../muc'/> element in the <presence>
    # stanza when we're just updating our presence, as opposed to joining the
    # MUC in the first place. This is a regression test for
    # <https://bugs.freedesktop.org/show_bug.cgi?id=29147>. XEP-0045 §7.4 shows
    # that you do not need to include this element in presence updates; if we
    # erroneously include it, some implementations take this to mean that we're
    # trying to join the MUC again and helpfully send us all the scrollback
    # again.
    x_muc_nodes = xpath.queryForNodes('/presence/x[@xmlns="%s"]' % ns.MUC,
                                      elem)
    assert x_muc_nodes is None, elem.toXml()

    # test that leaving the channel results in an unavailable message
    chan.Group.RemoveMembers(
        [chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, "SelfHandle")], 'booo')

    event = q.expect('stream-presence', to='[email protected]/test')
    elem = event.stanza
    assert elem['type'] == 'unavailable'
    status = [e for e in elem.elements() if e.name == 'status']
    assertLength(1, status)
    assertEquals(status[0].children[0], u'booo')
def test(q, bus, conn, stream):
    iq_event = q.expect('stream-iq',
                        to=None,
                        query_ns='vcard-temp',
                        query_name='vCard')

    acknowledge_iq(stream, iq_event.stanza)

    # send diso request
    m = domish.Element((None, 'iq'))
    m['from'] = '*****@*****.**'
    m['id'] = '1'
    m['type'] = 'get'
    query = m.addElement('query')
    query['xmlns'] = ns.DISCO_INFO
    stream.send(m)

    # wait for disco response
    event = q.expect('stream-iq',
                     iq_type='result',
                     query_ns=ns.DISCO_INFO,
                     to='*****@*****.**')

    features = set([
        str(f['var'])
        for f in xpath.queryForNodes('/iq/query/feature', event.stanza)
    ])

    # OLPC NS aren't announced
    assert len(olpc_features.intersection(features)) == 0

    # Use OLPC interface
    buddy_info_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo')
    call_async(q, buddy_info_iface, 'SetProperties',
               {'color': '#ff0000,#0000ff'})

    # wait for <presence> stanza
    event = q.expect('stream-presence')
    c_nodes = xpath.queryForNodes('/presence/c', event.stanza)
    assert c_nodes is not None
    assert len(c_nodes) == 1

    # send diso request
    m = domish.Element((None, 'iq'))
    m['from'] = '*****@*****.**'
    m['id'] = '2'
    m['type'] = 'get'
    query = m.addElement('query')
    query['xmlns'] = ns.DISCO_INFO
    stream.send(m)

    # wait for disco response
    event = q.expect('stream-iq',
                     iq_type='result',
                     query_ns=ns.DISCO_INFO,
                     to='*****@*****.**')
    assert event.stanza['id'] == '2'

    # OLPC NS are now announced
    features = set([
        str(f['var'])
        for f in xpath.queryForNodes('/iq/query/feature', event.stanza)
    ])

    assert olpc_features.issubset(features)
def _test_local_status(q, conn, stream, msg, show, expected_show=None):
    expected_show = expected_show or show
    away = expected_show in ('away', 'xa')

    self = conn.Properties.Get(cs.CONN, "SelfHandle")
    prev_presence = get_contacts_presences_sync(conn, [self])[self]
    was_away = prev_presence[0] in (cs.PRESENCE_AWAY,
                                    cs.PRESENCE_EXTENDED_AWAY)
    was_invisible = (prev_presence[0] == cs.PRESENCE_HIDDEN)

    if away:
        # Away and extended away are mapped to idle, that is per connection.
        # This means we use <presence/> instead of shared presence...
        if not was_invisible:
            # ... so in normal cases we don't expect the shared presence
            # stuff, but ...
            wrong_presence_pattern = EventPattern(
                'stream-iq', query_ns=ns.GOOGLE_SHARED_STATUS, iq_type='set')
        else:
            # ... when switching from invisible we have to leave invisible
            # first and then go away.
            wrong_presence_pattern = None
    elif was_away:
        # Non-away status, but we were away previously. Considering that we
        # went away using <presence/>, we need to also leave it using
        # <presence/> plus the shared status.
        wrong_presence_pattern = None
    else:
        # Normal case without away involvement; we just expect the use of
        # shared status.
        wrong_presence_pattern = EventPattern('stream-presence')

    if wrong_presence_pattern:
        q.forbid_events([wrong_presence_pattern])

    conn.SimplePresence.SetPresence(show, msg)

    max_status_message_length = int(stream.max_status_message_length)

    if not away or (away and was_invisible):
        event = q.expect('stream-iq',
                         query_ns=ns.GOOGLE_SHARED_STATUS,
                         iq_type='set')

        shared_show, shared_invisible = _show_to_shared_status_show(
            expected_show)

        _status = xpath.queryForNodes('//status', event.query)[0]
        assertEquals(msg[:max_status_message_length], _status.children[0])
        _show = xpath.queryForNodes('//show', event.query)[0]
        assertEquals(shared_show, _show.children[0])
        _invisible = xpath.queryForNodes('//invisible', event.query)[0]
        assertEquals(shared_invisible, _invisible.getAttribute('value'))

        if was_away or (away and was_invisible):
            q.expect('stream-presence')
    else:
        q.expect('stream-presence')

    q.expect('dbus-signal',
             signal='PresencesChanged',
             interface=cs.CONN_IFACE_SIMPLE_PRESENCE,
             args=[{
                 1: (presence_types[expected_show], expected_show,
                     msg[:max_status_message_length])
             }])

    if wrong_presence_pattern:
        q.unforbid_events([wrong_presence_pattern])
def no_muji_presences(muc):
    return EventPattern(
        'stream-presence',
        to=muc + "/test",
        predicate=lambda x: xpath.queryForNodes("/presence/muji", x.stanza))
Example #50
0
 def send_privacy_list(self, req_iq, list_items):
     req_list = xpath.queryForNodes('//list', req_iq)[0]
     iq = elem_iq(self, "result", id=req_iq["id"])(elem(
         ns.PRIVACY, 'query')(elem('list',
                                   name=req_list["name"])(*list_items)))
     self.send(iq)
Example #51
0
def test(q, bus, conn):
    call_async(q, conn.Future, 'EnsureSidecar', ycs.STATUS_IFACE)

    conn.Connect()

    # Now we're connected, the call we made earlier should return.
    e = q.expect('dbus-return', method='EnsureSidecar')
    path, props = e.value
    assertEquals({}, props)

    status = ProxyWrapper(bus.get_object(conn.bus_name, path),
                          ycs.STATUS_IFACE, {})

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals({}, discovered)

    # announce a contact with the right caps
    ver = compute_caps_hash([], [], banshee)
    txt_record = {
        "txtvers": "1",
        "status": "avail",
        "node": CLIENT_NAME,
        "ver": ver,
        "hash": "sha-1"
    }
    contact_name = "test-service@" + get_host_name()
    listener, port = setup_stream_listener(q, contact_name)

    announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port,
                               txt_record)

    handle = wait_for_contact_in_publish(q, bus, conn, contact_name)

    # this is the first presence, Salut connects to the contact
    e = q.expect('incoming-connection', listener=listener)
    incoming = e.connection

    # Salut looks up its capabilities
    event = q.expect('stream-iq', connection=incoming, query_ns=ns.DISCO_INFO)
    query_node = xpath.queryForNodes('/iq/query', event.stanza)[0]
    assertEquals(CLIENT_NAME + '#' + ver, query_node.attributes['node'])

    contact_handle = conn.RequestHandles(cs.HT_CONTACT, [contact_name])[0]

    # send good reply
    result = make_result_iq(event.stanza)
    query = result.firstChildElement()
    query['node'] = CLIENT_NAME + '#' + ver
    x = query.addElement((ns.X_DATA, 'x'))
    x['type'] = 'result'

    # FORM_TYPE
    field = x.addElement((None, 'field'))
    field['var'] = 'FORM_TYPE'
    field['type'] = 'hidden'
    field.addElement((None, 'value'),
                     content='urn:ytstenut:capabilities#org.gnome.Banshee')

    # type
    field = x.addElement((None, 'field'))
    field['var'] = 'type'
    field.addElement((None, 'value'), content='application')

    # name
    field = x.addElement((None, 'field'))
    field['var'] = 'name'
    field.addElement((None, 'value'), content='en_GB/Banshee Media Player')
    field.addElement((None, 'value'), content='fr/Banshee Lecteur de Musique')

    # capabilities
    field = x.addElement((None, 'field'))
    field['var'] = 'capabilities'
    field.addElement((None, 'value'),
                     content='urn:ytstenut:capabilities:yts-caps-audio')
    field.addElement((None, 'value'), content='urn:ytstenut:data:jingle:rtp')

    incoming.send(result)

    # this will be fired as text channel caps will be fired
    _, e = q.expect_many(
        EventPattern('dbus-signal',
                     signal='ContactCapabilitiesChanged',
                     predicate=lambda e: contact_handle in e.args[0]),
        EventPattern('dbus-signal', signal='ServiceAdded'))

    contact_id, service_name, details = e.args
    assertEquals(contact_name, contact_id)
    assertEquals('org.gnome.Banshee', service_name)

    type, name_map, caps = details
    assertEquals('application', type)
    assertEquals(
        {
            'en_GB': 'Banshee Media Player',
            'fr': 'Banshee Lecteur de Musique'
        }, name_map)
    assertSameSets([
        'urn:ytstenut:capabilities:yts-caps-audio',
        'urn:ytstenut:data:jingle:rtp'
    ], caps)

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(
        {
            contact_name: {
                'org.gnome.Banshee': ('application', {
                    'en_GB': 'Banshee Media Player',
                    'fr': 'Banshee Lecteur de Musique'
                }, [
                    'urn:ytstenut:capabilities:yts-caps-audio',
                    'urn:ytstenut:data:jingle:rtp'
                ])
            },
        }, discovered)

    # add evince
    tmp = banshee.copy()
    tmp.update(evince)
    ver = compute_caps_hash([], [], tmp)
    txt_record['ver'] = ver
    announcer.update(txt_record)

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

    # send good reply
    result['id'] = event.stanza['id']
    query['node'] = CLIENT_NAME + '#' + ver

    x = query.addElement((ns.X_DATA, 'x'))
    x['type'] = 'result'

    # FORM_TYPE
    field = x.addElement((None, 'field'))
    field['var'] = 'FORM_TYPE'
    field['type'] = 'hidden'
    field.addElement((None, 'value'),
                     content='urn:ytstenut:capabilities#org.gnome.Evince')

    # type
    field = x.addElement((None, 'field'))
    field['var'] = 'type'
    field.addElement((None, 'value'), content='application')

    # name
    field = x.addElement((None, 'field'))
    field['var'] = 'name'
    field.addElement((None, 'value'), content='en_GB/Evince Picture Viewer')
    field.addElement((None, 'value'), content='fr/Evince uh, ow do you say')

    # capabilities
    field = x.addElement((None, 'field'))
    field['var'] = 'capabilities'
    field.addElement((None, 'value'), content='urn:ytstenut:capabilities:pics')

    incoming.send(result)

    e = q.expect('dbus-signal', signal='ServiceAdded')

    contact_id, service_name, details = e.args
    assertEquals(contact_name, contact_id)
    assertEquals('org.gnome.Evince', service_name)

    type, name_map, caps = details
    assertEquals('application', type)
    assertEquals(
        {
            'en_GB': 'Evince Picture Viewer',
            'fr': 'Evince uh, ow do you say'
        }, name_map)
    assertSameSets(['urn:ytstenut:capabilities:pics'], caps)

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(
        {
            contact_name: {
                'org.gnome.Banshee': ('application', {
                    'en_GB': 'Banshee Media Player',
                    'fr': 'Banshee Lecteur de Musique'
                }, [
                    'urn:ytstenut:capabilities:yts-caps-audio',
                    'urn:ytstenut:data:jingle:rtp'
                ]),
                'org.gnome.Evince': ('application', {
                    'en_GB': 'Evince Picture Viewer',
                    'fr': 'Evince uh, ow do you say'
                }, ['urn:ytstenut:capabilities:pics'])
            }
        }, discovered)

    # remove evince
    ver = compute_caps_hash([], [], banshee)
    txt_record['ver'] = ver

    forbidden = [
        EventPattern('dbus-signal', signal='stream-iq', connection=incoming)
    ]
    q.forbid_events(forbidden)

    announcer.update(txt_record)

    e = q.expect('dbus-signal', signal='ServiceRemoved')

    contact_id, service_name = e.args
    assertEquals(contact_name, contact_id)
    assertEquals('org.gnome.Evince', service_name)

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(
        {
            contact_name: {
                'org.gnome.Banshee': ('application', {
                    'en_GB': 'Banshee Media Player',
                    'fr': 'Banshee Lecteur de Musique'
                }, [
                    'urn:ytstenut:capabilities:yts-caps-audio',
                    'urn:ytstenut:data:jingle:rtp'
                ])
            },
        }, discovered)

    sync_stream(q, incoming)

    q.unforbid_events(forbidden)

    # now just evince
    ver = compute_caps_hash([], [], evince)
    txt_record['ver'] = ver
    announcer.update(txt_record)

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

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

    x = query.addElement((ns.X_DATA, 'x'))
    x['type'] = 'result'

    # FORM_TYPE
    field = x.addElement((None, 'field'))
    field['var'] = 'FORM_TYPE'
    field['type'] = 'hidden'
    field.addElement((None, 'value'),
                     content='urn:ytstenut:capabilities#org.gnome.Evince')

    # type
    field = x.addElement((None, 'field'))
    field['var'] = 'type'
    field.addElement((None, 'value'), content='application')

    # name
    field = x.addElement((None, 'field'))
    field['var'] = 'name'
    field.addElement((None, 'value'), content='en_GB/Evince Picture Viewer')
    field.addElement((None, 'value'), content='fr/Evince uh, ow do you say')

    # capabilities
    field = x.addElement((None, 'field'))
    field['var'] = 'capabilities'
    field.addElement((None, 'value'), content='urn:ytstenut:capabilities:pics')

    incoming.send(result)

    sa, sr = q.expect_many(
        EventPattern('dbus-signal', signal='ServiceAdded'),
        EventPattern('dbus-signal', signal='ServiceRemoved'))

    contact_id, service_name, details = sa.args
    assertEquals(contact_name, contact_id)
    assertEquals('org.gnome.Evince', service_name)

    type, name_map, caps = details
    assertEquals('application', type)
    assertEquals(
        {
            'en_GB': 'Evince Picture Viewer',
            'fr': 'Evince uh, ow do you say'
        }, name_map)
    assertSameSets(['urn:ytstenut:capabilities:pics'], caps)

    contact_id, service_name = sr.args
    assertEquals(contact_name, contact_id)
    assertEquals('org.gnome.Banshee', service_name)

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(
        {
            contact_name: {
                'org.gnome.Evince': ('application', {
                    'en_GB': 'Evince Picture Viewer',
                    'fr': 'Evince uh, ow do you say'
                }, ['urn:ytstenut:capabilities:pics'])
            }
        }, discovered)

    # just banshee again
    ver = compute_caps_hash([], [], banshee)
    txt_record['ver'] = ver

    forbidden = [
        EventPattern('dbus-signal', signal='stream-iq', connection=incoming)
    ]
    q.forbid_events(forbidden)

    announcer.update(txt_record)

    sr, sa = q.expect_many(
        EventPattern('dbus-signal', signal='ServiceRemoved'),
        EventPattern('dbus-signal', signal='ServiceAdded'))

    contact_id, service_name = sr.args
    assertEquals(contact_name, contact_id)
    assertEquals('org.gnome.Evince', service_name)

    contact_id, service_name, details = sa.args
    assertEquals(contact_name, contact_id)
    assertEquals('org.gnome.Banshee', service_name)

    type, name_map, caps = details
    assertEquals('application', type)
    assertEquals(
        {
            'en_GB': 'Banshee Media Player',
            'fr': 'Banshee Lecteur de Musique'
        }, name_map)
    assertSameSets([
        'urn:ytstenut:capabilities:yts-caps-audio',
        'urn:ytstenut:data:jingle:rtp'
    ], caps)

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(
        {
            contact_name: {
                'org.gnome.Banshee': ('application', {
                    'en_GB': 'Banshee Media Player',
                    'fr': 'Banshee Lecteur de Musique'
                }, [
                    'urn:ytstenut:capabilities:yts-caps-audio',
                    'urn:ytstenut:data:jingle:rtp'
                ])
            }
        }, discovered)

    sync_stream(q, incoming)

    q.unforbid_events(forbidden)

    # both again
    ver = compute_caps_hash([], [], tmp)
    txt_record['ver'] = ver
    announcer.update(txt_record)

    sa = q.expect('dbus-signal', signal='ServiceAdded')

    contact_id, service_name, details = sa.args
    assertEquals(contact_name, contact_id)
    assertEquals('org.gnome.Evince', service_name)

    type, name_map, caps = details
    assertEquals('application', type)
    assertEquals(
        {
            'en_GB': 'Evince Picture Viewer',
            'fr': 'Evince uh, ow do you say'
        }, name_map)
    assertSameSets(['urn:ytstenut:capabilities:pics'], caps)

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals(
        {
            contact_name: {
                'org.gnome.Banshee': ('application', {
                    'en_GB': 'Banshee Media Player',
                    'fr': 'Banshee Lecteur de Musique'
                }, [
                    'urn:ytstenut:capabilities:yts-caps-audio',
                    'urn:ytstenut:data:jingle:rtp'
                ]),
                'org.gnome.Evince': ('application', {
                    'en_GB': 'Evince Picture Viewer',
                    'fr': 'Evince uh, ow do you say'
                }, ['urn:ytstenut:capabilities:pics'])
            }
        }, discovered)

    sync_stream(q, incoming)

    # and finally, nothing
    ver = compute_caps_hash([], [], {})
    txt_record['ver'] = ver
    announcer.update(txt_record)

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

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

    incoming.send(result)

    q.expect_many(
        EventPattern('dbus-signal',
                     signal='ServiceRemoved',
                     args=[contact_name, 'org.gnome.Banshee']),
        EventPattern('dbus-signal',
                     signal='ServiceRemoved',
                     args=[contact_name, 'org.gnome.Evince']))

    discovered = status.Get(ycs.STATUS_IFACE,
                            'DiscoveredServices',
                            dbus_interface=dbus.PROPERTIES_IFACE)
    assertEquals({}, discovered)
def test(q, bus, conn, stream):
    handles = {}
    handles['bob'] = conn.get_contact_handle_sync('bob@localhost')

    buddy_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo')
    act_prop_iface = dbus.Interface(conn,
                                    'org.laptop.Telepathy.ActivityProperties')
    call_async(q, buddy_iface, 'GetActivities', handles['bob'])

    event = q.expect('stream-iq', iq_type='get', to='bob@localhost')
    # Bob has no activities
    event.stanza['type'] = 'result'
    event.stanza['to'] = 'test@localhost'
    event.stanza['from'] = 'bob@localhost'
    stream.send(event.stanza)

    event = q.expect('dbus-return', method='GetActivities')
    # initially, Bob has no activities
    assert event.value == ([], )

    # Bob sends an activity properties message
    message = domish.Element(('jabber:client', 'message'))
    message['from'] = 'bob@localhost'
    message['to'] = 'test@localhost'
    properties = message.addElement((ns.OLPC_ACTIVITY_PROPS, 'properties'))
    properties['room'] = '*****@*****.**'
    properties['activity'] = 'foo_id'
    property = properties.addElement((None, 'property'))
    property['type'] = 'str'
    property['name'] = 'color'
    property.addContent('#ffff00,#00ffff')
    property = properties.addElement((None, 'property'))
    property['type'] = 'bool'
    property['name'] = 'private'
    property.addContent('1')

    stream.send(message)

    event = q.expect('dbus-signal', signal='ActivityPropertiesChanged')
    handles['chat'], props = event.args
    assert props == {'color': '#ffff00,#00ffff', 'private': True}

    event = q.expect('dbus-signal', signal='ActivitiesChanged')
    assert event.args[0] == handles['bob']
    acts = event.args[1]
    assert len(acts) == 1
    assert acts[0] == ('foo_id', handles['chat'])

    props = act_prop_iface.GetProperties(handles['chat'])
    assert props == {'color': '#ffff00,#00ffff', 'private': True}

    # Bobs invites us to the activity
    message = domish.Element((None, 'message'))
    message['from'] = '*****@*****.**'
    message['to'] = 'test@localhost'
    x = message.addElement((ns.MUC_USER, 'x'))
    invite = x.addElement((None, 'invite'))
    invite['from'] = 'bob@localhost'
    reason = invite.addElement((None, 'reason'))
    reason.addContent('No good reason')

    stream.send(message)

    event = q.expect('dbus-signal', signal='NewChannel')
    assert event.args[1] == cs.CHANNEL_TYPE_TEXT

    assert event.args[2] == 2  # handle type
    assert event.args[3] == handles['chat']  # handle

    text_chan = wrap_channel(bus.get_object(conn.bus_name, event.args[0]),
                             'Text')
    group_iface = text_chan.Group

    members = group_iface.GetAllMembers()[0]
    local_pending = group_iface.GetAllMembers()[1]
    remote_pending = group_iface.GetAllMembers()[2]

    assert len(members) == 1
    assert conn.inspect_contact_sync(members[0]) == 'bob@localhost'
    assert len(local_pending) == 1
    # FIXME: the username-part-is-nickname assumption
    assert conn.inspect_contact_sync(local_pending[0]) == \
            '[email protected]/test'
    assert len(remote_pending) == 0

    handles['chat_self'] = text_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP,
                                                    "SelfHandle")
    assert handles['chat_self'] == local_pending[0]

    # by now, we should have picked up the extra activity properties
    call_async(q, buddy_iface, 'GetActivities', handles['bob'])

    event = q.expect('stream-iq', iq_type='get', to='bob@localhost')
    # Bob still has no (public) activities
    event.stanza['type'] = 'result'
    event.stanza['to'] = 'test@localhost'
    event.stanza['from'] = 'bob@localhost'
    stream.send(event.stanza)

    event = q.expect('dbus-return', method='GetActivities')

    assert event.value == ([('foo_id', handles['chat'])], )

    # OK, now accept the invitation
    call_async(q, group_iface, 'AddMembers', [handles['chat_self']],
               'Oh, OK then')

    _, event, _ = q.expect_many(
        EventPattern('stream-presence', to='[email protected]/test'),
        EventPattern('dbus-signal', signal='MembersChanged'),
        EventPattern('dbus-return', method='AddMembers'))

    assert event.args == [
        '', [], [handles['bob']], [], [handles['chat_self']], 0,
        cs.GC_REASON_INVITED
    ]

    # Send presence for own membership of room.
    stream.send(
        make_muc_presence('owner', 'moderator', '*****@*****.**', 'test'))

    event = q.expect('dbus-signal', signal='MembersChanged')
    assert event.args == ['', [handles['chat_self']], [], [], [], 0, 0]

    call_async(q, buddy_iface, 'SetActivities', [('foo_id', handles['chat'])])

    event = q.expect('stream-iq', iq_type='set')
    event.stanza['type'] = 'result'
    event.stanza['to'] = 'test@localhost'
    event.stanza['from'] = 'test@localhost'
    stream.send(event.stanza)

    q.expect('dbus-return', method='SetActivities')
    call_async(q, act_prop_iface, 'SetProperties', handles['chat'], {
        'color': '#ffff00,#00ffff',
        'private': True
    })

    event = q.expect('dbus-signal', signal='ActivityPropertiesChanged')
    chat_handle, props = event.args
    assert chat_handle == handles['chat']
    assert props == {'color': '#ffff00,#00ffff', 'private': True}

    q.expect('dbus-return', method='SetProperties')
    # Test sending an invitation
    handles['alice'] = conn.get_contact_handle_sync('alice@localhost')
    call_async(q, group_iface, 'AddMembers', [handles['alice']],
               'I want to test invitations')

    event = q.expect('stream-message', to='alice@localhost')
    message = event.stanza

    properties = xpath.queryForNodes('/message/properties', message)
    assert (properties is not None and len(properties) == 1), repr(properties)
    assert properties[0].uri == ns.OLPC_ACTIVITY_PROPS
    assert properties[0]['room'] == '*****@*****.**'
    assert properties[0]['activity'] == 'foo_id'

    property = xpath.queryForNodes('/properties/property', properties[0])
    assert (property is not None and len(property) == 2), repr(property)
    seen = set()
    for p in property:
        seen.add(p['name'])
        if p['name'] == 'color':
            assert p['type'] == 'str'
            assert str(p) == '#ffff00,#00ffff'
        elif p['name'] == 'private':
            assert p['type'] == 'bool'
            assert str(p) == '1'
        else:
            assert False, 'Unexpected property %s' % p['name']
    assert 'color' in seen, seen
    assert 'private' in seen, seen

    event = q.expect('stream-message', to='*****@*****.**')
    message = event.stanza

    x = xpath.queryForNodes('/message/x', message)
    assert (x is not None and len(x) == 1), repr(x)
    assert x[0].uri == ns.MUC_USER

    invites = xpath.queryForNodes('/x/invite', x[0])
    assert (invites is not None and len(invites) == 1), repr(invites)
    assert invites[0]['to'] == 'alice@localhost'

    reasons = xpath.queryForNodes('/invite/reason', invites[0])
    assert (reasons is not None and len(reasons) == 1), repr(reasons)
    assert str(reasons[0]) == 'I want to test invitations'

    call_async(q, act_prop_iface, 'SetProperties', handles['chat'], {
        'color': '#f00baa,#f00baa',
        'private': True
    })

    event, apc_event, _ = q.expect_many(
        EventPattern('stream-message', to='alice@localhost'),
        EventPattern('dbus-signal', signal='ActivityPropertiesChanged'),
        EventPattern('dbus-return', method='SetProperties'),
    )
    message = event.stanza

    properties = xpath.queryForNodes('/message/properties', message)
    assert (properties is not None and len(properties) == 1), repr(properties)
    assert properties[0].uri == ns.OLPC_ACTIVITY_PROPS
    assert properties[0]['room'] == '*****@*****.**'
    assert properties[0]['activity'] == 'foo_id'

    property = xpath.queryForNodes('/properties/property', properties[0])
    assert (property is not None and len(property) == 2), repr(property)
    seen = set()
    for p in property:
        seen.add(p['name'])
        if p['name'] == 'color':
            assert p['type'] == 'str'
            assert str(p) == '#f00baa,#f00baa'
        elif p['name'] == 'private':
            assert p['type'] == 'bool'
            assert str(p) == '1'
        else:
            assert False, 'Unexpected property %s' % p['name']
    assert 'color' in seen, seen
    assert 'private' in seen, seen

    chat_handle, props = apc_event.args
    assert chat_handle == handles['chat']
    assert props == {'color': '#f00baa,#f00baa', 'private': True}
def test(q, bus, conn, stream):
    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")

    romeo, juliet, duncan = conn.get_contact_handles_sync(
        ['*****@*****.**', '*****@*****.**', '*****@*****.**'])

    # receive some roster pushes for the "initial" state
    iq = IQ(stream, 'set')
    iq['id'] = 'roster-push'
    query = iq.addElement(('jabber:iq:roster', 'query'))
    item = query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'both'
    group = item.addElement('group', content='Still alive')
    group = item.addElement('group', content='Capulets')
    stream.send(iq)

    iq = IQ(stream, 'set')
    iq['id'] = 'roster-push'
    query = iq.addElement(('jabber:iq:roster', 'query'))
    item = query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'both'
    group = item.addElement('group', content='Still alive')
    stream.send(iq)

    iq = IQ(stream, 'set')
    iq['id'] = 'roster-push'
    query = iq.addElement(('jabber:iq:roster', 'query'))
    item = query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'both'
    stream.send(iq)

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

    # the XMPP prpl puts people into some sort of group, probably called
    # Buddies
    groups = conn.Properties.Get(cs.CONN_IFACE_CONTACT_GROUPS, 'Groups')
    default_group = None

    for group in groups:
        if group in ('Capulets', 'Still alive'):
            continue

        if default_group is not None:
            raise AssertionError('Two unexplained groups: %s, %s' %
                                 (group, default_group))

        default_group = group

    call_async(q, conn.ContactList, 'GetContactListAttributes',
               [cs.CONN_IFACE_CONTACT_GROUPS], False)
    r = q.expect('dbus-return', method='GetContactListAttributes')

    assertSameSets(['Still alive'], r.value[0][romeo][cs.ATTR_GROUPS])
    assertSameSets(['Still alive', 'Capulets'],
                   r.value[0][juliet][cs.ATTR_GROUPS])
    assertSameSets([default_group], r.value[0][duncan][cs.ATTR_GROUPS])

    # We can't remove Duncan from the default group, because it's his only
    # group
    call_async(q, conn.ContactGroups, 'RemoveFromGroup', default_group,
               [duncan])
    q.expect('dbus-error', method='RemoveFromGroup', name=cs.NOT_AVAILABLE)
    call_async(q, conn.ContactGroups, 'SetGroupMembers', default_group, [])
    q.expect('dbus-error', method='SetGroupMembers', name=cs.NOT_AVAILABLE)
    # SetContactGroups just doesn't do anything in this situation
    call_async(q, conn.ContactGroups, 'SetContactGroups', duncan, [])
    q.expect('dbus-return', method='SetContactGroups')

    call_async(q, conn.ContactList, 'GetContactListAttributes',
               [cs.CONN_IFACE_CONTACT_GROUPS], False)
    r = q.expect('dbus-return', method='GetContactListAttributes')
    assertSameSets([default_group], r.value[0][duncan][cs.ATTR_GROUPS])

    # Make a new group and add Duncan to it
    call_async(q, conn.ContactGroups, 'AddToGroup', 'Scots', [duncan])
    iq, _, _ = q.expect_many(
        EventPattern('stream-iq',
                     iq_type='set',
                     query_name='query',
                     query_ns=ns.ROSTER),
        EventPattern('dbus-signal',
                     signal='GroupsChanged',
                     args=[[duncan], ['Scots'], []]),
        EventPattern('dbus-return', method='AddToGroup'),
    )
    assertEquals('*****@*****.**', iq.stanza.query.item['jid'])
    groups = set([
        str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza)
    ])
    assertLength(2, groups)
    assertContains(default_group, groups)
    assertContains('Scots', groups)

    # Now we can remove him from the default group. Much rejoicing.
    call_async(q, conn.ContactGroups, 'RemoveFromGroup', default_group,
               [duncan])
    iq, _, _ = q.expect_many(
        EventPattern('stream-iq',
                     iq_type='set',
                     query_name='query',
                     query_ns=ns.ROSTER),
        EventPattern('dbus-signal',
                     signal='GroupsChanged',
                     args=[[duncan], [], [default_group]]),
        EventPattern('dbus-return', method='RemoveFromGroup'),
    )
    assertEquals('*****@*****.**', iq.stanza.query.item['jid'])
    groups = set([
        str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza)
    ])
    assertLength(1, groups)
    assertContains('Scots', groups)

    # Test SetContactGroups, which didn't previously have proper coverage
    call_async(q, conn.ContactGroups, 'SetContactGroups', duncan,
               ['Scottish former kings'])
    iq, _, _, _ = q.expect_many(
        EventPattern('stream-iq',
                     iq_type='set',
                     query_name='query',
                     query_ns=ns.ROSTER),
        EventPattern('dbus-signal',
                     signal='GroupsChanged',
                     args=[[duncan], ['Scottish former kings'], []]),
        EventPattern('dbus-signal',
                     signal='GroupsChanged',
                     args=[[duncan], [], ['Scots']]),
        EventPattern('dbus-return', method='SetContactGroups'),
    )
    assertEquals('*****@*****.**', iq.stanza.query.item['jid'])
    groups = set([
        str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza)
    ])
    assertLength(2, groups)
    assertContains('Scots', groups)
    assertContains('Scottish former kings', groups)
    iq, = q.expect_many(
        EventPattern('stream-iq',
                     iq_type='set',
                     query_name='query',
                     query_ns=ns.ROSTER), )
    assertEquals('*****@*****.**', iq.stanza.query.item['jid'])
    groups = set([
        str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza)
    ])
    assertLength(1, groups)
    assertContains('Scottish former kings', groups)

    # Romeo dies. If he drops off the roster as a result, that would be
    # fd.o #21294. However, to fix that bug, Haze now puts him in the
    # default group.
    call_async(q, conn.ContactGroups, 'RemoveFromGroup', 'Still alive',
               [romeo])
    iq1, iq2, _, _, _ = q.expect_many(
        EventPattern('stream-iq',
                     iq_type='set',
                     query_name='query',
                     query_ns=ns.ROSTER),
        EventPattern('stream-iq',
                     iq_type='set',
                     query_name='query',
                     query_ns=ns.ROSTER),
        EventPattern('dbus-signal',
                     signal='GroupsChanged',
                     args=[[romeo], [default_group], []]),
        EventPattern('dbus-signal',
                     signal='GroupsChanged',
                     args=[[romeo], [], ['Still alive']]),
        EventPattern('dbus-return', method='RemoveFromGroup'),
    )

    assertEquals('*****@*****.**', iq1.stanza.query.item['jid'])
    groups = set([
        str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq1.stanza)
    ])
    assertLength(2, groups)
    assertContains('Still alive', groups)
    assertContains(default_group, groups)

    assertEquals('*****@*****.**', iq2.stanza.query.item['jid'])
    groups = set([
        str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq2.stanza)
    ])
    assertLength(1, groups)
    assertContains(default_group, groups)

    # Juliet dies. She's in another group already, so the workaround for
    # fd.o #21294 is not active.
    call_async(q, conn.ContactGroups, 'SetGroupMembers', 'Still alive', [])
    iq, _, _ = q.expect_many(
        EventPattern('stream-iq',
                     iq_type='set',
                     query_name='query',
                     query_ns=ns.ROSTER),
        EventPattern('dbus-signal',
                     signal='GroupsChanged',
                     args=[[juliet], [], ['Still alive']]),
        EventPattern('dbus-return', method='SetGroupMembers'),
    )
    assertEquals('*****@*****.**', iq.stanza.query.item['jid'])
    groups = set([
        str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza)
    ])
    assertLength(1, groups)
    assertContains('Capulets', groups)

    # At the end of a tragedy, everyone dies, so there's no need for this
    # group.
    call_async(q, conn.ContactGroups, 'RemoveGroup', 'Still alive')
    q.expect('dbus-signal', signal='GroupsRemoved', args=[['Still alive']])

    # Deleting a non-empty group is allowed. (It removes everyone.)
    call_async(q, conn.ContactGroups, 'RemoveGroup', 'Capulets')
    q.expect_many(
        EventPattern('dbus-signal',
                     signal='GroupsRemoved',
                     args=[['Capulets']]),
        EventPattern('dbus-signal',
                     signal='GroupsChanged',
                     args=[[juliet], [default_group], []]),
        EventPattern('dbus-signal',
                     signal='GroupsChanged',
                     args=[[juliet], [], ['Capulets']]),
    )
def test(q, bus, conn, stream, bytestream_cls, access_control):
    global last_tube_id

    t.check_conn_properties(q, conn)

    vcard_event, roster_event = q.expect_many(
        EventPattern('stream-iq',
                     to=None,
                     query_ns='vcard-temp',
                     query_name='vCard'),
        EventPattern('stream-iq', query_ns=ns.ROSTER))

    self_handle = conn.Properties.Get(cs.CONN, "SelfHandle")

    acknowledge_iq(stream, vcard_event.stanza)

    roster = roster_event.stanza
    roster['type'] = 'result'
    item = roster_event.query.addElement('item')
    item['jid'] = 'bob@localhost'  # Bob can do tubes
    item['subscription'] = 'both'
    stream.send(roster)

    bob_full_jid = 'bob@localhost/Bob'
    self_full_jid = 'test@localhost/Resource'

    # Send Bob presence and his tube caps
    presence = domish.Element(('jabber:client', 'presence'))
    presence['from'] = bob_full_jid
    presence['to'] = self_full_jid
    c = presence.addElement('c')
    c['xmlns'] = 'http://jabber.org/protocol/caps'
    c['node'] = 'http://example.com/ICantBelieveItsNotTelepathy'
    c['ver'] = '1.2.3'
    stream.send(presence)

    event = q.expect('stream-iq',
                     iq_type='get',
                     query_ns='http://jabber.org/protocol/disco#info',
                     to=bob_full_jid)
    result = event.stanza
    result['type'] = 'result'
    assert event.query['node'] == \
        'http://example.com/ICantBelieveItsNotTelepathy#1.2.3'
    feature = event.query.addElement('feature')
    feature['var'] = ns.TUBES
    stream.send(result)

    # A tube request can be done only if the contact has tube capabilities
    # Ensure that Bob's caps have been received
    sync_stream(q, stream)

    # Also ensure that all the new contact list channels have been announced,
    # so that the NewChannel(s) signals we look for after calling
    # RequestChannel are the ones we wanted.
    sync_dbus(bus, q, conn)

    bob_handle = conn.get_contact_handle_sync('bob@localhost')

    # let's try to accept a D-Bus tube using the new API
    bytestream = bytestream_cls(stream, q, 'gamma', bob_full_jid,
                                self_full_jid, True)

    last_tube_id += 1
    contact_offer_dbus_tube(bytestream, last_tube_id)

    def new_chan_predicate(e):
        path, props = e.args[0][0]
        return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE

    e = q.expect('dbus-signal',
                 signal='NewChannels',
                 predicate=new_chan_predicate)
    channels = e.args[0]
    assert len(channels) == 1
    path, props = channels[0]

    assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE
    assert props[cs.INITIATOR_HANDLE] == bob_handle
    assert props[cs.INITIATOR_ID] == 'bob@localhost'
    assert props[cs.INTERFACES] == [cs.CHANNEL_IFACE_TUBE]
    assert props[cs.REQUESTED] == False
    assert props[cs.TARGET_HANDLE] == bob_handle
    assert props[cs.TARGET_ID] == 'bob@localhost'
    assert props[cs.DBUS_TUBE_SERVICE_NAME] == 'com.example.TestCase2'
    assert props[cs.TUBE_PARAMETERS] == {'login': '******'}
    assert props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS] == [
        cs.SOCKET_ACCESS_CONTROL_CREDENTIALS,
        cs.SOCKET_ACCESS_CONTROL_LOCALHOST
    ]
    assert cs.TUBE_STATE not in props

    tube_chan = bus.get_object(conn.bus_name, path)
    tube_chan_iface = dbus.Interface(tube_chan, cs.CHANNEL)
    dbus_tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_DBUS_TUBE)

    status = tube_chan.Get(cs.CHANNEL_IFACE_TUBE,
                           'State',
                           dbus_interface=cs.PROPERTIES_IFACE)
    assert status == cs.TUBE_STATE_LOCAL_PENDING

    # try to accept using a wrong access control
    try:
        dbus_tube_iface.Accept(cs.SOCKET_ACCESS_CONTROL_PORT)
    except dbus.DBusException as e:
        assertEquals(e.get_dbus_name(), cs.INVALID_ARGUMENT)
    else:
        assert False

    # accept the tube (new API)
    call_async(q, dbus_tube_iface, 'Accept', access_control)

    events = q.expect_many(
        EventPattern('stream-iq', iq_type='result', query_ns=ns.SI),
        EventPattern('dbus-return', method='Accept'))

    iq_event = events[0]
    bytestream.check_si_reply(iq_event.stanza)
    tube = xpath.queryForNodes('/iq/si/tube[@xmlns="%s"]' % ns.TUBES,
                               iq_event.stanza)
    assert len(tube) == 1

    return_event = events[1]
    addr = return_event.value[0]
    assert len(addr) > 0

    # Open the bytestream
    _, state_event = bytestream.open_bytestream([], [
        EventPattern(
            'dbus-signal', signal='TubeChannelStateChanged', path=path)
    ])

    state_event = state_event[0]
    assert state_event.args[0] == cs.TUBE_STATE_OPEN

    # close the tube
    tube_chan_iface.Close()

    q.expect_many(EventPattern('dbus-signal', signal='Closed'),
                  EventPattern('dbus-signal', signal='ChannelClosed'))
Example #55
0
        conn.Location,
        'SetLocation',
        {
            'lat': dbus.Double(0.0, variant_level=1),
            'lon': 0.0,
            'language': 'en',
            'timestamp': date,
            'country': 'Congo',
            'accuracy': 1.4,
            # Gabble silently ignores unknown keys
            'badger': 'mushroom'
        })

    geoloc_iq_set_event = EventPattern(
        'stream-iq',
        predicate=lambda x: xpath.queryForNodes(
            "/iq/pubsub/publish/item/geoloc", x.stanza))

    event = q.expect_many(geoloc_iq_set_event)[0]
    geoloc = xpath.queryForNodes("/iq/pubsub/publish/item/geoloc",
                                 event.stanza)[0]
    assertEquals(geoloc.getAttribute((ns.XML, 'lang')), 'en')
    lon = xpath.queryForNodes('/geoloc/lon', geoloc)[0]
    assertEquals(float(str(lon)), 0.0)
    lat = xpath.queryForNodes('/geoloc/lat', geoloc)[0]
    assertEquals(float(str(lat)), 0.0)
    timestamp = xpath.queryForNodes('/geoloc/timestamp', geoloc)[0]
    assertEquals(str(timestamp), date_str)
    country = xpath.queryForNodes('/geoloc/country', geoloc)[0]
    assertEquals(str(country), 'Congo')
    lat = xpath.queryForNodes('/geoloc/accuracy', geoloc)[0]
    assertEquals(float(str(lat)), 1.4)
Example #56
0
def process_site(mcursor, nwsli, network):
    """Do our processing work"""

    url = ("http://water.weather.gov/ahps2/hydrograph_to_xml.php?"
           "gage=%s&output=xml") % (nwsli, )

    elementStream = domish.elementStream()
    roots = []
    results = []
    elementStream.DocumentStartEvent = roots.append
    elementStream.ElementEvent = lambda elem: roots[0].addChild(elem)
    elementStream.DocumentEndEvent = lambda: results.append(roots[0])
    try:
        req = requests.get(url, timeout=30)
        xml = req.content
        if xml.strip() == 'No results found for this gage.':
            print('No results for %s' % (nwsli, ))
            return
    except Exception as exp:
        print("DOWNLOAD ERROR")
        print(url)
        print(exp)
        return
    try:
        elementStream.parse(xml)
    except Exception as exp:
        print("XML ERROR")
        print(url)
        print(exp)
        return

    elem = results[0]

    nodes = xpath.queryForNodes('/site/sigstages', elem)
    if nodes is None:
        print("No data found for %s" % (nwsli, ))
        return

    sigstages = nodes[0]
    data = {
        'id': nwsli,
        'network': network,
        'sigstage_low': None,
        'sigstage_action': None,
        'sigstage_bankfull': None,
        'sigstage_flood': None,
        'sigstage_moderate': None,
        'sigstage_major': None,
        'sigstage_record': None
    }
    for s in sigstages.children:
        val = str(s)
        if val == '':
            continue
        data['sigstage_%s' % (s.name, )] = float(val)

    if 'sigstage_low' not in data:
        print('No Data %s %s' % (nwsli, network))
        return

    print(("%s %5.1f %5.1f %5.1f %5.1f %5.1f %5.1f %5.1f") %
          (data['id'], data['sigstage_low'] or -99, data['sigstage_action']
           or -99, data['sigstage_bankfull'] or -99, data['sigstage_flood']
           or -99, data['sigstage_moderate'] or -99, data['sigstage_major']
           or -99, data['sigstage_record'] or -99))
    mcursor.execute(
        """
        UPDATE stations SET sigstage_low = %(sigstage_low)s,
        sigstage_action = %(sigstage_action)s,
        sigstage_bankfull = %(sigstage_bankfull)s,
        sigstage_flood = %(sigstage_flood)s,
        sigstage_moderate = %(sigstage_moderate)s,
        sigstage_major = %(sigstage_major)s,
        sigstage_record = %(sigstage_record)s
        WHERE id = %(id)s and network = %(network)s
    """, data)
def test_two_clients(q, bus, conn, stream, contact1, contact2, contact_handle1,
                     contact_handle2, client, broken_hash):

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

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

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

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

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

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

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

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

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

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

    stream.send(result)

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

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

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

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

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

    # don't receive any further signals
    q.forbid_events(forbidden)
    sync_dbus(bus, q, conn)
    q.unforbid_events(forbidden)