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..980b50a74 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/provider/ChecksumProvider.java @@ -0,0 +1,90 @@ +/** + * + * 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..8570188a6 --- /dev/null +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/provider/JingleFileTransferProvider.java @@ -0,0 +1,120 @@ +/** + * + * 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.JingleFileTransfer; +import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChild; +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/bytestreams/socks5/Socks5BytestreamManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java index 38c0a6261..247e4de7e 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java @@ -538,7 +538,7 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream * @throws NotConnectedException * @throws InterruptedException */ - private List determineProxies() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + public List determineProxies() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { XMPPConnection connection = connection(); ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection); @@ -634,7 +634,7 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream * @return the stream host information of the local SOCKS5 proxy or null if local SOCKS5 proxy * is not running */ - private List getLocalStreamHost() { + public List getLocalStreamHost() { XMPPConnection connection = connection(); // get local proxy singleton Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy(); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamSession.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamSession.java index 06cf408ad..c44b85f2a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamSession.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamSession.java @@ -37,7 +37,7 @@ public class Socks5BytestreamSession implements BytestreamSession { /* flag to indicate if this session is a direct or mediated connection */ private final boolean isDirect; - protected Socks5BytestreamSession(Socket socket, boolean isDirect) { + public Socks5BytestreamSession(Socket socket, boolean isDirect) { this.socket = socket; this.isDirect = isDirect; } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java index 1c7b43670..15df6af4b 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java @@ -46,7 +46,7 @@ import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost; * * @author Henning Staib */ -class Socks5Client { +public class Socks5Client { private static final Logger LOGGER = Logger.getLogger(Socks5Client.class.getName()); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiator.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiator.java index 53d744776..993aaa829 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiator.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiator.java @@ -42,7 +42,7 @@ import org.jxmpp.jid.Jid; * * @author Henning Staib */ -class Socks5ClientForInitiator extends Socks5Client { +public class Socks5ClientForInitiator extends Socks5Client { /* the XMPP connection used to communicate with the SOCKS5 proxy */ private WeakReference connection; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java index 6f1b4bc3b..f7e89270a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java @@ -348,7 +348,7 @@ public final class Socks5Proxy { * * @param digest to be added to the list of allowed transfers */ - protected void addTransfer(String digest) { + public void addTransfer(String digest) { this.allowedConnections.add(digest); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Utils.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Utils.java index 0053cf362..bca0c5727 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Utils.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Utils.java @@ -29,7 +29,7 @@ import org.jxmpp.jid.Jid; * * @author Henning Staib */ -class Socks5Utils { +public class Socks5Utils { /** * Returns a SHA-1 digest of the given parameters as specified in INSTANCES = new WeakHashMap<>(); + private static final ExecutorService threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + + public static ExecutorService getThreadPool() { + return threadPool; + } + public static synchronized JingleManager getInstanceFor(XMPPConnection connection) { JingleManager jingleManager = INSTANCES.get(connection); if (jingleManager == null) { @@ -54,45 +65,53 @@ public final class JingleManager extends Manager { private final Map jingleSessionHandlers = new ConcurrentHashMap<>(); + private final JingleUtil jutil; + private JingleManager(XMPPConnection connection) { super(connection); + jutil = new JingleUtil(connection); + connection.registerIQRequestHandler( - new AbstractIqRequestHandler(Jingle.ELEMENT, Jingle.NAMESPACE, Type.set, Mode.async) { - @Override - public IQ handleIQRequest(IQ iqRequest) { - final Jingle jingle = (Jingle) iqRequest; + new AbstractIqRequestHandler(Jingle.ELEMENT, Jingle.NAMESPACE, Type.set, Mode.async) { + @Override + public IQ handleIQRequest(IQ iqRequest) { + final Jingle jingle = (Jingle) iqRequest; - if (jingle.getContents().isEmpty()) { - Jid from = jingle.getFrom(); - assert (from != null); - FullJid fullFrom = from.asFullJidOrThrow(); - String sid = jingle.getSid(); - FullJidAndSessionId fullJidAndSessionId = new FullJidAndSessionId(fullFrom, sid); - JingleSessionHandler jingleSessionHandler = jingleSessionHandlers.get(fullJidAndSessionId); - if (jingleSessionHandler == null) { - // TODO handle non existing jingle session handler. - return null; - } - return jingleSessionHandler.handleJingleSessionRequest(jingle, sid); - } + FullJid fullFrom = jingle.getFrom().asFullJidOrThrow(); + String sid = jingle.getSid(); + FullJidAndSessionId fullJidAndSessionId = new FullJidAndSessionId(fullFrom, sid); - if (jingle.getContents().size() > 1) { - LOGGER.severe("Jingle IQs with more then one content element are currently not supported by Smack"); - return null; - } + JingleSessionHandler sessionHandler = jingleSessionHandlers.get(fullJidAndSessionId); + if (sessionHandler != null) { + //Handle existing session + return sessionHandler.handleJingleSessionRequest(jingle); + } - JingleContent content = jingle.getContents().get(0); - JingleContentDescription description = content.getDescription(); - JingleHandler jingleDescriptionHandler = descriptionHandlers.get( - description.getNamespace()); - if (jingleDescriptionHandler == null) { - // TODO handle non existing content description handler. - return null; - } - return jingleDescriptionHandler.handleJingleRequest(jingle); + if (jingle.getAction() == JingleAction.session_initiate) { + + JingleContent content = jingle.getContents().get(0); + JingleContentDescription description = content.getDescription(); + JingleHandler jingleDescriptionHandler = descriptionHandlers.get( + description.getNamespace()); + + if (jingleDescriptionHandler == null) { + //Unsupported Application + LOGGER.log(Level.WARNING, "Unsupported Jingle application."); + return jutil.createSessionTerminateUnsupportedApplications(fullFrom, sid); } - }); + return jingleDescriptionHandler.handleJingleRequest(jingle); + } + + //Unknown session + LOGGER.log(Level.WARNING, "Unknown session."); + return jutil.createErrorUnknownSession(jingle); + } + }); + //Register transports. + JingleTransportMethodManager transportMethodManager = JingleTransportMethodManager.getInstanceFor(connection); + transportMethodManager.registerTransportManager(JingleIBBTransportManager.getInstanceFor(connection)); + transportMethodManager.registerTransportManager(JingleS5BTransportManager.getInstanceFor(connection)); } public JingleHandler registerDescriptionHandler(String namespace, JingleHandler handler) { @@ -109,30 +128,7 @@ public final class JingleManager extends Manager { return jingleSessionHandlers.remove(fullJidAndSessionId); } - private static final class FullJidAndSessionId { - final FullJid fullJid; - final String sessionId; - - private FullJidAndSessionId(FullJid fullJid, String sessionId) { - this.fullJid = fullJid; - this.sessionId = sessionId; - } - - @Override - public int hashCode() { - int hashCode = 31 * fullJid.hashCode(); - hashCode = 31 * hashCode + sessionId.hashCode(); - return hashCode; - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof FullJidAndSessionId)) { - return false; - } - FullJidAndSessionId otherFullJidAndSessionId = (FullJidAndSessionId) other; - return fullJid.equals(otherFullJidAndSessionId.fullJid) - && sessionId.equals(otherFullJidAndSessionId.sessionId); - } + public static String randomId() { + return StringUtils.randomString(24); } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSession.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSession.java index e86223371..9bd71d81e 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSession.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSession.java @@ -16,28 +16,108 @@ */ package org.jivesoftware.smackx.jingle; -import org.jxmpp.jid.Jid; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.Future; +import java.util.logging.Logger; -// TODO: Is this class still required? If not, then remove it. -public class JingleSession { +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.jingle.element.Jingle; +import org.jivesoftware.smackx.jingle.element.JingleContent; +import org.jivesoftware.smackx.jingle.transports.JingleTransportSession; - private final Jid initiator; +import org.jxmpp.jid.FullJid; - private final Jid responder; +public abstract class JingleSession implements JingleSessionHandler { + private static final Logger LOGGER = Logger.getLogger(JingleSession.class.getName()); + protected HashSet failedTransportMethods = new HashSet<>(); - private final String sid; + protected final FullJid local; - public JingleSession(Jid initiator, Jid responder, String sid) { - this.initiator = initiator; - this.responder = responder; + protected final FullJid remote; + + protected final Role role; + + protected final String sid; + + protected final List contents = new ArrayList<>(); + + protected ArrayList> queued = new ArrayList<>(); + protected JingleTransportSession transportSession; + + public JingleSession(FullJid initiator, FullJid responder, Role role, String sid) { + this(initiator, responder, role, sid, null); + } + + public JingleSession(FullJid initiator, FullJid responder, Role role, String sid, List contents) { + if (role == Role.initiator) { + this.local = initiator; + this.remote = responder; + } else { + this.local = responder; + this.remote = initiator; + } this.sid = sid; + this.role = role; + + if (contents != null) { + this.contents.addAll(contents); + } + } + + public FullJid getInitiator() { + return isInitiator() ? local : remote; + } + + public boolean isInitiator() { + return role == Role.initiator; + } + + public FullJid getResponder() { + return isResponder() ? local : remote; + } + + public boolean isResponder() { + return role == Role.responder; + } + + public FullJid getRemote() { + return remote; + } + + public FullJid getLocal() { + return local; + } + + public String getSessionId() { + return sid; + } + + public FullJidAndSessionId getFullJidAndSessionId() { + return new FullJidAndSessionId(remote, sid); + } + + public List getContents() { + return contents; + } + + public JingleTransportSession getTransportSession() { + return transportSession; + } + + protected void setTransportSession(JingleTransportSession transportSession) { + this.transportSession = transportSession; } @Override public int hashCode() { - int hashCode = 31 + initiator.hashCode(); - hashCode = 31 * hashCode + responder.hashCode(); - hashCode = 31 * hashCode + sid.hashCode(); + int hashCode = 31 + getInitiator().hashCode(); + hashCode = 31 * hashCode + getResponder().hashCode(); + hashCode = 31 * hashCode + getSessionId().hashCode(); return hashCode; } @@ -48,7 +128,113 @@ public class JingleSession { } JingleSession otherJingleSession = (JingleSession) other; - return initiator.equals(otherJingleSession.initiator) && responder.equals(otherJingleSession.responder) - && sid.equals(otherJingleSession.sid); + return getInitiator().equals(otherJingleSession.getInitiator()) + && getResponder().equals(otherJingleSession.getResponder()) + && sid.equals(otherJingleSession.sid); } + + @Override + public IQ handleJingleSessionRequest(Jingle jingle) { + try { + switch (jingle.getAction()) { + case content_accept: + return handleContentAccept(jingle); + case content_add: + return handleContentAdd(jingle); + case content_modify: + return handleContentModify(jingle); + case content_reject: + return handleContentReject(jingle); + case content_remove: + return handleContentRemove(jingle); + case description_info: + return handleDescriptionInfo(jingle); + case session_info: + return handleSessionInfo(jingle); + case security_info: + return handleSecurityInfo(jingle); + case session_accept: + return handleSessionAccept(jingle); + case transport_accept: + return handleTransportAccept(jingle); + case transport_info: + return transportSession.handleTransportInfo(jingle); + case session_initiate: + return handleSessionInitiate(jingle); + case transport_reject: + return handleTransportReject(jingle); + case session_terminate: + return handleSessionTerminate(jingle); + case transport_replace: + return handleTransportReplace(jingle); + default: + return IQ.createResultIQ(jingle); + } + } catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) { + return null; //TODO: + } + } + + protected IQ handleSessionInitiate(Jingle sessionInitiate) throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException { + return IQ.createResultIQ(sessionInitiate); + } + + protected IQ handleSessionTerminate(Jingle sessionTerminate) { + return IQ.createResultIQ(sessionTerminate); + } + + protected IQ handleSessionInfo(Jingle sessionInfo) { + return IQ.createResultIQ(sessionInfo); + } + + protected IQ handleSessionAccept(Jingle sessionAccept) throws SmackException.NotConnectedException, InterruptedException { + return IQ.createResultIQ(sessionAccept); + } + + protected IQ handleContentAdd(Jingle contentAdd) { + return IQ.createResultIQ(contentAdd); + } + + protected IQ handleContentAccept(Jingle contentAccept) { + return IQ.createResultIQ(contentAccept); + } + + protected IQ handleContentModify(Jingle contentModify) { + return IQ.createResultIQ(contentModify); + } + + protected IQ handleContentReject(Jingle contentReject) { + return IQ.createResultIQ(contentReject); + } + + protected IQ handleContentRemove(Jingle contentRemove) { + return IQ.createResultIQ(contentRemove); + } + + protected IQ handleDescriptionInfo(Jingle descriptionInfo) { + return IQ.createResultIQ(descriptionInfo); + } + + protected IQ handleSecurityInfo(Jingle securityInfo) { + return IQ.createResultIQ(securityInfo); + } + + protected IQ handleTransportAccept(Jingle transportAccept) throws SmackException.NotConnectedException, InterruptedException { + return IQ.createResultIQ(transportAccept); + } + + protected IQ handleTransportReplace(Jingle transportReplace) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + return IQ.createResultIQ(transportReplace); + } + + protected IQ handleTransportReject(Jingle transportReject) { + return IQ.createResultIQ(transportReject); + } + + public abstract XMPPConnection getConnection(); + + public abstract void onTransportMethodFailed(String namespace); + } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSessionHandler.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSessionHandler.java index b830bdbe6..a57ede3ff 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSessionHandler.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSessionHandler.java @@ -21,6 +21,6 @@ import org.jivesoftware.smackx.jingle.element.Jingle; public interface JingleSessionHandler { - IQ handleJingleSessionRequest(Jingle jingle, String sessionId); + IQ handleJingleSessionRequest(Jingle jingle); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleTransportMethodManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleTransportMethodManager.java new file mode 100644 index 000000000..19206384b --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleTransportMethodManager.java @@ -0,0 +1,132 @@ +/** + * + * 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 java.util.HashMap; +import java.util.Iterator; +import java.util.Set; +import java.util.WeakHashMap; + +import org.jivesoftware.smack.Manager; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smackx.jingle.element.Jingle; +import org.jivesoftware.smackx.jingle.element.JingleContent; +import org.jivesoftware.smackx.jingle.element.JingleContentTransport; +import org.jivesoftware.smackx.jingle.transports.JingleTransportManager; +import org.jivesoftware.smackx.jingle.transports.jingle_ibb.element.JingleIBBTransport; +import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport; + +/** + * Manager where TransportMethods are registered. + */ +public final class JingleTransportMethodManager extends Manager { + + private static final WeakHashMap INSTANCES = new WeakHashMap<>(); + + private final HashMap> transportManagers = new HashMap<>(); + + private static final String[] transportPreference = new String[] { + JingleS5BTransport.NAMESPACE_V1, + JingleIBBTransport.NAMESPACE_V1 + }; + + private JingleTransportMethodManager(XMPPConnection connection) { + super(connection); + } + + public static JingleTransportMethodManager getInstanceFor(XMPPConnection connection) { + JingleTransportMethodManager manager = INSTANCES.get(connection); + if (manager == null) { + manager = new JingleTransportMethodManager(connection); + INSTANCES.put(connection, manager); + } + return manager; + } + + public void registerTransportManager(JingleTransportManager manager) { + transportManagers.put(manager.getNamespace(), manager); + } + + public static JingleTransportManager getTransportManager(XMPPConnection connection, String namespace) { + return getInstanceFor(connection).getTransportManager(namespace); + } + + public JingleTransportManager getTransportManager(String namespace) { + return transportManagers.get(namespace); + } + + public static JingleTransportManager getTransportManager(XMPPConnection connection, Jingle request) { + return getInstanceFor(connection).getTransportManager(request); + } + public JingleTransportManager getTransportManager(Jingle request) { + + JingleContent content = request.getContents().get(0); + if (content == null) { + return null; + } + + JingleContentTransport transport = content.getJingleTransport(); + if (transport == null) { + return null; + } + + return getTransportManager(transport.getNamespace()); + } + + public JingleTransportManager getBestAvailableTransportManager(XMPPConnection connection) { + return getInstanceFor(connection).getBestAvailableTransportManager(); + } + + public JingleTransportManager getBestAvailableTransportManager() { + JingleTransportManager tm; + for (String ns : transportPreference) { + tm = getTransportManager(ns); + if (tm != null) { + return tm; + } + } + + Iterator it = transportManagers.keySet().iterator(); + if (it.hasNext()) { + return getTransportManager(it.next()); + } + + return null; + } + + public JingleTransportManager getBestAvailableTransportManager(Set except) { + JingleTransportManager tm; + for (String ns : transportPreference) { + tm = getTransportManager(ns); + if (tm != null) { + if (except.contains(tm.getNamespace())) { + continue; + } + return tm; + } + } + + for (String ns : transportManagers.keySet()) { + if (except.contains(ns)) { + continue; + } + return getTransportManager(ns); + } + + return null; + } +} 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..8f47fea94 --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleUtil.java @@ -0,0 +1,508 @@ +/** + * + * 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 create and send jingle stanzas. + */ +public class JingleUtil { + + private final XMPPConnection connection; + + public JingleUtil(XMPPConnection connection) { + this.connection = connection; + } + + public Jingle createSessionInitiate(FullJid recipient, + String sessionId, + JingleContent.Creator contentCreator, + String contentName, + JingleContent.Senders contentSenders, + JingleContentDescription description, + JingleContentTransport transport) { + + 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) + .setTransport(transport); + + Jingle jingle = jb.addJingleContent(cb.build()).build(); + jingle.setFrom(connection.getUser()); + jingle.setTo(recipient); + + return jingle; + } + + public Jingle createSessionInitiateFileOffer(FullJid recipient, + String sessionId, + JingleContent.Creator contentCreator, + String contentName, + JingleContentDescription description, + JingleContentTransport transport) { + return createSessionInitiate(recipient, sessionId, contentCreator, contentName, + JingleContent.Senders.initiator, description, transport); + } + + public IQ sendSessionInitiateFileOffer(FullJid recipient, + String sessionId, + JingleContent.Creator contentCreator, + String contentName, + JingleContentDescription description, + JingleContentTransport transport) + throws SmackException.NotConnectedException, InterruptedException, + XMPPException.XMPPErrorException, SmackException.NoResponseException { + + Jingle jingle = createSessionInitiateFileOffer(recipient, sessionId, contentCreator, contentName, description, transport); + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + 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 jingle = createSessionInitiate(recipient, sessionId, contentCreator, contentName, contentSenders, + description, transport); + + return connection.createStanzaCollectorAndSend(jingle).nextResult(); + } + + public Jingle createSessionAccept(FullJid recipient, + String sessionId, + JingleContent.Creator contentCreator, + String contentName, + JingleContent.Senders contentSenders, + JingleContentDescription description, + JingleContentTransport transport) { + + 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) + .setTransport(transport); + + Jingle jingle = jb.addJingleContent(cb.build()).build(); + jingle.setTo(recipient); + jingle.setFrom(connection.getUser()); + + return jingle; + } + + 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 jingle = createSessionAccept(recipient, sessionId, contentCreator, contentName, contentSenders, + description, transport); + + return connection.createStanzaCollectorAndSend(jingle).nextResult(); + } + + public Jingle createSessionTerminate(FullJid recipient, String sessionId, JingleReason reason) { + 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 jingle; + } + + public Jingle createSessionTerminate(FullJid recipient, String sessionId, JingleReason.Reason reason) { + return createSessionTerminate(recipient, sessionId, new JingleReason(reason)); + } + + 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 jingle = createSessionTerminate(recipient, sessionId, reason); + + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public Jingle createSessionTerminateDecline(FullJid recipient, String sessionId) { + return createSessionTerminate(recipient, sessionId, JingleReason.Reason.decline); + } + + public IQ sendSessionTerminateDecline(FullJid recipient, String sessionId) + throws SmackException.NotConnectedException, InterruptedException, + XMPPException.XMPPErrorException, SmackException.NoResponseException { + + Jingle jingle = createSessionTerminateDecline(recipient, sessionId); + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public Jingle createSessionTerminateSuccess(FullJid recipient, String sessionId) { + return createSessionTerminate(recipient, sessionId, JingleReason.Reason.success); + } + + public IQ sendSessionTerminateSuccess(FullJid recipient, String sessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + + Jingle jingle = createSessionTerminateSuccess(recipient, sessionId); + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public Jingle createSessionTerminateBusy(FullJid recipient, String sessionId) { + return createSessionTerminate(recipient, sessionId, JingleReason.Reason.busy); + } + + public IQ sendSessionTerminateBusy(FullJid recipient, String sessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + + Jingle jingle = createSessionTerminateBusy(recipient, sessionId); + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public Jingle createSessionTerminateAlternativeSession(FullJid recipient, String sessionId, String altSessionId) { + return createSessionTerminate(recipient, sessionId, JingleReason.AlternativeSession(altSessionId)); + } + + public IQ sendSessionTerminateAlternativeSession(FullJid recipient, String sessionId, String altSessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + + Jingle jingle = createSessionTerminateAlternativeSession(recipient, sessionId, altSessionId); + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public Jingle createSessionTerminateCancel(FullJid recipient, String sessionId) { + return createSessionTerminate(recipient, sessionId, JingleReason.Reason.cancel); + } + + public IQ sendSessionTerminateCancel(FullJid recipient, + String sessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + + Jingle jingle = createSessionTerminateCancel(recipient, sessionId); + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public Jingle createSessionTerminateContentCancel(FullJid recipient, String sessionId, + JingleContent.Creator contentCreator, String contentName) { + 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 jingle; + } + + public IQ sendSessionTerminateContentCancel(FullJid recipient, String sessionId, + JingleContent.Creator contentCreator, String contentName) + throws SmackException.NotConnectedException, InterruptedException, + XMPPException.XMPPErrorException, SmackException.NoResponseException { + Jingle jingle = createSessionTerminateContentCancel(recipient, sessionId, contentCreator, contentName); + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public Jingle createSessionTerminateUnsupportedTransports(FullJid recipient, String sessionId) { + return createSessionTerminate(recipient, sessionId, JingleReason.Reason.unsupported_transports); + } + + public IQ sendSessionTerminateUnsupportedTransports(FullJid recipient, String sessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + Jingle jingle = createSessionTerminateUnsupportedTransports(recipient, sessionId); + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public Jingle createSessionTerminateFailedTransport(FullJid recipient, String sessionId) { + return createSessionTerminate(recipient, sessionId, JingleReason.Reason.failed_transport); + } + + public IQ sendSessionTerminateFailedTransport(FullJid recipient, String sessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + Jingle jingle = createSessionTerminateFailedTransport(recipient, sessionId); + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public Jingle createSessionTerminateUnsupportedApplications(FullJid recipient, String sessionId) { + return createSessionTerminate(recipient, sessionId, JingleReason.Reason.unsupported_applications); + } + + public IQ sendSessionTerminateUnsupportedApplications(FullJid recipient, String sessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + Jingle jingle = createSessionTerminateUnsupportedApplications(recipient, sessionId); + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public Jingle createSessionTerminateFailedApplication(FullJid recipient, String sessionId) { + return createSessionTerminate(recipient, sessionId, JingleReason.Reason.failed_application); + } + + public IQ sendSessionTerminateFailedApplication(FullJid recipient, String sessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + Jingle jingle = createSessionTerminateFailedApplication(recipient, sessionId); + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public Jingle createSessionTerminateIncompatibleParameters(FullJid recipient, String sessionId) { + return createSessionTerminate(recipient, sessionId, JingleReason.Reason.incompatible_parameters); + } + + public IQ sendSessionTerminateIncompatibleParameters(FullJid recipient, String sessionId) + throws InterruptedException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + Jingle jingle = createSessionTerminateIncompatibleParameters(recipient, sessionId); + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public IQ sendContentRejectFileNotAvailable(FullJid recipient, String sessionId, JingleContentDescription description) { + return null; //TODO Later + } + + public Jingle createSessionPing(FullJid recipient, String sessionId) { + Jingle.Builder jb = Jingle.getBuilder(); + jb.setSessionId(sessionId) + .setAction(JingleAction.session_info); + + Jingle jingle = jb.build(); + jingle.setFrom(connection.getUser()); + jingle.setTo(recipient); + + return jingle; + } + + public IQ sendSessionPing(FullJid recipient, String sessionId) + throws SmackException.NotConnectedException, InterruptedException, + XMPPException.XMPPErrorException, SmackException.NoResponseException { + Jingle jingle = createSessionPing(recipient, sessionId); + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public IQ createAck(Jingle jingle) { + return IQ.createResultIQ(jingle); + } + + public void sendAck(Jingle jingle) throws SmackException.NotConnectedException, InterruptedException { + connection.sendStanza(createAck(jingle)); + } + + public Jingle createTransportReplace(FullJid recipient, FullJid initiator, String sessionId, + JingleContent.Creator contentCreator, String contentName, + JingleContentTransport transport) { + Jingle.Builder jb = Jingle.getBuilder(); + jb.setInitiator(initiator) + .setSessionId(sessionId) + .setAction(JingleAction.transport_replace); + + JingleContent.Builder cb = JingleContent.getBuilder(); + cb.setName(contentName).setCreator(contentCreator).setTransport(transport); + Jingle jingle = jb.addJingleContent(cb.build()).build(); + + jingle.setTo(recipient); + jingle.setFrom(connection.getUser()); + + return jingle; + } + + public IQ sendTransportReplace(FullJid recipient, FullJid initiator, String sessionId, + JingleContent.Creator contentCreator, String contentName, + JingleContentTransport transport) + throws SmackException.NotConnectedException, InterruptedException, + XMPPException.XMPPErrorException, SmackException.NoResponseException { + Jingle jingle = createTransportReplace(recipient, initiator, sessionId, contentCreator, contentName, transport); + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public Jingle createTransportAccept(FullJid recipient, FullJid initiator, String sessionId, + JingleContent.Creator contentCreator, String contentName, + JingleContentTransport transport) { + Jingle.Builder jb = Jingle.getBuilder(); + jb.setAction(JingleAction.transport_accept) + .setInitiator(initiator) + .setSessionId(sessionId); + + JingleContent.Builder cb = JingleContent.getBuilder(); + cb.setCreator(contentCreator).setName(contentName).setTransport(transport); + + Jingle jingle = jb.addJingleContent(cb.build()).build(); + jingle.setTo(recipient); + jingle.setFrom(connection.getUser()); + + return jingle; + } + + public IQ sendTransportAccept(FullJid recipient, FullJid initiator, String sessionId, + JingleContent.Creator contentCreator, String contentName, + JingleContentTransport transport) + throws SmackException.NotConnectedException, InterruptedException, + XMPPException.XMPPErrorException, SmackException.NoResponseException { + Jingle jingle = createTransportAccept(recipient, initiator, sessionId, contentCreator, contentName, transport); + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + public Jingle createTransportReject(FullJid recipient, FullJid initiator, String sessionId, + JingleContent.Creator contentCreator, String contentName, + JingleContentTransport transport) { + Jingle.Builder jb = Jingle.getBuilder(); + jb.setAction(JingleAction.transport_reject) + .setInitiator(initiator) + .setSessionId(sessionId); + + JingleContent.Builder cb = JingleContent.getBuilder(); + cb.setCreator(contentCreator).setName(contentName).setTransport(transport); + + Jingle jingle = jb.addJingleContent(cb.build()).build(); + jingle.setTo(recipient); + jingle.setFrom(connection.getUser()); + + return jingle; + } + + public IQ sendTransportReject(FullJid recipient, FullJid initiator, String sessionId, + JingleContent.Creator contentCreator, String contentName, + JingleContentTransport transport) + throws SmackException.NotConnectedException, InterruptedException, + XMPPException.XMPPErrorException, SmackException.NoResponseException { + Jingle jingle = createTransportReject(recipient, initiator, sessionId, contentCreator, contentName, transport); + return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow(); + } + + /* + * #################################################################################################### + */ + + public IQ createErrorUnknownSession(Jingle request) { + XMPPError.Builder error = XMPPError.getBuilder(); + error.setCondition(XMPPError.Condition.item_not_found) + .addExtension(JingleError.UNKNOWN_SESSION); + return IQ.createErrorResponse(request, error); + } + + public void sendErrorUnknownSession(Jingle request) + throws SmackException.NotConnectedException, InterruptedException { + connection.sendStanza(createErrorUnknownSession(request)); + } + + public IQ createErrorUnknownInitiator(Jingle request) { + return IQ.createErrorResponse(request, XMPPError.Condition.service_unavailable); + } + + public void sendErrorUnknownInitiator(Jingle request) + throws SmackException.NotConnectedException, InterruptedException { + connection.sendStanza(createErrorUnknownInitiator(request)); + } + + public IQ createErrorUnsupportedInfo(Jingle request) { + XMPPError.Builder error = XMPPError.getBuilder(); + error.setCondition(XMPPError.Condition.feature_not_implemented) + .addExtension(JingleError.UNSUPPORTED_INFO); + return IQ.createErrorResponse(request, error); + } + + public void sendErrorUnsupportedInfo(Jingle request) + throws SmackException.NotConnectedException, InterruptedException { + connection.sendStanza(createErrorUnsupportedInfo(request)); + } + + public IQ createErrorTieBreak(Jingle request) { + XMPPError.Builder error = XMPPError.getBuilder(); + error.setCondition(XMPPError.Condition.conflict) + .addExtension(JingleError.TIE_BREAK); + return IQ.createErrorResponse(request, error); + } + + public void sendErrorTieBreak(Jingle request) + throws SmackException.NotConnectedException, InterruptedException { + connection.sendStanza(createErrorTieBreak(request)); + } + + public IQ createErrorOutOfOrder(Jingle request) { + XMPPError.Builder error = XMPPError.getBuilder(); + error.setCondition(XMPPError.Condition.unexpected_request) + .addExtension(JingleError.OUT_OF_ORDER); + return IQ.createErrorResponse(request, error); + } + + public void sendErrorOutOfOrder(Jingle request) + throws SmackException.NotConnectedException, InterruptedException { + connection.sendStanza(createErrorOutOfOrder(request)); + } + + public IQ createErrorMalformedRequest(Jingle request) { + return IQ.createErrorResponse(request, XMPPError.Condition.bad_request); + } + + public void sendErrorMalformedRequest(Jingle request) + throws SmackException.NotConnectedException, InterruptedException { + connection.sendStanza(createErrorMalformedRequest(request)); + } +} 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/Jingle.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java index 8e8b2f580..3106563d8 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java @@ -122,6 +122,10 @@ public final class Jingle extends IQ { return action; } + public JingleReason getReason() { + return reason; + } + /** * Get a List of the contents. * 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..b91eb758b 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; } @@ -124,6 +124,10 @@ public class JingleReason implements NamedElement { return xml; } + public Reason asEnum() { + return reason; + } + public static class AlternativeSession extends JingleReason { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportInitiationCallback.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportInitiationCallback.java new file mode 100644 index 000000000..79932c1e0 --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportInitiationCallback.java @@ -0,0 +1,30 @@ +/** + * + * 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.transports; + +import org.jivesoftware.smackx.bytestreams.BytestreamSession; + +/** + * Callback for bytestream session creation of TransportManagers. + */ +public interface JingleTransportInitiationCallback { + + void onSessionInitiated(BytestreamSession bytestreamSession); + + void onException(Exception e); + +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportInitiationException.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportInitiationException.java new file mode 100644 index 000000000..8971d2821 --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportInitiationException.java @@ -0,0 +1,33 @@ +/** + * + * 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.transports; + +/** + * Created by vanitas on 25.06.17. + */ +public abstract class JingleTransportInitiationException extends Exception { + private static final long serialVersionUID = 1L; + + + public static class ProxyError extends JingleTransportInitiationException { + private static final long serialVersionUID = 1L; + } + + public static class CandidateError extends JingleTransportInitiationException { + private static final long serialVersionUID = 1L; + } +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportManager.java new file mode 100644 index 000000000..75d8797b0 --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportManager.java @@ -0,0 +1,74 @@ +/** + * + * 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.transports; + +import org.jivesoftware.smack.ConnectionListener; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.element.JingleContentTransport; + +/** + * Manager for a JingleTransport method. + * @param JingleContentTransport. + */ +public abstract class JingleTransportManager implements ConnectionListener { + + private final XMPPConnection connection; + + public JingleTransportManager(XMPPConnection connection) { + this.connection = connection; + connection.addConnectionListener(this); + } + + public XMPPConnection getConnection() { + return connection; + } + + public abstract String getNamespace(); + + public abstract JingleTransportSession transportSession(JingleSession jingleSession); + + + @Override + public void connected(XMPPConnection connection) { + } + + @Override + public void connectionClosed() { + + } + + @Override + public void connectionClosedOnError(Exception e) { + + } + + @Override + public void reconnectionSuccessful() { + + } + + @Override + public void reconnectingIn(int seconds) { + + } + + @Override + public void reconnectionFailed(Exception e) { + + } +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportSession.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportSession.java new file mode 100644 index 000000000..fab36419d --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/JingleTransportSession.java @@ -0,0 +1,62 @@ +/** + * + * 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.transports; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.element.Jingle; +import org.jivesoftware.smackx.jingle.element.JingleContent; +import org.jivesoftware.smackx.jingle.element.JingleContentTransport; + +/** + * Created by vanitas on 20.06.17. + */ +public abstract class JingleTransportSession { + protected final JingleSession jingleSession; + protected T ourProposal, theirProposal; + + public JingleTransportSession(JingleSession session) { + this.jingleSession = session; + } + + public abstract T createTransport(); + + public void processJingle(Jingle jingle) { + if (jingle.getContents().size() == 0) { + return; + } + + JingleContent content = jingle.getContents().get(0); + JingleContentTransport t = content.getJingleTransport(); + + if (t != null && t.getNamespace().equals(getNamespace())) { + setTheirProposal(t); + } + } + + public abstract void setTheirProposal(JingleContentTransport transport); + + public abstract void initiateOutgoingSession(JingleTransportInitiationCallback callback); + + public abstract void initiateIncomingSession(JingleTransportInitiationCallback callback); + + public abstract String getNamespace(); + + public abstract IQ handleTransportInfo(Jingle transportInfo); + + public abstract JingleTransportManager transportManager(); +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_ibb/JingleIBBTransportManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_ibb/JingleIBBTransportManager.java new file mode 100644 index 000000000..1c52512ec --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_ibb/JingleIBBTransportManager.java @@ -0,0 +1,64 @@ +/** + * + * 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.transports.jingle_ibb; + +import java.util.WeakHashMap; + +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.provider.JingleContentProviderManager; +import org.jivesoftware.smackx.jingle.transports.JingleTransportManager; +import org.jivesoftware.smackx.jingle.transports.JingleTransportSession; +import org.jivesoftware.smackx.jingle.transports.jingle_ibb.element.JingleIBBTransport; +import org.jivesoftware.smackx.jingle.transports.jingle_ibb.provider.JingleIBBTransportProvider; + +/** + * Manager for Jingle InBandBytestream transports (XEP-0261). + */ +public final class JingleIBBTransportManager extends JingleTransportManager { + + private static final WeakHashMap INSTANCES = new WeakHashMap<>(); + + private JingleIBBTransportManager(XMPPConnection connection) { + super(connection); + JingleContentProviderManager.addJingleContentTransportProvider(getNamespace(), new JingleIBBTransportProvider()); + } + + public static JingleIBBTransportManager getInstanceFor(XMPPConnection connection) { + JingleIBBTransportManager manager = INSTANCES.get(connection); + if (manager == null) { + manager = new JingleIBBTransportManager(connection); + INSTANCES.put(connection, manager); + } + return manager; + } + + @Override + public String getNamespace() { + return JingleIBBTransport.NAMESPACE_V1; + } + + @Override + public JingleTransportSession transportSession(JingleSession jingleSession) { + return new JingleIBBTransportSession(jingleSession); + } + + @Override + public void authenticated(XMPPConnection connection, boolean resumed) { + //Nothing to do. + } +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_ibb/JingleIBBTransportSession.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_ibb/JingleIBBTransportSession.java new file mode 100644 index 000000000..61efc9e1a --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_ibb/JingleIBBTransportSession.java @@ -0,0 +1,115 @@ +/** + * + * 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.transports.jingle_ibb; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.bytestreams.BytestreamListener; +import org.jivesoftware.smackx.bytestreams.BytestreamRequest; +import org.jivesoftware.smackx.bytestreams.BytestreamSession; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.element.Jingle; +import org.jivesoftware.smackx.jingle.element.JingleContentTransport; +import org.jivesoftware.smackx.jingle.transports.JingleTransportInitiationCallback; +import org.jivesoftware.smackx.jingle.transports.JingleTransportManager; +import org.jivesoftware.smackx.jingle.transports.JingleTransportSession; +import org.jivesoftware.smackx.jingle.transports.jingle_ibb.element.JingleIBBTransport; + +public class JingleIBBTransportSession extends JingleTransportSession { + private static final Logger LOGGER = Logger.getLogger(JingleIBBTransportSession.class.getName()); + + private final JingleIBBTransportManager transportManager; + + public JingleIBBTransportSession(JingleSession session) { + super(session); + transportManager = JingleIBBTransportManager.getInstanceFor(session.getConnection()); + } + + @Override + public JingleIBBTransport createTransport() { + + if (theirProposal == null) { + return new JingleIBBTransport(); + } else { + return new JingleIBBTransport(theirProposal.getBlockSize(), theirProposal.getSessionId()); + } + + } + + @Override + public void setTheirProposal(JingleContentTransport transport) { + theirProposal = (JingleIBBTransport) transport; + } + + @Override + public void initiateOutgoingSession(JingleTransportInitiationCallback callback) { + LOGGER.log(Level.INFO, "Initiate Jingle InBandBytestream session."); + + BytestreamSession session; + try { + session = InBandBytestreamManager.getByteStreamManager(jingleSession.getConnection()) + .establishSession(jingleSession.getRemote(), theirProposal.getSessionId()); + callback.onSessionInitiated(session); + } catch (SmackException.NoResponseException | InterruptedException | SmackException.NotConnectedException | XMPPException.XMPPErrorException e) { + callback.onException(e); + } + } + + @Override + public void initiateIncomingSession(final JingleTransportInitiationCallback callback) { + LOGGER.log(Level.INFO, "Await Jingle InBandBytestream session."); + + InBandBytestreamManager.getByteStreamManager(jingleSession.getConnection()).addIncomingBytestreamListener(new BytestreamListener() { + @Override + public void incomingBytestreamRequest(BytestreamRequest request) { + if (request.getFrom().asFullJidIfPossible().equals(jingleSession.getRemote()) + && request.getSessionID().equals(theirProposal.getSessionId())) { + BytestreamSession session; + + try { + session = request.accept(); + } catch (InterruptedException | SmackException | XMPPException.XMPPErrorException e) { + callback.onException(e); + return; + } + callback.onSessionInitiated(session); + } + } + }); + } + + @Override + public String getNamespace() { + return transportManager.getNamespace(); + } + + @Override + public IQ handleTransportInfo(Jingle transportInfo) { + return IQ.createResultIQ(transportInfo); + //TODO + } + + @Override + public JingleTransportManager transportManager() { + return JingleIBBTransportManager.getInstanceFor(jingleSession.getConnection()); + } +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportManager.java new file mode 100644 index 000000000..2274aaac7 --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportManager.java @@ -0,0 +1,234 @@ +/** + * + * 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.transports.jingle_s5b; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy; +import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; +import org.jivesoftware.smackx.jingle.JingleSession; +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.provider.JingleContentProviderManager; +import org.jivesoftware.smackx.jingle.transports.JingleTransportManager; +import org.jivesoftware.smackx.jingle.transports.JingleTransportSession; +import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport; +import org.jivesoftware.smackx.jingle.transports.jingle_s5b.provider.JingleS5BTransportProvider; + +import org.jxmpp.jid.FullJid; +import org.jxmpp.jid.Jid; + +/** + * Manager for Jingle SOCKS5 Bytestream transports (XEP-0261). + */ +public final class JingleS5BTransportManager extends JingleTransportManager { + + private static final Logger LOGGER = Logger.getLogger(JingleS5BTransportManager.class.getName()); + + private static final WeakHashMap INSTANCES = new WeakHashMap<>(); + + private List localStreamHosts = null; + private List availableStreamHosts = null; + + private static boolean useLocalCandidates = true; + private static boolean useExternalCandidates = true; + + private JingleS5BTransportManager(XMPPConnection connection) { + super(connection); + JingleContentProviderManager.addJingleContentTransportProvider(getNamespace(), new JingleS5BTransportProvider()); + } + + public static JingleS5BTransportManager getInstanceFor(XMPPConnection connection) { + JingleS5BTransportManager manager = INSTANCES.get(connection); + if (manager == null) { + manager = new JingleS5BTransportManager(connection); + INSTANCES.put(connection, manager); + } + return manager; + } + + @Override + public String getNamespace() { + return JingleS5BTransport.NAMESPACE_V1; + } + + @Override + public JingleTransportSession transportSession(JingleSession jingleSession) { + return new JingleS5BTransportSession(jingleSession); + } + + private List queryAvailableStreamHosts() throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException { + Socks5BytestreamManager s5m = Socks5BytestreamManager.getBytestreamManager(getConnection()); + List proxies = s5m.determineProxies(); + return determineStreamHostInfo(proxies); + } + + private List queryLocalStreamHosts() { + return Socks5BytestreamManager.getBytestreamManager(getConnection()) + .getLocalStreamHost(); + } + + public List getAvailableStreamHosts() throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException { + if (availableStreamHosts == null) { + availableStreamHosts = queryAvailableStreamHosts(); + } + return availableStreamHosts; + } + + public List getLocalStreamHosts() { + if (localStreamHosts == null) { + localStreamHosts = queryLocalStreamHosts(); + } + return localStreamHosts; + } + + public List determineStreamHostInfo(List proxies) { + XMPPConnection connection = getConnection(); + List streamHosts = new ArrayList<>(); + + Iterator iterator = proxies.iterator(); + while (iterator.hasNext()) { + Jid proxy = iterator.next(); + Bytestream request = new Bytestream(); + request.setType(IQ.Type.get); + request.setTo(proxy); + try { + Bytestream response = connection.createStanzaCollectorAndSend(request).nextResultOrThrow(); + streamHosts.addAll(response.getStreamHosts()); + } + catch (Exception e) { + iterator.remove(); + } + } + + return streamHosts; + } + + + @Override + public void authenticated(XMPPConnection connection, boolean resumed) { + if (!resumed) try { + Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); + if (!socks5Proxy.isRunning()) { + socks5Proxy.start(); + } + localStreamHosts = queryLocalStreamHosts(); + availableStreamHosts = queryAvailableStreamHosts(); + } catch (InterruptedException | SmackException.NoResponseException | SmackException.NotConnectedException | XMPPException.XMPPErrorException e) { + LOGGER.log(Level.WARNING, "Could not query available StreamHosts: " + e, e); + } + } + + public Jingle createCandidateUsed(FullJid recipient, FullJid initiator, String sessionId, JingleContent.Senders contentSenders, + JingleContent.Creator contentCreator, String contentName, String streamId, + String candidateId) { + Jingle.Builder jb = Jingle.getBuilder(); + jb.setSessionId(sessionId).setInitiator(initiator).setAction(JingleAction.transport_info); + + JingleContent.Builder cb = JingleContent.getBuilder(); + cb.setName(contentName).setCreator(contentCreator).setSenders(contentSenders); + + JingleS5BTransport.Builder tb = JingleS5BTransport.getBuilder(); + tb.setCandidateUsed(candidateId).setStreamId(streamId); + + Jingle jingle = jb.addJingleContent(cb.setTransport(tb.build()).build()).build(); + jingle.setFrom(getConnection().getUser().asFullJidOrThrow()); + jingle.setTo(recipient); + + return jingle; + } + + public Jingle createCandidateError(FullJid remote, FullJid initiator, String sessionId, JingleContent.Senders senders, JingleContent.Creator creator, String name, String streamId) { + Jingle.Builder jb = Jingle.getBuilder(); + jb.setSessionId(sessionId).setInitiator(initiator).setAction(JingleAction.transport_info); + + JingleContent.Builder cb = JingleContent.getBuilder(); + cb.setName(name).setCreator(creator).setSenders(senders); + + JingleS5BTransport.Builder tb = JingleS5BTransport.getBuilder(); + tb.setCandidateError().setStreamId(streamId); + + Jingle jingle = jb.addJingleContent(cb.setTransport(tb.build()).build()).build(); + jingle.setFrom(getConnection().getUser().asFullJidOrThrow()); + jingle.setTo(remote); + + return jingle; + } + + public Jingle createProxyError(FullJid remote, FullJid initiator, String sessionId, + JingleContent.Senders senders, JingleContent.Creator creator, + String name, String streamId) { + Jingle.Builder jb = Jingle.getBuilder(); + jb.setSessionId(sessionId).setAction(JingleAction.transport_info).setInitiator(initiator); + + JingleContent.Builder cb = JingleContent.getBuilder(); + cb.setSenders(senders).setCreator(creator).setName(name); + + JingleS5BTransport.Builder tb = JingleS5BTransport.getBuilder(); + tb.setStreamId(sessionId).setProxyError().setStreamId(streamId); + + Jingle jingle = jb.addJingleContent(cb.setTransport(tb.build()).build()).build(); + jingle.setTo(remote); + jingle.setFrom(getConnection().getUser().asFullJidOrThrow()); + return jingle; + } + + public Jingle createCandidateActivated(FullJid remote, FullJid initiator, String sessionId, + JingleContent.Senders senders, JingleContent.Creator creator, + String name, String streamId, String candidateId) { + Jingle.Builder jb = Jingle.getBuilder(); + jb.setInitiator(initiator).setSessionId(sessionId).setAction(JingleAction.transport_info); + + JingleContent.Builder cb = JingleContent.getBuilder(); + cb.setName(name).setCreator(creator).setSenders(senders); + + JingleS5BTransport.Builder tb = JingleS5BTransport.getBuilder(); + tb.setStreamId(streamId).setCandidateActivated(candidateId); + + Jingle jingle = jb.addJingleContent(cb.setTransport(tb.build()).build()).build(); + jingle.setFrom(getConnection().getUser().asFullJidOrThrow()); + jingle.setTo(remote); + return jingle; + } + + public static void setUseLocalCandidates(boolean localCandidates) { + JingleS5BTransportManager.useLocalCandidates = localCandidates; + } + + public static void setUseExternalCandidates(boolean externalCandidates) { + JingleS5BTransportManager.useExternalCandidates = externalCandidates; + } + + public static boolean isUseLocalCandidates() { + return useLocalCandidates; + } + + public static boolean isUseExternalCandidates() { + return useExternalCandidates; + } +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportSession.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportSession.java new file mode 100644 index 000000000..ba03e1d58 --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportSession.java @@ -0,0 +1,361 @@ +/** + * + * 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.transports.jingle_s5b; + +import java.io.IOException; +import java.net.Socket; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeoutException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5Client; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5ClientForInitiator; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils; +import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; +import org.jivesoftware.smackx.jingle.JingleManager; +import org.jivesoftware.smackx.jingle.JingleSession; +import org.jivesoftware.smackx.jingle.element.Jingle; +import org.jivesoftware.smackx.jingle.element.JingleContent; +import org.jivesoftware.smackx.jingle.element.JingleContentTransport; +import org.jivesoftware.smackx.jingle.element.JingleContentTransportCandidate; +import org.jivesoftware.smackx.jingle.transports.JingleTransportInitiationCallback; +import org.jivesoftware.smackx.jingle.transports.JingleTransportSession; +import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport; +import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportCandidate; +import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportInfo; + +/** + * Handler that handles Jingle Socks5Bytestream transports (XEP-0260). + */ +public class JingleS5BTransportSession extends JingleTransportSession { + private static final Logger LOGGER = Logger.getLogger(JingleS5BTransportSession.class.getName()); + + private JingleTransportInitiationCallback callback; + + public JingleS5BTransportSession(JingleSession jingleSession) { + super(jingleSession); + } + + private UsedCandidate ourChoice, theirChoice; + + @Override + public JingleS5BTransport createTransport() { + if (ourProposal == null) { + ourProposal = createTransport(JingleManager.randomId(), Bytestream.Mode.tcp); + } + return ourProposal; + } + + @Override + public void setTheirProposal(JingleContentTransport transport) { + theirProposal = (JingleS5BTransport) transport; + } + + public JingleS5BTransport createTransport(String sid, Bytestream.Mode mode) { + JingleS5BTransport.Builder jb = JingleS5BTransport.getBuilder() + .setStreamId(sid).setMode(mode).setDestinationAddress( + Socks5Utils.createDigest(sid, jingleSession.getLocal(), jingleSession.getRemote())); + + //Local host + if (JingleS5BTransportManager.isUseLocalCandidates()) { + for (Bytestream.StreamHost host : transportManager().getLocalStreamHosts()) { + jb.addTransportCandidate(new JingleS5BTransportCandidate(host, 100, JingleS5BTransportCandidate.Type.direct)); + } + } + + List remoteHosts = Collections.emptyList(); + if (JingleS5BTransportManager.isUseExternalCandidates()) { + try { + remoteHosts = transportManager().getAvailableStreamHosts(); + } catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) { + LOGGER.log(Level.WARNING, "Could not determine available StreamHosts.", e); + } + } + + for (Bytestream.StreamHost host : remoteHosts) { + jb.addTransportCandidate(new JingleS5BTransportCandidate(host, 0, JingleS5BTransportCandidate.Type.proxy)); + } + + return jb.build(); + } + + public void setTheirTransport(JingleContentTransport transport) { + theirProposal = (JingleS5BTransport) transport; + } + + @Override + public void initiateOutgoingSession(JingleTransportInitiationCallback callback) { + this.callback = callback; + initiateSession(); + } + + @Override + public void initiateIncomingSession(JingleTransportInitiationCallback callback) { + this.callback = callback; + initiateSession(); + } + + private void initiateSession() { + Socks5Proxy.getSocks5Proxy().addTransfer(createTransport().getDestinationAddress()); + JingleContent content = jingleSession.getContents().get(0); + UsedCandidate usedCandidate = chooseFromProposedCandidates(theirProposal); + if (usedCandidate == null) { + ourChoice = CANDIDATE_FAILURE; + Jingle candidateError = transportManager().createCandidateError( + jingleSession.getRemote(), jingleSession.getInitiator(), jingleSession.getSessionId(), + content.getSenders(), content.getCreator(), content.getName(), theirProposal.getStreamId()); + try { + jingleSession.getConnection().sendStanza(candidateError); + } catch (SmackException.NotConnectedException | InterruptedException e) { + LOGGER.log(Level.WARNING, "Could not send candidate-error.", e); + } + } else { + ourChoice = usedCandidate; + Jingle jingle = transportManager().createCandidateUsed(jingleSession.getRemote(), jingleSession.getInitiator(), jingleSession.getSessionId(), + content.getSenders(), content.getCreator(), content.getName(), theirProposal.getStreamId(), ourChoice.candidate.getCandidateId()); + try { + jingleSession.getConnection().createStanzaCollectorAndSend(jingle) + .nextResultOrThrow(); + } catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) { + LOGGER.log(Level.WARNING, "Could not send candidate-used.", e); + } + } + connectIfReady(); + } + + private UsedCandidate chooseFromProposedCandidates(JingleS5BTransport proposal) { + for (JingleContentTransportCandidate c : proposal.getCandidates()) { + JingleS5BTransportCandidate candidate = (JingleS5BTransportCandidate) c; + + try { + return connectToTheirCandidate(candidate); + } catch (InterruptedException | TimeoutException | XMPPException | SmackException | IOException e) { + LOGGER.log(Level.WARNING, "Could not connect to " + candidate.getHost(), e); + } + } + LOGGER.log(Level.WARNING, "Failed to connect to any candidate."); + return null; + } + + private UsedCandidate connectToTheirCandidate(JingleS5BTransportCandidate candidate) + throws InterruptedException, TimeoutException, SmackException, XMPPException, IOException { + Bytestream.StreamHost streamHost = candidate.getStreamHost(); + String address = streamHost.getAddress(); + Socks5Client socks5Client = new Socks5Client(streamHost, theirProposal.getDestinationAddress()); + Socket socket = socks5Client.getSocket(10 * 1000); + LOGGER.log(Level.INFO, "Connected to their StreamHost " + address + " using dstAddr " + + theirProposal.getDestinationAddress()); + return new UsedCandidate(theirProposal, candidate, socket); + } + + private UsedCandidate connectToOurCandidate(JingleS5BTransportCandidate candidate) + throws InterruptedException, TimeoutException, SmackException, XMPPException, IOException { + Bytestream.StreamHost streamHost = candidate.getStreamHost(); + String address = streamHost.getAddress(); + Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator( + streamHost, ourProposal.getDestinationAddress(), jingleSession.getConnection(), + ourProposal.getStreamId(), jingleSession.getRemote()); + Socket socket = socks5Client.getSocket(10 * 1000); + LOGGER.log(Level.INFO, "Connected to our StreamHost " + address + " using dstAddr " + + ourProposal.getDestinationAddress()); + return new UsedCandidate(ourProposal, candidate, socket); + } + + @Override + public String getNamespace() { + return JingleS5BTransport.NAMESPACE_V1; + } + + @Override + public IQ handleTransportInfo(Jingle transportInfo) { + JingleS5BTransportInfo info = (JingleS5BTransportInfo) transportInfo.getContents().get(0).getJingleTransport().getInfo(); + + switch (info.getElementName()) { + case JingleS5BTransportInfo.CandidateUsed.ELEMENT: + return handleCandidateUsed(transportInfo); + + case JingleS5BTransportInfo.CandidateActivated.ELEMENT: + return handleCandidateActivate(transportInfo); + + case JingleS5BTransportInfo.CandidateError.ELEMENT: + return handleCandidateError(transportInfo); + + case JingleS5BTransportInfo.ProxyError.ELEMENT: + return handleProxyError(transportInfo); + } + //We should never go here, but lets be gracious... + return IQ.createResultIQ(transportInfo); + } + + public IQ handleCandidateUsed(Jingle jingle) { + JingleS5BTransportInfo info = (JingleS5BTransportInfo) jingle.getContents().get(0).getJingleTransport().getInfo(); + String candidateId = ((JingleS5BTransportInfo.CandidateUsed) info).getCandidateId(); + theirChoice = new UsedCandidate(ourProposal, ourProposal.getCandidate(candidateId), null); + + if (theirChoice.candidate == null) { + /* + TODO: Booooooh illegal candidateId!! Go home!!!!11elf + */ + } + + connectIfReady(); + + return IQ.createResultIQ(jingle); + } + + public IQ handleCandidateActivate(Jingle jingle) { + LOGGER.log(Level.INFO, "handleCandidateActivate"); + Socks5BytestreamSession bs = new Socks5BytestreamSession(ourChoice.socket, + ourChoice.candidate.getJid().asBareJid().equals(jingleSession.getRemote().asBareJid())); + callback.onSessionInitiated(bs); + return IQ.createResultIQ(jingle); + } + + public IQ handleCandidateError(Jingle jingle) { + theirChoice = CANDIDATE_FAILURE; + connectIfReady(); + return IQ.createResultIQ(jingle); + } + + public IQ handleProxyError(Jingle jingle) { + //TODO + return IQ.createResultIQ(jingle); + } + + /** + * Determine, which candidate (ours/theirs) is the nominated one. + * Connect to this candidate. If it is a proxy and it is ours, activate it and connect. + * If its a proxy and it is theirs, wait for activation. + * If it is not a proxy, just connect. + */ + private void connectIfReady() { + JingleContent content = jingleSession.getContents().get(0); + if (ourChoice == null || theirChoice == null) { + // Not yet ready. + LOGGER.log(Level.INFO, "Not ready."); + return; + } + + if (ourChoice == CANDIDATE_FAILURE && theirChoice == CANDIDATE_FAILURE) { + LOGGER.log(Level.INFO, "Failure."); + jingleSession.onTransportMethodFailed(getNamespace()); + return; + } + + LOGGER.log(Level.INFO, "Ready."); + + //Determine nominated candidate. + UsedCandidate nominated; + if (ourChoice != CANDIDATE_FAILURE && theirChoice != CANDIDATE_FAILURE) { + if (ourChoice.candidate.getPriority() > theirChoice.candidate.getPriority()) { + nominated = ourChoice; + } else if (ourChoice.candidate.getPriority() < theirChoice.candidate.getPriority()) { + nominated = theirChoice; + } else { + nominated = jingleSession.isInitiator() ? ourChoice : theirChoice; + } + } else if (ourChoice != CANDIDATE_FAILURE) { + nominated = ourChoice; + } else { + nominated = theirChoice; + } + + if (nominated == theirChoice) { + LOGGER.log(Level.INFO, "Their choice, so our proposed candidate is used."); + boolean isProxy = nominated.candidate.getType() == JingleS5BTransportCandidate.Type.proxy; + try { + nominated = connectToOurCandidate(nominated.candidate); + } catch (InterruptedException | IOException | XMPPException | SmackException | TimeoutException e) { + LOGGER.log(Level.INFO, "Could not connect to our candidate.", e); + //TODO: Proxy-Error + return; + } + + if (isProxy) { + LOGGER.log(Level.INFO, "Is external proxy. Activate it."); + Bytestream activate = new Bytestream(ourProposal.getStreamId()); + activate.setMode(null); + activate.setType(IQ.Type.set); + activate.setTo(nominated.candidate.getJid()); + activate.setToActivate(jingleSession.getRemote()); + activate.setFrom(jingleSession.getLocal()); + try { + jingleSession.getConnection().createStanzaCollectorAndSend(activate).nextResultOrThrow(); + } catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) { + LOGGER.log(Level.WARNING, "Could not activate proxy.", e); + return; + } + + LOGGER.log(Level.INFO, "Send candidate-activate."); + Jingle candidateActivate = transportManager().createCandidateActivated( + jingleSession.getRemote(), jingleSession.getInitiator(), jingleSession.getSessionId(), + content.getSenders(), content.getCreator(), content.getName(), nominated.transport.getStreamId(), + nominated.candidate.getCandidateId()); + try { + jingleSession.getConnection().createStanzaCollectorAndSend(candidateActivate) + .nextResultOrThrow(); + } catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) { + LOGGER.log(Level.WARNING, "Could not send candidate-activated", e); + return; + } + } + + LOGGER.log(Level.INFO, "Start transmission."); + Socks5BytestreamSession bs = new Socks5BytestreamSession(nominated.socket, !isProxy); + callback.onSessionInitiated(bs); + + } + //Our choice + else { + LOGGER.log(Level.INFO, "Our choice, so their candidate was used."); + boolean isProxy = nominated.candidate.getType() == JingleS5BTransportCandidate.Type.proxy; + if (!isProxy) { + LOGGER.log(Level.INFO, "Direct connection."); + Socks5BytestreamSession bs = new Socks5BytestreamSession(nominated.socket, true); + callback.onSessionInitiated(bs); + } else { + LOGGER.log(Level.INFO, "Our choice was their external proxy. wait for candidate-activate."); + } + } + } + + @Override + public JingleS5BTransportManager transportManager() { + return JingleS5BTransportManager.getInstanceFor(jingleSession.getConnection()); + } + + private static class UsedCandidate { + private final Socket socket; + private final JingleS5BTransport transport; + private final JingleS5BTransportCandidate candidate; + + public UsedCandidate(JingleS5BTransport transport, JingleS5BTransportCandidate candidate, Socket socket) { + this.socket = socket; + this.transport = transport; + this.candidate = candidate; + } + } + + private static final UsedCandidate CANDIDATE_FAILURE = new UsedCandidate(null, null, null); +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/elements/JingleS5BTransport.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/elements/JingleS5BTransport.java index 3d1a3759d..a70b5d9a6 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/elements/JingleS5BTransport.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/elements/JingleS5BTransport.java @@ -93,7 +93,7 @@ public class JingleS5BTransport extends JingleContentTransport { private String streamId; private String dstAddr; private Bytestream.Mode mode; - private ArrayList candidates = new ArrayList<>(); + private final ArrayList candidates = new ArrayList<>(); private JingleContentTransportInfo info; public Builder setStreamId(String sid) { @@ -112,11 +112,22 @@ public class JingleS5BTransport extends JingleContentTransport { } public Builder addTransportCandidate(JingleS5BTransportCandidate candidate) { + if (info != null) { + throw new IllegalStateException("Builder has already an info set. " + + "The transport can only have either an info or transport candidates, not both."); + } this.candidates.add(candidate); return this; } public Builder setTransportInfo(JingleContentTransportInfo info) { + if (!candidates.isEmpty()) { + throw new IllegalStateException("Builder has already at least one candidate set. " + + "The transport can only have either an info or transport candidates, not both."); + } + if (this.info != null) { + throw new IllegalStateException("Builder has already an info set."); + } this.info = info; return this; } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/elements/JingleS5BTransportCandidate.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/elements/JingleS5BTransportCandidate.java index 284fc72e9..b16e5f563 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/elements/JingleS5BTransportCandidate.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/elements/JingleS5BTransportCandidate.java @@ -54,8 +54,12 @@ public final class JingleS5BTransportCandidate extends JingleContentTransportCan Objects.requireNonNull(candidateId); Objects.requireNonNull(host); Objects.requireNonNull(jid); + if (priority < 0) { - throw new IllegalArgumentException("Priority MUST be present and NOT less than 0."); + throw new IllegalArgumentException("Priority MUST NOT be less than 0."); + } + if (port < 0) { + throw new IllegalArgumentException("Port MUST NOT be less than 0."); } this.cid = candidateId; @@ -66,8 +70,8 @@ public final class JingleS5BTransportCandidate extends JingleContentTransportCan this.type = type; } - public JingleS5BTransportCandidate(Bytestream.StreamHost streamHost, int priority) { - this(StringUtils.randomString(24), streamHost.getAddress(), streamHost.getJID(), streamHost.getPort(), priority, Type.proxy); + public JingleS5BTransportCandidate(Bytestream.StreamHost streamHost, int priority, Type type) { + this(StringUtils.randomString(24), streamHost.getAddress(), streamHost.getJID(), streamHost.getPort(), priority, type); } public enum Type { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/package-info.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/package-info.java new file mode 100644 index 000000000..9cfe634a5 --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/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-0261: Jingle In-Band Bytestreams. + */ +package org.jivesoftware.smackx.jingle.transports; diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java index a5b0859b8..a4dd53bb6 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java @@ -102,10 +102,15 @@ public class Socks5ProxyTest { @Test public void shouldPreserveAddressOrderOnInsertions() { Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); - List addresses = new ArrayList(proxy.getLocalAddresses()); - addresses.add("1"); - addresses.add("2"); - addresses.add("3"); + List addresses = new ArrayList<>(proxy.getLocalAddresses()); + + for (int i = 1 ; i <= 3; i++) { + String addr = Integer.toString(i); + if (!addresses.contains(addr)) { + addresses.add(addr); + } + } + for (String address : addresses) { proxy.addLocalAddress(address); } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleContentProviderManagerTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleContentProviderManagerTest.java new file mode 100644 index 000000000..8b9109426 --- /dev/null +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleContentProviderManagerTest.java @@ -0,0 +1,50 @@ +/** + * + * 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 static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertNull; + +import org.jivesoftware.smack.test.util.SmackTestSuite; +import org.jivesoftware.smackx.jingle.provider.JingleContentProviderManager; +import org.jivesoftware.smackx.jingle.transports.jingle_ibb.element.JingleIBBTransport; +import org.jivesoftware.smackx.jingle.transports.jingle_ibb.provider.JingleIBBTransportProvider; +import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport; +import org.jivesoftware.smackx.jingle.transports.jingle_s5b.provider.JingleS5BTransportProvider; + +import org.junit.Test; + +/** + * Tests for the JingleContentProviderManager. + */ +public class JingleContentProviderManagerTest extends SmackTestSuite { + + @Test + public void transportProviderTest() { + assertNull(JingleContentProviderManager.getJingleContentTransportProvider(JingleIBBTransport.NAMESPACE_V1)); + assertNull(JingleContentProviderManager.getJingleContentTransportProvider(JingleS5BTransport.NAMESPACE_V1)); + + JingleIBBTransportProvider ibbProvider = new JingleIBBTransportProvider(); + JingleContentProviderManager.addJingleContentTransportProvider(JingleIBBTransport.NAMESPACE_V1, ibbProvider); + assertEquals(ibbProvider, JingleContentProviderManager.getJingleContentTransportProvider(JingleIBBTransport.NAMESPACE_V1)); + + assertNull(JingleContentProviderManager.getJingleContentTransportProvider(JingleS5BTransport.NAMESPACE_V1)); + JingleS5BTransportProvider s5bProvider = new JingleS5BTransportProvider(); + JingleContentProviderManager.addJingleContentTransportProvider(JingleS5BTransport.NAMESPACE_V1, s5bProvider); + assertEquals(s5bProvider, JingleContentProviderManager.getJingleContentTransportProvider(JingleS5BTransport.NAMESPACE_V1)); + } +} diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleContentTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleContentTest.java index 49bd0fde4..bd1a430a8 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleContentTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleContentTest.java @@ -22,7 +22,6 @@ import static junit.framework.TestCase.assertNotSame; import static junit.framework.TestCase.assertNull; import org.jivesoftware.smack.test.util.SmackTestSuite; - import org.jivesoftware.smackx.jingle.element.JingleContent; import org.junit.Test; @@ -46,7 +45,7 @@ public class JingleContentTest extends SmackTestSuite { } @Test - public void parserTest() { + public void parserTest() throws Exception { JingleContent.Builder builder = JingleContent.getBuilder(); diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleErrorTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleErrorTest.java index d21ecf73d..6ca6d88a9 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleErrorTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleErrorTest.java @@ -19,8 +19,9 @@ package org.jivesoftware.smackx.jingle; import static junit.framework.TestCase.assertEquals; import org.jivesoftware.smack.test.util.SmackTestSuite; - +import org.jivesoftware.smack.test.util.TestUtils; import org.jivesoftware.smackx.jingle.element.JingleError; +import org.jivesoftware.smackx.jingle.provider.JingleErrorProvider; import org.junit.Test; @@ -30,20 +31,37 @@ import org.junit.Test; public class JingleErrorTest extends SmackTestSuite { @Test - public void parserTest() { - assertEquals("", - JingleError.fromString("out-of-order").toXML().toString()); - assertEquals("", - JingleError.fromString("tie-break").toXML().toString()); - assertEquals("", - JingleError.fromString("unknown-session").toXML().toString()); - assertEquals("", - JingleError.fromString("unsupported-info").toXML().toString()); - assertEquals("unknown-session", JingleError.fromString("unknown-session").getMessage()); + public void tieBreakTest() throws Exception { + String xml = ""; + JingleError error = new JingleErrorProvider().parse(TestUtils.getParser(xml)); + assertEquals(xml, error.toXML().toString()); + } + + @Test + public void unknownSessionTest() throws Exception { + String xml = ""; + JingleError error = new JingleErrorProvider().parse(TestUtils.getParser(xml)); + assertEquals(xml, error.toXML().toString()); + } + + @Test + public void unsupportedInfoTest() throws Exception { + String xml = ""; + JingleError error = new JingleErrorProvider().parse(TestUtils.getParser(xml)); + assertEquals(xml, error.toXML().toString()); + } + + @Test + public void outOfOrderTest() throws Exception { + String xml = ""; + JingleError error = new JingleErrorProvider().parse(TestUtils.getParser(xml)); + assertEquals(xml, error.toXML().toString()); } @Test(expected = IllegalArgumentException.class) public void illegalArgumentTest() { JingleError.fromString("inexistent-error"); } + + } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleSessionTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleSessionTest.java deleted file mode 100644 index 02acf9b5e..000000000 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleSessionTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * - * 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 static junit.framework.TestCase.assertEquals; -import static junit.framework.TestCase.assertNotSame; - -import org.jivesoftware.smack.test.util.SmackTestSuite; -import org.jivesoftware.smack.util.StringUtils; - -import org.junit.Test; -import org.jxmpp.jid.Jid; -import org.jxmpp.jid.impl.JidCreate; -import org.jxmpp.stringprep.XmppStringprepException; - -/** - * Test JingleSession class. - */ -public class JingleSessionTest extends SmackTestSuite { - - @Test - public void sessionTest() throws XmppStringprepException { - Jid romeo = JidCreate.from("romeo@montague.lit"); - Jid juliet = JidCreate.from("juliet@capulet.lit"); - String sid = StringUtils.randomString(24); - - JingleSession s1 = new JingleSession(romeo, juliet, sid); - JingleSession s2 = new JingleSession(juliet, romeo, sid); - JingleSession s3 = new JingleSession(romeo, juliet, StringUtils.randomString(23)); - JingleSession s4 = new JingleSession(juliet, romeo, sid); - - assertNotSame(s1, s2); - assertNotSame(s1, s3); - assertNotSame(s2, s3); - assertEquals(s2, s4); - assertEquals(s2.hashCode(), s4.hashCode()); - } -} diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleUtilTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleUtilTest.java new file mode 100644 index 000000000..500ee2279 --- /dev/null +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/JingleUtilTest.java @@ -0,0 +1,102 @@ +/** + * + * 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.DummyConnection; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.test.util.SmackTestSuite; +import org.jivesoftware.smackx.jingle.element.Jingle; +import org.jivesoftware.smackx.jingle.element.JingleContent; + +import org.junit.Before; +import org.junit.Test; +import org.jxmpp.jid.FullJid; +import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.stringprep.XmppStringprepException; + +/** + * Test the JingleUtil class. + */ +public class JingleUtilTest extends SmackTestSuite { + + private XMPPConnection connection; + private JingleUtil jutil; + + @Before + public void setup() { + connection = new DummyConnection( + DummyConnection.getDummyConfigurationBuilder() + .setUsernameAndPassword("romeo@montague.lit", + "iluvJulibabe13").build()); + jutil = new JingleUtil(connection); + } + + @Test + public void sessionInitiateTest() throws XmppStringprepException { + FullJid romeo = connection.getUser().asFullJidOrThrow(); + FullJid juliet = JidCreate.fullFrom("juliet@capulet.example/yn0cl4bnw0yr3vym"); + + String sid = "851ba2"; + String contentName = "a-file-offer"; + Jingle jingle = jutil.createSessionInitiate(juliet, sid, + JingleContent.Creator.initiator, contentName, JingleContent.Senders.initiator, null, null); + + + + String expected = + "" + + "" + + "" + + "" + + "" + + "1969-07-21T02:56:15Z" + + "This is a test. If this were a real file..." + + "text/plain" + + "test.txt" + + "" + + "6144" + + "w0mcJylzCn+AfvuGdqkty2+KP48=" + + "" + + "" + + " " + + "" + + "" + + "" + + "" + + "" + + ""; + } +} diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/transports/jingle_ibb/JingleIBBTransportTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/transports/jingle_ibb/JingleIBBTransportTest.java index 70e0411b3..e33b2b814 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/transports/jingle_ibb/JingleIBBTransportTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/transports/jingle_ibb/JingleIBBTransportTest.java @@ -69,5 +69,8 @@ public class JingleIBBTransportTest extends SmackTestSuite { assertEquals(transport3.getNamespace(), JingleIBBTransport.NAMESPACE_V1); assertEquals(transport3.getElementName(), "transport"); + + JingleIBBTransport transport4 = new JingleIBBTransport("session-id"); + assertEquals(JingleIBBTransport.DEFAULT_BLOCK_SIZE, transport4.getBlockSize()); } } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportTest.java index 33e616efd..1df9a9410 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportTest.java @@ -16,11 +16,14 @@ */ package org.jivesoftware.smackx.jingle.transports.jingle_s5b; +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertNotNull; import static junit.framework.TestCase.assertNull; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static junit.framework.TestCase.assertTrue; import org.jivesoftware.smack.test.util.SmackTestSuite; + import org.jivesoftware.smack.test.util.TestUtils; import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport; @@ -29,7 +32,9 @@ import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTr import org.jivesoftware.smackx.jingle.transports.jingle_s5b.provider.JingleS5BTransportProvider; import org.junit.Test; +import org.jxmpp.jid.FullJid; import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.stringprep.XmppStringprepException; /** * Test Provider and serialization. @@ -76,8 +81,13 @@ public class JingleS5BTransportTest extends SmackTestSuite { assertEquals(Bytestream.Mode.tcp, transport.getMode()); assertEquals(3, transport.getCandidates().size()); + assertTrue(transport.hasCandidate("hft54dqy")); + assertFalse(transport.hasCandidate("invalidId")); JingleS5BTransportCandidate candidate1 = (JingleS5BTransportCandidate) transport.getCandidates().get(0); + assertEquals(candidate1, transport.getCandidate("hft54dqy")); + assertNotNull(candidate1.getStreamHost()); + assertEquals(JingleS5BTransportCandidate.Type.direct.getWeight(), candidate1.getType().getWeight()); assertEquals("hft54dqy", candidate1.getCandidateId()); assertEquals("192.168.4.1", candidate1.getHost()); assertEquals(JidCreate.from("romeo@montague.lit/orchard"), candidate1.getJid()); @@ -128,6 +138,7 @@ public class JingleS5BTransportTest extends SmackTestSuite { JingleS5BTransport proxyErrorTransport = new JingleS5BTransportProvider() .parse(TestUtils.getParser(proxyError)); assertNull(proxyErrorTransport.getDestinationAddress()); + assertNotNull(proxyErrorTransport.getInfo()); assertNotNull(candidateErrorTransport.getInfo()); assertEquals("vj3hs98y", proxyErrorTransport.getStreamId()); assertEquals(JingleS5BTransportInfo.ProxyError(), @@ -140,7 +151,7 @@ public class JingleS5BTransportTest extends SmackTestSuite { ""; JingleS5BTransport candidateUsedTransport = new JingleS5BTransportProvider() .parse(TestUtils.getParser(candidateUsed)); - assertNotNull(candidateErrorTransport.getInfo()); + assertNotNull(candidateUsedTransport.getInfo()); assertEquals(JingleS5BTransportInfo.CandidateUsed("hr65dqyd"), candidateUsedTransport.getInfo()); assertEquals("hr65dqyd", @@ -154,7 +165,9 @@ public class JingleS5BTransportTest extends SmackTestSuite { ""; JingleS5BTransport candidateActivatedTransport = new JingleS5BTransportProvider() .parse(TestUtils.getParser(candidateActivated)); + assertNotNull(candidateActivatedTransport.getInfo()); assertNotNull(candidateErrorTransport.getInfo()); + assertEquals(JingleS5BTransportInfo.CandidateActivated("hr65dqyd"), candidateActivatedTransport.getInfo()); assertEquals("hr65dqyd", @@ -162,4 +175,50 @@ public class JingleS5BTransportTest extends SmackTestSuite { candidateActivatedTransport.getInfo()).getCandidateId()); assertEquals(candidateActivated, candidateActivatedTransport.toXML().toString()); } + + @Test(expected = IllegalArgumentException.class) + public void candidateBuilderInvalidPortTest() { + JingleS5BTransportCandidate.getBuilder().setPort(-5); + } + + @Test(expected = IllegalArgumentException.class) + public void candidateBuilderInvalidPriorityTest() { + JingleS5BTransportCandidate.getBuilder().setPriority(-1000); + } + + @Test(expected = IllegalArgumentException.class) + public void transportCandidateIllegalPriorityTest() throws XmppStringprepException { + FullJid jid = JidCreate.fullFrom("test@test.test/test"); + JingleS5BTransportCandidate candidate = new JingleS5BTransportCandidate( + "cid", "host", jid, 5555, -30, JingleS5BTransportCandidate.Type.proxy); + } + + @Test(expected = IllegalArgumentException.class) + public void transportCandidateIllegalPortTest() throws XmppStringprepException { + FullJid jid = JidCreate.fullFrom("test@test.test/test"); + JingleS5BTransportCandidate candidate = new JingleS5BTransportCandidate( + "cid", "host", jid, -5555, 30, JingleS5BTransportCandidate.Type.proxy); + } + + @Test + public void candidateFromStreamHostTest() throws XmppStringprepException { + FullJid jid = JidCreate.fullFrom("test@test.test/test"); + String host = "host.address"; + int port = 1234; + Bytestream.StreamHost streamHost = new Bytestream.StreamHost(jid, host, port); + + JingleS5BTransportCandidate candidate = new JingleS5BTransportCandidate(streamHost, 2000, JingleS5BTransportCandidate.Type.direct); + + assertEquals(2000, candidate.getPriority()); + assertEquals(jid, candidate.getJid()); + assertEquals(host, candidate.getHost()); + assertEquals(port, candidate.getPort()); + + assertEquals(streamHost.toXML().toString(), candidate.getStreamHost().toXML().toString()); + } + + @Test(expected = IllegalArgumentException.class) + public void typeFromIllegalStringTest() { + JingleS5BTransportCandidate.Type.fromString("illegal-type"); + } }