Example #1
0
def test_conflict(q, gateways_iface, stream):
    call_async(q, gateways_iface, 'Register',
            'sip.example.com', '8675309', 'jenny')
    e = q.expect('stream-iq', iq_type='set', query_name='query',
            query_ns=ns.REGISTER, to='sip.example.com')
    assertEquals('8675309', xpath.queryForString('/query/username', e.query))
    assertEquals('jenny', xpath.queryForString('/query/password', e.query))
    error = domish.Element((None, 'error'))
    error['type'] = 'cancel'
    error['code'] = '409'
    error.addElement((ns.STANZA, 'conflict'))
    send_error_reply(stream, e.stanza, error)
    q.expect('dbus-error', method='Register', name=cs.REGISTRATION_EXISTS)
Example #2
0
def test_not_acceptable(q, gateways_iface, stream):
    call_async(q, gateways_iface, 'Register',
            'fully-captcha-enabled.example.com', 'lalala', 'stoats')
    e = q.expect('stream-iq', iq_type='set', query_name='query',
            query_ns=ns.REGISTER, to='fully-captcha-enabled.example.com')
    assertEquals('lalala', xpath.queryForString('/query/username', e.query))
    assertEquals('stoats', xpath.queryForString('/query/password', e.query))
    error = domish.Element((None, 'error'))
    error['type'] = 'modify'
    error['code'] = '406'
    error.addElement((ns.STANZA, 'not-acceptable'))
    send_error_reply(stream, e.stanza, error)
    q.expect('dbus-error', method='Register', name=cs.NOT_AVAILABLE)
Example #3
0
def test_success(q, gateways_iface, stream):
    call_async(q, gateways_iface, 'Register',
            'talkd.example.com', '1970', 's3kr1t')
    e = q.expect('stream-iq', iq_type='set', query_name='query',
            query_ns=ns.REGISTER, to='talkd.example.com')
    assertEquals('1970', xpath.queryForString('/query/username', e.query))
    assertEquals('s3kr1t', xpath.queryForString('/query/password', e.query))
    acknowledge_iq(stream, e.stanza)
    q.expect_many(
            EventPattern('dbus-return', method='Register'),
            EventPattern('stream-presence', presence_type='subscribe',
                to='talkd.example.com'),
            )
    stream.send(make_presence('talkd.example.com', type='subscribed'))
Example #4
0
 def testStaticMethods(self):
     self.assertEquals(xpath.matches("/foo/bar", self.e), True)
     self.assertEquals(xpath.queryForNodes("/foo/bar", self.e),
                       [self.bar1, self.bar2, self.bar4])
     self.assertEquals(xpath.queryForString("/foo", self.e), "somecontent")
     self.assertEquals(xpath.queryForStringList("/foo", self.e),
                       ["somecontent", "somemorecontent"])
def test_conflict(q, gateways_iface, stream):
    call_async(q, gateways_iface, 'Register', 'sip.example.com', '8675309',
               'jenny')
    e = q.expect('stream-iq',
                 iq_type='set',
                 query_name='query',
                 query_ns=ns.REGISTER,
                 to='sip.example.com')
    assertEquals('8675309', xpath.queryForString('/query/username', e.query))
    assertEquals('jenny', xpath.queryForString('/query/password', e.query))
    error = domish.Element((None, 'error'))
    error['type'] = 'cancel'
    error['code'] = '409'
    error.addElement((ns.STANZA, 'conflict'))
    send_error_reply(stream, e.stanza, error)
    q.expect('dbus-error', method='Register', name=cs.REGISTRATION_EXISTS)
Example #6
0
    def processMessagePC(self, elem):
        # log.msg("processMessagePC() called from %s...." % (elem['from'],))
        _from = jid.JID(elem["from"])
        if (elem["from"] == self.config['bot.xmppdomain']):
            log.msg("MESSAGE FROM SERVER?")
            return
        # Intercept private messages via a chatroom, can't do that :)
        if _from.host == self.config['bot.mucservice']:
            log.msg("ERROR: message is MUC private chat")
            return

        if _from.userhost() != "iembot_ingest@%s" % (
                                            self.config['bot.xmppdomain']):
            log.msg("ERROR: message not from iembot_ingest")
            return

        # Go look for body to see routing info!
        # Get the body string
        bstring = xpath.queryForString('/message/body', elem)
        if not bstring:
            log.msg("Nothing found in body?")
            return

        if elem.x and elem.x.hasAttribute("channels"):
            channels = elem.x['channels'].split(",")
        else:
            # The body string contains
            channel = bstring.split(":", 1)[0]
            channels = [channel, ]
            # Send to chatroom, clip body of channel notation
            # elem.body.children[0] = meat

        # Always send to botstalk
        elem['to'] = "botstalk@%s" % (self.config['bot.mucservice'],)
        elem['type'] = "groupchat"
        self.send_groupchat_elem(elem)

        for channel in channels:
            for room in self.routingtable.get(channel, []):
                elem['to'] = "%s@%s" % (room, self.config['bot.mucservice'])
                self.send_groupchat_elem(elem)
            for page in self.tw_routingtable.get(channel, []):
                if page not in self.tw_access_tokens:
                    log.msg(("Failed to tweet due to no access_tokens for %s"
                             ) % (page,))
                    continue
                # Require the x.twitter attribute to be set to prevent
                # confusion with some ingestors still sending tweets themself
                if not elem.x.hasAttribute("twitter"):
                    continue
                twtextra = {}
                if (elem.x and elem.x.hasAttribute("lat") and
                        elem.x.hasAttribute("long")):
                    twtextra['lat'] = elem.x['lat']
                    twtextra['long'] = elem.x['long']
                log.msg("Sending tweet '%s' to page '%s'" % (elem.x['twitter'],
                                                             page))
                # Finally, actually tweet, this is in basicbot
                self.tweet(elem.x['twitter'], self.tw_access_tokens[page],
                           twtextra=twtextra, twituser=page)
