Ejemplo n.º 1
0
def test(q, bus, conn, stream):
    # Gabble asks for the roster; the server sends back an empty roster.
    event = q.expect('stream-iq', query_ns=ns.ROSTER)
    acknowledge_iq(stream, event.stanza)

    pairs = expect_contact_list_signals(q, bus, conn,
            ['stored'])
    stored = check_contact_list_signals(q, bus, conn, pairs.pop(0),
            cs.HT_LIST, 'stored', [])

    # The server sends us a roster push without an id=''. WTF!
    iq = make_roster_push(stream, jid, 'both')
    del iq['id']
    stream.send(iq)

    h = conn.RequestHandles(cs.HT_CONTACT, [jid])[0]
    q.expect_many(
        EventPattern('dbus-signal', signal='MembersChanged',
            args=['', [h], [], [], [], 0, 0], path=stored.object_path),
        EventPattern('dbus-signal', signal='ContactsChanged',
            args=[{ h: (cs.SUBSCRIPTION_STATE_YES,
                    cs.SUBSCRIPTION_STATE_YES, ''), },
                []],
            ),
        )

    # Verify that Gabble didn't crash while trying to ack the push.
    sync_stream(q, stream)

    # Just for completeness, let's repeat this test with a malicious roster
    # push from a contact (rather than from our server). Our server's *really*
    # broken if it allows this. Nonetheless...
    iq = make_roster_push(stream, '*****@*****.**', 'both')
    del iq['id']
    iq['from'] = '*****@*****.**'
    stream.send(iq)

    q.forbid_events(
        [ EventPattern('dbus-signal', signal='MembersChanged',
              path=stored.object_path),
          EventPattern('dbus-signal', signal='ContactsChanged'),
        ])
    # Make sure Gabble's got the evil push...
    sync_stream(q, stream)
    # ...and make sure it's not emitted anything.
    sync_dbus(bus, q, conn)
Ejemplo n.º 2
0
def test(q, bus, conn, stream):
    # Gabble asks for the roster; the server sends back an empty roster.
    event = q.expect('stream-iq', query_ns=ns.ROSTER)
    acknowledge_iq(stream, event.stanza)

    pairs = expect_contact_list_signals(q, bus, conn,
            ['stored'])
    stored = check_contact_list_signals(q, bus, conn, pairs.pop(0),
            cs.HT_LIST, 'stored', [])

    # Some malicious peer sends us a roster push to try to trick us into
    # showing them on our roster. Gabble should know better than to trust it.
    iq = make_roster_push(stream, jid, 'both')
    iq['from'] = jid
    stream.send(iq)

    q.forbid_events(
        [ EventPattern('dbus-signal', signal='MembersChanged',
              path=stored.object_path),
          EventPattern('dbus-signal', signal='ContactsChanged'),
        ])

    e = q.expect('stream-iq', iq_type='error')
Ejemplo n.º 3
0
def test(q, bus, conn, stream):

    call_async(q, conn.ContactList, 'GetContactListAttributes', [], False)
    q.expect('dbus-error', method='GetContactListAttributes',
            name=cs.NOT_YET)

    event = q.expect('stream-iq', query_ns=ns.ROSTER)

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

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

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

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

    stream.send(event.stanza)

    # slight implementation detail: TpBaseContactList emits ContactsChanged
    # before it announces its channels
    s = q.expect('dbus-signal', signal='ContactsChanged',
            interface=cs.CONN_IFACE_CONTACT_LIST, path=conn.object_path)

    amy, bob, che = conn.RequestHandles(cs.HT_CONTACT,
            ['*****@*****.**', '*****@*****.**', '*****@*****.**'])

    assertEquals([{
        amy: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_YES, ''),
        bob: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_YES, ''),
        che: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_NO, ''),
        }, []], s.args)

    pairs = expect_contact_list_signals(q, bus, conn,
            ['publish', 'subscribe', 'stored'])

    # this is emitted last, so clients can tell when the initial state dump
    # has finished
    q.expect('dbus-signal', signal='ContactListStateChanged',
            args=[cs.CONTACT_LIST_STATE_SUCCESS])

    call_async(q, conn.ContactList, 'GetContactListAttributes', [], False)
    r = q.expect('dbus-return', method='GetContactListAttributes')
    assertEquals(({
        amy: {
            cs.CONN_IFACE_CONTACT_LIST + '/subscribe':
                cs.SUBSCRIPTION_STATE_YES,
            cs.CONN_IFACE_CONTACT_LIST + '/publish': cs.SUBSCRIPTION_STATE_YES,
            cs.CONN + '/contact-id': '*****@*****.**',
            },
        bob: {
            cs.CONN_IFACE_CONTACT_LIST + '/subscribe':
                cs.SUBSCRIPTION_STATE_NO,
            cs.CONN_IFACE_CONTACT_LIST + '/publish': cs.SUBSCRIPTION_STATE_YES,
            cs.CONN + '/contact-id': '*****@*****.**',
            },
        che: {
            cs.CONN_IFACE_CONTACT_LIST + '/subscribe':
                cs.SUBSCRIPTION_STATE_YES,
            cs.CONN_IFACE_CONTACT_LIST + '/publish': cs.SUBSCRIPTION_STATE_NO,
            cs.CONN + '/contact-id': '*****@*****.**',
            },
        },), r.value)

    check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST,
            'publish', ['*****@*****.**', '*****@*****.**'])
    check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST,
            'subscribe', ['*****@*****.**', '*****@*****.**'])
    check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST,
            'stored', ['*****@*****.**', '*****@*****.**', '*****@*****.**'])

    assertLength(0, pairs)      # i.e. we've checked all of them
