def test(q, bus, conn, stream):
    conn.Connect()

    _, iq_event = q.expect_many(
        EventPattern('dbus-signal', signal='StatusChanged',
            args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]),
        EventPattern('stream-iq', to=None, query_ns='vcard-temp',
            query_name='vCard'))

    acknowledge_iq(stream, iq_event.stanza)

    call_async(q, conn, 'RequestHandles', cs.HT_ROOM, ['*****@*****.**'])

    event = q.expect('stream-iq', to='conf.localhost',
            query_ns='http://jabber.org/protocol/disco#info')
    result = make_result_iq(stream, event.stanza)
    feature = result.firstChildElement().addElement('feature')
    feature['var'] = 'http://jabber.org/protocol/muc'
    stream.send(result)

    event = q.expect('dbus-return', method='RequestHandles')
    handles = event.value[0]
    room_handle = handles[0]

    # join the muc
    call_async(q, conn, 'RequestChannel',
        cs.CHANNEL_TYPE_TEXT, cs.HT_ROOM, room_handle, True)

    _, stream_event = q.expect_many(
        EventPattern('dbus-signal', signal='MembersChanged',
            args=[u'', [], [], [], [2], 0, 0]),
        EventPattern('stream-presence', to='[email protected]/test'))

    # Send presence for other member of room.
    stream.send(make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob'))

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

    q.expect('dbus-signal', signal='MembersChanged',
            args=[u'', [2, 3], [], [], [], 0, 0])

    assert conn.InspectHandles(cs.HT_CONTACT, [2, 3]) == \
        ['[email protected]/test', '[email protected]/bob']
    bob_handle = 3

    event = q.expect('dbus-return', method='RequestChannel')
    text_chan = bus.get_object(conn.bus_name, event.value[0])

    # Bob offers a muc tube
    tube_id = 666
    stream_id = 1234
    presence = make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob')
    tubes = presence.addElement((ns.TUBES, 'tubes'))
    tube = tubes.addElement((None, 'tube'))
    tube['type'] = 'dbus'
    tube['service'] = 'org.telepathy.freedesktop.test'
    tube['id'] = str(tube_id)
    tube['stream-id'] = str(stream_id)
    tube['dbus-name'] = ':2.Y2Fzc2lkeS10ZXN0MgAA'
    tube['initiator'] = '[email protected]/bob'
    parameters = tube.addElement((None, 'parameters'))
    parameter = parameters.addElement((None, 'parameter'))
    parameter['name'] = 's'
    parameter['type'] = 'str'
    parameter.addContent('hello')
    parameter = parameters.addElement((None, 'parameter'))
    parameter['name'] = 'ay'
    parameter['type'] = 'bytes'
    parameter.addContent('aGVsbG8=')
    parameter = parameters.addElement((None, 'parameter'))
    parameter['name'] = 'u'
    parameter['type'] = 'uint'
    parameter.addContent('123')
    parameter = parameters.addElement((None, 'parameter'))
    parameter['name'] = 'i'
    parameter['type'] = 'int'
    parameter.addContent('-123')

    stream.send(presence)

    # tubes channel is automatically created
    event = q.expect('dbus-signal', signal='NewChannel')

    if event.args[1] == cs.CHANNEL_TYPE_TEXT:
        # skip this one, try the next one
        event = q.expect('dbus-signal', signal='NewChannel')

    assert event.args[1] == cs.CHANNEL_TYPE_TUBES, event.args
    assert event.args[2] == cs.HT_ROOM
    assert event.args[3] == room_handle

    tubes_chan = bus.get_object(conn.bus_name, event.args[0])
    tubes_iface = dbus.Interface(tubes_chan, event.args[1])

    channel_props = tubes_chan.GetAll(
        cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE)
    assert channel_props['TargetID'] == '*****@*****.**', channel_props
    assert channel_props['Requested'] == False
    assert channel_props['InitiatorID'] == ''
    assert channel_props['InitiatorHandle'] == 0

    tubes_self_handle = tubes_chan.GetSelfHandle(
        dbus_interface=cs.CHANNEL_IFACE_GROUP)

    q.expect('dbus-signal', signal='NewTube',
        args=[tube_id, bob_handle, 0, 'org.telepathy.freedesktop.test', sample_parameters, 0])

    expected_tube = (tube_id, bob_handle, cs.TUBE_TYPE_DBUS,
        'org.telepathy.freedesktop.test', sample_parameters,
        cs.TUBE_STATE_LOCAL_PENDING)
    tubes = tubes_iface.ListTubes(byte_arrays=True)
    assert len(tubes) == 1, unwrap(tubes)
    t.check_tube_in_tubes(expected_tube, tubes)

    # reject the tube
    tubes_iface.CloseTube(tube_id)
    q.expect('dbus-signal', signal='TubeClosed', args=[tube_id])

    # close the text channel
    text_chan.Close()
def test(q, bus, conn, stream, bytestream_cls,
        address_type, access_control, access_control_param):
    address1 = t.set_up_echo(q, address_type, True, streamfile='stream')
    address2 = t.set_up_echo(q, address_type, True, streamfile='stream2')

    t.check_conn_properties(q, conn)

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

    self_handle = conn.GetSelfHandle()

    acknowledge_iq(stream, vcard_event.stanza)

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

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

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

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

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

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

    # Test tubes with Bob. Bob has tube capabilities.
    bob_handle = conn.RequestHandles(1, ['bob@localhost'])[0]

    # old tubes API
    call_async(q, conn, 'RequestChannel', cs.CHANNEL_TYPE_TUBES, cs.HT_CONTACT,
        bob_handle, True)

    ret, old_sig, new_sig = q.expect_many(
        EventPattern('dbus-return', method='RequestChannel'),
        EventPattern('dbus-signal', signal='NewChannel'),
        EventPattern('dbus-signal', signal='NewChannels'),
        )

    assert len(ret.value) == 1
    chan_path = ret.value[0]

    t.check_NewChannel_signal(old_sig.args, cs.CHANNEL_TYPE_TUBES, chan_path,
            bob_handle, True)
    t.check_NewChannels_signal(conn, new_sig.args, cs.CHANNEL_TYPE_TUBES, chan_path,
            bob_handle, 'bob@localhost', self_handle)
    old_tubes_channel_properties = new_sig.args[0][0]

    t.check_conn_properties(q, conn, [old_tubes_channel_properties])

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

    # the NewTube signal (old API) is not fired now as the tube wasn't offered
    # yet
    ret, old_sig, new_sig = q.expect_many(
        EventPattern('dbus-return', method='CreateChannel'),
        EventPattern('dbus-signal', signal='NewChannel'),
        EventPattern('dbus-signal', signal='NewChannels'),
        )

    assert len(ret.value) == 2 # CreateChannel returns 2 values: o, a{sv}
    new_chan_path = ret.value[0]
    new_chan_prop_asv = ret.value[1]
    # State and Parameters are mutables so not announced
    assert cs.TUBE_STATE not in new_chan_prop_asv
    assert cs.TUBE_PARAMETERS not in new_chan_prop_asv
    assert new_chan_path.find("StreamTube") != -1, new_chan_path
    assert new_chan_path.find("SITubesChannel") == -1, new_chan_path
    # The path of the Channel.Type.Tubes object MUST be different to the path
    # of the Channel.Type.StreamTube object !
    assert chan_path != new_chan_path

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

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

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

    t.check_NewChannel_signal(old_sig.args, cs.CHANNEL_TYPE_STREAM_TUBE,
            new_chan_path, bob_handle, True)
    t.check_NewChannels_signal(conn, new_sig.args, cs.CHANNEL_TYPE_STREAM_TUBE,
            new_chan_path, bob_handle, 'bob@localhost', self_handle)
    stream_tube_channel_properties = new_sig.args[0][0]
    assert cs.TUBE_STATE not in stream_tube_channel_properties
    assert cs.TUBE_PARAMETERS not in stream_tube_channel_properties

    t.check_conn_properties(q, conn,
            [old_tubes_channel_properties, stream_tube_channel_properties])

    tubes_chan = bus.get_object(conn.bus_name, chan_path)
    tubes_iface = dbus.Interface(tubes_chan, cs.CHANNEL_TYPE_TUBES)

    t.check_channel_properties(q, bus, conn, tubes_chan, cs.CHANNEL_TYPE_TUBES,
            bob_handle, "bob@localhost")

    # Create another tube using old API
    call_async(q, tubes_iface, 'OfferStreamTube',
        'echo', sample_parameters, address_type, address1,
        access_control, access_control_param)

    event, return_event, new_chan, new_chans = q.expect_many(
        EventPattern('stream-message'),
        EventPattern('dbus-return', method='OfferStreamTube'),
        EventPattern('dbus-signal', signal='NewChannel'),
        EventPattern('dbus-signal', signal='NewChannels'))

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

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

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


    # the tube channel (new API) is announced
    t.check_NewChannel_signal(new_chan.args, cs.CHANNEL_TYPE_STREAM_TUBE,
        None, bob_handle, False)
    t.check_NewChannels_signal(conn, new_chans.args, cs.CHANNEL_TYPE_STREAM_TUBE,
        new_chan.args[0], bob_handle, "bob@localhost", self_handle)

    props = new_chans.args[0][0][1]
    assert cs.TUBE_STATE not in props
    assert cs.TUBE_PARAMETERS not in props

    # We offered a tube using the old tube API and created one with the new
    # API, so there are 2 tubes. Check the new tube API works
    assert len(filter(lambda x:
                  x[1] == cs.CHANNEL_TYPE_TUBES,
                  conn.ListChannels())) == 1
    channels = filter(lambda x:
      x[1] == cs.CHANNEL_TYPE_STREAM_TUBE and
      x[0] == new_chan_path,
      conn.ListChannels())
    assert len(channels) == 1
    assert new_chan_path == channels[0][0]

    old_tube_chan = bus.get_object(conn.bus_name, new_chan.args[0])

    tube_basic_props = old_tube_chan.GetAll(cs.CHANNEL,
            dbus_interface=cs.PROPERTIES_IFACE)
    assert tube_basic_props.get("InitiatorHandle") == self_handle

    stream_tube_props = old_tube_chan.GetAll(cs.CHANNEL_TYPE_STREAM_TUBE,
            dbus_interface=cs.PROPERTIES_IFACE)
    assert stream_tube_props.get("Service") == "echo", stream_tube_props

    old_tube_props = old_tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE,
            dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True)
    assert old_tube_props.get("Parameters") == dbus.Dictionary(sample_parameters)

    # Tube have been created using the old API and so is already offered
    assert old_tube_props['State'] == cs.TUBE_CHANNEL_STATE_REMOTE_PENDING

    t.check_channel_properties(q, bus, conn, tubes_chan, cs.CHANNEL_TYPE_TUBES,
            bob_handle, "bob@localhost")
    t.check_channel_properties(q, bus, conn, old_tube_chan,
            cs.CHANNEL_TYPE_STREAM_TUBE, bob_handle, "bob@localhost",
            cs.TUBE_CHANNEL_STATE_REMOTE_PENDING)

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

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

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

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

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

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

    # check NewTube signal (old API)
    id, initiator_handle, type, service, params, state = new_tube_sig.args
    assert initiator_handle == self_handle
    assert type == 1 # stream
    assert service == 'newecho'
    assert params == new_sample_parameters
    assert state == 1 # remote pending

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

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

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

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

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

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

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

    expected_tube = (stream_tube_id, self_handle, cs.TUBE_TYPE_STREAM, 'echo',
        sample_parameters, cs.TUBE_STATE_OPEN)
    tubes = tubes_iface.ListTubes(byte_arrays=True)
    t.check_tube_in_tubes(expected_tube, tubes)

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

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

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

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

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

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

    expected_tube = (new_stream_tube_id, self_handle, cs.TUBE_TYPE_STREAM,
        'newecho', new_sample_parameters, cs.TUBE_STATE_OPEN)
    t.check_tube_in_tubes (expected_tube, tubes_iface.ListTubes(byte_arrays=True))

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

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

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

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

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

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

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

    t.cleanup()