def test_not_acceptable(q, gateways_iface, stream):
    call_async(q, gateways_iface, 'Register',
               'fully-captcha-enabled.example.com', 'lalala', 'stoats')
    e = q.expect('stream-iq',
                 iq_type='set',
                 query_name='query',
                 query_ns=ns.REGISTER,
                 to='fully-captcha-enabled.example.com')
    assertEquals('lalala', xpath.queryForString('/query/username', e.query))
    assertEquals('stoats', xpath.queryForString('/query/password', e.query))
    error = domish.Element((None, 'error'))
    error['type'] = 'modify'
    error['code'] = '406'
    error.addElement((ns.STANZA, 'not-acceptable'))
    send_error_reply(stream, e.stanza, error)
    q.expect('dbus-error', method='Register', name=cs.NOT_AVAILABLE)
Example #8
0
    def check(vcard):
        assertEquals(mime_type,
            xpath.queryForString('/vCard/PHOTO/TYPE', vcard))

        binval = xpath.queryForString('/vCard/PHOTO/BINVAL', vcard)

        # <http://xmpp.org/extensions/xep-0153.html#bizrules-image> says:
        #
        #  5. The image data MUST conform to the base64Binary datatype and thus
        #     be encoded in accordance with Section 6.8 of RFC 2045, which
        #     recommends that base64 data should have lines limited to at most
        #     76 characters in length.
        lines = binval.split('\n')
        for line in lines:
            assert len(line) <= 76, line

        assertEquals(image_data, base64.decodestring(binval))
Example #9
0
 def onMessage(self, msg):
     jidparts = jid.parse(msg['from'])
     userhost = "%s@%s" % (jidparts[0], jidparts[1])
     
     if self.rooms.has_key(userhost):
         room_id = self.rooms[userhost][0]
         body = xpath.queryForString("/message/body", msg)
         return Message(speaker=jidparts[2], message=body, room_id=room_id, created_at=datetime.datetime.now()).save()
Example #10
0
    def check(vcard):
        assertEquals(mime_type, xpath.queryForString('/vCard/PHOTO/TYPE',
                                                     vcard))

        binval = xpath.queryForString('/vCard/PHOTO/BINVAL', vcard)

        # <http://xmpp.org/extensions/xep-0153.html#bizrules-image> says:
        #
        #  5. The image data MUST conform to the base64Binary datatype and thus
        #     be encoded in accordance with Section 6.8 of RFC 2045, which
        #     recommends that base64 data should have lines limited to at most
        #     76 characters in length.
        lines = binval.split('\n')
        for line in lines:
            assert len(line) <= 76, line

        assertEquals(image_data, base64.decodestring(binval))
 def testStaticMethods(self):
     self.assertEquals(xpath.matches("/foo/bar", self.e),
                       True)
     self.assertEquals(xpath.queryForNodes("/foo/bar", self.e),
                       [self.bar1, self.bar2, self.bar4])
     self.assertEquals(xpath.queryForString("/foo", self.e),
                       "somecontent")
     self.assertEquals(xpath.queryForStringList("/foo", self.e),
                       ["somecontent", "somemorecontent"])
def _session_terminate_predicate(event, msg):
    reason = xpath.queryForNodes("/iq"
                               "/jingle[@action='session-terminate']"
                               "/reason/failed-application",
                               event.stanza)
    reason_text = xpath.queryForString("/iq/jingle/reason/text",
                                       event.stanza)

    return reason is not None and reason_text == msg
Example #13
0
def message_processor(stanza):
    """ Process a message stanza """
    body = xpath.queryForString("/message/body", stanza)
    log.msg("Message from %s Body: %s" % (stanza["from"], body))
    if body is None:
        return
    if body.lower().strip() == "shutdown":
        log.msg("I got shutdown message, shutting down...")
        reactor.callWhenRunning(reactor.stop)  # @UndefinedVariable
def test_success(q, gateways_iface, stream):
    call_async(q, gateways_iface, 'Register', 'talkd.example.com', '1970',
               's3kr1t')
    e = q.expect('stream-iq',
                 iq_type='set',
                 query_name='query',
                 query_ns=ns.REGISTER,
                 to='talkd.example.com')
    assertEquals('1970', xpath.queryForString('/query/username', e.query))
    assertEquals('s3kr1t', xpath.queryForString('/query/password', e.query))
    acknowledge_iq(stream, e.stanza)
    q.expect_many(
        EventPattern('dbus-return', method='Register'),
        EventPattern('stream-presence',
                     presence_type='subscribe',
                     to='talkd.example.com'),
    )
    stream.send(make_presence('talkd.example.com', type='subscribed'))
Example #15
0
 def message_processor(self, stanza):
     """ Process a message stanza """
     body = xpath.queryForString("/message/body", stanza)
     log.msg("Message from %s Body: %s" % (stanza['from'], body))
     if body is None:
         return
     if body.lower().strip() == "shutdown":
         log.msg("I got shutdown message, shutting down...")
         reactor.callWhenRunning(reactor.stop)
