diff options
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 Binary files differnew file mode 100644 index 0000000..ce5f193 --- /dev/null +++ b/libs/3rdParty/jxmpp-core-0.5.0.jar diff --git a/libs/3rdParty/jxmpp-jid-0.5.0.jar b/libs/3rdParty/jxmpp-jid-0.5.0.jar Binary files differnew file mode 100644 index 0000000..e65dee7 --- /dev/null +++ b/libs/3rdParty/jxmpp-jid-0.5.0.jar diff --git a/libs/3rdParty/smack/smack-core-4.2.1.jar b/libs/3rdParty/smack/smack-core-4.2.1.jar Binary files differnew file mode 100644 index 0000000..8026a4f --- /dev/null +++ b/libs/3rdParty/smack/smack-core-4.2.1.jar diff --git a/libs/3rdParty/smack/smack-extensions-4.2.1.jar b/libs/3rdParty/smack/smack-extensions-4.2.1.jar Binary files differnew file mode 100644 index 0000000..858bbd0 --- /dev/null +++ b/libs/3rdParty/smack/smack-extensions-4.2.1.jar diff --git a/libs/3rdParty/smack/smack-im-4.2.1.jar b/libs/3rdParty/smack/smack-im-4.2.1.jar Binary files differnew file mode 100644 index 0000000..6e9c61a --- /dev/null +++ b/libs/3rdParty/smack/smack-im-4.2.1.jar diff --git a/libs/3rdParty/xmlpull-1.1.3.1.jar b/libs/3rdParty/xmlpull-1.1.3.1.jar Binary files differnew file mode 100644 index 0000000..cbc149d --- /dev/null +++ b/libs/3rdParty/xmlpull-1.1.3.1.jar diff --git a/libs/3rdParty/xpp3_min-1.1.4c.jar b/libs/3rdParty/xpp3_min-1.1.4c.jar Binary files differnew file mode 100644 index 0000000..813a9a8 --- /dev/null +++ b/libs/3rdParty/xpp3_min-1.1.4c.jar 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 |