From a0a7f4e20c6dbcb14f97431537e6965eb40a808b Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 31 Dec 2020 00:05:53 +0100 Subject: [PATCH] Initial support for XEP-0447: Stateless File Sharing --- .../element/FileSharingElement.java | 88 +++++++++++++++++++ .../element/SourcesElement.java | 62 +++++++++++++ .../element/package-info.java | 22 +++++ .../stateless_file_sharing/package-info.java | 22 +++++ .../provider/FileSharingElementProvider.java | 82 +++++++++++++++++ .../provider/package-info.java | 22 +++++ .../FileSharingElementTest.java | 84 ++++++++++++++++++ 7 files changed, 382 insertions(+) create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/element/FileSharingElement.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/element/SourcesElement.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/element/package-info.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/package-info.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/provider/FileSharingElementProvider.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/provider/package-info.java create mode 100644 smack-experimental/src/test/java/org/jivesoftware/smackx/stateless_file_sharing/FileSharingElementTest.java diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/element/FileSharingElement.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/element/FileSharingElement.java new file mode 100644 index 000000000..c5ada69ba --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/element/FileSharingElement.java @@ -0,0 +1,88 @@ +/** + * + * Copyright 2020 Paul Schaub + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.stateless_file_sharing.element; + +import javax.xml.namespace.QName; + +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.packet.XmlEnvironment; +import org.jivesoftware.smack.util.EqualsUtil; +import org.jivesoftware.smack.util.HashCode; +import org.jivesoftware.smack.util.XmlStringBuilder; +import org.jivesoftware.smackx.file_metadata.element.FileMetadataElement; + +public class FileSharingElement implements ExtensionElement { + + public static final String ELEMENT = "file-sharing"; + public static final String NAMESPACE = "urn:xmpp:sfs:0"; + public static final QName QNAME = new QName(NAMESPACE, ELEMENT); + + private final FileMetadataElement metadataElement; + private final SourcesElement sourcesElement; + + public FileSharingElement(FileMetadataElement metadata, SourcesElement sources) { + this.metadataElement = metadata; + this.sourcesElement = sources; + } + + public FileMetadataElement getMetadata() { + return metadataElement; + } + + public SourcesElement getSources() { + return sourcesElement; + } + + @Override + public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { + return new XmlStringBuilder(this) + .rightAngleBracket() + .append(getMetadata()) + .append(getSources()) + .closeElement(this); + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public int hashCode() { + return HashCode.builder() + .append(getElementName()) + .append(getNamespace()) + .append(getMetadata()) + .append(getSources()) + .build(); + } + + @Override + public boolean equals(Object obj) { + return EqualsUtil.equals(this, obj, (equalsBuilder, other) -> + equalsBuilder + .append(getElementName(), other.getElementName()) + .append(getNamespace(), other.getNamespace()) + .append(getMetadata(), other.getMetadata()) + .append(getSources(), other.getSources())); + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/element/SourcesElement.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/element/SourcesElement.java new file mode 100644 index 000000000..e9eae86ff --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/element/SourcesElement.java @@ -0,0 +1,62 @@ +/** + * + * Copyright 2020 Paul Schaub + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.stateless_file_sharing.element; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.packet.NamedElement; +import org.jivesoftware.smack.packet.XmlEnvironment; +import org.jivesoftware.smack.util.XmlStringBuilder; +import org.jivesoftware.smackx.url_address_information.element.UrlDataElement; + +public class SourcesElement implements NamedElement { + + public static final String ELEMENT = "sources"; + + private final List urlDataElements = new ArrayList<>(); + private final List otherSourceElements = new ArrayList<>(); + + public SourcesElement(List urlDataElements, List otherSourceElements) { + this.urlDataElements.addAll(urlDataElements); + this.otherSourceElements.addAll(otherSourceElements); + } + + @Override + public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { + return new XmlStringBuilder(this) + .rightAngleBracket() + .append(getUrlDataElements()) + .append(getOtherSourceElements()) + .closeElement(this); + } + + public List getUrlDataElements() { + return Collections.unmodifiableList(urlDataElements); + } + + public List getOtherSourceElements() { + return Collections.unmodifiableList(otherSourceElements); + } + + @Override + public String getElementName() { + return ELEMENT; + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/element/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/element/package-info.java new file mode 100644 index 000000000..71cd9dfc1 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/element/package-info.java @@ -0,0 +1,22 @@ +/** + * + * Copyright 2020 Paul Schaub + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Element classes for XEP-0447: Stateless File Sharing. + * + * @see XEP-0447: Stateless File Sharing + */ +package org.jivesoftware.smackx.stateless_file_sharing.element; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/package-info.java new file mode 100644 index 000000000..796682ceb --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/package-info.java @@ -0,0 +1,22 @@ +/** + * + * Copyright 2020 Paul Schaub + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Smack's API for XEP-0447: Stateless File Sharing. + * + * @see XEP-0447: Stateless File Sharing + */ +package org.jivesoftware.smackx.stateless_file_sharing; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/provider/FileSharingElementProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/provider/FileSharingElementProvider.java new file mode 100644 index 000000000..ceb35f8d4 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/provider/FileSharingElementProvider.java @@ -0,0 +1,82 @@ +/** + * + * Copyright 2020 Paul Schaub + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.stateless_file_sharing.provider; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.packet.XmlEnvironment; +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.parsing.StandardExtensionElementProvider; +import org.jivesoftware.smack.provider.ExtensionElementProvider; +import org.jivesoftware.smack.provider.ProviderManager; +import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smack.xml.XmlPullParserException; +import org.jivesoftware.smackx.file_metadata.element.FileMetadataElement; +import org.jivesoftware.smackx.file_metadata.provider.FileMetadataElementProvider; +import org.jivesoftware.smackx.stateless_file_sharing.element.FileSharingElement; +import org.jivesoftware.smackx.stateless_file_sharing.element.SourcesElement; +import org.jivesoftware.smackx.url_address_information.element.UrlDataElement; +import org.jivesoftware.smackx.url_address_information.provider.UrlDataElementProvider; + +public class FileSharingElementProvider extends ExtensionElementProvider { + + public static final FileSharingElementProvider INSTANCE = new FileSharingElementProvider(); + + @Override + public FileSharingElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) + throws XmlPullParserException, IOException, SmackParsingException { + FileMetadataElement fileMetadataElement = null; + SourcesElement sourcesElement = null; + List urlDataElements = new ArrayList<>(); + List otherSourceElements = new ArrayList<>(); + do { + XmlPullParser.TagEvent event = parser.nextTag(); + String name = parser.getName(); + + if (event == XmlPullParser.TagEvent.START_ELEMENT) { + if (name.equals(FileMetadataElement.ELEMENT)) { + fileMetadataElement = FileMetadataElementProvider.TEST_INSTANCE.parse(parser, xmlEnvironment); + } else if (name.equals(SourcesElement.ELEMENT)) { + int innerDepth = parser.getDepth(); + do { + XmlPullParser.TagEvent innerEvent = parser.nextTag(); + String innerName = parser.getName(); + if (innerEvent.equals(XmlPullParser.TagEvent.START_ELEMENT)) { + if (innerName.equals(UrlDataElement.ELEMENT)) { + urlDataElements.add(UrlDataElementProvider.INSTANCE.parse(parser)); + } else { + ExtensionElementProvider provider = ProviderManager.getExtensionProvider(innerName, parser.getNamespace()); + if (provider == null) { + provider = new StandardExtensionElementProvider(); + } + otherSourceElements.add(provider.parse(parser)); + } + } else { + if (innerName.equals(SourcesElement.ELEMENT)) { + sourcesElement = new SourcesElement(urlDataElements, otherSourceElements); + } + } + } while (parser.getDepth() != innerDepth); + } + } + } while (parser.getDepth() != initialDepth); + return new FileSharingElement(fileMetadataElement, sourcesElement); + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/provider/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/provider/package-info.java new file mode 100644 index 000000000..7f844cc74 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/stateless_file_sharing/provider/package-info.java @@ -0,0 +1,22 @@ +/** + * + * Copyright 2020 Paul Schaub + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Provider classes for XEP-0447: Stateless File Sharing. + * + * @see XEP-0447: Stateless File Sharing + */ +package org.jivesoftware.smackx.stateless_file_sharing.provider; diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/stateless_file_sharing/FileSharingElementTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/stateless_file_sharing/FileSharingElementTest.java new file mode 100644 index 000000000..2029d7d64 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/stateless_file_sharing/FileSharingElementTest.java @@ -0,0 +1,84 @@ +package org.jivesoftware.smackx.stateless_file_sharing; + +import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.util.Collections; + +import org.jivesoftware.smack.packet.StandardExtensionElement; +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.test.util.SmackTestSuite; +import org.jivesoftware.smack.test.util.TestUtils; +import org.jivesoftware.smack.xml.XmlPullParserException; +import org.jivesoftware.smackx.file_metadata.element.FileMetadataElement; +import org.jivesoftware.smackx.hashes.HashManager; +import org.jivesoftware.smackx.hashes.element.HashElement; +import org.jivesoftware.smackx.stateless_file_sharing.element.FileSharingElement; +import org.jivesoftware.smackx.stateless_file_sharing.element.SourcesElement; +import org.jivesoftware.smackx.stateless_file_sharing.provider.FileSharingElementProvider; +import org.jivesoftware.smackx.url_address_information.element.UrlDataElement; + +import org.junit.jupiter.api.Test; + +public class FileSharingElementTest extends SmackTestSuite { + + @Test + public void simpleElementTest() throws XmlPullParserException, IOException, SmackParsingException { + FileSharingElement fileSharingElement = new FileSharingElement( + FileMetadataElement.builder() + .setMediaType("image/jpeg") + .setName("summit.jpg") + .setSize(3032449) + .setDimensions(4096, 2160) + .addHash(new HashElement(HashManager.ALGORITHM.SHA3_256, "2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=")) + .addHash(new HashElement(HashManager.ALGORITHM.BLAKE2B256, "2AfMGH8O7UNPTvUVAM9aK13mpCY=")) + .addDescription("Photo from the summit.") + .addOtherChildElement( + StandardExtensionElement.builder("thumbnail", "urn:xmpp:thumbs:1") + .addAttribute("uri", "cid:sha1+ffd7c8d28e9c5e82afea41f97108c6b4@bob.xmpp.org") + .addAttribute("media-type", "image/png") + .addAttribute("width", "128") + .addAttribute("height", "96") + .build()) + .build(), + new SourcesElement(Collections.singletonList( + new UrlDataElement( + "https://download.montague.lit/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/summit.jpg", + null + ) + ), Collections.singletonList( + StandardExtensionElement.builder("jinglepub", "urn:xmpp:jinglepub:1") + .addAttribute("from", "romeo@montague.lit/resource") + .addAttribute("id", "9559976B-3FBF-4E7E-B457-2DAA225972BB") + .addElement(new StandardExtensionElement("description", "urn:xmpp:jingle:apps:file-transfer:5")) + .build() + ))); + + final String expectedXml = "" + + " \n" + + " \n" + + " image/jpeg\n" + + " summit.jpg\n" + + " 3032449\n" + + " 4096x2160\n" + + " 2XarmwTlNxDAMkvymloX3S5+VbylNrJt/l5QyPa+YoU=\n" + + " 2AfMGH8O7UNPTvUVAM9aK13mpCY=\n" + + " Photo from the summit.\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " " + + " \n" + + " \n" + + " \n" + + " "; + assertXmlSimilar(expectedXml, fileSharingElement.toXML().toString()); + + FileSharingElement parsed = FileSharingElementProvider.INSTANCE.parse(TestUtils.getParser(expectedXml)); + // While I'd rather test for equality here, we have to compare XML instead, as thumbnail is not implemented + // and we have to fall back to a StandardExtensionElement which has a non-ideal equals() implementation. + assertXmlSimilar(expectedXml, parsed.toXML().toString()); + } +}