Ejemplo n.º 4
0
def test(q, bus, conns, streams):

    conn1, conn2 = conns
    stream1, stream2 = streams

    # Connection 1
    conn1.Connect()
    q.expect('dbus-signal', signal='StatusChanged',
             args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED],
             path=conn1.object.object_path)
    q.expect('stream-authenticated')
    q.expect('dbus-signal', signal='PresencesChanged',
             args=[{1L: (cs.PRESENCE_AVAILABLE, 'available', '')}],
             path=conn1.object.object_path)
    q.expect('dbus-signal', signal='StatusChanged',
             args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED],
             path=conn1.object.object_path)

    pairs = expect_contact_list_signals(q, bus, conn1,
            ['publish', 'subscribe', 'stored'])

    check_contact_list_signals(q, bus, conn1, pairs.pop(0), cs.HT_LIST,
            'publish', [])
    check_contact_list_signals(q, bus, conn1, pairs.pop(0), cs.HT_LIST,
            'subscribe', [])
    check_contact_list_signals(q, bus, conn1, pairs.pop(0), cs.HT_LIST,
            'stored', [])
    assertLength(0, pairs)      # i.e. we popped and checked all of them

    # Connection 2
    conn2.Connect()
    q.expect('dbus-signal', signal='StatusChanged',
             args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED],
             path=conn2.object.object_path)
    q.expect('stream-authenticated')
    q.expect('dbus-signal', signal='PresencesChanged',
             args=[{1L: (cs.PRESENCE_AVAILABLE, 'available', '')}],
             path=conn2.object.object_path)
    q.expect('dbus-signal', signal='StatusChanged',
             args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED],
             path=conn2.object.object_path)

    pairs = expect_contact_list_signals(q, bus, conn2,
            ['publish', 'subscribe', 'stored'])

    check_contact_list_signals(q, bus, conn2, pairs.pop(0), cs.HT_LIST,
            'publish', [])
    check_contact_list_signals(q, bus, conn2, pairs.pop(0), cs.HT_LIST,
            'subscribe', [])
    check_contact_list_signals(q, bus, conn2, pairs.pop(0), cs.HT_LIST,
            'stored', [])
    assertLength(0, pairs)      # i.e. we popped and checked all of them