def test(q, bus, conn, stream, bytestream_cls, address_type, access_control, access_control_param):
    if bytestream_cls in [BytestreamS5BRelay, BytestreamS5BRelayBugged]:
        # disable SOCKS5 relay tests because proxy can't be used with muc
        # contacts atm
        return

    conn.Connect()

    _, iq_event = q.expect_many(
        EventPattern("dbus-signal", signal="StatusChanged", args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]),
        EventPattern("stream-iq", to=None, query_ns="vcard-temp", query_name="vCard"),
    )

    acknowledge_iq(stream, iq_event.stanza)

    self_handle = conn.GetSelfHandle()
    self_name = conn.InspectHandles(cs.HT_CONTACT, [self_handle])[0]

    t.check_conn_properties(q, conn)

    room_handle, tubes_chan, tubes_iface = get_muc_tubes_channel(q, bus, conn, stream, "*****@*****.**")

    tubes_self_handle = tubes_chan.GetSelfHandle(dbus_interface=cs.CHANNEL_IFACE_GROUP)

    bob_handle = conn.RequestHandles(cs.HT_CONTACT, ["[email protected]/bob"])[0]

    address = t.create_server(q, address_type)

    # offer stream tube (old API) using an Unix socket
    call_async(
        q,
        tubes_iface,
        "OfferStreamTube",
        "echo",
        sample_parameters,
        address_type,
        address,
        access_control,
        access_control_param,
    )

    new_tube_event, stream_event, _, new_channels_event = q.expect_many(
        EventPattern("dbus-signal", signal="NewTube"),
        EventPattern("stream-presence", to="[email protected]/test"),
        EventPattern("dbus-return", method="OfferStreamTube"),
        EventPattern("dbus-signal", signal="NewChannels"),
    )

    # handle new_tube_event
    stream_tube_id = new_tube_event.args[0]
    assert new_tube_event.args[1] == tubes_self_handle
    assert new_tube_event.args[2] == 1  # Stream
    assert new_tube_event.args[3] == "echo"
    assert new_tube_event.args[4] == sample_parameters
    assert new_tube_event.args[5] == cs.TUBE_CHANNEL_STATE_OPEN

    # handle stream_event
    # We announce our newly created tube in our muc presence
    presence = stream_event.stanza
    x_nodes = xpath.queryForNodes('/presence/x[@xmlns="http://jabber.org/' 'protocol/muc"]', presence)
    assert x_nodes is not None
    assert len(x_nodes) == 1

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

    tube_nodes = xpath.queryForNodes("/tubes/tube", tubes_nodes[0])
    assert tube_nodes is not None
    assert len(tube_nodes) == 1
    for tube in tube_nodes:
        assert tube["type"] == "stream"
        assert not tube.hasAttribute("initiator")
        assert tube["service"] == "echo"
        assert not tube.hasAttribute("stream-id")
        assert not tube.hasAttribute("dbus-name")
        assert tube["id"] == str(stream_tube_id)

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

    # tube is also announced using new API
    channels = new_channels_event.args[0]
    assert len(channels) == 1
    path, props = channels[0]
    assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE
    assert props[cs.INITIATOR_HANDLE] == tubes_self_handle
    assert props[cs.INITIATOR_ID] == "[email protected]/test"
    assert props[cs.INTERFACES] == [cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE]
    assert props[cs.REQUESTED] == True
    assert props[cs.TARGET_HANDLE] == room_handle
    assert props[cs.TARGET_ID] == "*****@*****.**"
    assert props[cs.STREAM_TUBE_SERVICE] == "echo"

    tube_chan = bus.get_object(conn.bus_name, path)
    tube_props = tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True)
    assert tube_props["Parameters"] == sample_parameters
    assert tube_props["State"] == cs.TUBE_CHANNEL_STATE_OPEN

    tubes = tubes_iface.ListTubes(byte_arrays=True)
    assert tubes == [
        (stream_tube_id, tubes_self_handle, 1, "echo", sample_parameters, cs.TUBE_CHANNEL_STATE_OPEN)  # Stream
    ]

    assert len(tubes) == 1, unwrap(tubes)
    expected_tube = (
        stream_tube_id,
        tubes_self_handle,
        cs.TUBE_TYPE_STREAM,
        "echo",
        sample_parameters,
        cs.TUBE_STATE_OPEN,
    )
    t.check_tube_in_tubes(expected_tube, tubes)

    # FIXME: if we use an unknown JID here, everything fails
    # (the code uses lookup where it should use ensure)

    bytestream = connect_to_tube(stream, q, bytestream_cls, "*****@*****.**", stream_tube_id)

    iq_event, socket_event, _, conn_event = q.expect_many(
        EventPattern("stream-iq", iq_type="result"),
        EventPattern("socket-connected"),
        EventPattern(
            "dbus-signal",
            signal="StreamTubeNewConnection",
            args=[stream_tube_id, bob_handle],
            interface=cs.CHANNEL_TYPE_TUBES,
        ),
        EventPattern("dbus-signal", signal="NewRemoteConnection", interface=cs.CHANNEL_TYPE_STREAM_TUBE),
    )

    protocol = socket_event.protocol

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

    handle, access, conn_id = conn_event.args
    assert handle == bob_handle

    use_tube(q, bytestream, protocol, conn_id)

    # offer a stream tube to another room (new API)
    address = t.create_server(q, address_type, block_reading=True)

    request = {
        cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE,
        cs.TARGET_HANDLE_TYPE: cs.HT_ROOM,
        cs.TARGET_ID: "*****@*****.**",
        cs.STREAM_TUBE_SERVICE: "newecho",
    }
    _, _, new_tube_path, new_tube_props = join_muc(q, bus, conn, stream, "*****@*****.**", request)

    # first text and tubes channels are announced
    event = q.expect("dbus-signal", signal="NewChannels")
    channels = event.args[0]
    assert len(channels) == 2
    path1, prop1 = channels[0]
    path2, prop2 = channels[1]
    assert sorted([prop1[cs.CHANNEL_TYPE], prop2[cs.CHANNEL_TYPE]]) == [cs.CHANNEL_TYPE_TEXT, cs.CHANNEL_TYPE_TUBES]

    got_text, got_tubes = False, False
    for path, props in channels:
        if props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT:
            got_text = True
        elif props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TUBES:
            got_tubes = True
        else:
            assert False

        assert props[cs.INITIATOR_HANDLE] == self_handle
        assert props[cs.INITIATOR_ID] == self_name
        assert cs.CHANNEL_IFACE_GROUP in props[cs.INTERFACES]
        assert props[cs.TARGET_ID] == "*****@*****.**"
        assert props[cs.REQUESTED] == False

    assert (got_text, got_tubes) == (True, True)

    # now the tube channel is announced
    # FIXME: in this case, all channels should probably be announced together
    event = q.expect("dbus-signal", signal="NewChannels")
    channels = event.args[0]
    assert len(channels) == 1
    path, prop = channels[0]
    assert prop[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE
    assert prop[cs.INITIATOR_ID] == "[email protected]/test"
    assert prop[cs.REQUESTED] == True
    assert prop[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM
    assert prop[cs.TARGET_ID] == "*****@*****.**"
    assert prop[cs.STREAM_TUBE_SERVICE] == "newecho"

    # check that the tube channel is in the channels list
    all_channels = conn.Get(cs.CONN_IFACE_REQUESTS, "Channels", dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True)
    assertContains((path, prop), all_channels)

    tube_chan = bus.get_object(conn.bus_name, path)
    stream_tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_STREAM_TUBE)
    chan_iface = dbus.Interface(tube_chan, cs.CHANNEL)
    tube_props = tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE)

    assert tube_props["State"] == cs.TUBE_CHANNEL_STATE_NOT_OFFERED

    # offer the tube
    call_async(q, stream_tube_iface, "Offer", address_type, address, access_control, {"foo": "bar"})

    new_tube_event, stream_event, _, status_event = q.expect_many(
        EventPattern("dbus-signal", signal="NewTube"),
        EventPattern("stream-presence", to="[email protected]/test"),
        EventPattern("dbus-return", method="Offer"),
        EventPattern("dbus-signal", signal="TubeChannelStateChanged", args=[cs.TUBE_CHANNEL_STATE_OPEN]),
    )

    tube_self_handle = tube_chan.GetSelfHandle(dbus_interface=cs.CHANNEL_IFACE_GROUP)
    assert conn.InspectHandles(cs.HT_CONTACT, [tube_self_handle]) == ["[email protected]/test"]

    # handle new_tube_event
    stream_tube_id = new_tube_event.args[0]
    assert new_tube_event.args[2] == 1  # Stream
    assert new_tube_event.args[3] == "newecho"
    assert new_tube_event.args[4] == {"foo": "bar"}
    assert new_tube_event.args[5] == cs.TUBE_CHANNEL_STATE_OPEN

    presence = stream_event.stanza
    x_nodes = xpath.queryForNodes('/presence/x[@xmlns="http://jabber.org/' 'protocol/muc"]', presence)
    assert x_nodes is not None
    assert len(x_nodes) == 1

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

    tube_nodes = xpath.queryForNodes("/tubes/tube", tubes_nodes[0])
    assert tube_nodes is not None
    assert len(tube_nodes) == 1
    for tube in tube_nodes:
        assert tube["type"] == "stream"
        assert not tube.hasAttribute("initiator")
        assert tube["service"] == "newecho"
        assert not tube.hasAttribute("stream-id")
        assert not tube.hasAttribute("dbus-name")
        assert tube["id"] == str(stream_tube_id)

    params = {}
    parameter_nodes = xpath.queryForNodes("/tube/parameters/parameter", tube)
    for node in parameter_nodes:
        assert node["name"] not in params
        params[node["name"]] = (node["type"], str(node))
    assert params == {"foo": ("str", "bar")}

    bob_handle = conn.RequestHandles(cs.HT_CONTACT, ["[email protected]/bob"])[0]

    bytestream = connect_to_tube(stream, q, bytestream_cls, "*****@*****.**", stream_tube_id)

    iq_event, socket_event, conn_event = q.expect_many(
        EventPattern("stream-iq", iq_type="result"),
        EventPattern("socket-connected"),
        EventPattern("dbus-signal", signal="NewRemoteConnection", interface=cs.CHANNEL_TYPE_STREAM_TUBE),
    )

    handle, access, conn_id = conn_event.args
    assert handle == bob_handle

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

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

    use_tube(q, bytestream, protocol, conn_id)

    chan_iface.Close()
    q.expect_many(EventPattern("dbus-signal", signal="Closed"), EventPattern("dbus-signal", signal="ChannelClosed"))
