summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--.project17
-rw-r--r--libs/3rdParty/jxmpp-core-0.5.0.jarbin0 -> 25250 bytes
-rw-r--r--libs/3rdParty/jxmpp-jid-0.5.0.jarbin0 -> 23565 bytes
-rw-r--r--libs/3rdParty/smack/smack-core-4.2.1.jarbin0 -> 356202 bytes
-rw-r--r--libs/3rdParty/smack/smack-extensions-4.2.1.jarbin0 -> 702466 bytes
-rw-r--r--libs/3rdParty/smack/smack-im-4.2.1.jarbin0 -> 71650 bytes
-rw-r--r--libs/3rdParty/xmlpull-1.1.3.1.jarbin0 -> 7188 bytes
-rw-r--r--libs/3rdParty/xpp3_min-1.1.4c.jarbin0 -> 24956 bytes
-rw-r--r--src/de/thedevstack/smackx/filetransferhttp/FileTransferHttp.java5
-rw-r--r--src/de/thedevstack/smackx/filetransferhttp/FileTransferHttpManager.java77
-rw-r--r--src/de/thedevstack/smackx/filetransferhttp/element/FileList.java40
-rw-r--r--src/de/thedevstack/smackx/filetransferhttp/element/RemoteFile.java79
-rw-r--r--src/de/thedevstack/smackx/filetransferhttp/element/RemoteFileInfo.java54
-rw-r--r--src/de/thedevstack/smackx/filetransferhttp/element/Request.java19
-rw-r--r--src/de/thedevstack/smackx/filetransferhttp/provider/FileListProvider.java118
-rw-r--r--test/de/thedevstack/smackx/filetransferhttp/provider/FileListProviderTest.java117
-rw-r--r--xsds/file.xsd26
18 files changed, 554 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ddb0a2d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+bin
+.settings
diff --git a/.project b/.project
new file mode 100644
index 0000000..ab99ea0
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>thedevstack-smack-extensions</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/libs/3rdParty/jxmpp-core-0.5.0.jar b/libs/3rdParty/jxmpp-core-0.5.0.jar
new file mode 100644
index 0000000..ce5f193
--- /dev/null
+++ b/libs/3rdParty/jxmpp-core-0.5.0.jar
Binary files differ
diff --git a/libs/3rdParty/jxmpp-jid-0.5.0.jar b/libs/3rdParty/jxmpp-jid-0.5.0.jar
new file mode 100644
index 0000000..e65dee7
--- /dev/null
+++ b/libs/3rdParty/jxmpp-jid-0.5.0.jar
Binary files differ
diff --git a/libs/3rdParty/smack/smack-core-4.2.1.jar b/libs/3rdParty/smack/smack-core-4.2.1.jar
new file mode 100644
index 0000000..8026a4f
--- /dev/null
+++ b/libs/3rdParty/smack/smack-core-4.2.1.jar
Binary files differ
diff --git a/libs/3rdParty/smack/smack-extensions-4.2.1.jar b/libs/3rdParty/smack/smack-extensions-4.2.1.jar
new file mode 100644
index 0000000..858bbd0
--- /dev/null
+++ b/libs/3rdParty/smack/smack-extensions-4.2.1.jar
Binary files differ
diff --git a/libs/3rdParty/smack/smack-im-4.2.1.jar b/libs/3rdParty/smack/smack-im-4.2.1.jar
new file mode 100644
index 0000000..6e9c61a
--- /dev/null
+++ b/libs/3rdParty/smack/smack-im-4.2.1.jar
Binary files differ
diff --git a/libs/3rdParty/xmlpull-1.1.3.1.jar b/libs/3rdParty/xmlpull-1.1.3.1.jar
new file mode 100644
index 0000000..cbc149d
--- /dev/null
+++ b/libs/3rdParty/xmlpull-1.1.3.1.jar
Binary files differ
diff --git a/libs/3rdParty/xpp3_min-1.1.4c.jar b/libs/3rdParty/xpp3_min-1.1.4c.jar
new file mode 100644
index 0000000..813a9a8
--- /dev/null
+++ b/libs/3rdParty/xpp3_min-1.1.4c.jar
Binary files differ
diff --git a/src/de/thedevstack/smackx/filetransferhttp/FileTransferHttp.java b/src/de/thedevstack/smackx/filetransferhttp/FileTransferHttp.java
new file mode 100644
index 0000000..2b00ed4
--- /dev/null
+++ b/src/de/thedevstack/smackx/filetransferhttp/FileTransferHttp.java
@@ -0,0 +1,5 @@
+package de.thedevstack.smackx.filetransferhttp;
+
+public interface FileTransferHttp {
+ String NAMESPACE = "urn:xmpp:filetransfer:http";
+}
diff --git a/src/de/thedevstack/smackx/filetransferhttp/FileTransferHttpManager.java b/src/de/thedevstack/smackx/filetransferhttp/FileTransferHttpManager.java
new file mode 100644
index 0000000..fffbcbd
--- /dev/null
+++ b/src/de/thedevstack/smackx/filetransferhttp/FileTransferHttpManager.java
@@ -0,0 +1,77 @@
+package de.thedevstack.smackx.filetransferhttp;
+
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.logging.Logger;
+
+import org.jivesoftware.smack.ConnectionCreationListener;
+import org.jivesoftware.smack.Manager;
+import org.jivesoftware.smack.XMPPConnection;
+import org.jivesoftware.smack.XMPPConnectionRegistry;
+import org.jivesoftware.smack.SmackException.NoResponseException;
+import org.jivesoftware.smack.SmackException.NotConnectedException;
+import org.jivesoftware.smack.XMPPException.XMPPErrorException;
+import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
+
+import de.thedevstack.smackx.filetransferhttp.element.FileList;
+import de.thedevstack.smackx.filetransferhttp.element.Request;
+
+public class FileTransferHttpManager extends Manager {
+
+ private static final Logger LOGGER = Logger.getLogger(FileTransferHttpManager.class.getName());
+
+ static {
+ XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
+ @Override
+ public void connectionCreated(XMPPConnection connection) {
+ getInstanceFor(connection);
+ }
+ });
+ }
+
+ private static final Map<XMPPConnection, FileTransferHttpManager> INSTANCES = new WeakHashMap<>();
+
+
+ /**
+ * Obtain the HttpFileUploadManager responsible for a connection.
+ *
+ * @param connection the connection object.
+ * @return a HttpFileUploadManager instance
+ */
+ public static synchronized FileTransferHttpManager getInstanceFor(XMPPConnection connection) {
+ FileTransferHttpManager fileTransferHttpManager = INSTANCES.get(connection);
+
+ if (fileTransferHttpManager == null) {
+ fileTransferHttpManager = new FileTransferHttpManager(connection);
+ INSTANCES.put(connection, fileTransferHttpManager);
+ }
+
+ return fileTransferHttpManager;
+ }
+
+ private FileTransferHttpManager(XMPPConnection connection) {
+ super(connection);
+ }
+
+ public FileList requestFileList() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
+ final XMPPConnection connection = connection();
+ Request request = new Request();
+ request.setTo(connection.getXMPPServiceDomain());
+ return connection.createStanzaCollectorAndSend(request).nextResultOrThrow();
+ }
+
+
+ /**
+ * Returns true if XMPP Carbons are supported by the server.
+ *
+ * @return true if supported
+ * @throws NotConnectedException
+ * @throws XMPPErrorException
+ * @throws NoResponseException
+ * @throws InterruptedException
+ */
+ public boolean isSupportedByServer() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
+ return ServiceDiscoveryManager.getInstanceFor(connection()).serverSupportsFeature(FileTransferHttp.NAMESPACE);
+ }
+
+}
diff --git a/src/de/thedevstack/smackx/filetransferhttp/element/FileList.java b/src/de/thedevstack/smackx/filetransferhttp/element/FileList.java
new file mode 100644
index 0000000..563ca15
--- /dev/null
+++ b/src/de/thedevstack/smackx/filetransferhttp/element/FileList.java
@@ -0,0 +1,40 @@
+package de.thedevstack.smackx.filetransferhttp.element;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jivesoftware.smack.packet.IQ;
+
+import de.thedevstack.smackx.filetransferhttp.FileTransferHttp;
+
+public class FileList extends IQ {
+ private List<RemoteFile> files = new ArrayList<>();
+
+ public FileList() {
+ super("list", FileTransferHttp.NAMESPACE);
+ }
+
+ public List<RemoteFile> getFiles() {
+ return files;
+ }
+
+ @Override
+ protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
+ xml.rightAngleBracket();
+
+ if (0 < this.files.size()) {
+ for (RemoteFile file : this.files) {
+ xml.append(file.toXML());
+ }
+ } else {
+ xml.emptyElement("empty");
+ }
+
+ return xml;
+ }
+
+ public void addRemoteFile(RemoteFile remoteFile) {
+ this.files.add(remoteFile);
+ }
+
+}
diff --git a/src/de/thedevstack/smackx/filetransferhttp/element/RemoteFile.java b/src/de/thedevstack/smackx/filetransferhttp/element/RemoteFile.java
new file mode 100644
index 0000000..483d8b3
--- /dev/null
+++ b/src/de/thedevstack/smackx/filetransferhttp/element/RemoteFile.java
@@ -0,0 +1,79 @@
+package de.thedevstack.smackx.filetransferhttp.element;
+
+import org.jivesoftware.smack.packet.NamedElement;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+
+public class RemoteFile implements NamedElement {
+ private String url;
+ private long timestamp;
+ private RemoteFileInfo fileInfo;
+ private String from;
+ private String to;
+
+ public RemoteFile(String url, long timestamp) {
+ this.url = url;
+ this.timestamp = timestamp;
+ }
+
+ public String getFilename() {
+ return (null != fileInfo) ? fileInfo.getFilename() : null;
+ }
+
+ public long getSize() {
+ return (null != fileInfo) ? fileInfo.getSize() : -1;
+ }
+
+ public String getContentType() {
+ return (null != fileInfo) ? fileInfo.getContentType() : null;
+ }
+
+ public RemoteFileInfo getFileInfo() {
+ return fileInfo;
+ }
+
+ public void setFileInfo(RemoteFileInfo fileInfo) {
+ this.fileInfo = fileInfo;
+ }
+
+ public String getFrom() {
+ return from;
+ }
+
+ public void setFrom(String from) {
+ this.from = from;
+ }
+
+ public String getTo() {
+ return to;
+ }
+
+ public void setTo(String to) {
+ this.to = to;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ @Override
+ public String getElementName() {
+ return "file";
+ }
+
+ @Override
+ public CharSequence toXML() {
+ XmlStringBuilder xml = new XmlStringBuilder(this);
+ xml.attribute("timestamp", String.valueOf(this.timestamp));
+ xml.optAttribute("from", this.from);
+ xml.optAttribute("to", this.to);
+ xml.rightAngleBracket();
+ xml.element("url", this.url);
+ xml.append(fileInfo.toXML());
+ xml.closeElement(this);
+ return xml;
+ }
+}
diff --git a/src/de/thedevstack/smackx/filetransferhttp/element/RemoteFileInfo.java b/src/de/thedevstack/smackx/filetransferhttp/element/RemoteFileInfo.java
new file mode 100644
index 0000000..caeb2c8
--- /dev/null
+++ b/src/de/thedevstack/smackx/filetransferhttp/element/RemoteFileInfo.java
@@ -0,0 +1,54 @@
+package de.thedevstack.smackx.filetransferhttp.element;
+
+import org.jivesoftware.smack.packet.NamedElement;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+
+public class RemoteFileInfo implements NamedElement {
+ private final String filename;
+ private final long size;
+ private String contentType;
+
+ public RemoteFileInfo(String filename, long size) {
+ this.filename = filename;
+ this.size = size;
+ }
+
+ public RemoteFileInfo(String filename, long size, String contentType) {
+ this(filename, size);
+ this.contentType = contentType;
+ }
+
+
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ @Override
+ public String getElementName() {
+ return "file-info";
+ }
+
+ @Override
+ public CharSequence toXML() {
+ XmlStringBuilder xml = new XmlStringBuilder(this);
+ xml.rightAngleBracket();
+ xml.element("filename", this.filename);
+ xml.optElement("size", this.size);
+ xml.optElement("content-type", this.contentType);
+ xml.closeElement(this);
+ return xml;
+ }
+}
diff --git a/src/de/thedevstack/smackx/filetransferhttp/element/Request.java b/src/de/thedevstack/smackx/filetransferhttp/element/Request.java
new file mode 100644
index 0000000..ac974b9
--- /dev/null
+++ b/src/de/thedevstack/smackx/filetransferhttp/element/Request.java
@@ -0,0 +1,19 @@
+package de.thedevstack.smackx.filetransferhttp.element;
+
+import org.jivesoftware.smack.packet.IQ;
+
+import de.thedevstack.smackx.filetransferhttp.FileTransferHttp;
+
+public class Request extends IQ {
+ public Request() {
+ super("request", FileTransferHttp.NAMESPACE);
+ setType(Type.get);
+ }
+
+ @Override
+ protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
+ xml.attribute("type", "list");
+ xml.setEmptyElement();
+ return xml;
+ }
+}
diff --git a/src/de/thedevstack/smackx/filetransferhttp/provider/FileListProvider.java b/src/de/thedevstack/smackx/filetransferhttp/provider/FileListProvider.java
new file mode 100644
index 0000000..d6738b6
--- /dev/null
+++ b/src/de/thedevstack/smackx/filetransferhttp/provider/FileListProvider.java
@@ -0,0 +1,118 @@
+package de.thedevstack.smackx.filetransferhttp.provider;
+
+import java.io.IOException;
+
+import org.jivesoftware.smack.SmackException;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smack.util.ParserUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import de.thedevstack.smackx.filetransferhttp.element.FileList;
+import de.thedevstack.smackx.filetransferhttp.element.RemoteFile;
+import de.thedevstack.smackx.filetransferhttp.element.RemoteFileInfo;
+
+public class FileListProvider extends IQProvider<FileList> {
+
+ @Override
+ public FileList parse(XmlPullParser parser, int initialDepth) throws Exception {
+ String namespace = parser.getNamespace();
+ FileList fileList = new FileList();
+
+ int event = -1;
+ int currentDepth = -1;
+ do {
+ event = parser.next();
+ currentDepth = parser.getDepth();
+
+ if (XmlPullParser.START_TAG == event) {
+ String name = parser.getName();
+ switch (name) {
+ case "file":
+ RemoteFile remoteFile = this.parseRemoteFile(parser, currentDepth);
+ if (null != remoteFile) {
+ fileList.addRemoteFile(remoteFile);
+ }
+ break;
+ }
+ }
+
+ } while (event != XmlPullParser.END_TAG && currentDepth != initialDepth);
+
+ return fileList;
+ }
+
+ protected RemoteFile parseRemoteFile(XmlPullParser parser, int initialDepth) throws IOException, XmlPullParserException {
+ long timestamp = ParserUtils.getLongAttribute(parser, "timestamp");
+ String to = parser.getAttributeValue("", "to");
+ String from = parser.getAttributeValue("", "from");
+ String url = null;
+ RemoteFileInfo remoteFileInfo = null;
+
+ int event = -1;
+ int currentDepth = -1;
+ do {
+ event = parser.next();
+ currentDepth = parser.getDepth();
+
+ if (XmlPullParser.START_TAG == event) {
+ String name = parser.getName();
+ switch (name) {
+ case "url":
+ url = parser.nextText();
+ break;
+ case "file-info":
+ remoteFileInfo = parseRemoteFileInfo(parser, currentDepth);
+ break;
+ }
+ }
+
+ } while (event != XmlPullParser.END_TAG && currentDepth != initialDepth);
+
+ RemoteFile remoteFile = new RemoteFile(url, timestamp);
+ remoteFile.setFileInfo(remoteFileInfo);
+ remoteFile.setTo(to);
+ remoteFile.setFrom(from);
+
+ return remoteFile;
+ }
+
+ protected RemoteFileInfo parseRemoteFileInfo(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException {
+ long size = -1;
+ String contentType = null;
+ String filename = null;
+
+ int event = -1;
+ int currentDepth = -1;
+ do {
+ event = parser.next();
+ currentDepth = parser.getDepth();
+
+ if (XmlPullParser.START_TAG == event) {
+ String name = parser.getName();
+ switch (name) {
+ case "filename":
+ filename = parser.nextText();
+ break;
+ case "size":
+ String sizeString = parser.nextText();
+ if (null != sizeString) {
+ size = Long.valueOf(sizeString);
+ }
+ break;
+ case "content-type":
+ contentType = parser.nextText();
+ break;
+ }
+ }
+
+ } while (event != XmlPullParser.END_TAG && currentDepth != initialDepth);
+
+ if (0 >= size || null == filename || filename.isEmpty()) {
+ return null;
+ }
+
+ return new RemoteFileInfo(filename, size, contentType);
+ }
+
+}
diff --git a/test/de/thedevstack/smackx/filetransferhttp/provider/FileListProviderTest.java b/test/de/thedevstack/smackx/filetransferhttp/provider/FileListProviderTest.java
new file mode 100644
index 0000000..8301dbe
--- /dev/null
+++ b/test/de/thedevstack/smackx/filetransferhttp/provider/FileListProviderTest.java
@@ -0,0 +1,117 @@
+package de.thedevstack.smackx.filetransferhttp.provider;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import de.thedevstack.smackx.filetransferhttp.element.FileList;
+import de.thedevstack.smackx.filetransferhttp.element.RemoteFile;
+import de.thedevstack.smackx.filetransferhttp.element.RemoteFileInfo;
+
+public class FileListProviderTest {
+
+ @Test
+ public void testParseRemoteFileInfo() throws Exception {
+ String xml = "<file-info>"
+ + "<filename>my_juliet.png</filename>"
+ + "<size>23456</size>"
+ + "<content-type>image/png</content-type>"
+ + "</file-info>";
+ XmlPullParser parser = PacketParserUtils.getParserFor(xml);
+ FileListProvider provider = new FileListProvider();
+ RemoteFileInfo info = provider.parseRemoteFileInfo(parser, 0);
+ assertNotNull(info);
+ assertEquals("my_juliet.png", info.getFilename());
+ assertEquals(23456, info.getSize());
+ assertEquals("image/png", info.getContentType());
+ }
+
+ @Test
+ public void testParseRemoteFileInfoWithMissingChildElements() throws Exception {
+ String xml = "<file-info>"
+ + "<size>23456</size>"
+ + "<content-type>image/png</content-type>"
+ + "</file-info>";
+ XmlPullParser parser = PacketParserUtils.getParserFor(xml);
+ FileListProvider provider = new FileListProvider();
+ RemoteFileInfo info = provider.parseRemoteFileInfo(parser, 0);
+ assertNull(info);
+
+ xml = "<file-info>"
+ + "<filename>my_juliet.png</filename>"
+ + "<content-type>image/png</content-type>"
+ + "</file-info>";
+ parser = PacketParserUtils.getParserFor(xml);
+ provider = new FileListProvider();
+ info = provider.parseRemoteFileInfo(parser, 0);
+ assertNull(info);
+
+
+ xml = "<file-info>"
+ + "<filename>my_juliet.png</filename>"
+ + "<size>23456</size>"
+ + "</file-info>";
+ parser = PacketParserUtils.getParserFor(xml);
+ provider = new FileListProvider();
+ info = provider.parseRemoteFileInfo(parser, 0);
+ assertNotNull(info);
+ assertEquals("my_juliet.png", info.getFilename());
+ assertEquals(23456, info.getSize());
+ assertNull(info.getContentType());
+ }
+
+ @Test
+ public void testParseRemoteFile() throws XmlPullParserException, IOException {
+ String xml = "<file timestamp='1234567890' to='julia@capulet.tld' from='romeo@montague.tld'>"
+ + "<file-info>"
+ + "<filename>my_juliet.png</filename>"
+ + "<size>23456</size>"
+ + "<content-type>image/png</content-type>"
+ + "</file-info>"
+ + "</file>";
+ XmlPullParser parser = PacketParserUtils.getParserFor(xml);
+ FileListProvider provider = new FileListProvider();
+ RemoteFile file = provider.parseRemoteFile(parser, 0);
+ assertNotNull(file);
+ assertEquals(1234567890, file.getTimestamp());
+ assertEquals("my_juliet.png", file.getFilename());
+ assertEquals(23456, file.getSize());
+ assertEquals("image/png", file.getContentType());
+ assertEquals("julia@capulet.tld", file.getTo());
+ assertEquals("romeo@montague.tld", file.getFrom());
+ }
+
+ @Test
+ public void testParse() throws Exception {
+ String xml = "<list>"
+ + "<file timestamp='1234567890' to='julia@capulet.tld' from='romeo@montague.tld'>"
+ + "<file-info>"
+ + "<filename>my_juliet.png</filename>"
+ + "<size>23456</size>"
+ + "<content-type>image/png</content-type>"
+ + "</file-info>"
+ + "</file>"
+ + "</list>";
+ XmlPullParser parser = PacketParserUtils.getParserFor(xml);
+ FileListProvider provider = new FileListProvider();
+ FileList fileList = provider.parse(parser, 0);
+ assertNotNull(fileList);
+ assertNotNull(fileList.getFiles());
+ assertEquals(1, fileList.getFiles().size());
+ RemoteFile file = fileList.getFiles().get(0);
+ assertEquals(1234567890, file.getTimestamp());
+ assertEquals("my_juliet.png", file.getFilename());
+ assertEquals(23456, file.getSize());
+ assertEquals("image/png", file.getContentType());
+ assertEquals("julia@capulet.tld", file.getTo());
+ assertEquals("romeo@montague.tld", file.getFrom());
+ }
+}
diff --git a/xsds/file.xsd b/xsds/file.xsd
new file mode 100644
index 0000000..f018dc5
--- /dev/null
+++ b/xsds/file.xsd
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="urn:xmpp:filetransfer:http"
+ xmlns="urn:xmpp:filetransfer:http" elementFormDefault="qualified">
+ <xs:element name="file" type="File"/>
+
+ <xs:complexType name="File">
+ <xs:sequence>
+ <xs:element name="url" type="xs:anyURI"/>
+ <xs:element name="file-info" type="FileInfo"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="FileInfo">
+ <xs:sequence>
+ <xs:element name="filename" type="xs:string"/>
+ <xs:element name="size" type="xs:positiveInteger"/>
+ <xs:element name="content-type" type="xs:string" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute name="from" type="xs:string" use="optional"/>
+ <xs:attribute name="to" type="xs:string" use="optional"/>
+ <xs:attribute name="timestamp" type="xs:positiveInteger"/>
+ </xs:complexType>
+
+
+</xs:schema> \ No newline at end of file