Ejemplo n.º 5
0
def test(q, bus, conn, stream):
    event = q.expect('stream-iq', query_ns=ns.ROSTER)
    event.stanza['type'] = 'result'

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

    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'from'
    item.addElement('group', content='men')

    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'to'
    item.addElement('group', content='men')

    stream.send(event.stanza)

    # slight implementation detail: TpBaseContactList emits ContactsChanged
    # etc. before it announces its channels, and it emits one CGC per group.
    s1, s2 = q.expect_many(
        EventPattern('dbus-signal', signal='GroupsChanged',
            interface=cs.CONN_IFACE_CONTACT_GROUPS, path=conn.object_path,
            predicate=lambda e: 'women' in e.args[1]),
        EventPattern('dbus-signal', signal='GroupsChanged',
            interface=cs.CONN_IFACE_CONTACT_GROUPS, path=conn.object_path,
            predicate=lambda e: 'men' in e.args[1]),
        )

    amy, bob, che = conn.RequestHandles(cs.HT_CONTACT,
            ['*****@*****.**', '*****@*****.**', '*****@*****.**'])

    assertEquals([[amy], ['women'], []], s1.args)
    assertEquals([[bob, che], ['men'], []], s2.args)

    pairs = expect_contact_list_signals(q, bus, conn, [], ['men', 'women'])

    q.expect('dbus-signal', signal='ContactListStateChanged',
            args=[cs.CONTACT_LIST_STATE_SUCCESS])

    check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_GROUP,
            'men', ['*****@*****.**', '*****@*****.**'])
    check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_GROUP,
            'women', ['*****@*****.**'])

    assertLength(0, pairs)      # i.e. we've checked all of them

    # change Amy's groups
    call_async(q, conn.ContactGroups, 'SetContactGroups', amy,
            ['ladies', 'people starting with A'])

    s, iq = q.expect_many(
        EventPattern('dbus-signal', signal='GroupsCreated'),
        EventPattern('stream-iq', iq_type='set',
            query_name='query', query_ns=ns.ROSTER),
        )

    assertEquals(set(('ladies', 'people starting with A')), set(s.args[0]))

    jid, groups = parse_roster_change_request(iq.query, iq.stanza)
    assertEquals('*****@*****.**', jid)
    assertEquals(set(('ladies', 'people starting with A')), groups)

    acknowledge_iq(stream, iq.stanza)
    q.expect('dbus-return', method='SetContactGroups')

    # Now the server sends us a roster push.
    send_roster_push(stream, '*****@*****.**', ['people starting with A', 'ladies'])

    # We get a single signal corresponding to that roster push
    e = q.expect('dbus-signal', signal='GroupsChanged',
            predicate=lambda e: e.args[0] == [amy])
    assertEquals(set(['ladies', 'people starting with A']), set(e.args[1]))
    assertEquals(['women'], e.args[2])

    # check that Amy's state is what we expected
    attrs = conn.Contacts.GetContactAttributes([amy],
            [cs.CONN_IFACE_CONTACT_GROUPS], False)[amy]
    # make the group list order-independent
    attrs[cs.CONN_IFACE_CONTACT_GROUPS + '/groups'] = \
        set(attrs[cs.CONN_IFACE_CONTACT_GROUPS + '/groups'])

    assertEquals({ cs.CONN_IFACE_CONTACT_GROUPS + '/groups':
                set(['ladies', 'people starting with A']),
            cs.CONN + '/contact-id': '*****@*****.**' }, attrs)

    for it_worked in (False, True):
        # remove a group with a member (the old API couldn't do this)
        call_async(q, conn.ContactGroups, 'RemoveGroup',
                'people starting with A')

        iq = q.expect('stream-iq', iq_type='set',
                query_name='query', query_ns=ns.ROSTER)

        jid, groups = parse_roster_change_request(iq.query, iq.stanza)
        assertEquals('*****@*****.**', jid)
        assertEquals(set(('ladies',)), groups)

        acknowledge_iq(stream, iq.stanza)

        # we emit these as soon as the IQ is ack'd, so that we can indicate
        # group removal...
        q.expect('dbus-signal', signal='GroupsRemoved',
                args=[['people starting with A']])
        q.expect('dbus-signal', signal='GroupsChanged',
                args=[[amy], [], ['people starting with A']])

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

        if it_worked:
            # ... although in fact this is what *actually* removes Amy from the
            # group
            send_roster_push(stream, '*****@*****.**', ['ladies'])
        else:
            # if the change didn't "stick", this message will revert it
            send_roster_push(stream, '*****@*****.**', ['ladies', 'people starting with A'])

            q.expect('dbus-signal', signal='GroupsCreated',
                    args=[['people starting with A']])
            q.expect('dbus-signal', signal='GroupsChanged',
                    args=[[amy], ['people starting with A'], []])

            sync_dbus(bus, q, conn)
            sync_stream(q, stream)
            assertEquals({
                    cs.CONN_IFACE_CONTACT_GROUPS + '/groups':
                        ['ladies', 'people starting with A'],
                    cs.CONN + '/contact-id':
                        '*****@*****.**' },
                conn.Contacts.GetContactAttributes([amy],
                    [cs.CONN_IFACE_CONTACT_GROUPS], False)[amy])

    # sanity check: after all that, we expect Amy to be in group 'ladies' only
    sync_dbus(bus, q, conn)
    sync_stream(q, stream)
    assertEquals({ cs.CONN_IFACE_CONTACT_GROUPS + '/groups': ['ladies'],
            cs.CONN + '/contact-id': '*****@*****.**' },
        conn.Contacts.GetContactAttributes([amy],
            [cs.CONN_IFACE_CONTACT_GROUPS], False)[amy])

    # Rename group 'ladies' to 'girls'
    call_async(q, conn.ContactGroups, 'RenameGroup', 'ladies', 'girls')

    # Amy is added to 'girls'
    e = q.expect('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER)
    jid, groups = parse_roster_change_request(e.query, e.stanza)
    assertEquals('*****@*****.**', jid)
    assertEquals(set(['girls', 'ladies']), groups)

    send_roster_push(stream, '*****@*****.**', ['girls', 'ladies'])
    acknowledge_iq(stream, e.stanza)

    # Amy is removed from 'ladies'
    e = q.expect('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER)
    jid, groups = parse_roster_change_request(e.query, e.stanza)
    assertEquals('*****@*****.**', jid)
    assertEquals(set(['girls']), groups)

    send_roster_push(stream, '*****@*****.**', ['girls'])
    acknowledge_iq(stream, e.stanza)

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

    # check everything has been updated
    groups = conn.Properties.Get(cs.CONN_IFACE_CONTACT_GROUPS, 'Groups')
    assertContains('girls', groups)
    assertDoesNotContain('ladies', groups)

    contacts = conn.ContactList.GetContactListAttributes([cs.CONN_IFACE_CONTACT_GROUPS], False)
    assertEquals(['girls'], contacts[amy][cs.CONN_IFACE_CONTACT_GROUPS + '/groups'])
def test(q, bus, conn, stream, remove, local, modern):
    # Gabble asks for the roster; the server sends back an empty roster.
    event = q.expect("stream-iq", query_ns=ns.ROSTER)
    event.stanza["type"] = "result"
    stream.send(event.stanza)

    pairs = expect_contact_list_signals(q, bus, conn, ["publish", "subscribe", "stored"])

    check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, "publish", [])
    subscribe = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, "subscribe", [])
    stored = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST, "stored", [])

    assertLength(0, pairs)  # i.e. we've checked all of them

    self_handle = conn.GetSelfHandle()
    h = conn.RequestHandles(cs.HT_CONTACT, [jid])[0]

    # Another client logged into our account (Gajim, say) wants to subscribe to
    # Marco's presence. First, per RFC 3921 it 'SHOULD perform a "roster set"
    # for the new roster item':
    #
    #   <iq type='set'>
    #     <query xmlns='jabber:iq:roster'>
    #       <item jid='*****@*****.**'/>
    #     </query>
    #   </iq>
    #
    # 'As a result, the user's server (1) MUST initiate a roster push for the
    # new roster item to all available resources associated with this user that
    # have requested the roster, setting the 'subscription' attribute to a
    # value of "none"':
    iq = IQ(stream, "set")
    item = iq.addElement((ns.ROSTER, "query")).addElement("item")
    item["jid"] = jid
    item["subscription"] = "none"
    stream.send(iq)

    # In response, Gabble should add Marco to stored:
    q.expect_many(
        EventPattern("dbus-signal", signal="MembersChanged", args=["", [h], [], [], [], 0, 0], path=stored.object_path),
        EventPattern(
            "dbus-signal",
            signal="ContactsChanged",
            args=[{h: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_NO, "")}, []],
        ),
    )

    # Gajim sends a <presence type='subscribe'/> to Marco. 'As a result, the
    # user's server MUST initiate a second roster push to all of the user's
    # available resources that have requested the roster, setting [...]
    # ask='subscribe' attribute in the roster item [for Marco]:
    iq = IQ(stream, "set")
    item = iq.addElement((ns.ROSTER, "query")).addElement("item")
    item["jid"] = jid
    item["subscription"] = "none"
    item["ask"] = "subscribe"
    stream.send(iq)

    # In response, Gabble should add Marco to subscribe:remote-pending:
    q.expect_many(
        EventPattern(
            "dbus-signal",
            signal="MembersChanged",
            args=["", [], [], [], [h], self_handle, 0],
            path=subscribe.object_path,
        ),
        EventPattern(
            "dbus-signal",
            signal="ContactsChanged",
            args=[{h: (cs.SUBSCRIPTION_STATE_ASK, cs.SUBSCRIPTION_STATE_NO, "")}, []],
        ),
    )

    # The user decides that they don't care what Marco's baking after all
    # (maybe they read his blog instead?) and:
    if remove:
        # ...removes him from the roster...
        if local:
            # ...by telling Gabble to remove him from stored.
            if modern:
                call_async(q, conn.ContactList, "RemoveContacts", [h])
            else:
                call_async(q, stored.Group, "RemoveMembers", [h], "")

            event = q.expect("stream-iq", iq_type="set", query_ns=ns.ROSTER)
            item = event.query.firstChildElement()
            assertEquals(jid, item["jid"])
            assertEquals("remove", item["subscription"])
        else:
            # ...using the other client.
            pass

        # The server must 'inform all of the user's available resources that
        # have requested the roster of the roster item removal':
        iq = IQ(stream, "set")
        item = iq.addElement((ns.ROSTER, "query")).addElement("item")
        item["jid"] = jid
        item["subscription"] = "remove"
        # When Marco found this bug, this roster update included:
        item["ask"] = "subscribe"
        # which is a bit weird: I don't think the server should send that when
        # the contact's being removed. I think Gabble should ignore it, so I'm
        # including it in the test.
        stream.send(iq)

        # In response, Gabble should announce that Marco has been removed from
        # subscribe:remote-pending and stored:members:
        q.expect_many(
            EventPattern(
                "dbus-signal", signal="MembersChanged", args=["", [], [h], [], [], 0, 0], path=subscribe.object_path
            ),
            EventPattern(
                "dbus-signal", signal="MembersChanged", args=["", [], [h], [], [], 0, 0], path=stored.object_path
            ),
            EventPattern("dbus-signal", signal="ContactsChanged", args=[{}, [h]]),
        )

        if local and modern:
            acknowledge_iq(stream, event.stanza)
            q.expect("dbus-return", method="RemoveContacts")
            # FIXME: when we depend on a new enough tp-glib we can expect
            # RemoveMembers to return here in the local case, too
    else:
        # ...rescinds the subscription request...
        if local:
            # ...by telling Gabble to remove him from 'subscribe'.
            if modern:
                call_async(q, conn.ContactList, "Unsubscribe", [h])
            else:
                subscribe.Group.RemoveMembers([h], "")

            events = [EventPattern("stream-presence", to=jid, presence_type="unsubscribe")]

            if modern:
                events.append(EventPattern("dbus-return", method="Unsubscribe"))

            event = q.expect_many(*events)[0]
        else:
            # ...in the other client.
            pass

        # In response, the server sends a roster update:
        iq = IQ(stream, "set")
        item = iq.addElement((ns.ROSTER, "query")).addElement("item")
        item["jid"] = jid
        item["subscription"] = "none"
        # no ask='subscribe' any more.
        stream.send(iq)

        # In response, Gabble should announce that Marco has been removed from
        # subscribe:remote-pending. It shouldn't wait for the <presence
        # type='unsubscribed'/> ack before doing so: empirical tests reveal
        # that it's never delivered.
        q.expect_many(
            EventPattern(
                "dbus-signal", signal="MembersChanged", args=["", [], [h], [], [], 0, 0], path=subscribe.object_path
            ),
            EventPattern(
                "dbus-signal",
                signal="ContactsChanged",
                args=[{h: (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_NO, "")}, []],
            ),
        )