def test(q, bus, conn, stream, access_control):
    conn.Connect()

    _, iq_event = q.expect_many(
        EventPattern('dbus-signal', signal='StatusChanged',
            args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]),
        EventPattern('stream-iq', to=None, query_ns='vcard-temp',
            query_name='vCard'))

    acknowledge_iq(stream, iq_event.stanza)

    # check if we can request muc D-Bus tube
    t.check_conn_properties(q, conn)

    self_handle = conn.GetSelfHandle()
    self_name = conn.InspectHandles(1, [self_handle])[0]

    handle, tubes_chan, tubes_iface = get_muc_tubes_channel(q, bus, conn,
        stream, '*****@*****.**')

    # Exercise basic Channel Properties from spec 0.17.7
    channel_props = tubes_chan.GetAll(cs.CHANNEL,
            dbus_interface=dbus.PROPERTIES_IFACE)
    assert channel_props.get('TargetHandle') == handle,\
            (channel_props.get('TargetHandle'), handle)
    assert channel_props.get('TargetHandleType') == 2,\
            channel_props.get('TargetHandleType')
    assert channel_props.get('ChannelType') == cs.CHANNEL_TYPE_TUBES,\
            channel_props.get('ChannelType')
    assert 'Interfaces' in channel_props, channel_props
    assert cs.CHANNEL_IFACE_GROUP in channel_props['Interfaces'], \
            channel_props['Interfaces']
    assert channel_props['TargetID'] == '*****@*****.**', channel_props
    assert channel_props['Requested'] == True
    assert channel_props['InitiatorID'] == 'test@localhost'
    assert channel_props['InitiatorHandle'] == conn.GetSelfHandle()

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

    tubes_self_handle = tubes_chan.GetSelfHandle(
        dbus_interface=cs.CHANNEL_IFACE_GROUP)
    assert group_props['SelfHandle'] == tubes_self_handle

    # Offer a D-Bus tube (old API)
    call_async(q, tubes_iface, 'OfferDBusTube',
            'com.example.TestCase', sample_parameters)

    new_tube_event, presence_event, offer_return_event, dbus_changed_event = \
        q.expect_many(
        EventPattern('dbus-signal', signal='NewTube'),
        EventPattern('stream-presence', to='[email protected]/test'),
        EventPattern('dbus-return', method='OfferDBusTube'),
        EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_TUBES))

    # handle new_tube_event
    dbus_tube_id = new_tube_event.args[0]
    assert new_tube_event.args[1] == tubes_self_handle
    assert new_tube_event.args[2] == cs.TUBE_TYPE_DBUS
    assert new_tube_event.args[3] == 'com.example.TestCase'
    assert new_tube_event.args[4] == sample_parameters
    assert new_tube_event.args[5] == cs.TUBE_STATE_OPEN

    # handle offer_return_event
    assert offer_return_event.value[0] == dbus_tube_id

    # handle presence_event
    # We announce our newly created tube in our muc presence
    presence = presence_event.stanza
    dbus_stream_id, my_bus_name = check_tube_in_presence(presence, dbus_tube_id, '[email protected]/test')

    # handle dbus_changed_event
    assert dbus_changed_event.args[0] == dbus_tube_id
    assert dbus_changed_event.args[1][0][0] == tubes_self_handle
    assert dbus_changed_event.args[1][0][1] == my_bus_name

    # handle offer_return_event
    assert dbus_tube_id == offer_return_event.value[0]

    tubes = tubes_iface.ListTubes(byte_arrays=True)
    assert len(tubes) == 1
    expected_tube = (dbus_tube_id, tubes_self_handle, cs.TUBE_TYPE_DBUS,
        'com.example.TestCase', sample_parameters, cs.TUBE_STATE_OPEN)
    t.check_tube_in_tubes(expected_tube, tubes)

    dbus_tube_adr = tubes_iface.GetDBusTubeAddress(dbus_tube_id)
    tube = Connection(dbus_tube_adr)
    fire_signal_on_tube(q, tube, '*****@*****.**', dbus_stream_id, my_bus_name)

    # offer a D-Bus tube to another room using new API
    muc = '*****@*****.**'
    request = {
        cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE,
        cs.TARGET_HANDLE_TYPE: cs.HT_ROOM,
        cs.TARGET_ID: '*****@*****.**',
        cs.DBUS_TUBE_SERVICE_NAME: 'com.example.TestCase',
    }
    join_muc(q, bus, conn, stream, muc, request=request)

    # first text and tubes channels are announced
    event = q.expect('dbus-signal', signal='NewChannels')
    channels = event.args[0]
    assert len(channels) == 2
    path1, prop1 = channels[0]
    path2, prop2 = channels[1]
    assert sorted([prop1[cs.CHANNEL_TYPE], prop2[cs.CHANNEL_TYPE]]) == \
        [cs.CHANNEL_TYPE_TEXT, cs.CHANNEL_TYPE_TUBES]

    got_text, got_tubes = False, False
    for path, props in channels:
        if props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT:
            got_text = True

            text_chan = dbus.Interface(bus.get_object(conn.bus_name, path),
                cs.CHANNEL)
        elif props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TUBES:
            got_tubes = True

            tubes_iface = dbus.Interface(bus.get_object(conn.bus_name, path),
                cs.CHANNEL_TYPE_TUBES)
        else:
            assert False

        assert props[cs.INITIATOR_HANDLE] == self_handle
        assert props[cs.INITIATOR_ID] == self_name
        assert cs.CHANNEL_IFACE_GROUP in props[cs.INTERFACES]
        assert props[cs.TARGET_ID] == '*****@*****.**'
        assert props[cs.REQUESTED] == False

    assert (got_text, got_tubes) == (True, True)

    # now the tube channel is announced
    # FIXME: in this case, all channels should probably be announced together
    event = q.expect('dbus-signal', signal='NewChannels')
    channels = event.args[0]
    assert len(channels) == 1
    path, prop = channels[0]
    assert prop[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE
    assert prop[cs.INITIATOR_ID] == '[email protected]/test'
    assert prop[cs.REQUESTED] == True
    assert prop[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM
    assert prop[cs.TARGET_ID] == '*****@*****.**'
    assert prop[cs.DBUS_TUBE_SERVICE_NAME] == 'com.example.TestCase'
    assert prop[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS] == [cs.SOCKET_ACCESS_CONTROL_CREDENTIALS,
        cs.SOCKET_ACCESS_CONTROL_LOCALHOST]

    # check that the tube channel is in the channels list
    all_channels = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels',
        dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True)
    assertContains((path, prop), all_channels)

    tube_chan = bus.get_object(conn.bus_name, path)
    dbus_tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_DBUS_TUBE)
    chan_iface = dbus.Interface(tube_chan, cs.CHANNEL)
    tube_props = tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE,
        byte_arrays=True)

    assert tube_props['State'] == cs.TUBE_CHANNEL_STATE_NOT_OFFERED

    # try to offer using a wrong access control
    try:
        dbus_tube_iface.Offer(sample_parameters, cs.SOCKET_ACCESS_CONTROL_PORT)
    except dbus.DBusException, e:
        assertEquals(e.get_dbus_name(), cs.INVALID_ARGUMENT)
