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 index 229c70719..40dc1aaa2 100644 --- 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 @@ -27,7 +27,7 @@ import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildEleme public class JingleFileTransfer extends JingleContentDescription { public static final String NAMESPACE_V5 = "urn:xmpp:jingle:apps:file-transfer:5"; - protected JingleFileTransfer(List payloads) { + public JingleFileTransfer(List payloads) { super(payloads); } 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/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 index 713daf7d5..e5f29d974 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleUtil.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleUtil.java @@ -1,3 +1,19 @@ +/** + * + * 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;