Ejemplo n.º 7
0
def test(q, bus, conn, stream):
    event = q.expect('stream-iq', query_ns=ns.ROSTER)
    event.stanza['type'] = 'result'

    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'both'
    item.addElement('group', content='women')
    item.addElement('group', content='affected-by-fdo-12791')

    # This is a broken roster - Amy appears twice. This should only happen
    # if the server is somehow buggy. This was my initial attempt at
    # reproducing fd.o #12791 - I doubt it's very realistic, but we shouldn't
    # assert, regardless of what input we get!
    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'both'
    item.addElement('group', content='women')

    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'from'
    item.addElement('group', content='men')

    # This is what was *actually* strange about the #12791 submitter's roster -
    # Bob appears, fully subscribed, but also there's an attempt to subscribe
    # to one of Bob's resources. We now ignore such items
    item = event.query.addElement('item')
    item['jid'] = '[email protected]/Resource'
    item['subscription'] = 'none'
    item['ask'] = 'subscribe'

    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'to'
    item.addElement('group', content='men')

    stream.send(event.stanza)

    pairs = expect_contact_list_signals(q, bus, conn,
            ['publish', 'subscribe', 'stored'],
            ['men', 'women', 'affected-by-fdo-12791'])

    check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST,
            'publish', ['*****@*****.**', '*****@*****.**'])
    check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST,
            'subscribe', ['*****@*****.**', '*****@*****.**'])
    check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST,
            'stored', ['*****@*****.**', '*****@*****.**', '*****@*****.**'])
    check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_GROUP,
            'men', ['*****@*****.**', '*****@*****.**'])
    check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_GROUP,
            'women', ['*****@*****.**'])
    check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_GROUP,
            'affected-by-fdo-12791', [])

    assertLength(0, pairs)      # i.e. we've checked all of them