def test(q, bus, conn, stream, access_control):
    iq_event = q.expect("stream-iq", to=None, query_ns="vcard-temp", query_name="vCard")

    acknowledge_iq(stream, iq_event.stanza)

    # check if we can request muc D-Bus tube
    t.check_conn_properties(q, conn)

    self_handle = conn.GetSelfHandle()
    self_name = conn.InspectHandles(1, [self_handle])[0]

    handle, tubes_chan, tubes_iface = get_muc_tubes_channel(q, bus, conn, stream, "*****@*****.**")

    # Exercise basic Channel Properties from spec 0.17.7
    channel_props = tubes_chan.GetAll(cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE)
    assert channel_props.get("TargetHandle") == handle, (channel_props.get("TargetHandle"), handle)
    assert channel_props.get("TargetHandleType") == 2, channel_props.get("TargetHandleType")
    assert channel_props.get("ChannelType") == cs.CHANNEL_TYPE_TUBES, channel_props.get("ChannelType")
    assert "Interfaces" in channel_props, channel_props
    assert cs.CHANNEL_IFACE_GROUP in channel_props["Interfaces"], channel_props["Interfaces"]
    assert channel_props["TargetID"] == "*****@*****.**", channel_props
    assert channel_props["Requested"] == True
    assert channel_props["InitiatorID"] == "test@localhost"
    assert channel_props["InitiatorHandle"] == conn.GetSelfHandle()

    # Exercise Group Properties from spec 0.17.6 (in a basic way)
    group_props = tubes_chan.GetAll(cs.CHANNEL_IFACE_GROUP, dbus_interface=dbus.PROPERTIES_IFACE)
    assert "SelfHandle" in group_props, group_props
    assert "HandleOwners" in group_props, group_props
    assert "Members" in group_props, group_props
    assert "LocalPendingMembers" in group_props, group_props
    assert "RemotePendingMembers" in group_props, group_props
    assert "GroupFlags" in group_props, group_props

    tubes_self_handle = tubes_chan.GetSelfHandle(dbus_interface=cs.CHANNEL_IFACE_GROUP)
    assert group_props["SelfHandle"] == tubes_self_handle

    # Offer a D-Bus tube (old API)
    call_async(q, tubes_iface, "OfferDBusTube", "com.example.TestCase", sample_parameters)

    new_tube_event, presence_event, offer_return_event, dbus_changed_event = q.expect_many(
        EventPattern("dbus-signal", signal="NewTube"),
        EventPattern("stream-presence", to="[email protected]/test"),
        EventPattern("dbus-return", method="OfferDBusTube"),
        EventPattern("dbus-signal", signal="DBusNamesChanged", interface=cs.CHANNEL_TYPE_TUBES),
    )

    # handle new_tube_event
    dbus_tube_id = new_tube_event.args[0]
    assert new_tube_event.args[1] == tubes_self_handle
    assert new_tube_event.args[2] == cs.TUBE_TYPE_DBUS
    assert new_tube_event.args[3] == "com.example.TestCase"
    assert new_tube_event.args[4] == sample_parameters
    assert new_tube_event.args[5] == cs.TUBE_STATE_OPEN

    # handle offer_return_event
    assert offer_return_event.value[0] == dbus_tube_id

    # handle presence_event
    # We announce our newly created tube in our muc presence
    presence = presence_event.stanza
    dbus_stream_id, my_bus_name = check_tube_in_presence(presence, dbus_tube_id, "[email protected]/test")

    # handle dbus_changed_event
    assert dbus_changed_event.args[0] == dbus_tube_id
    assert dbus_changed_event.args[1][0][0] == tubes_self_handle
    assert dbus_changed_event.args[1][0][1] == my_bus_name

    # handle offer_return_event
    assert dbus_tube_id == offer_return_event.value[0]

    tubes = tubes_iface.ListTubes(byte_arrays=True)
    assert len(tubes) == 1
    expected_tube = (
        dbus_tube_id,
        tubes_self_handle,
        cs.TUBE_TYPE_DBUS,
        "com.example.TestCase",
        sample_parameters,
        cs.TUBE_STATE_OPEN,
    )
    t.check_tube_in_tubes(expected_tube, tubes)

    dbus_tube_adr = tubes_iface.GetDBusTubeAddress(dbus_tube_id)
    tube = Connection(dbus_tube_adr)
    fire_signal_on_tube(q, tube, "*****@*****.**", dbus_stream_id, my_bus_name)

    # offer a D-Bus tube to another room using new API
    muc = "*****@*****.**"
    request = {
        cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE,
        cs.TARGET_HANDLE_TYPE: cs.HT_ROOM,
        cs.TARGET_ID: "*****@*****.**",
        cs.DBUS_TUBE_SERVICE_NAME: "com.example.TestCase",
    }
    join_muc(q, bus, conn, stream, muc, request=request)

    # The order in which the NewChannels signals are fired is
    # undefined -- it could be the (tubes, text) channels first, or it
    # could be the tube channel first; so let's accept either order
    # here.

    first, second = q.expect_many(
        EventPattern("dbus-signal", signal="NewChannels"), EventPattern("dbus-signal", signal="NewChannels")
    )

    # NewChannels signal with the text and tubes channels together.
    def nc_textandtubes(event):
        channels = event.args[0]
        assert len(channels) == 2
        path1, prop1 = channels[0]
        path2, prop2 = channels[1]
        assert sorted([prop1[cs.CHANNEL_TYPE], prop2[cs.CHANNEL_TYPE]]) == [cs.CHANNEL_TYPE_TEXT, cs.CHANNEL_TYPE_TUBES]

        got_text, got_tubes = False, False
        for path, props in channels:
            if props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT:
                got_text = True

                text_chan = dbus.Interface(bus.get_object(conn.bus_name, path), cs.CHANNEL)
            elif props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TUBES:
                got_tubes = True

                tubes_iface = dbus.Interface(bus.get_object(conn.bus_name, path), cs.CHANNEL_TYPE_TUBES)
            else:
                assert False

            assert props[cs.INITIATOR_HANDLE] == self_handle
            assert props[cs.INITIATOR_ID] == self_name
            assert cs.CHANNEL_IFACE_GROUP in props[cs.INTERFACES]
            assert props[cs.TARGET_ID] == "*****@*****.**"
            assert props[cs.REQUESTED] == False

        assert (got_text, got_tubes) == (True, True)

        return text_chan

    # NewChannels signal with the tube channel.
    def nc_tube(event):
        # FIXME: in this case, all channels should probably be announced together
        channels = event.args[0]
        assert len(channels) == 1
        path, prop = channels[0]
        assert prop[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE
        assert prop[cs.INITIATOR_ID] == "[email protected]/test"
        assert prop[cs.REQUESTED] == True
        assert prop[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM
        assert prop[cs.TARGET_ID] == "*****@*****.**"
        assert prop[cs.DBUS_TUBE_SERVICE_NAME] == "com.example.TestCase"
        assert prop[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS] == [
            cs.SOCKET_ACCESS_CONTROL_CREDENTIALS,
            cs.SOCKET_ACCESS_CONTROL_LOCALHOST,
        ]

        return path, prop

    if len(first.args[0]) == 1:
        path, prop = nc_tube(first)
        text_chan = nc_textandtubes(second)
    else:
        text_chan = nc_textandtubes(first)
        path, prop = nc_tube(second)

    # check that the tube channel is in the channels list
    all_channels = conn.Get(cs.CONN_IFACE_REQUESTS, "Channels", dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True)
    assertContains((path, prop), all_channels)

    tube_chan = bus.get_object(conn.bus_name, path)
    dbus_tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_DBUS_TUBE)
    chan_iface = dbus.Interface(tube_chan, cs.CHANNEL)
    tube_props = tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True)

    assert tube_props["State"] == cs.TUBE_CHANNEL_STATE_NOT_OFFERED

    # try to offer using a wrong access control
    try:
        dbus_tube_iface.Offer(sample_parameters, cs.SOCKET_ACCESS_CONTROL_PORT)
    except dbus.DBusException, e:
        assertEquals(e.get_dbus_name(), cs.INVALID_ARGUMENT)