Example #16
0
def connect_and_send_form(q, conn, stream):
    conn.Connect()
    q.expect('dbus-signal', signal='StatusChanged',
        args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED])

    event = q.expect('stream-iq', query_ns=ns.REGISTER)
    result = make_result_iq(stream, event.stanza)
    query = result.firstChildElement()
    query.addElement('username')
    query.addElement('password')

    stream.send(result)

    event = q.expect('stream-iq')
    iq = event.stanza
    assert xpath.queryForString('/iq/query/username', iq) == 'test'
    assert xpath.queryForString('/iq/query/password', iq) == 'pass'

    return iq
 def test_staticMethods(self):
     """
     Test basic operation of the static methods.
     """
     self.assertEquals(xpath.matches("/foo/bar", self.e), True)
     self.assertEquals(
         xpath.queryForNodes("/foo/bar", self.e), [self.bar1, self.bar2, self.bar4, self.bar5, self.bar6, self.bar7]
     )
     self.assertEquals(xpath.queryForString("/foo", self.e), "somecontent")
     self.assertEquals(xpath.queryForStringList("/foo", self.e), ["somecontent", "somemorecontent"])
def _session_terminate_predicate(event, reason, msg, jp):
    matches = jp.match_jingle_action(event.query, "session-terminate")

    if matches and jp.is_modern_jingle():
        reason = xpath.queryForNodes("/iq" "/jingle[@action='session-terminate']" "/reason/%s" % reason, event.stanza)
        reason_text = xpath.queryForString("/iq/jingle/reason/text", event.stanza)

        return bool(reason) and reason_text == msg

    return matches
Example #19
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 connect_and_send_form(q, conn, stream):
    conn.Connect()
    q.expect('dbus-signal',
             signal='StatusChanged',
             args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED])

    event = q.expect('stream-iq', query_ns=ns.REGISTER)
    result = make_result_iq(stream, event.stanza)
    query = result.firstChildElement()
    query.addElement('username')
    query.addElement('password')

    stream.send(result)

    event = q.expect('stream-iq')
    iq = event.stanza
    assert xpath.queryForString('/iq/query/username', iq) == 'test'
    assert xpath.queryForString('/iq/query/password', iq) == 'pass'

    return iq
Example #21
0
 def test_staticMethods(self):
     """
     Test basic operation of the static methods.
     """
     self.assertEqual(xpath.matches("/foo/bar", self.e), True)
     self.assertEqual(
         xpath.queryForNodes("/foo/bar", self.e),
         [self.bar1, self.bar2, self.bar4, self.bar5, self.bar6, self.bar7])
     self.assertEqual(xpath.queryForString("/foo", self.e), "somecontent")
     self.assertEqual(xpath.queryForStringList("/foo", self.e),
                      ["somecontent", "somemorecontent"])
    def bindIq(self, iq):
        resource = xpath.queryForString('/iq/bind/resource', iq)
        if self.resource is not None:
            assertEquals(self.resource, resource)
        else:
            assert resource is not None

        result = IQ(self.xmlstream, "result")
        result["id"] = iq["id"]
        bind = result.addElement((NS_XMPP_BIND, 'bind'))
        jid = bind.addElement('jid', content=('test@localhost/%s' % resource))
        self.xmlstream.send(result)

        self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
Example #23
0
    def bindIq(self, iq):
        resource = xpath.queryForString('/iq/bind/resource', iq)
        if self.resource is not None:
            assertEquals(self.resource, resource)
        else:
            assert resource is not None

        result = IQ(self.xmlstream, "result")
        result["id"] = iq["id"]
        bind = result.addElement((ns.NS_XMPP_BIND, 'bind'))
        self.bare_jid = '%s@%s' % (self.username, self.xmlstream.domain)
        self.full_jid = '%s/%s' % (self.bare_jid, resource)
        jid = bind.addElement('jid', content=self.full_jid)
        self.xmlstream.send(result)

        self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
    def bindIq(self, iq):
        resource = xpath.queryForString('/iq/bind/resource', iq)
        if self.resource is not None:
            assertEquals(self.resource, resource)
        else:
            assert resource is not None

        result = IQ(self.xmlstream, "result")
        result["id"] = iq["id"]
        bind = result.addElement((ns.NS_XMPP_BIND, 'bind'))
        self.bare_jid = '%s@%s' % (self.username, self.xmlstream.domain)
        self.full_jid = '%s/%s' % (self.bare_jid, resource)
        jid = bind.addElement('jid', content=self.full_jid)
        self.xmlstream.send(result)

        self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
Example #25
0
    def onPresence(self, pres):
        to = jid.JID(pres['to'])
        fr = jid.JID(pres['from'])
        account, roomname = self.parseCampfireName(to)

        def handleAuth(campfire):
            if campfire is None:
                self.sendErrorPresence(pres, "not-allowed", "cancel", NS_MUC)
            else:
                self.initializeRoom(campfire, roomname, to, jid.JID(pres['from']))

        password = xpath.queryForString("/presence/x/password", pres)
        if pres.getAttribute('type') == "unavailable":
            self.smokey.putCampfireOut(account, to)
        elif password == "":
            self.sendErrorPresence(pres, "not-authorized")
        else:
            log.msg("attempting to auth %s for room %s on account %s" % (to.resource, roomname, account))
            self.smokey.initCampfire(account, fr, to, password).addCallback(handleAuth)