def test(q, bus, conn, stream, modern=True, queued=False):
    event = q.expect('stream-iq', query_ns=ns.ROSTER)
    event.stanza['type'] = 'result'

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

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

    stream.send(event.stanza)

    # slight implementation detail: TpBaseContactList emits ContactsChanged
    # before it announces its channels
    q.expect('dbus-signal', signal='ContactsChanged',
            interface=cs.CONN_IFACE_CONTACT_LIST, path=conn.object_path,
            args=[{quux_handle:
                (cs.SUBSCRIPTION_STATE_NO, cs.SUBSCRIPTION_STATE_NO, '')}, []])

    pairs = expect_contact_list_signals(q, bus, conn,
            ['publish', 'subscribe', 'stored'])

    check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST,
            'publish', [])
    check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST,
            'subscribe', [])
    stored = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST,
            'stored', ['*****@*****.**'])

    assertLength(0, pairs)      # i.e. we've checked all of them

    if queued:
        conn.Aliasing.SetAliases({quux_handle: 'Quux'})
        set_aliases = q.expect('stream-iq', query_ns=ns.ROSTER)
        item = set_aliases.query.firstChildElement()
        assertEquals('*****@*****.**', item['jid'])
        assertEquals('Quux', item['name'])

    expectations = [
            EventPattern('stream-iq',
            iq_type='set', query_ns=ns.ROSTER),
            ]

    if modern:
        call_async(q, conn.ContactList, 'RemoveContacts', [quux_handle])
    else:
        call_async(q, stored.Group, 'RemoveMembers', [quux_handle], '')

    if queued:
        # finish off the previous thing we were doing, so removal can proceed
        acknowledge_iq(stream, set_aliases.stanza)

    event = q.expect_many(*expectations)[0]
    item = event.query.firstChildElement()
    assertEquals('*****@*****.**', item['jid'])
    assertEquals('remove', item['subscription'])

    send_roster_push(stream, '*****@*****.**', 'remove')

    q.expect_many(
            EventPattern('dbus-signal', interface=cs.CHANNEL_IFACE_GROUP,
                path=stored.object_path, signal='MembersChanged',
                args=['', [], [quux_handle], [], [], 0, 0]),
            EventPattern('dbus-signal', interface=cs.CONN_IFACE_CONTACT_LIST,
                path=conn.object_path, signal='ContactsChanged',
                args=[{}, [quux_handle]]),
            EventPattern('stream-iq', iq_id='push', iq_type='result'),
            )

    acknowledge_iq(stream, event.stanza)

    if modern:
        q.expect('dbus-return', method='RemoveContacts')