def test(q, bus, conn, stream, bytestream_cls,
        address_type, access_control, access_control_param):
    if bytestream_cls in [BytestreamS5BRelay, BytestreamS5BRelayBugged]:
        # disable SOCKS5 relay tests because proxy can't be used with muc
        # contacts atm
        return

    iq_event, disco_event = q.expect_many(
        EventPattern('stream-iq', to=None, query_ns='vcard-temp',
            query_name='vCard'),
        EventPattern('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS))

    acknowledge_iq(stream, iq_event.stanza)

    announce_socks5_proxy(q, stream, disco_event.stanza)

    call_async(q, conn, 'RequestHandles', 2,
        ['*****@*****.**'])

    event = q.expect('dbus-return', method='RequestHandles')
    handles = event.value[0]
    room_handle = handles[0]

    # join the muc
    call_async(q, conn, 'RequestChannel', cs.CHANNEL_TYPE_TEXT, cs.HT_ROOM,
        room_handle, True)

    _, stream_event = q.expect_many(
        EventPattern('dbus-signal', signal='MembersChanged',
            args=[u'', [], [], [], [2], 0, 0]),
        EventPattern('stream-presence', to='[email protected]/test'))

    # Send presence for other member of room.
    stream.send(make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob'))

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

    q.expect('dbus-signal', signal='MembersChanged',
            args=[u'', [2, 3], [], [], [], 0, 0])

    assert conn.InspectHandles(1, [2]) == ['[email protected]/test']
    assert conn.InspectHandles(1, [3]) == ['[email protected]/bob']
    bob_handle = 3

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

    # Bob offers a stream tube
    stream_tube_id = 666
    presence = make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob')
    tubes = presence.addElement((ns.TUBES, 'tubes'))
    tube = tubes.addElement((None, 'tube'))
    tube['type'] = 'stream'
    tube['service'] = 'echo'
    tube['id'] = str(stream_tube_id)
    parameters = tube.addElement((None, 'parameters'))
    parameter = parameters.addElement((None, 'parameter'))
    parameter['name'] = 's'
    parameter['type'] = 'str'
    parameter.addContent('hello')
    parameter = parameters.addElement((None, 'parameter'))
    parameter['name'] = 'ay'
    parameter['type'] = 'bytes'
    parameter.addContent('aGVsbG8=')
    parameter = parameters.addElement((None, 'parameter'))
    parameter['name'] = 'u'
    parameter['type'] = 'uint'
    parameter.addContent('123')
    parameter = parameters.addElement((None, 'parameter'))
    parameter['name'] = 'i'
    parameter['type'] = 'int'
    parameter.addContent('-123')

    stream.send(presence)

    # text channel
    event, new_event = q.expect_many(
        EventPattern('dbus-signal', signal='NewChannel'),
        EventPattern('dbus-signal', signal='NewChannels'))

    assert event.args[1] == cs.CHANNEL_TYPE_TEXT, event.args

    channels = new_event.args[0]
    assert len(channels) == 1
    path, props = channels[0]
    assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT

    # tubes channel is automatically created
    event, new_event = q.expect_many(
        EventPattern('dbus-signal', signal='NewChannel'),
        EventPattern('dbus-signal', signal='NewChannels'))

    assert event.args[1] == cs.CHANNEL_TYPE_TUBES, event.args
    assert event.args[2] == cs.HT_ROOM
    assert event.args[3] == room_handle

    tubes_chan = bus.get_object(conn.bus_name, event.args[0])
    tubes_iface = dbus.Interface(tubes_chan, event.args[1])

    channel_props = tubes_chan.GetAll(cs.CHANNEL, dbus_interface=cs.PROPERTIES_IFACE)
    assert channel_props['TargetID'] == '*****@*****.**', channel_props
    assert channel_props['Requested'] == False
    assert channel_props['InitiatorID'] == ''
    assert channel_props['InitiatorHandle'] == 0

    channels = new_event.args[0]
    assert len(channels) == 1
    path, props = channels[0]
    assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TUBES

    tubes_self_handle = tubes_chan.GetSelfHandle(dbus_interface=cs.CHANNEL_IFACE_GROUP)

    q.expect('dbus-signal', signal='NewTube',
        args=[stream_tube_id, bob_handle, 1, 'echo', sample_parameters, 0])

    expected_tube = (stream_tube_id, bob_handle, cs.TUBE_TYPE_STREAM, 'echo',
        sample_parameters, cs.TUBE_STATE_LOCAL_PENDING)
    tubes = tubes_iface.ListTubes(byte_arrays=True)
    assert tubes == [(
        stream_tube_id,
        bob_handle,
        1,      # Stream
        'echo',
        sample_parameters,
        cs.TUBE_CHANNEL_STATE_LOCAL_PENDING
        )]

    assert len(tubes) == 1, unwrap(tubes)
    t.check_tube_in_tubes(expected_tube, tubes)

    # tube channel is also announced (new API)
    new_event = q.expect('dbus-signal', signal='NewChannels')

    channels = new_event.args[0]
    assert len(channels) == 1
    path, props = channels[0]
    assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE
    assert props[cs.INITIATOR_HANDLE] == bob_handle
    assert props[cs.INITIATOR_ID] == '[email protected]/bob'
    assert props[cs.INTERFACES] == [cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE]
    assert props[cs.REQUESTED] == False
    assert props[cs.TARGET_HANDLE] == room_handle
    assert props[cs.TARGET_ID] == '*****@*****.**'
    assert props[cs.STREAM_TUBE_SERVICE] == 'echo'
    assert props[cs.TUBE_PARAMETERS] == {'s': 'hello', 'ay': 'hello', 'u': 123, 'i': -123}
    assert access_control in \
            props[cs.STREAM_TUBE_SUPPORTED_SOCKET_TYPES][address_type]

    tube_chan = bus.get_object(conn.bus_name, path)
    tube_props = tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE,
        byte_arrays=True)
    assert tube_props['Parameters'] == sample_parameters
    assert tube_props['State'] == cs.TUBE_CHANNEL_STATE_LOCAL_PENDING

    # Accept the tube
    call_async(q, tubes_iface, 'AcceptStreamTube', stream_tube_id,
        address_type, access_control, access_control_param, byte_arrays=True)

    accept_return_event, _ = q.expect_many(
        EventPattern('dbus-return', method='AcceptStreamTube'),
        EventPattern('dbus-signal', signal='TubeStateChanged',
            args=[stream_tube_id, 2]))

    address = accept_return_event.value[0]

    socket_event, si_event, conn_id = t.connect_to_cm_socket(q, '[email protected]/bob',
        address_type, address, access_control, access_control_param)

    protocol = socket_event.protocol
    protocol.sendData("hello initiator")

    def accept_tube_si_connection():
        bytestream, profile = create_from_si_offer(stream, q, bytestream_cls, si_event.stanza,
                '[email protected]/test')

        assert profile == ns.TUBES

        muc_stream_node = xpath.queryForNodes('/iq/si/muc-stream[@xmlns="%s"]' %
            ns.TUBES, si_event.stanza)[0]
        assert muc_stream_node is not None
        assert muc_stream_node['tube'] == str(stream_tube_id)

        # set the real jid of the target as 'to' because the XMPP server changes
        # it when delivering the IQ
        result, si = bytestream.create_si_reply(si_event.stanza, 'test@localhost/Resource')
        si.addElement((ns.TUBES, 'tube'))
        stream.send(result)

        bytestream.wait_bytestream_open()
        return bytestream

    bytestream = accept_tube_si_connection()

    binary = bytestream.get_data()
    assert binary == 'hello initiator'

    # reply on the socket
    bytestream.send_data('hi joiner!')

    q.expect('socket-data', protocol=protocol, data="hi joiner!")

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

    # establish another tube connection
    socket_event, si_event, conn_id = t.connect_to_cm_socket(q, '[email protected]/bob',
        address_type, address, access_control, access_control_param)

    # bytestream is refused
    send_error_reply(stream, si_event.stanza)
    e, _ = q.expect_many(
        EventPattern('dbus-signal', signal='ConnectionClosed'),
        EventPattern('socket-disconnected'))
    assertEquals(conn_id, e.args[0])
    assertEquals(cs.CONNECTION_REFUSED, e.args[1])

    # establish another tube connection
    socket_event, si_event, conn_id = t.connect_to_cm_socket(q, '[email protected]/bob',
        address_type, address, access_control, access_control_param)

    protocol = socket_event.protocol
    bytestream = accept_tube_si_connection()

    # disconnect local socket
    protocol.transport.loseConnection()
    e, _ = q.expect_many(
        EventPattern('dbus-signal', signal='ConnectionClosed'),
        EventPattern('socket-disconnected'))
    assertEquals(conn_id, e.args[0])
    assertEquals(cs.CANCELLED, e.args[1])

    # OK, we're done
    disconnect_conn(q, conn, stream,
        [EventPattern('dbus-signal', signal='TubeClosed', args=[stream_tube_id])])
def test(q, bus, conn, stream, bytestream_cls,
       address_type, access_control, access_control_param):
    if bytestream_cls in [BytestreamS5BRelay, BytestreamS5BRelayBugged]:
        # disable SOCKS5 relay tests because proxy can't be used with muc
        # contacts atm
        return

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

    acknowledge_iq(stream, iq_event.stanza)

    self_handle = conn.GetSelfHandle()
    self_name = conn.InspectHandles(cs.HT_CONTACT, [self_handle])[0]

    t.check_conn_properties(q, conn)

    room_handle, tubes_chan, tubes_iface = get_muc_tubes_channel(q, bus, conn,
        stream, '*****@*****.**')

    tubes_self_handle = tubes_chan.GetSelfHandle(dbus_interface=cs.CHANNEL_IFACE_GROUP)

    bob_handle = conn.RequestHandles(cs.HT_CONTACT, ['[email protected]/bob'])[0]

    address = t.create_server(q, address_type)

    # offer stream tube (old API) using an Unix socket
    call_async(q, tubes_iface, 'OfferStreamTube',
        'echo', sample_parameters, address_type, address,
        access_control, access_control_param)

    new_tube_event, stream_event, _, new_channels_event = q.expect_many(
        EventPattern('dbus-signal', signal='NewTube'),
        EventPattern('stream-presence', to='[email protected]/test'),
        EventPattern('dbus-return', method='OfferStreamTube'),
        EventPattern('dbus-signal', signal='NewChannels'))

    # handle new_tube_event
    stream_tube_id = new_tube_event.args[0]
    assert new_tube_event.args[1] == tubes_self_handle
    assert new_tube_event.args[2] == 1       # Stream
    assert new_tube_event.args[3] == 'echo'
    assert new_tube_event.args[4] == sample_parameters
    assert new_tube_event.args[5] == cs.TUBE_CHANNEL_STATE_OPEN

    # handle stream_event
    # We announce our newly created tube in our muc presence
    presence = stream_event.stanza
    tubes_nodes = xpath.queryForNodes('/presence/tubes[@xmlns="%s"]'
        % ns.TUBES, presence)
    assert tubes_nodes is not None
    assert len(tubes_nodes) == 1

    tube_nodes = xpath.queryForNodes('/tubes/tube', tubes_nodes[0])
    assert tube_nodes is not None
    assert len(tube_nodes) == 1
    for tube in tube_nodes:
        assert tube['type'] == 'stream'
        assert not tube.hasAttribute('initiator')
        assert tube['service'] == 'echo'
        assert not tube.hasAttribute('stream-id')
        assert not tube.hasAttribute('dbus-name')
        assert tube['id'] == str(stream_tube_id)

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

    # tube is also announced using new API
    channels = new_channels_event.args[0]
    assert len(channels) == 1
    path, props = channels[0]
    assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE
    assert props[cs.INITIATOR_HANDLE] == tubes_self_handle
    assert props[cs.INITIATOR_ID] == '[email protected]/test'
    assert props[cs.INTERFACES] == [cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE]
    assert props[cs.REQUESTED] == True
    assert props[cs.TARGET_HANDLE] == room_handle
    assert props[cs.TARGET_ID] == '*****@*****.**'
    assert props[cs.STREAM_TUBE_SERVICE] == 'echo'

    tube_chan = bus.get_object(conn.bus_name, path)
    tube_props = tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE,
        byte_arrays=True)
    assert tube_props['Parameters'] == sample_parameters
    assert tube_props['State'] == cs.TUBE_CHANNEL_STATE_OPEN

    tubes = tubes_iface.ListTubes(byte_arrays=True)
    assert tubes == [(
        stream_tube_id,
        tubes_self_handle,
        1,      # Stream
        'echo',
        sample_parameters,
        cs.TUBE_CHANNEL_STATE_OPEN
        )]

    assert len(tubes) == 1, unwrap(tubes)
    expected_tube = (stream_tube_id, tubes_self_handle, cs.TUBE_TYPE_STREAM,
        'echo', sample_parameters, cs.TUBE_STATE_OPEN)
    t.check_tube_in_tubes(expected_tube, tubes)

    # FIXME: if we use an unknown JID here, everything fails
    # (the code uses lookup where it should use ensure)

    bytestream = connect_to_tube(stream, q, bytestream_cls, '*****@*****.**', stream_tube_id)

    iq_event, socket_event, _, conn_event = q.expect_many(
        EventPattern('stream-iq', iq_type='result'),
        EventPattern('socket-connected'),
        EventPattern('dbus-signal', signal='StreamTubeNewConnection',
            args=[stream_tube_id, bob_handle], interface=cs.CHANNEL_TYPE_TUBES),
        EventPattern('dbus-signal', signal='NewRemoteConnection',
            interface=cs.CHANNEL_TYPE_STREAM_TUBE))

    protocol = socket_event.protocol

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

    handle, access, conn_id = conn_event.args
    assert handle == bob_handle

    use_tube(q, bytestream, protocol, conn_id)

    # offer a stream tube to another room (new API)
    address = t.create_server(q, address_type, block_reading=True)

    request = {
        cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE,
        cs.TARGET_HANDLE_TYPE: cs.HT_ROOM,
        cs.TARGET_ID: '*****@*****.**',
        cs.STREAM_TUBE_SERVICE: 'newecho',
    }
    _, _, new_tube_path, new_tube_props = \
        join_muc(q, bus, conn, stream, '*****@*****.**', request)

    # The order in which the NewChannels signals are fired is
    # undefined -- it could be the (tubes, text) channels first, or it
    # could be the tube channel first; so let's accept either order
    # here.

    first, second = q.expect_many(
        EventPattern('dbus-signal', signal='NewChannels'),
        EventPattern('dbus-signal', signal='NewChannels'))

    # NewChannels signal with the text and tubes channels together.
    def nc_textandtubes(event):
        channels = event.args[0]
        assert len(channels) == 2
        path1, prop1 = channels[0]
        path2, prop2 = channels[1]
        assert sorted([prop1[cs.CHANNEL_TYPE], prop2[cs.CHANNEL_TYPE]]) == \
            [cs.CHANNEL_TYPE_TEXT, cs.CHANNEL_TYPE_TUBES]

        got_text, got_tubes = False, False
        for path, props in channels:
            if props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT:
                got_text = True
            elif props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TUBES:
                got_tubes = True
            else:
                assert False

            assert props[cs.INITIATOR_HANDLE] == self_handle
            assert props[cs.INITIATOR_ID] == self_name
            assert cs.CHANNEL_IFACE_GROUP in props[cs.INTERFACES]
            assert props[cs.TARGET_ID] == '*****@*****.**'
            assert props[cs.REQUESTED] == False

        assert (got_text, got_tubes) == (True, True)

    # NewChannels signal with the tube channel.
    def nc_tube(event):
        # FIXME: in this case, all channels should probably be announced together
        channels = event.args[0]
        assert len(channels) == 1
        path, prop = channels[0]
        assert prop[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE
        assert prop[cs.INITIATOR_ID] == '[email protected]/test'
        assert prop[cs.REQUESTED] == True
        assert prop[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM
        assert prop[cs.TARGET_ID] == '*****@*****.**'
        assert prop[cs.STREAM_TUBE_SERVICE] == 'newecho'

        return path, prop

    if len(first.args[0]) == 1:
        path, prop = nc_tube(first)
        nc_textandtubes(second)
    else:
        nc_textandtubes(first)
        path, prop = nc_tube(second)

    # check that the tube channel is in the channels list
    all_channels = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels',
        dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True)
    assertContains((path, prop), all_channels)

    tube_chan = bus.get_object(conn.bus_name, path)
    stream_tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_STREAM_TUBE)
    chan_iface = dbus.Interface(tube_chan, cs.CHANNEL)
    tube_props = tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE)

    assert tube_props['State'] == cs.TUBE_CHANNEL_STATE_NOT_OFFERED

    # offer the tube
    call_async(q, stream_tube_iface, 'Offer', address_type, address, access_control, {'foo': 'bar'})

    new_tube_event, stream_event, _, status_event = q.expect_many(
        EventPattern('dbus-signal', signal='NewTube'),
        EventPattern('stream-presence', to='[email protected]/test'),
        EventPattern('dbus-return', method='Offer'),
        EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[cs.TUBE_CHANNEL_STATE_OPEN]))

    tube_self_handle = tube_chan.GetSelfHandle(dbus_interface=cs.CHANNEL_IFACE_GROUP)
    assert conn.InspectHandles(cs.HT_CONTACT, [tube_self_handle]) == ['[email protected]/test']

    # handle new_tube_event
    stream_tube_id = new_tube_event.args[0]
    assert new_tube_event.args[2] == 1       # Stream
    assert new_tube_event.args[3] == 'newecho'
    assert new_tube_event.args[4] == {'foo': 'bar'}
    assert new_tube_event.args[5] == cs.TUBE_CHANNEL_STATE_OPEN

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

    tube_nodes = xpath.queryForNodes('/tubes/tube', tubes_nodes[0])
    assert tube_nodes is not None
    assert len(tube_nodes) == 1
    for tube in tube_nodes:
        assert tube['type'] == 'stream'
        assert not tube.hasAttribute('initiator')
        assert tube['service'] == 'newecho'
        assert not tube.hasAttribute('stream-id')
        assert not tube.hasAttribute('dbus-name')
        assert tube['id'] == str(stream_tube_id)

    params = {}
    parameter_nodes = xpath.queryForNodes('/tube/parameters/parameter', tube)
    for node in parameter_nodes:
        assert node['name'] not in params
        params[node['name']] = (node['type'], str(node))
    assert params == {'foo': ('str', 'bar')}

    bob_handle = conn.RequestHandles(cs.HT_CONTACT, ['[email protected]/bob'])[0]

    bytestream = connect_to_tube(stream, q, bytestream_cls, '*****@*****.**', stream_tube_id)

    iq_event, socket_event, conn_event = q.expect_many(
        EventPattern('stream-iq', iq_type='result'),
        EventPattern('socket-connected'),
        EventPattern('dbus-signal', signal='NewRemoteConnection',
            interface=cs.CHANNEL_TYPE_STREAM_TUBE))

    handle, access, conn_id = conn_event.args
    assert handle == bob_handle

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

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

    use_tube(q, bytestream, protocol, conn_id)

    chan_iface.Close()
    q.expect_many(
        EventPattern('dbus-signal', signal='Closed'),
        EventPattern('dbus-signal', signal='ChannelClosed'))