def join_muc(q, bus, conn, stream, muc, request=None): """ Joins 'muc', returning the muc's handle, a proxy object for the channel, its path and its immutable properties just after the CreateChannel event has fired. The room contains one other member. """ if request is None: request = { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: muc, } muc_handle = request_muc_handle(q, conn, stream, muc) requests = dbus.Interface(conn, cs.CONN_IFACE_REQUESTS) call_async(q, requests, 'CreateChannel', dbus.Dictionary(request, signature='sv')) q.expect('stream-presence', to='%s/test' % muc) # Send presence for other member of room. stream.send(make_muc_presence('owner', 'moderator', muc, 'bob')) # Send presence for own membership of room. stream.send(make_muc_presence('none', 'participant', muc, 'test')) event = q.expect('dbus-return', method='CreateChannel') path, props = event.value chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['Messages']) return (muc_handle, chan, path, props)
def test(q, bus, conn, stream): jp = JingleProtocol031() remote_jid = '[email protected]/misc' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] path, _ = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: remote_handle}) chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia') # In Gabble, the StreamedMedia channel is secretly also the SessionHandler. # Let's make up a proxy and call some methods on it. They should fail # gracefully, rather than crashing Gabble. session_handler = make_channel_proxy(conn, path, 'Media.SessionHandler') try: session_handler.Ready() except DBusException, e: assertEquals(cs.NOT_AVAILABLE, e.get_dbus_name())
def setup(q, bus, conn, stream, op_user=True): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) # test MUC channel call_async(q, conn.Requests, 'CreateChannel', {cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: '#test'}) ret, _, _ = q.expect_many(EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='MembersChanged'), EventPattern('stream-MODE', data=['#test'])) chan = wrap_channel(bus.get_object(conn.bus_name, ret.value[0]), 'Text', extra=['RoomConfig1']) change_channel_mode(stream, '+n') q.expect('dbus-signal', signal='PropertiesChanged', args=[cs.CHANNEL_IFACE_ROOM_CONFIG, {'ConfigurationRetrieved': True}, []]) if op_user: change_channel_mode(stream, '+o test') q.expect_many(EventPattern('dbus-signal', signal='GroupFlagsChanged', args=[cs.GF_MESSAGE_REMOVE | cs.GF_CAN_REMOVE, 0]), EventPattern('dbus-signal', signal='PropertiesChanged', args=[cs.CHANNEL_IFACE_ROOM_CONFIG, {'CanUpdateConfiguration': True}, []])) return chan
def check_neither(q, conn, bus, stream, remote_handle): """ Make a channel without specifying InitialAudio or InitialVideo; check that it's announced with both False, and that they're both present and false in GetAll(). """ path, props = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: remote_handle}) assertContains((cs.INITIAL_AUDIO, False), props.items()) assertContains((cs.INITIAL_VIDEO, False), props.items()) chan = wrap_channel(bus.get_object(conn.bus_name, path), cs.CHANNEL_TYPE_STREAMED_MEDIA, ['MediaSignalling']) props = chan.Properties.GetAll(cs.CHANNEL_TYPE_STREAMED_MEDIA) assertContains(('InitialAudio', False), props.items()) assertContains(('InitialVideo', False), props.items()) # We shouldn't have started a session yet, so there shouldn't be any # session handlers. Strictly speaking, there could be a session handler # with no stream handlers, but... session_handlers = chan.MediaSignalling.GetSessionHandlers() assertLength(0, session_handlers)
def join_muc(q, bus, conn, stream, muc, request=None, also_capture=[], role='participant', affiliation='none'): """ Joins 'muc', returning a proxy object for the channel, its path and its immutable properties just after the CreateChannel event has fired. The room contains one other member. """ try_to_join_muc(q, bus, conn, stream, muc, request=request) # Send presence for other member of room. stream.send(make_muc_presence('owner', 'moderator', muc, 'bob')) # Send presence for own membership of room. stream.send(make_muc_presence(affiliation, role, muc, 'test')) captured = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), *also_capture) path, props = captured[0].value chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['Subject.DRAFT']) return (chan, path, props) + tuple(captured[1:])
def incoming(jp, q, bus, conn, stream): remote_jid = 'skinny.fists@heaven/antennas' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] for a, v in [(True, False), (False, True), (True, True)]: if v and not jp.can_do_video(): continue if not a and v and not jp.can_do_video_only(): continue jt.incoming_call(audio=a, video=v) e = q.expect('dbus-signal', signal='NewChannels') chans = e.args[0] assertLength(1, chans) path, props = chans[0] assertEquals(cs.CHANNEL_TYPE_STREAMED_MEDIA, props[cs.CHANNEL_TYPE]) assertEquals(a, props[cs.INITIAL_AUDIO]) assertEquals(v, props[cs.INITIAL_VIDEO]) chan = wrap_channel(bus.get_object(conn.bus_name, path), cs.CHANNEL_TYPE_STREAMED_MEDIA) chan.Close()
def test(jp, q, bus, conn, stream): if not jp.can_do_video_only(): return remote_jid = '[email protected]/Foo' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] chan_path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) chan = wrap_channel(bus.get_object(conn.bus_name, chan_path), 'StreamedMedia', ['MediaSignalling', 'Group', 'CallState', 'DTMF']) chan_props = chan.Properties.GetAll(cs.CHANNEL) assert cs.CHANNEL_IFACE_DTMF in chan_props['Interfaces'], \ chan_props['Interfaces'] chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_VIDEO]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') video_path = e.args[0] stream_handler = make_channel_proxy(conn, video_path, 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.get_video_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) stream.send(make_result_iq(stream, e.stanza)) jt.parse_session_initiate(e.query) jt.accept() # Gabble tells s-e to start sending q.expect('dbus-signal', signal='SetStreamSending', args=[True], path=video_path) # We don't actually have an audio stream, so this is a non-starter. call_async(q, chan.DTMF, 'StartTone', 666, 3) q.expect('dbus-error', method='StartTone', name=cs.NOT_AVAILABLE) call_async(q, chan.DTMF, 'MultipleTones', '**666##') q.expect('dbus-error', method='MultipleTones', name=cs.NOT_AVAILABLE) # We can still stop all the tones that are playing (a no-op). call_async(q, chan.DTMF, 'StopTone', 666) q.expect('dbus-return', method='StopTone') chan.Group.RemoveMembers([self_handle], 'closed') e = q.expect('dbus-signal', signal='Closed', path=chan_path)
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.Properties.Get(cs.CONN, "SelfHandle") self_name = conn.inspect_contact_sync(self_handle) # 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) e = q.expect('dbus-signal', signal='NewChannels') channels = e.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 = wrap_channel(bus.get_object(conn.bus_name, path), 'DBusTube') tube_props = tube_chan.Properties.GetAll(cs.CHANNEL_IFACE_TUBE, byte_arrays=True) assert tube_props['State'] == cs.TUBE_CHANNEL_STATE_NOT_OFFERED # try to offer using a wrong access control try: tube_chan.DBusTube.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): muc_handle = request_muc_handle(q, conn, stream, '*****@*****.**') call_async(q, conn, 'RequestChannel', cs.CHANNEL_TYPE_TEXT, cs.HT_ROOM, muc_handle, True) q.expect('stream-presence', to='[email protected]/test') # Send presence for own membership of room. stream.send( make_muc_presence('owner', 'moderator', '*****@*****.**', 'test')) iq, ret = q.expect_many( EventPattern('stream-iq', to='*****@*****.**', iq_type='get', query_ns=ns.MUC_OWNER), EventPattern('dbus-return', method='RequestChannel')) handle_muc_get_iq(stream, iq.stanza) text_chan = wrap_channel( bus.get_object(conn.bus_name, ret.value[0]), 'Text') props = dict([(name, id) for id, name, sig, flags in text_chan.TpProperties.ListProperties()]) call_async(q, text_chan.TpProperties, 'SetProperties', [(props['password'], 'foo'), (props['password-required'], True)]) event = q.expect('stream-iq', to='*****@*****.**', iq_type='get', query_ns=ns.MUC_OWNER) handle_muc_get_iq(stream, event.stanza) event = q.expect('stream-iq', to='*****@*****.**', iq_type='set', query_ns=ns.MUC_OWNER) fields = xpath.queryForNodes('/iq/query/x/field', event.stanza) form = {} for field in fields: values = xpath.queryForNodes('/field/value', field) form[field['var']] = [str(v) for v in values] assert form == {'password': ['foo'], 'password_protected': ['1'], 'muc#roomconfig_presencebroadcast' : ['moderator', 'participant', 'visitor']} acknowledge_iq(stream, event.stanza) event = q.expect('dbus-signal', signal='PropertiesChanged') assert event.args == [[(props['password'], 'foo'), (props['password-required'], True)]] q.expect('dbus-return', method='SetProperties', value=()) call_async(q, text_chan.TpProperties, 'SetProperties', [(31337, 'foo'), (props['password-required'], True)]) q.expect('dbus-error', name=cs.INVALID_ARGUMENT) call_async(q, text_chan.TpProperties, 'SetProperties', [(props['password'], True), (props['password-required'], 'foo')]) q.expect('dbus-error', name=cs.NOT_AVAILABLE) call_async(q, text_chan.TpProperties, 'SetProperties', [(props['subject-contact'], 42)]) q.expect('dbus-error', name=cs.PERMISSION_DENIED)
def join(q, bus, conn): call_async(q, conn.Requests, "CreateChannel", { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.ROOM_NAME: CHANNEL, }) q.expect('stream-JOIN') event = q.expect('dbus-return', method='CreateChannel') path, props = event.value return wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['Destroyable', 'Messages'])
def test(q, bus, conn, stream): def send_own_message(to, text): iq = elem_iq(stream, 'set', from_='chat.facebook.com')(elem(NS_FACEBOOK_MESSAGES, 'own-message', to=to, self='false')( elem('body')(text))) stream.send(iq) q.expect('stream-iq', iq_type='result', iq_id=iq['id']) # First, test receiving an own-message stanza for a message sent to a # contact we have an open channel for. jid = '*****@*****.**' _, path, props = conn.Requests.EnsureChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, }) channel = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['Messages']) handle = props[cs.TARGET_HANDLE] text = u'omg omg its ur birthdayy <3 <3 xoxoxoxo' send_own_message(to=jid, text=text) e = q.expect('dbus-signal', signal='MessageReceived') message, = e.args assertLength(1, message) header = message[0] assertEquals(handle, header['message-sender']) assertEquals(cs.MT_DELIVERY_REPORT, header['message-type']) assertEquals(cs.DELIVERY_STATUS_ACCEPTED, header['delivery-status']) assertContains('delivery-echo', header) echo = header['delivery-echo'] echo_header, echo_body = echo assertEquals(conn.Properties.Get(cs.CONN, "SelfHandle"), echo_header['message-sender']) assertEquals('text/plain', echo_body['content-type']) assertEquals(text, echo_body['content']) channel.Text.AcknowledgePendingMessages([header['pending-message-id']]) channel.Close() # Now test receiving an own-message stanza for a message sent to a contact # we don't have a channel open for. It should be ignored (but acked). This # is consistent with delivery failure reports. q.forbid_events([EventPattern('dbus-signal', signal='MessageReceived')]) send_own_message(to='*****@*****.**', text=u'please ignore this message') sync_dbus(bus, q, conn)
def test(q, bus, conn, stream): def send_own_message(to, text): iq = elem_iq(stream, 'set', from_='chat.facebook.com')( elem(NS_FACEBOOK_MESSAGES, 'own-message', to=to, self='false')( elem('body')(text) ) ) stream.send(iq) q.expect('stream-iq', iq_type='result', iq_id=iq['id']) # First, test receiving an own-message stanza for a message sent to a # contact we have an open channel for. jid = '*****@*****.**' _, path, props = conn.Requests.EnsureChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, }) channel = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['Messages']) handle = props[cs.TARGET_HANDLE] text = u'omg omg its ur birthdayy <3 <3 xoxoxoxo' send_own_message(to=jid, text=text) e = q.expect('dbus-signal', signal='MessageReceived') message, = e.args assertLength(1, message) header = message[0] assertEquals(handle, header['message-sender']) assertEquals(cs.MT_DELIVERY_REPORT, header['message-type']) assertEquals(cs.DELIVERY_STATUS_ACCEPTED, header['delivery-status']) assertContains('delivery-echo', header) echo = header['delivery-echo'] echo_header, echo_body = echo assertEquals(conn.GetSelfHandle(), echo_header['message-sender']) assertEquals('text/plain', echo_body['content-type']) assertEquals(text, echo_body['content']) channel.Text.AcknowledgePendingMessages([header['pending-message-id']]) channel.Close() # Now test receiving an own-message stanza for a message sent to a contact # we don't have a channel open for. It should be ignored (but acked). This # is consistent with delivery failure reports. q.forbid_events([EventPattern('dbus-signal', signal='MessageReceived')]) send_own_message(to='*****@*****.**', text=u'please ignore this message') sync_dbus(bus, q, conn)
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.Properties.Get(cs.CONN, "SelfHandle") self_name = conn.inspect_contact_sync(self_handle) # 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) e = q.expect('dbus-signal', signal='NewChannels') channels = e.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 = wrap_channel(bus.get_object(conn.bus_name, path), 'DBusTube') tube_props = tube_chan.Properties.GetAll(cs.CHANNEL_IFACE_TUBE, byte_arrays=True) assert tube_props['State'] == cs.TUBE_CHANNEL_STATE_NOT_OFFERED # try to offer using a wrong access control try: tube_chan.DBusTube.Offer(sample_parameters, cs.SOCKET_ACCESS_CONTROL_PORT) except dbus.DBusException, e: assertEquals(e.get_dbus_name(), cs.INVALID_ARGUMENT)
def text_channel(q, bus, conn, method, jid, presence=True): request_text_channel(q, bus, conn, method, jid) e, _ = q.expect_many(EventPattern('dbus-return', method=method), EventPattern('dbus-signal', signal='NewChannels')) # sigh if method == 'EnsureChannel': path = e.value[1] else: path = e.value[0] text_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') return (text_chan,) + e.value
def test(q, bus, conn, stream): conn.Connect() q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]), EventPattern('irc-connected')) e = q.expect('dbus-signal', signal='NewChannels') channels = e.args[0] path, props = channels[0] channel = wrap_channel(bus.get_object(conn.bus_name, path), cs.CHANNEL_TYPE_SERVER_TLS_CONNECTION) channel.Close() q.expect('dbus-signal', signal='StatusChanged', args=[2, 2])
def stream_tube(q, bus, conn, method, jid): request_stream_tube(q, bus, conn, method, jid) e, _ = q.expect_many(EventPattern('dbus-return', method=method), EventPattern('dbus-signal', signal='NewChannels')) # sigh if method == 'EnsureChannel': path = e.value[1] else: path = e.value[0] tube_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamTube') return (tube_chan,) + e.value
def test(q, bus, conn, stream): conn.Connect() event = q.expect('stream-iq', query_ns=ns.ROSTER) # send back empty roster event.stanza['type'] = 'result' stream.send(event.stanza) while True: event = q.expect('dbus-signal', signal='NewChannel') path, type, handle_type, handle, suppress_handler = event.args if type != cs.CHANNEL_TYPE_CONTACT_LIST: continue chan_name = conn.InspectHandles(handle_type, [handle])[0] if chan_name == 'subscribe': break chan = wrap_channel(bus.get_object(conn.bus_name, path), 'ContactList') assertLength(0, chan.Group.GetMembers()) # request subscription handle = conn.RequestHandles(cs.HT_CONTACT, ['*****@*****.**'])[0] chan.Group.AddMembers([handle], '') event = q.expect('stream-iq', iq_type='set', query_ns=ns.ROSTER) item = event.query.firstChildElement() assertEquals('*****@*****.**', item["jid"]) acknowledge_iq(stream, event.stanza) event = q.expect('stream-presence', presence_type='subscribe') presence = domish.Element(('jabber:client', 'presence')) presence['from'] = '*****@*****.**' presence['type'] = 'subscribed' stream.send(presence) q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=['', [handle], [], [], [], 0, 0]), EventPattern('stream-presence'), )
def test(q, bus, conn, stream): path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: GUYBRUSH, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') # Let's start out with an empty roster, eh? e = q.expect('stream-iq', iq_type='get', query_ns=ns.ROSTER) e.stanza['type'] = 'result' stream.send(e.stanza) report_received_on_open_channel(q, bus, conn, stream, chan) report_ignored_without_channel(q, bus, conn, stream) not_sending_request_to_contact(q, bus, conn, stream, chan) # FIXME: This test is disabled because of stupidity in the presence cache. # See the comment in receipts_conceivably_supported(). #sending_request_to_presenceless_contact(q, bus, conn, stream, chan) sending_request_to_cappy_contact(q, bus, conn, stream, chan) replying_to_requests(q, bus, conn, stream)
def incoming(jp, q, bus, conn, stream): remote_jid = 'skinny.fists@heaven/antennas' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] for a, v in [("audio1", None), (None, "video1"), ("audio1", "video1")]: if v!= None and not jp.can_do_video(): continue if a == None and v != None and not jp.can_do_video_only(): continue jt.incoming_call(audio=a, video=v) e = q.expect('dbus-signal', signal='NewChannels', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args[0][0][1].values()) chans = e.args[0] assertLength(1, chans) path, props = chans[0] assertEquals(cs.CHANNEL_TYPE_STREAMED_MEDIA, props[cs.CHANNEL_TYPE]) assertEquals(a != None, props[cs.INITIAL_AUDIO]) assertEquals(v != None, props[cs.INITIAL_VIDEO]) # FIXME: This doesn't check non-Google contacts that can only do one # media type, as such contacts as simulated by JingleTest2 can always # do both. assertEquals(not jp.can_do_video() or not jp.can_do_video_only(), props[cs.IMMUTABLE_STREAMS]) chan = wrap_channel(bus.get_object(conn.bus_name, path), cs.CHANNEL_TYPE_STREAMED_MEDIA) chan.Close()
def test(q, bus, conn): self_name = 'testsuite' + '@' + avahitest.get_host_name() conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L]) # FIXME: this is a hack to be sure to have all the contact list channels # announced so they won't interfere with the muc ones announces. wait_for_contact_list(q, conn) # check if we can request tube channels properties = conn.Properties.GetAll(cs.CONN_IFACE_REQUESTS) assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM}, [cs.TARGET_HANDLE, cs.TARGET_ID, cs.STREAM_TUBE_SERVICE], ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] # create muc channel using new API call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: 'my-second-room', cs.STREAM_TUBE_SERVICE: 'loldongs', }) ret, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) tube_path = ret.value[0] chan = wrap_channel(bus.get_object(conn.bus_name, tube_path), 'StreamTube') tube_props = ret.value[1] assert tube_props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE assert tube_props[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM assert tube_props[cs.TARGET_ID] == 'my-second-room' assert tube_props[cs.REQUESTED] == True assert tube_props[cs.INITIATOR_HANDLE] == conn.Properties.Get(cs.CONN, "SelfHandle") assert tube_props[cs.INITIATOR_ID] == self_name # text and tube channels are announced channels = new_sig.args[0] assert len(channels) == 1 handle = tube_props[cs.TARGET_HANDLE] path, props = channels[0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE assert path == tube_path assert props == tube_props assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM assert props[cs.TARGET_HANDLE] == handle assert props[cs.TARGET_ID] == 'my-second-room' assert props[cs.INITIATOR_HANDLE] == conn.Properties.Get(cs.CONN, "SelfHandle") assert props[cs.INITIATOR_ID] == self_name # ensure the same channel # TODO: the muc channel doesn't bother to look at existing tubes # before creating a new one. once that's fixed, uncomment this. # yours, ensured_path, _ = conn.Requests.EnsureChannel( # { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, # cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, # cs.TARGET_HANDLE: handle, # cs.STREAM_TUBE_SERVICE: 'loldongs', # }) # assert not yours # assert ensured_path == tube_path, (ensured_path, tube_path) conn.Disconnect() q.expect_many( EventPattern('dbus-signal', signal='Closed', path=tube_path), EventPattern('dbus-signal', signal='ChannelClosed', args=[tube_path]), EventPattern('dbus-signal', signal='StatusChanged', args=[2, 1]), )
def test(q, bus, conn, stream): self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") jid = '*****@*****.**' full_jid = '[email protected]/Foo' foo_handle = conn.get_contact_handle_sync(jid) path = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: foo_handle, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') presence = make_presence(full_jid, status='hello', caps={ 'node': 'http://telepathy.freedesktop.org/homeopathy', 'ver': '0.1', }) stream.send(presence) version_event = q.expect( 'stream-iq', to=full_jid, query_ns=ns.DISCO_INFO, query_node='http://telepathy.freedesktop.org/homeopathy#0.1') result = make_result_iq(stream, version_event.stanza) query = result.firstChildElement() feature = query.addElement('feature') feature['var'] = ns.CHAT_STATES stream.send(result) sync_stream(q, stream) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(self_handle, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_INACTIVE, states.get(foo_handle, cs.CHAT_STATE_INACTIVE)) # Receiving chat states: # Composing... stream.send(make_message(full_jid, state='composing')) changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(foo_handle, handle) assertEquals(cs.CHAT_STATE_COMPOSING, state) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(self_handle, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_COMPOSING, states.get(foo_handle, cs.CHAT_STATE_INACTIVE)) # Message! stream.send(make_message(full_jid, body='hello', state='active')) changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(foo_handle, handle) assertEquals(cs.CHAT_STATE_ACTIVE, state) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(self_handle, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_ACTIVE, states.get(foo_handle, cs.CHAT_STATE_INACTIVE)) # Assert that a redundant chat-state change doesn't emit a signal forbidden = [ EventPattern('dbus-signal', signal='ChatStateChanged', args=[foo_handle, cs.CHAT_STATE_ACTIVE]) ] q.forbid_events(forbidden) m = domish.Element((None, 'message')) m['from'] = '[email protected]/Foo' m['type'] = 'chat' m.addElement((ns.CHAT_STATES, 'active')) m.addElement('body', content='hello') stream.send(m) sync_dbus(bus, q, conn) sync_stream(q, stream) q.unforbid_events(forbidden) # Sending chat states: # Composing... chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) stream_message = q.expect('stream-message') check_state_notification(stream_message.stanza, 'composing') states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_COMPOSING, states.get(self_handle, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_ACTIVE, states.get(foo_handle, cs.CHAT_STATE_INACTIVE)) # XEP 0085: # every content message SHOULD contain an <active/> notification. chan.send_msg_sync('hi.') stream_message = q.expect('stream-message') elem = stream_message.stanza assertEquals('chat', elem['type']) check_state_notification(elem, 'active', allow_body=True) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_ACTIVE, states.get(self_handle, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_ACTIVE, states.get(foo_handle, cs.CHAT_STATE_INACTIVE)) def is_body(e): if e.name == 'body': assert e.children[0] == u'hi.', e.toXml() return True return False assert len([x for x in elem.elements() if is_body(x)]) == 1, elem.toXml() # Close the channel without acking the received message. The peer should # get a <gone/> notification, and the channel should respawn. chan.Close() gone, _ = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Closed'), ) check_state_notification(gone.stanza, 'gone') # Reusing the proxy object because we happen to know it'll be at the same # path... # Destroy the channel. The peer shouldn't get a <gone/> notification, since # we already said we were gone and haven't sent them any messages to the # contrary. es = [EventPattern('stream-message')] q.forbid_events(es) chan.Destroyable.Destroy() sync_stream(q, stream) # Make the channel anew. path = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: foo_handle, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') # Close it immediately; the peer should again not get a <gone/> # notification, since we haven't sent any notifications on that channel. chan.Close() sync_stream(q, stream) q.unforbid_events(es) # XEP-0085 §5.1 defines how to negotiate support for chat states with a # contact in the absence of capabilities. This is useful when talking to # invisible contacts, for example. # First, if we receive a message from a contact, containing an <active/> # notification, they support chat states, so we should send them. jid = '*****@*****.**' full_jid = jid + '/GTalk' path = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') stream.send(make_message(full_jid, body='i am invisible', state='active')) changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) assertEquals(cs.CHAT_STATE_ACTIVE, changed.args[1]) # We've seen them send a chat state notification, so we should send them # notifications when the UI tells us to. chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) stream_message = q.expect('stream-message', to=full_jid) check_state_notification(stream_message.stanza, 'composing') changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(cs.CHAT_STATE_COMPOSING, state) assertEquals(self_handle, handle) chan.send_msg_sync('very convincing') stream_message = q.expect('stream-message', to=full_jid) check_state_notification(stream_message.stanza, 'active', allow_body=True) # Now, test the case where we start the negotiation, and the contact # turns out to support chat state notifications. jid = '*****@*****.**' full_jid = jid + '/GTalk' path = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') # We shouldn't send any notifications until we actually send a message. # But ChatStateChanged is still emitted locally e = EventPattern('stream-message', to=jid) q.forbid_events([e]) for i in [ cs.CHAT_STATE_ACTIVE, cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_INACTIVE ]: chan.ChatState.SetChatState(i) changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(i, state) assertEquals(self_handle, handle) sync_stream(q, stream) q.unforbid_events([e]) # When we send a message, say we're active. chan.send_msg_sync('is anyone there?') stream_message = q.expect('stream-message', to=jid) check_state_notification(stream_message.stanza, 'active', allow_body=True) # The D-Bus property changes, too changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(cs.CHAT_STATE_ACTIVE, state) assertEquals(self_handle, handle) # We get a notification back from our contact. stream.send(make_message(full_jid, state='composing')) # Wait until gabble tells us the chat-state of the remote party has # changed so we know gabble knows chat state notification are supported changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(cs.CHAT_STATE_COMPOSING, state) assertNotEquals(foo_handle, handle) # So now we know they support notification, so should send notifications. chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) # This doesn't check whether we're sending to the bare jid, or the # jid+resource. In fact, the notification is sent to the bare jid, because # we only update which jid we send to when we actually receive a message, # not when we receive a notification. wjt thinks this is less surprising # than the alternative: # # • I'm talking to you on my N900, and signed in on my laptop; # • I enter one character in a tab to you on my laptop, and then delete # it; # • Now your messages to me appear on my laptop (until I send you another # one from my N900)! stream_message = q.expect('stream-message') check_state_notification(stream_message.stanza, 'composing') # The D-Bus property changes, too changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(cs.CHAT_STATE_COMPOSING, state) assertEquals(self_handle, handle) # But! Now they start messaging us from a different client, which *doesn't* # support notifications. other_jid = jid + '/Library' stream.send(make_message(other_jid, body='grr, library computers')) q.expect('dbus-signal', signal='MessageReceived') # Okay, we should stop sending typing notifications. e = EventPattern('stream-message', to=other_jid) q.forbid_events([e]) for i in [ cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE ]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) # Now, test the case where we start the negotiation, and the contact # does not support chat state notifications jid = '*****@*****.**' full_jid = jid + '/Nonsense' path = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') # We shouldn't send any notifications until we actually send a message. e = EventPattern('stream-message', to=jid) q.forbid_events([e]) for i in [ cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE ]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) # When we send a message, say we're active. chan.send_msg_sync('#n900 #maemo #zomg #woo #yay http://bit.ly/n900') stream_message = q.expect('stream-message', to=jid) check_state_notification(stream_message.stanza, 'active', allow_body=True) # They reply without a chat state. stream.send(make_message(full_jid, body="posted.")) q.expect('dbus-signal', signal='MessageReceived') # Okay, we shouldn't send any more. e = EventPattern('stream-message', to=other_jid) q.forbid_events([e]) for i in [ cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE ]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) chan.send_msg_sync('@stephenfry simmer down') message = q.expect('stream-message') states = [x for x in message.stanza.elements() if x.uri == ns.CHAT_STATES] assertLength(0, states)
def test(q, bus, conn, stream, send_early_description_info=False): jp = JingleProtocol031() jt2 = JingleTest2(jp, conn, q, stream, 'test@localhost', '[email protected]/Foo') jt2.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, ["[email protected]/Foo"])[0] # Remote end calls us jt2.incoming_call() # FIXME: these signals are not observable by real clients, since they # happen before NewChannels. # The caller is in members e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [remote_handle], [], [], [], 0, 0]) # We're pending because of remote_handle e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [], [], [self_handle], [], remote_handle, cs.GC_REASON_INVITED]) chan = wrap_channel(bus.get_object(conn.bus_name, e.path), 'StreamedMedia') # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' if send_early_description_info: """ Regression test for a bug where Gabble would crash if you sent it description-info before calling Ready() on the relevant StreamHandler, and then for a bug where Gabble would never accept the call if a description-info was received before all StreamHandlers were Ready(). """ node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'description-info', [ jp.Content('stream1', 'initiator', 'both', jp.Description('audio', [ ])) ]) ]) stream.send(jp.xml(node)) sync_stream(q, stream) session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() chan.Group.AddMembers([self_handle], 'accepted') # S-E gets notified about a newly-created stream e = q.expect('dbus-signal', signal='NewStreamHandler') id1 = e.args[1] stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') # We are now in members too e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [self_handle], [], [], [], self_handle, cs.GC_REASON_NONE]) # we are now both in members members = chan.Group.GetMembers() assert set(members) == set([self_handle, remote_handle]), members local_codecs = [('GSM', 3, 8000, {}), ('PCMA', 8, 8000, {'helix':'woo yay'}), ('PCMU', 0, 8000, {}) ] local_codecs_dbus = jt2.dbusify_codecs_with_params(local_codecs) stream_handler.NewNativeCandidate("fake", jt2.get_remote_transports_dbus()) stream_handler.Ready(local_codecs_dbus) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) stream_handler.CodecsUpdated(local_codecs_dbus) local_codecs = [('GSM', 3, 8000, {}), ('PCMA', 8, 8000, {'gstreamer':'rock on'}), ('PCMU', 0, 8000, {}) ] local_codecs_dbus = jt2.dbusify_codecs_with_params(local_codecs) stream_handler.CodecsUpdated(local_codecs_dbus) # First IQ is transport-info; also, we expect to be told what codecs the # other end wants. e, src = q.expect_many( EventPattern('stream-iq', predicate=jp.action_predicate('transport-info')), EventPattern('dbus-signal', signal='SetRemoteCodecs') ) assertEquals('[email protected]/Foo', e.query['initiator']) assert jt2.audio_codecs == [ (name, id, rate, parameters) for id, name, type, rate, channels, parameters in unwrap(src.args[0]) ], \ (jt2.audio_codecs, unwrap(src.args[0])) stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, []))) # S-E reports codec intersection, after which gabble can send acceptance stream_handler.SupportedCodecs(local_codecs_dbus) # Second one is session-accept e = q.expect('stream-iq', predicate=jp.action_predicate('session-accept')) # farstream is buggy, and tells tp-fs to tell Gabble to change the third # codec's clockrate. This isn't legal, so Gabble says no. new_codecs = [ ('GSM', 3, 8000, {}), ('PCMA', 8, 8000, {}), ('PCMU', 0, 4000, {}) ] call_async(q, stream_handler, 'CodecsUpdated', jt2.dbusify_codecs(new_codecs)) event = q.expect('dbus-error', method='CodecsUpdated') assert event.error.get_dbus_name() == cs.INVALID_ARGUMENT, \ event.error.get_dbus_name() # With its tail between its legs, tp-fs decides it wants to add some # parameters to the first two codecs, not changing the third. new_codecs = [ ('GSM', 3, 8000, {'type': 'banana'}), ('PCMA', 8, 8000, {'helix': 'BUFFERING'}), ('PCMU', 0, 8000, {}) ] stream_handler.CodecsUpdated(jt2.dbusify_codecs_with_params(new_codecs)) audio_content = jt2.audio_names[0] e = q.expect('stream-iq', iq_type='set', predicate=lambda x: xpath.queryForNodes("/iq/jingle[@action='description-info']", x.stanza)) payload_types = xpath.queryForNodes( "/iq/jingle/content[@name='%s']/description/payload-type" % audio_content, e.stanza) # Gabble SHOULD only include the changed codecs in description-info assert len(payload_types) == 2, payload_types payload_types_tupled = [ (pt['name'], int(pt['id']), int(pt['clockrate']), extract_params(pt)) for pt in payload_types ] assert sorted(payload_types_tupled) == sorted(new_codecs[0:2]), \ (payload_types_tupled, new_codecs[0:2]) # The remote end decides it wants to change the number of channels in the # third codec. This is not meant to happen, so Gabble should send it an IQ # error back. node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'description-info', [ jp.Content(audio_content, 'initiator', 'both', jp.Description('audio', [ jp.PayloadType('PCMU', '1600', '0') ])) ]) ]) stream.send(jp.xml(node)) q.expect('stream-iq', iq_type='error', predicate=lambda x: x.stanza['id'] == node[2]['id']) # Instead, the remote end decides to add a parameter to the third codec. new_codecs = [ ('GSM', 3, 8000, {}), ('PCMA', 8, 8000, {}), ('PCMU', 0, 8000, {'choppy': 'false'}), ] # As per the XEP, it only sends the ones which have changed. c = new_codecs[2] node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'description-info', [ jp.Content(audio_content, 'initiator', 'both', jp.Description('audio', [ jp.PayloadType(c[0], str(c[2]), str(c[1]), c[3]) ])) ]) ]) stream.send(jp.xml(node)) # Gabble should patch its idea of the remote codecs with the update it just # got, and emit SetRemoteCodecs for them all. e = q.expect('dbus-signal', signal='SetRemoteCodecs') new_codecs_dbus = unwrap(jt2.dbusify_codecs_with_params(new_codecs)) announced = unwrap(e.args[0]) assert new_codecs_dbus == announced, (new_codecs_dbus, announced) # We close the session by removing the stream chan.StreamedMedia.RemoveStreams([id1]) e = q.expect('stream-iq', iq_type='set', predicate=lambda x: xpath.queryForNodes("/iq/jingle[@action='session-terminate']", x.stanza))
def test(q, bus, conn, stream): room = '*****@*****.**' call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: room }) expected_muc_jid = '%s/%s' % (room, 'test') q.expect('stream-presence', to=expected_muc_jid) # tell gabble the room needs a password denied = \ elem('jabber:client', 'presence', from_=expected_muc_jid, type='error')( elem(ns.MUC, 'x'), elem('error', type='auth')( elem(ns.STANZA, 'not-authorized'), ), ) stream.send(denied) cc, _, _ = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels'), EventPattern('dbus-signal', signal='PasswordFlagsChanged', args=[cs.PASSWORD_FLAG_PROVIDE, 0])) chan = wrap_channel(bus.get_object(conn.bus_name, cc.value[0]), 'Text') flags = chan.Password.GetPasswordFlags() assertEquals(cs.PASSWORD_FLAG_PROVIDE, flags) call_async(q, chan.Password, 'ProvidePassword', 'brand new benz') expect_attempt(q, expected_muc_jid, 'brand new benz') # Try again while the first attempt is outstanding. Gabble should say no. call_async(q, chan.Password, 'ProvidePassword', 'faster faster') q.expect('dbus-error', method='ProvidePassword') # Sorry, wrong password. stream.send(denied) ret = q.expect('dbus-return', method='ProvidePassword') assert not ret.value[0] call_async(q, chan.Password, 'ProvidePassword', 'bougie friends') expect_attempt(q, expected_muc_jid, 'bougie friends') # Well, this may be the right password, but actually that nick is in use. presence = elem('presence', from_=expected_muc_jid, type='error')( elem(ns.MUC, 'x'), elem('error', type='cancel')( elem(ns.STANZA, 'conflict'), )) stream.send(presence) # Okay, so Gabble tries again, with a new JID *and the same password*. expected_muc_jid = expected_muc_jid + '_' expect_attempt(q, expected_muc_jid, 'bougie friends') # Hey this worked. stream.send(make_muc_presence('none', 'participant', room, 'test_')) ret, _ = q.expect_many( EventPattern('dbus-return', method='ProvidePassword'), EventPattern('dbus-signal', signal='PasswordFlagsChanged', args=[0, cs.PASSWORD_FLAG_PROVIDE])) assert ret.value[0]
def test(jp, q, bus, conn, stream): # this test uses multiple streams if not jp.is_modern_jingle(): return remote_jid = '[email protected]/Foo' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] chan_path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) chan = wrap_channel(bus.get_object(conn.bus_name, chan_path), 'StreamedMedia', ['MediaSignalling', 'Group', 'CallState', 'DTMF']) chan_props = chan.Properties.GetAll(cs.CHANNEL) assert cs.CHANNEL_IFACE_DTMF in chan_props['Interfaces'], \ chan_props['Interfaces'] assertEquals('', chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'InitialTones')) assertEquals('', chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'DeferredTones')) chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') audio_path = e.args[0] stream_handler = make_channel_proxy(conn, audio_path, 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) stream.send(make_result_iq(stream, e.stanza)) jt.parse_session_initiate(e.query) jt.accept() # Gabble tells s-e to start sending q.expect('dbus-signal', signal='SetStreamSending', args=[True], path=audio_path) chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) e = q.expect('dbus-signal', signal='NewStreamHandler') audio2_path = e.args[0] # The Stream_ID is specified to be ignored; we use 666 here. call_async(q, chan.DTMF, 'StartTone', 666, 3) q.expect_many( EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio2_path), EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='SendingTones', args=['3'], path=chan_path), EventPattern('dbus-return', method='StartTone'), ) call_async(q, chan.DTMF, 'StopTone', 666) q.expect_many( EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio2_path), EventPattern('dbus-signal', signal='StoppedTones', args=[True], path=chan_path), EventPattern('dbus-return', method='StopTone'), ) call_async(q, chan.DTMF, 'MultipleTones', '123w*#') q.expect_many( EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio_path, args=[1]), EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio2_path, args=[1]), EventPattern('dbus-signal', signal='SendingTones', args=['123w*#'], path=chan_path), EventPattern('dbus-return', method='MultipleTones'), ) q.expect_many( EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio2_path), ) q.expect_many( EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio_path, args=[2]), EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio2_path, args=[2]), ) q.expect_many( EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio2_path), ) q.expect_many( EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio_path, args=[3]), EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio2_path, args=[3]), ) q.expect_many( EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio2_path), EventPattern('dbus-signal', signal='StoppedTones', args=[False], path=chan_path), EventPattern('dbus-signal', signal='TonesDeferred', args=['*#']), ) assertEquals('*#', chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'DeferredTones')) forbidden = [EventPattern('dbus-signal', signal='StartTelephonyEvent', args=[9])] q.forbid_events(forbidden) # This is technically a race condition, but this dialstring is almost # certainly long enough that the Python script will win the race, i.e. # cancel before Gabble processes the whole dialstring. call_async(q, chan.DTMF, 'MultipleTones', '1,1' * 100) q.expect('dbus-return', method='MultipleTones') call_async(q, chan.DTMF, 'MultipleTones', '9') q.expect('dbus-error', method='MultipleTones', name=cs.SERVICE_BUSY) call_async(q, chan.DTMF, 'StartTone', 666, 9) q.expect('dbus-error', method='StartTone', name=cs.SERVICE_BUSY) call_async(q, chan.DTMF, 'StopTone', 666) q.expect_many( EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio2_path), EventPattern('dbus-signal', signal='StoppedTones', args=[True], path=chan_path), EventPattern('dbus-return', method='StopTone'), ) # emitting any sound resets TonesDeferred assertEquals('', chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'DeferredTones')) q.unforbid_events(forbidden) chan.Group.RemoveMembers([self_handle], 'closed') e = q.expect('dbus-signal', signal='Closed', path=chan_path)
def test(q, bus, conn, stream): # <wjt> I need a random name generator # <fledermaus> Macro-Variable Spin Gel contact = '*****@*****.**' contact_a = '%s/n810' % contact contact_b = '%s/laptop' % contact path, _ = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: contact, }) chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') # When we start a conversation, Gabble should send to the bare JID. chan.send_msg_sync('hey, you around?') q.expect('stream-message', to=contact) # A particular resource replies. m = domish.Element((None, 'message')) m['from'] = contact_a m['type'] = 'chat' m.addElement('body', content="i'm on a beach at Gran Canaria!") stream.send(m) q.expect('dbus-signal', signal='MessageReceived') # Now that we got a reply from a particular resource, Gabble should reply # there. chan.send_msg_sync('nice') q.expect('stream-message', to=contact_a) # Now another resource messages us m = domish.Element((None, 'message')) m['from'] = contact_b m['type'] = 'chat' m.addElement('body', content="I brought my laptop to the Empathy hackfest") stream.send(m) q.expect('dbus-signal', signal='MessageReceived') # Gabble should have updated the resource it's sending to. chan.send_msg_sync("don't get sand in the keyboard") e = q.expect('stream-message', to=contact_b) # But actually that resource has gone offline: m = e.stanza m['from'] = contact_b m['type'] = 'error' del m['to'] err = m.addElement((None, 'error')) err['type'] = 'cancel' err.addElement((ns.STANZA, 'item-not-found')) stream.send(m) q.expect('dbus-signal', signal='MessageReceived') # So as a result, Gabble should send the next message to the bare JID. chan.send_msg_sync("... i guess my warning was too late") q.expect('stream-message', to=contact)
def test(q, bus, conn, stream): self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") # When Gabble initially requests its avatar from the server, it discovers # it has none. expect_and_handle_get_vcard(q, stream) handle, signalled_token = q.expect('dbus-signal', signal='AvatarUpdated').args assertEquals(self_handle, handle) assertEquals('', signalled_token) # The user sets an avatar. call_async(q, conn.Avatars, 'SetAvatar', AVATAR_1_DATA, AVATAR_1_MIME_TYPE) expect_and_handle_get_vcard(q, stream) expect_and_handle_set_vcard(q, stream) # It's signalled on D-Bus … set_ret, avatar_updated = q.expect_many( EventPattern('dbus-return', method='SetAvatar'), EventPattern('dbus-signal', signal='AvatarUpdated'), ) returned_token, = set_ret.value handle, signalled_token = avatar_updated.args assertEquals(self_handle, handle) assertEquals(returned_token, signalled_token) # … and also on XMPP. broadcast = q.expect('stream-presence', to=None) broadcast_hash = extract_hash_from_presence(broadcast.stanza) assertEquals(AVATAR_1_SHA1, broadcast_hash) # If applications ask Gabble for information about the user's own avatar, # it should be able to answer. (Strictly speaking, expecting Gabble to know # the avatar data is risky because Gabble discards cached vCards after a # while, but we happen to know it takes 20 seconds or so for that to # happen.) known = conn.Avatars.GetKnownAvatarTokens([self_handle]) assertEquals({self_handle: signalled_token}, known) conn.Avatars.RequestAvatars([self_handle]) retrieved = q.expect('dbus-signal', signal='AvatarRetrieved') handle, token, data, mime_type = retrieved.args assertEquals(self_handle, handle) assertEquals(signalled_token, token) assertEquals(AVATAR_1_DATA, data) assertEquals(AVATAR_1_MIME_TYPE, mime_type) # Well, that was quite easy. How about we join a MUC? XEP-0153 §4.1 says: # If a client supports the protocol defined herein, it […] SHOULD # also include the update child in directed presence stanzas (e.g., # directed presence sent when joining Multi-User Chat [5] rooms). # — http://xmpp.org/extensions/xep-0153.html#bizrules-presence join_event = try_to_join_muc(q, bus, conn, stream, MUC) directed_hash = extract_hash_from_presence(join_event.stanza) assertEquals(AVATAR_1_SHA1, directed_hash) # There are two others in the MUC: fredrik has no avatar, wendy has an # avatar. We, of course, have our own avatar. stream.send(make_muc_presence('none', 'participant', MUC, 'fredrik')) stream.send( make_muc_presence('none', 'participant', MUC, 'wendy', photo=AVATAR_2_SHA1)) stream.send( make_muc_presence('owner', 'moderator', MUC, 'test', photo=AVATAR_1_SHA1)) path, _ = q.expect('dbus-return', method='CreateChannel').value chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') members = chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'Members') assertLength(3, members) fredrik, wendy, muc_self_handle = conn.get_contact_handles_sync( ['%s/%s' % (MUC, x) for x in ["fredrik", "wendy", "test"]]) known = conn.Avatars.GetKnownAvatarTokens(members) # <https://bugs.freedesktop.org/show_bug.cgi?id=32017>: this assertion # failed, the MUC self handle's token was the empty string. assertEquals(AVATAR_1_SHA1, known[muc_self_handle]) assertEquals(AVATAR_2_SHA1, known[wendy]) assertEquals('', known[fredrik]) # 'k, cool. Wendy loves our avatar and switches to it. stream.send( make_muc_presence('none', 'participant', MUC, 'wendy', photo=AVATAR_1_SHA1)) # Okay this is technically assuming that we just expose the SHA1 sums # directly which is not guaranteed … but we do. q.expect('dbus-signal', signal='AvatarUpdated', args=[wendy, AVATAR_1_SHA1]) # Fredrik switches too. stream.send( make_muc_presence('none', 'participant', MUC, 'fredrik', photo=AVATAR_1_SHA1)) q.expect('dbus-signal', signal='AvatarUpdated', args=[fredrik, AVATAR_1_SHA1]) # And we switch to some other avatar. Gabble should update its vCard, and # then update its MUC presence (which the test, acting as the MUC server, # must echo). call_async(q, conn.Avatars, 'SetAvatar', AVATAR_2_DATA, AVATAR_2_MIME_TYPE) expect_and_handle_get_vcard(q, stream) expect_and_handle_set_vcard(q, stream) muc_presence = q.expect('stream-presence', to=('%s/test' % MUC)) directed_hash = extract_hash_from_presence(muc_presence.stanza) stream.send( make_muc_presence('owner', 'moderator', MUC, 'test', photo=directed_hash)) # Gabble should signal an avatar update for both our global self-handle and # our MUC self-handle. (The first of these of course does not need to wait # for the MUC server to echo our presence.) q.expect_many( EventPattern('dbus-signal', signal='AvatarUpdated', args=[self_handle, AVATAR_2_SHA1]), EventPattern('dbus-signal', signal='AvatarUpdated', args=[muc_self_handle, AVATAR_2_SHA1]), )
def test_gtalk_weirdness(q, bus, conn, stream, room_jid): """ There's a strange bug in the Google Talk MUC server where it sends the <conflict/> stanza twice. This has been reported to their server team; but in any case it triggered a crazy bug in Gabble, so here's a regression test. """ # Implementation detail: Gabble uses the first part of your jid (if you # don't have an alias) as your room nickname, and appends an underscore a # few times before giving up. jids = ['%s/test%s' % (room_jid, x) for x in ['', '_', '__']] member, member_, member__ = jids # Gabble should never get as far as trying to join as 'test__' since # joining as 'test_' will succeed. q.forbid_events([EventPattern('stream-presence', to=member__)]) call_async( q, conn.Requests, 'CreateChannel', dbus.Dictionary( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: room_jid, }, signature='sv')) # Gabble first tries to join as test q.expect('stream-presence', to=member) # Google Talk says no from 'test', twice. presence = elem('presence', from_=member, type='error')(elem(ns.MUC, 'x'), elem('error', type='cancel')(elem( ns.STANZA, 'conflict'), )) stream.send(presence) stream.send(presence) # Gabble should try to join again as test_ q.expect('stream-presence', to=member_) # Since 'test_' is not in use in the MUC, joining should succeed. According # to XEP-0045 §7.1.3 <http://xmpp.org/extensions/xep-0045.html#enter-pres>: # The service MUST first send the complete list of the existing occupants # to the new occupant and only then send the new occupant's own presence # to the new occupant # but groupchat.google.com cheerfully violates this. stream.send(make_muc_presence('none', 'participant', room_jid, 'test_')) # Here's some other random person, who owns the MUC. stream.send( make_muc_presence('owner', 'moderator', room_jid, 'foobar_gmail.com')) # And here's our hypothetical other self. stream.send(make_muc_presence('none', 'participant', room_jid, 'test')) # The Gabble bug makes this time out: because Gabble thinks it's joining as # test__ it ignores the presence for test_, since it's not flagged with # code='210' to say “this is you”. (This is acceptable behaviour by the # server: it only needs to include code='210' if it's assigned the client a # name other than the one it asked for. # # The forbidden stream-presence event above doesn't blow up here because # servicetest doesn't process events on the 'stream-*' queue at all when # we're not waiting for one. But during disconnection in the test clean-up, # the forbidden event is encountered and correctly flagged up. event = q.expect('dbus-return', method='CreateChannel') path, _ = event.value text_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') # As far as Gabble's concerned, the two other participants joined # immediately after we did. We can't request handles for them before we # try to join the MUC, because until we do so, Gabble doesn't know that # room_jid is a MUC, and so considers these three JIDs to be different # resources of the same contact. There is no race between this method # returning and MembersChangedDetailed firing, because libdbus reorders # messages when you make blocking calls. handle, handle_, handle__, foobar_handle = conn.get_contact_handles_sync( jids + ['%s/foobar_gmail.com' % room_jid]) q.expect('dbus-signal', signal='MembersChangedDetailed', predicate=lambda e: e.args[0:4] == [[foobar_handle], [], [], []]) q.expect('dbus-signal', signal='MembersChangedDetailed', predicate=lambda e: e.args[0:4] == [[handle], [], [], []]) group_props = text_chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP) assertEquals(handle_, group_props['SelfHandle']) assertSameSets([handle, handle_, foobar_handle], group_props['Members'])
def test(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0, 1]) alice_handle, bob_handle = conn.get_contact_handles_sync(['alice', 'bob']) call_async( q, conn.Requests, 'CreateChannel', { CHANNEL_TYPE: CHANNEL_TYPE_TEXT, TARGET_HANDLE_TYPE: HT_ROOM, TARGET_ID: room }) q.expect('stream-JOIN') event = q.expect('dbus-return', method='CreateChannel') path = event.value[0] channel = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['Subject2']) assertContains(CHANNEL_IFACE_SUBJECT, channel.Properties.Get(CHANNEL, 'Interfaces')) # No topic set subject_props = channel.Properties.GetAll(CHANNEL_IFACE_SUBJECT) assertEquals('', subject_props['Subject']) assertEquals(0x7fffffffffffffffL, subject_props['Timestamp']) assertEquals('', subject_props['Actor']) assertEquals(0, subject_props['ActorHandle']) # Before the topic arrives from the server, check that our API works okay. # FIXME: when we make SetSubject return asynchronously, this will need # revising. test_can_set(q, stream, channel) # We're told the channel's topic, and (in a separte message) who set it and # when. stream.sendMessage('332', stream.nick, room, ':Test123', prefix='idle.test.server') stream.sendMessage('333', stream.nick, room, 'bob', '1307802600', prefix='idle.test.server') # FIXME: signal these together, if possible. expect_subject_props_changed(q, {'Subject': 'Test123'}) expect_subject_props_changed(q, { 'Timestamp': 1307802600, 'Actor': 'bob', 'ActorHandle': bob_handle, }, exact_timestamp=True) # Another user changes the topic. stream.sendMessage('TOPIC', room, ':I am as high as a kite', prefix='alice') expect_subject_props_changed( q, { 'Subject': 'I am as high as a kite', 'Actor': 'alice', 'ActorHandle': alice_handle, 'Timestamp': 1234, }) # BIP omits the : for the trailing parameter if it's a single word, make # sure we pass that as well stream.sendMessage('TOPIC', room, 'badgers!', prefix='alice') expect_subject_props_changed( q, { 'Subject': 'badgers!', 'Actor': 'alice', 'ActorHandle': alice_handle, 'Timestamp': 1234, }) test_can_set(q, stream, channel) # Topic is read/write, if we get ops it should stay that way forbidden = [ EventPattern('dbus-signal', signal='PropertiesChanged', predicate=lambda e: e.args[0] == CHANNEL_IFACE_SUBJECT) ] q.forbid_events(forbidden) # Set ops, check that t flag becomes a no-op change_channel_mode(stream, '+o ' + stream.nick) change_channel_mode(stream, '+t') change_channel_mode(stream, '-t') change_channel_mode(stream, '-o ' + stream.nick) # Check that other flags don't cause issues change_channel_mode(stream, '+n') change_channel_mode(stream, '+n') change_channel_mode(stream, '+to ' + stream.nick) change_channel_mode(stream, '-to ' + stream.nick) sync_stream(q, stream) sync_dbus(bus, q, conn) q.unforbid_events(forbidden) # back to normal? test_can_set(q, stream, channel) # Check if setting ops gives us write access on +t channels change_channel_mode(stream, '+t') expect_and_check_can_set(q, channel, False) change_channel_mode(stream, '+o ' + stream.nick) expect_and_check_can_set(q, channel, True) change_channel_mode(stream, '-o ' + stream.nick) expect_and_check_can_set(q, channel, False) change_channel_mode(stream, '-t') expect_and_check_can_set(q, channel, True) # And back to normal again ? test_can_set(q, stream, channel) channel.Subject2.SetSubject('') # Verify that we send an empty final parameter ("clear the topic") as # opposed to no final parameter ("what is the topic"). q.expect('stream-TOPIC', data=[room, ''])
def initiate(self): if self.incoming: self.context.incoming_call(self.medias) else: self.chan_path = self.conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: self.remote_handle, cs.CALL_INITIAL_AUDIO: self.initial_audio_content_name is not None, cs.CALL_INITIAL_AUDIO_NAME: self.initial_audio_content_name or "", cs.CALL_INITIAL_VIDEO: self.initial_video_content_name is not None, cs.CALL_INITIAL_VIDEO_NAME: self.initial_video_content_name or "", })[0] nc = self.q.expect('dbus-signal', signal='NewChannels') assertLength(1, nc.args) assertLength(1, nc.args[0]) # one channel assertLength(2, nc.args[0][0]) # two struct members self.chan_path, props = nc.args[0][0] self.check_channel_props(props, True) self.chan = wrap_channel( self.bus.get_object(self.conn.bus_name, self.chan_path), 'Call1', ['Hold']) call_props = self.chan.Properties.GetAll(cs.CHANNEL_TYPE_CALL) self.check_call_properties(call_props) for c in call_props['Contents']: self.add_content(c, True) if not self.incoming: self.chan.Call1.Accept() self.q.expect_many(*self.stream_dbus_signal_event( 'ReceivingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_PENDING_START])) for c in self.contents: c.stream.Media.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED) mdo = c.Get(cs.CALL_CONTENT_IFACE_MEDIA, 'MediaDescriptionOffer') md = self.bus.get_object(self.conn.bus_name, mdo[0]) md.Accept(self.context.get_audio_md_dbus(self.remote_handle)) self.q.expect_many( EventPattern('dbus-signal', signal='MediaDescriptionOfferDone', path=c.__dbus_object_path__), EventPattern('dbus-signal', signal='LocalMediaDescriptionChanged', path=c.__dbus_object_path__), EventPattern('dbus-signal', signal='RemoteMediaDescriptionsChanged', path=c.__dbus_object_path__)) mdo = c.Get(cs.CALL_CONTENT_IFACE_MEDIA, 'MediaDescriptionOffer') assertEquals(('/', {}), mdo) self.add_candidates(c.stream) self.invite_event = self.q.expect('sip-invite')
def test(q, bus, conn, stream): self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") call_async( q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: '*****@*****.**' }) gfc, _, _, _ = q.expect_many( # Initial group flags EventPattern('dbus-signal', signal='GroupFlagsChanged', predicate=lambda e: e.args[0] != 0), EventPattern('dbus-signal', signal='MembersChangedDetailed', predicate=lambda e: e.args[3] == [2]), # Removing CAN_ADD EventPattern('dbus-signal', signal='GroupFlagsChanged', args=[0, cs.GF_CAN_ADD], predicate=lambda e: e.args[0] == 0), EventPattern('stream-presence', to='[email protected]/test')) assert gfc.args[1] == 0 # Send presence for anonymous other member of room. stream.send( make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob')) # Send presence for anonymous other member of room (2) stream.send( make_muc_presence('owner', 'moderator', '*****@*****.**', 'brian')) # Send presence for nonymous other member of room. stream.send( make_muc_presence('none', 'participant', '*****@*****.**', 'che', '*****@*****.**')) # Send presence for nonymous other member of room (2) stream.send( make_muc_presence('none', 'participant', '*****@*****.**', 'chris', '*****@*****.**')) # Send presence for own membership of room. stream.send( make_muc_presence('none', 'participant', '*****@*****.**', 'test')) # Since we received MUC presence that contains an owner JID, the # OWNERS_NOT_AVAILABLE flag should be removed. event = q.expect('dbus-signal', signal='GroupFlagsChanged', args=[0, cs.GF_HANDLE_OWNERS_NOT_AVAILABLE]) event = q.expect('dbus-signal', signal='HandleOwnersChanged') owners = event.args[0] event = q.expect('dbus-signal', signal='MembersChangedDetailed') added = event.args[0] [test, bob, brian, che, che_owner, chris, chris_owner] = \ conn.get_contact_handles_sync( [ '[email protected]/test', '[email protected]/bob', '[email protected]/brian', '[email protected]/che', '*****@*****.**', '[email protected]/chris', '*****@*****.**', ]) expected_members = sorted([test, bob, brian, che, chris]) expected_owners = { test: self_handle, bob: 0, brian: 0, che: che_owner, chris: chris_owner } assertEquals(expected_members, sorted(added)) assertEquals(expected_owners, owners) event = q.expect('dbus-return', method='CreateChannel') chan = wrap_channel(bus.get_object(conn.bus_name, event.value[0]), 'Text') # Exercise HandleOwners owners = chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'HandleOwners') assertEquals(che_owner, owners[che]) assertEquals(chris_owner, owners[chris]) # Exercise D-Bus properties all = chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP) assert all[u'LocalPendingMembers'] == [], all assert sorted(all[u'Members']) == expected_members, all assert all[u'RemotePendingMembers'] == [], all assert all[u'SelfHandle'] == test, all assert all[u'HandleOwners'] == expected_owners, all flags = all[u'GroupFlags'] assertFlagsSet(cs.GF_CHANNEL_SPECIFIC_HANDLES, flags) assertFlagsUnset(cs.GF_HANDLE_OWNERS_NOT_AVAILABLE, flags)
def test(q, bus, conn, stream, channel_type): jt = JingleTest2(JingleProtocol031(), conn, q, stream, "test@localhost", "*****@*****.**") jt.prepare() self_handle = conn.GetSelfHandle() handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0] request = {cs.CHANNEL_TYPE: channel_type, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: handle} if channel_type == cs.CHANNEL_TYPE_CALL: request[cs.CALL_INITIAL_AUDIO] = True # Ensure a channel that doesn't exist yet. call_async(q, conn.Requests, "EnsureChannel", request) ret, old_sig, new_sig = q.expect_many( EventPattern("dbus-return", method="EnsureChannel"), EventPattern( "dbus-signal", signal="NewChannel", predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args ), EventPattern( "dbus-signal", signal="NewChannels", predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args[0][0][1].values(), ), ) yours, path, props = ret.value # this channel was created in response to our EnsureChannel call, so it # should be ours. assert yours, ret.value sig_path, sig_ct, sig_ht, sig_h, sig_sh = old_sig.args assertEquals(sig_path, path) assertEquals(channel_type, sig_ct) assertEquals(cs.HT_CONTACT, sig_ht) assertEquals(handle, sig_h) assert sig_sh # suppress handler assertLength(1, new_sig.args) assertLength(1, new_sig.args[0]) # one channel assertLength(2, new_sig.args[0][0]) # two struct members assertEquals(path, new_sig.args[0][0][0]) emitted_props = new_sig.args[0][0][1] assertEquals(channel_type, emitted_props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_CONTACT, emitted_props[cs.TARGET_HANDLE_TYPE]) assertEquals(handle, emitted_props[cs.TARGET_HANDLE]) assertEquals(jt.peer_bare_jid, emitted_props[cs.TARGET_ID]) assert emitted_props[cs.REQUESTED] assertEquals(self_handle, emitted_props[cs.INITIATOR_HANDLE]) assertEquals("test@localhost", emitted_props[cs.INITIATOR_ID]) # Now ensure a media channel with the same contact, and check it's the # same. call_async(q, conn.Requests, "EnsureChannel", request) event = q.expect("dbus-return", method="EnsureChannel") yours2, path2, props2 = event.value # We should have got back the same channel we created a page or so ago. assertEquals(path2, path) # It's not been created for this call, so Yours should be False. assert not yours2 # Time passes ... afterwards we close the chan chan = wrap_channel(bus.get_object(conn.bus_name, path), "StreamedMedia") chan.Close() # Ensure a channel that doesn't exist yet. call_async(q, conn.Requests, "EnsureChannel", request) # Re-ensure a channel that is hopefully still pending creation. call_async(q, conn.Requests, "EnsureChannel", request) ret, ret2, old_sig, new_sig = q.expect_many( EventPattern("dbus-return", method="EnsureChannel"), EventPattern("dbus-return", method="EnsureChannel"), EventPattern( "dbus-signal", signal="NewChannel", predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args ), EventPattern( "dbus-signal", signal="NewChannels", predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args[0][0][1].values(), ), ) yours, path, props = ret.value # this channel was created in response to our EnsureChannel call, so it # should be ours. assert yours, ret.value sig_path, sig_ct, sig_ht, sig_h, sig_sh = old_sig.args assertEquals(sig_path, path) assertEquals(channel_type, sig_ct) assertEquals(cs.HT_CONTACT, sig_ht) assertEquals(handle, sig_h) assert sig_sh # suppress handler assertLength(1, new_sig.args) assertLength(1, new_sig.args[0]) # one channel assertLength(2, new_sig.args[0][0]) # two struct members assertEquals(path, new_sig.args[0][0][0]) emitted_props = new_sig.args[0][0][1] assertEquals(channel_type, emitted_props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_CONTACT, emitted_props[cs.TARGET_HANDLE_TYPE]) assertEquals(handle, emitted_props[cs.TARGET_HANDLE]) assertEquals(jt.peer_bare_jid, emitted_props[cs.TARGET_ID]) assert emitted_props[cs.REQUESTED] assertEquals(self_handle, emitted_props[cs.INITIATOR_HANDLE]) assertEquals("test@localhost", emitted_props[cs.INITIATOR_ID]) yours2, path2, props2 = ret2.value # We should have got back the same channel we created a page or so ago. assertEquals(path2, path) # It's not been created for this call, so Yours should be False. assert not yours2 # Time passes ... afterwards we close the chan chan = wrap_channel(bus.get_object(conn.bus_name, path), "StreamedMedia") chan.Close() # The remaining checks don't apply to calls if channel_type == cs.CHANNEL_TYPE_CALL: return # Now, create an anonymous channel with RequestChannel, add the other # person to it with RequestStreams, then Ensure a media channel with that # person. We should get the anonymous channel back. call_async(q, conn, "RequestChannel", channel_type, 0, 0, True) ret, old_sig, new_sig = q.expect_many( EventPattern("dbus-return", method="RequestChannel"), EventPattern( "dbus-signal", signal="NewChannel", predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args ), EventPattern( "dbus-signal", signal="NewChannels", predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args[0][0][1].values(), ), ) path = ret.value[0] assertEquals([path, channel_type, cs.HT_NONE, 0, True], old_sig.args) assertLength(1, new_sig.args) assertLength(1, new_sig.args[0]) # one channel assertLength(2, new_sig.args[0][0]) # two struct members assertEquals(path, new_sig.args[0][0][0]) emitted_props = new_sig.args[0][0][1] assertEquals(channel_type, emitted_props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_NONE, emitted_props[cs.TARGET_HANDLE_TYPE]) assertEquals(0, emitted_props[cs.TARGET_HANDLE]) assertEquals("", emitted_props[cs.TARGET_ID]) assert emitted_props[cs.REQUESTED] assertEquals(self_handle, emitted_props[cs.INITIATOR_HANDLE]) assertEquals("test@localhost", emitted_props[cs.INITIATOR_ID]) chan = wrap_channel(bus.get_object(conn.bus_name, path), "StreamedMedia") # Request streams with the other person. This should make them the # channel's "peer" property. chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) # Now, Ensuring a media channel with handle should yield the channel just # created. call_async(q, conn.Requests, "EnsureChannel", request) event = q.expect("dbus-return", method="EnsureChannel") yours, path2, _ = event.value # we should have got back the anonymous channel we got with requestchannel # and called RequestStreams(handle) on. assertEquals(path2, path) # It's not been created for this call, so Yours should be False. assert not yours chan.Close()
def test(q, bus, conn, stream, access_control): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) muc = '*****@*****.**' _, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, muc) # Bob offers a stream tube bob_bus_name = ':2.Ym9i' presence = make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob') tubes = presence.addElement((ns.TUBES, 'tubes')) tube = tubes.addElement((None, 'tube')) tube['type'] = 'dbus' tube['initiator'] = '[email protected]/bob' tube['stream-id'] = '10' tube['id'] = '1' tube['service'] = 'com.example.Test' tube['dbus-name'] = bob_bus_name parameters = tube.addElement((None, 'parameters')) parameter = parameters.addElement((None, 'parameter')) parameter['type'] = 'str' parameter['name'] = 'foo' parameter.addContent('bar') stream.send(presence) # tube channel is created def new_chan_predicate(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE event = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) channels = event.args[0] path, props = channels[0] assertEquals(cs.CHANNEL_TYPE_DBUS_TUBE, props[cs.CHANNEL_TYPE]) assertEquals('[email protected]/bob', props[cs.INITIATOR_ID]) bob_handle = props[cs.INITIATOR_HANDLE] assertSameSets([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE], props[cs.INTERFACES]) assertEquals(False, props[cs.REQUESTED]) assertEquals('*****@*****.**', props[cs.TARGET_ID]) assertEquals('com.example.Test', props[cs.DBUS_TUBE_SERVICE_NAME]) assertEquals({'foo': 'bar'}, props[cs.TUBE_PARAMETERS]) assertEquals([ cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST ], props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS]) tube_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'DBusTube') # only Bob is in DBusNames dbus_names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assertEquals({bob_handle: bob_bus_name}, dbus_names) call_async(q, tube_chan.DBusTube, 'Accept', access_control) return_event, names_changed1, names_changed2, presence_event = q.expect_many( EventPattern('dbus-return', method='Accept'), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE), EventPattern('stream-presence', to='[email protected]/test', predicate=lambda e: t.presence_contains_tube(e))) tube_addr = return_event.value[0] assert len(tube_addr) > 0 # check presence stanza tube_node = xpath.queryForNodes('/presence/tubes/tube', presence_event.stanza)[0] assertEquals('[email protected]/bob', tube_node['initiator']) assertEquals('com.example.Test', tube_node['service']) assertEquals('10', tube_node['stream-id']) assertEquals('dbus', tube_node['type']) assertEquals('1', tube_node['id']) self_bus_name = tube_node['dbus-name'] tubes_self_handle = tube_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'SelfHandle') assertNotEquals(0, tubes_self_handle) # both of us are in DBusNames now dbus_names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assertEquals({ bob_handle: bob_bus_name, tubes_self_handle: self_bus_name }, dbus_names) added, removed = names_changed1.args assertEquals({bob_handle: bob_bus_name}, added) assertEquals([], removed) added, removed = names_changed2.args assertEquals({tubes_self_handle: self_bus_name}, added) assertEquals([], removed) tube_chan.Channel.Close() q.expect_many(EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'))
def test(q, bus, conn, stream): # Bob has invited us to an activity. message = domish.Element((None, 'message')) message['from'] = '*****@*****.**' message['to'] = 'test@localhost' x = message.addElement(('http://jabber.org/protocol/muc#user', 'x')) invite = x.addElement((None, 'invite')) invite['from'] = 'bob@localhost' reason = invite.addElement((None, 'reason')) reason.addContent('No good reason') stream.send(message) event = q.expect('dbus-signal', signal='NewChannels') path, props = event.args[0][0] assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_ROOM, props[cs.TARGET_HANDLE_TYPE]) assertEquals(1, props[cs.TARGET_HANDLE]) text_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') members = text_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'Members') local_pending = text_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'LocalPendingMembers') remote_pending = text_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'RemotePendingMembers') assert len(members) == 1 assert conn.inspect_contact_sync(members[0]) == 'bob@localhost' bob_handle = members[0] assert len(local_pending) == 1 # FIXME: the username-part-is-nickname assumption assert conn.inspect_contact_sync(local_pending[0][0]) == \ '[email protected]/test' assert len(remote_pending) == 0 room_self_handle = text_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, "SelfHandle") assert room_self_handle == local_pending[0][0] channel_props = text_chan.Properties.GetAll(cs.CHANNEL) assert channel_props['TargetID'] == '*****@*****.**', channel_props assert channel_props['Requested'] == False assert channel_props['InitiatorID'] == 'bob@localhost' assert channel_props['InitiatorHandle'] == bob_handle # set ourselves to away and back again, to check that we don't send any # presence to the MUC before the invite has been accepted conn.SimplePresence.SetPresence('away', 'failure') conn.SimplePresence.SetPresence('available', 'success') # accept the invitation call_async(q, text_chan.Group, 'AddMembers', [room_self_handle], 'Oh, OK then') event, event2, _ = q.expect_many( EventPattern('stream-presence', to='[email protected]/test'), EventPattern('dbus-signal', signal='MembersChangedDetailed'), EventPattern('dbus-return', method='AddMembers') ) # check that the status we joined with was available / success elem = event.stanza show = [e for e in elem.elements() if e.name == 'show'] assert not show status = [e for e in elem.elements() if e.name == 'status'][0] assert status assert status.children[0] == u'success' # We are added as remote pending while joining the room. The inviter (Bob) # is removed for now. It will be re-added with his channel specific handle # once we have joined. added, removed, local_pending, remote_pending, details = event2.args assertEquals([], added) assertEquals([bob_handle], removed) assertEquals([], local_pending) assertEquals([room_self_handle], remote_pending) assertEquals(cs.GC_REASON_INVITED, details['change-reason']) # Send presence for Bob's membership of room. stream.send(make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob')) # Send presence for own membership of room. stream.send(make_muc_presence('owner', 'moderator', '*****@*****.**', 'test')) event = q.expect('dbus-signal', signal='MembersChangedDetailed') room_bob_handle = conn.get_contact_handle_sync('[email protected]/bob') added, removed, local_pending, remote_pending, details = event.args assertEquals([room_self_handle, room_bob_handle], added) assertEquals([], removed) assertEquals([], local_pending) assertEquals([], remote_pending) # Test sending an invitation alice_handle = conn.get_contact_handle_sync('alice@localhost') call_async(q, text_chan.Group, 'AddMembers', [alice_handle], 'I want to test invitations') event = q.expect('stream-message', to='*****@*****.**') message = event.stanza x = xpath.queryForNodes('/message/x', message) assert (x is not None and len(x) == 1), repr(x) assert x[0].uri == 'http://jabber.org/protocol/muc#user' invites = xpath.queryForNodes('/x/invite', x[0]) assert (invites is not None and len(invites) == 1), repr(invites) assert invites[0]['to'] == 'alice@localhost' reasons = xpath.queryForNodes('/invite/reason', invites[0]) assert (reasons is not None and len(reasons) == 1), repr(reasons) assert str(reasons[0]) == 'I want to test invitations'
def test(q, bus, conn, stream): room = '*****@*****.**' handle = request_muc_handle(q, conn, stream, room) call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_HANDLE: handle}) expected_muc_jid = '%s/%s' % (room, 'test') q.expect('stream-presence', to=expected_muc_jid) # tell gabble the room needs a password denied = \ elem('jabber:client', 'presence', from_=expected_muc_jid, type='error')( elem(ns.MUC, 'x'), elem('error', type='auth')( elem(ns.STANZA, 'not-authorized'), ), ) stream.send(denied) cc, _, _ = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels'), EventPattern('dbus-signal', signal='PasswordFlagsChanged', args=[cs.PASSWORD_FLAG_PROVIDE, 0])) chan = wrap_channel(bus.get_object(conn.bus_name, cc.value[0]), 'Text', ['Password']) flags = chan.Password.GetPasswordFlags() assertEquals(cs.PASSWORD_FLAG_PROVIDE, flags) call_async(q, chan.Password, 'ProvidePassword', 'brand new benz') expect_attempt(q, expected_muc_jid, 'brand new benz') # Try again while the first attempt is outstanding. Gabble should say no. call_async(q, chan.Password, 'ProvidePassword', 'faster faster') q.expect('dbus-error', method='ProvidePassword') # Sorry, wrong password. stream.send(denied) ret = q.expect('dbus-return', method='ProvidePassword') assert not ret.value[0] call_async(q, chan.Password, 'ProvidePassword', 'bougie friends') expect_attempt(q, expected_muc_jid, 'bougie friends') # Well, this may be the right password, but actually that nick is in use. presence = elem('presence', from_=expected_muc_jid, type='error')( elem(ns.MUC, 'x'), elem('error', type='cancel')( elem(ns.STANZA, 'conflict'), )) stream.send(presence) # Okay, so Gabble tries again, with a new JID *and the same password*. expected_muc_jid = expected_muc_jid + '_' expect_attempt(q, expected_muc_jid, 'bougie friends') # Hey this worked. stream.send(make_muc_presence('none', 'participant', room, 'test_')) ret, _ = q.expect_many( EventPattern('dbus-return', method='ProvidePassword'), EventPattern('dbus-signal', signal='PasswordFlagsChanged', args=[0, cs.PASSWORD_FLAG_PROVIDE])) assert ret.value[0]
def test(q, bus, conn, stream): id = '1845a1a9-f7bc-4a2e-a885-633aadc81e1b' # <message type="chat"><body>hello</body</message> m = domish.Element((None, 'message')) m['from'] = '[email protected]/Pidgin' m['id'] = id m['type'] = 'chat' m.addElement('body', content='hello') stream.send(m) event = q.expect('dbus-signal', signal='NewChannels') path, props = event.args[0][0] text_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_CONTACT, props[cs.TARGET_HANDLE_TYPE]) foo_at_bar_dot_com_handle = props[cs.TARGET_HANDLE] jid = conn.inspect_contact_sync(foo_at_bar_dot_com_handle) assertEquals('*****@*****.**', jid) # Exercise basic Channel Properties from spec 0.17.7 channel_props = text_chan.Properties.GetAll(cs.CHANNEL) assertEquals(props[cs.TARGET_HANDLE], channel_props.get('TargetHandle')) assertEquals(cs.HT_CONTACT, channel_props.get('TargetHandleType')) assertEquals(cs.CHANNEL_TYPE_TEXT, channel_props.get('ChannelType')) assertContains(cs.CHANNEL_IFACE_CHAT_STATE, channel_props.get('Interfaces')) assertContains(cs.CHANNEL_IFACE_MESSAGES, channel_props.get('Interfaces')) assertEquals(jid, channel_props['TargetID']) assertEquals(False, channel_props['Requested']) assertEquals(props[cs.INITIATOR_HANDLE], channel_props['InitiatorHandle']) assertEquals(jid, channel_props['InitiatorID']) message_received = q.expect('dbus-signal', signal='MessageReceived') # Check that C.I.Messages.MessageReceived looks right. message = message_received.args[0] # message should have two parts: the header and one content part assert len(message) == 2, message header, body = message assert header['message-sender'] == foo_at_bar_dot_com_handle, header # the spec says that message-type "MAY be omitted for normal chat # messages." assert 'message-type' not in header or header['message-type'] == 0, header # We don't make any uniqueness guarantees about the tokens on incoming # messages, so we use the id='' provided at the protocol level. assertEquals(id, header['message-token']) assert body['content-type'] == 'text/plain', body assert body['content'] == 'hello', body # Remove the message from the pending message queue, and check that # PendingMessagesRemoved fires. message_id = header['pending-message-id'] text_chan.Text.AcknowledgePendingMessages([message_id]) removed = q.expect('dbus-signal', signal='PendingMessagesRemoved') removed_ids = removed.args[0] assert len(removed_ids) == 1, removed_ids assert removed_ids[0] == message_id, (removed_ids, message_id) # Send a Notice using the Messages API greeting = [ dbus.Dictionary({ 'message-type': 2, # Notice }, signature='sv'), { 'content-type': 'text/plain', 'content': u"what up", } ] sent_token = text_chan.Messages.SendMessage(greeting, dbus.UInt32(0)) stream_message, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='MessageSent'), ) elt = stream_message.stanza assert elt.name == 'message' assert elt['type'] == 'normal' body = list(stream_message.stanza.elements())[0] assert body.name == 'body' assert body.children[0] == u'what up' sent_message = message_sent.args[0] assert len(sent_message) == 2, sent_message header = sent_message[0] assert header['message-type'] == 2, header # Notice assert header['message-token'] == sent_token, header assertEquals(conn.Properties.Get(cs.CONN, "SelfHandle"), header['message-sender']) assertEquals('test@localhost', header['message-sender-id']) body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'what up', body assert message_sent.args[2] == sent_token # Send a message using Channel.Type.Text API text_chan.Text.Send(0, 'goodbye') stream_message, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='MessageSent'), ) elt = stream_message.stanza assert elt.name == 'message' assert elt['type'] == 'chat' body = list(stream_message.stanza.elements())[0] assert body.name == 'body' assert body.children[0] == u'goodbye' sent_message = message_sent.args[0] assert len(sent_message) == 2, sent_message header = sent_message[0] # the spec says that message-type "MAY be omitted for normal chat # messages." assert 'message-type' not in header or header['message-type'] == 0, header body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'goodbye', body # And now let's try a message with a malformed type='' attribute. malformed = elem( 'message', from_='[email protected]/fubber', type="'")( elem('body')(u'Internettt!'), elem('subject')(u'xyzzy'), elem('thread')(u'6666'), ) stream.send(malformed) event = q.expect('dbus-signal', signal='MessageReceived') message, = event.args assertLength(2, message) header, body = message # Gabble should treat the unparseable type as if it were 'normal' or # omitted (not to be confused with Telepathy's Normal, which is 'chat' in # XMPP...) assertEquals(cs.MT_NOTICE, header['message-type'])
def test(q, bus, conn, stream): self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: '*****@*****.**' }) gfc, _, _, _ = q.expect_many( # Initial group flags EventPattern('dbus-signal', signal='GroupFlagsChanged', predicate=lambda e: e.args[0] != 0), EventPattern('dbus-signal', signal='MembersChangedDetailed', predicate=lambda e: e.args[3] == [2]), # Removing CAN_ADD EventPattern('dbus-signal', signal='GroupFlagsChanged', args = [0, cs.GF_CAN_ADD], predicate=lambda e: e.args[0] == 0), EventPattern('stream-presence', to='[email protected]/test')) assert gfc.args[1] == 0 # Send presence for anonymous other member of room. stream.send(make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob')) # Send presence for anonymous other member of room (2) stream.send(make_muc_presence('owner', 'moderator', '*****@*****.**', 'brian')) # Send presence for nonymous other member of room. stream.send(make_muc_presence('none', 'participant', '*****@*****.**', 'che', '*****@*****.**')) # Send presence for nonymous other member of room (2) stream.send(make_muc_presence('none', 'participant', '*****@*****.**', 'chris', '*****@*****.**')) # Send presence for own membership of room. stream.send(make_muc_presence('none', 'participant', '*****@*****.**', 'test')) # Since we received MUC presence that contains an owner JID, the # OWNERS_NOT_AVAILABLE flag should be removed. event = q.expect('dbus-signal', signal='GroupFlagsChanged', args = [0, cs.GF_HANDLE_OWNERS_NOT_AVAILABLE ]) event = q.expect('dbus-signal', signal='HandleOwnersChanged') owners = event.args[0] event = q.expect('dbus-signal', signal='MembersChangedDetailed') added = event.args[0] [test, bob, brian, che, che_owner, chris, chris_owner] = \ conn.get_contact_handles_sync( [ '[email protected]/test', '[email protected]/bob', '[email protected]/brian', '[email protected]/che', '*****@*****.**', '[email protected]/chris', '*****@*****.**', ]) expected_members = sorted([test, bob, brian, che, chris]) expected_owners = { test: self_handle, bob: 0, brian: 0, che: che_owner, chris: chris_owner } assertEquals(expected_members, sorted(added)) assertEquals(expected_owners, owners) event = q.expect('dbus-return', method='CreateChannel') chan = wrap_channel(bus.get_object(conn.bus_name, event.value[0]), 'Text') # Exercise HandleOwners owners = chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'HandleOwners') assertEquals(che_owner, owners[che]) assertEquals(chris_owner, owners[chris]) # Exercise D-Bus properties all = chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP) assert all[u'LocalPendingMembers'] == [], all assert sorted(all[u'Members']) == expected_members, all assert all[u'RemotePendingMembers'] == [], all assert all[u'SelfHandle'] == test, all assert all[u'HandleOwners'] == expected_owners, all flags = all[u'GroupFlags'] assertFlagsSet(cs.GF_CHANNEL_SPECIFIC_HANDLES, flags) assertFlagsUnset(cs.GF_HANDLE_OWNERS_NOT_AVAILABLE, flags)
def test(q, bus, conn, stream, access_control): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) muc = '*****@*****.**' _, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, muc) # Bob offers a stream tube bob_bus_name = ':2.Ym9i' presence = make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob') tubes = presence.addElement((ns.TUBES, 'tubes')) tube = tubes.addElement((None, 'tube')) tube['type'] = 'dbus' tube['initiator'] = '[email protected]/bob' tube['stream-id'] = '10' tube['id'] = '1' tube['service'] = 'com.example.Test' tube['dbus-name'] = bob_bus_name parameters = tube.addElement((None, 'parameters')) parameter = parameters.addElement((None, 'parameter')) parameter['type'] = 'str' parameter['name'] = 'foo' parameter.addContent('bar') stream.send(presence) # tube channel is created def new_chan_predicate(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE event = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) channels = event.args[0] path, props = channels[0] assertEquals(cs.CHANNEL_TYPE_DBUS_TUBE, props[cs.CHANNEL_TYPE]) assertEquals('[email protected]/bob', props[cs.INITIATOR_ID]) bob_handle = props[cs.INITIATOR_HANDLE] assertSameSets([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE], props[cs.INTERFACES]) assertEquals(False, props[cs.REQUESTED]) assertEquals('*****@*****.**', props[cs.TARGET_ID]) assertEquals('com.example.Test', props[cs.DBUS_TUBE_SERVICE_NAME]) assertEquals({'foo': 'bar'}, props[cs.TUBE_PARAMETERS]) assertEquals([cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST], props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS]) tube_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'DBusTube') # only Bob is in DBusNames dbus_names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assertEquals({bob_handle: bob_bus_name}, dbus_names) call_async(q, tube_chan.DBusTube, 'Accept', access_control) return_event, names_changed1, names_changed2, presence_event = q.expect_many( EventPattern('dbus-return', method='Accept'), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE), EventPattern('stream-presence', to='[email protected]/test', predicate=lambda e: t.presence_contains_tube(e))) tube_addr = return_event.value[0] assert len(tube_addr) > 0 # check presence stanza tube_node = xpath.queryForNodes('/presence/tubes/tube', presence_event.stanza)[0] assertEquals('[email protected]/bob', tube_node['initiator']) assertEquals('com.example.Test', tube_node['service']) assertEquals('10', tube_node['stream-id']) assertEquals('dbus', tube_node['type']) assertEquals('1', tube_node['id']) self_bus_name = tube_node['dbus-name'] tubes_self_handle = tube_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'SelfHandle') assertNotEquals(0, tubes_self_handle) # both of us are in DBusNames now dbus_names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assertEquals({bob_handle: bob_bus_name, tubes_self_handle: self_bus_name}, dbus_names) added, removed = names_changed1.args assertEquals({bob_handle: bob_bus_name}, added) assertEquals([], removed) added, removed = names_changed2.args assertEquals({tubes_self_handle: self_bus_name}, added) assertEquals([], removed) tube_chan.Channel.Close() q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'))
def test(q, bus, conn, stream): # Bob has invited us to an activity. message = domish.Element((None, 'message')) message['from'] = '*****@*****.**' message['to'] = 'test@localhost' x = message.addElement(('http://jabber.org/protocol/muc#user', 'x')) invite = x.addElement((None, 'invite')) invite['from'] = 'bob@localhost' reason = invite.addElement((None, 'reason')) reason.addContent('No good reason') stream.send(message) event = q.expect('dbus-signal', signal='NewChannels') path, props = event.args[0][0] assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_ROOM, props[cs.TARGET_HANDLE_TYPE]) assertEquals(1, props[cs.TARGET_HANDLE]) text_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') members = text_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'Members') local_pending = text_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'LocalPendingMembers') remote_pending = text_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'RemotePendingMembers') assert len(members) == 1 assert conn.inspect_contact_sync(members[0]) == 'bob@localhost' bob_handle = members[0] assert len(local_pending) == 1 # FIXME: the username-part-is-nickname assumption assert conn.inspect_contact_sync(local_pending[0][0]) == \ '[email protected]/test' assert len(remote_pending) == 0 room_self_handle = text_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, "SelfHandle") assert room_self_handle == local_pending[0][0] channel_props = text_chan.Properties.GetAll(cs.CHANNEL) assert channel_props['TargetID'] == '*****@*****.**', channel_props assert channel_props['Requested'] == False assert channel_props['InitiatorID'] == 'bob@localhost' assert channel_props['InitiatorHandle'] == bob_handle # set ourselves to away and back again, to check that we don't send any # presence to the MUC before the invite has been accepted conn.SimplePresence.SetPresence('away', 'failure') conn.SimplePresence.SetPresence('available', 'success') # accept the invitation call_async(q, text_chan.Group, 'AddMembers', [room_self_handle], 'Oh, OK then') event, event2, _ = q.expect_many( EventPattern('stream-presence', to='[email protected]/test'), EventPattern('dbus-signal', signal='MembersChangedDetailed'), EventPattern('dbus-return', method='AddMembers')) # check that the status we joined with was available / success elem = event.stanza show = [e for e in elem.elements() if e.name == 'show'] assert not show status = [e for e in elem.elements() if e.name == 'status'][0] assert status assert status.children[0] == u'success' # We are added as remote pending while joining the room. The inviter (Bob) # is removed for now. It will be re-added with his channel specific handle # once we have joined. added, removed, local_pending, remote_pending, details = event2.args assertEquals([], added) assertEquals([bob_handle], removed) assertEquals([], local_pending) assertEquals([room_self_handle], remote_pending) assertEquals(cs.GC_REASON_INVITED, details['change-reason']) # Send presence for Bob's membership of room. stream.send( make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob')) # Send presence for own membership of room. stream.send( make_muc_presence('owner', 'moderator', '*****@*****.**', 'test')) event = q.expect('dbus-signal', signal='MembersChangedDetailed') room_bob_handle = conn.get_contact_handle_sync('[email protected]/bob') added, removed, local_pending, remote_pending, details = event.args assertEquals([room_self_handle, room_bob_handle], added) assertEquals([], removed) assertEquals([], local_pending) assertEquals([], remote_pending) # Test sending an invitation alice_handle = conn.get_contact_handle_sync('alice@localhost') call_async(q, text_chan.Group, 'AddMembers', [alice_handle], 'I want to test invitations') event = q.expect('stream-message', to='*****@*****.**') message = event.stanza x = xpath.queryForNodes('/message/x', message) assert (x is not None and len(x) == 1), repr(x) assert x[0].uri == 'http://jabber.org/protocol/muc#user' invites = xpath.queryForNodes('/x/invite', x[0]) assert (invites is not None and len(invites) == 1), repr(invites) assert invites[0]['to'] == 'alice@localhost' reasons = xpath.queryForNodes('/invite/reason', invites[0]) assert (reasons is not None and len(reasons) == 1), repr(reasons) assert str(reasons[0]) == 'I want to test invitations'
def worker(jp, q, bus, conn, stream): jp.features.append(ns.JINGLE_TRANSPORT_ICEUDP) jt2 = JingleTest2(jp, conn, q, stream, 'test@localhost', '[email protected]/Foo') jt2.prepare() remote_handle = conn.RequestHandles(cs.HT_CONTACT, ["[email protected]/Foo"])[0] # Remote end calls us node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'session-initiate', [ jp.Content('stream1', 'initiator', 'both', jp.Description('audio', [ jp.PayloadType(name, str(rate), str(id), parameters) for (name, id, rate, parameters) in jt2.audio_codecs ]), jp.TransportIceUdp()) ]) ]) stream.send(jp.xml(node)) nc, e = q.expect_many( EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewSessionHandler'), ) path = nc.args[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia') hrggh = chan.ListProperties(dbus_interface=cs.TP_AWKWARD_PROPERTIES) id = [x for x, name, _, _ in hrggh if name == 'nat-traversal'][0] nrgrg = chan.GetProperties([id], dbus_interface=cs.TP_AWKWARD_PROPERTIES) _, nat_traversal = nrgrg[0] assertEquals('ice-udp', nat_traversal) session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() chan.Group.AddMembers([conn.GetSelfHandle()], 'accepted') # S-E gets notified about a newly-created stream e = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt2.get_remote_transports_dbus()) stream_handler.Ready(jt2.get_audio_codecs_dbus()) stream_handler.StreamState(2) # First one is transport-info e = q.expect('stream-iq', predicate=jp.action_predicate('transport-info')) transport_stanza = xpath.queryForNodes( "/iq/jingle/content/transport[@xmlns='%s']" % ns.JINGLE_TRANSPORT_ICEUDP, e.stanza)[0] # username assertEquals(transport_stanza['ufrag'], jt2.remote_transports[0][7]) # password assertEquals(transport_stanza['pwd'], jt2.remote_transports[0][8]) children = list(transport_stanza.elements()) assertLength(1, children) stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, []))) # Set codec intersection so gabble can accept the session stream_handler.SupportedCodecs(jt2.get_audio_codecs_dbus()) # Second one is session-accept e = q.expect('stream-iq', predicate=jp.action_predicate('session-accept')) assert xpath.queryForNodes("/iq/jingle/content/transport[@xmlns='%s']" % ns.JINGLE_TRANSPORT_ICEUDP, e.stanza) stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, []))) # Connected! Blah, blah, ... jt2.terminate() e = q.expect('dbus-signal', signal='Close')
def test_join(q, bus, conn, stream, room_jid, transient_conflict): """ Tells Gabble to join a MUC, but make the first nick it tries conflict with an existing member of the MUC. If transient_conflict is True, then when Gabble successfully joins with a different nick the originally conflicting user turns out not actually to be in the room (they left while we were retrying). """ # Implementation detail: Gabble uses the first part of your jid (if you # don't have an alias) as your room nickname, and appends an underscore a # few times before giving up. member, member_ = [room_jid + '/' + x for x in ['test', 'test_']] call_async( q, conn.Requests, 'CreateChannel', dbus.Dictionary( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: room_jid, }, signature='sv')) # Gabble first tries to join as test q.expect('stream-presence', to=member) # MUC says no: there's already someone called test in room_jid presence = elem('presence', from_=member, type='error')(elem(ns.MUC, 'x'), elem('error', type='cancel')(elem( ns.STANZA, 'conflict'), )) stream.send(presence) # Gabble tries again as test_ q.expect('stream-presence', to=member_) # MUC says yes! if not transient_conflict: # Send the other member of the room's presence. This is the nick we # originally wanted. stream.send(make_muc_presence('owner', 'moderator', room_jid, 'test')) # If gabble erroneously thinks the other user's presence is our own, it'll # think that it's got the whole userlist now. If so, syncing here will make # CreateChannel incorrectly return here. sync_stream(q, stream) sync_dbus(bus, q, conn) # Send presence for own membership of room. stream.send(make_muc_presence('none', 'participant', room_jid, 'test_')) # Only now should we have finished joining the room. event = q.expect('dbus-return', method='CreateChannel') path, props = event.value text_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') group_props = unwrap(text_chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP)) t, t_ = conn.get_contact_handles_sync([member, member_]) # Check that Gabble think our nickname in the room is test_, not test muc_self_handle = group_props['SelfHandle'] assert muc_self_handle == t_, (muc_self_handle, t_, t) members = group_props['Members'] if transient_conflict: # The user we originally conflicted with isn't actually here; check # there's exactly one member (test_). assert members == [t_], (members, t_, t) else: # Check there are exactly two members (test and test_) assertSameSets([t, t_], members) # In either case, there should be no pending members. assert len(group_props['LocalPendingMembers']) == 0, group_props assert len(group_props['RemotePendingMembers']) == 0, group_props # Check that test_'s handle owner is us, and that test (if it's there) has # no owner. handle_owners = group_props['HandleOwners'] assertEquals(conn.Properties.Get(cs.CONN, "SelfHandle"), handle_owners[t_]) if not transient_conflict: assertEquals(0, handle_owners[t]) # test that closing the channel results in an unavailable message to the # right jid text_chan.Close() event = q.expect('stream-presence', to=member_) assertEquals('unavailable', event.stanza['type'])
def test(q, bus, conn, stream): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) buddy_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo') act_prop_iface = dbus.Interface(conn, 'org.laptop.Telepathy.ActivityProperties') bob_handle = conn.get_contact_handle_sync('bob@localhost') # Bob invites us to a chatroom, pre-seeding properties message = domish.Element(('jabber:client', 'message')) message['from'] = 'bob@localhost' message['to'] = 'test@localhost' properties = message.addElement((ns.OLPC_ACTIVITY_PROPS, 'properties')) properties['room'] = '*****@*****.**' properties['activity'] = 'foo_id' property = properties.addElement((None, 'property')) property['type'] = 'str' property['name'] = 'title' property.addContent('From the invitation') property = properties.addElement((None, 'property')) property['type'] = 'bool' property['name'] = 'private' property.addContent('1') stream.send(message) message = domish.Element((None, 'message')) message['from'] = '*****@*****.**' message['to'] = 'test@localhost' x = message.addElement((ns.MUC_USER, 'x')) invite = x.addElement((None, 'invite')) invite['from'] = 'bob@localhost' reason = invite.addElement((None, 'reason')) reason.addContent('No good reason') stream.send(message) event = q.expect('dbus-signal', signal='NewChannel') assert event.args[1] == cs.CHANNEL_TYPE_TEXT assert event.args[2] == 2 # handle type assert event.args[3] == 1 # handle room_handle = 1 text_chan = wrap_channel(bus.get_object(conn.bus_name, event.args[0]), 'Text') group_iface = text_chan.Group members = group_iface.GetAllMembers()[0] local_pending = group_iface.GetAllMembers()[1] remote_pending = group_iface.GetAllMembers()[2] assert len(members) == 1 assert conn.inspect_contact_sync(members[0]) == 'bob@localhost' bob_handle = members[0] assert len(local_pending) == 1 # FIXME: the username-part-is-nickname assumption assert conn.inspect_contact_sync(local_pending[0]) == \ '[email protected]/test' assert len(remote_pending) == 0 room_self_handle = text_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, "SelfHandle") assert room_self_handle == local_pending[0] # by now, we should have picked up the extra activity properties buddy_iface = dbus.Interface(conn, 'org.laptop.Telepathy.BuddyInfo') call_async(q, buddy_iface, 'GetActivities', bob_handle) event = q.expect('stream-iq', iq_type='get', to='bob@localhost') # Bob still has no (public) activities event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'bob@localhost' stream.send(event.stanza) event = q.expect('dbus-return', method='GetActivities') assert event.value == ([('foo_id', room_handle)], ) props = act_prop_iface.GetProperties(room_handle) assert len(props) == 2 assert props['title'] == 'From the invitation' assert props['private'] == True # Now Bob changes the properties message = domish.Element(('jabber:client', 'message')) message['from'] = 'bob@localhost' message['to'] = 'test@localhost' properties = message.addElement((ns.OLPC_ACTIVITY_PROPS, 'properties')) properties['room'] = '*****@*****.**' properties['activity'] = 'foo_id' property = properties.addElement((None, 'property')) property['type'] = 'str' property['name'] = 'title' property.addContent('Mushroom, mushroom') property = properties.addElement((None, 'property')) property['type'] = 'bool' property['name'] = 'private' property.addContent('0') stream.send(message) event = q.expect('dbus-signal', signal='ActivityPropertiesChanged') assert event.args == [ room_handle, { 'title': 'Mushroom, mushroom', 'private': False } ] assert act_prop_iface.GetProperties(room_handle) == \ event.args[1] # OK, now accept the invitation call_async(q, group_iface, 'AddMembers', [room_self_handle], 'Oh, OK then') q.expect_many( EventPattern('stream-presence', to='[email protected]/test'), EventPattern('dbus-signal', signal='MembersChanged', args=[ '', [], [bob_handle], [], [room_self_handle], 0, cs.GC_REASON_INVITED ]), EventPattern('dbus-return', method='AddMembers'), ) # Send presence for own membership of room. stream.send( make_muc_presence('owner', 'moderator', '*****@*****.**', 'test')) event = q.expect('dbus-signal', signal='MembersChanged') assert event.args == ['', [room_self_handle], [], [], [], 0, 0] call_async(q, buddy_iface, 'SetActivities', [('foo_id', room_handle)]) event = q.expect('stream-iq', iq_type='set') # Now that it's not private, it'll go in my PEP event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) q.expect('dbus-return', method='SetActivities') # Bob changes the properties and tells the room he's done so message = domish.Element(('jabber:client', 'message')) message['from'] = '[email protected]/bob' message['to'] = '*****@*****.**' properties = message.addElement((ns.OLPC_ACTIVITY_PROPS, 'properties')) properties['activity'] = 'foo_id' property = properties.addElement((None, 'property')) property['type'] = 'str' property['name'] = 'title' property.addContent('Badger badger badger') property = properties.addElement((None, 'property')) property['type'] = 'bool' property['name'] = 'private' property.addContent('0') stream.send(message) event = q.expect('stream-iq', iq_type='set') message = event.stanza activities = xpath.queryForNodes('/iq/pubsub/publish/item/activities', message) assert (activities is not None and len(activities) == 1), repr(activities) assert activities[0].uri == ns.OLPC_ACTIVITY_PROPS properties = xpath.queryForNodes('/activities/properties', activities[0]) assert (properties is not None and len(properties) == 1), repr(properties) assert properties[0].uri == ns.OLPC_ACTIVITY_PROPS assert properties[0]['room'] == '*****@*****.**' assert properties[0]['activity'] == 'foo_id' property = xpath.queryForNodes('/properties/property', properties[0]) assert (property is not None and len(property) == 2), repr(property) seen = set() for p in property: seen.add(p['name']) if p['name'] == 'title': assert p['type'] == 'str' assert str(p) == 'Badger badger badger' elif p['name'] == 'private': assert p['type'] == 'bool' assert str(p) == '0' else: assert False, 'Unexpected property %s' % p['name'] assert 'title' in seen, seen assert 'private' in seen, seen event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) act_prop_iface = dbus.Interface(conn, 'org.laptop.Telepathy.ActivityProperties') # test sets the title and sets private back to True call_async(q, act_prop_iface, 'SetProperties', room_handle, { 'title': 'I can set the properties too', 'private': True }) event = q.expect('stream-message', to='*****@*****.**') message = event.stanza properties = xpath.queryForNodes('/message/properties', message) assert (properties is not None and len(properties) == 1), repr(properties) assert properties[0].uri == ns.OLPC_ACTIVITY_PROPS assert properties[0]['room'] == '*****@*****.**' assert properties[0]['activity'] == 'foo_id' property = xpath.queryForNodes('/properties/property', properties[0]) assert (property is not None and len(property) == 2), repr(property) seen = set() for p in property: seen.add(p['name']) if p['name'] == 'title': assert p['type'] == 'str' assert str(p) == 'I can set the properties too' elif p['name'] == 'private': assert p['type'] == 'bool' assert str(p) == '1' else: assert False, 'Unexpected property %s' % p['name'] assert 'title' in seen, seen assert 'private' in seen, seen event = q.expect('stream-iq', iq_type='set') event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) message = event.stanza activities = xpath.queryForNodes('/iq/pubsub/publish/item/activities', message) assert (activities is not None and len(activities) == 1), repr(activities) assert activities[0].uri == ns.OLPC_ACTIVITY_PROPS properties = xpath.queryForNodes('/activities/properties', activities[0]) assert properties is None, repr(properties) event = q.expect('stream-iq', iq_type='set') event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) message = event.stanza activities = xpath.queryForNodes('/iq/pubsub/publish/item/activities', message) assert (activities is not None and len(activities) == 1), repr(activities) assert activities[0].uri == ns.OLPC_ACTIVITIES activity = xpath.queryForNodes('/activities/activity', activities[0]) assert activity is None, repr(activity) q.expect('dbus-return', method='SetProperties') # test sets the title and sets private back to True call_async(q, act_prop_iface, 'SetProperties', room_handle, { 'title': 'I can set the properties too', 'private': False }) event = q.expect('stream-message', to='*****@*****.**') message = event.stanza properties = xpath.queryForNodes('/message/properties', message) assert (properties is not None and len(properties) == 1), repr(properties) assert properties[0].uri == ns.OLPC_ACTIVITY_PROPS assert properties[0]['room'] == '*****@*****.**' assert properties[0]['activity'] == 'foo_id' property = xpath.queryForNodes('/properties/property', properties[0]) assert (property is not None and len(property) == 2), repr(property) seen = set() for p in property: seen.add(p['name']) if p['name'] == 'title': assert p['type'] == 'str' assert str(p) == 'I can set the properties too' elif p['name'] == 'private': assert p['type'] == 'bool' assert str(p) == '0' else: assert False, 'Unexpected property %s' % p['name'] assert 'title' in seen, seen assert 'private' in seen, seen event = q.expect('stream-iq', iq_type='set') event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) message = event.stanza activities = xpath.queryForNodes('/iq/pubsub/publish/item/activities', message) assert (activities is not None and len(activities) == 1), repr(activities) assert activities[0].uri == ns.OLPC_ACTIVITY_PROPS properties = xpath.queryForNodes('/activities/properties', activities[0]) assert (properties is not None and len(properties) == 1), repr(properties) assert properties[0].uri == ns.OLPC_ACTIVITY_PROPS assert properties[0]['room'] == '*****@*****.**' assert properties[0]['activity'] == 'foo_id' property = xpath.queryForNodes('/properties/property', properties[0]) assert (property is not None and len(property) == 2), repr(property) seen = set() for p in property: seen.add(p['name']) if p['name'] == 'title': assert p['type'] == 'str' assert str(p) == 'I can set the properties too' elif p['name'] == 'private': assert p['type'] == 'bool' assert str(p) == '0' else: assert False, 'Unexpected property %s' % p['name'] assert 'title' in seen, seen assert 'private' in seen, seen event = q.expect('stream-iq', iq_type='set') event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) message = event.stanza activities = xpath.queryForNodes('/iq/pubsub/publish/item/activities', message) assert (activities is not None and len(activities) == 1), repr(activities) assert activities[0].uri == ns.OLPC_ACTIVITIES activity = xpath.queryForNodes('/activities/activity', activities[0]) assert (activity is not None and len(activity) == 1), repr(activity) assert activity[0]['room'] == '*****@*****.**' assert activity[0]['type'] == 'foo_id' # sic q.expect('dbus-return', method='SetProperties') text_chan.Close() # we must echo the MUC presence so the room will actually close event = q.expect('stream-presence', to='[email protected]/test', presence_type='unavailable') echo_muc_presence(q, stream, event.stanza, 'none', 'participant') event = q.expect('stream-iq', iq_type='set') event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) message = event.stanza activities = xpath.queryForNodes('/iq/pubsub/publish/item/activities', message) assert (activities is not None and len(activities) == 1), repr(activities) assert activities[0].uri == ns.OLPC_ACTIVITIES activity = xpath.queryForNodes('/activities/activity', activities[0]) assert activity is None, repr(activity) event = q.expect('stream-iq', iq_type='set') event.stanza['type'] = 'result' event.stanza['to'] = 'test@localhost' event.stanza['from'] = 'test@localhost' stream.send(event.stanza) message = event.stanza activities = xpath.queryForNodes('/iq/pubsub/publish/item/activities', message) assert (activities is not None and len(activities) == 1), repr(activities) assert activities[0].uri == ns.OLPC_ACTIVITY_PROPS properties = xpath.queryForNodes('/activities/properties', activities[0]) assert properties is None, repr(properties)
def test(q, bus, conn, stream): self_handle = conn.GetSelfHandle() jid = '*****@*****.**' full_jid = '[email protected]/Foo' foo_handle = conn.RequestHandles(cs.HT_CONTACT, [jid])[0] path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: foo_handle, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState', 'Destroyable']) presence = make_presence(full_jid, status='hello', caps={ 'node': 'http://telepathy.freedesktop.org/homeopathy', 'ver' : '0.1', }) stream.send(presence) version_event = q.expect('stream-iq', to=full_jid, query_ns=ns.DISCO_INFO, query_node='http://telepathy.freedesktop.org/homeopathy#0.1') result = make_result_iq(stream, version_event.stanza) query = result.firstChildElement() feature = query.addElement('feature') feature['var'] = ns.CHAT_STATES stream.send(result) sync_stream(q, stream) # Receiving chat states: # Composing... stream.send(make_message(full_jid, state='composing')) changed = q.expect('dbus-signal', signal='ChatStateChanged') handle, state = changed.args assertEquals(foo_handle, handle) assertEquals(cs.CHAT_STATE_COMPOSING, state) # Message! stream.send(make_message(full_jid, body='hello', state='active')) changed = q.expect('dbus-signal', signal='ChatStateChanged') handle, state = changed.args assertEquals(foo_handle, handle) assertEquals(cs.CHAT_STATE_ACTIVE, state) # Sending chat states: # Composing... chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) stream_message = q.expect('stream-message') check_state_notification(stream_message.stanza, 'composing') # XEP 0085: # every content message SHOULD contain an <active/> notification. chan.Text.Send(0, 'hi.') stream_message = q.expect('stream-message') elem = stream_message.stanza assertEquals('chat', elem['type']) check_state_notification(elem, 'active', allow_body=True) def is_body(e): if e.name == 'body': assert e.children[0] == u'hi.', e.toXml() return True return False assert len([x for x in elem.elements() if is_body(x)]) == 1, elem.toXml() # Close the channel without acking the received message. The peer should # get a <gone/> notification, and the channel should respawn. chan.Close() gone, _, _ = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='NewChannel'), ) check_state_notification(gone.stanza, 'gone') # Reusing the proxy object because we happen to know it'll be at the same # path... # Destroy the channel. The peer shouldn't get a <gone/> notification, since # we already said we were gone and haven't sent them any messages to the # contrary. es = [EventPattern('stream-message')] q.forbid_events(es) chan.Destroyable.Destroy() sync_stream(q, stream) # Make the channel anew. path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: foo_handle, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState', 'Destroyable']) # Close it immediately; the peer should again not get a <gone/> # notification, since we haven't sent any notifications on that channel. chan.Close() sync_stream(q, stream) q.unforbid_events(es) # XEP-0085 §5.1 defines how to negotiate support for chat states with a # contact in the absence of capabilities. This is useful when talking to # invisible contacts, for example. # First, if we receive a message from a contact, containing an <active/> # notification, they support chat states, so we should send them. jid = '*****@*****.**' full_jid = jid + '/GTalk' path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState']) stream.send(make_message(full_jid, body='i am invisible', state='active')) changed = q.expect('dbus-signal', signal='ChatStateChanged') assertEquals(cs.CHAT_STATE_ACTIVE, changed.args[1]) # We've seen them send a chat state notification, so we should send them # notifications when the UI tells us to. chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) stream_message = q.expect('stream-message', to=full_jid) check_state_notification(stream_message.stanza, 'composing') changed = q.expect('dbus-signal', signal='ChatStateChanged') handle, state = changed.args assertEquals(cs.CHAT_STATE_COMPOSING, state) assertEquals(self_handle, handle) chan.Text.Send(0, 'very convincing') stream_message = q.expect('stream-message', to=full_jid) check_state_notification(stream_message.stanza, 'active', allow_body=True) # Now, test the case where we start the negotiation, and the contact # turns out to support chat state notifications. jid = '*****@*****.**' full_jid = jid + '/GTalk' path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState']) # We shouldn't send any notifications until we actually send a message. e = EventPattern('stream-message', to=jid) q.forbid_events([e]) for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) # When we send a message, say we're active. chan.Text.Send(0, 'is anyone there?') stream_message = q.expect('stream-message', to=jid) check_state_notification(stream_message.stanza, 'active', allow_body=True) # We get a notification back from our contact. stream.send(make_message(full_jid, state='composing')) # Wait until gabble tells us the chat-state of the remote party has # changed so we know gabble knows chat state notification are supported changed = q.expect('dbus-signal', signal='ChatStateChanged') handle, state = changed.args assertEquals(cs.CHAT_STATE_COMPOSING, state) assertNotEquals(self_handle, handle) # So now we know they support notification, so should send notifications. chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) # This doesn't check whether we're sending to the bare jid, or the # jid+resource. In fact, the notification is sent to the bare jid, because # we only update which jid we send to when we actually receive a message, # not when we receive a notification. wjt thinks this is less surprising # than the alternative: # # • I'm talking to you on my N900, and signed in on my laptop; # • I enter one character in a tab to you on my laptop, and then delete # it; # • Now your messages to me appear on my laptop (until I send you another # one from my N900)! stream_message = q.expect('stream-message') check_state_notification(stream_message.stanza, 'composing') # But! Now they start messaging us from a different client, which *doesn't* # support notifications. other_jid = jid + '/Library' stream.send(make_message(other_jid, body='grr, library computers')) q.expect('dbus-signal', signal='Received') # Okay, we should stop sending typing notifications. e = EventPattern('stream-message', to=other_jid) q.forbid_events([e]) for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) # Now, test the case where we start the negotiation, and the contact # does not support chat state notifications jid = '*****@*****.**' full_jid = jid + '/Nonsense' path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState']) # We shouldn't send any notifications until we actually send a message. e = EventPattern('stream-message', to=jid) q.forbid_events([e]) for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) # When we send a message, say we're active. chan.Text.Send(0, '#n900 #maemo #zomg #woo #yay http://bit.ly/n900') stream_message = q.expect('stream-message', to=jid) check_state_notification(stream_message.stanza, 'active', allow_body=True) # They reply without a chat state. stream.send(make_message(full_jid, body="posted.")) q.expect('dbus-signal', signal='Received') # Okay, we shouldn't send any more. e = EventPattern('stream-message', to=other_jid) q.forbid_events([e]) for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) chan.Text.Send(0, '@stephenfry simmer down') message = q.expect('stream-message') states = [x for x in message.stanza.elements() if x.uri == ns.CHAT_STATES] assertLength(0, states)
def test(q, bus, conn): contact1_name, conn2, contact2_name, contact2_handle_on_conn1,\ contact1_handle_on_conn2 = t.connect_two_accounts(q, bus, conn) conn1_self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") conn2_self_handle = conn2.Properties.Get(cs.CONN, "SelfHandle") # first connection: join muc muc_handle1, group1 = t.join_muc(q, conn, muc_name) # Can we request muc D-Bus tubes? properties = conn.GetAll(cs.CONN_IFACE_REQUESTS, dbus_interface=cs.PROPERTIES_IFACE) assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM}, [cs.TARGET_HANDLE, cs.TARGET_ID, cs.DBUS_TUBE_SERVICE_NAME] ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] # request a stream tube channel (new API) requestotron = dbus.Interface(conn, cs.CONN_IFACE_REQUESTS) requestotron.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: muc_name, cs.DBUS_TUBE_SERVICE_NAME: 'com.example.TestCase' }) e = q.expect('dbus-signal', signal='NewChannels') channels = e.args[0] assert len(channels) == 1 # get the list of all channels to check that newly announced ones are in it all_channels = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) path, props = channels[0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE assert props[cs.REQUESTED] == True assertSameSets([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE], props[cs.INTERFACES]) assert props[cs.DBUS_TUBE_SERVICE_NAME] == 'com.example.TestCase' assert props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS] == [ cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST ] contact1_tube = wrap_channel(bus.get_object(conn.bus_name, path), 'DBusTube') tube1_path = path assert (path, props) in all_channels, (path, props) state = contact1_tube.Properties.Get(cs.CHANNEL_IFACE_TUBE, 'State') assert state == cs.TUBE_CHANNEL_STATE_NOT_OFFERED call_async(q, contact1_tube.DBusTube, 'Offer', sample_parameters, cs.SOCKET_ACCESS_CONTROL_CREDENTIALS) _, e = q.expect_many( EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[cs.TUBE_CHANNEL_STATE_OPEN]), EventPattern('dbus-return', method='Offer')) tube_addr1 = e.value[0] state = contact1_tube.Properties.Get(cs.CHANNEL_IFACE_TUBE, 'State') assert state == cs.TUBE_CHANNEL_STATE_OPEN check_dbus_names(contact1_tube, [conn1_self_handle]) t.invite_to_muc(q, group1, conn2, contact2_handle_on_conn1, contact1_handle_on_conn2) # tubes channel is created e, dbus_names_e = q.expect_many( EventPattern('dbus-signal', signal='NewChannels'), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE)) channels = e.args[0] assert len(channels) == 1 # get the list of all channels to check that newly announced ones are in it all_channels = conn2.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) path, props = channels[0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE assert props[cs.REQUESTED] == False assertSameSets([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE], props[cs.INTERFACES]) assert props[cs.TUBE_PARAMETERS] == sample_parameters assert props[cs.DBUS_TUBE_SERVICE_NAME] == 'com.example.TestCase' assert props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS] == [ cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST ] contact2_tube = wrap_channel(bus.get_object(conn.bus_name, path), 'DBusTube') tube2_path = path assert (path, props) in all_channels, (path, props) # second connection: check DBusNamesChanged signal assert dbus_names_e.path == tube2_path added, removed = dbus_names_e.args assert added.keys() == [contact1_handle_on_conn2] assert removed == [] state = contact2_tube.Properties.Get(cs.CHANNEL_IFACE_TUBE, 'State') assert state == cs.TUBE_CHANNEL_STATE_LOCAL_PENDING # first connection: contact2 is not in the tube yet check_dbus_names(contact1_tube, [conn1_self_handle]) # second connection: accept the tube (new API) tube_addr2 = unix_socket_adr = contact2_tube.DBusTube.Accept( cs.SOCKET_ACCESS_CONTROL_CREDENTIALS) state = contact2_tube.Properties.Get(cs.CHANNEL_IFACE_TUBE, 'State') assert state == cs.TUBE_CHANNEL_STATE_OPEN e, dbus_names_e = q.expect_many( EventPattern('dbus-signal', signal='TubeChannelStateChanged', path=tube2_path, args=[cs.TUBE_CHANNEL_STATE_OPEN]), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE, path=tube1_path)) added, removed = dbus_names_e.args assert added.keys() == [contact2_handle_on_conn1] assert removed == [] check_dbus_names(contact1_tube, [conn1_self_handle, contact2_handle_on_conn1]) check_dbus_names(contact2_tube, [conn2_self_handle, contact1_handle_on_conn2]) tube2_names = contact2_tube.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) tube_conn1 = dbus.connection.Connection(tube_addr1) tube_conn2 = dbus.connection.Connection(tube_addr2) obj1 = Test(tube_conn1, q) # fire 'MySig' signal on the tube def my_sig_cb(arg, sender=None): assert tube2_names[contact1_handle_on_conn2] == sender q.append(Event('tube-dbus-signal', signal='MySig', args=[arg])) tube_conn2.add_signal_receiver(my_sig_cb, 'MySig', IFACE, path=PATH, sender_keyword='sender') obj1.MySig('hello') q.expect('tube-dbus-signal', signal='MySig', args=['hello']) # call remote method def my_method_cb(result): q.append(Event('tube-dbus-return', method='MyMethod', value=[result])) def my_method_error(e): assert False, e tube_conn2.get_object(tube2_names[contact1_handle_on_conn2], PATH).MyMethod(42, dbus_interface=IFACE, reply_handler=my_method_cb, error_handler=my_method_error) q.expect('tube-dbus-call', method='MyMethod', args=[42]) q.expect('tube-dbus-return', method='MyMethod', value=[420]) call_async(q, contact1_tube, 'Close') _, _, _, dbus_names_e = q.expect_many( EventPattern('dbus-return', method='Close'), EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE, path=tube2_path)) # Contact1 is removed from the tube added, removed = dbus_names_e.args assert added == {} assert removed == [contact1_handle_on_conn2] check_dbus_names(contact2_tube, [conn2_self_handle]) call_async(q, contact2_tube, 'Close') q.expect_many(EventPattern('dbus-return', method='Close'), EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) conn.Disconnect() conn2.Disconnect()
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.Properties.Get(cs.CONN, "SelfHandle") self_name = conn.inspect_contact_sync(self_handle) # 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) exv = q.expect('dbus-signal', signal='NewChannels') channels = exv.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 = wrap_channel(bus.get_object(conn.bus_name, path), 'DBusTube') tube_props = tube_chan.Properties.GetAll(cs.CHANNEL_IFACE_TUBE, byte_arrays=True) assert tube_props['State'] == cs.TUBE_CHANNEL_STATE_NOT_OFFERED # try to offer using a wrong access control try: tube_chan.DBusTube.Offer(sample_parameters, cs.SOCKET_ACCESS_CONTROL_PORT) except dbus.DBusException as e: assertEquals(e.get_dbus_name(), cs.INVALID_ARGUMENT) else: assert False # offer the tube call_async(q, tube_chan.DBusTube, 'Offer', sample_parameters, access_control) presence_event, return_event, status_event, dbus_changed_event = q.expect_many( EventPattern('stream-presence', to='[email protected]/test', predicate=lambda e: t.presence_contains_tube(e)), EventPattern('dbus-return', method='Offer'), EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[cs.TUBE_CHANNEL_STATE_OPEN]), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE)) tube_self_handle = tube_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'SelfHandle') assert tube_self_handle != 0 # handle presence_event # We announce our newly created tube in our muc presence presence = presence_event.stanza dbus_stream_id, my_bus_name, dbus_tube_id = check_tube_in_presence( presence, '[email protected]/test') # handle dbus_changed_event added, removed = dbus_changed_event.args assert added == {tube_self_handle: my_bus_name} assert removed == [] dbus_tube_adr = return_event.value[0] bob_bus_name = ':2.Ym9i' bob_handle = conn.get_contact_handle_sync('[email protected]/bob') def bob_in_tube(): presence = elem('presence', from_='[email protected]/bob', to='*****@*****.**')( elem('x', xmlns=ns.MUC_USER), elem('tubes', xmlns=ns.TUBES)( elem('tube', type='dbus', initiator='[email protected]/test', service='com.example.TestCase', id=str(dbus_tube_id))(elem('parameters')( elem('parameter', name='ay', type='bytes')(u'aGVsbG8='), elem('parameter', name='s', type='str')(u'hello'), elem('parameter', name='i', type='int')(u'-123'), elem('parameter', name='u', type='uint')(u'123'))))) # have to add stream-id and dbus-name attributes manually as we can't use # keyword with '-'... tube_node = xpath.queryForNodes('/presence/tubes/tube', presence)[0] tube_node['stream-id'] = dbus_stream_id tube_node['dbus-name'] = bob_bus_name stream.send(presence) # Bob joins the tube bob_in_tube() dbus_changed_event = q.expect('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE) added, removed = dbus_changed_event.args assert added == {bob_handle: bob_bus_name} assert removed == [] tube = Connection(dbus_tube_adr) fire_signal_on_tube(q, tube, '*****@*****.**', dbus_stream_id, my_bus_name) names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assert names == {tube_self_handle: my_bus_name, bob_handle: bob_bus_name} # Bob leave the tube presence = elem('presence', from_='[email protected]/bob', to='*****@*****.**')(elem('x', xmlns=ns.MUC_USER), elem('tubes', xmlns=ns.TUBES)) stream.send(presence) dbus_changed_event = q.expect('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE) added, removed = dbus_changed_event.args assert added == {} assert removed == [bob_handle] names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assert names == {tube_self_handle: my_bus_name} tube_chan.Channel.Close() _, _, event = q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'), EventPattern('stream-presence', to='[email protected]/test', presence_type='unavailable')) # we must echo the MUC presence so the room will actually close # and we should wait to make sure gabble has actually parsed our # echo before trying to rejoin echo_muc_presence(q, stream, event.stanza, 'none', 'participant') sync_stream(q, stream) # rejoin the room call_async( q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: '*****@*****.**' }) q.expect('stream-presence', to='[email protected]/test') # Bob is in the room and in the tube bob_in_tube() # Send presence for own membership of room. stream.send(make_muc_presence('none', 'participant', muc, 'test')) def new_tube(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE def new_text(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT # tube and text is created text_event, tube_event = q.expect_many( EventPattern('dbus-signal', signal='NewChannels', predicate=new_text), EventPattern('dbus-signal', signal='NewChannels', predicate=new_tube)) channels = exv.args[0] tube_path, props = tube_event.args[0][0] assertEquals(cs.CHANNEL_TYPE_DBUS_TUBE, props[cs.CHANNEL_TYPE]) assertEquals('[email protected]/test', props[cs.INITIATOR_ID]) assertEquals(False, props[cs.REQUESTED]) assertEquals(cs.HT_ROOM, props[cs.TARGET_HANDLE_TYPE]) assertEquals('com.example.TestCase', props[cs.DBUS_TUBE_SERVICE_NAME]) _, props = text_event.args[0][0] assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE]) assertEquals(True, props[cs.REQUESTED]) # tube is local-pending tube_chan = bus.get_object(conn.bus_name, tube_path) state = tube_chan.Get(cs.CHANNEL_IFACE_TUBE, 'State', dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(cs.TUBE_STATE_LOCAL_PENDING, state)
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) t.check_conn_properties(q, conn) bob_handle = conn.get_contact_handle_sync('[email protected]/bob') address = t.create_server(q, address_type) def new_chan_predicate(e): types = [] for _, props in e.args[0]: types.append(props[cs.CHANNEL_TYPE]) return cs.CHANNEL_TYPE_STREAM_TUBE in types def find_stream_tube(channels): for path, props in channels: if props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE: return path, props return None, None # 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) e = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) path, prop = find_stream_tube(e.args[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 = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamTube') 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, tube_chan.StreamTube, 'Offer', address_type, address, access_control, {'foo': 'bar'}) stream_event, _, status_event = q.expect_many( EventPattern('stream-presence', to='[email protected]/test', predicate=lambda e: t.presence_contains_tube(e)), EventPattern('dbus-return', method='Offer'), EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[cs.TUBE_CHANNEL_STATE_OPEN])) tube_self_handle = tube_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'SelfHandle') assert conn.inspect_contact_sync( tube_self_handle) == '[email protected]/test' 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 stream_tube_id = 666 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') stream_tube_id = int(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.get_contact_handle_sync('[email protected]/bob') 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) tube_chan.Channel.Close() q.expect_many(EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) t.cleanup()
def test(q, bus, conn, stream): self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") jid = '*****@*****.**' foo_handle = conn.get_contact_handle_sync(jid) call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: foo_handle }) ret, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) text_chan = wrap_channel(bus.get_object(conn.bus_name, ret.value[0]), 'Text') chan_iface = dbus.Interface(text_chan, cs.CHANNEL) assert len(new_sig.args) == 1 assert len(new_sig.args[0]) == 1 # one channel assert len(new_sig.args[0][0]) == 2 # two struct members assert new_sig.args[0][0][0] == ret.value[0] emitted_props = new_sig.args[0][0][1] assert emitted_props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT assert emitted_props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT assert emitted_props[cs.TARGET_HANDLE] == foo_handle assert emitted_props[cs.TARGET_ID] == jid assert emitted_props[cs.REQUESTED] == True assert emitted_props[cs.INITIATOR_HANDLE] == self_handle assert emitted_props[cs.INITIATOR_ID] == 'test@localhost' channel_props = text_chan.GetAll( cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assert channel_props['TargetID'] == jid,\ (channel_props['TargetID'], jid) assert channel_props['Requested'] == True assert channel_props['InitiatorHandle'] == self_handle,\ (channel_props['InitiatorHandle'], self_handle) assert channel_props['InitiatorID'] == 'test@localhost',\ channel_props['InitiatorID'] text_chan.send_msg_sync('hey') event = q.expect('stream-message') elem = event.stanza assert elem.name == 'message' assert elem['type'] == 'chat' body = list(event.stanza.elements())[0] assert body.name == 'body' assert body.children[0] == u'hey' # <message type="chat"><body>hello</body</message> m = domish.Element((None, 'message')) m['from'] = '[email protected]/Pidgin' m['type'] = 'chat' m.addElement('body', content='hello') stream.send(m) event = q.expect('dbus-signal', signal='MessageReceived') msg = event.args[0] assertEquals(foo_handle, msg[0]['message-sender']) assertEquals('hello', msg[1]['content']) messages = text_chan.Properties.Get(cs.CHANNEL_IFACE_MESSAGES, 'PendingMessages') assertEquals([msg], messages) # close the channel without acking the message; it comes back call_async(q, chan_iface, 'Close') old, new = q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'), ) assert old.path == text_chan.object_path,\ (old.path, text_chan.object_path) assert new.args[0] == text_chan.object_path,\ (new.args[0], text_chan.object_path) event = q.expect('dbus-signal', signal='NewChannels') path, props = event.args[0][0] assertEquals(text_chan.object_path, path) assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE]) assertEquals(cs.HT_CONTACT, props[cs.TARGET_HANDLE_TYPE]) assertEquals(foo_handle, props[cs.TARGET_HANDLE]) event = q.expect('dbus-return', method='Close') # it now behaves as if the message had initiated it channel_props = text_chan.GetAll( cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assert channel_props['TargetID'] == jid,\ (channel_props['TargetID'], jid) assert channel_props['Requested'] == False assert channel_props['InitiatorHandle'] == foo_handle,\ (channel_props['InitiatorHandle'], foo_handle) assert channel_props['InitiatorID'] == '*****@*****.**',\ channel_props['InitiatorID'] # the message is still there messages = text_chan.Properties.Get(cs.CHANNEL_IFACE_MESSAGES, 'PendingMessages') msg[0]['rescued'] = True assertEquals([msg], messages) # acknowledge it text_chan.Text.AcknowledgePendingMessages([msg[0]['pending-message-id']]) messages = text_chan.Properties.Get(cs.CHANNEL_IFACE_MESSAGES, 'PendingMessages') assertEquals([], messages) # close the channel again call_async(q, chan_iface, 'Close') event = q.expect('dbus-signal', signal='Closed') assert event.path == text_chan.object_path,\ (event.path, text_chan.object_path) event = q.expect('dbus-return', method='Close') # assert that it stays dead this time! try: chan_iface.GetChannelType() except dbus.DBusException: pass else: raise AssertionError("Why won't it die?")
def test(q, bus, conn, stream, use_room=False): conn.Connect() q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]), EventPattern('irc-connected')) q.expect('dbus-signal', signal='SelfHandleChanged') q.expect('dbus-signal', signal='StatusChanged', args=[0, 1]) self_handle = conn.Get(cs.CONN, 'SelfHandle', dbus_interface=cs.PROPERTIES_IFACE) request = build_request(conn, '#idletest', use_room) call_async(q, conn.Requests, 'CreateChannel', request) # Idle should try to join the channel. q.expect('stream-JOIN') # Meanwhile, in another application... call_async(q, conn, 'EnsureChannel', request, dbus_interface=cs.CONN_IFACE_REQUESTS) sync_dbus(bus, q, conn) # Now the ircd responds: stream.sendJoin('#idletest') cc, ec = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-return', method='EnsureChannel'), ) nc = q.expect('dbus-signal', signal='NewChannels') path, props = cc.value assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT assertSameSets([ cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_PASSWORD, cs.CHANNEL_IFACE_MESSAGES, cs.CHANNEL_IFACE_ROOM, cs.CHANNEL_IFACE_SUBJECT, cs.CHANNEL_IFACE_ROOM_CONFIG, cs.CHANNEL_IFACE_DESTROYABLE, ], props[cs.INTERFACES]) assert props[cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM assert props[cs.TARGET_ID] == '#idletest' assertEquals('#idletest', props[cs.ROOM_NAME]) assertEquals('', props[cs.ROOM_SERVER]) assert props[cs.REQUESTED] assert props[cs.INITIATOR_HANDLE] == self_handle assert props[cs.INITIATOR_ID] == \ conn.inspect_contacts_sync([self_handle])[0] ec_yours, ec_path, ec_props = ec.value assert not ec_yours assert ec_path == path assert ec_props == props channels = nc.args[0] assert len(channels) == 1 nc_path, nc_props = channels[0] assert nc_path == path assert nc_props == props # And again? ec_ = conn.EnsureChannel(request, dbus_interface=cs.CONN_IFACE_REQUESTS) assert ec.value == ec_ chans = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE) assert len(chans) == 1 assert chans[0] == (path, props) chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['Destroyable', 'Messages']) # Put an unacknowledged message into the channel stream.sendMessage('PRIVMSG', '#idletest', ':oi oi', prefix='lol') q.expect('dbus-signal', signal='MessageReceived', path=path) # Make sure Close()ing the channel makes it respawn. This avoids the old # bug where empathy-chat crashing booted you out of all your channels. patterns = [EventPattern('stream-PART')] q.forbid_events(patterns) chan.Close() q.expect('dbus-signal', signal='Closed', path=chan.object_path) e = q.expect('dbus-signal', signal='NewChannels') path, props = e.args[0][0] assertEquals(chan.object_path, path) # We requested the channel originally, but we didn't request it popping # back up. assertEquals(0, props[cs.INITIATOR_HANDLE]) assert not props[cs.REQUESTED] # The unacknowledged message should still be there and be marked as rescued. messages = chan.Properties.Get(cs.CHANNEL_IFACE_MESSAGES, 'PendingMessages') assertLength(1, messages) assert messages[0][0]['rescued'], messages[0] # Check that ensuring a respawned channel does what you'd expect. ec_yours, ec_path, ec_props = conn.EnsureChannel( request, dbus_interface=cs.CONN_IFACE_REQUESTS) assert not ec_yours assertEquals(chan.object_path, ec_path) assertEquals(props, ec_props) sync_stream(q, stream) q.unforbid_events(patterns) chan.RemoveMembers([self_handle], "bye bye cruel\r\nworld", dbus_interface=cs.CHANNEL_IFACE_GROUP) part_event = q.expect('stream-PART') # This is a regression test for # <https://bugs.freedesktop.org/show_bug.cgi?id=34812>, where part messages # were not correctly colon-quoted. # # It is also a regression test for # <https://bugs.freedesktop.org/show_bug.cgi?id=34840>, where newlines # weren't stripped from part messages. We check that both \r and \n are # replaced by harmless spaces. assertEquals("bye bye cruel world", part_event.data[1]) stream.sendPart('#idletest', stream.nick) q.expect('dbus-signal', signal='Closed') chans = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE) assert len(chans) == 0
def test(q, bus, conn, stream): self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") jid = '*****@*****.**' foo_handle = conn.get_contact_handle_sync(jid) call_async( q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: foo_handle }) ret, sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) text_chan = wrap_channel(bus.get_object(conn.bus_name, ret.value[0]), 'Text') path, props = sig.args[0][0] assertEquals(ret.value[0], path) assertEquals(cs.CHANNEL_TYPE_TEXT, props[cs.CHANNEL_TYPE]) # check that handle type == contact handle assertEquals(cs.HT_CONTACT, props[cs.TARGET_HANDLE_TYPE]) assertEquals(foo_handle, props[cs.TARGET_HANDLE]) # Exercise basic Channel Properties from spec 0.17.7 channel_props = text_chan.GetAll(cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assert channel_props.get('TargetHandle') == foo_handle,\ (channel_props.get('TargetHandle'), foo_handle) assert channel_props.get('TargetHandleType') == 1,\ channel_props.get('TargetHandleType') assert channel_props.get('ChannelType') == \ cs.CHANNEL_TYPE_TEXT,\ channel_props.get('ChannelType') assert cs.CHANNEL_IFACE_CHAT_STATE in \ channel_props.get('Interfaces', ()), \ channel_props.get('Interfaces') assert channel_props['TargetID'] == jid,\ (channel_props['TargetID'], jid) assert channel_props['Requested'] == True assert channel_props['InitiatorHandle'] == self_handle,\ (channel_props['InitiatorHandle'], self_handle) assert channel_props['InitiatorID'] == 'test@localhost',\ channel_props['InitiatorID'] text_chan.send_msg_sync('hey') event = q.expect('stream-message') elem = event.stanza assert elem.name == 'message' assert elem['type'] == 'chat' body = list(event.stanza.elements())[0] assert body.name == 'body' assert body.children[0] == u'hey' # <message type="chat"><body>hello</body</message> m = domish.Element((None, 'message')) m['from'] = '[email protected]/Pidgin' m['type'] = 'chat' m.addElement('body', content='hello') stream.send(m) event = q.expect('dbus-signal', signal='MessageReceived') msg = event.args[0] assertEquals('hello', msg[1]['content'])
def test(jp, q, bus, conn, stream, peer_removes_final_content): jt = JingleTest2(jp, conn, q, stream, 'test@localhost', '[email protected]/Foo') jt.prepare() handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0] path = conn.RequestChannel( cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia') chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') stream_id = e.args[1] stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) # Before sending the initiate, request another stream chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_VIDEO]) e = q.expect('dbus-signal', signal='NewStreamHandler') stream_id2 = e.args[1] stream_handler2 = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler2.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) # Before the CM can initiate session, we modify a stream direction. This # should result in a no-op since there's no need to inform the peer of # change. chan.StreamedMedia.RequestStreamDirection(stream_id2, cs.MEDIA_STREAM_DIRECTION_RECEIVE) # We set both streams as ready, which will trigger the session initiate stream_handler.Ready(jt.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) stream_handler2.Ready(jt.get_audio_codecs_dbus()) stream_handler2.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) # We changed our mind locally, don't want video chan.StreamedMedia.RemoveStreams([stream_id2]) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) stream.send(make_result_iq(stream, e.stanza)) jt.parse_session_initiate(e.query) # Gabble sends content-remove for the video stream... e2 = q.expect('stream-iq', predicate=jp.action_predicate('content-remove')) # ...but before the peer notices, they accept the call. jt.accept() # Only now the remote end removes the video stream; if gabble mistakenly # marked it as accepted on session acceptance, it'll crash right about # now. If it's good, stream will be really removed, and # we can proceed. stream.send(make_result_iq(stream, e2.stanza)) q.expect('dbus-signal', signal='StreamRemoved') # Actually, we *do* want video! chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_VIDEO]) e = q.expect('dbus-signal', signal='NewStreamHandler') stream2_id = e.args[1] stream_handler2 = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler2.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler2.Ready(jt.get_audio_codecs_dbus()) stream_handler2.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('content-add')) c = e.query.firstChildElement() assertEquals('initiator', c['creator']) stream.send(make_result_iq(stream, e.stanza)) # Peer accepts jt.content_accept(e.query, 'video') # Let's start sending and receiving video! q.expect_many( EventPattern('dbus-signal', signal='SetStreamPlaying', args=[True]), EventPattern('dbus-signal', signal='SetStreamSending', args=[True]), ) # Now, the call draws to a close. # We first remove the original stream chan.StreamedMedia.RemoveStreams([stream_id]) e = q.expect('stream-iq', predicate=jp.action_predicate('content-remove')) content_remove_ack = make_result_iq(stream, e.stanza) if peer_removes_final_content: # The peer removes the final countdo^W content. From a footnote (!) in # XEP 0166: # If the content-remove results in zero content definitions for the # session, the entity that receives the content-remove SHOULD send # a session-terminate action to the other party (since a session # with no content definitions is void). # So, Gabble should respond to the content-remove with a # session-terminate. node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.peer, 'content-remove', [ jp.Content(c['name'], c['creator'], c['senders']) ]) ]) stream.send(jp.xml(node)) else: # The Telepathy client removes the second stream; Gabble should # terminate the session rather than sending a content-remove. chan.StreamedMedia.RemoveStreams([stream2_id]) st, closed = q.expect_many( EventPattern('stream-iq', predicate=jp.action_predicate('session-terminate')), # Gabble shouldn't wait for the peer to ack the terminate before # considering the call finished. EventPattern('dbus-signal', signal='Closed', path=path)) # Only now does the peer ack the content-remove. This serves as a # regression test for contents outliving the session; if the content didn't # die properly, this crashed Gabble. stream.send(content_remove_ack) sync_stream(q, stream) # The peer can ack the terminate too, just for completeness. stream.send(make_result_iq(stream, st.stanza))
def test(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) self_handle = conn.GetSelfHandle() jid = '*****@*****.**' foo_handle = conn.RequestHandles(cs.HT_CONTACT, [jid])[0] path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: foo_handle, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState', 'Destroyable']) presence = make_presence('[email protected]/Foo', status='hello', caps={ 'node': 'http://telepathy.freedesktop.org/homeopathy', 'ver' : '0.1', }) stream.send(presence) version_event = q.expect('stream-iq', to='[email protected]/Foo', query_ns='http://jabber.org/protocol/disco#info', query_node='http://telepathy.freedesktop.org/homeopathy#0.1') result = make_result_iq(stream, version_event.stanza) query = result.firstChildElement() feature = query.addElement('feature') feature['var'] = 'http://jabber.org/protocol/chatstates' stream.send(result) sync_stream(q, stream) # Receiving chat states: # Composing... m = domish.Element((None, 'message')) m['from'] = '[email protected]/Foo' m['type'] = 'chat' m.addElement((ns.CHAT_STATES, 'composing')) stream.send(m) changed = q.expect('dbus-signal', signal='ChatStateChanged') handle, state = changed.args assertEquals(foo_handle, handle) assertEquals(cs.CHAT_STATE_COMPOSING, state) # Message! m = domish.Element((None, 'message')) m['from'] = '[email protected]/Foo' m['type'] = 'chat' m.addElement((ns.CHAT_STATES, 'active')) m.addElement('body', content='hello') stream.send(m) changed = q.expect('dbus-signal', signal='ChatStateChanged') handle, state = changed.args assertEquals(foo_handle, handle) assertEquals(cs.CHAT_STATE_ACTIVE, state) # Sending chat states: # Composing... call_async(q, chan.ChatState, 'SetChatState', cs.CHAT_STATE_COMPOSING) stream_message = q.expect('stream-message') check_state_notification(stream_message.stanza, 'composing') # XEP 0085: # every content message SHOULD contain an <active/> notification. call_async(q, chan.Text, 'Send', 0, 'hi.') stream_message = q.expect('stream-message') elem = stream_message.stanza assert elem.name == 'message' assert elem['type'] == 'chat', elem['type'] def is_body(e): if e.name == 'body': assert e.children[0] == u'hi.', e.toXml() return True return False def is_active(e): if e.uri == ns.CHAT_STATES: assert e.name == 'active', e.toXml() return True return False children = list(elem.elements()) assert len(filter(is_body, children)) == 1, elem.toXml() assert len(filter(is_active, children)) == 1, elem.toXml() # Close the channel without acking the received message. The peer should # get a <gone/> notification, and the channel should respawn. chan.Close() gone, _, _ = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='NewChannel'), ) check_state_notification(gone.stanza, 'gone') # Reusing the proxy object because we happen to know it'll be at the same # path... # Destroy the channel. The peer shouldn't get a <gone/> notification, since # we already said we were gone and haven't sent them any messages to the # contrary. es = [EventPattern('stream-message')] q.forbid_events(es) chan.Destroyable.Destroy() sync_stream(q, stream) # Make the channel anew. path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: foo_handle, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState', 'Destroyable']) # Close it immediately; the peer should again not get a <gone/> # notification, since we haven't sent any notifications on that channel. chan.Close() sync_stream(q, stream)
def initiate(self): """Brind the call to INITIALISING state. This method will fill the channel, contents and streams members.""" # Ensure a channel that doesn't exist yet. if self.incoming: if self.initial_audio and self.initial_video: self.jt2.incoming_call(audio='audio1', video='video1') elif self.initial_audio: self.jt2.incoming_call(audio='audio1', video=None) else: self.jt2.incoming_call(audio=None, video='video1') else: ret = self.conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: self.peer_handle, cs.CALL_INITIAL_AUDIO: self.initial_audio, cs.CALL_INITIAL_VIDEO: self.initial_video, }) signal = self.q.expect('dbus-signal', signal='NewChannels', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args[0][0][1].values()) assertLength(1, signal.args) assertLength(1, signal.args[0]) # one channel assertLength(2, signal.args[0][0]) # two struct members emitted_props = signal.args[0][0][1] assertEquals( cs.CHANNEL_TYPE_CALL, emitted_props[cs.CHANNEL_TYPE]) peer_bare_jid = self.PEER_JID.split('/')[0] assertEquals(self.peer_handle, emitted_props[cs.TARGET_HANDLE]) assertEquals(cs.HT_CONTACT, emitted_props[cs.TARGET_HANDLE_TYPE]) assertEquals(peer_bare_jid, emitted_props[cs.TARGET_ID]) assertEquals(not self.incoming, emitted_props[cs.REQUESTED]) if self.incoming: assertEquals(self.peer_handle, emitted_props[cs.INITIATOR_HANDLE]) assertEquals(peer_bare_jid, emitted_props[cs.INITIATOR_ID]) else: assertEquals(self.self_handle, emitted_props[cs.INITIATOR_HANDLE]) assertEquals(self.SELF_JID, emitted_props[cs.INITIATOR_ID]) assertEquals(self.initial_audio, emitted_props[cs.CALL_INITIAL_AUDIO]) assertEquals(self.initial_video, emitted_props[cs.CALL_INITIAL_VIDEO]) chan_path = signal.args[0][0][0] self.chan = wrap_channel( self.bus.get_object(self.conn.bus_name, chan_path), 'Call') properties = self.chan.GetAll(cs.CHANNEL_TYPE_CALL, dbus_interface=dbus.PROPERTIES_IFACE) # Check if all the properties are there assertEquals(sorted([ "Contents", "CallMembers", "CallState", "CallFlags", "CallStateReason", "CallStateDetails", "HardwareStreaming", "InitialAudio", "InitialAudioName", "InitialVideo", "InitialVideoName", "MutableContents", "InitialTransport", "MemberIdentifiers" ]), sorted(properties.keys())) # Remote member is the target assertEquals([self.peer_handle], list(properties["CallMembers"].keys())) assertEquals(0, properties["CallMembers"][self.peer_handle]) # No Hardware Streaming for you assertEquals(False, properties["HardwareStreaming"]) # Store content and stream nb_contents = self.initial_audio + self.initial_video assertLength(nb_contents, properties["Contents"]) for content_path in properties["Contents"]: self.store_content(content_path) if self.initial_audio: assert self.audio_content if self.initial_video: assert self.video_content
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) t.check_conn_properties(q, conn) bob_handle = conn.get_contact_handle_sync('[email protected]/bob') address = t.create_server(q, address_type) def new_chan_predicate(e): types = [] for _, props in e.args[0]: types.append(props[cs.CHANNEL_TYPE]) return cs.CHANNEL_TYPE_STREAM_TUBE in types def find_stream_tube(channels): for path, props in channels: if props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE: return path, props return None, None # 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) e = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) path, prop = find_stream_tube(e.args[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 = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamTube') 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, tube_chan.StreamTube, 'Offer', address_type, address, access_control, {'foo': 'bar'}) stream_event, _, status_event = q.expect_many( EventPattern('stream-presence', to='[email protected]/test', predicate=lambda e: t.presence_contains_tube(e)), EventPattern('dbus-return', method='Offer'), EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[cs.TUBE_CHANNEL_STATE_OPEN])) tube_self_handle = tube_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'SelfHandle') assert conn.inspect_contact_sync(tube_self_handle) == '[email protected]/test' 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 stream_tube_id = 666 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') stream_tube_id = int(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.get_contact_handle_sync('[email protected]/bob') 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) tube_chan.Channel.Close() q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed')) t.cleanup()
def test(q, bus, conn, stream): self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") # When Gabble initially requests its avatar from the server, it discovers # it has none. expect_and_handle_get_vcard(q, stream) handle, signalled_token = q.expect('dbus-signal', signal='AvatarUpdated').args assertEquals(self_handle, handle) assertEquals('', signalled_token) # The user sets an avatar. call_async(q, conn.Avatars, 'SetAvatar', AVATAR_1_DATA, AVATAR_1_MIME_TYPE) expect_and_handle_get_vcard(q, stream) expect_and_handle_set_vcard(q, stream) # It's signalled on D-Bus … set_ret, avatar_updated = q.expect_many( EventPattern('dbus-return', method='SetAvatar'), EventPattern('dbus-signal', signal='AvatarUpdated'), ) returned_token, = set_ret.value handle, signalled_token = avatar_updated.args assertEquals(self_handle, handle) assertEquals(returned_token, signalled_token) # … and also on XMPP. broadcast = q.expect('stream-presence', to=None) broadcast_hash = extract_hash_from_presence(broadcast.stanza) assertEquals(AVATAR_1_SHA1, broadcast_hash) # If applications ask Gabble for information about the user's own avatar, # it should be able to answer. (Strictly speaking, expecting Gabble to know # the avatar data is risky because Gabble discards cached vCards after a # while, but we happen to know it takes 20 seconds or so for that to # happen.) known = conn.Avatars.GetKnownAvatarTokens([self_handle]) assertEquals({self_handle: signalled_token}, known) conn.Avatars.RequestAvatars([self_handle]) retrieved = q.expect('dbus-signal', signal='AvatarRetrieved') handle, token, data, mime_type = retrieved.args assertEquals(self_handle, handle) assertEquals(signalled_token, token) assertEquals(AVATAR_1_DATA, data) assertEquals(AVATAR_1_MIME_TYPE, mime_type) # Well, that was quite easy. How about we join a MUC? XEP-0153 §4.1 says: # If a client supports the protocol defined herein, it […] SHOULD # also include the update child in directed presence stanzas (e.g., # directed presence sent when joining Multi-User Chat [5] rooms). # — http://xmpp.org/extensions/xep-0153.html#bizrules-presence join_event = try_to_join_muc(q, bus, conn, stream, MUC) directed_hash = extract_hash_from_presence(join_event.stanza) assertEquals(AVATAR_1_SHA1, directed_hash) # There are two others in the MUC: fredrik has no avatar, wendy has an # avatar. We, of course, have our own avatar. stream.send(make_muc_presence('none', 'participant', MUC, 'fredrik')) stream.send(make_muc_presence('none', 'participant', MUC, 'wendy', photo=AVATAR_2_SHA1)) stream.send(make_muc_presence('owner', 'moderator', MUC, 'test', photo=AVATAR_1_SHA1)) path, _ = q.expect('dbus-return', method='CreateChannel').value chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') members = chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'Members') assertLength(3, members) fredrik, wendy, muc_self_handle = conn.get_contact_handles_sync( ['%s/%s' % (MUC, x) for x in ["fredrik", "wendy", "test"]]) known = conn.Avatars.GetKnownAvatarTokens(members) # <https://bugs.freedesktop.org/show_bug.cgi?id=32017>: this assertion # failed, the MUC self handle's token was the empty string. assertEquals(AVATAR_1_SHA1, known[muc_self_handle]) assertEquals(AVATAR_2_SHA1, known[wendy]) assertEquals('', known[fredrik]) # 'k, cool. Wendy loves our avatar and switches to it. stream.send(make_muc_presence('none', 'participant', MUC, 'wendy', photo=AVATAR_1_SHA1)) # Okay this is technically assuming that we just expose the SHA1 sums # directly which is not guaranteed … but we do. q.expect('dbus-signal', signal='AvatarUpdated', args=[wendy, AVATAR_1_SHA1]) # Fredrik switches too. stream.send(make_muc_presence('none', 'participant', MUC, 'fredrik', photo=AVATAR_1_SHA1)) q.expect('dbus-signal', signal='AvatarUpdated', args=[fredrik, AVATAR_1_SHA1]) # And we switch to some other avatar. Gabble should update its vCard, and # then update its MUC presence (which the test, acting as the MUC server, # must echo). call_async(q, conn.Avatars, 'SetAvatar', AVATAR_2_DATA, AVATAR_2_MIME_TYPE) expect_and_handle_get_vcard(q, stream) expect_and_handle_set_vcard(q, stream) muc_presence = q.expect('stream-presence', to=('%s/test' % MUC)) directed_hash = extract_hash_from_presence(muc_presence.stanza) stream.send(make_muc_presence('owner', 'moderator', MUC, 'test', photo=directed_hash)) # Gabble should signal an avatar update for both our global self-handle and # our MUC self-handle. (The first of these of course does not need to wait # for the MUC server to echo our presence.) q.expect_many( EventPattern('dbus-signal', signal='AvatarUpdated', args=[self_handle, AVATAR_2_SHA1]), EventPattern('dbus-signal', signal='AvatarUpdated', args=[muc_self_handle, AVATAR_2_SHA1]), )
def test(q, bus, conn): self_name = 'testsuite' + '@' + avahitest.get_host_name() conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L]) # FIXME: this is a hack to be sure to have all the contact list channels # announced so they won't interfere with the roomlist ones announces. wait_for_contact_list(q, conn) # check if we can request roomlist channels properties = conn.GetAll(tp_name_prefix + '.Connection.Interface.Requests', dbus_interface='org.freedesktop.DBus.Properties') assert ({tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_ROOM_LIST, tp_name_prefix + '.Channel.TargetHandleType': 0, }, [], ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] requestotron = dbus.Interface( conn, tp_name_prefix + '.Connection.Interface.Requests') # create roomlist channel using new API call_async( q, requestotron, 'CreateChannel', { tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_ROOM_LIST, tp_name_prefix + '.Channel.TargetHandleType': 0, }) ret, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) path2 = ret.value[0] chan2 = wrap_channel(bus.get_object(conn.bus_name, path2), "RoomList") props = ret.value[1] assert props[tp_name_prefix + '.Channel.ChannelType'] ==\ cs.CHANNEL_TYPE_ROOM_LIST assert props[tp_name_prefix + '.Channel.TargetHandleType'] == 0 assert props[tp_name_prefix + '.Channel.TargetHandle'] == 0 assert props[tp_name_prefix + '.Channel.TargetID'] == '' assert props[tp_name_prefix + '.Channel.Requested'] == True assert props[tp_name_prefix + '.Channel.InitiatorHandle'] \ == conn.Properties.Get(cs.CONN, "SelfHandle") assert props[tp_name_prefix + '.Channel.InitiatorID'] \ == self_name assert props[tp_name_prefix + '.Channel.Type.RoomList.Server'] == '' assert new_sig.args[0][0][0] == path2 assert new_sig.args[0][0][1] == props assert chan2.Properties.Get(cs.CHANNEL_TYPE_ROOM_LIST, 'Server') == '' # ensure roomlist channel yours, ensured_path, ensured_props = requestotron.EnsureChannel({ tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_ROOM_LIST, tp_name_prefix + '.Channel.TargetHandleType': 0, }) assert not yours assert ensured_path == path2, (ensured_path, path2) # Closing roomlist channels crashed Salut for a while. chan2.Close() q.expect_many( EventPattern('dbus-signal', signal='Closed', path=path2), EventPattern('dbus-signal', signal='ChannelClosed', args=[path2]), ) conn.Disconnect() q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[2, 1]), )