From 2f3de3a64d4bda4bad8243eef879657a96d82402 Mon Sep 17 00:00:00 2001 From: vanitasvitae Date: Sat, 17 Jun 2017 00:56:32 +0200 Subject: [PATCH] Add Jingle File Transfer elements and JingleUtil class --- .../JingleFileTransferManager.java | 43 +++ .../jingle_filetransfer/element/Checksum.java | 63 +++++ .../element/JingleFileTransfer.java | 38 +++ .../element/JingleFileTransferChild.java | 167 ++++++++++++ .../jingle_filetransfer/element/Range.java | 135 ++++++++++ .../element/package-info.java | 22 ++ .../jingle_filetransfer/package-info.java | 21 ++ .../provider/ChecksumProvider.java | 89 +++++++ .../provider/JingleFileTransferProvider.java | 119 +++++++++ .../provider/package-info.java | 22 ++ .../smackx/jingle/JingleUtil.java | 251 ++++++++++++++++++ .../org/jivesoftware/smackx/jingle/Role.java | 23 ++ .../element/JingleContentTransport.java | 3 +- .../smackx/jingle/element/JingleReason.java | 2 +- 14 files changed, 995 insertions(+), 3 deletions(-) create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/Checksum.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/JingleFileTransfer.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/JingleFileTransferChild.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/Range.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/package-info.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/package-info.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/provider/ChecksumProvider.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/provider/JingleFileTransferProvider.java create mode 100644 smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/provider/package-info.java create mode 100644 smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleUtil.java create mode 100644 smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/Role.java diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java new file mode 100644 index 000000000..7b96cdbd4 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java @@ -0,0 +1,43 @@ +/** + * + * Copyright 2017 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.jingle_filetransfer; + +import java.util.WeakHashMap; + +import org.jivesoftware.smack.Manager; +import org.jivesoftware.smack.XMPPConnection; + +/** + * Manager for JingleFileTransfer (XEP-0234). + */ +public final class JingleFileTransferManager extends Manager { + + private static final WeakHashMap INSTANCES = new WeakHashMap<>(); + + private JingleFileTransferManager(XMPPConnection connection) { + super(connection); + } + + public static JingleFileTransferManager getInstanceFor(XMPPConnection connection) { + JingleFileTransferManager manager = INSTANCES.get(connection); + if (manager == null) { + manager = new JingleFileTransferManager(connection); + INSTANCES.put(connection, manager); + } + return manager; + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/Checksum.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/Checksum.java new file mode 100644 index 000000000..a841c8ae7 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/Checksum.java @@ -0,0 +1,63 @@ +/** + * + * Copyright 2017 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.jingle_filetransfer.element; + +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.util.Objects; +import org.jivesoftware.smack.util.XmlStringBuilder; +import org.jivesoftware.smackx.jingle.element.JingleContent; + +/** + * Checksum element. + */ +public class Checksum implements ExtensionElement { + public static final String ELEMENT = "checksum"; + public static final String ATTR_CREATOR = "creator"; + public static final String ATTR_NAME = "name"; + + private final JingleContent.Creator creator; + private final String name; + private final JingleFileTransferChild file; + + public Checksum(JingleContent.Creator creator, String name, JingleFileTransferChild file) { + this.creator = creator; + this.name = name; + this.file = Objects.requireNonNull(file, "file MUST NOT be null."); + Objects.requireNonNull(file.getHash(), "file MUST contain at least one hash element."); + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public CharSequence toXML() { + XmlStringBuilder sb = new XmlStringBuilder(this); + sb.optAttribute(ATTR_CREATOR, creator); + sb.optAttribute(ATTR_NAME, name); + sb.rightAngleBracket(); + sb.element(file); + sb.closeElement(this); + return sb; + } + + @Override + public String getNamespace() { + return JingleFileTransfer.NAMESPACE_V5; + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/JingleFileTransfer.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/JingleFileTransfer.java new file mode 100644 index 000000000..40dc1aaa2 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/JingleFileTransfer.java @@ -0,0 +1,38 @@ +/** + * + * Copyright 2017 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.jingle_filetransfer.element; + +import java.util.List; + +import org.jivesoftware.smackx.jingle.element.JingleContentDescription; +import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement; + +/** + * File element. + */ +public class JingleFileTransfer extends JingleContentDescription { + public static final String NAMESPACE_V5 = "urn:xmpp:jingle:apps:file-transfer:5"; + + public JingleFileTransfer(List payloads) { + super(payloads); + } + + @Override + public String getNamespace() { + return NAMESPACE_V5; + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/JingleFileTransferChild.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/JingleFileTransferChild.java new file mode 100644 index 000000000..9634d0a1b --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/JingleFileTransferChild.java @@ -0,0 +1,167 @@ +/** + * + * Copyright 2017 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.jingle_filetransfer.element; + +import java.io.File; +import java.util.Date; + +import org.jivesoftware.smack.util.XmlStringBuilder; +import org.jivesoftware.smackx.hashes.element.HashElement; +import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement; + +/** + * Content of type File. + */ +public class JingleFileTransferChild extends JingleContentDescriptionChildElement { + public static final String ELEMENT = "file"; + public static final String ELEM_DATE = "date"; + public static final String ELEM_DESC = "desc"; + public static final String ELEM_MEDIA_TYPE = "media-type"; + public static final String ELEM_NAME = "name"; + public static final String ELEM_SIZE = "size"; + + private final Date date; + private final String desc; + private final HashElement hash; + private final String mediaType; + private final String name; + private final int size; + private final Range range; + + public JingleFileTransferChild(Date date, String desc, HashElement hash, String mediaType, String name, int size, Range range) { + this.date = date; + this.desc = desc; + this.hash = hash; + this.mediaType = mediaType; + this.name = name; + this.size = size; + this.range = range; + } + + public Date getDate() { + return date; + } + + public String getDescription() { + return desc; + } + + public HashElement getHash() { + return hash; + } + + public String getMediaType() { + return mediaType; + } + + public String getName() { + return name; + } + + public int getSize() { + return size; + } + + public Range getRange() { + return range; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public CharSequence toXML() { + XmlStringBuilder sb = new XmlStringBuilder(this); + sb.rightAngleBracket(); + + sb.optElement(ELEM_DATE, date); + sb.optElement(ELEM_DESC, desc); + sb.optElement(ELEM_MEDIA_TYPE, mediaType); + sb.optElement(ELEM_NAME, name); + sb.optElement(range); + if (size > 0) { + sb.element(ELEM_SIZE, Integer.toString(size)); + } + sb.optElement(hash); + sb.closeElement(this); + return sb; + } + + public static Builder getBuilder() { + return new Builder(); + } + + public static final class Builder { + private Date date; + private String desc; + private HashElement hash; + private String mediaType; + private String name; + private int size; + private Range range; + + private Builder() { + } + + public Builder setDate(Date date) { + this.date = date; + return this; + } + + public Builder setDescription(String desc) { + this.desc = desc; + return this; + } + + public Builder setHash(HashElement hash) { + this.hash = hash; + return this; + } + + public Builder setMediaType(String mediaType) { + this.mediaType = mediaType; + return this; + } + + public Builder setName(String name) { + this.name = name; + return this; + } + + public Builder setSize(int size) { + this.size = size; + return this; + } + + public Builder setRange(Range range) { + this.range = range; + return this; + } + + public JingleFileTransferChild build() { + return new JingleFileTransferChild(date, desc, hash, mediaType, name, size, range); + } + + public Builder setFile(File file) { + return setDate(new Date(file.lastModified())) + .setName(file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf("/") + 1)) + .setSize((int) file.length()); + } + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/Range.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/Range.java new file mode 100644 index 000000000..9d4f867ad --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/Range.java @@ -0,0 +1,135 @@ +/** + * + * Copyright © 2017 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.jingle_filetransfer.element; + +import org.jivesoftware.smack.packet.NamedElement; +import org.jivesoftware.smack.util.XmlStringBuilder; +import org.jivesoftware.smackx.hashes.element.HashElement; + +/** + * RangeElement which specifies, which range of a file shall be transferred. + */ +public class Range implements NamedElement { + + public static final String ELEMENT = "range"; + public static final String ATTR_OFFSET = "offset"; + public static final String ATTR_LENGTH = "length"; + + private final int offset, length; + private final HashElement hash; + + /** + * Create a Range element with default values. + */ + public Range() { + this(0, -1, null); + } + + /** + * Create a Range element with specified length. + * @param length length of the transmitted data in bytes. + */ + public Range(int length) { + this(0, length, null); + } + + /** + * Create a Range element with specified offset and length. + * @param offset offset in bytes from the beginning of the transmitted data. + * @param length number of bytes that shall be transferred. + */ + public Range(int offset, int length) { + this(offset, length, null); + } + + /** + * Create a Range element with specified offset, length and hash. + * @param offset offset in bytes from the beginning of the transmitted data. + * @param length number of bytes that shall be transferred. + * @param hash hash of the bytes in the specified range. + */ + public Range(int offset, int length, HashElement hash) { + this.offset = offset; + this.length = length; + this.hash = hash; + } + + /** + * Return the index of the offset. + * This marks the begin of the specified range. + * @return offset + */ + public int getOffset() { + return offset; + } + + /** + * Return the length of the range. + * @return length + */ + public int getLength() { + return length; + } + + /** + * Return the hash element that contains a checksum of the bytes specified in the range. + * @return hash element + */ + public HashElement getHash() { + return hash; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public CharSequence toXML() { + XmlStringBuilder sb = new XmlStringBuilder(this); + + if (offset > 0) { + sb.attribute(ATTR_OFFSET, offset); + } + if (length > 0) { + sb.attribute(ATTR_LENGTH, length); + } + + if (hash != null) { + sb.rightAngleBracket(); + sb.element(hash); + sb.closeElement(this); + } else { + sb.closeEmptyElement(); + } + return sb; + } + + @Override + public boolean equals(Object other) { + if (other == null || !(other instanceof Range)) { + return false; + } + + return this.hashCode() == other.hashCode(); + } + + @Override + public int hashCode() { + return toXML().toString().hashCode(); + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/package-info.java new file mode 100644 index 000000000..da2e22582 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/element/package-info.java @@ -0,0 +1,22 @@ +/** + * + * Copyright 2017 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-0234: Jingle File Transfer. + * Elements. + */ +package org.jivesoftware.smackx.jingle_filetransfer.element; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/package-info.java new file mode 100644 index 000000000..86fb45ecc --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/package-info.java @@ -0,0 +1,21 @@ +/** + * + * Copyright 2017 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-0234: Jingle File Transfer. + */ +package org.jivesoftware.smackx.jingle_filetransfer; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/provider/ChecksumProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/provider/ChecksumProvider.java new file mode 100644 index 000000000..e91270ccc --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/provider/ChecksumProvider.java @@ -0,0 +1,89 @@ +/** + * + * Copyright 2017 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.jingle_filetransfer.provider; + +import static org.xmlpull.v1.XmlPullParser.END_TAG; +import static org.xmlpull.v1.XmlPullParser.START_TAG; + +import org.jivesoftware.smack.provider.ExtensionElementProvider; +import org.jivesoftware.smackx.hashes.element.HashElement; +import org.jivesoftware.smackx.hashes.provider.HashElementProvider; +import org.jivesoftware.smackx.jingle.element.JingleContent; +import org.jivesoftware.smackx.jingle_filetransfer.element.Checksum; +import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChild; +import org.jivesoftware.smackx.jingle_filetransfer.element.Range; +import org.xmlpull.v1.XmlPullParser; + +/** + * Provider for the Checksum element. + */ +public class ChecksumProvider extends ExtensionElementProvider { + @Override + public Checksum parse(XmlPullParser parser, int initialDepth) throws Exception { + JingleContent.Creator creator = null; + String creatorString = parser.getAttributeValue(null, Checksum.ATTR_CREATOR); + if (creatorString != null) { + creator = JingleContent.Creator.valueOf(creatorString); + } + String name = parser.getAttributeValue(null, Checksum.ATTR_NAME); + + + JingleFileTransferChild.Builder cb = JingleFileTransferChild.getBuilder(); + HashElement hashElement = null; + Range range = null; + + boolean go = true; + while (go) { + int tag = parser.nextTag(); + String n = parser.getText(); + + if (tag == START_TAG) { + switch (n) { + case HashElement.ELEMENT: + hashElement = new HashElementProvider().parse(parser); + break; + + case Range.ELEMENT: + String offset = parser.getAttributeValue(null, Range.ATTR_OFFSET); + String length = parser.getAttributeValue(null, Range.ATTR_LENGTH); + int o = offset == null ? 0 : Integer.parseInt(offset); + int l = length == null ? -1 : Integer.parseInt(length); + range = new Range(o, l); + } + } else if (tag == END_TAG) { + switch (n) { + case Range.ELEMENT: + if (hashElement != null && range != null) { + range = new Range(range.getOffset(), range.getLength(), hashElement); + hashElement = null; + } + break; + + case JingleFileTransferChild.ELEMENT: + if (hashElement != null) { + cb.setHash(hashElement); + } + if (range != null) { + cb.setRange(range); + } + go = false; + } + } + } + return new Checksum(creator, name, cb.build()); + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/provider/JingleFileTransferProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/provider/JingleFileTransferProvider.java new file mode 100644 index 000000000..2805e9953 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/provider/JingleFileTransferProvider.java @@ -0,0 +1,119 @@ +/** + * + * Copyright 2017 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.jingle_filetransfer.provider; + +import static org.xmlpull.v1.XmlPullParser.END_TAG; +import static org.xmlpull.v1.XmlPullParser.START_TAG; + +import java.util.ArrayList; + +import org.jivesoftware.smackx.hashes.element.HashElement; +import org.jivesoftware.smackx.hashes.provider.HashElementProvider; +import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement; +import org.jivesoftware.smackx.jingle.provider.JingleContentDescriptionProvider; +import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChild; +import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransfer; +import org.jivesoftware.smackx.jingle_filetransfer.element.Range; +import org.jxmpp.util.XmppDateTime; +import org.xmlpull.v1.XmlPullParser; + +/** + * Provider for JingleContentDescriptionFileTransfer elements. + */ +public class JingleFileTransferProvider + extends JingleContentDescriptionProvider { + + @Override + public JingleFileTransfer parse(XmlPullParser parser, int initialDepth) throws Exception { + ArrayList payloads = new ArrayList<>(); + boolean inRange = false; + JingleFileTransferChild.Builder builder = JingleFileTransferChild.getBuilder(); + HashElement inRangeHash = null; + + int offset = 0; + int length = -1; + + while (true) { + + int tag = parser.nextTag(); + String elem = parser.getName(); + + if (tag == START_TAG) { + switch (elem) { + case JingleFileTransferChild.ELEM_DATE: + builder.setDate(XmppDateTime.parseXEP0082Date(parser.nextText())); + break; + + case JingleFileTransferChild.ELEM_DESC: + builder.setDescription(parser.nextText()); + break; + + case JingleFileTransferChild.ELEM_MEDIA_TYPE: + builder.setMediaType(parser.nextText()); + break; + + case JingleFileTransferChild.ELEM_NAME: + builder.setName(parser.nextText()); + break; + + case JingleFileTransferChild.ELEM_SIZE: + builder.setSize(Integer.parseInt(parser.nextText())); + break; + + case Range.ELEMENT: + inRange = true; + String offsetString = parser.getAttributeValue(null, Range.ATTR_OFFSET); + String lengthString = parser.getAttributeValue(null, Range.ATTR_LENGTH); + offset = (offsetString != null ? Integer.parseInt(offsetString) : 0); + length = (lengthString != null ? Integer.parseInt(lengthString) : -1); + + if (parser.isEmptyElementTag()) { + inRange = false; + builder.setRange(new Range(offset, length)); + } + break; + + case HashElement.ELEMENT: + if (inRange) { + inRangeHash = new HashElementProvider().parse(parser); + } else { + builder.setHash(new HashElementProvider().parse(parser)); + } + break; + } + + } else if (tag == END_TAG) { + switch (elem) { + + case Range.ELEMENT: + inRange = false; + builder.setRange(new Range(offset, length, inRangeHash)); + inRangeHash = null; + break; + + case JingleFileTransferChild.ELEMENT: + payloads.add(builder.build()); + builder = JingleFileTransferChild.getBuilder(); + break; + + case JingleFileTransfer.ELEMENT: + return new JingleFileTransfer(payloads); + } + } + } + } +} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/provider/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/provider/package-info.java new file mode 100644 index 000000000..85163fdeb --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/provider/package-info.java @@ -0,0 +1,22 @@ +/** + * + * Copyright 2017 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-0234: Jingle File Transfer. + * Providers. + */ +package org.jivesoftware.smackx.jingle_filetransfer.provider; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleUtil.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleUtil.java new file mode 100644 index 000000000..e5f29d974 --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleUtil.java @@ -0,0 +1,251 @@ +/** + * + * Copyright 2017 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.jingle; + +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.XMPPError; +import org.jivesoftware.smackx.jingle.element.Jingle; +import org.jivesoftware.smackx.jingle.element.JingleAction; +import org.jivesoftware.smackx.jingle.element.JingleContent; +import org.jivesoftware.smackx.jingle.element.JingleContentDescription; +import org.jivesoftware.smackx.jingle.element.JingleContentTransport; +import org.jivesoftware.smackx.jingle.element.JingleError; +import org.jivesoftware.smackx.jingle.element.JingleReason; +import org.jxmpp.jid.FullJid; + +/** + * Util to quickly send jingle stanzas. + */ +public class JingleUtil { + + private final XMPPConnection connection; + + public JingleUtil(XMPPConnection connection) { + this.connection = connection; + } + + public IQ sendSessionInitiate(FullJid recipient, + String sessionId, + JingleContent.Creator contentCreator, + String contentName, + JingleContent.Senders contentSenders, + JingleContentDescription description, + JingleContentTransport transport) + throws SmackException.NotConnectedException, InterruptedException, + XMPPException.XMPPErrorException, SmackException.NoResponseException { + + Jingle.Builder jb = Jingle.getBuilder(); + jb.setAction(JingleAction.session_initiate) + .setSessionId(sessionId) + .setInitiator(connection.getUser()); + + JingleContent.Builder cb = JingleContent.getBuilder(); + cb.setCreator(contentCreator) + .setName(contentName) + .setSenders(contentSenders) + .setDescription(description) + .addTransport(transport); + + Jingle jingle = jb.addJingleContent(cb.build()).build(); + jingle.setFrom(connection.getUser()); + jingle.setTo(recipient); + + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public IQ sendSessionAccept(FullJid recipient, + String sessionId, + JingleContent.Creator contentCreator, + String contentName, + JingleContent.Senders contentSenders, + JingleContentDescription description, + JingleContentTransport transport) + throws SmackException.NotConnectedException, InterruptedException, + XMPPException.XMPPErrorException, SmackException.NoResponseException { + + Jingle.Builder jb = Jingle.getBuilder(); + jb.setResponder(connection.getUser()) + .setAction(JingleAction.session_accept) + .setSessionId(sessionId); + + JingleContent.Builder cb = JingleContent.getBuilder(); + cb.setCreator(contentCreator) + .setName(contentName) + .setSenders(contentSenders) + .setDescription(description) + .addTransport(transport); + + Jingle jingle = jb.addJingleContent(cb.build()).build(); + jingle.setTo(recipient); + jingle.setFrom(connection.getUser()); + + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + private IQ sendSessionTerminate(FullJid recipient, String sessionId, JingleReason.Reason reason) + throws SmackException.NotConnectedException, InterruptedException, + XMPPException.XMPPErrorException, SmackException.NoResponseException { + + return sendSessionTerminate(recipient, sessionId, new JingleReason(reason)); + } + + private IQ sendSessionTerminate(FullJid recipient, String sessionId, JingleReason reason) + throws SmackException.NotConnectedException, InterruptedException, + XMPPException.XMPPErrorException, SmackException.NoResponseException { + + Jingle.Builder jb = Jingle.getBuilder(); + jb.setAction(JingleAction.session_terminate) + .setSessionId(sessionId) + .setReason(reason); + + Jingle jingle = jb.build(); + jingle.setFrom(connection.getUser()); + jingle.setTo(recipient); + + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public IQ sendSessionTerminateDecline(FullJid recipient, + String sessionId) + throws SmackException.NotConnectedException, InterruptedException, + XMPPException.XMPPErrorException, SmackException.NoResponseException { + return sendSessionTerminate(recipient, sessionId, JingleReason.Reason.decline); + } + + public IQ sendSessionTerminateSuccess(FullJid recipient, + String sessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + return sendSessionTerminate(recipient, sessionId, JingleReason.Reason.success); + } + + public IQ sendSessionTerminateBusy(FullJid recipient, + String sessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + return sendSessionTerminate(recipient, sessionId, JingleReason.Reason.busy); + } + + public IQ sendSessionTerminateAlternativeSession(FullJid recipient, + String sessionId, + String altSessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + return sendSessionTerminate(recipient, sessionId, JingleReason.AlternativeSession(altSessionId)); + } + + public IQ sendSessionTerminateCancel(FullJid recipient, + String sessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + return sendSessionTerminate(recipient, sessionId, JingleReason.Reason.cancel); + } + + public IQ sendSessionTerminateContentCancel(FullJid recipient, + String sessionId, + JingleContent.Creator contentCreator, + String contentName) + throws SmackException.NotConnectedException, InterruptedException, + XMPPException.XMPPErrorException, SmackException.NoResponseException { + + Jingle.Builder jb = Jingle.getBuilder(); + jb.setAction(JingleAction.session_terminate) + .setSessionId(sessionId); + + JingleContent.Builder cb = JingleContent.getBuilder(); + cb.setCreator(contentCreator).setName(contentName); + + Jingle jingle = jb.addJingleContent(cb.build()).build(); + jingle.setFrom(connection.getUser()); + jingle.setTo(recipient); + + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public IQ sendSessionTerminateUnsupportedTransports(FullJid recipient, String sessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + return sendSessionTerminate(recipient, sessionId, JingleReason.Reason.unsupported_transports); + } + + public IQ sendSessionTerminateFailedTransport(FullJid recipient, String sessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + return sendSessionTerminate(recipient, sessionId, JingleReason.Reason.failed_transport); + } + + public IQ sendSessionTerminateUnsupportedApplications(FullJid recipient, String sessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + return sendSessionTerminate(recipient, sessionId, JingleReason.Reason.unsupported_applications); + } + + public IQ sendSessionTerminateFailedApplication(FullJid recipient, String sessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + return sendSessionTerminate(recipient, sessionId, JingleReason.Reason.failed_application); + } + + public IQ sendSessionTerminateIncompatibleParameters(FullJid recipient, String sessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + return sendSessionTerminate(recipient, sessionId, JingleReason.Reason.incompatible_parameters); + } + + public IQ sendSessionPing(FullJid recipient, String sessionId) + throws SmackException.NotConnectedException, InterruptedException, + XMPPException.XMPPErrorException, SmackException.NoResponseException { + + Jingle.Builder jb = Jingle.getBuilder(); + jb.setSessionId(sessionId) + .setAction(JingleAction.session_info); + + Jingle jingle = jb.build(); + jingle.setFrom(connection.getUser()); + jingle.setTo(recipient); + + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public void sendErrorUnknownSession(Jingle request) + throws SmackException.NotConnectedException, InterruptedException { + + XMPPError.Builder error = XMPPError.getBuilder(); + error.setCondition(XMPPError.Condition.item_not_found) + .addExtension(JingleError.UNKNOWN_SESSION); + + connection.sendStanza(IQ.createErrorResponse(request, error)); + } + + public void sendErrorUnknownInitiator(Jingle request) + throws SmackException.NotConnectedException, InterruptedException { + connection.sendStanza(IQ.createErrorResponse(request, XMPPError.Condition.service_unavailable)); + } + + public void sendErrorUnsupportedInfo(Jingle request) + throws SmackException.NotConnectedException, InterruptedException { + + XMPPError.Builder error = XMPPError.getBuilder(); + error.setCondition(XMPPError.Condition.feature_not_implemented) + .addExtension(JingleError.UNSUPPORTED_INFO); + + connection.sendStanza(IQ.createErrorResponse(request, error)); + } +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/Role.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/Role.java new file mode 100644 index 000000000..0473b0894 --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/Role.java @@ -0,0 +1,23 @@ +/** + * + * Copyright 2017 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.jingle; + +public enum Role { + initiator, + responder, + ; +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentTransport.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentTransport.java index 156caf3cd..49f6dc66a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentTransport.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentTransport.java @@ -74,7 +74,7 @@ public abstract class JingleContentTransport implements ExtensionElement { XmlStringBuilder xml = new XmlStringBuilder(this); addExtraAttributes(xml); - if (candidates.isEmpty()) { + if (candidates.isEmpty() && infos.isEmpty()) { xml.closeEmptyElement(); } else { @@ -87,5 +87,4 @@ public abstract class JingleContentTransport implements ExtensionElement { return xml; } - } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java index 2855ccb8f..a975968b2 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java @@ -104,7 +104,7 @@ public class JingleReason implements NamedElement { protected final Reason reason; - protected JingleReason(Reason reason) { + public JingleReason(Reason reason) { this.reason = reason; }