Example #26
0
    def onPresence(self, pres):
        to = jid.JID(pres['to'])
        fr = jid.JID(pres['from'])
        account, roomname = self.parseCampfireName(to)

        def handleAuth(campfire):
            if campfire is None:
                self.sendErrorPresence(pres, "not-allowed", "cancel", NS_MUC)
            else:
                self.initializeRoom(campfire, roomname, to,
                                    jid.JID(pres['from']))

        password = xpath.queryForString("/presence/x/password", pres)
        if pres.getAttribute('type') == "unavailable":
            self.smokey.putCampfireOut(account, to)
        elif password == "":
            self.sendErrorPresence(pres, "not-authorized")
        else:
            log.msg("attempting to auth %s for room %s on account %s" %
                    (to.resource, roomname, account))
            self.smokey.initCampfire(account, fr, to,
                                     password).addCallback(handleAuth)
Example #27
0
def extract_hash_from_presence(stanza):
    return xpath.queryForString(
        '/presence/x[@xmlns="%s"]/photo' % ns.VCARD_TEMP_UPDATE,
        stanza)
Example #28
0
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.GetSelfHandle()
    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))
    assertEquals(hello_binval, xpath.queryForString('/iq/vCard/PHOTO/BINVAL',
        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 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))
    assertEquals(hello_binval, xpath.queryForString('/iq/vCard/PHOTO/BINVAL',
        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))

    # 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)
    conn.Disconnect()
    q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
Example #29
0
 def sendMsgRoom(room):
     if room is not None:
         room.say(xpath.queryForString("/message/body", msg))
Example #30
0
 def sendMsgRoom(room):
     if room is not None:
         room.say(xpath.queryForString("/message/body", msg))