Ejemplo n.º 9
0
def test_inital_roster(q, bus, conn, stream):
    """
    This part of the test checks that Gabble correctly alters on which lists
    contacts appear based on the google:roster attributes and special-cases.
    """

    event = q.expect('stream-iq', query_ns=ns.ROSTER)
    query = event.query
    assertContains('gr', query.localPrefixes)
    assertEquals(ns.GOOGLE_ROSTER, query.localPrefixes['gr'])
    # We support version 2 of Google's extensions.
    assertEquals('2', query[(ns.GOOGLE_ROSTER, 'ext')])

    result = make_result_iq(stream, event.stanza)
    query = result.firstChildElement()
    add_gr_attributes(query)

    # Gabble suppresses contacts labelled as "hidden" from all roster channels.
    add_roster_item(query, '*****@*****.**', 'both', False,
        {'gr:t': 'H'})
    # Gabble should hide contacts on the Google roster with subscription="none"
    # and ask!="subscribe", to hide contacts which are actually just email
    # addresses. (This is in line with Pidgin; the code there was added by Sean
    # Egan, who worked on Google Talk for Google at the time.)
    add_roster_item(query, '*****@*****.**', 'none',
        False)
    # This contact is remote pending, so we shouldn't suppress it.
    add_roster_item(query, '*****@*****.**', 'none', True)
    add_roster_item(query, '*****@*****.**', 'both', False,
        {'gr:autosub': 'true'})
    # These contacts are blocked but we're subscribed to them, so they should
    # show up in all of the lists.
    add_roster_item(query, '*****@*****.**', 'both', False,
        {'gr:t': 'B'})
    add_roster_item(query, '*****@*****.**', 'both', False,
        {'gr:t': 'B'})
    # This contact is blocked, and we have no other subscription to them; so,
    # they should not show up in 'stored'.
    add_roster_item(query, '*****@*****.**', 'none', False,
        {'gr:t': 'B'})

    # Send back the roster
    stream.send(result)

    # Since s-b-h had the "hidden" flag set, we don't expect them to be on any
    # lists. But we do want the "autosub" contact to be visible; see
    # <https://bugs.launchpad.net/ubuntu/+source/telepathy-gabble/+bug/398293>,
    # where Gabble was incorrectly hiding valid contacts.

    mutually_subscribed_contacts = ['*****@*****.**',
        '*****@*****.**',
        '*****@*****.**']
    rp_contacts = ['*****@*****.**']
    blocked_contacts = ['*****@*****.**',
        '*****@*****.**',
        '*****@*****.**']

    pairs = expect_contact_list_signals(q, bus, conn,
            ['publish', 'subscribe', 'stored', 'deny'])

    publish = check_contact_list_signals(q, bus, conn, pairs.pop(0),
            cs.HT_LIST, 'publish', mutually_subscribed_contacts)
    subscribe = check_contact_list_signals(q, bus, conn, pairs.pop(0),
            cs.HT_LIST, 'subscribe', mutually_subscribed_contacts,
            rp_contacts=rp_contacts)
    stored = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST,
            'stored', mutually_subscribed_contacts + rp_contacts)
    deny = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST,
            'deny', blocked_contacts)

    assertLength(0, pairs)      # i.e. we've checked all of them

    return (publish, subscribe, stored, deny)
