summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2008-11-26 16:44:00 +0000
committerGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2008-11-26 16:44:00 +0000
commit4ff131707342763d35e3d66a2a6b81ddbb47e097 (patch)
treebff02737f142b69bd3ab9d5e1e4969641ada1e3c /tests
parentafa043cca638ef9d55185750ca02f3010f0e21c0 (diff)
parent4acc915be8b49609ae9a0339c04d6b2fa8c389cd (diff)
downloadtelepathy-salut-4ff131707342763d35e3d66a2a6b81ddbb47e097.tar.gz
Merge branch 'master' into gibber-crash
Conflicts: tests/twisted/Makefile.am
Diffstat (limited to 'tests')
-rw-r--r--tests/twisted/Makefile.am20
-rw-r--r--tests/twisted/avahi/test-receive-and-send-file.py314
-rw-r--r--tests/twisted/avahi/test-receive-file-cancelled-immediately.py198
-rw-r--r--tests/twisted/avahi/test-receive-file-decline.py209
-rw-r--r--tests/twisted/avahi/test-receive-file-not-found.py215
-rw-r--r--tests/twisted/avahi/test-receive-file.py218
-rw-r--r--tests/twisted/avahi/test-send-file-and-cancel-immediately.py202
-rw-r--r--tests/twisted/avahi/test-send-file-and-disconnect.py183
-rw-r--r--tests/twisted/avahi/test-send-file-declined.py202
-rw-r--r--tests/twisted/avahi/test-send-file-item-not-found.py202
-rw-r--r--tests/twisted/avahi/test-send-file-provide-immediately.py228
-rw-r--r--tests/twisted/avahi/test-send-file-to-unknown-contact.py101
-rw-r--r--tests/twisted/avahi/test-send-file-wait-to-provide.py235
-rw-r--r--tests/twisted/avahi/test-tube-close.py82
-rw-r--r--tests/twisted/avahi/test-tube.py150
-rw-r--r--tests/twisted/avahi/test-two-tubes.py180
-rw-r--r--tests/twisted/trivialstream.py70
17 files changed, 3007 insertions, 2 deletions
diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am
index 1b471b94..daebef8b 100644
--- a/tests/twisted/Makefile.am
+++ b/tests/twisted/Makefile.am
@@ -6,10 +6,25 @@ TWISTED_AVAHI_TESTS = \
avahi/test-register.py \
avahi/test-aliases.py \
avahi/test-close-local-pending-room.py \
+ avahi/test-send-file-and-cancel-immediately.py \
+ avahi/test-send-file-and-disconnect.py \
+ avahi/test-send-file-declined.py \
+ avahi/test-send-file-item-not-found.py \
+ avahi/test-send-file-provide-immediately.py \
+ avahi/test-send-file-to-unknown-contact.py \
+ avahi/test-send-file-wait-to-provide.py \
+ avahi/test-receive-and-send-file.py \
+ avahi/test-receive-file.py \
+ avahi/test-receive-file-cancelled-immediately.py \
+ avahi/test-receive-file-decline.py \
+ avahi/test-receive-file-not-found.py \
avahi/test-text-channel.py \
avahi/test-ichat-composing.py \
avahi/test-ichat-incoming-msg.py \
- avahi/test-room-list.py
+ avahi/test-room-list.py \
+ avahi/test-tube.py \
+ avahi/test-two-tubes.py \
+ avahi/test-tube-close.py
TWISTED_AVAHI_OLPC_TESTS = \
avahi/test-olpc-activity-announcements.py
@@ -48,7 +63,8 @@ EXTRA_DIST = \
$(TWISTED_AVAHI_TESTS) \
$(TWISTED_BASIC_TESTS) \
saluttest.py \
- servicetest.py
+ servicetest.py \
+ trivialstream.py
CLEANFILES = salut-[1-9]*.log *.pyc */*.pyc
diff --git a/tests/twisted/avahi/test-receive-and-send-file.py b/tests/twisted/avahi/test-receive-and-send-file.py
new file mode 100644
index 00000000..d74ddb80
--- /dev/null
+++ b/tests/twisted/avahi/test-receive-and-send-file.py
@@ -0,0 +1,314 @@
+import httplib
+import urlparse
+import dbus
+import socket
+import md5
+import avahi
+import BaseHTTPServer
+import urllib
+
+from saluttest import exec_test
+from avahitest import AvahiAnnouncer, AvahiListener
+from avahitest import get_host_name
+
+from xmppstream import setup_stream_listener, connect_to_stream
+from servicetest import make_channel_proxy, EventPattern
+
+from twisted.words.xish import domish, xpath
+
+from dbus import PROPERTIES_IFACE
+
+CONNECTION_INTERFACE_REQUESTS = 'org.freedesktop.Telepathy.Connection.Interface.Requests'
+CHANNEL_INTERFACE ='org.freedesktop.Telepathy.Channel'
+CHANNEL_TYPE_FILE_TRANSFER = 'org.freedesktop.Telepathy.Channel.Type.FileTransfer.DRAFT'
+
+HT_CONTACT = 1
+HT_CONTACT_LIST = 3
+TEXT_MESSAGE_TYPE_NORMAL = dbus.UInt32(0)
+
+FT_STATE_NONE = 0
+FT_STATE_PENDING = 1
+FT_STATE_ACCEPTED = 2
+FT_STATE_OPEN = 3
+FT_STATE_COMPLETED = 4
+FT_STATE_CANCELLED = 5
+
+FT_STATE_CHANGE_REASON_NONE = 0
+FT_STATE_CHANGE_REASON_REQUESTED = 1
+
+FILE_HASH_TYPE_NONE = 0
+FILE_HASH_TYPE_MD5 = 1
+
+SOCKET_ADDRESS_TYPE_UNIX = 0
+SOCKET_ADDRESS_TYPE_IPV4 = 2
+
+SOCKET_ACCESS_CONTROL_LOCALHOST = 0
+
+# File to Offer
+FILE_DATA = "What a nice file"
+FILE_SIZE = len(FILE_DATA)
+FILE_NAME = 'The foo.txt'
+FILE_CONTENT_TYPE = 'text/plain'
+FILE_DESCRIPTION = 'A nice file to test'
+FILE_HASH_TYPE = FILE_HASH_TYPE_MD5
+m = md5.new()
+m.update(FILE_DATA)
+FILE_HASH = m.hexdigest()
+
+def test(q, bus, conn):
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
+ basic_txt = { "txtvers": "1", "status": "avail" }
+
+ self_handle = conn.GetSelfHandle()
+ self_handle_name = conn.InspectHandles(HT_CONTACT, [self_handle])[0]
+
+ contact_name = "test-file-sender@" + get_host_name()
+ listener, port = setup_stream_listener(q, contact_name)
+
+ AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt)
+
+ publish_handle = conn.RequestHandles(HT_CONTACT_LIST, ["publish"])[0]
+ publish = conn.RequestChannel(
+ "org.freedesktop.Telepathy.Channel.Type.ContactList",
+ HT_CONTACT_LIST, publish_handle, False)
+
+ handle = 0
+ # Wait until the record shows up in publish
+ while handle == 0:
+ e = q.expect('dbus-signal', signal='MembersChanged', path=publish)
+ for h in e.args[1]:
+ name = conn.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact_name:
+ handle = h
+
+ requests_iface = dbus.Interface(conn, CONNECTION_INTERFACE_REQUESTS)
+
+ # Create a connection to send the FT stanza
+ AvahiListener(q).listen_for_service("_presence._tcp")
+ e = q.expect('service-added', name = self_handle_name,
+ protocol = avahi.PROTO_INET)
+ service = e.service
+ service.resolve()
+
+ e = q.expect('service-resolved', service = service)
+
+ outbound = connect_to_stream(q, contact_name,
+ self_handle_name, str(e.pt), e.port)
+
+ e = q.expect('connection-result')
+ assert e.succeeded, e.reason
+ e = q.expect('stream-opened', connection = outbound)
+
+ # Setup the HTTP server
+ httpd = BaseHTTPServer.HTTPServer(('', 0), HTTPHandler)
+
+ # connected to Salut, now send the IQ
+ iq = domish.Element((None, 'iq'))
+ iq['to'] = self_handle_name
+ iq['from'] = contact_name
+ iq['type'] = 'set'
+ iq['id'] = 'gibber-file-transfer-0'
+ query = iq.addElement(('jabber:iq:oob', 'query'))
+ url = 'http://127.0.0.1:%u/gibber-file-transfer-0/%s' % (httpd.server_port, urllib.quote(FILE_NAME))
+ url_node = query.addElement('url', content=url)
+ url_node['type'] = 'file'
+ url_node['size'] = str(FILE_SIZE)
+ url_node['mimeType'] = FILE_CONTENT_TYPE
+ query.addElement('desc', content=FILE_DESCRIPTION)
+ outbound.send(iq)
+
+ e =q.expect('dbus-signal', signal='NewChannels')
+ channels = e.args[0]
+ assert len(channels) == 1
+ path, props = channels[0]
+
+ # check channel properties
+ # org.freedesktop.Telepathy.Channel D-Bus properties
+ assert props[CHANNEL_INTERFACE + '.ChannelType'] == CHANNEL_TYPE_FILE_TRANSFER
+ assert props[CHANNEL_INTERFACE + '.Interfaces'] == []
+ assert props[CHANNEL_INTERFACE + '.TargetHandle'] == handle
+ assert props[CHANNEL_INTERFACE + '.TargetID'] == contact_name
+ assert props[CHANNEL_INTERFACE + '.TargetHandleType'] == HT_CONTACT
+ assert props[CHANNEL_INTERFACE + '.Requested'] == False
+ assert props[CHANNEL_INTERFACE + '.InitiatorHandle'] == handle
+ assert props[CHANNEL_INTERFACE + '.InitiatorID'] == contact_name
+
+ # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.State'] == FT_STATE_PENDING
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentType'] == FILE_CONTENT_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Filename'] == FILE_NAME
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Size'] == FILE_SIZE
+ # FT's protocol doesn't allow us the send the hash info
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType'] == FILE_HASH_TYPE_NONE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash'] == ''
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Description'] == FILE_DESCRIPTION
+ # FT's protocol doesn't allow us the send the date info
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Date'] == 0
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.AvailableSocketTypes'] == \
+ {SOCKET_ADDRESS_TYPE_UNIX: [SOCKET_ACCESS_CONTROL_LOCALHOST]}
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.TransferredBytes'] == 0
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'] == 0
+
+ channel = make_channel_proxy(conn, path, 'Channel')
+ ft_channel = make_channel_proxy(conn, path, 'Channel.Type.FileTransfer.DRAFT')
+ ft_props = dbus.Interface(bus.get_object(conn.object.bus_name, path), PROPERTIES_IFACE)
+
+ address = ft_channel.AcceptFile(SOCKET_ADDRESS_TYPE_UNIX, SOCKET_ACCESS_CONTROL_LOCALHOST, "", 5)
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_ACCEPTED
+ assert reason == FT_STATE_CHANGE_REASON_REQUESTED
+
+ e = q.expect('dbus-signal', signal='InitialOffsetDefined')
+ offset = e.args[0]
+ # We don't support resume
+ assert offset == 0
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_OPEN
+ assert reason == FT_STATE_CHANGE_REASON_NONE
+
+ # Connect to Salut's socket
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ s.connect(address)
+
+ httpd.handle_request()
+
+ # Receiver inform us he finished to download the file
+ q.expect('stream-iq', iq_type='result')
+
+ # Read the file from Salut's socket
+ data = ''
+ read = 0
+ while read < FILE_SIZE:
+ data += s.recv(FILE_SIZE - read)
+ read = len(data)
+ assert data == FILE_DATA
+
+ e = q.expect('dbus-signal', signal='TransferredBytesChanged')
+ count = e.args[0]
+ while count < FILE_SIZE:
+ # Catch TransferredBytesChanged until we transfered all the data
+ e = q.expect('dbus-signal', signal='TransferredBytesChanged')
+ count = e.args[0]
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_COMPLETED
+ assert reason == FT_STATE_CHANGE_REASON_NONE
+
+ channel.Close()
+ q.expect('dbus-signal', signal='Closed')
+
+ # now send a file. We'll reuse the same XMPP connection
+ path, props = requests_iface.CreateChannel({
+ CHANNEL_INTERFACE + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
+ CHANNEL_INTERFACE + '.TargetHandleType': HT_CONTACT,
+ CHANNEL_INTERFACE + '.TargetHandle': handle,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentType': FILE_CONTENT_TYPE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Filename': FILE_NAME,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Size': FILE_SIZE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType': FILE_HASH_TYPE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash': FILE_HASH,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Description': FILE_DESCRIPTION,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Date': 1225278834,
+ CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset': 0,
+ })
+
+ channel = make_channel_proxy(conn, path, 'Channel')
+ ft_channel = make_channel_proxy(conn, path, 'Channel.Type.FileTransfer.DRAFT')
+ ft_props = dbus.Interface(bus.get_object(conn.object.bus_name, path), PROPERTIES_IFACE)
+
+ iq_event = q.expect('stream-iq')
+
+ assert iq_event.iq_type == 'set'
+ assert iq_event.connection == outbound
+ iq = iq_event.stanza
+ assert iq['to'] == contact_name
+ query = iq.firstChildElement()
+ assert query.uri == 'jabber:iq:oob'
+ url_node = xpath.queryForNodes("/iq/query/url", iq)[0]
+ assert url_node['type'] == 'file'
+ assert url_node['size'] == str(FILE_SIZE)
+ assert url_node['mimeType'] == FILE_CONTENT_TYPE
+ url = url_node.children[0]
+ _, host, file, _, _, _ = urlparse.urlparse(url)
+ urllib.unquote(file) == FILE_NAME
+ desc_node = xpath.queryForNodes("/iq/query/desc", iq)[0]
+ desc = desc_node.children[0]
+ assert desc == FILE_DESCRIPTION
+
+ address = ft_channel.ProvideFile(SOCKET_ADDRESS_TYPE_UNIX, SOCKET_ACCESS_CONTROL_LOCALHOST, "")
+
+ # state is still Pending as remote didn't accept the transfer yet
+ state = ft_props.Get(CHANNEL_TYPE_FILE_TRANSFER, 'State')
+ assert state == FT_STATE_PENDING
+
+ # Connect HTTP client to the CM and request the file
+ http = httplib.HTTPConnection(host)
+ http.request('GET', file)
+
+ e = q.expect('dbus-signal', signal='InitialOffsetDefined')
+ offset = e.args[0]
+ # We don't support resume
+ assert offset == 0
+
+ # Channel is open. We can start to send the file
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_OPEN
+ assert reason == FT_STATE_CHANGE_REASON_NONE
+
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ s.connect(address)
+ s.send(FILE_DATA)
+
+ e = q.expect('dbus-signal', signal='TransferredBytesChanged')
+
+ count = e.args[0]
+ while count < FILE_SIZE:
+ # Catch TransferredBytesChanged until we transfered all the data
+ e = q.expect('dbus-signal', signal='TransferredBytesChanged')
+ count = e.args[0]
+
+ response = http.getresponse()
+ assert (response.status, response.reason) == (200, 'OK')
+ data = response.read(FILE_SIZE)
+ # Did we received the right file?
+ assert data == FILE_DATA
+
+ # Inform sender that we received all the file from the OOB transfer
+ reply = domish.Element(('', 'iq'))
+ reply['to'] = iq['from']
+ reply['from'] = iq['to']
+ reply['type'] = 'result'
+ reply['id'] = iq['id']
+ outbound.send(reply)
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_COMPLETED
+ assert reason == FT_STATE_CHANGE_REASON_NONE
+
+ channel.Close()
+ q.expect('dbus-signal', signal='Closed')
+
+ conn.Disconnect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[2L, 1L])
+
+class HTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+ def do_GET(self):
+ # is that the right file ?
+ filename = self.path.rsplit('/', 2)[-1]
+ assert filename == urllib.quote(FILE_NAME)
+
+ self.send_response(200)
+ self.send_header('Content-type', FILE_CONTENT_TYPE)
+ self.end_headers()
+ self.wfile.write(FILE_DATA)
+
+if __name__ == '__main__':
+ exec_test(test)
diff --git a/tests/twisted/avahi/test-receive-file-cancelled-immediately.py b/tests/twisted/avahi/test-receive-file-cancelled-immediately.py
new file mode 100644
index 00000000..96f8ee64
--- /dev/null
+++ b/tests/twisted/avahi/test-receive-file-cancelled-immediately.py
@@ -0,0 +1,198 @@
+import httplib
+import urlparse
+import dbus
+import socket
+import md5
+import avahi
+import BaseHTTPServer
+import urllib
+
+from saluttest import exec_test
+from avahitest import AvahiAnnouncer, AvahiListener
+from avahitest import get_host_name
+
+from xmppstream import setup_stream_listener, connect_to_stream
+from servicetest import make_channel_proxy, EventPattern
+
+from twisted.words.xish import domish
+
+from dbus import PROPERTIES_IFACE
+
+CONNECTION_INTERFACE_REQUESTS = 'org.freedesktop.Telepathy.Connection.Interface.Requests'
+CHANNEL_INTERFACE ='org.freedesktop.Telepathy.Channel'
+CHANNEL_TYPE_FILE_TRANSFER = 'org.freedesktop.Telepathy.Channel.Type.FileTransfer.DRAFT'
+
+HT_CONTACT = 1
+HT_CONTACT_LIST = 3
+TEXT_MESSAGE_TYPE_NORMAL = dbus.UInt32(0)
+
+FT_STATE_NONE = 0
+FT_STATE_PENDING = 1
+FT_STATE_ACCEPTED = 2
+FT_STATE_OPEN = 3
+FT_STATE_COMPLETED = 4
+FT_STATE_CANCELLED = 5
+
+FT_STATE_CHANGE_REASON_NONE = 0
+FT_STATE_CHANGE_REASON_REQUESTED = 1
+FT_STATE_CHANGE_REASON_REMOTE_STOPPED = 3
+
+FILE_HASH_TYPE_NONE = 0
+FILE_HASH_TYPE_MD5 = 1
+
+SOCKET_ADDRESS_TYPE_UNIX = 0
+SOCKET_ADDRESS_TYPE_IPV4 = 2
+
+SOCKET_ACCESS_CONTROL_LOCALHOST = 0
+
+# File to Offer
+FILE_DATA = "What a nice file"
+FILE_SIZE = len(FILE_DATA)
+FILE_NAME = 'The foo.txt'
+FILE_CONTENT_TYPE = 'text/plain'
+FILE_DESCRIPTION = 'A nice file to test'
+FILE_HASH_TYPE = FILE_HASH_TYPE_MD5
+m = md5.new()
+m.update(FILE_DATA)
+FILE_HASH = m.hexdigest()
+
+def test(q, bus, conn):
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
+ basic_txt = { "txtvers": "1", "status": "avail" }
+
+ self_handle = conn.GetSelfHandle()
+ self_handle_name = conn.InspectHandles(HT_CONTACT, [self_handle])[0]
+
+ contact_name = "test-file-sender@" + get_host_name()
+ listener, port = setup_stream_listener(q, contact_name)
+
+ AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt)
+
+ publish_handle = conn.RequestHandles(HT_CONTACT_LIST, ["publish"])[0]
+ publish = conn.RequestChannel(
+ "org.freedesktop.Telepathy.Channel.Type.ContactList",
+ HT_CONTACT_LIST, publish_handle, False)
+
+ handle = 0
+ # Wait until the record shows up in publish
+ while handle == 0:
+ e = q.expect('dbus-signal', signal='MembersChanged', path=publish)
+ for h in e.args[1]:
+ name = conn.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact_name:
+ handle = h
+
+ requests_iface = dbus.Interface(conn, CONNECTION_INTERFACE_REQUESTS)
+
+ # Create a connection to send the FT stanza
+ AvahiListener(q).listen_for_service("_presence._tcp")
+ e = q.expect('service-added', name = self_handle_name,
+ protocol = avahi.PROTO_INET)
+ service = e.service
+ service.resolve()
+
+ e = q.expect('service-resolved', service = service)
+
+ outbound = connect_to_stream(q, contact_name,
+ self_handle_name, str(e.pt), e.port)
+
+ e = q.expect('connection-result')
+ assert e.succeeded, e.reason
+ e = q.expect('stream-opened', connection = outbound)
+
+ # Setup the HTTP server
+ httpd = BaseHTTPServer.HTTPServer(('', 0), HTTPHandler)
+
+ # connected to Salut, now send the IQ
+ iq = domish.Element((None, 'iq'))
+ iq['to'] = self_handle_name
+ iq['from'] = contact_name
+ iq['type'] = 'set'
+ iq['id'] = 'gibber-file-transfer-0'
+ query = iq.addElement(('jabber:iq:oob', 'query'))
+ url = 'http://127.0.0.1:%u/gibber-file-transfer-0/%s' % (httpd.server_port, urllib.quote(FILE_NAME))
+ url_node = query.addElement('url', content=url)
+ url_node['type'] = 'file'
+ url_node['size'] = str(FILE_SIZE)
+ url_node['mimeType'] = FILE_CONTENT_TYPE
+ query.addElement('desc', content=FILE_DESCRIPTION)
+ outbound.send(iq)
+
+ e =q.expect('dbus-signal', signal='NewChannels')
+ channels = e.args[0]
+ assert len(channels) == 1
+ path, props = channels[0]
+
+ # check channel properties
+ # org.freedesktop.Telepathy.Channel D-Bus properties
+ assert props[CHANNEL_INTERFACE + '.ChannelType'] == CHANNEL_TYPE_FILE_TRANSFER
+ assert props[CHANNEL_INTERFACE + '.Interfaces'] == []
+ assert props[CHANNEL_INTERFACE + '.TargetHandle'] == handle
+ assert props[CHANNEL_INTERFACE + '.TargetID'] == contact_name
+ assert props[CHANNEL_INTERFACE + '.TargetHandleType'] == HT_CONTACT
+ assert props[CHANNEL_INTERFACE + '.Requested'] == False
+ assert props[CHANNEL_INTERFACE + '.InitiatorHandle'] == handle
+ assert props[CHANNEL_INTERFACE + '.InitiatorID'] == contact_name
+
+ # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.State'] == FT_STATE_PENDING
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentType'] == FILE_CONTENT_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Filename'] == FILE_NAME
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Size'] == FILE_SIZE
+ # FT's protocol doesn't allow us the send the hash info
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType'] == FILE_HASH_TYPE_NONE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash'] == ''
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Description'] == FILE_DESCRIPTION
+ # FT's protocol doesn't allow us the send the date info
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Date'] == 0
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.AvailableSocketTypes'] == \
+ {SOCKET_ADDRESS_TYPE_UNIX: [SOCKET_ACCESS_CONTROL_LOCALHOST]}
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.TransferredBytes'] == 0
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'] == 0
+
+ channel = make_channel_proxy(conn, path, 'Channel')
+ ft_channel = make_channel_proxy(conn, path, 'Channel.Type.FileTransfer.DRAFT')
+ ft_props = dbus.Interface(bus.get_object(conn.object.bus_name, path), PROPERTIES_IFACE)
+
+ # sender cancels FT immediately so stop to listen to the HTTP socket
+ httpd.server_close()
+
+ address = ft_channel.AcceptFile(SOCKET_ADDRESS_TYPE_UNIX, SOCKET_ACCESS_CONTROL_LOCALHOST, "", 5)
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_ACCEPTED
+ assert reason == FT_STATE_CHANGE_REASON_REQUESTED
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_OPEN
+ assert reason == FT_STATE_CHANGE_REASON_NONE
+
+ # Connect to Salut's socket
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ s.connect(address)
+
+ # Salut can't connect to download the file
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_CANCELLED
+ assert reason == FT_STATE_CHANGE_REASON_REMOTE_STOPPED
+
+ channel.Close()
+ q.expect('dbus-signal', signal='Closed')
+
+class HTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+ def do_GET(self):
+ # is that the right file ?
+ filename = self.path.rsplit('/', 2)[-1]
+ assert filename == urllib.quote(FILE_NAME)
+
+ self.send_response(200)
+ self.send_header('Content-type', FILE_CONTENT_TYPE)
+ self.end_headers()
+ self.wfile.write(FILE_DATA)
+
+if __name__ == '__main__':
+ exec_test(test)
diff --git a/tests/twisted/avahi/test-receive-file-decline.py b/tests/twisted/avahi/test-receive-file-decline.py
new file mode 100644
index 00000000..1c827c0b
--- /dev/null
+++ b/tests/twisted/avahi/test-receive-file-decline.py
@@ -0,0 +1,209 @@
+import httplib
+import urlparse
+import dbus
+import socket
+import md5
+import avahi
+import BaseHTTPServer
+import urllib
+
+from saluttest import exec_test
+from avahitest import AvahiAnnouncer, AvahiListener
+from avahitest import get_host_name
+
+from xmppstream import setup_stream_listener, connect_to_stream
+from servicetest import make_channel_proxy, EventPattern
+
+from twisted.words.xish import domish
+
+from dbus import PROPERTIES_IFACE
+
+CONNECTION_INTERFACE_REQUESTS = 'org.freedesktop.Telepathy.Connection.Interface.Requests'
+CHANNEL_INTERFACE ='org.freedesktop.Telepathy.Channel'
+CHANNEL_TYPE_FILE_TRANSFER = 'org.freedesktop.Telepathy.Channel.Type.FileTransfer.DRAFT'
+
+HT_CONTACT = 1
+HT_CONTACT_LIST = 3
+TEXT_MESSAGE_TYPE_NORMAL = dbus.UInt32(0)
+
+FT_STATE_NONE = 0
+FT_STATE_PENDING = 1
+FT_STATE_ACCEPTED = 2
+FT_STATE_OPEN = 3
+FT_STATE_COMPLETED = 4
+FT_STATE_CANCELLED = 5
+
+FT_STATE_CHANGE_REASON_NONE = 0
+FT_STATE_CHANGE_REASON_REQUESTED = 1
+FT_STATE_CHANGE_REASON_LOCAL_STOPPED = 2
+
+FILE_HASH_TYPE_NONE = 0
+FILE_HASH_TYPE_MD5 = 1
+
+SOCKET_ADDRESS_TYPE_UNIX = 0
+SOCKET_ADDRESS_TYPE_IPV4 = 2
+
+SOCKET_ACCESS_CONTROL_LOCALHOST = 0
+
+# File to Offer
+FILE_DATA = "What a nice file"
+FILE_SIZE = len(FILE_DATA)
+FILE_NAME = 'The foo.txt'
+FILE_CONTENT_TYPE = 'text/plain'
+FILE_DESCRIPTION = 'A nice file to test'
+FILE_HASH_TYPE = FILE_HASH_TYPE_MD5
+m = md5.new()
+m.update(FILE_DATA)
+FILE_HASH = m.hexdigest()
+
+def test(q, bus, conn):
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
+ basic_txt = { "txtvers": "1", "status": "avail" }
+
+ self_handle = conn.GetSelfHandle()
+ self_handle_name = conn.InspectHandles(HT_CONTACT, [self_handle])[0]
+
+ contact_name = "test-file-sender@" + get_host_name()
+ listener, port = setup_stream_listener(q, contact_name)
+
+ AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt)
+
+ publish_handle = conn.RequestHandles(HT_CONTACT_LIST, ["publish"])[0]
+ publish = conn.RequestChannel(
+ "org.freedesktop.Telepathy.Channel.Type.ContactList",
+ HT_CONTACT_LIST, publish_handle, False)
+
+ handle = 0
+ # Wait until the record shows up in publish
+ while handle == 0:
+ e = q.expect('dbus-signal', signal='MembersChanged', path=publish)
+ for h in e.args[1]:
+ name = conn.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact_name:
+ handle = h
+
+ requests_iface = dbus.Interface(conn, CONNECTION_INTERFACE_REQUESTS)
+
+ # Create a connection to send the FT stanza
+ AvahiListener(q).listen_for_service("_presence._tcp")
+ e = q.expect('service-added', name = self_handle_name,
+ protocol = avahi.PROTO_INET)
+ service = e.service
+ service.resolve()
+
+ e = q.expect('service-resolved', service = service)
+
+ outbound = connect_to_stream(q, contact_name,
+ self_handle_name, str(e.pt), e.port)
+
+ e = q.expect('connection-result')
+ assert e.succeeded, e.reason
+ e = q.expect('stream-opened', connection = outbound)
+
+ # Setup the HTTP server
+ httpd = BaseHTTPServer.HTTPServer(('', 0), HTTPHandler)
+
+ # connected to Salut, now send the IQ
+ iq = domish.Element((None, 'iq'))
+ iq['to'] = self_handle_name
+ iq['from'] = contact_name
+ iq['type'] = 'set'
+ iq['id'] = 'gibber-file-transfer-0'
+ query = iq.addElement(('jabber:iq:oob', 'query'))
+ url = 'http://127.0.0.1:%u/gibber-file-transfer-0/%s' % (httpd.server_port, urllib.quote(FILE_NAME))
+ url_node = query.addElement('url', content=url)
+ url_node['type'] = 'file'
+ url_node['size'] = str(FILE_SIZE)
+ url_node['mimeType'] = FILE_CONTENT_TYPE
+ query.addElement('desc', content=FILE_DESCRIPTION)
+ outbound.send(iq)
+
+ e = q.expect('dbus-signal', signal='NewChannels')
+ channels = e.args[0]
+ assert len(channels) == 1
+ path, props = channels[0]
+
+ # check channel properties
+ # org.freedesktop.Telepathy.Channel D-Bus properties
+ assert props[CHANNEL_INTERFACE + '.ChannelType'] == CHANNEL_TYPE_FILE_TRANSFER
+ assert props[CHANNEL_INTERFACE + '.Interfaces'] == []
+ assert props[CHANNEL_INTERFACE + '.TargetHandle'] == handle
+ assert props[CHANNEL_INTERFACE + '.TargetID'] == contact_name
+ assert props[CHANNEL_INTERFACE + '.TargetHandleType'] == HT_CONTACT
+ assert props[CHANNEL_INTERFACE + '.Requested'] == False
+ assert props[CHANNEL_INTERFACE + '.InitiatorHandle'] == handle
+ assert props[CHANNEL_INTERFACE + '.InitiatorID'] == contact_name
+
+ # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.State'] == FT_STATE_PENDING
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentType'] == FILE_CONTENT_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Filename'] == FILE_NAME
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Size'] == FILE_SIZE
+ # FT's protocol doesn't allow us the send the hash info
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType'] == FILE_HASH_TYPE_NONE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash'] == ''
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Description'] == FILE_DESCRIPTION
+ # FT's protocol doesn't allow us the send the date info
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Date'] == 0
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.AvailableSocketTypes'] == \
+ {SOCKET_ADDRESS_TYPE_UNIX: [SOCKET_ACCESS_CONTROL_LOCALHOST]}
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.TransferredBytes'] == 0
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'] == 0
+
+ channel = make_channel_proxy(conn, path, 'Channel')
+
+ # decline FT
+ channel.Close()
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_CANCELLED
+ assert reason == FT_STATE_CHANGE_REASON_LOCAL_STOPPED
+ q.expect('dbus-signal', signal='Closed')
+
+ # Re send offer (this is a regression test as Salut used to crash at this
+ # point)
+ iq = domish.Element((None, 'iq'))
+ iq['to'] = self_handle_name
+ iq['from'] = contact_name
+ iq['type'] = 'set'
+ iq['id'] = 'gibber-file-transfer-1'
+ query = iq.addElement(('jabber:iq:oob', 'query'))
+ url = 'http://127.0.0.1:%u/gibber-file-transfer-0/%s' % (httpd.server_port, urllib.quote(FILE_NAME))
+ url_node = query.addElement('url', content=url)
+ url_node['type'] = 'file'
+ url_node['size'] = str(FILE_SIZE)
+ url_node['mimeType'] = FILE_CONTENT_TYPE
+ query.addElement('desc', content=FILE_DESCRIPTION)
+ outbound.send(iq)
+
+ e = q.expect('dbus-signal', signal='NewChannels')
+ channels = e.args[0]
+ assert len(channels) == 1
+ path, props = channels[0]
+
+ channel = make_channel_proxy(conn, path, 'Channel')
+
+ # decline FT
+ channel.Close()
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_CANCELLED
+ assert reason == FT_STATE_CHANGE_REASON_LOCAL_STOPPED
+ q.expect('dbus-signal', signal='Closed')
+
+class HTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+ def do_GET(self):
+ # is that the right file ?
+ filename = self.path.rsplit('/', 2)[-1]
+ assert filename == urllib.quote(FILE_NAME)
+
+ self.send_response(200)
+ self.send_header('Content-type', FILE_CONTENT_TYPE)
+ self.end_headers()
+ self.wfile.write(FILE_DATA)
+
+if __name__ == '__main__':
+ exec_test(test)
diff --git a/tests/twisted/avahi/test-receive-file-not-found.py b/tests/twisted/avahi/test-receive-file-not-found.py
new file mode 100644
index 00000000..25c1429e
--- /dev/null
+++ b/tests/twisted/avahi/test-receive-file-not-found.py
@@ -0,0 +1,215 @@
+import httplib
+import urlparse
+import dbus
+import socket
+import md5
+import avahi
+import BaseHTTPServer
+
+from saluttest import exec_test
+from avahitest import AvahiAnnouncer, AvahiListener
+from avahitest import get_host_name
+
+from xmppstream import setup_stream_listener, connect_to_stream
+from servicetest import make_channel_proxy, EventPattern
+
+from twisted.words.xish import domish, xpath
+
+from dbus import PROPERTIES_IFACE
+
+CONNECTION_INTERFACE_REQUESTS = 'org.freedesktop.Telepathy.Connection.Interface.Requests'
+CHANNEL_INTERFACE ='org.freedesktop.Telepathy.Channel'
+CHANNEL_TYPE_FILE_TRANSFER = 'org.freedesktop.Telepathy.Channel.Type.FileTransfer.DRAFT'
+
+HT_CONTACT = 1
+HT_CONTACT_LIST = 3
+TEXT_MESSAGE_TYPE_NORMAL = dbus.UInt32(0)
+
+FT_STATE_NONE = 0
+FT_STATE_PENDING = 1
+FT_STATE_ACCEPTED = 2
+FT_STATE_OPEN = 3
+FT_STATE_COMPLETED = 4
+FT_STATE_CANCELLED = 5
+
+FT_STATE_CHANGE_REASON_NONE = 0
+FT_STATE_CHANGE_REASON_REQUESTED = 1
+FT_STATE_CHANGE_REASON_LOCAL_STOPPED = 2
+FT_STATE_CHANGE_REASON_REMOTE_STOPPED = 3
+FT_STATE_CHANGE_REASON_LOCAL_ERROR = 4
+FT_STATE_CHANGE_REASON_REMOTE_ERROR = 5
+
+FILE_HASH_TYPE_NONE = 0
+FILE_HASH_TYPE_MD5 = 1
+
+SOCKET_ADDRESS_TYPE_UNIX = 0
+SOCKET_ADDRESS_TYPE_IPV4 = 2
+
+SOCKET_ACCESS_CONTROL_LOCALHOST = 0
+
+# File to Offer
+FILE_DATA = "What a nice fileeeeeeeeeeeeeee"
+FILE_SIZE = len(FILE_DATA)
+FILE_NAME = 'foo.txt'
+FILE_CONTENT_TYPE = 'text/plain'
+FILE_DESCRIPTION = 'A nice file to test'
+FILE_HASH_TYPE = FILE_HASH_TYPE_MD5
+m = md5.new()
+m.update(FILE_DATA)
+FILE_HASH = m.hexdigest()
+
+def test(q, bus, conn):
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
+ basic_txt = { "txtvers": "1", "status": "avail" }
+
+ self_handle = conn.GetSelfHandle()
+ self_handle_name = conn.InspectHandles(HT_CONTACT, [self_handle])[0]
+
+ contact_name = "test-file-sender@" + get_host_name()
+ listener, port = setup_stream_listener(q, contact_name)
+
+ AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt)
+
+ publish_handle = conn.RequestHandles(HT_CONTACT_LIST, ["publish"])[0]
+ publish = conn.RequestChannel(
+ "org.freedesktop.Telepathy.Channel.Type.ContactList",
+ HT_CONTACT_LIST, publish_handle, False)
+
+ handle = 0
+ # Wait until the record shows up in publish
+ while handle == 0:
+ e = q.expect('dbus-signal', signal='MembersChanged', path=publish)
+ for h in e.args[1]:
+ name = conn.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact_name:
+ handle = h
+
+ requests_iface = dbus.Interface(conn, CONNECTION_INTERFACE_REQUESTS)
+
+ # Create a connection to send the FT stanza
+ AvahiListener(q).listen_for_service("_presence._tcp")
+ e = q.expect('service-added', name = self_handle_name,
+ protocol = avahi.PROTO_INET)
+ service = e.service
+ service.resolve()
+
+ e = q.expect('service-resolved', service = service)
+
+ outbound = connect_to_stream(q, contact_name,
+ self_handle_name, str(e.pt), e.port)
+
+ e = q.expect('connection-result')
+ assert e.succeeded, e.reason
+ e = q.expect('stream-opened', connection = outbound)
+
+ # Setup the HTTP server
+ httpd = BaseHTTPServer.HTTPServer(('', 0), HTTPHandler)
+
+ # connected to Salut, now send the IQ
+ iq = domish.Element((None, 'iq'))
+ iq['to'] = self_handle_name
+ iq['from'] = contact_name
+ iq['type'] = 'set'
+ iq['id'] = 'gibber-file-transfer-0'
+ query = iq.addElement(('jabber:iq:oob', 'query'))
+ url = 'http://127.0.0.1:%u/gibber-file-transfer-0/%s' % (httpd.server_port, FILE_NAME)
+ url_node = query.addElement('url', content=url)
+ url_node['type'] = 'file'
+ url_node['size'] = str(FILE_SIZE)
+ url_node['mimeType'] = FILE_CONTENT_TYPE
+ query.addElement('desc', content=FILE_DESCRIPTION)
+ outbound.send(iq)
+
+ e =q.expect('dbus-signal', signal='NewChannels')
+ channels = e.args[0]
+ assert len(channels) == 1
+ path, props = channels[0]
+
+ # check channel properties
+ # org.freedesktop.Telepathy.Channel D-Bus properties
+ assert props[CHANNEL_INTERFACE + '.ChannelType'] == CHANNEL_TYPE_FILE_TRANSFER
+ assert props[CHANNEL_INTERFACE + '.Interfaces'] == []
+ assert props[CHANNEL_INTERFACE + '.TargetHandle'] == handle
+ assert props[CHANNEL_INTERFACE + '.TargetID'] == contact_name
+ assert props[CHANNEL_INTERFACE + '.TargetHandleType'] == HT_CONTACT
+ assert props[CHANNEL_INTERFACE + '.Requested'] == False
+ assert props[CHANNEL_INTERFACE + '.InitiatorHandle'] == handle
+ assert props[CHANNEL_INTERFACE + '.InitiatorID'] == contact_name
+
+ # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.State'] == FT_STATE_PENDING
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentType'] == FILE_CONTENT_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Filename'] == FILE_NAME
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Size'] == FILE_SIZE
+ # FT's protocol doesn't allow us the send the hash info
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType'] == FILE_HASH_TYPE_NONE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash'] == ''
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Description'] == FILE_DESCRIPTION
+ # FT's protocol doesn't allow us the send the date info
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Date'] == 0
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.AvailableSocketTypes'] == \
+ {SOCKET_ADDRESS_TYPE_UNIX: [SOCKET_ACCESS_CONTROL_LOCALHOST]}
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.TransferredBytes'] == 0
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'] == 0
+
+ channel = make_channel_proxy(conn, path, 'Channel')
+ ft_channel = make_channel_proxy(conn, path, 'Channel.Type.FileTransfer.DRAFT')
+ ft_props = dbus.Interface(bus.get_object(conn.object.bus_name, path), PROPERTIES_IFACE)
+
+ address = ft_channel.AcceptFile(SOCKET_ADDRESS_TYPE_UNIX, SOCKET_ACCESS_CONTROL_LOCALHOST, "", 5)
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_ACCEPTED
+ assert reason == FT_STATE_CHANGE_REASON_REQUESTED
+
+ e = q.expect('dbus-signal', signal='InitialOffsetDefined')
+ offset = e.args[0]
+ # We don't support resume
+ assert offset == 0
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_OPEN
+ assert reason == FT_STATE_CHANGE_REASON_NONE
+
+ # Connect to Salut's socket
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ s.connect(address)
+
+ httpd.handle_request()
+
+ # Receiver inform us he finished to download the file
+ e = q.expect('stream-iq', iq_type='error')
+ iq = e.stanza
+ error_node = xpath.queryForNodes("/iq/error", iq)[0]
+ assert error_node['code'] == '404'
+ assert error_node['type'] == 'cancel'
+ not_found_node = error_node.firstChildElement()
+ assert not_found_node.name == 'item-not-found'
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_CANCELLED
+ assert reason == FT_STATE_CHANGE_REASON_LOCAL_ERROR
+
+ transferred = ft_props.Get(CHANNEL_TYPE_FILE_TRANSFER, 'TransferredBytes')
+ # no byte has been transferred as the transfer failed
+ assert transferred == 0
+
+ channel.Close()
+ q.expect('dbus-signal', signal='Closed')
+
+class HTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+ def do_GET(self):
+ # is that the right file ?
+ filename = self.path.rsplit('/', 2)[-1]
+ assert filename == FILE_NAME
+
+ self.send_response(404)
+ self.end_headers()
+ self.wfile.write(FILE_DATA)
+
+if __name__ == '__main__':
+ exec_test(test)
diff --git a/tests/twisted/avahi/test-receive-file.py b/tests/twisted/avahi/test-receive-file.py
new file mode 100644
index 00000000..03b4e96f
--- /dev/null
+++ b/tests/twisted/avahi/test-receive-file.py
@@ -0,0 +1,218 @@
+import httplib
+import urlparse
+import dbus
+import socket
+import md5
+import avahi
+import BaseHTTPServer
+import urllib
+
+from saluttest import exec_test
+from avahitest import AvahiAnnouncer, AvahiListener
+from avahitest import get_host_name
+
+from xmppstream import setup_stream_listener, connect_to_stream
+from servicetest import make_channel_proxy, EventPattern
+
+from twisted.words.xish import domish
+
+from dbus import PROPERTIES_IFACE
+
+CONNECTION_INTERFACE_REQUESTS = 'org.freedesktop.Telepathy.Connection.Interface.Requests'
+CHANNEL_INTERFACE ='org.freedesktop.Telepathy.Channel'
+CHANNEL_TYPE_FILE_TRANSFER = 'org.freedesktop.Telepathy.Channel.Type.FileTransfer.DRAFT'
+
+HT_CONTACT = 1
+HT_CONTACT_LIST = 3
+TEXT_MESSAGE_TYPE_NORMAL = dbus.UInt32(0)
+
+FT_STATE_NONE = 0
+FT_STATE_PENDING = 1
+FT_STATE_ACCEPTED = 2
+FT_STATE_OPEN = 3
+FT_STATE_COMPLETED = 4
+FT_STATE_CANCELLED = 5
+
+FT_STATE_CHANGE_REASON_NONE = 0
+FT_STATE_CHANGE_REASON_REQUESTED = 1
+
+FILE_HASH_TYPE_NONE = 0
+FILE_HASH_TYPE_MD5 = 1
+
+SOCKET_ADDRESS_TYPE_UNIX = 0
+SOCKET_ADDRESS_TYPE_IPV4 = 2
+
+SOCKET_ACCESS_CONTROL_LOCALHOST = 0
+
+# File to Offer
+FILE_DATA = "What a nice file"
+FILE_SIZE = len(FILE_DATA)
+FILE_NAME = 'The foo.txt'
+FILE_CONTENT_TYPE = 'text/plain'
+FILE_DESCRIPTION = 'A nice file to test'
+FILE_HASH_TYPE = FILE_HASH_TYPE_MD5
+m = md5.new()
+m.update(FILE_DATA)
+FILE_HASH = m.hexdigest()
+
+def test(q, bus, conn):
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
+ basic_txt = { "txtvers": "1", "status": "avail" }
+
+ self_handle = conn.GetSelfHandle()
+ self_handle_name = conn.InspectHandles(HT_CONTACT, [self_handle])[0]
+
+ contact_name = "test-file-sender@" + get_host_name()
+ listener, port = setup_stream_listener(q, contact_name)
+
+ AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt)
+
+ publish_handle = conn.RequestHandles(HT_CONTACT_LIST, ["publish"])[0]
+ publish = conn.RequestChannel(
+ "org.freedesktop.Telepathy.Channel.Type.ContactList",
+ HT_CONTACT_LIST, publish_handle, False)
+
+ handle = 0
+ # Wait until the record shows up in publish
+ while handle == 0:
+ e = q.expect('dbus-signal', signal='MembersChanged', path=publish)
+ for h in e.args[1]:
+ name = conn.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact_name:
+ handle = h
+
+ requests_iface = dbus.Interface(conn, CONNECTION_INTERFACE_REQUESTS)
+
+ # Create a connection to send the FT stanza
+ AvahiListener(q).listen_for_service("_presence._tcp")
+ e = q.expect('service-added', name = self_handle_name,
+ protocol = avahi.PROTO_INET)
+ service = e.service
+ service.resolve()
+
+ e = q.expect('service-resolved', service = service)
+
+ outbound = connect_to_stream(q, contact_name,
+ self_handle_name, str(e.pt), e.port)
+
+ e = q.expect('connection-result')
+ assert e.succeeded, e.reason
+ e = q.expect('stream-opened', connection = outbound)
+
+ # Setup the HTTP server
+ httpd = BaseHTTPServer.HTTPServer(('', 0), HTTPHandler)
+
+ # connected to Salut, now send the IQ
+ iq = domish.Element((None, 'iq'))
+ iq['to'] = self_handle_name
+ iq['from'] = contact_name
+ iq['type'] = 'set'
+ iq['id'] = 'gibber-file-transfer-0'
+ query = iq.addElement(('jabber:iq:oob', 'query'))
+ url = 'http://127.0.0.1:%u/gibber-file-transfer-0/%s' % (httpd.server_port, urllib.quote(FILE_NAME))
+ url_node = query.addElement('url', content=url)
+ url_node['type'] = 'file'
+ url_node['size'] = str(FILE_SIZE)
+ url_node['mimeType'] = FILE_CONTENT_TYPE
+ query.addElement('desc', content=FILE_DESCRIPTION)
+ outbound.send(iq)
+
+ e =q.expect('dbus-signal', signal='NewChannels')
+ channels = e.args[0]
+ assert len(channels) == 1
+ path, props = channels[0]
+
+ # check channel properties
+ # org.freedesktop.Telepathy.Channel D-Bus properties
+ assert props[CHANNEL_INTERFACE + '.ChannelType'] == CHANNEL_TYPE_FILE_TRANSFER
+ assert props[CHANNEL_INTERFACE + '.Interfaces'] == []
+ assert props[CHANNEL_INTERFACE + '.TargetHandle'] == handle
+ assert props[CHANNEL_INTERFACE + '.TargetID'] == contact_name
+ assert props[CHANNEL_INTERFACE + '.TargetHandleType'] == HT_CONTACT
+ assert props[CHANNEL_INTERFACE + '.Requested'] == False
+ assert props[CHANNEL_INTERFACE + '.InitiatorHandle'] == handle
+ assert props[CHANNEL_INTERFACE + '.InitiatorID'] == contact_name
+
+ # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.State'] == FT_STATE_PENDING
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentType'] == FILE_CONTENT_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Filename'] == FILE_NAME
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Size'] == FILE_SIZE
+ # FT's protocol doesn't allow us the send the hash info
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType'] == FILE_HASH_TYPE_NONE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash'] == ''
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Description'] == FILE_DESCRIPTION
+ # FT's protocol doesn't allow us the send the date info
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Date'] == 0
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.AvailableSocketTypes'] == \
+ {SOCKET_ADDRESS_TYPE_UNIX: [SOCKET_ACCESS_CONTROL_LOCALHOST]}
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.TransferredBytes'] == 0
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'] == 0
+
+ channel = make_channel_proxy(conn, path, 'Channel')
+ ft_channel = make_channel_proxy(conn, path, 'Channel.Type.FileTransfer.DRAFT')
+ ft_props = dbus.Interface(bus.get_object(conn.object.bus_name, path), PROPERTIES_IFACE)
+
+ address = ft_channel.AcceptFile(SOCKET_ADDRESS_TYPE_UNIX, SOCKET_ACCESS_CONTROL_LOCALHOST, "", 5)
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_ACCEPTED
+ assert reason == FT_STATE_CHANGE_REASON_REQUESTED
+
+ e = q.expect('dbus-signal', signal='InitialOffsetDefined')
+ offset = e.args[0]
+ # We don't support resume
+ assert offset == 0
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_OPEN
+ assert reason == FT_STATE_CHANGE_REASON_NONE
+
+ # Connect to Salut's socket
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ s.connect(address)
+
+ httpd.handle_request()
+
+ # Receiver inform us he finished to download the file
+ q.expect('stream-iq', iq_type='result')
+
+ # Read the file from Salut's socket
+ data = ''
+ read = 0
+ while read < FILE_SIZE:
+ data += s.recv(FILE_SIZE - read)
+ read = len(data)
+ assert data == FILE_DATA
+
+ e = q.expect('dbus-signal', signal='TransferredBytesChanged')
+ count = e.args[0]
+ while count < FILE_SIZE:
+ # Catch TransferredBytesChanged until we transfered all the data
+ e = q.expect('dbus-signal', signal='TransferredBytesChanged')
+ count = e.args[0]
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_COMPLETED
+ assert reason == FT_STATE_CHANGE_REASON_NONE
+
+ channel.Close()
+ q.expect('dbus-signal', signal='Closed')
+
+class HTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+ def do_GET(self):
+ # is that the right file ?
+ filename = self.path.rsplit('/', 2)[-1]
+ assert filename == urllib.quote(FILE_NAME)
+
+ self.send_response(200)
+ self.send_header('Content-type', FILE_CONTENT_TYPE)
+ self.end_headers()
+ self.wfile.write(FILE_DATA)
+
+if __name__ == '__main__':
+ exec_test(test)
diff --git a/tests/twisted/avahi/test-send-file-and-cancel-immediately.py b/tests/twisted/avahi/test-send-file-and-cancel-immediately.py
new file mode 100644
index 00000000..3954e236
--- /dev/null
+++ b/tests/twisted/avahi/test-send-file-and-cancel-immediately.py
@@ -0,0 +1,202 @@
+import httplib
+import urlparse
+import dbus
+import socket
+import md5
+import urllib
+import errno
+
+from saluttest import exec_test
+from avahitest import AvahiAnnouncer
+from avahitest import get_host_name
+
+from xmppstream import setup_stream_listener
+from servicetest import make_channel_proxy, EventPattern
+
+from twisted.words.xish import domish, xpath
+
+from dbus import PROPERTIES_IFACE
+
+CONNECTION_INTERFACE_REQUESTS = 'org.freedesktop.Telepathy.Connection.Interface.Requests'
+CHANNEL_INTERFACE ='org.freedesktop.Telepathy.Channel'
+CHANNEL_TYPE_FILE_TRANSFER = 'org.freedesktop.Telepathy.Channel.Type.FileTransfer.DRAFT'
+
+HT_CONTACT = 1
+HT_CONTACT_LIST = 3
+TEXT_MESSAGE_TYPE_NORMAL = dbus.UInt32(0)
+
+FT_STATE_NONE = 0
+FT_STATE_PENDING = 1
+FT_STATE_ACCEPTED = 2
+FT_STATE_OPEN = 3
+FT_STATE_COMPLETED = 4
+FT_STATE_CANCELLED = 5
+
+FT_STATE_CHANGE_REASON_NONE = 0
+FT_STATE_CHANGE_REASON_REQUESTED = 1
+FT_STATE_CHANGE_REASON_LOCAL_STOPPED = 2
+
+FILE_HASH_TYPE_MD5 = 1
+
+SOCKET_ADDRESS_TYPE_UNIX = 0
+SOCKET_ADDRESS_TYPE_IPV4 = 2
+
+SOCKET_ACCESS_CONTROL_LOCALHOST = 0
+
+# File to Offer
+FILE_DATA = "That works!"
+FILE_SIZE = len(FILE_DATA)
+FILE_NAME = 'My test.txt'
+FILE_CONTENT_TYPE = 'text/plain'
+FILE_DESCRIPTION = 'A nice file to test'
+FILE_HASH_TYPE = FILE_HASH_TYPE_MD5
+m = md5.new()
+m.update(FILE_DATA)
+FILE_HASH = m.hexdigest()
+
+# TODO: There is lot of duplicated code between FT tests. Would be good to
+# refactor them.
+
+def test(q, bus, conn):
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
+ basic_txt = { "txtvers": "1", "status": "avail" }
+
+ self_handle = conn.GetSelfHandle()
+ self_handle_name = conn.InspectHandles(HT_CONTACT, [self_handle])[0]
+
+ contact_name = "test-file-receiver@" + get_host_name()
+ listener, port = setup_stream_listener(q, contact_name)
+
+ AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt)
+
+ publish_handle = conn.RequestHandles(HT_CONTACT_LIST, ["publish"])[0]
+ publish = conn.RequestChannel(
+ "org.freedesktop.Telepathy.Channel.Type.ContactList",
+ HT_CONTACT_LIST, publish_handle, False)
+
+ handle = 0
+ # Wait until the record shows up in publish
+ while handle == 0:
+ e = q.expect('dbus-signal', signal='MembersChanged', path=publish)
+ for h in e.args[1]:
+ name = conn.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact_name:
+ handle = h
+
+ requests_iface = dbus.Interface(conn, CONNECTION_INTERFACE_REQUESTS)
+
+ # check if we can request FileTransfer channels
+ properties = conn.GetAll(
+ 'org.freedesktop.Telepathy.Connection.Interface.Requests',
+ dbus_interface='org.freedesktop.DBus.Properties')
+
+ assert ({CHANNEL_INTERFACE + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
+ CHANNEL_INTERFACE + '.TargetHandleType': HT_CONTACT},
+ [CHANNEL_INTERFACE + '.TargetHandle',
+ CHANNEL_INTERFACE + '.TargetID',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentType',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Filename',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Size',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Description',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Date',
+ CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'],
+ ) in properties.get('RequestableChannelClasses'),\
+ properties['RequestableChannelClasses']
+
+ path, props = requests_iface.CreateChannel({
+ CHANNEL_INTERFACE + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
+ CHANNEL_INTERFACE + '.TargetHandleType': HT_CONTACT,
+ CHANNEL_INTERFACE + '.TargetHandle': handle,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentType': FILE_CONTENT_TYPE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Filename': FILE_NAME,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Size': FILE_SIZE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType': FILE_HASH_TYPE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash': FILE_HASH,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Description': FILE_DESCRIPTION,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Date': 1225278834,
+ CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset': 0,
+ })
+
+ # org.freedesktop.Telepathy.Channel D-Bus properties
+ assert props[CHANNEL_INTERFACE + '.ChannelType'] == CHANNEL_TYPE_FILE_TRANSFER
+ assert props[CHANNEL_INTERFACE + '.Interfaces'] == []
+ assert props[CHANNEL_INTERFACE + '.TargetHandle'] == handle
+ assert props[CHANNEL_INTERFACE + '.TargetID'] == contact_name
+ assert props[CHANNEL_INTERFACE + '.TargetHandleType'] == HT_CONTACT
+ assert props[CHANNEL_INTERFACE + '.Requested'] == True
+ assert props[CHANNEL_INTERFACE + '.InitiatorHandle'] == self_handle
+ assert props[CHANNEL_INTERFACE + '.InitiatorID'] == self_handle_name
+
+ # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.State'] == FT_STATE_PENDING
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentType'] == FILE_CONTENT_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Filename'] == FILE_NAME
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Size'] == FILE_SIZE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType'] == FILE_HASH_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash'] == FILE_HASH
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Description'] == FILE_DESCRIPTION
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Date'] == 1225278834
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.AvailableSocketTypes'] == \
+ {SOCKET_ADDRESS_TYPE_UNIX: [SOCKET_ACCESS_CONTROL_LOCALHOST]}
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.TransferredBytes'] == 0
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'] == 0
+
+ channel = make_channel_proxy(conn, path, 'Channel')
+ ft_channel = make_channel_proxy(conn, path, 'Channel.Type.FileTransfer.DRAFT')
+ ft_props = dbus.Interface(bus.get_object(conn.object.bus_name, path), PROPERTIES_IFACE)
+
+ conn_event, iq_event = q.expect_many(
+ EventPattern('incoming-connection', listener = listener),
+ EventPattern('stream-iq'))
+
+ incoming = conn_event.connection
+
+ assert iq_event.iq_type == 'set'
+ assert iq_event.connection == incoming
+ iq = iq_event.stanza
+ assert iq['to'] == contact_name
+ query = iq.firstChildElement()
+ assert query.uri == 'jabber:iq:oob'
+ url_node = xpath.queryForNodes("/iq/query/url", iq)[0]
+ assert url_node['type'] == 'file'
+ assert url_node['size'] == str(FILE_SIZE)
+ assert url_node['mimeType'] == FILE_CONTENT_TYPE
+ url = url_node.children[0]
+ _, host, file, _, _, _ = urlparse.urlparse(url)
+ urllib.unquote(file) == FILE_NAME
+ desc_node = xpath.queryForNodes("/iq/query/desc", iq)[0]
+ desc = desc_node.children[0]
+ assert desc == FILE_DESCRIPTION
+
+ address = ft_channel.ProvideFile(SOCKET_ADDRESS_TYPE_UNIX, SOCKET_ACCESS_CONTROL_LOCALHOST, "")
+
+ # state is still Pending as remote didn't accept the transfer yet
+ state = ft_props.Get(CHANNEL_TYPE_FILE_TRANSFER, 'State')
+ assert state == FT_STATE_PENDING
+
+ # cancel the transfer before the receiver accepts it
+ channel.Close()
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_CANCELLED
+ assert reason == FT_STATE_CHANGE_REASON_LOCAL_STOPPED
+
+ q.expect('dbus-signal', signal='Closed')
+
+ # Connect HTTP client to the CM and request the file
+ http = httplib.HTTPConnection(host)
+ # can't retry the file as the transfer was cancelled
+ try:
+ http.request('GET', file)
+ except socket.error, e:
+ code, msg = e.args
+ assert errno.errorcode[code] == 'ECONNREFUSED'
+ else:
+ assert False
+
+if __name__ == '__main__':
+ exec_test(test)
diff --git a/tests/twisted/avahi/test-send-file-and-disconnect.py b/tests/twisted/avahi/test-send-file-and-disconnect.py
new file mode 100644
index 00000000..aa2def19
--- /dev/null
+++ b/tests/twisted/avahi/test-send-file-and-disconnect.py
@@ -0,0 +1,183 @@
+import httplib
+import urlparse
+import dbus
+import socket
+import md5
+import urllib
+
+from saluttest import exec_test
+from avahitest import AvahiAnnouncer
+from avahitest import get_host_name
+
+from xmppstream import setup_stream_listener
+from servicetest import make_channel_proxy, EventPattern
+
+from twisted.words.xish import domish, xpath
+
+from dbus import PROPERTIES_IFACE
+
+CONNECTION_INTERFACE_REQUESTS = 'org.freedesktop.Telepathy.Connection.Interface.Requests'
+CHANNEL_INTERFACE ='org.freedesktop.Telepathy.Channel'
+CHANNEL_TYPE_FILE_TRANSFER = 'org.freedesktop.Telepathy.Channel.Type.FileTransfer.DRAFT'
+
+HT_CONTACT = 1
+HT_CONTACT_LIST = 3
+TEXT_MESSAGE_TYPE_NORMAL = dbus.UInt32(0)
+
+FT_STATE_NONE = 0
+FT_STATE_PENDING = 1
+FT_STATE_ACCEPTED = 2
+FT_STATE_OPEN = 3
+FT_STATE_COMPLETED = 4
+FT_STATE_CANCELLED = 5
+
+FT_STATE_CHANGE_REASON_NONE = 0
+FT_STATE_CHANGE_REASON_REQUESTED = 1
+
+FILE_HASH_TYPE_MD5 = 1
+
+SOCKET_ADDRESS_TYPE_UNIX = 0
+SOCKET_ADDRESS_TYPE_IPV4 = 2
+
+SOCKET_ACCESS_CONTROL_LOCALHOST = 0
+
+# File to Offer
+FILE_DATA = "That works!"
+FILE_SIZE = len(FILE_DATA)
+FILE_NAME = 'My test.txt'
+FILE_CONTENT_TYPE = 'text/plain'
+FILE_DESCRIPTION = 'A nice file to test'
+FILE_HASH_TYPE = FILE_HASH_TYPE_MD5
+m = md5.new()
+m.update(FILE_DATA)
+FILE_HASH = m.hexdigest()
+
+# TODO: There is lot of duplicated code between FT tests. Would be good to
+# refactor them.
+
+def test(q, bus, conn):
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
+ basic_txt = { "txtvers": "1", "status": "avail" }
+
+ self_handle = conn.GetSelfHandle()
+ self_handle_name = conn.InspectHandles(HT_CONTACT, [self_handle])[0]
+
+ contact_name = "test-file-receiver@" + get_host_name()
+ listener, port = setup_stream_listener(q, contact_name)
+
+ AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt)
+
+ publish_handle = conn.RequestHandles(HT_CONTACT_LIST, ["publish"])[0]
+ publish = conn.RequestChannel(
+ "org.freedesktop.Telepathy.Channel.Type.ContactList",
+ HT_CONTACT_LIST, publish_handle, False)
+
+ handle = 0
+ # Wait until the record shows up in publish
+ while handle == 0:
+ e = q.expect('dbus-signal', signal='MembersChanged', path=publish)
+ for h in e.args[1]:
+ name = conn.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact_name:
+ handle = h
+
+ requests_iface = dbus.Interface(conn, CONNECTION_INTERFACE_REQUESTS)
+
+ # check if we can request FileTransfer channels
+ properties = conn.GetAll(
+ 'org.freedesktop.Telepathy.Connection.Interface.Requests',
+ dbus_interface='org.freedesktop.DBus.Properties')
+
+ assert ({CHANNEL_INTERFACE + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
+ CHANNEL_INTERFACE + '.TargetHandleType': HT_CONTACT},
+ [CHANNEL_INTERFACE + '.TargetHandle',
+ CHANNEL_INTERFACE + '.TargetID',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentType',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Filename',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Size',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Description',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Date',
+ CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'],
+ ) in properties.get('RequestableChannelClasses'),\
+ properties['RequestableChannelClasses']
+
+ path, props = requests_iface.CreateChannel({
+ CHANNEL_INTERFACE + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
+ CHANNEL_INTERFACE + '.TargetHandleType': HT_CONTACT,
+ CHANNEL_INTERFACE + '.TargetHandle': handle,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentType': FILE_CONTENT_TYPE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Filename': FILE_NAME,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Size': FILE_SIZE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType': FILE_HASH_TYPE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash': FILE_HASH,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Description': FILE_DESCRIPTION,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Date': 1225278834,
+ CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset': 0,
+ })
+
+ # org.freedesktop.Telepathy.Channel D-Bus properties
+ assert props[CHANNEL_INTERFACE + '.ChannelType'] == CHANNEL_TYPE_FILE_TRANSFER
+ assert props[CHANNEL_INTERFACE + '.Interfaces'] == []
+ assert props[CHANNEL_INTERFACE + '.TargetHandle'] == handle
+ assert props[CHANNEL_INTERFACE + '.TargetID'] == contact_name
+ assert props[CHANNEL_INTERFACE + '.TargetHandleType'] == HT_CONTACT
+ assert props[CHANNEL_INTERFACE + '.Requested'] == True
+ assert props[CHANNEL_INTERFACE + '.InitiatorHandle'] == self_handle
+ assert props[CHANNEL_INTERFACE + '.InitiatorID'] == self_handle_name
+
+ # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.State'] == FT_STATE_PENDING
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentType'] == FILE_CONTENT_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Filename'] == FILE_NAME
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Size'] == FILE_SIZE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType'] == FILE_HASH_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash'] == FILE_HASH
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Description'] == FILE_DESCRIPTION
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Date'] == 1225278834
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.AvailableSocketTypes'] == \
+ {SOCKET_ADDRESS_TYPE_UNIX: [SOCKET_ACCESS_CONTROL_LOCALHOST]}
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.TransferredBytes'] == 0
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'] == 0
+
+ channel = make_channel_proxy(conn, path, 'Channel')
+ ft_channel = make_channel_proxy(conn, path, 'Channel.Type.FileTransfer.DRAFT')
+ ft_props = dbus.Interface(bus.get_object(conn.object.bus_name, path), PROPERTIES_IFACE)
+
+ conn_event, iq_event = q.expect_many(
+ EventPattern('incoming-connection', listener = listener),
+ EventPattern('stream-iq'))
+
+ incoming = conn_event.connection
+
+ assert iq_event.iq_type == 'set'
+ assert iq_event.connection == incoming
+ iq = iq_event.stanza
+ assert iq['to'] == contact_name
+ query = iq.firstChildElement()
+ assert query.uri == 'jabber:iq:oob'
+ url_node = xpath.queryForNodes("/iq/query/url", iq)[0]
+ assert url_node['type'] == 'file'
+ assert url_node['size'] == str(FILE_SIZE)
+ assert url_node['mimeType'] == FILE_CONTENT_TYPE
+ url = url_node.children[0]
+ _, host, file, _, _, _ = urlparse.urlparse(url)
+ urllib.unquote(file) == FILE_NAME
+ desc_node = xpath.queryForNodes("/iq/query/desc", iq)[0]
+ desc = desc_node.children[0]
+ assert desc == FILE_DESCRIPTION
+
+ address = ft_channel.ProvideFile(SOCKET_ADDRESS_TYPE_UNIX, SOCKET_ACCESS_CONTROL_LOCALHOST, "")
+
+ # state is still Pending as remote didn't accept the transfer yet
+ state = ft_props.Get(CHANNEL_TYPE_FILE_TRANSFER, 'State')
+ assert state == FT_STATE_PENDING
+
+ # regression test: Salut used to crash at this point
+ conn.Disconnect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[2L, 1L])
+
+if __name__ == '__main__':
+ exec_test(test)
diff --git a/tests/twisted/avahi/test-send-file-declined.py b/tests/twisted/avahi/test-send-file-declined.py
new file mode 100644
index 00000000..6b7ca9ca
--- /dev/null
+++ b/tests/twisted/avahi/test-send-file-declined.py
@@ -0,0 +1,202 @@
+import httplib
+import urlparse
+import dbus
+import socket
+import md5
+
+from saluttest import exec_test
+from avahitest import AvahiAnnouncer
+from avahitest import get_host_name
+
+from xmppstream import setup_stream_listener
+from servicetest import make_channel_proxy, EventPattern
+
+from twisted.words.xish import domish, xpath
+
+from dbus import PROPERTIES_IFACE
+
+CONNECTION_INTERFACE_REQUESTS = 'org.freedesktop.Telepathy.Connection.Interface.Requests'
+CHANNEL_INTERFACE ='org.freedesktop.Telepathy.Channel'
+CHANNEL_TYPE_FILE_TRANSFER = 'org.freedesktop.Telepathy.Channel.Type.FileTransfer.DRAFT'
+
+HT_CONTACT = 1
+HT_CONTACT_LIST = 3
+TEXT_MESSAGE_TYPE_NORMAL = dbus.UInt32(0)
+
+FT_STATE_NONE = 0
+FT_STATE_PENDING = 1
+FT_STATE_ACCEPTED = 2
+FT_STATE_OPEN = 3
+FT_STATE_COMPLETED = 4
+FT_STATE_CANCELLED = 5
+
+FT_STATE_CHANGE_REASON_NONE = 0
+FT_STATE_CHANGE_REASON_REQUESTED = 1
+FT_STATE_CHANGE_REASON_LOCAL_STOPPED = 2
+FT_STATE_CHANGE_REASON_REMOTE_STOPPED = 3
+FT_STATE_CHANGE_REASON_LOCAL_ERROR = 4
+FT_STATE_CHANGE_REASON_REMOTE_ERROR = 5
+
+FILE_HASH_TYPE_MD5 = 1
+
+SOCKET_ADDRESS_TYPE_UNIX = 0
+SOCKET_ADDRESS_TYPE_IPV4 = 2
+
+SOCKET_ACCESS_CONTROL_LOCALHOST = 0
+
+# File to Offer
+FILE_DATA = "That works!"
+FILE_SIZE = len(FILE_DATA)
+FILE_NAME = 'test.txt'
+FILE_CONTENT_TYPE = 'text/plain'
+FILE_DESCRIPTION = 'A nice file to test'
+FILE_HASH_TYPE = FILE_HASH_TYPE_MD5
+m = md5.new()
+m.update(FILE_DATA)
+FILE_HASH = m.hexdigest()
+
+def test(q, bus, conn):
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
+ basic_txt = { "txtvers": "1", "status": "avail" }
+
+ self_handle = conn.GetSelfHandle()
+ self_handle_name = conn.InspectHandles(HT_CONTACT, [self_handle])[0]
+
+ contact_name = "test-file-receiver@" + get_host_name()
+ listener, port = setup_stream_listener(q, contact_name)
+
+ AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt)
+
+ publish_handle = conn.RequestHandles(HT_CONTACT_LIST, ["publish"])[0]
+ publish = conn.RequestChannel(
+ "org.freedesktop.Telepathy.Channel.Type.ContactList",
+ HT_CONTACT_LIST, publish_handle, False)
+
+ handle = 0
+ # Wait until the record shows up in publish
+ while handle == 0:
+ e = q.expect('dbus-signal', signal='MembersChanged', path=publish)
+ for h in e.args[1]:
+ name = conn.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact_name:
+ handle = h
+
+ requests_iface = dbus.Interface(conn, CONNECTION_INTERFACE_REQUESTS)
+
+ # check if we can request FileTransfer channels
+ properties = conn.GetAll(
+ 'org.freedesktop.Telepathy.Connection.Interface.Requests',
+ dbus_interface='org.freedesktop.DBus.Properties')
+
+ assert ({CHANNEL_INTERFACE + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
+ CHANNEL_INTERFACE + '.TargetHandleType': HT_CONTACT},
+ [CHANNEL_INTERFACE + '.TargetHandle',
+ CHANNEL_INTERFACE + '.TargetID',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentType',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Filename',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Size',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Description',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Date',
+ CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'],
+ ) in properties.get('RequestableChannelClasses'),\
+ properties['RequestableChannelClasses']
+
+ path, props = requests_iface.CreateChannel({
+ CHANNEL_INTERFACE + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
+ CHANNEL_INTERFACE + '.TargetHandleType': HT_CONTACT,
+ CHANNEL_INTERFACE + '.TargetHandle': handle,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentType': FILE_CONTENT_TYPE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Filename': FILE_NAME,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Size': FILE_SIZE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType': FILE_HASH_TYPE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash': FILE_HASH,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Description': FILE_DESCRIPTION,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Date': 1225278834,
+ CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset': 0,
+ })
+
+ # org.freedesktop.Telepathy.Channel D-Bus properties
+ assert props[CHANNEL_INTERFACE + '.ChannelType'] == CHANNEL_TYPE_FILE_TRANSFER
+ assert props[CHANNEL_INTERFACE + '.Interfaces'] == []
+ assert props[CHANNEL_INTERFACE + '.TargetHandle'] == handle
+ assert props[CHANNEL_INTERFACE + '.TargetID'] == contact_name
+ assert props[CHANNEL_INTERFACE + '.TargetHandleType'] == HT_CONTACT
+ assert props[CHANNEL_INTERFACE + '.Requested'] == True
+ assert props[CHANNEL_INTERFACE + '.InitiatorHandle'] == self_handle
+ assert props[CHANNEL_INTERFACE + '.InitiatorID'] == self_handle_name
+
+ # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.State'] == FT_STATE_PENDING
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentType'] == FILE_CONTENT_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Filename'] == FILE_NAME
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Size'] == FILE_SIZE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType'] == FILE_HASH_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash'] == FILE_HASH
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Description'] == FILE_DESCRIPTION
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Date'] == 1225278834
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.AvailableSocketTypes'] == \
+ {SOCKET_ADDRESS_TYPE_UNIX: [SOCKET_ACCESS_CONTROL_LOCALHOST]}
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.TransferredBytes'] == 0
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'] == 0
+
+ channel = make_channel_proxy(conn, path, 'Channel')
+ ft_channel = make_channel_proxy(conn, path, 'Channel.Type.FileTransfer.DRAFT')
+ ft_props = dbus.Interface(bus.get_object(conn.object.bus_name, path), PROPERTIES_IFACE)
+
+ address = ft_channel.ProvideFile(SOCKET_ADDRESS_TYPE_UNIX, SOCKET_ACCESS_CONTROL_LOCALHOST, "")
+
+ conn_event, iq_event = q.expect_many(
+ EventPattern('incoming-connection', listener = listener),
+ EventPattern('stream-iq'))
+
+ incoming = conn_event.connection
+
+ assert iq_event.iq_type == 'set'
+ assert iq_event.connection == incoming
+ iq = iq_event.stanza
+ assert iq['to'] == contact_name
+ query = iq.firstChildElement()
+ assert query.uri == 'jabber:iq:oob'
+ url_node = xpath.queryForNodes("/iq/query/url", iq)[0]
+ assert url_node['type'] == 'file'
+ assert url_node['size'] == str(FILE_SIZE)
+ assert url_node['mimeType'] == FILE_CONTENT_TYPE
+ url = url_node.children[0]
+ assert url.endswith(FILE_NAME)
+ desc_node = xpath.queryForNodes("/iq/query/desc", iq)[0]
+ desc = desc_node.children[0]
+ assert desc == FILE_DESCRIPTION
+
+ # Receiver declines the file offer
+ reply = domish.Element(('', 'iq'))
+ reply['to'] = iq['from']
+ reply['from'] = iq['to']
+ reply['type'] = 'error'
+ reply['id'] = iq['id']
+ query = reply.addElement(('jabber:iq:oob', 'query'))
+ url_node = query.addElement('url', content=url)
+ query.addElement('desc', content=desc)
+ error_node = reply.addElement((None, 'error'))
+ error_node['code'] = '406'
+ error_node['type'] = 'modify'
+ not_acceptable_node = error_node.addElement(('urn:ietf:params:xml:ns:xmpp-stanzas',
+ 'not-acceptable'))
+ incoming.send(reply)
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_CANCELLED, state
+ assert reason == FT_STATE_CHANGE_REASON_REMOTE_STOPPED
+
+ transferred = ft_props.Get(CHANNEL_TYPE_FILE_TRANSFER, 'TransferredBytes')
+ # no byte has been transferred as the file was declined
+ assert transferred == 0
+
+ channel.Close()
+ q.expect('dbus-signal', signal='Closed')
+
+if __name__ == '__main__':
+ exec_test(test)
diff --git a/tests/twisted/avahi/test-send-file-item-not-found.py b/tests/twisted/avahi/test-send-file-item-not-found.py
new file mode 100644
index 00000000..095dd5bd
--- /dev/null
+++ b/tests/twisted/avahi/test-send-file-item-not-found.py
@@ -0,0 +1,202 @@
+import httplib
+import urlparse
+import dbus
+import socket
+import md5
+
+from saluttest import exec_test
+from avahitest import AvahiAnnouncer
+from avahitest import get_host_name
+
+from xmppstream import setup_stream_listener
+from servicetest import make_channel_proxy, EventPattern
+
+from twisted.words.xish import domish, xpath
+
+from dbus import PROPERTIES_IFACE
+
+CONNECTION_INTERFACE_REQUESTS = 'org.freedesktop.Telepathy.Connection.Interface.Requests'
+CHANNEL_INTERFACE ='org.freedesktop.Telepathy.Channel'
+CHANNEL_TYPE_FILE_TRANSFER = 'org.freedesktop.Telepathy.Channel.Type.FileTransfer.DRAFT'
+
+HT_CONTACT = 1
+HT_CONTACT_LIST = 3
+TEXT_MESSAGE_TYPE_NORMAL = dbus.UInt32(0)
+
+FT_STATE_NONE = 0
+FT_STATE_PENDING = 1
+FT_STATE_ACCEPTED = 2
+FT_STATE_OPEN = 3
+FT_STATE_COMPLETED = 4
+FT_STATE_CANCELLED = 5
+
+FT_STATE_CHANGE_REASON_NONE = 0
+FT_STATE_CHANGE_REASON_REQUESTED = 1
+FT_STATE_CHANGE_REASON_LOCAL_STOPPED = 2
+FT_STATE_CHANGE_REASON_REMOTE_STOPPED = 3
+FT_STATE_CHANGE_REASON_LOCAL_ERROR = 4
+FT_STATE_CHANGE_REASON_REMOTE_ERROR = 5
+
+FILE_HASH_TYPE_MD5 = 1
+
+SOCKET_ADDRESS_TYPE_UNIX = 0
+SOCKET_ADDRESS_TYPE_IPV4 = 2
+
+SOCKET_ACCESS_CONTROL_LOCALHOST = 0
+
+# File to Offer
+FILE_DATA = "That works!"
+FILE_SIZE = len(FILE_DATA)
+FILE_NAME = 'test.txt'
+FILE_CONTENT_TYPE = 'text/plain'
+FILE_DESCRIPTION = 'A nice file to test'
+FILE_HASH_TYPE = FILE_HASH_TYPE_MD5
+m = md5.new()
+m.update(FILE_DATA)
+FILE_HASH = m.hexdigest()
+
+def test(q, bus, conn):
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
+ basic_txt = { "txtvers": "1", "status": "avail" }
+
+ self_handle = conn.GetSelfHandle()
+ self_handle_name = conn.InspectHandles(HT_CONTACT, [self_handle])[0]
+
+ contact_name = "test-file-receiver@" + get_host_name()
+ listener, port = setup_stream_listener(q, contact_name)
+
+ AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt)
+
+ publish_handle = conn.RequestHandles(HT_CONTACT_LIST, ["publish"])[0]
+ publish = conn.RequestChannel(
+ "org.freedesktop.Telepathy.Channel.Type.ContactList",
+ HT_CONTACT_LIST, publish_handle, False)
+
+ handle = 0
+ # Wait until the record shows up in publish
+ while handle == 0:
+ e = q.expect('dbus-signal', signal='MembersChanged', path=publish)
+ for h in e.args[1]:
+ name = conn.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact_name:
+ handle = h
+
+ requests_iface = dbus.Interface(conn, CONNECTION_INTERFACE_REQUESTS)
+
+ # check if we can request FileTransfer channels
+ properties = conn.GetAll(
+ 'org.freedesktop.Telepathy.Connection.Interface.Requests',
+ dbus_interface='org.freedesktop.DBus.Properties')
+
+ assert ({CHANNEL_INTERFACE + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
+ CHANNEL_INTERFACE + '.TargetHandleType': HT_CONTACT},
+ [CHANNEL_INTERFACE + '.TargetHandle',
+ CHANNEL_INTERFACE + '.TargetID',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentType',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Filename',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Size',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Description',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Date',
+ CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'],
+ ) in properties.get('RequestableChannelClasses'),\
+ properties['RequestableChannelClasses']
+
+ path, props = requests_iface.CreateChannel({
+ CHANNEL_INTERFACE + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
+ CHANNEL_INTERFACE + '.TargetHandleType': HT_CONTACT,
+ CHANNEL_INTERFACE + '.TargetHandle': handle,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentType': FILE_CONTENT_TYPE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Filename': FILE_NAME,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Size': FILE_SIZE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType': FILE_HASH_TYPE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash': FILE_HASH,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Description': FILE_DESCRIPTION,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Date': 1225278834,
+ CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset': 0,
+ })
+
+ # org.freedesktop.Telepathy.Channel D-Bus properties
+ assert props[CHANNEL_INTERFACE + '.ChannelType'] == CHANNEL_TYPE_FILE_TRANSFER
+ assert props[CHANNEL_INTERFACE + '.Interfaces'] == []
+ assert props[CHANNEL_INTERFACE + '.TargetHandle'] == handle
+ assert props[CHANNEL_INTERFACE + '.TargetID'] == contact_name
+ assert props[CHANNEL_INTERFACE + '.TargetHandleType'] == HT_CONTACT
+ assert props[CHANNEL_INTERFACE + '.Requested'] == True
+ assert props[CHANNEL_INTERFACE + '.InitiatorHandle'] == self_handle
+ assert props[CHANNEL_INTERFACE + '.InitiatorID'] == self_handle_name
+
+ # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.State'] == FT_STATE_PENDING
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentType'] == FILE_CONTENT_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Filename'] == FILE_NAME
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Size'] == FILE_SIZE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType'] == FILE_HASH_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash'] == FILE_HASH
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Description'] == FILE_DESCRIPTION
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Date'] == 1225278834
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.AvailableSocketTypes'] == \
+ {SOCKET_ADDRESS_TYPE_UNIX: [SOCKET_ACCESS_CONTROL_LOCALHOST]}
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.TransferredBytes'] == 0
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'] == 0
+
+ channel = make_channel_proxy(conn, path, 'Channel')
+ ft_channel = make_channel_proxy(conn, path, 'Channel.Type.FileTransfer.DRAFT')
+ ft_props = dbus.Interface(bus.get_object(conn.object.bus_name, path), PROPERTIES_IFACE)
+
+ address = ft_channel.ProvideFile(SOCKET_ADDRESS_TYPE_UNIX, SOCKET_ACCESS_CONTROL_LOCALHOST, "")
+
+ conn_event, iq_event = q.expect_many(
+ EventPattern('incoming-connection', listener = listener),
+ EventPattern('stream-iq'))
+
+ incoming = conn_event.connection
+
+ assert iq_event.iq_type == 'set'
+ assert iq_event.connection == incoming
+ iq = iq_event.stanza
+ assert iq['to'] == contact_name
+ query = iq.firstChildElement()
+ assert query.uri == 'jabber:iq:oob'
+ url_node = xpath.queryForNodes("/iq/query/url", iq)[0]
+ assert url_node['type'] == 'file'
+ assert url_node['size'] == str(FILE_SIZE)
+ assert url_node['mimeType'] == FILE_CONTENT_TYPE
+ url = url_node.children[0]
+ assert url.endswith(FILE_NAME)
+ desc_node = xpath.queryForNodes("/iq/query/desc", iq)[0]
+ desc = desc_node.children[0]
+ assert desc == FILE_DESCRIPTION
+
+ # Receiver can't retrieve the file
+ reply = domish.Element(('', 'iq'))
+ reply['to'] = iq['from']
+ reply['from'] = iq['to']
+ reply['type'] = 'error'
+ reply['id'] = iq['id']
+ query = reply.addElement(('jabber:iq:oob', 'query'))
+ url_node = query.addElement('url', content=url)
+ query.addElement('desc', content=desc)
+ error_node = reply.addElement((None, 'error'))
+ error_node['code'] = '404'
+ error_node['type'] = 'modify'
+ error_node.addElement(('urn:ietf:params:xml:ns:xmpp-stanzas',
+ 'item-not-found'))
+ incoming.send(reply)
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_CANCELLED, state
+ assert reason == FT_STATE_CHANGE_REASON_REMOTE_ERROR
+
+ transferred = ft_props.Get(CHANNEL_TYPE_FILE_TRANSFER, 'TransferredBytes')
+ # no byte has been transferred as the transfer failed
+ assert transferred == 0
+
+ channel.Close()
+ q.expect('dbus-signal', signal='Closed')
+
+if __name__ == '__main__':
+ exec_test(test)
diff --git a/tests/twisted/avahi/test-send-file-provide-immediately.py b/tests/twisted/avahi/test-send-file-provide-immediately.py
new file mode 100644
index 00000000..73f1fda5
--- /dev/null
+++ b/tests/twisted/avahi/test-send-file-provide-immediately.py
@@ -0,0 +1,228 @@
+import httplib
+import urlparse
+import dbus
+import socket
+import md5
+import urllib
+
+from saluttest import exec_test
+from avahitest import AvahiAnnouncer
+from avahitest import get_host_name
+
+from xmppstream import setup_stream_listener
+from servicetest import make_channel_proxy, EventPattern
+
+from twisted.words.xish import domish, xpath
+
+from dbus import PROPERTIES_IFACE
+
+CONNECTION_INTERFACE_REQUESTS = 'org.freedesktop.Telepathy.Connection.Interface.Requests'
+CHANNEL_INTERFACE ='org.freedesktop.Telepathy.Channel'
+CHANNEL_TYPE_FILE_TRANSFER = 'org.freedesktop.Telepathy.Channel.Type.FileTransfer.DRAFT'
+
+HT_CONTACT = 1
+HT_CONTACT_LIST = 3
+TEXT_MESSAGE_TYPE_NORMAL = dbus.UInt32(0)
+
+FT_STATE_NONE = 0
+FT_STATE_PENDING = 1
+FT_STATE_ACCEPTED = 2
+FT_STATE_OPEN = 3
+FT_STATE_COMPLETED = 4
+FT_STATE_CANCELLED = 5
+
+FT_STATE_CHANGE_REASON_NONE = 0
+FT_STATE_CHANGE_REASON_REQUESTED = 1
+
+FILE_HASH_TYPE_MD5 = 1
+
+SOCKET_ADDRESS_TYPE_UNIX = 0
+SOCKET_ADDRESS_TYPE_IPV4 = 2
+
+SOCKET_ACCESS_CONTROL_LOCALHOST = 0
+
+# File to Offer
+FILE_DATA = "That works!"
+FILE_SIZE = len(FILE_DATA)
+FILE_NAME = 'My test.txt'
+FILE_CONTENT_TYPE = 'text/plain'
+FILE_DESCRIPTION = 'A nice file to test'
+FILE_HASH_TYPE = FILE_HASH_TYPE_MD5
+m = md5.new()
+m.update(FILE_DATA)
+FILE_HASH = m.hexdigest()
+
+# TODO: There is lot of duplicated code between FT tests. Would be good to
+# refactor them.
+
+def test(q, bus, conn):
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
+ basic_txt = { "txtvers": "1", "status": "avail" }
+
+ self_handle = conn.GetSelfHandle()
+ self_handle_name = conn.InspectHandles(HT_CONTACT, [self_handle])[0]
+
+ contact_name = "test-file-receiver@" + get_host_name()
+ listener, port = setup_stream_listener(q, contact_name)
+
+ AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt)
+
+ publish_handle = conn.RequestHandles(HT_CONTACT_LIST, ["publish"])[0]
+ publish = conn.RequestChannel(
+ "org.freedesktop.Telepathy.Channel.Type.ContactList",
+ HT_CONTACT_LIST, publish_handle, False)
+
+ handle = 0
+ # Wait until the record shows up in publish
+ while handle == 0:
+ e = q.expect('dbus-signal', signal='MembersChanged', path=publish)
+ for h in e.args[1]:
+ name = conn.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact_name:
+ handle = h
+
+ requests_iface = dbus.Interface(conn, CONNECTION_INTERFACE_REQUESTS)
+
+ # check if we can request FileTransfer channels
+ properties = conn.GetAll(
+ 'org.freedesktop.Telepathy.Connection.Interface.Requests',
+ dbus_interface='org.freedesktop.DBus.Properties')
+
+ assert ({CHANNEL_INTERFACE + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
+ CHANNEL_INTERFACE + '.TargetHandleType': HT_CONTACT},
+ [CHANNEL_INTERFACE + '.TargetHandle',
+ CHANNEL_INTERFACE + '.TargetID',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentType',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Filename',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Size',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Description',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Date',
+ CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'],
+ ) in properties.get('RequestableChannelClasses'),\
+ properties['RequestableChannelClasses']
+
+ path, props = requests_iface.CreateChannel({
+ CHANNEL_INTERFACE + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
+ CHANNEL_INTERFACE + '.TargetHandleType': HT_CONTACT,
+ CHANNEL_INTERFACE + '.TargetHandle': handle,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentType': FILE_CONTENT_TYPE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Filename': FILE_NAME,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Size': FILE_SIZE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType': FILE_HASH_TYPE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash': FILE_HASH,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Description': FILE_DESCRIPTION,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Date': 1225278834,
+ CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset': 0,
+ })
+
+ # org.freedesktop.Telepathy.Channel D-Bus properties
+ assert props[CHANNEL_INTERFACE + '.ChannelType'] == CHANNEL_TYPE_FILE_TRANSFER
+ assert props[CHANNEL_INTERFACE + '.Interfaces'] == []
+ assert props[CHANNEL_INTERFACE + '.TargetHandle'] == handle
+ assert props[CHANNEL_INTERFACE + '.TargetID'] == contact_name
+ assert props[CHANNEL_INTERFACE + '.TargetHandleType'] == HT_CONTACT
+ assert props[CHANNEL_INTERFACE + '.Requested'] == True
+ assert props[CHANNEL_INTERFACE + '.InitiatorHandle'] == self_handle
+ assert props[CHANNEL_INTERFACE + '.InitiatorID'] == self_handle_name
+
+ # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.State'] == FT_STATE_PENDING
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentType'] == FILE_CONTENT_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Filename'] == FILE_NAME
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Size'] == FILE_SIZE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType'] == FILE_HASH_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash'] == FILE_HASH
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Description'] == FILE_DESCRIPTION
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Date'] == 1225278834
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.AvailableSocketTypes'] == \
+ {SOCKET_ADDRESS_TYPE_UNIX: [SOCKET_ACCESS_CONTROL_LOCALHOST]}
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.TransferredBytes'] == 0
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'] == 0
+
+ channel = make_channel_proxy(conn, path, 'Channel')
+ ft_channel = make_channel_proxy(conn, path, 'Channel.Type.FileTransfer.DRAFT')
+ ft_props = dbus.Interface(bus.get_object(conn.object.bus_name, path), PROPERTIES_IFACE)
+
+ conn_event, iq_event = q.expect_many(
+ EventPattern('incoming-connection', listener = listener),
+ EventPattern('stream-iq'))
+
+ incoming = conn_event.connection
+
+ assert iq_event.iq_type == 'set'
+ assert iq_event.connection == incoming
+ iq = iq_event.stanza
+ assert iq['to'] == contact_name
+ query = iq.firstChildElement()
+ assert query.uri == 'jabber:iq:oob'
+ url_node = xpath.queryForNodes("/iq/query/url", iq)[0]
+ assert url_node['type'] == 'file'
+ assert url_node['size'] == str(FILE_SIZE)
+ assert url_node['mimeType'] == FILE_CONTENT_TYPE
+ url = url_node.children[0]
+ _, host, file, _, _, _ = urlparse.urlparse(url)
+ urllib.unquote(file) == FILE_NAME
+ desc_node = xpath.queryForNodes("/iq/query/desc", iq)[0]
+ desc = desc_node.children[0]
+ assert desc == FILE_DESCRIPTION
+
+ address = ft_channel.ProvideFile(SOCKET_ADDRESS_TYPE_UNIX, SOCKET_ACCESS_CONTROL_LOCALHOST, "")
+
+ # state is still Pending as remote didn't accept the transfer yet
+ state = ft_props.Get(CHANNEL_TYPE_FILE_TRANSFER, 'State')
+ assert state == FT_STATE_PENDING
+
+ # Connect HTTP client to the CM and request the file
+ http = httplib.HTTPConnection(host)
+ http.request('GET', file)
+
+ e = q.expect('dbus-signal', signal='InitialOffsetDefined')
+ offset = e.args[0]
+ # We don't support resume
+ assert offset == 0
+
+ # Channel is open. We can start to send the file
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_OPEN
+ assert reason == FT_STATE_CHANGE_REASON_NONE
+
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ s.connect(address)
+ s.send(FILE_DATA)
+
+ e = q.expect('dbus-signal', signal='TransferredBytesChanged')
+
+ count = e.args[0]
+ while count < FILE_SIZE:
+ # Catch TransferredBytesChanged until we transfered all the data
+ e = q.expect('dbus-signal', signal='TransferredBytesChanged')
+ count = e.args[0]
+
+ response = http.getresponse()
+ assert (response.status, response.reason) == (200, 'OK')
+ data = response.read(FILE_SIZE)
+ # Did we received the right file?
+ assert data == FILE_DATA
+
+ # Inform sender that we received all the file from the OOB transfer
+ reply = domish.Element(('', 'iq'))
+ reply['to'] = iq['from']
+ reply['from'] = iq['to']
+ reply['type'] = 'result'
+ reply['id'] = iq['id']
+ incoming.send(reply)
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_COMPLETED
+ assert reason == FT_STATE_CHANGE_REASON_NONE
+
+ channel.Close()
+ q.expect('dbus-signal', signal='Closed')
+
+if __name__ == '__main__':
+ exec_test(test)
diff --git a/tests/twisted/avahi/test-send-file-to-unknown-contact.py b/tests/twisted/avahi/test-send-file-to-unknown-contact.py
new file mode 100644
index 00000000..0f1c8f77
--- /dev/null
+++ b/tests/twisted/avahi/test-send-file-to-unknown-contact.py
@@ -0,0 +1,101 @@
+import httplib
+import urlparse
+import dbus
+import socket
+import md5
+
+from saluttest import exec_test
+from avahitest import AvahiAnnouncer
+from avahitest import get_host_name
+
+from xmppstream import setup_stream_listener
+from servicetest import make_channel_proxy, EventPattern, call_async
+
+from twisted.words.xish import domish, xpath
+
+from dbus import PROPERTIES_IFACE
+
+CONNECTION_INTERFACE_REQUESTS = 'org.freedesktop.Telepathy.Connection.Interface.Requests'
+CHANNEL_INTERFACE ='org.freedesktop.Telepathy.Channel'
+CHANNEL_TYPE_FILE_TRANSFER = 'org.freedesktop.Telepathy.Channel.Type.FileTransfer.DRAFT'
+
+HT_CONTACT = 1
+HT_CONTACT_LIST = 3
+TEXT_MESSAGE_TYPE_NORMAL = dbus.UInt32(0)
+
+FT_STATE_NONE = 0
+FT_STATE_PENDING = 1
+FT_STATE_ACCEPTED = 2
+FT_STATE_OPEN = 3
+FT_STATE_COMPLETED = 4
+FT_STATE_CANCELLED = 5
+
+FT_STATE_CHANGE_REASON_NONE = 0
+FT_STATE_CHANGE_REASON_REQUESTED = 1
+
+FILE_HASH_TYPE_MD5 = 1
+
+SOCKET_ADDRESS_TYPE_UNIX = 0
+SOCKET_ADDRESS_TYPE_IPV4 = 2
+
+SOCKET_ACCESS_CONTROL_LOCALHOST = 0
+
+# File to Offer
+FILE_DATA = "That works!"
+FILE_SIZE = len(FILE_DATA)
+FILE_NAME = 'test.txt'
+FILE_CONTENT_TYPE = 'text/plain'
+FILE_DESCRIPTION = 'A nice file to test'
+FILE_HASH_TYPE = FILE_HASH_TYPE_MD5
+m = md5.new()
+m.update(FILE_DATA)
+FILE_HASH = m.hexdigest()
+
+def test(q, bus, conn):
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
+
+ contact_name = "test-file-receiver@" + get_host_name()
+ handle = conn.RequestHandles(HT_CONTACT, [contact_name])[0]
+
+ requests_iface = dbus.Interface(conn, CONNECTION_INTERFACE_REQUESTS)
+
+ # check if we can request FileTransfer channels
+ properties = conn.GetAll(
+ 'org.freedesktop.Telepathy.Connection.Interface.Requests',
+ dbus_interface='org.freedesktop.DBus.Properties')
+
+ assert ({CHANNEL_INTERFACE + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
+ CHANNEL_INTERFACE + '.TargetHandleType': HT_CONTACT},
+ [CHANNEL_INTERFACE + '.TargetHandle',
+ CHANNEL_INTERFACE + '.TargetID',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentType',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Filename',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Size',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Description',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Date',
+ CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'],
+ ) in properties.get('RequestableChannelClasses'),\
+ properties['RequestableChannelClasses']
+
+ call_async(q, requests_iface, 'CreateChannel',
+ {CHANNEL_INTERFACE + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
+ CHANNEL_INTERFACE + '.TargetHandleType': HT_CONTACT,
+ CHANNEL_INTERFACE + '.TargetHandle': handle,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentType': FILE_CONTENT_TYPE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Filename': FILE_NAME,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Size': FILE_SIZE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType': FILE_HASH_TYPE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash': FILE_HASH,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Description': FILE_DESCRIPTION,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Date': 1225278834,
+ CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset': 0,
+ })
+
+ e = q.expect('dbus-error')
+ assert e.error.get_dbus_name() == 'org.freedesktop.Telepathy.Errors.NotAvailable'
+
+if __name__ == '__main__':
+ exec_test(test)
diff --git a/tests/twisted/avahi/test-send-file-wait-to-provide.py b/tests/twisted/avahi/test-send-file-wait-to-provide.py
new file mode 100644
index 00000000..2c7b521b
--- /dev/null
+++ b/tests/twisted/avahi/test-send-file-wait-to-provide.py
@@ -0,0 +1,235 @@
+import httplib
+import urlparse
+import dbus
+import socket
+import md5
+import urllib
+
+from saluttest import exec_test
+from avahitest import AvahiAnnouncer
+from avahitest import get_host_name
+
+from xmppstream import setup_stream_listener
+from servicetest import make_channel_proxy, EventPattern
+
+from twisted.words.xish import domish, xpath
+
+from dbus import PROPERTIES_IFACE
+
+CONNECTION_INTERFACE_REQUESTS = 'org.freedesktop.Telepathy.Connection.Interface.Requests'
+CHANNEL_INTERFACE ='org.freedesktop.Telepathy.Channel'
+CHANNEL_TYPE_FILE_TRANSFER = 'org.freedesktop.Telepathy.Channel.Type.FileTransfer.DRAFT'
+
+HT_CONTACT = 1
+HT_CONTACT_LIST = 3
+TEXT_MESSAGE_TYPE_NORMAL = dbus.UInt32(0)
+
+FT_STATE_NONE = 0
+FT_STATE_PENDING = 1
+FT_STATE_ACCEPTED = 2
+FT_STATE_OPEN = 3
+FT_STATE_COMPLETED = 4
+FT_STATE_CANCELLED = 5
+
+FT_STATE_CHANGE_REASON_NONE = 0
+FT_STATE_CHANGE_REASON_REQUESTED = 1
+
+FILE_HASH_TYPE_MD5 = 1
+
+SOCKET_ADDRESS_TYPE_UNIX = 0
+SOCKET_ADDRESS_TYPE_IPV4 = 2
+
+SOCKET_ACCESS_CONTROL_LOCALHOST = 0
+
+# File to Offer
+FILE_DATA = "That works!"
+FILE_SIZE = len(FILE_DATA)
+FILE_NAME = 'test.txt'
+FILE_CONTENT_TYPE = 'text/plain'
+FILE_DESCRIPTION = 'A nice file to test'
+FILE_HASH_TYPE = FILE_HASH_TYPE_MD5
+m = md5.new()
+m.update(FILE_DATA)
+FILE_HASH = m.hexdigest()
+
+def test(q, bus, conn):
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
+ basic_txt = { "txtvers": "1", "status": "avail" }
+
+ self_handle = conn.GetSelfHandle()
+ self_handle_name = conn.InspectHandles(HT_CONTACT, [self_handle])[0]
+
+ contact_name = "test-file-receiver@" + get_host_name()
+ listener, port = setup_stream_listener(q, contact_name)
+
+ AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt)
+
+ publish_handle = conn.RequestHandles(HT_CONTACT_LIST, ["publish"])[0]
+ publish = conn.RequestChannel(
+ "org.freedesktop.Telepathy.Channel.Type.ContactList",
+ HT_CONTACT_LIST, publish_handle, False)
+
+ handle = 0
+ # Wait until the record shows up in publish
+ while handle == 0:
+ e = q.expect('dbus-signal', signal='MembersChanged', path=publish)
+ for h in e.args[1]:
+ name = conn.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact_name:
+ handle = h
+
+ requests_iface = dbus.Interface(conn, CONNECTION_INTERFACE_REQUESTS)
+
+ # check if we can request FileTransfer channels
+ properties = conn.GetAll(
+ 'org.freedesktop.Telepathy.Connection.Interface.Requests',
+ dbus_interface='org.freedesktop.DBus.Properties')
+
+ assert ({CHANNEL_INTERFACE + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
+ CHANNEL_INTERFACE + '.TargetHandleType': HT_CONTACT},
+ [CHANNEL_INTERFACE + '.TargetHandle',
+ CHANNEL_INTERFACE + '.TargetID',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentType',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Filename',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Size',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType',
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Description',
+ CHANNEL_TYPE_FILE_TRANSFER + '.Date',
+ CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'],
+ ) in properties.get('RequestableChannelClasses'),\
+ properties['RequestableChannelClasses']
+
+ path, props = requests_iface.CreateChannel({
+ CHANNEL_INTERFACE + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
+ CHANNEL_INTERFACE + '.TargetHandleType': HT_CONTACT,
+ CHANNEL_INTERFACE + '.TargetHandle': handle,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentType': FILE_CONTENT_TYPE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Filename': FILE_NAME,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Size': FILE_SIZE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType': FILE_HASH_TYPE,
+ CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash': FILE_HASH,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Description': FILE_DESCRIPTION,
+ CHANNEL_TYPE_FILE_TRANSFER + '.Date': 1225278834,
+ CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset': 0,
+ })
+
+ # org.freedesktop.Telepathy.Channel D-Bus properties
+ assert props[CHANNEL_INTERFACE + '.ChannelType'] == CHANNEL_TYPE_FILE_TRANSFER
+ assert props[CHANNEL_INTERFACE + '.Interfaces'] == []
+ assert props[CHANNEL_INTERFACE + '.TargetHandle'] == handle
+ assert props[CHANNEL_INTERFACE + '.TargetID'] == contact_name
+ assert props[CHANNEL_INTERFACE + '.TargetHandleType'] == HT_CONTACT
+ assert props[CHANNEL_INTERFACE + '.Requested'] == True
+ assert props[CHANNEL_INTERFACE + '.InitiatorHandle'] == self_handle
+ assert props[CHANNEL_INTERFACE + '.InitiatorID'] == self_handle_name
+
+ # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.State'] == FT_STATE_PENDING
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentType'] == FILE_CONTENT_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Filename'] == FILE_NAME
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Size'] == FILE_SIZE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHashType'] == FILE_HASH_TYPE
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.ContentHash'] == FILE_HASH
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Description'] == FILE_DESCRIPTION
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.Date'] == 1225278834
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.AvailableSocketTypes'] == \
+ {SOCKET_ADDRESS_TYPE_UNIX: [SOCKET_ACCESS_CONTROL_LOCALHOST]}
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.TransferredBytes'] == 0
+ assert props[CHANNEL_TYPE_FILE_TRANSFER + '.InitialOffset'] == 0
+
+ channel = make_channel_proxy(conn, path, 'Channel')
+ ft_channel = make_channel_proxy(conn, path, 'Channel.Type.FileTransfer.DRAFT')
+ ft_props = dbus.Interface(bus.get_object(conn.object.bus_name, path), PROPERTIES_IFACE)
+
+ conn_event, iq_event = q.expect_many(
+ EventPattern('incoming-connection', listener = listener),
+ EventPattern('stream-iq'))
+
+ incoming = conn_event.connection
+
+ assert iq_event.iq_type == 'set'
+ assert iq_event.connection == incoming
+ iq = iq_event.stanza
+ assert iq['to'] == contact_name
+ query = iq.firstChildElement()
+ assert query.uri == 'jabber:iq:oob'
+ url_node = xpath.queryForNodes("/iq/query/url", iq)[0]
+ assert url_node['type'] == 'file'
+ assert url_node['size'] == str(FILE_SIZE)
+ assert url_node['mimeType'] == FILE_CONTENT_TYPE
+ url = url_node.children[0]
+ _, host, file, _, _, _ = urlparse.urlparse(url)
+ urllib.unquote(file) == FILE_NAME
+ desc_node = xpath.queryForNodes("/iq/query/desc", iq)[0]
+ desc = desc_node.children[0]
+ assert desc == FILE_DESCRIPTION
+
+ # state is still Pending as remote didn't accept the transfer yet
+ state = ft_props.Get(CHANNEL_TYPE_FILE_TRANSFER, 'State')
+ assert state == FT_STATE_PENDING
+
+ # Connect HTTP client to the CM and request the file
+ http = httplib.HTTPConnection(host)
+ http.request('GET', file)
+
+ # Remote accepted the transfer
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_ACCEPTED, state
+ assert reason == FT_STATE_CHANGE_REASON_NONE
+
+ address = ft_channel.ProvideFile(SOCKET_ADDRESS_TYPE_UNIX, SOCKET_ACCESS_CONTROL_LOCALHOST, "")
+
+ e = q.expect('dbus-signal', signal='InitialOffsetDefined')
+ offset = e.args[0]
+ # We don't support resume
+ assert offset == 0
+
+ # Channel is open. We can start to send the file
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_OPEN
+ assert reason == FT_STATE_CHANGE_REASON_REQUESTED
+
+ offset = ft_props.Get(CHANNEL_TYPE_FILE_TRANSFER, 'InitialOffset')
+ # We don't support resume
+ assert offset == 0
+
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ s.connect(address)
+ s.send(FILE_DATA)
+
+ e = q.expect('dbus-signal', signal='TransferredBytesChanged')
+
+ count = e.args[0]
+ while count < FILE_SIZE:
+ # Catch TransferredBytesChanged until we transfered all the data
+ e = q.expect('dbus-signal', signal='TransferredBytesChanged')
+ count = e.args[0]
+
+ response = http.getresponse()
+ assert (response.status, response.reason) == (200, 'OK')
+ data = response.read(FILE_SIZE)
+ # Did we received the right file?
+ assert data == FILE_DATA
+
+ # Inform sender that we received all the file from the OOB transfer
+ reply = domish.Element(('', 'iq'))
+ reply['to'] = iq['from']
+ reply['from'] = iq['to']
+ reply['type'] = 'result'
+ reply['id'] = iq['id']
+ incoming.send(reply)
+
+ e = q.expect('dbus-signal', signal='FileTransferStateChanged')
+ state, reason = e.args
+ assert state == FT_STATE_COMPLETED
+ assert reason == FT_STATE_CHANGE_REASON_NONE
+
+ channel.Close()
+ q.expect('dbus-signal', signal='Closed')
+
+if __name__ == '__main__':
+ exec_test(test)
diff --git a/tests/twisted/avahi/test-tube-close.py b/tests/twisted/avahi/test-tube-close.py
new file mode 100644
index 00000000..42d61ed3
--- /dev/null
+++ b/tests/twisted/avahi/test-tube-close.py
@@ -0,0 +1,82 @@
+"""
+Offer a 1-1 stream tube and close the connection. Salut must just send a
+stanza to close the tube and disconnect.
+"""
+
+from saluttest import exec_test
+from avahitest import AvahiAnnouncer, AvahiListener
+from avahitest import get_host_name
+import avahi
+
+from xmppstream import setup_stream_listener, connect_to_stream
+from servicetest import make_channel_proxy
+
+from twisted.words.xish import xpath, domish
+
+import dbus
+
+PUBLISHED_NAME="test-tube"
+
+CHANNEL_TYPE_TUBES = "org.freedesktop.Telepathy.Channel.Type.Tubes"
+HT_CONTACT = 1
+HT_CONTACT_LIST = 3
+SOCKET_ADDRESS_TYPE_IPV4 = dbus.UInt32(2)
+SOCKET_ACCESS_CONTROL_LOCALHOST = dbus.UInt32(0)
+
+print "FIXME: test-tube-close.py disabled because sending a close stanza on "
+print "disconnection is not yet implemented in telepathy-salut. It requires "
+print "to ensure the XmppConnection and reestablish it"
+# exiting 77 causes automake to consider the test to have been skipped
+raise SystemExit(77)
+
+def test(q, bus, conn):
+ # Salut will not connect to this socket, the test finishs before
+ socket_address = ('0.0.0.0', dbus.UInt16(0))
+
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
+ basic_txt = { "txtvers": "1", "status": "avail" }
+
+ contact_name = PUBLISHED_NAME + get_host_name()
+ listener, port = setup_stream_listener(q, contact_name)
+
+ announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt)
+
+ publish_handle = conn.RequestHandles(HT_CONTACT_LIST, ["publish"])[0]
+ publish = conn.RequestChannel(
+ "org.freedesktop.Telepathy.Channel.Type.ContactList",
+ HT_CONTACT_LIST, publish_handle, False)
+
+ handle = 0
+ # Wait until the record shows up in publish
+ while handle == 0:
+ e = q.expect('dbus-signal', signal='MembersChanged', path=publish)
+ for h in e.args[1]:
+ name = conn.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact_name:
+ handle = h
+
+ t = conn.RequestChannel(CHANNEL_TYPE_TUBES, HT_CONTACT, handle,
+ True)
+ tubes_channel = make_channel_proxy(conn, t, "Channel.Type.Tubes")
+
+ tubes_channel.OfferStreamTube("http", dbus.Dictionary({}),
+ SOCKET_ADDRESS_TYPE_IPV4, socket_address,
+ SOCKET_ACCESS_CONTROL_LOCALHOST, "")
+
+ e = q.expect('stream-iq')
+
+ # Close the connection just after the tube has been offered.
+ conn.Disconnect()
+
+ # receive the close stanza for the tube
+ event = q.expect('stream-message')
+ message = event.stanza
+ close_node = xpath.queryForNodes('/message/close[@xmlns="%s"]' % NS_TUBES,
+ message)
+ assert close_node is not None
+
+ q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
+
+if __name__ == '__main__':
+ exec_test(test)
diff --git a/tests/twisted/avahi/test-tube.py b/tests/twisted/avahi/test-tube.py
new file mode 100644
index 00000000..9720b2ba
--- /dev/null
+++ b/tests/twisted/avahi/test-tube.py
@@ -0,0 +1,150 @@
+from saluttest import exec_test
+from avahitest import AvahiAnnouncer, AvahiListener
+from avahitest import get_host_name
+import avahi
+import dbus
+import os
+import errno
+import string
+
+from xmppstream import setup_stream_listener, connect_to_stream
+from servicetest import make_channel_proxy, Event
+
+from twisted.words.xish import xpath, domish
+from twisted.internet.protocol import Factory, Protocol, ClientCreator
+from twisted.internet import reactor
+
+PUBLISHED_NAME="test-tube"
+
+CHANNEL_TYPE_TUBES = "org.freedesktop.Telepathy.Channel.Type.Tubes"
+HT_CONTACT = 1
+HT_CONTACT_LIST = 3
+TEXT_MESSAGE_TYPE_NORMAL = dbus.UInt32(0)
+SOCKET_ADDRESS_TYPE_UNIX = dbus.UInt32(0)
+SOCKET_ADDRESS_TYPE_IPV4 = dbus.UInt32(2)
+SOCKET_ACCESS_CONTROL_LOCALHOST = dbus.UInt32(0)
+
+sample_parameters = dbus.Dictionary({
+ 's': 'hello',
+ 'ay': dbus.ByteArray('hello'),
+ 'u': dbus.UInt32(123),
+ 'i': dbus.Int32(-123),
+ }, signature='sv')
+
+test_string = "This string travels on a tube !"
+
+def test(q, bus, conn):
+
+ # define a basic tcp server that echoes what the client says, but with
+ # swapcase
+ class TrivialServer(Protocol):
+ def dataReceived(self, data):
+ self.transport.write(string.swapcase(data))
+ e = Event('server-data-received', service = self, data = data)
+ q.append(e)
+
+ # define a basic tcp client
+ class ClientGreeter(Protocol):
+ def dataReceived(self, data):
+ e = Event('client-data-received', service = self, data = data)
+ q.append(e)
+ def client_connected_cb(p):
+ e = Event('client-connected', transport = p.transport)
+ q.append(e)
+
+ # create the server
+ factory = Factory()
+ factory.protocol = TrivialServer
+ server_socket_address = os.getcwd() + '/stream'
+ try:
+ os.remove(server_socket_address)
+ except OSError, e:
+ if e.errno != errno.ENOENT:
+ raise
+ l = reactor.listenUNIX(server_socket_address, factory)
+
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
+ basic_txt = { "txtvers": "1", "status": "avail" }
+
+ contact_name = PUBLISHED_NAME + get_host_name()
+ listener, port = setup_stream_listener(q, contact_name)
+
+ announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt)
+
+ publish_handle = conn.RequestHandles(HT_CONTACT_LIST, ["publish"])[0]
+ publish = conn.RequestChannel(
+ "org.freedesktop.Telepathy.Channel.Type.ContactList",
+ HT_CONTACT_LIST, publish_handle, False)
+
+ handle = 0
+ # Wait until the record shows up in publish
+ while handle == 0:
+ e = q.expect('dbus-signal', signal='MembersChanged', path=publish)
+ for h in e.args[1]:
+ name = conn.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact_name:
+ handle = h
+
+ t = conn.RequestChannel(CHANNEL_TYPE_TUBES, HT_CONTACT, handle,
+ True)
+ tubes_channel = make_channel_proxy(conn, t, "Channel.Type.Tubes")
+
+ tube_id = tubes_channel.OfferStreamTube("http", sample_parameters,
+ SOCKET_ADDRESS_TYPE_UNIX, dbus.ByteArray(server_socket_address),
+ SOCKET_ACCESS_CONTROL_LOCALHOST, "")
+
+ e = q.expect('stream-iq')
+ iq_tube = xpath.queryForNodes('/iq/tube', e.stanza)[0]
+ transport = xpath.queryForNodes('/iq/tube/transport', e.stanza)[0]
+ assert iq_tube.attributes['type'] == 'stream'
+ assert iq_tube.attributes['service'] == 'http'
+ assert iq_tube.attributes['id'] is not None
+ port = transport.attributes['port']
+ assert port is not None
+ port = int(port)
+ assert port > 1024
+ assert port < 65536
+
+ params = {}
+ parameter_nodes = xpath.queryForNodes('/iq/tube/parameters/parameter',
+ e.stanza)
+ for node in parameter_nodes:
+ assert node['name'] not in params
+ params[node['name']] = (node['type'], str(node))
+ assert params == {'ay': ('bytes', 'aGVsbG8='),
+ 's': ('str', 'hello'),
+ 'i': ('int', '-123'),
+ 'u': ('uint', '123'),
+ }, params
+
+ # find the right host/IP address because Salut checks it
+ self_handle = conn.GetSelfHandle()
+ self_handle_name = conn.InspectHandles(HT_CONTACT, [self_handle])[0]
+ AvahiListener(q).listen_for_service("_presence._tcp")
+ e = q.expect('service-added', name = self_handle_name,
+ protocol = avahi.PROTO_INET)
+ service = e.service
+ service.resolve()
+ e = q.expect('service-resolved', service = service)
+ host_name = e.host_name
+
+ client = ClientCreator(reactor, ClientGreeter)
+ client.connectTCP(host_name, port).addCallback(client_connected_cb)
+
+ e = q.expect('client-connected')
+ client_transport = e.transport
+ client_transport.write(test_string)
+
+ e = q.expect('server-data-received')
+ assert e.data == test_string
+
+ e = q.expect('client-data-received')
+ assert e.data == string.swapcase(test_string)
+
+ # Close the tube propertly
+ tubes_channel.CloseTube(tube_id)
+ conn.Disconnect()
+
+if __name__ == '__main__':
+ exec_test(test)
diff --git a/tests/twisted/avahi/test-two-tubes.py b/tests/twisted/avahi/test-two-tubes.py
new file mode 100644
index 00000000..0f3d2fb5
--- /dev/null
+++ b/tests/twisted/avahi/test-two-tubes.py
@@ -0,0 +1,180 @@
+from saluttest import exec_test, make_connection
+from avahitest import AvahiAnnouncer, AvahiListener
+from avahitest import get_host_name
+import avahi
+import dbus
+import os
+import errno
+import string
+
+from xmppstream import setup_stream_listener, connect_to_stream
+from servicetest import make_channel_proxy, Event
+
+from twisted.words.xish import xpath, domish
+from twisted.internet.protocol import Factory, Protocol, ClientCreator
+from twisted.internet import reactor
+
+CHANNEL_TYPE_TUBES = "org.freedesktop.Telepathy.Channel.Type.Tubes"
+HT_CONTACT = 1
+HT_CONTACT_LIST = 3
+TEXT_MESSAGE_TYPE_NORMAL = dbus.UInt32(0)
+SOCKET_ADDRESS_TYPE_UNIX = dbus.UInt32(0)
+SOCKET_ADDRESS_TYPE_IPV4 = dbus.UInt32(2)
+SOCKET_ACCESS_CONTROL_LOCALHOST = dbus.UInt32(0)
+
+sample_parameters = dbus.Dictionary({
+ 's': 'hello',
+ 'ay': dbus.ByteArray('hello'),
+ 'u': dbus.UInt32(123),
+ 'i': dbus.Int32(-123),
+ }, signature='sv')
+
+test_string = "This string travels on a tube !"
+
+def test(q, bus, conn):
+
+ # define a basic tcp server that echoes what the client says, but with
+ # swapcase
+ class TrivialServer(Protocol):
+ def dataReceived(self, data):
+ self.transport.write(string.swapcase(data))
+ e = Event('server-data-received', service = self, data = data)
+ q.append(e)
+
+ # define a basic tcp client
+ class ClientGreeter(Protocol):
+ def dataReceived(self, data):
+ e = Event('client-data-received', service = self, data = data)
+ q.append(e)
+ def client_connected_cb(p):
+ e = Event('client-connected', transport = p.transport)
+ q.append(e)
+
+ # create the server
+ factory = Factory()
+ factory.protocol = TrivialServer
+ server_socket_address = os.getcwd() + '/stream'
+ try:
+ os.remove(server_socket_address)
+ except OSError, e:
+ if e.errno != errno.ENOENT:
+ raise
+ l = reactor.listenUNIX(server_socket_address, factory)
+
+ # first connection: connect
+ contact1_name = "testsuite" + "@" + get_host_name()
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
+
+ # second connection: connect
+ conn2_params = {
+ 'published-name': 'testsuite2',
+ 'first-name': 'test2',
+ 'last-name': 'suite2',
+ }
+ contact2_name = "testsuite2" + "@" + get_host_name()
+ conn2 = make_connection(bus, q.append, conn2_params)
+ conn2.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L])
+
+ # first connection: get the contact list
+ publish_handle = conn.RequestHandles(HT_CONTACT_LIST, ["publish"])[0]
+ conn1_publish = conn.RequestChannel(
+ "org.freedesktop.Telepathy.Channel.Type.ContactList",
+ HT_CONTACT_LIST, publish_handle, False)
+ conn1_publish_proxy = bus.get_object(conn.bus_name, conn1_publish)
+
+ # second connection: get the contact list
+ publish_handle = conn2.RequestHandles(HT_CONTACT_LIST, ["publish"])[0]
+ conn2_publish = conn2.RequestChannel(
+ "org.freedesktop.Telepathy.Channel.Type.ContactList",
+ HT_CONTACT_LIST, publish_handle, False)
+ conn2_publish_proxy = bus.get_object(conn2.bus_name, conn2_publish)
+
+ # first connection: wait to see contact2
+ # The signal MembersChanged may be already emitted... check the Members
+ # property first
+ contact2_handle_on_conn1 = 0
+ conn1_members = conn1_publish_proxy.Get(
+ 'org.freedesktop.Telepathy.Channel.Interface.Group', 'Members',
+ dbus_interface='org.freedesktop.DBus.Properties')
+ for h in conn1_members:
+ name = conn.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact2_name:
+ contact2_handle_on_conn1 = h
+ while contact2_handle_on_conn1 == 0:
+ e = q.expect('dbus-signal', signal='MembersChanged', path=conn1_publish)
+ for h in e.args[1]:
+ name = conn.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact2_name:
+ contact2_handle_on_conn1 = h
+
+ # second connection: wait to see contact1
+ # The signal MembersChanged may be already emitted... check the Members
+ # property first
+ contact1_handle_on_conn2 = 0
+ conn2_members = conn2_publish_proxy.Get(
+ 'org.freedesktop.Telepathy.Channel.Interface.Group', 'Members',
+ dbus_interface='org.freedesktop.DBus.Properties')
+ for h in conn2_members:
+ name = conn.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact1_name:
+ contact1_handle_on_conn2 = h
+ while contact1_handle_on_conn2 == 0:
+ e = q.expect('dbus-signal', signal='MembersChanged', path=conn2_publish)
+ for h in e.args[1]:
+ name = conn2.InspectHandles(HT_CONTACT, [h])[0]
+ if name == contact1_name:
+ contact1_handle_on_conn2 = h
+
+ # do tubes
+ t = conn.RequestChannel(CHANNEL_TYPE_TUBES, HT_CONTACT,
+ contact2_handle_on_conn1, True)
+ contact1_tubes_channel = make_channel_proxy(conn, t, "Channel.Type.Tubes")
+
+ tube_id = contact1_tubes_channel.OfferStreamTube("http", sample_parameters,
+ SOCKET_ADDRESS_TYPE_UNIX, dbus.ByteArray(server_socket_address),
+ SOCKET_ACCESS_CONTROL_LOCALHOST, "")
+
+ contact2_channeltype = None
+ while contact2_channeltype == None:
+ e = q.expect('dbus-signal', signal='NewChannel')
+ if (e.args[1] == CHANNEL_TYPE_TUBES) and (e.path.endswith("testsuite2") == True):
+ contact2_objpath = e.args[0]
+ contact2_channeltype = e.args[1]
+
+ contact2_tubes_channel = make_channel_proxy(conn2, contact2_objpath, "Channel.Type.Tubes")
+
+ contact2_tubes = contact2_tubes_channel.ListTubes()
+ assert len(contact2_tubes) == 1
+ contact2_tube = contact2_tubes[0]
+ assert contact2_tube[0] is not None # tube id
+ assert contact2_tube[1] is not None # initiator
+ assert contact2_tube[2] == 1 # type = stream tube
+ assert contact2_tube[3] == 'http' # service = http
+ assert contact2_tube[4] is not None # parameters
+ assert contact2_tube[5] == 0, contact2_tube[5] # status = local pending
+
+ unix_socket_adr = contact2_tubes_channel.AcceptStreamTube(
+ contact2_tube[0], 0, 0, '', byte_arrays=True)
+
+ client = ClientCreator(reactor, ClientGreeter)
+ client.connectUNIX(unix_socket_adr).addCallback(client_connected_cb)
+
+ e = q.expect('client-connected')
+ client_transport = e.transport
+ client_transport.write(test_string)
+
+ e = q.expect('server-data-received')
+ assert e.data == test_string
+
+ e = q.expect('client-data-received')
+ assert e.data == string.swapcase(test_string)
+
+ # Close the tube propertly
+ contact1_tubes_channel.CloseTube(tube_id)
+ conn.Disconnect()
+ conn2.Disconnect()
+
+if __name__ == '__main__':
+ exec_test(test)
diff --git a/tests/twisted/trivialstream.py b/tests/twisted/trivialstream.py
new file mode 100644
index 00000000..ca80b755
--- /dev/null
+++ b/tests/twisted/trivialstream.py
@@ -0,0 +1,70 @@
+import dbus.glib
+import gobject
+import sys
+import time
+import os
+import socket
+import tempfile
+import random
+import string
+
+class TrivialStream:
+ def __init__(self, socket_address=None):
+ self.socket_address = socket_address
+
+ def read_socket(self, s):
+ try:
+ data = s.recv(1024)
+ if len(data) > 0:
+ print "received:", data
+ except socket.error, e:
+ pass
+ return True
+
+ def write_socket(self, s, msg):
+ print "send:", msg
+ try:
+ s = s.send(msg)
+ except socket.error, e:
+ pass
+ return True
+
+class TrivialStreamServer(TrivialStream):
+ def __init__(self):
+ TrivialStream.__init__(self)
+
+ def run(self):
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.setblocking(1)
+ s.settimeout(0.1)
+ s.bind(("127.0.0.1", 0))
+
+ self.socket_address = s.getsockname()
+ print "Trivial Server lauched on socket", self.socket_address
+ s.listen(1)
+
+ gobject.timeout_add(1000, self.accept_client, s)
+
+ def accept_client(self, s):
+ try:
+ s2, addr = s.accept()
+ s2.setblocking(1)
+ s2.setblocking(0.1)
+ self.handle_client(s2)
+ return True
+ except socket.timeout:
+ return True
+
+ def handle_client(self, s):
+ gobject.timeout_add(5000, self.write_socket, s, "hi !")
+
+class TrivialStreamClient(TrivialStream):
+ def __init__(self, socket_address):
+ TrivialStream.__init__(self, socket_address)
+
+ def connect(self):
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect(self.socket_address)
+ print "Trivial client connected to", self.socket_address
+ gobject.timeout_add(1000, self.read_socket, s)
+