Example #31
0
def test(q, bus, conn, stream):
    event = q.expect('stream-iq',
                     to=None,
                     query_ns='vcard-temp',
                     query_name='vCard')

    call_async(
        q, conn.ContactInfo, 'SetContactInfo',
        [(u'fn', [], [u'Wee Ninja']),
         (u'n', [], [u'Ninja', u'Wee', u'', u'', u'-san']),
         (u'org', [], ['Collabora, Ltd.']),
         (u'adr', ['type=work', 'type=postal', 'type=parcel'], [
             '', '', '11 Kings Parade', 'Cambridge', 'Cambridgeshire',
             'CB2 1SJ', 'UK'
         ]),
         (u'label', ['type=work'], [
             '11 Kings Parade\n'
             'Cambridge\n'
             'Cambridgeshire\n'
             'CB2 1SJ\n'
             'UK\n'
         ]), (u'tel', ['type=voice', 'type=work'], ['+44 1223 362967']),
         (u'tel', ['type=voice', 'type=work'], ['+44 7700 900753']),
         (u'email', ['type=internet', 'type=pref'
                     ], ['*****@*****.**']),
         (u'email', ['type=internet'], ['*****@*****.**']),
         (u'x-jabber', [], ['*****@*****.**']),
         (u'x-jabber', [], ['*****@*****.**']),
         (u'url', [], ['http://www.thinkgeek.com/geektoys/plush/8823/']),
         (u'nickname', [], [u'HR Ninja']),
         (u'nickname', [], [u'Enforcement Ninja'])])

    # We don't acknowledge the initial vCard get until we're sure that Gabble's
    # received our call to SetContactInfo. This ensures that it will issue a
    # set when we send the reply, rather than issuing another get to ensure the
    # vCard is really, absolutely up to date before editing it.
    sync_dbus(bus, q, conn)
    acknowledge_iq(stream, event.stanza)

    vcard_set_event = q.expect('stream-iq',
                               iq_type='set',
                               query_ns='vcard-temp',
                               query_name='vCard')

    assertLength(
        2, xpath.queryForNodes('/iq/vCard/NICKNAME', vcard_set_event.stanza))
    nicknames = []
    for nickname in xpath.queryForNodes('/iq/vCard/NICKNAME',
                                        vcard_set_event.stanza):
        nicknames.append(str(nickname))
    assertEquals(['HR Ninja', 'Enforcement Ninja'], nicknames)

    assertEquals(
        None, xpath.queryForNodes('/iq/vCard/PHOTO', vcard_set_event.stanza))
    assertLength(1, xpath.queryForNodes('/iq/vCard/N', vcard_set_event.stanza))
    assertEquals(
        'Wee', xpath.queryForString('/iq/vCard/N/GIVEN',
                                    vcard_set_event.stanza))
    assertEquals(
        'Ninja',
        xpath.queryForString('/iq/vCard/N/FAMILY', vcard_set_event.stanza))
    assertEquals(
        '-san',
        xpath.queryForString('/iq/vCard/N/SUFFIX', vcard_set_event.stanza))
    assertEquals('Wee Ninja',
                 xpath.queryForString('/iq/vCard/FN', vcard_set_event.stanza))

    assertLength(1, xpath.queryForNodes('/iq/vCard/ORG',
                                        vcard_set_event.stanza))
    assertEquals(
        'Collabora, Ltd.',
        xpath.queryForString('/iq/vCard/ORG/ORGNAME', vcard_set_event.stanza))
    assertEquals(
        None,
        xpath.queryForNodes('/iq/vCard/ORG/ORGUNIT', vcard_set_event.stanza))

    assertLength(
        1, xpath.queryForNodes('/iq/vCard/LABEL', vcard_set_event.stanza))
    lines = xpath.queryForNodes('/iq/vCard/LABEL/LINE', vcard_set_event.stanza)
    assertLength(5, lines)
    for i, exp_line in enumerate(
        ['11 Kings Parade', 'Cambridge', 'Cambridgeshire', 'CB2 1SJ', 'UK']):
        assertEquals(exp_line, str(lines[i]))

    assertLength(2, xpath.queryForNodes('/iq/vCard/TEL',
                                        vcard_set_event.stanza))
    for tel in xpath.queryForNodes('/iq/vCard/TEL', vcard_set_event.stanza):
        assertLength(1, xpath.queryForNodes('/TEL/NUMBER', tel))
        assertContains(xpath.queryForString('/TEL/NUMBER', tel),
                       ('+44 1223 362967', '+44 7700 900753'))
        assertLength(1, xpath.queryForNodes('/TEL/VOICE', tel))
        assertLength(1, xpath.queryForNodes('/TEL/WORK', tel))

    assertLength(
        2, xpath.queryForNodes('/iq/vCard/EMAIL', vcard_set_event.stanza))
    for email in xpath.queryForNodes('/iq/vCard/EMAIL',
                                     vcard_set_event.stanza):
        assertContains(xpath.queryForString('/EMAIL/USERID', email),
                       ('*****@*****.**', '*****@*****.**'))
        assertLength(1, xpath.queryForNodes('/EMAIL/INTERNET', email))
        if 'collabora' in xpath.queryForString('/EMAIL/USERID', email):
            assertLength(1, xpath.queryForNodes('/EMAIL/PREF', email))
        else:
            assertEquals(None, xpath.queryForNodes('/EMAIL/PREF', email))

    assertLength(
        2, xpath.queryForNodes('/iq/vCard/JABBERID', vcard_set_event.stanza))
    for jid in xpath.queryForNodes('/iq/vCard/JABBERID',
                                   vcard_set_event.stanza):
        assertContains(xpath.queryForString('/JABBERID', jid),
                       ('*****@*****.**', '*****@*****.**'))

    acknowledge_iq(stream, vcard_set_event.stanza)
    q.expect_many(
        EventPattern('dbus-return', method='SetContactInfo'),
        EventPattern('dbus-signal',
                     signal='AliasesChanged',
                     predicate=lambda e: e.args[0][0][1] == 'HR Ninja'),
        EventPattern('dbus-signal', signal='ContactInfoChanged'),
    )

    # Exercise various invalid SetContactInfo operations
    sync_stream(q, stream)
    sync_dbus(bus, q, conn)

    forbidden = [EventPattern('stream-iq', query_ns='vcard-temp')]
    q.forbid_events(forbidden)

    # unknown field
    call_async(q, conn.ContactInfo, 'SetContactInfo',
               [('x-salary', [], ['547 espressos per month'])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # not enough values for a simple field
    call_async(q, conn.ContactInfo, 'SetContactInfo', [('fn', [], [])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # too many values for a simple field
    call_async(q, conn.ContactInfo, 'SetContactInfo',
               [('fn', [], ['Wee', 'Ninja'])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # unsupported type-parameter for a simple field
    call_async(q, conn.ContactInfo, 'SetContactInfo',
               [('fn', ['language=ja'], ['Wee Ninja'])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # unsupported type-parameter for a structured field
    call_async(
        q, conn.ContactInfo, 'SetContactInfo',
        [(u'n', ['language=ja'], [u'Ninja', u'Wee', u'', u'', u'-san'])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # unsupported type-parameter for LABEL
    call_async(
        q, conn.ContactInfo, 'SetContactInfo',
        [('label', ['language=en'], ['Collabora Ltd.\n11 Kings Parade'])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # not enough values for LABEL
    call_async(q, conn.ContactInfo, 'SetContactInfo', [('label', [], [])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # too many values for LABEL
    call_async(q, conn.ContactInfo, 'SetContactInfo',
               [('label', [], ['11 Kings Parade', 'Cambridge'])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # unsupported type-parameter for ORG
    call_async(q, conn.ContactInfo, 'SetContactInfo',
               [('org', ['language=en'], ['Collabora Ltd.'])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # empty ORG
    call_async(q, conn.ContactInfo, 'SetContactInfo', [('org', [], [])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # not enough values for N
    call_async(q, conn.ContactInfo, 'SetContactInfo', [('n', [], ['Ninja'])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # too many values for N
    call_async(
        q, conn.ContactInfo, 'SetContactInfo',
        [('n', [],
          'what could it mean if you have too many field values?'.split())])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    q.unforbid_events(forbidden)

    # Following a reshuffle, Company Policy Enforcement is declared to be
    # a sub-department within Human Resources, and the ninja no longer
    # qualifies for a company phone

    vcard_in = [
        (u'fn', [], [u'Wee Ninja']),
        (u'n', [], [u'Ninja', u'Wee', u'', u'', u'-san']),
        (u'org', [],
         ['Collabora, Ltd.', 'Human Resources', 'Company Policy Enforcement']),
        (u'adr', ['type=work', 'type=postal', 'type=parcel'], [
            '', '', '11 Kings Parade', 'Cambridge', 'Cambridgeshire',
            'CB2 1SJ', 'UK'
        ]), (u'tel', ['type=voice', 'type=work'], ['+44 1223 362967']),
        (u'email', ['type=internet',
                    'type=pref'], ['*****@*****.**']),
        (u'email', ['type=internet'], ['*****@*****.**']),
        (u'url', [], ['http://www.thinkgeek.com/geektoys/plush/8823/']),
        (u'nickname', [], [u'HR Ninja']),
        (u'nickname', [], [u'Enforcement Ninja'])
    ]

    call_async(q, conn.ContactInfo, 'SetContactInfo', vcard_in)

    event = q.expect('stream-iq',
                     iq_type='get',
                     query_ns='vcard-temp',
                     query_name='vCard')
    repeat_previous_vcard(stream, event.stanza, vcard_set_event.stanza)

    _, vcard_set_event = q.expect_many(
        EventPattern('dbus-signal', signal='ContactInfoChanged'),
        EventPattern('stream-iq',
                     iq_type='set',
                     query_ns='vcard-temp',
                     query_name='vCard'),
    )

    assertLength(1, xpath.queryForNodes('/iq/vCard/ORG',
                                        vcard_set_event.stanza))
    assertEquals(
        'Collabora, Ltd.',
        xpath.queryForString('/iq/vCard/ORG/ORGNAME', vcard_set_event.stanza))
    units = xpath.queryForNodes('/iq/vCard/ORG/ORGUNIT',
                                vcard_set_event.stanza)
    assertLength(2, units)
    for i, exp_unit in enumerate(
        ['Human Resources', 'Company Policy Enforcement']):
        assertEquals(exp_unit, str(units[i]))

    assertLength(1, xpath.queryForNodes('/iq/vCard/TEL',
                                        vcard_set_event.stanza))
    for tel in xpath.queryForNodes('/iq/vCard/TEL', vcard_set_event.stanza):
        assertLength(1, xpath.queryForNodes('/TEL/NUMBER', tel))
        assertEquals('+44 1223 362967',
                     xpath.queryForString('/TEL/NUMBER', tel))
        assertLength(1, xpath.queryForNodes('/TEL/VOICE', tel))
        assertLength(1, xpath.queryForNodes('/TEL/WORK', tel))

    acknowledge_iq(stream, vcard_set_event.stanza)
    _, event = q.expect_many(
        EventPattern('dbus-return', method='SetContactInfo'),
        EventPattern('dbus-signal', signal='ContactInfoChanged'),
    )

    vcard_out = event.args[1][:]

    # the only change we expect to see is that perhaps the fields are
    # re-ordered, and perhaps the types on the 'tel' are re-ordered

    assertEquals(vcard_in[4][0], 'tel')
    vcard_in[4][1].sort()
    assertEquals(vcard_out[4][0], 'tel')
    vcard_out[4][1].sort()
    assertEquals(vcard_in, vcard_out)

    # Finally, the ninja decides that publishing his contact details is not
    # very ninja-like, and decides to be anonymous. The first (most important)
    # of his nicknames from the old vCard is kept, due to nickname's dual role
    # as ContactInfo and the alias.
    call_async(q, conn.ContactInfo, 'SetContactInfo', [])

    event = q.expect('stream-iq',
                     iq_type='get',
                     query_ns='vcard-temp',
                     query_name='vCard')
    repeat_previous_vcard(stream, event.stanza, vcard_set_event.stanza)

    vcard_set_event = q.expect('stream-iq',
                               iq_type='set',
                               query_ns='vcard-temp',
                               query_name='vCard')
    assertLength(1, xpath.queryForNodes('/iq/vCard/*', vcard_set_event.stanza))
    assertEquals(
        'HR Ninja',
        xpath.queryForString('/iq/vCard/NICKNAME', vcard_set_event.stanza))

    acknowledge_iq(stream, vcard_set_event.stanza)
    q.expect_many(
        EventPattern('dbus-return', method='SetContactInfo'),
        EventPattern('dbus-signal', signal='ContactInfoChanged'),
    )
Example #32
0
def extract_hash_from_presence(stanza):
    return xpath.queryForString(
        '/presence/x[@xmlns="%s"]/photo' % ns.VCARD_TEMP_UPDATE, stanza)
def test(q, bus, conn, stream):
    event = q.expect('stream-iq', to=None, query_ns='vcard-temp',
            query_name='vCard')

    call_async(q, conn.ContactInfo, 'SetContactInfo',
               [(u'fn', [], [u'Wee Ninja']),
                (u'n', [], [u'Ninja', u'Wee', u'', u'', u'-san']),
                (u'org', [], ['Collabora, Ltd.']),
                (u'adr', ['type=work','type=postal','type=parcel'],
                    ['', '', '11 Kings Parade', 'Cambridge', 'Cambridgeshire',
                        'CB2 1SJ', 'UK']),
                (u'label', ['type=work'], [
                    '11 Kings Parade\n'
                    'Cambridge\n'
                    'Cambridgeshire\n'
                    'CB2 1SJ\n'
                    'UK\n']),
                (u'tel', ['type=voice','type=work'], ['+44 1223 362967']),
                (u'tel', ['type=voice','type=work'], ['+44 7700 900753']),
                (u'email', ['type=internet','type=pref'],
                    ['*****@*****.**']),
                (u'email', ['type=internet'], ['*****@*****.**']),
                (u'x-jabber', [], ['*****@*****.**']),
                (u'x-jabber', [], ['*****@*****.**']),
                (u'url', [], ['http://www.thinkgeek.com/geektoys/plush/8823/']),
                (u'nickname', [], [u'HR Ninja']),
                (u'nickname', [], [u'Enforcement Ninja'])])

    # We don't acknowledge the initial vCard get until we're sure that Gabble's
    # received our call to SetContactInfo. This ensures that it will issue a
    # set when we send the reply, rather than issuing another get to ensure the
    # vCard is really, absolutely up to date before editing it.
    sync_dbus(bus, q, conn)
    acknowledge_iq(stream, event.stanza)

    vcard_set_event = q.expect('stream-iq', iq_type='set',
                query_ns='vcard-temp', query_name='vCard')

    assertLength(2, xpath.queryForNodes('/iq/vCard/NICKNAME',
        vcard_set_event.stanza))
    nicknames = []
    for nickname in xpath.queryForNodes('/iq/vCard/NICKNAME',
            vcard_set_event.stanza):
        nicknames.append(str(nickname))
    assertEquals(['HR Ninja', 'Enforcement Ninja'], nicknames)

    assertEquals(None, xpath.queryForNodes('/iq/vCard/PHOTO',
        vcard_set_event.stanza))
    assertLength(1, xpath.queryForNodes('/iq/vCard/N',
        vcard_set_event.stanza))
    assertEquals('Wee', xpath.queryForString('/iq/vCard/N/GIVEN',
        vcard_set_event.stanza))
    assertEquals('Ninja', xpath.queryForString('/iq/vCard/N/FAMILY',
        vcard_set_event.stanza))
    assertEquals('-san', xpath.queryForString('/iq/vCard/N/SUFFIX',
        vcard_set_event.stanza))
    assertEquals('Wee Ninja', xpath.queryForString('/iq/vCard/FN',
        vcard_set_event.stanza))

    assertLength(1, xpath.queryForNodes('/iq/vCard/ORG',
        vcard_set_event.stanza))
    assertEquals('Collabora, Ltd.',
            xpath.queryForString('/iq/vCard/ORG/ORGNAME',
                vcard_set_event.stanza))
    assertEquals(None, xpath.queryForNodes('/iq/vCard/ORG/ORGUNIT',
                vcard_set_event.stanza))

    assertLength(1, xpath.queryForNodes('/iq/vCard/LABEL',
        vcard_set_event.stanza))
    lines = xpath.queryForNodes('/iq/vCard/LABEL/LINE', vcard_set_event.stanza)
    assertLength(5, lines)
    for i, exp_line in enumerate(['11 Kings Parade', 'Cambridge',
        'Cambridgeshire', 'CB2 1SJ', 'UK']):
        assertEquals(exp_line, str(lines[i]))

    assertLength(2, xpath.queryForNodes('/iq/vCard/TEL',
        vcard_set_event.stanza))
    for tel in xpath.queryForNodes('/iq/vCard/TEL', vcard_set_event.stanza):
        assertLength(1, xpath.queryForNodes('/TEL/NUMBER', tel))
        assertContains(xpath.queryForString('/TEL/NUMBER', tel),
                ('+44 1223 362967', '+44 7700 900753'))
        assertLength(1, xpath.queryForNodes('/TEL/VOICE', tel))
        assertLength(1, xpath.queryForNodes('/TEL/WORK', tel))

    assertLength(2, xpath.queryForNodes('/iq/vCard/EMAIL',
        vcard_set_event.stanza))
    for email in xpath.queryForNodes('/iq/vCard/EMAIL',
            vcard_set_event.stanza):
        assertContains(xpath.queryForString('/EMAIL/USERID', email),
                ('*****@*****.**', '*****@*****.**'))
        assertLength(1, xpath.queryForNodes('/EMAIL/INTERNET', email))
        if 'collabora' in xpath.queryForString('/EMAIL/USERID', email):
            assertLength(1, xpath.queryForNodes('/EMAIL/PREF', email))
        else:
            assertEquals(None, xpath.queryForNodes('/EMAIL/PREF', email))

    assertLength(2, xpath.queryForNodes('/iq/vCard/JABBERID',
        vcard_set_event.stanza))
    for jid in xpath.queryForNodes('/iq/vCard/JABBERID',
            vcard_set_event.stanza):
        assertContains(xpath.queryForString('/JABBERID', jid),
                ('*****@*****.**', '*****@*****.**'))

    acknowledge_iq(stream, vcard_set_event.stanza)
    q.expect_many(
            EventPattern('dbus-return', method='SetContactInfo'),
            EventPattern('dbus-signal', signal='AliasesChanged',
                predicate=lambda e: e.args[0][0][1] == 'HR Ninja'),
            EventPattern('dbus-signal', signal='ContactInfoChanged'),
            )

    # Exercise various invalid SetContactInfo operations
    sync_stream(q, stream)
    sync_dbus(bus, q, conn)

    forbidden = [EventPattern('stream-iq', query_ns='vcard-temp')]
    q.forbid_events(forbidden)

    # unknown field
    call_async(q, conn.ContactInfo, 'SetContactInfo',
            [('x-salary', [], ['547 espressos per month'])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # not enough values for a simple field
    call_async(q, conn.ContactInfo, 'SetContactInfo',
            [('fn', [], [])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # too many values for a simple field
    call_async(q, conn.ContactInfo, 'SetContactInfo',
            [('fn', [], ['Wee', 'Ninja'])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # unsupported type-parameter for a simple field
    call_async(q, conn.ContactInfo, 'SetContactInfo',
            [('fn', ['language=ja'], ['Wee Ninja'])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # unsupported type-parameter for a structured field
    call_async(q, conn.ContactInfo, 'SetContactInfo',
            [(u'n', ['language=ja'], [u'Ninja', u'Wee', u'', u'', u'-san'])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # unsupported type-parameter for LABEL
    call_async(q, conn.ContactInfo, 'SetContactInfo',
            [('label', ['language=en'], ['Collabora Ltd.\n11 Kings Parade'])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # not enough values for LABEL
    call_async(q, conn.ContactInfo, 'SetContactInfo',
            [('label', [], [])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # too many values for LABEL
    call_async(q, conn.ContactInfo, 'SetContactInfo',
            [('label', [], ['11 Kings Parade', 'Cambridge'])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # unsupported type-parameter for ORG
    call_async(q, conn.ContactInfo, 'SetContactInfo',
            [('org', ['language=en'], ['Collabora Ltd.'])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # empty ORG
    call_async(q, conn.ContactInfo, 'SetContactInfo',
            [('org', [], [])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # not enough values for N
    call_async(q, conn.ContactInfo, 'SetContactInfo',
            [('n', [], ['Ninja'])])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    # too many values for N
    call_async(q, conn.ContactInfo, 'SetContactInfo',
            [('n', [],
            'what could it mean if you have too many field values?'.split())])
    q.expect('dbus-error', method='SetContactInfo', name=cs.INVALID_ARGUMENT)

    q.unforbid_events(forbidden)

    # Following a reshuffle, Company Policy Enforcement is declared to be
    # a sub-department within Human Resources, and the ninja no longer
    # qualifies for a company phone

    vcard_in = [(u'fn', [], [u'Wee Ninja']),
                (u'n', [], [u'Ninja', u'Wee', u'', u'', u'-san']),
                (u'org', [], ['Collabora, Ltd.',
                    'Human Resources', 'Company Policy Enforcement']),
                (u'adr', ['type=work','type=postal','type=parcel'],
                    ['', '', '11 Kings Parade', 'Cambridge', 'Cambridgeshire',
                        'CB2 1SJ', 'UK']),
                (u'tel', ['type=voice','type=work'], ['+44 1223 362967']),
                (u'email', ['type=internet','type=pref'],
                    ['*****@*****.**']),
                (u'email', ['type=internet'], ['*****@*****.**']),
                (u'url', [], ['http://www.thinkgeek.com/geektoys/plush/8823/']),
                (u'nickname', [], [u'HR Ninja']),
                (u'nickname', [], [u'Enforcement Ninja'])]

    call_async(q, conn.ContactInfo, 'SetContactInfo', vcard_in)

    event = q.expect('stream-iq', iq_type='get', query_ns='vcard-temp',
        query_name='vCard')
    repeat_previous_vcard(stream, event.stanza, vcard_set_event.stanza)

    _, vcard_set_event = q.expect_many(
            EventPattern('dbus-signal', signal='ContactInfoChanged'),
            EventPattern('stream-iq', iq_type='set', query_ns='vcard-temp',
                query_name='vCard'),
            )

    assertLength(1, xpath.queryForNodes('/iq/vCard/ORG',
        vcard_set_event.stanza))
    assertEquals('Collabora, Ltd.',
            xpath.queryForString('/iq/vCard/ORG/ORGNAME',
                vcard_set_event.stanza))
    units = xpath.queryForNodes('/iq/vCard/ORG/ORGUNIT',
            vcard_set_event.stanza)
    assertLength(2, units)
    for i, exp_unit in enumerate(['Human Resources',
            'Company Policy Enforcement']):
        assertEquals(exp_unit, str(units[i]))

    assertLength(1, xpath.queryForNodes('/iq/vCard/TEL',
        vcard_set_event.stanza))
    for tel in xpath.queryForNodes('/iq/vCard/TEL', vcard_set_event.stanza):
        assertLength(1, xpath.queryForNodes('/TEL/NUMBER', tel))
        assertEquals('+44 1223 362967',
                xpath.queryForString('/TEL/NUMBER', tel))
        assertLength(1, xpath.queryForNodes('/TEL/VOICE', tel))
        assertLength(1, xpath.queryForNodes('/TEL/WORK', tel))

    acknowledge_iq(stream, vcard_set_event.stanza)
    _, event = q.expect_many(
            EventPattern('dbus-return', method='SetContactInfo'),
            EventPattern('dbus-signal', signal='ContactInfoChanged'),
            )

    vcard_out = event.args[1][:]

    # the only change we expect to see is that perhaps the fields are
    # re-ordered, and perhaps the types on the 'tel' are re-ordered

    assertEquals(vcard_in[4][0], 'tel')
    vcard_in[4][1].sort()
    assertEquals(vcard_out[4][0], 'tel')
    vcard_out[4][1].sort()
    assertEquals(vcard_in, vcard_out)

    # Finally, the ninja decides that publishing his contact details is not
    # very ninja-like, and decides to be anonymous. The first (most important)
    # of his nicknames from the old vCard is kept, due to nickname's dual role
    # as ContactInfo and the alias.
    call_async(q, conn.ContactInfo, 'SetContactInfo', [])

    event = q.expect('stream-iq', iq_type='get', query_ns='vcard-temp',
        query_name='vCard')
    repeat_previous_vcard(stream, event.stanza, vcard_set_event.stanza)

    vcard_set_event = q.expect('stream-iq', iq_type='set',
            query_ns='vcard-temp', query_name='vCard')
    assertLength(1, xpath.queryForNodes('/iq/vCard/*',
        vcard_set_event.stanza))
    assertEquals('HR Ninja', xpath.queryForString('/iq/vCard/NICKNAME',
        vcard_set_event.stanza))

    acknowledge_iq(stream, vcard_set_event.stanza)
    q.expect_many(
            EventPattern('dbus-return', method='SetContactInfo'),
            EventPattern('dbus-signal', signal='ContactInfoChanged'),
            )
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', b'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(b'hello').decode()

    # 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)