Ejemplo n.º 10
0
def test(q, bus, conn, stream, modern=True, remove=False):

    call_async(q, conn.ContactList, 'GetContactListAttributes', [], False)
    q.expect('dbus-error', method='GetContactListAttributes',
            name=cs.NOT_YET)

    event = q.expect('stream-iq', query_ns=ns.ROSTER)

    item = event.query.addElement('item')
    item['jid'] = '*****@*****.**'
    item['subscription'] = 'both'
    event.stanza['type'] = 'result'
    stream.send(event.stanza)

    holly, dave, arnold, kristine, cat = conn.RequestHandles(cs.HT_CONTACT,
            ['*****@*****.**', '*****@*****.**', '*****@*****.**',
                '*****@*****.**', '*****@*****.**'])

    # slight implementation detail: TpBaseContactList emits ContactsChanged
    # before it announces its channels
    s = q.expect('dbus-signal', signal='ContactsChanged',
            interface=cs.CONN_IFACE_CONTACT_LIST, path=conn.object_path)
    assertEquals([{
        holly: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_YES, ''),
        }, []], s.args)

    pairs = expect_contact_list_signals(q, bus, conn,
            ['publish', 'subscribe', 'stored'])

    # this is emitted last, so clients can tell when the initial state dump
    # has finished
    q.expect('dbus-signal', signal='ContactListStateChanged',
            args=[cs.CONTACT_LIST_STATE_SUCCESS])

    call_async(q, conn.ContactList, 'GetContactListAttributes', [], False)
    r = q.expect('dbus-return', method='GetContactListAttributes')
    assertEquals(({
        holly: {
            cs.CONN_IFACE_CONTACT_LIST + '/publish':
                cs.SUBSCRIPTION_STATE_YES,
            cs.CONN_IFACE_CONTACT_LIST + '/subscribe':
                cs.SUBSCRIPTION_STATE_YES,
            cs.CONN + '/contact-id': '*****@*****.**',
            }
        },), r.value)

    # check that the channels were as we expected too
    publish = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST,
            'publish', ['*****@*****.**'])
    check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST,
            'subscribe', ['*****@*****.**'])
    stored = check_contact_list_signals(q, bus, conn, pairs.pop(0), cs.HT_LIST,
            'stored', ['*****@*****.**'])
    assertLength(0, pairs)      # i.e. we've checked all of them

    # publication authorized for Dave, Holly (the former is pre-authorization,
    # the latter is a no-op)
    if modern:
        call_async(q, conn.ContactList, 'AuthorizePublication', [dave, holly])
        event = q.expect('dbus-return', method='AuthorizePublication')
    else:
        call_async(q, publish.Group, 'AddMembers', [dave, holly], '')
        event = q.expect('dbus-return', method='AddMembers')

    # Receive authorization requests from the contacts

    # We pre-authorized Dave, so this is automatically approved
    presence = domish.Element(('jabber:client', 'presence'))
    presence['type'] = 'subscribe'
    presence['from'] = '*****@*****.**'
    stream.send(presence)

    q.expect_many(
            EventPattern('dbus-signal', signal='ContactsChanged',
                args=[{dave: (cs.SUBSCRIPTION_STATE_NO,
                    cs.SUBSCRIPTION_STATE_ASK,
                    '')}, []]),
            EventPattern('stream-presence', presence_type='subscribed',
                to='*****@*****.**'),
            )

    # Our server responds to Dave being authorized
    send_roster_push(stream, '*****@*****.**', 'from')
    q.expect_many(
            EventPattern('stream-iq', iq_type='result', iq_id='push'),
            EventPattern('dbus-signal', signal='ContactsChanged',
                args=[{dave: (cs.SUBSCRIPTION_STATE_NO,
                    cs.SUBSCRIPTION_STATE_YES, '')}, []]),
            )

    # The request from Kristine needs authorization (below)
    presence['from'] = '*****@*****.**'
    stream.send(presence)

    q.expect('dbus-signal', signal='ContactsChanged',
            args=[{kristine: (cs.SUBSCRIPTION_STATE_NO,
                cs.SUBSCRIPTION_STATE_ASK, '')}, []])

    # This request from Arnold is dealt with below
    presence['from'] = '*****@*****.**'
    stream.send(presence)

    q.expect('dbus-signal', signal='ContactsChanged',
            args=[{arnold: (cs.SUBSCRIPTION_STATE_NO,
                cs.SUBSCRIPTION_STATE_ASK, '')}, []])

    if modern:
        returning_method = 'AuthorizePublication'
        call_async(q, conn.ContactList, 'AuthorizePublication',
                [kristine, holly])
    else:
        returning_method = 'AddMembers'
        call_async(q, publish.Group, 'AddMembers', [kristine, holly], '')

    q.expect_many(
            EventPattern('dbus-return', method=returning_method),
            EventPattern('stream-presence', presence_type='subscribed',
                to='*****@*****.**'),
            )

    # Our server acknowledges that we authorized Kristine. Holly's state
    # does not change.
    send_roster_push(stream, '*****@*****.**', 'from')
    q.expect_many(
            EventPattern('dbus-signal', signal='ContactsChanged',
                args=[{kristine: (cs.SUBSCRIPTION_STATE_NO,
                    cs.SUBSCRIPTION_STATE_YES,
                    '')}, []]),
            EventPattern('stream-iq', iq_type='result', iq_id='push'),
            )

    # Arnold gives up waiting for us, and cancels his request
    presence['from'] = '*****@*****.**'
    presence['type'] = 'unsubscribe'
    stream.send(presence)

    q.expect_many(
            EventPattern('dbus-signal', signal='ContactsChanged',
                args=[{arnold: (cs.SUBSCRIPTION_STATE_NO,
                    cs.SUBSCRIPTION_STATE_REMOVED_REMOTELY, '')}, []]),
            EventPattern('stream-presence', presence_type='unsubscribed',
                to='*****@*****.**'),
            )

    # We can acknowledge that with RemoveContacts or with Unpublish.
    # The old Chan.T.ContactList API can't acknowledge RemovedRemotely,
    # because it sees it as "not there at all" and the group logic drops
    # the "redundant" request.

    if remove:
        returning_method = 'RemoveContacts'
        call_async(q, conn.ContactList, 'RemoveContacts', [arnold])
    else:
        returning_method = 'Unpublish'
        call_async(q, conn.ContactList, 'Unpublish', [arnold])

    # Even if we Unpublish() here, Arnold was never on our XMPP roster,
    # so setting his publish state to SUBSCRIPTION_STATE_NO should result
    # in his removal.
    q.expect_many(
            EventPattern('dbus-return', method=returning_method),
            EventPattern('dbus-signal', signal='ContactsChanged',
                args=[{}, [arnold]]),
            )

    # Rejecting an authorization request also works
    presence = domish.Element(('jabber:client', 'presence'))
    presence['type'] = 'subscribe'
    presence['from'] = '*****@*****.**'
    stream.send(presence)

    q.expect('dbus-signal', signal='ContactsChanged',
            args=[{cat: (cs.SUBSCRIPTION_STATE_NO,
                cs.SUBSCRIPTION_STATE_ASK,
                '')}, []])

    if modern:
        if remove:
            returning_method = 'RemoveContacts'
            call_async(q, conn.ContactList, 'RemoveContacts', [cat])
        else:
            returning_method = 'Unpublish'
            call_async(q, conn.ContactList, 'Unpublish', [cat])
    else:
        returning_method = 'RemoveMembers'

        if remove:
            call_async(q, stored.Group, 'RemoveMembers', [cat], '')
        else:
            call_async(q, publish.Group, 'RemoveMembers', [cat], '')

    # As above, the only reason the Cat is on our contact list is the pending
    # publish request, so Unpublish really results in removal.
    q.expect_many(
            EventPattern('dbus-return', method=returning_method),
            EventPattern('dbus-signal', signal='ContactsChanged',
                args=[{}, [cat]]),
            )

    # Redundant API calls (removing an absent contact, etc.) cause no network
    # traffic, and succeed.
    forbidden = [EventPattern('stream-iq', query_ns=ns.ROSTER),
            EventPattern('stream-presence')]
    sync_stream(q, stream)
    sync_dbus(bus, q, conn)
    q.forbid_events(forbidden)

    call_async(q, conn.ContactList, 'AuthorizePublication',
            [kristine, holly, dave])
    call_async(q, conn.ContactList, 'Unpublish', [arnold, cat])
    call_async(q, conn.ContactList, 'RemoveContacts', [arnold, cat])
    q.expect_many(
            EventPattern('dbus-return', method='AuthorizePublication'),
            EventPattern('dbus-return', method='Unpublish'),
            EventPattern('dbus-return', method='RemoveContacts'),
            )

    sync_stream(q, stream)
    sync_dbus(bus, q, conn)
    q.unforbid_events(forbidden)

    # There's one more case: revoking the publish permission of someone who is
    # genuinely on the roster.

    if modern:
        if remove:
            returning_method = 'RemoveContacts'
            call_async(q, conn.ContactList, 'RemoveContacts', [holly])
        else:
            returning_method = 'Unpublish'
            call_async(q, conn.ContactList, 'Unpublish', [holly])
    else:
        returning_method = 'RemoveMembers'

        if remove:
            call_async(q, stored.Group, 'RemoveMembers', [holly], '')
        else:
            call_async(q, publish.Group, 'RemoveMembers', [holly], '')

    if remove:
        iq = q.expect('stream-iq', iq_type='set', query_ns=ns.ROSTER,
                query_name='query')

        acknowledge_iq(stream, iq.stanza)

        if modern:
            q.expect('dbus-return', method='RemoveContacts')
        # FIXME: when we depend on a new enough tp-glib, expect RemoveMembers
        # to return here too

        send_roster_push(stream, '*****@*****.**', 'remove')
        q.expect_many(
                EventPattern('stream-iq', iq_type='result', iq_id='push'),
                EventPattern('dbus-signal', signal='ContactsChanged',
                    args=[{}, [holly]]),
                )
    else:
        q.expect_many(
                EventPattern('dbus-return', method=returning_method),
                EventPattern('stream-presence', presence_type='unsubscribed',
                    to='*****@*****.**'),
                )

        send_roster_push(stream, '*****@*****.**', 'to')
        q.expect_many(
                EventPattern('stream-iq', iq_type='result', iq_id='push'),
                EventPattern('dbus-signal', signal='ContactsChanged',
                    args=[{holly:
                        (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_NO, ''),
                        }, []]),
                )