def test(q, bus, conn, stream): (muc_handle, chan, user, bob) = join_muc_and_check(q, bus, conn, stream, MUC) stream.send( elem('message', from_=BOB, to='test@localhost/Resource', type='groupchat', jid='*****@*****.**')( elem(ns.CHAT_STATES, 'composing'), elem('google:nosave', 'x', value='disabled'), elem('http://jabber.org/protocol/archive', 'record', otr='false'), )) e = q.expect('dbus-signal', signal='ChatStateChanged') contact, state = e.args assertEquals(bob, contact) assertEquals(cs.CHAT_STATE_COMPOSING, state) stream.send( elem('message', from_=BOB, to='test@localhost/Resource', type='groupchat', jid='*****@*****.**')( elem(ns.CHAT_STATES, 'paused'), elem('google:nosave', 'x', value='disabled'), elem('http://jabber.org/protocol/archive', 'record', otr='false'), )) e = q.expect('dbus-signal', signal='ChatStateChanged') contact, state = e.args assertEquals(bob, contact) assertEquals(cs.CHAT_STATE_PAUSED, state)
def test(q, bus, conn, stream): room = '*****@*****.**' our_jid = room + '/test' bob_jid = room + '/bob' marco_jid = room + '/marco' room_handle, chan, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, room) # Here are a few scrollback messages. One from us; one from bob; and one # from marco, who's no longer in the room. stream.send( elem('message', from_=our_jid, type='groupchat')( elem('body')( u'i really hate the muc xep' ), elem(ns.X_DELAY, 'x', from_=room, stamp='20090910T12:34:56') ) ) stream.send( elem('message', from_=bob_jid, type='groupchat')( elem('body')( u'yeah, it totally sucks' ), elem(ns.X_DELAY, 'x', from_=room, stamp='20090910T12:45:56') ) ) stream.send( elem('message', from_=marco_jid, type='groupchat')( elem('body')( u'we should start a riot' ), elem(ns.X_DELAY, 'x', from_=room, stamp='20090910T12:56:56') ) ) m1 = q.expect('dbus-signal', signal='MessageReceived') m2 = q.expect('dbus-signal', signal='MessageReceived') m3 = q.expect('dbus-signal', signal='MessageReceived') def badger(event): assertEquals(chan.object_path, event.path) message, = event.args header = message[0] assertContains('scrollback', header) assert header['scrollback'] assertContains('message-sender', header) return header['message-sender'] me = badger(m1) bob = badger(m2) marco = badger(m3) assertEquals([our_jid, bob_jid, marco_jid], conn.InspectHandles(cs.HT_CONTACT, [ me, bob, marco ]))
def test(q, bus, conn, stream): room = '*****@*****.**' our_jid = room + '/test' bob_jid = room + '/bob' marco_jid = room + '/marco' chan, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, room) # Here are a few scrollback messages. One from us; one from bob; and one # from marco, who's no longer in the room. stream.send( elem('message', from_=our_jid, type='groupchat')(elem('body')(u'i really hate the muc xep'), elem(ns.X_DELAY, 'x', from_=room, stamp='20090910T12:34:56'))) stream.send( elem('message', from_=bob_jid, type='groupchat')(elem('body')(u'yeah, it totally sucks'), elem(ns.X_DELAY, 'x', from_=room, stamp='20090910T12:45:56'))) stream.send( elem('message', from_=marco_jid, type='groupchat')(elem('body')(u'we should start a riot'), elem(ns.X_DELAY, 'x', from_=room, stamp='20090910T12:56:56'))) m1 = q.expect('dbus-signal', signal='MessageReceived') m2 = q.expect('dbus-signal', signal='MessageReceived') m3 = q.expect('dbus-signal', signal='MessageReceived') def badger(event): assertEquals(chan.object_path, event.path) message, = event.args header = message[0] assertContains('scrollback', header) assert header['scrollback'] assertContains('message-sender', header) return header['message-sender'] me = badger(m1) bob = badger(m2) marco = badger(m3) assertEquals([our_jid, bob_jid, marco_jid], conn.inspect_contacts_sync([me, bob, marco]))
def test(q, bus, conn, stream): muc = '*****@*****.**' text_chan, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, muc) # Suppose we don't have permission to speak in this MUC. Send a message to # the channel, and have the MUC reject it as unauthorized. send_message_and_expect_error(q, stream, text_chan, test_handle, bob_handle, u"hi r ther ne warez n this chanel?", '401', 'auth', 'not-authorized', delivery_status=cs.DELIVERY_STATUS_PERMANENTLY_FAILED, send_error_value=cs.SendError.PERMISSION_DENIED) # This time, we get rate-limited. # <https://bugs.freedesktop.org/show_bug.cgi?id=43166> send_message_and_expect_error(q, stream, text_chan, test_handle, bob_handle, "faster faster", '500', 'wait', 'resource-constraint', delivery_status=cs.DELIVERY_STATUS_TEMPORARILY_FAILED, # Yuck this isn't a very good name is it? send_error_value=cs.SendError.TOO_LONG) # How about an error message in the reply? This is from Prosody. See # https://bugs.freedesktop.org/show_bug.cgi?id=43166#c9 send_message_and_expect_error(q, stream, text_chan, test_handle, bob_handle, content=u"fair enough", code=None, type_='wait', element='policy-violation', error_message='The room is currently overactive, please try again later', delivery_status=cs.DELIVERY_STATUS_TEMPORARILY_FAILED, # Maybe we should expand the SendError codes some day, because this one # is l-a-m-e. send_error_value=cs.SendError.PERMISSION_DENIED)
def test(q, bus, conn, stream): room = '*****@*****.**' room_handle, chan, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, room) # Exercise basic Channel Properties from spec 0.17.7 channel_props = chan.Properties.GetAll(cs.CHANNEL) assertEquals(room_handle, channel_props.get('TargetHandle')) assertEquals(cs.HT_ROOM, channel_props.get('TargetHandleType')) assertEquals(cs.CHANNEL_TYPE_TEXT, channel_props.get('ChannelType')) interfaces = channel_props.get('Interfaces') assertContains(cs.CHANNEL_IFACE_GROUP, interfaces) assertContains(cs.CHANNEL_IFACE_PASSWORD, interfaces) assertDoesNotContain(cs.TP_AWKWARD_PROPERTIES, interfaces) assertContains(cs.CHANNEL_IFACE_CHAT_STATE, interfaces) assertContains(cs.CHANNEL_IFACE_MESSAGES, interfaces) assert channel_props['TargetID'] == '*****@*****.**', channel_props assert channel_props['Requested'] == True assert channel_props['InitiatorID'] == 'test@localhost' assert channel_props['InitiatorHandle'] == conn.GetSelfHandle() # Exercise Group Properties from spec 0.17.6 (in a basic way) group_props = chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP) assert 'HandleOwners' in group_props, group_props assert 'Members' in group_props, group_props assert 'LocalPendingMembers' in group_props, group_props assert 'RemotePendingMembers' in group_props, group_props assert 'GroupFlags' in group_props, group_props # Test receiving a message from Bob in the MUC message = domish.Element((None, 'message')) message['from'] = '[email protected]/bob' message['type'] = 'groupchat' body = message.addElement('body', content='hello') stream.send(message) received, message_received = q.expect_many( EventPattern('dbus-signal', signal='Received'), EventPattern('dbus-signal', signal='MessageReceived'), ) # Check Channel.Type.Text.Received: # sender: bob assert received.args[2] == bob_handle # message type: normal assert received.args[3] == 0 # flags: none assert received.args[4] == 0 # body assert received.args[5] == 'hello' # Check Channel.Interface.Messages.MessageReceived: message = message_received.args[0] # message should have two parts: the header and one content part assert len(message) == 2, message header, body = message assert header['message-sender'] == bob_handle, header # the spec says that message-type "SHOULD be omitted for normal chat # messages." assert 'message-type' not in header, header assert body['content-type'] == 'text/plain', body assert body['content'] == 'hello', body # Remove the message from the pending message queue, and check that # PendingMessagesRemoved fires. message_id = header['pending-message-id'] chan.Text.AcknowledgePendingMessages([message_id]) removed = q.expect('dbus-signal', signal='PendingMessagesRemoved') removed_ids = removed.args[0] assert len(removed_ids) == 1, removed_ids assert removed_ids[0] == message_id, (removed_ids, message_id) # Send an action using the Messages API greeting = [ dbus.Dictionary({ 'message-type': 1, # Action }, signature='sv'), { 'content-type': 'text/plain', 'content': u"peers through a gap in the curtains", } ] # We ask for delivery reports (which MUCs provide) and read reports (which # MUCs do not provide). sent_token = chan.Messages.SendMessage(greeting, cs.MSG_SENDING_FLAGS_REPORT_DELIVERY | cs.MSG_SENDING_FLAGS_REPORT_READ) assert sent_token stream_message, sent, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Sent'), EventPattern('dbus-signal', signal='MessageSent'), ) sent_message, flags, token = message_sent.args assert len(sent_message) == 2, sent_message header = sent_message[0] assert header['message-type'] == 1, header # Action assertEquals(test_handle, header['message-sender']) assertEquals('[email protected]/test', header['message-sender-id']) body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'peers through a gap in the curtains', body # Of the flags passed to SendMessage, Gabble should only report the # DELIVERY flag, since the other is not supported. assertEquals(cs.MSG_SENDING_FLAGS_REPORT_DELIVERY, flags) assertEquals(sent_token, token) assert sent.args[1] == 1, sent.args # Action assert sent.args[2] == u'peers through a gap in the curtains', sent.args assert message_sent.args[2] == sent_token elem = stream_message.stanza assert elem.name == 'message' assert elem['type'] == 'groupchat', repr(elem) assert elem['id'] == sent_token, repr(elem) assert elem['to'] == '*****@*****.**', repr(elem) for sub_elem in stream_message.stanza.elements(): if sub_elem.name == 'body': found_body = True assert sub_elem.children[0] == u'/me peers through a gap in the curtains' break assert found_body # reflect the sent message back to the MUC elem['from'] = '[email protected]/test' stream.send(elem) # Check that we got the corresponding delivery report report, old_received = q.expect_many( EventPattern('dbus-signal', signal='MessageReceived'), EventPattern('dbus-signal', signal='Received'), ) assert len(report.args) == 1, report.args parts = report.args[0] # The delivery report should just be a header, no body. assert len(parts) == 1, parts part = parts[0] # The intended recipient was the MUC, so there's no contact handle # suitable for being 'message-sender'. assert 'message-sender' not in part or part['message-sender'] == 0, part assert part['message-type'] == 4, part # Message_Type_Delivery_Report assert part['delivery-status'] == 1, part # Delivery_Status_Delivered assert part['delivery-token'] == sent_token, part assert 'delivery-error' not in part, part assert 'delivery-echo' in part, part # Check that the included echo is from us, and matches all the keys in the # message we sent. echo = part['delivery-echo'] assert len(echo) == len(greeting), (echo, greeting) assert echo[0]['message-sender'] == test_handle, echo[0] assert echo[0]['message-token'] == sent_token, echo[0] for i in range(0, len(echo)): for key in greeting[i]: assert key in echo[i], (i, key, echo) assert echo[i][key] == greeting[i][key], (i, key, echo, greeting) # The Text.Received signal should be a "you're not tall enough" stub id, timestamp, sender, type, flags, text = old_received.args assert sender == 0, old_received.args assert type == 4, old_received.args # Message_Type_Delivery_Report assert flags == 2, old_received.args # Non_Text_Content assert text == '', old_received.args # Send a normal message using the Channel.Type.Text API chan.Text.Send(0, 'goodbye') event, sent, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Sent'), EventPattern('dbus-signal', signal='MessageSent'), ) sent_message, flags, _ = message_sent.args assert len(sent_message) == 2, sent_message header = sent_message[0] assert 'message-type' not in header, header # Normal body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'goodbye', body # The caller didn't ask for delivery reports (how could they? they're using # the old API), but the server's going to send us an echo anyway, so # Gabble's within its rights to pretend that the caller asked. assert flags in [0, cs.MSG_SENDING_FLAGS_REPORT_DELIVERY], flags assert sent.args[1] == 0, sent.args # Normal assert sent.args[2] == u'goodbye', sent.args sent_token = message_sent.args[2] elem = event.stanza assert elem.name == 'message' assert elem['type'] == 'groupchat' assert elem['id'] == message_sent.args[2] body = list(event.stanza.elements())[0] assert body.name == 'body' assert body.children[0] == u'goodbye' # reflect the sent message back to the MUC elem['from'] = '[email protected]/test' stream.send(elem) # TODO: check for a delivery report. # test that presence changes are sent via the MUC conn.SimplePresence.SetPresence('away', 'hurrah') event = q.expect('stream-presence', to='[email protected]/test') elem = event.stanza show = [e for e in elem.elements() if e.name == 'show'][0] assert show assert show.children[0] == u'away' status = [e for e in elem.elements() if e.name == 'status'][0] assert status assert status.children[0] == u'hurrah' # Check that there's no <x xmlns='.../muc'/> element in the <presence> # stanza when we're just updating our presence, as opposed to joining the # MUC in the first place. This is a regression test for # <https://bugs.freedesktop.org/show_bug.cgi?id=29147>. XEP-0045 §7.4 shows # that you do not need to include this element in presence updates; if we # erroneously include it, some implementations take this to mean that we're # trying to join the MUC again and helpfully send us all the scrollback # again. x_muc_nodes = xpath.queryForNodes('/presence/x[@xmlns="%s"]' % ns.MUC, elem) assert x_muc_nodes is None, elem.toXml() # test that leaving the channel results in an unavailable message chan.Group.RemoveMembers([chan.Group.GetSelfHandle()], 'booo') event = q.expect('stream-presence', to='[email protected]/test') elem = event.stanza assert elem['type'] == 'unavailable' status = [e for e in elem.elements() if e.name == 'status'] assertLength(1, status) assertEquals(status[0].children[0], u'booo')
def test(q, bus, conn, stream, access_control): conn.Connect() _, iq_event = q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]), EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard')) acknowledge_iq(stream, iq_event.stanza) 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) # tubes channel is created event = q.expect('dbus-signal', signal='NewChannels') channels = event.args[0] path, props = channels[0] # tube channel is created event = q.expect('dbus-signal', signal='NewChannels') 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] assertEquals([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 = bus.get_object(conn.bus_name, path) tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_IFACE_TUBE) dbus_tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_DBUS_TUBE) tube_chan_iface = dbus.Interface(tube_chan, cs.CHANNEL) # 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, dbus_tube_iface, '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')) 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.GetSelfHandle(dbus_interface=cs.CHANNEL_IFACE_GROUP) 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_iface.Close() q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'))
def test(q, bus, conn, stream): (muc_handle, chan, user, bob) = join_muc_and_check(q, bus, conn, stream, MUC) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(user, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_INACTIVE, states.get(bob, cs.CHAT_STATE_INACTIVE)) stream.send( elem('message', from_=BOB, to='test@localhost/Resource', type='groupchat', jid='*****@*****.**')( elem(ns.CHAT_STATES, 'composing'), elem('google:nosave', 'x', value='disabled'), elem('http://jabber.org/protocol/archive', 'record', otr='false'), )) e = q.expect('dbus-signal', signal='ChatStateChanged') contact, state = e.args assertEquals(bob, contact) assertEquals(cs.CHAT_STATE_COMPOSING, state) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(user, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_COMPOSING, states.get(bob, cs.CHAT_STATE_INACTIVE)) stream.send( elem('message', from_=BOB, to='test@localhost/Resource', type='groupchat', jid='*****@*****.**')( elem(ns.CHAT_STATES, 'paused'), elem('google:nosave', 'x', value='disabled'), elem('http://jabber.org/protocol/archive', 'record', otr='false'), )) e = q.expect('dbus-signal', signal='ChatStateChanged') contact, state = e.args assertEquals(bob, contact) assertEquals(cs.CHAT_STATE_PAUSED, state) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(user, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_PAUSED, states.get(bob, cs.CHAT_STATE_INACTIVE)) # # Bob leaves # stream.send( # elem('presence', from_=BOB, to='test@localhost/Resource', # type='unavailable')) # e = q.expect('dbus-signal', signal='ChatStateChanged') # contact, state = e.args # assertEquals(bob, contact) # assertEquals(cs.CHAT_STATE_GONE, state) # states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') # assertEquals(cs.CHAT_STATE_INACTIVE, # states.get(user, cs.CHAT_STATE_INACTIVE)) # # Bob no longer has any chat state at all # assertEquals(None, states.get(bob, None)) # 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(user, cs.CHAT_STATE_INACTIVE)) # XEP 0085: # every content message SHOULD contain an <active/> notification. chan.Text.Send(0, 'hi.') stream_message = q.expect('stream-message') stanza = stream_message.stanza check_state_notification(stanza, 'active', allow_body=True) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_ACTIVE, states.get(user, 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 stanza.elements() if is_body(x)]) == 1, stanza.toXml()
def test(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) room = '*****@*****.**' room_handle, chan, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, room) # Exercise basic Channel Properties from spec 0.17.7 channel_props = chan.Properties.GetAll(cs.CHANNEL) assert channel_props.get('TargetHandle') == room_handle,\ (channel_props.get('TargetHandle'), room_handle) assert channel_props.get('TargetHandleType') == cs.HT_ROOM,\ channel_props.get('TargetHandleType') assert channel_props.get('ChannelType') == \ cs.CHANNEL_TYPE_TEXT,\ channel_props.get('ChannelType') assert cs.CHANNEL_IFACE_GROUP in \ channel_props.get('Interfaces', ()), \ channel_props.get('Interfaces') assert cs.CHANNEL_IFACE_PASSWORD in \ channel_props.get('Interfaces', ()), \ channel_props.get('Interfaces') assert cs.TP_AWKWARD_PROPERTIES in \ channel_props.get('Interfaces', ()), \ channel_props.get('Interfaces') assert cs.CHANNEL_IFACE_CHAT_STATE in \ channel_props.get('Interfaces', ()), \ channel_props.get('Interfaces') assert cs.CHANNEL_IFACE_MESSAGES in \ channel_props.get('Interfaces', ()), \ channel_props.get('Interfaces') assert channel_props['TargetID'] == '*****@*****.**', channel_props assert channel_props['Requested'] == True assert channel_props['InitiatorID'] == 'test@localhost' assert channel_props['InitiatorHandle'] == conn.GetSelfHandle() # Exercise Group Properties from spec 0.17.6 (in a basic way) group_props = chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP) assert 'HandleOwners' in group_props, group_props assert 'Members' in group_props, group_props assert 'LocalPendingMembers' in group_props, group_props assert 'RemotePendingMembers' in group_props, group_props assert 'GroupFlags' in group_props, group_props # Test receiving a message from Bob in the MUC message = domish.Element((None, 'message')) message['from'] = '[email protected]/bob' message['type'] = 'groupchat' body = message.addElement('body', content='hello') stream.send(message) received, message_received = q.expect_many( EventPattern('dbus-signal', signal='Received'), EventPattern('dbus-signal', signal='MessageReceived'), ) # Check Channel.Type.Text.Received: # sender: bob assert received.args[2] == bob_handle # message type: normal assert received.args[3] == 0 # flags: none assert received.args[4] == 0 # body assert received.args[5] == 'hello' # Check Channel.Interface.Messages.MessageReceived: message = message_received.args[0] # message should have two parts: the header and one content part assert len(message) == 2, message header, body = message assert header['message-sender'] == bob_handle, header # the spec says that message-type "SHOULD be omitted for normal chat # messages." assert 'message-type' not in header, header assert body['content-type'] == 'text/plain', body assert body['content'] == 'hello', body # Remove the message from the pending message queue, and check that # PendingMessagesRemoved fires. message_id = header['pending-message-id'] chan.Text.AcknowledgePendingMessages([message_id]) removed = q.expect('dbus-signal', signal='PendingMessagesRemoved') removed_ids = removed.args[0] assert len(removed_ids) == 1, removed_ids assert removed_ids[0] == message_id, (removed_ids, message_id) # Send an action using the Messages API greeting = [ dbus.Dictionary({ 'message-type': 1, # Action }, signature='sv'), { 'content-type': 'text/plain', 'content': u"peers through a gap in the curtains", } ] sent_token = chan.Messages.SendMessage(greeting, dbus.UInt32(0)) assert sent_token stream_message, sent, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Sent'), EventPattern('dbus-signal', signal='MessageSent'), ) sent_message = message_sent.args[0] assert len(sent_message) == 2, sent_message header = sent_message[0] assert header['message-type'] == 1, header # Action body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'peers through a gap in the curtains', body assert sent.args[1] == 1, sent.args # Action assert sent.args[2] == u'peers through a gap in the curtains', sent.args assert message_sent.args[2] == sent_token elem = stream_message.stanza assert elem.name == 'message' assert elem['type'] == 'groupchat', repr(elem) assert elem['id'] == sent_token, repr(elem) assert elem['to'] == '*****@*****.**', repr(elem) for sub_elem in stream_message.stanza.elements(): if sub_elem.name == 'body': found_body = True assert sub_elem.children[0] == u'/me peers through a gap in the curtains' break assert found_body # reflect the sent message back to the MUC elem['from'] = '[email protected]/test' stream.send(elem) # Check that we got the corresponding delivery report report, old_received = q.expect_many( EventPattern('dbus-signal', signal='MessageReceived'), EventPattern('dbus-signal', signal='Received'), ) assert len(report.args) == 1, report.args parts = report.args[0] # The delivery report should just be a header, no body. assert len(parts) == 1, parts part = parts[0] # The intended recipient was the MUC, so there's no contact handle # suitable for being 'message-sender'. assert 'message-sender' not in part or part['message-sender'] == 0, part assert part['message-type'] == 4, part # Message_Type_Delivery_Report assert part['delivery-status'] == 1, part # Delivery_Status_Delivered assert part['delivery-token'] == sent_token, part assert 'delivery-error' not in part, part assert 'delivery-echo' in part, part # Check that the included echo is from us, and matches all the keys in the # message we sent. echo = part['delivery-echo'] assert len(echo) == len(greeting), (echo, greeting) assert echo[0]['message-sender'] == test_handle, echo[0] assert echo[0]['message-token'] == sent_token, echo[0] for i in range(0, len(echo)): for key in greeting[i]: assert key in echo[i], (i, key, echo) assert echo[i][key] == greeting[i][key], (i, key, echo, greeting) # The Text.Received signal should be a "you're not tall enough" stub id, timestamp, sender, type, flags, text = old_received.args assert sender == 0, old_received.args assert type == 4, old_received.args # Message_Type_Delivery_Report assert flags == 2, old_received.args # Non_Text_Content assert text == '', old_received.args # Send a normal message using the Channel.Type.Text API chan.Text.Send(0, 'goodbye') event, sent, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Sent'), EventPattern('dbus-signal', signal='MessageSent'), ) sent_message = message_sent.args[0] assert len(sent_message) == 2, sent_message header = sent_message[0] assert 'message-type' not in header, header # Normal body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'goodbye', body assert sent.args[1] == 0, sent.args # Normal assert sent.args[2] == u'goodbye', sent.args sent_token = message_sent.args[2] elem = event.stanza assert elem.name == 'message' assert elem['type'] == 'groupchat' assert elem['id'] == message_sent.args[2] body = list(event.stanza.elements())[0] assert body.name == 'body' assert body.children[0] == u'goodbye' # reflect the sent message back to the MUC elem['from'] = '[email protected]/test' stream.send(elem) # TODO: check for a delivery report. # test that presence changes are sent via the MUC conn.Presence.SetStatus({'away':{'message':'hurrah'}}) event = q.expect('stream-presence', to='[email protected]/test') elem = event.stanza show = [e for e in elem.elements() if e.name == 'show'][0] assert show assert show.children[0] == u'away' status = [e for e in elem.elements() if e.name == 'status'][0] assert status assert status.children[0] == u'hurrah' # test that leaving the channel results in an unavailable message chan.Group.RemoveMembers([chan.Group.GetSelfHandle()], 'booo') event = q.expect('stream-presence', to='[email protected]/test') elem = event.stanza assert elem['type'] == 'unavailable' status = [e for e in elem.elements() if e.name == 'status'] assertLength(1, status) assertEquals(status[0].children[0], u'booo')
def test(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) muc = '*****@*****.**' _, text_chan, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, muc) # Suppose we don't have permission to speak in this MUC. Send a message to # the channel, and have the MUC reject it as unauthorized. content = u"hi r ther ne warez n this chanel?" greeting = [ dbus.Dictionary({ }, signature='sv'), { 'content-type': 'text/plain', 'content': content, } ] sent_token = dbus.Interface(text_chan, cs.CHANNEL_IFACE_MESSAGES) \ .SendMessage(greeting, dbus.UInt32(0)) stream_message, _, _ = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Sent'), EventPattern('dbus-signal', signal='MessageSent'), ) # computer says no elem = stream_message.stanza elem['from'] = '*****@*****.**' elem['to'] = '[email protected]/test' elem['type'] = 'error' error = elem.addElement('error') error['code'] = '401' error['type'] = 'auth' error.addElement((ns.STANZA, 'not-authorized')) stream.send(elem) # check that we got a failed delivery report and a SendError send_error, received, message_received = q.expect_many( EventPattern('dbus-signal', signal='SendError'), EventPattern('dbus-signal', signal='Received'), EventPattern('dbus-signal', signal='MessageReceived'), ) PERMISSION_DENIED = 3 err, timestamp, type, text = send_error.args assert err == PERMISSION_DENIED, send_error.args # there's no way to tell when the original message was sent from the error stanza assert timestamp == 0, send_error.args # Gabble can't determine the type of the original message; see muc/test-muc.py # assert type == 0, send_error.args assert text == content, send_error.args # The Text.Received signal should be a "you're not tall enough" stub id, timestamp, sender, type, flags, text = received.args assert sender == 0, received.args assert type == 4, received.args # Message_Type_Delivery_Report assert flags == 2, received.args # Non_Text_Content assert text == '', received.args # Check that the Messages.MessageReceived signal was a failed delivery report assert len(message_received.args) == 1, message_received.args parts = message_received.args[0] # The delivery report should just be a header, no body. assert len(parts) == 1, parts part = parts[0] # The intended recipient was the MUC, so there's no contact handle # suitable for being 'message-sender'. assert 'message-sender' not in part or part['message-sender'] == 0, part assert part['message-type'] == 4, part # Message_Type_Delivery_Report assert part['delivery-status'] == 3, part # Delivery_Status_Permanently_Failed assert part['delivery-error'] == PERMISSION_DENIED, part assert part['delivery-token'] == sent_token, part # Check that the included echo is from us, and matches all the keys in the # message we sent. assert 'delivery-echo' in part, part echo = part['delivery-echo'] assert len(echo) == len(greeting), (echo, greeting) assert echo[0]['message-sender'] == test_handle, echo[0] assert echo[0]['message-token'] == sent_token, echo[0] for i in range(0, len(echo)): for key in greeting[i]: assert key in echo[i], (i, key, echo) assert echo[i][key] == greeting[i][key], (i, key, echo, greeting)
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): muc = "*****@*****.**" _, text_chan, test_handle, bob_handle = join_muc_and_check(q, bus, conn, stream, muc) # Suppose we don't have permission to speak in this MUC. Send a message to # the channel, and have the MUC reject it as unauthorized. content = u"hi r ther ne warez n this chanel?" greeting = [dbus.Dictionary({}, signature="sv"), {"content-type": "text/plain", "content": content}] sent_token = dbus.Interface(text_chan, cs.CHANNEL_IFACE_MESSAGES).SendMessage(greeting, dbus.UInt32(0)) stream_message, _, _ = q.expect_many( EventPattern("stream-message"), EventPattern("dbus-signal", signal="Sent"), EventPattern("dbus-signal", signal="MessageSent"), ) # computer says no elem = stream_message.stanza elem["from"] = "*****@*****.**" elem["to"] = "[email protected]/test" elem["type"] = "error" error = elem.addElement("error") error["code"] = "401" error["type"] = "auth" error.addElement((ns.STANZA, "not-authorized")) stream.send(elem) # check that we got a failed delivery report and a SendError send_error, received, message_received = q.expect_many( EventPattern("dbus-signal", signal="SendError"), EventPattern("dbus-signal", signal="Received"), EventPattern("dbus-signal", signal="MessageReceived"), ) PERMISSION_DENIED = 3 err, timestamp, type, text = send_error.args assert err == PERMISSION_DENIED, send_error.args # there's no way to tell when the original message was sent from the error stanza assert timestamp == 0, send_error.args # Gabble can't determine the type of the original message; see muc/test-muc.py # assert type == 0, send_error.args assert text == content, send_error.args # The Text.Received signal should be a "you're not tall enough" stub id, timestamp, sender, type, flags, text = received.args assert sender == 0, received.args assert type == 4, received.args # Message_Type_Delivery_Report assert flags == 2, received.args # Non_Text_Content assert text == "", received.args # Check that the Messages.MessageReceived signal was a failed delivery report assert len(message_received.args) == 1, message_received.args parts = message_received.args[0] # The delivery report should just be a header, no body. assert len(parts) == 1, parts part = parts[0] # The intended recipient was the MUC, so there's no contact handle # suitable for being 'message-sender'. assert "message-sender" not in part or part["message-sender"] == 0, part assert part["message-type"] == 4, part # Message_Type_Delivery_Report assert part["delivery-status"] == 3, part # Delivery_Status_Permanently_Failed assert part["delivery-error"] == PERMISSION_DENIED, part assert part["delivery-token"] == sent_token, part # Check that the included echo is from us, and matches all the keys in the # message we sent. assert "delivery-echo" in part, part echo = part["delivery-echo"] assert len(echo) == len(greeting), (echo, greeting) assert echo[0]["message-sender"] == test_handle, echo[0] assert echo[0]["message-token"] == sent_token, echo[0] for i in range(0, len(echo)): for key in greeting[i]: assert key in echo[i], (i, key, echo) assert echo[i][key] == greeting[i][key], (i, key, echo, greeting)
def test(q, bus, conn, stream): room = '*****@*****.**' chan, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, room) # Exercise basic Channel Properties from spec 0.17.7 channel_props = chan.Properties.GetAll(cs.CHANNEL) assertEquals(cs.HT_ROOM, channel_props.get('TargetHandleType')) assertEquals(cs.CHANNEL_TYPE_TEXT, channel_props.get('ChannelType')) interfaces = channel_props.get('Interfaces') assertContains(cs.CHANNEL_IFACE_GROUP, interfaces) assertContains(cs.CHANNEL_IFACE_PASSWORD, interfaces) assertContains(cs.CHANNEL_IFACE_CHAT_STATE, interfaces) assertContains(cs.CHANNEL_IFACE_MESSAGES, interfaces) assert channel_props['TargetID'] == '*****@*****.**', channel_props assert channel_props['Requested'] == True assert channel_props['InitiatorID'] == 'test@localhost' assert channel_props['InitiatorHandle'] == conn.Properties.Get( cs.CONN, "SelfHandle") # Exercise Group Properties from spec 0.17.6 (in a basic way) group_props = chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP) assert 'HandleOwners' in group_props, group_props assert 'Members' in group_props, group_props assert 'LocalPendingMembers' in group_props, group_props assert 'RemotePendingMembers' in group_props, group_props assert 'GroupFlags' in group_props, group_props # Test receiving a message from Bob in the MUC message = domish.Element((None, 'message')) message['from'] = '[email protected]/bob' message['type'] = 'groupchat' body = message.addElement('body', content='hello') stream.send(message) message_received = q.expect('dbus-signal', signal='MessageReceived') # Check Channel.Interface.Messages.MessageReceived: message = message_received.args[0] # message should have two parts: the header and one content part assert len(message) == 2, message header, body = message assert header['message-sender'] == bob_handle, header # the spec says that message-type "SHOULD be omitted for normal chat # messages." assert 'message-type' not in header, header assert body['content-type'] == 'text/plain', body assert body['content'] == 'hello', body # Remove the message from the pending message queue, and check that # PendingMessagesRemoved fires. message_id = header['pending-message-id'] chan.Text.AcknowledgePendingMessages([message_id]) removed = q.expect('dbus-signal', signal='PendingMessagesRemoved') removed_ids = removed.args[0] assert len(removed_ids) == 1, removed_ids assert removed_ids[0] == message_id, (removed_ids, message_id) # Send an action using the Messages API greeting = [ dbus.Dictionary( { 'message-type': 1, # Action }, signature='sv'), { 'content-type': 'text/plain', 'content': u"peers through a gap in the curtains", } ] # We ask for delivery reports (which MUCs provide) and read reports (which # MUCs do not provide). sent_token = chan.Messages.SendMessage( greeting, cs.MSG_SENDING_FLAGS_REPORT_DELIVERY | cs.MSG_SENDING_FLAGS_REPORT_READ) assert sent_token stream_message, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='MessageSent'), ) sent_message, flags, token = message_sent.args assert len(sent_message) == 2, sent_message header = sent_message[0] assert header['message-type'] == 1, header # Action assertEquals(test_handle, header['message-sender']) assertEquals('[email protected]/test', header['message-sender-id']) body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'peers through a gap in the curtains', body # Of the flags passed to SendMessage, Gabble should only report the # DELIVERY flag, since the other is not supported. assertEquals(cs.MSG_SENDING_FLAGS_REPORT_DELIVERY, flags) assertEquals(sent_token, token) assert message_sent.args[2] == sent_token elem = stream_message.stanza assert elem.name == 'message' assert elem['type'] == 'groupchat', repr(elem) assert elem['id'] == sent_token, repr(elem) assert elem['to'] == '*****@*****.**', repr(elem) for sub_elem in stream_message.stanza.elements(): if sub_elem.name == 'body': found_body = True assert sub_elem.children[ 0] == u'/me peers through a gap in the curtains' break assert found_body # reflect the sent message back to the MUC elem['from'] = '[email protected]/test' stream.send(elem) # Check that we got the corresponding delivery report report = q.expect('dbus-signal', signal='MessageReceived') assert len(report.args) == 1, report.args parts = report.args[0] # The delivery report should just be a header, no body. assert len(parts) == 1, parts part = parts[0] # The intended recipient was the MUC, so there's no contact handle # suitable for being 'message-sender'. assert 'message-sender' not in part or part['message-sender'] == 0, part assert part['message-type'] == 4, part # Message_Type_Delivery_Report assert part['delivery-status'] == 1, part # Delivery_Status_Delivered assert part['delivery-token'] == sent_token, part assert 'delivery-error' not in part, part assert 'delivery-echo' in part, part # Check that the included echo is from us, and matches all the keys in the # message we sent. echo = part['delivery-echo'] assert len(echo) == len(greeting), (echo, greeting) assert echo[0]['message-sender'] == test_handle, echo[0] assert echo[0]['message-token'] == sent_token, echo[0] for i in range(0, len(echo)): for key in greeting[i]: assert key in echo[i], (i, key, echo) assert echo[i][key] == greeting[i][key], (i, key, echo, greeting) # Send a normal message using the Channel.Type.Text API chan.send_msg_sync('goodbye') event, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='MessageSent'), ) sent_message, flags, _ = message_sent.args assert len(sent_message) == 2, sent_message header = sent_message[0] body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'goodbye', body # The caller didn't ask for delivery reports (how could they? they're using # the old API), but the server's going to send us an echo anyway, so # Gabble's within its rights to pretend that the caller asked. assert flags in [0, cs.MSG_SENDING_FLAGS_REPORT_DELIVERY], flags sent_token = message_sent.args[2] elem = event.stanza assert elem.name == 'message' assert elem['type'] == 'groupchat' assert elem['id'] == message_sent.args[2] body = list(event.stanza.elements())[0] assert body.name == 'body' assert body.children[0] == u'goodbye' # reflect the sent message back to the MUC elem['from'] = '[email protected]/test' stream.send(elem) # TODO: check for a delivery report. # test that presence changes are sent via the MUC conn.SimplePresence.SetPresence('away', 'hurrah') event = q.expect('stream-presence', to='[email protected]/test') elem = event.stanza show = [e for e in elem.elements() if e.name == 'show'][0] assert show assert show.children[0] == u'away' status = [e for e in elem.elements() if e.name == 'status'][0] assert status assert status.children[0] == u'hurrah' # Check that there's no <x xmlns='.../muc'/> element in the <presence> # stanza when we're just updating our presence, as opposed to joining the # MUC in the first place. This is a regression test for # <https://bugs.freedesktop.org/show_bug.cgi?id=29147>. XEP-0045 §7.4 shows # that you do not need to include this element in presence updates; if we # erroneously include it, some implementations take this to mean that we're # trying to join the MUC again and helpfully send us all the scrollback # again. x_muc_nodes = xpath.queryForNodes('/presence/x[@xmlns="%s"]' % ns.MUC, elem) assert x_muc_nodes is None, elem.toXml() # test that leaving the channel results in an unavailable message chan.Group.RemoveMembers( [chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, "SelfHandle")], 'booo') event = q.expect('stream-presence', to='[email protected]/test') elem = event.stanza assert elem['type'] == 'unavailable' status = [e for e in elem.elements() if e.name == 'status'] assertLength(1, status) assertEquals(status[0].children[0], u'booo')
def test(q, bus, conn, stream): (chan, user, bob) = join_muc_and_check(q, bus, conn, stream, MUC) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(user, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_INACTIVE, states.get(bob, cs.CHAT_STATE_INACTIVE)) stream.send( elem('message', from_=BOB, to='test@localhost/Resource', type='groupchat', jid='*****@*****.**')( elem(ns.CHAT_STATES, 'composing'), elem('google:nosave', 'x', value='disabled'), elem('http://jabber.org/protocol/archive', 'record', otr='false'), )) e = q.expect('dbus-signal', signal='ChatStateChanged') contact, state = e.args assertEquals(bob, contact) assertEquals(cs.CHAT_STATE_COMPOSING, state) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(user, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_COMPOSING, states.get(bob, cs.CHAT_STATE_INACTIVE)) stream.send( elem('message', from_=BOB, to='test@localhost/Resource', type='groupchat', jid='*****@*****.**')( elem(ns.CHAT_STATES, 'paused'), elem('google:nosave', 'x', value='disabled'), elem('http://jabber.org/protocol/archive', 'record', otr='false'), )) e = q.expect('dbus-signal', signal='ChatStateChanged') contact, state = e.args assertEquals(bob, contact) assertEquals(cs.CHAT_STATE_PAUSED, state) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(user, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_PAUSED, states.get(bob, cs.CHAT_STATE_INACTIVE)) # Bob leaves presence = make_muc_presence('owner', 'none', MUC, 'bob') presence['type'] = 'unavailable' stream.send(presence) e = q.expect('dbus-signal', signal='ChatStateChanged') contact, state = e.args assertEquals(bob, contact) assertEquals(cs.CHAT_STATE_GONE, state) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(user, cs.CHAT_STATE_INACTIVE)) # Bob no longer has any chat state at all assertEquals(None, states.get(bob, None)) # 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(user, 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') stanza = stream_message.stanza check_state_notification(stanza, 'active', allow_body=True) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_ACTIVE, states.get(user, cs.CHAT_STATE_INACTIVE)) bodies = list(stanza.elements(uri=ns.CLIENT, name='body')) assertLength(1, bodies) assertEquals(u'hi.', bodies[0].children[0]) # If we get an error with type='wait', stop sending chat states. stanza['type'] = 'error' stanza['from'] = MUC stanza['to'] = 'test@localhost/Resource' error = stanza.addElement('error') error['type'] = 'wait' error.addElement((ns.STANZA, 'resource-constraint')) stream.send(stanza) q.expect('dbus-signal', signal='MessageReceived', predicate=lambda e: e.args[0][0]['message-type'] == cs.MT_DELIVERY_REPORT) q.forbid_events([ EventPattern('stream-message', to=MUC, predicate=lambda e: get_state_notification(e.stanza) is not None) ]) # User starts typing again but nothing should be seen or heard on the stream. chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) sync_stream(q, stream)