mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-06-17 00:44:50 +02:00
Add support for XEP-0166, XEP-0260, XEP-0261
This commit is contained in:
parent
9bb0ed150b
commit
d7f974dc69
|
@ -57,7 +57,7 @@ public class ParserUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void forwardToEndTagOfDepth(XmlPullParser parser, int depth)
|
public static void forwardToEndTagOfDepth(XmlPullParser parser, int depth)
|
||||||
throws XmlPullParserException, IOException {
|
throws XmlPullParserException, IOException {
|
||||||
int event = parser.getEventType();
|
int event = parser.getEventType();
|
||||||
while (!(event == XmlPullParser.END_TAG && parser.getDepth() == depth)) {
|
while (!(event == XmlPullParser.END_TAG && parser.getDepth() == depth)) {
|
||||||
event = parser.next();
|
event = parser.next();
|
||||||
|
@ -107,7 +107,9 @@ public class ParserUtils {
|
||||||
}
|
}
|
||||||
Jid jid = JidCreate.from(jidString);
|
Jid jid = JidCreate.from(jidString);
|
||||||
|
|
||||||
if (!jid.hasLocalpart()) return null;
|
if (!jid.hasLocalpart()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
EntityFullJid fullJid = jid.asEntityFullJidIfPossible();
|
EntityFullJid fullJid = jid.asEntityFullJidIfPossible();
|
||||||
if (fullJid != null) {
|
if (fullJid != null) {
|
||||||
|
@ -135,25 +137,25 @@ public class ParserUtils {
|
||||||
*/
|
*/
|
||||||
public static Boolean getBooleanAttribute(XmlPullParser parser, String name) {
|
public static Boolean getBooleanAttribute(XmlPullParser parser, String name) {
|
||||||
String valueString = parser.getAttributeValue("", name);
|
String valueString = parser.getAttributeValue("", name);
|
||||||
if (valueString == null)
|
if (valueString == null) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
valueString = valueString.toLowerCase(Locale.US);
|
valueString = valueString.toLowerCase(Locale.US);
|
||||||
if (valueString.equals("true") || valueString.equals("0")) {
|
if (valueString.equals("true") || valueString.equals("0")) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean getBooleanAttribute(XmlPullParser parser, String name,
|
public static boolean getBooleanAttribute(XmlPullParser parser, String name,
|
||||||
boolean defaultValue) {
|
boolean defaultValue) {
|
||||||
Boolean bool = getBooleanAttribute(parser, name);
|
Boolean bool = getBooleanAttribute(parser, name);
|
||||||
if (bool == null) {
|
if (bool == null) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
else {
|
return bool;
|
||||||
return bool;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getIntegerAttributeOrThrow(XmlPullParser parser, String name, String throwMessage) throws SmackException {
|
public static int getIntegerAttributeOrThrow(XmlPullParser parser, String name, String throwMessage) throws SmackException {
|
||||||
|
@ -166,8 +168,9 @@ public class ParserUtils {
|
||||||
|
|
||||||
public static Integer getIntegerAttribute(XmlPullParser parser, String name) {
|
public static Integer getIntegerAttribute(XmlPullParser parser, String name) {
|
||||||
String valueString = parser.getAttributeValue("", name);
|
String valueString = parser.getAttributeValue("", name);
|
||||||
if (valueString == null)
|
if (valueString == null) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
return Integer.valueOf(valueString);
|
return Integer.valueOf(valueString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,9 +179,7 @@ public class ParserUtils {
|
||||||
if (integer == null) {
|
if (integer == null) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
else {
|
return integer;
|
||||||
return integer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getIntegerFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException {
|
public static int getIntegerFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||||
|
@ -188,8 +189,9 @@ public class ParserUtils {
|
||||||
|
|
||||||
public static Long getLongAttribute(XmlPullParser parser, String name) {
|
public static Long getLongAttribute(XmlPullParser parser, String name) {
|
||||||
String valueString = parser.getAttributeValue("", name);
|
String valueString = parser.getAttributeValue("", name);
|
||||||
if (valueString == null)
|
if (valueString == null) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
return Long.valueOf(valueString);
|
return Long.valueOf(valueString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,9 +200,7 @@ public class ParserUtils {
|
||||||
if (l == null) {
|
if (l == null) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
else {
|
return l;
|
||||||
return l;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double getDoubleFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException {
|
public static double getDoubleFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||||
|
@ -210,8 +210,9 @@ public class ParserUtils {
|
||||||
|
|
||||||
public static Double getDoubleAttribute(XmlPullParser parser, String name) {
|
public static Double getDoubleAttribute(XmlPullParser parser, String name) {
|
||||||
String valueString = parser.getAttributeValue("", name);
|
String valueString = parser.getAttributeValue("", name);
|
||||||
if (valueString == null)
|
if (valueString == null) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
return Double.valueOf(valueString);
|
return Double.valueOf(valueString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,9 +221,23 @@ public class ParserUtils {
|
||||||
if (d == null) {
|
if (d == null) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
else {
|
return d;
|
||||||
return d;
|
}
|
||||||
|
|
||||||
|
public static Short getShortAttribute(XmlPullParser parser, String name) {
|
||||||
|
String valueString = parser.getAttributeValue("", name);
|
||||||
|
if (valueString == null) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
return Short.valueOf(valueString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static short getShortAttribute(XmlPullParser parser, String name, short defaultValue) {
|
||||||
|
Short s = getShortAttribute(parser, name);
|
||||||
|
if (s == null) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Date getDateFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {
|
public static Date getDateFromNextText(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {
|
||||||
|
|
|
@ -271,6 +271,11 @@ public class XmlStringBuilder implements Appendable, CharSequence {
|
||||||
return attribute(name, String.valueOf(value));
|
return attribute(name, String.valueOf(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public XmlStringBuilder attribute(String name, long value) {
|
||||||
|
assert name != null;
|
||||||
|
return attribute(name, String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
public XmlStringBuilder optAttribute(String name, String value) {
|
public XmlStringBuilder optAttribute(String name, String value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
attribute(name, value);
|
attribute(name, value);
|
||||||
|
@ -278,6 +283,13 @@ public class XmlStringBuilder implements Appendable, CharSequence {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public XmlStringBuilder optAttribute(String name, Long value) {
|
||||||
|
if (value != null) {
|
||||||
|
attribute(name, value);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new attribute to this builder, with the {@link java.util.Date} instance as its value,
|
* Add a new attribute to this builder, with the {@link java.util.Date} instance as its value,
|
||||||
* which will get formated with {@link XmppDateTime#formatXEP0082Date(Date)}
|
* which will get formated with {@link XmppDateTime#formatXEP0082Date(Date)}
|
||||||
|
|
|
@ -1,43 +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_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<XMPPConnection, JingleFileTransferManager> 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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +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_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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,167 +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_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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,135 +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_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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,90 +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_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<Checksum> {
|
|
||||||
@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());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,120 +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_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<JingleFileTransfer> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JingleFileTransfer parse(XmlPullParser parser, int initialDepth) throws Exception {
|
|
||||||
ArrayList<JingleContentDescriptionChildElement> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -361,7 +361,7 @@ public final class Socks5Proxy {
|
||||||
*
|
*
|
||||||
* @param digest to be removed from the list of allowed transfers
|
* @param digest to be removed from the list of allowed transfers
|
||||||
*/
|
*/
|
||||||
protected void removeTransfer(String digest) {
|
public void removeTransfer(String digest) {
|
||||||
this.allowedConnections.remove(digest);
|
this.allowedConnections.remove(digest);
|
||||||
this.connectionMap.remove(digest);
|
this.connectionMap.remove(digest);
|
||||||
}
|
}
|
||||||
|
|
|
@ -455,4 +455,26 @@ public class Bytestream extends IQ {
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (this == other) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(other instanceof Bytestream)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytestream o = (Bytestream) other;
|
||||||
|
|
||||||
|
return o.getMode() == getMode() &&
|
||||||
|
o.getSessionID().equals(getSessionID()) &&
|
||||||
|
o.getStreamHosts().equals(getStreamHosts());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 31 * getStreamHosts().hashCode() + 29 * getSessionID().hashCode() + 23 * getMode().hashCode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Florian Schmaus
|
* Copyright 2017 Paul Schaub
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,11 +16,18 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle;
|
package org.jivesoftware.smackx.jingle;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
/**
|
||||||
import org.jivesoftware.smackx.jingle.element.Jingle;
|
* Created by vanitas on 27.07.17.
|
||||||
|
*/
|
||||||
|
public interface JingleDescriptionController {
|
||||||
|
|
||||||
public interface JingleHandler {
|
enum State {
|
||||||
|
pending, //Not yet accepted by us/peer
|
||||||
IQ handleJingleRequest(Jingle jingle);
|
negotiating, //Accepted, but still negotiating transports etc.
|
||||||
|
active, //Bytestream initialized and active
|
||||||
|
cancelled, //We/Peer cancelled the transmission
|
||||||
|
ended //Successfully ended
|
||||||
|
}
|
||||||
|
|
||||||
|
State getState();
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Florian Schmaus
|
* Copyright 2017 Paul Schaub
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,11 +16,17 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle;
|
package org.jivesoftware.smackx.jingle;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smackx.jingle.component.JingleContent;
|
||||||
import org.jivesoftware.smackx.jingle.element.Jingle;
|
import org.jivesoftware.smackx.jingle.component.JingleSession;
|
||||||
|
|
||||||
public interface JingleSessionHandler {
|
/**
|
||||||
|
* Manager for JingleDescription components.
|
||||||
|
*/
|
||||||
|
public interface JingleDescriptionManager {
|
||||||
|
|
||||||
IQ handleJingleSessionRequest(Jingle jingle);
|
String getNamespace();
|
||||||
|
|
||||||
|
void notifySessionInitiate(JingleSession session);
|
||||||
|
|
||||||
|
void notifyContentAdd(JingleSession session, JingleContent content);
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Florian Schmaus
|
* Copyright 2017 Paul Schaub
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,119 +16,272 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle;
|
package org.jivesoftware.smackx.jingle;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smack.Manager;
|
import org.jivesoftware.smack.Manager;
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
|
import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
|
||||||
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
|
import org.jivesoftware.smack.iqrequest.IQRequestHandler;
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.IQ.Type;
|
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smackx.jingle.element.Jingle;
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.adapter.JingleDescriptionAdapter;
|
||||||
|
import org.jivesoftware.smackx.jingle.adapter.JingleSecurityAdapter;
|
||||||
|
import org.jivesoftware.smackx.jingle.adapter.JingleTransportAdapter;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleSession;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleAction;
|
import org.jivesoftware.smackx.jingle.element.JingleAction;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
|
import org.jivesoftware.smackx.jingle.element.JingleReasonElement;
|
||||||
import org.jivesoftware.smackx.jingle.transports.jingle_ibb.JingleIBBTransportManager;
|
import org.jivesoftware.smackx.jingle.exception.UnsupportedDescriptionException;
|
||||||
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.JingleS5BTransportManager;
|
import org.jivesoftware.smackx.jingle.exception.UnsupportedSecurityException;
|
||||||
|
import org.jivesoftware.smackx.jingle.exception.UnsupportedTransportException;
|
||||||
|
import org.jivesoftware.smackx.jingle.provider.JingleContentDescriptionProvider;
|
||||||
|
import org.jivesoftware.smackx.jingle.provider.JingleContentSecurityProvider;
|
||||||
|
import org.jivesoftware.smackx.jingle.provider.JingleContentTransportProvider;
|
||||||
|
import org.jivesoftware.smackx.jingle.util.FullJidAndSessionId;
|
||||||
|
import org.jivesoftware.smackx.jingle.util.Role;
|
||||||
|
|
||||||
import org.jxmpp.jid.FullJid;
|
import org.jxmpp.jid.FullJid;
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager for Jingle (XEP-0166).
|
||||||
|
*/
|
||||||
public final class JingleManager extends Manager {
|
public final class JingleManager extends Manager {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(JingleManager.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(JingleManager.class.getName());
|
||||||
|
private static final WeakHashMap<XMPPConnection, JingleManager> INSTANCES = new WeakHashMap<>();
|
||||||
|
|
||||||
private static final Map<XMPPConnection, JingleManager> INSTANCES = new WeakHashMap<>();
|
private static final WeakHashMap<String, JingleContentDescriptionProvider<?>> descriptionProviders = new WeakHashMap<>();
|
||||||
|
private static final WeakHashMap<String, JingleContentTransportProvider<?>> transportProviders = new WeakHashMap<>();
|
||||||
|
private static final WeakHashMap<String, JingleContentSecurityProvider<?>> securityProviders = new WeakHashMap<>();
|
||||||
|
|
||||||
private static final ExecutorService threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
private static final WeakHashMap<String, JingleDescriptionAdapter<?>> descriptionAdapters = new WeakHashMap<>();
|
||||||
|
private static final WeakHashMap<String, JingleTransportAdapter<?>> transportAdapters = new WeakHashMap<>();
|
||||||
|
private static final WeakHashMap<String, JingleSecurityAdapter<?>> securityAdapters = new WeakHashMap<>();
|
||||||
|
|
||||||
public static ExecutorService getThreadPool() {
|
private final WeakHashMap<String, JingleDescriptionManager> descriptionManagers = new WeakHashMap<>();
|
||||||
return threadPool;
|
private final WeakHashMap<String, JingleTransportManager> transportManagers = new WeakHashMap<>();
|
||||||
}
|
private final WeakHashMap<String, JingleSecurityManager> securityManagers = new WeakHashMap<>();
|
||||||
|
|
||||||
public static synchronized JingleManager getInstanceFor(XMPPConnection connection) {
|
private final ConcurrentHashMap<FullJidAndSessionId, JingleSession> jingleSessions = new ConcurrentHashMap<>();
|
||||||
JingleManager jingleManager = INSTANCES.get(connection);
|
|
||||||
if (jingleManager == null) {
|
|
||||||
jingleManager = new JingleManager(connection);
|
|
||||||
INSTANCES.put(connection, jingleManager);
|
|
||||||
}
|
|
||||||
return jingleManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Map<String, JingleHandler> descriptionHandlers = new ConcurrentHashMap<>();
|
private JingleManager(final XMPPConnection connection) {
|
||||||
|
|
||||||
private final Map<FullJidAndSessionId, JingleSessionHandler> jingleSessionHandlers = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
private final JingleUtil jutil;
|
|
||||||
|
|
||||||
private JingleManager(XMPPConnection connection) {
|
|
||||||
super(connection);
|
super(connection);
|
||||||
|
|
||||||
jutil = new JingleUtil(connection);
|
|
||||||
|
|
||||||
connection.registerIQRequestHandler(
|
connection.registerIQRequestHandler(
|
||||||
new AbstractIqRequestHandler(Jingle.ELEMENT, Jingle.NAMESPACE, Type.set, Mode.async) {
|
new AbstractIqRequestHandler(JingleElement.ELEMENT, JingleElement.NAMESPACE, IQ.Type.set, IQRequestHandler.Mode.async) {
|
||||||
@Override
|
@Override
|
||||||
public IQ handleIQRequest(IQ iqRequest) {
|
public IQ handleIQRequest(IQ iqRequest) {
|
||||||
final Jingle jingle = (Jingle) iqRequest;
|
final JingleElement jingle = (JingleElement) iqRequest;
|
||||||
|
|
||||||
FullJid fullFrom = jingle.getFrom().asFullJidOrThrow();
|
FullJid fullFrom = jingle.getFrom().asFullJidOrThrow();
|
||||||
String sid = jingle.getSid();
|
String sid = jingle.getSid();
|
||||||
FullJidAndSessionId fullJidAndSessionId = new FullJidAndSessionId(fullFrom, sid);
|
FullJidAndSessionId fullJidAndSessionId = new FullJidAndSessionId(fullFrom, sid);
|
||||||
|
|
||||||
JingleSessionHandler sessionHandler = jingleSessionHandlers.get(fullJidAndSessionId);
|
JingleSession session = jingleSessions.get(fullJidAndSessionId);
|
||||||
if (sessionHandler != null) {
|
|
||||||
//Handle existing session
|
|
||||||
return sessionHandler.handleJingleSessionRequest(jingle);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jingle.getAction() == JingleAction.session_initiate) {
|
// We have not seen this session before.
|
||||||
|
// Either it is fresh, or unknown.
|
||||||
|
if (session == null) {
|
||||||
|
LOGGER.log(Level.INFO, connection().getUser().asFullJidOrThrow() + " received unknown session: " + jingle.getFrom().asFullJidOrThrow() + " " + jingle.getSid());
|
||||||
|
if (jingle.getAction() == JingleAction.session_initiate) {
|
||||||
|
//fresh. phew!
|
||||||
|
try {
|
||||||
|
session = JingleSession.fromSessionInitiate(JingleManager.this, jingle);
|
||||||
|
jingleSessions.put(fullJidAndSessionId, session);
|
||||||
|
} catch (UnsupportedDescriptionException e) {
|
||||||
|
return JingleElement.createSessionTerminate(jingle.getFrom().asFullJidOrThrow(),
|
||||||
|
jingle.getSid(), JingleReasonElement.Reason.unsupported_applications);
|
||||||
|
} catch (UnsupportedTransportException e) {
|
||||||
|
return JingleElement.createSessionTerminate(jingle.getFrom().asFullJidOrThrow(),
|
||||||
|
jingle.getSid(), JingleReasonElement.Reason.unsupported_transports);
|
||||||
|
} catch (UnsupportedSecurityException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Unsupported Security: " + e, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
JingleContent content = jingle.getContents().get(0);
|
} else {
|
||||||
JingleContentDescription description = content.getDescription();
|
// Unknown session. Error!
|
||||||
JingleHandler jingleDescriptionHandler = descriptionHandlers.get(
|
return JingleElement.createJingleErrorUnknownSession(jingle);
|
||||||
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
|
return session.handleJingleRequest(jingle);
|
||||||
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) {
|
public static JingleManager getInstanceFor(XMPPConnection connection) {
|
||||||
return descriptionHandlers.put(namespace, handler);
|
JingleManager manager = INSTANCES.get(connection);
|
||||||
|
|
||||||
|
if (manager == null) {
|
||||||
|
manager = new JingleManager(connection);
|
||||||
|
INSTANCES.put(connection, manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
return manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleSessionHandler registerJingleSessionHandler(FullJid otherJid, String sessionId, JingleSessionHandler sessionHandler) {
|
public static void addJingleDescriptionAdapter(JingleDescriptionAdapter<?> adapter) {
|
||||||
FullJidAndSessionId fullJidAndSessionId = new FullJidAndSessionId(otherJid, sessionId);
|
descriptionAdapters.put(adapter.getNamespace(), adapter);
|
||||||
return jingleSessionHandlers.put(fullJidAndSessionId, sessionHandler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleSessionHandler unregisterJingleSessionHandler(FullJid otherJid, String sessionId, JingleSessionHandler sessionHandler) {
|
public static void addJingleTransportAdapter(JingleTransportAdapter<?> adapter) {
|
||||||
FullJidAndSessionId fullJidAndSessionId = new FullJidAndSessionId(otherJid, sessionId);
|
transportAdapters.put(adapter.getNamespace(), adapter);
|
||||||
return jingleSessionHandlers.remove(fullJidAndSessionId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String randomId() {
|
public static void addJingleSecurityAdapter(JingleSecurityAdapter<?> adapter) {
|
||||||
return StringUtils.randomString(24);
|
securityAdapters.put(adapter.getNamespace(), adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JingleDescriptionAdapter<?> getJingleDescriptionAdapter(String namespace) {
|
||||||
|
return descriptionAdapters.get(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JingleTransportAdapter<?> getJingleTransportAdapter(String namespace) {
|
||||||
|
return transportAdapters.get(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JingleSecurityAdapter<?> getJingleSecurityAdapter(String namespace) {
|
||||||
|
return securityAdapters.get(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addJingleDescriptionProvider(JingleContentDescriptionProvider<?> provider) {
|
||||||
|
descriptionProviders.put(provider.getNamespace(), provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeJingleDescriptionProvider(String namespace) {
|
||||||
|
descriptionProviders.remove(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JingleContentDescriptionProvider<?> getJingleDescriptionProvider(String namespace) {
|
||||||
|
return descriptionProviders.get(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addJingleTransportProvider(JingleContentTransportProvider<?> provider) {
|
||||||
|
transportProviders.put(provider.getNamespace(), provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeJingleTransportProvider(String namespace) {
|
||||||
|
transportProviders.remove(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JingleContentTransportProvider<?> getJingleTransportProvider(String namespace) {
|
||||||
|
return transportProviders.get(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addJingleSecurityProvider(JingleContentSecurityProvider<?> provider) {
|
||||||
|
securityProviders.put(provider.getNamespace(), provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeJingleSecurityProvider(String namespace) {
|
||||||
|
securityProviders.remove(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JingleContentSecurityProvider<?> getJingleSecurityProvider(String namespace) {
|
||||||
|
return securityProviders.get(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addJingleDescriptionManager(JingleDescriptionManager manager) {
|
||||||
|
descriptionManagers.put(manager.getNamespace(), manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleDescriptionManager getDescriptionManager(String namespace) {
|
||||||
|
return descriptionManagers.get(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addJingleTransportManager(JingleTransportManager manager) {
|
||||||
|
transportManagers.put(manager.getNamespace(), manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleTransportManager getTransportManager(String namespace) {
|
||||||
|
return transportManagers.get(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addJingleSecurityManager(JingleSecurityManager manager) {
|
||||||
|
securityManagers.put(manager.getNamespace(), manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleSecurityManager getSecurityManager(String namespace) {
|
||||||
|
return securityManagers.get(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<JingleTransportManager> getAvailableTransportManagers(Jid to) throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
|
||||||
|
return getAvailableTransportManagers(to, Collections.<String>emptySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<JingleTransportManager> getAvailableTransportManagers(Jid to, Set<String> except) throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
|
||||||
|
Set<String> available = new HashSet<>(transportManagers.keySet());
|
||||||
|
available.removeAll(except);
|
||||||
|
List<JingleTransportManager> remaining = new ArrayList<>();
|
||||||
|
|
||||||
|
for (String namespace : available) {
|
||||||
|
if (ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(to, namespace)) {
|
||||||
|
remaining.add(transportManagers.get(namespace));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(remaining, new Comparator<JingleTransportManager>() {
|
||||||
|
@Override
|
||||||
|
public int compare(JingleTransportManager t0, JingleTransportManager t1) {
|
||||||
|
return t1.compareTo(t0); //Invert otherwise ascending order to descending.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleTransportManager getBestAvailableTransportManager(Jid to) throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
|
||||||
|
return getBestAvailableTransportManager(to, Collections.<String>emptySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleTransportManager getBestAvailableTransportManager(Jid to, Set<String> except) throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
|
||||||
|
List<JingleTransportManager> managers = getAvailableTransportManagers(to, except);
|
||||||
|
|
||||||
|
if (managers.size() > 0) {
|
||||||
|
return managers.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XMPPConnection getConnection() {
|
||||||
|
return connection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleSession createSession(Role role, FullJid peer) {
|
||||||
|
JingleSession session;
|
||||||
|
|
||||||
|
if (role == Role.initiator) {
|
||||||
|
session = new JingleSession(this, connection().getUser().asFullJidOrThrow(), peer,
|
||||||
|
role, StringUtils.randomString(24));
|
||||||
|
} else {
|
||||||
|
session = new JingleSession(this, peer, connection().getUser().asFullJidOrThrow(),
|
||||||
|
role, StringUtils.randomString(24));
|
||||||
|
}
|
||||||
|
|
||||||
|
jingleSessions.put(new FullJidAndSessionId(peer, session.getSessionId()), session);
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSession(JingleSession session) {
|
||||||
|
if (!jingleSessions.containsValue(session)) {
|
||||||
|
jingleSessions.put(new FullJidAndSessionId(session.getPeer(), session.getSessionId()), session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeSession(JingleSession session) {
|
||||||
|
jingleSessions.remove(new FullJidAndSessionId(session.getPeer(), session.getSessionId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager for JingleSecurity components.
|
||||||
|
*/
|
||||||
|
public interface JingleSecurityManager {
|
||||||
|
|
||||||
|
String getNamespace();
|
||||||
|
}
|
|
@ -1,230 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright 2017 Florian Schmaus
|
|
||||||
*
|
|
||||||
* 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.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
|
||||||
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;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.FullJid;
|
|
||||||
|
|
||||||
public abstract class JingleSession implements JingleSessionHandler {
|
|
||||||
protected HashSet<String> failedTransportMethods = new HashSet<>();
|
|
||||||
|
|
||||||
protected final FullJid local;
|
|
||||||
|
|
||||||
protected final FullJid remote;
|
|
||||||
|
|
||||||
protected final Role role;
|
|
||||||
|
|
||||||
protected final String sid;
|
|
||||||
|
|
||||||
protected final List<JingleContent> contents = new ArrayList<>();
|
|
||||||
|
|
||||||
protected ArrayList<Future<?>> 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<JingleContent> 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<JingleContent> getContents() {
|
|
||||||
return contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JingleTransportSession<?> getTransportSession() {
|
|
||||||
return transportSession;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setTransportSession(JingleTransportSession<?> transportSession) {
|
|
||||||
this.transportSession = transportSession;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int hashCode = 31 + getInitiator().hashCode();
|
|
||||||
hashCode = 31 * hashCode + getResponder().hashCode();
|
|
||||||
hashCode = 31 * hashCode + getSessionId().hashCode();
|
|
||||||
return hashCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object other) {
|
|
||||||
if (!(other instanceof JingleSession)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
JingleSession otherJingleSession = (JingleSession) other;
|
|
||||||
return getInitiator().equals(otherJingleSession.getInitiator())
|
|
||||||
&& getResponder().equals(otherJingleSession.getResponder())
|
|
||||||
&& sid.equals(otherJingleSession.sid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IQ handleJingleSessionRequest(Jingle jingle) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected IQ handleSessionInitiate(Jingle sessionInitiate) {
|
|
||||||
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) {
|
|
||||||
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) {
|
|
||||||
return IQ.createResultIQ(transportAccept);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected IQ handleTransportReplace(Jingle transportReplace) {
|
|
||||||
return IQ.createResultIQ(transportReplace);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected IQ handleTransportReject(Jingle transportReject) {
|
|
||||||
return IQ.createResultIQ(transportReject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract XMPPConnection getConnection();
|
|
||||||
|
|
||||||
public abstract void onTransportMethodFailed(String namespace);
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.smackx.jingle.component.JingleContent;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager for JingleTransport components.
|
||||||
|
*/
|
||||||
|
public interface JingleTransportManager extends Comparable<JingleTransportManager> {
|
||||||
|
|
||||||
|
String getNamespace();
|
||||||
|
|
||||||
|
JingleTransport<?> createTransportForInitiator(JingleContent content);
|
||||||
|
|
||||||
|
JingleTransport<?> createTransportForResponder(JingleContent content, JingleTransport<?> peersTransport);
|
||||||
|
|
||||||
|
JingleTransport<?> createTransportForResponder(JingleContent content, JingleContentTransportElement peersTransportElement);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a (usually) positive integer, which is used to define a strict order over the set of available transport
|
||||||
|
* managers.
|
||||||
|
* @return priority.
|
||||||
|
*/
|
||||||
|
int getPriority();
|
||||||
|
}
|
|
@ -1,132 +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 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<XMPPConnection, JingleTransportMethodManager> INSTANCES = new WeakHashMap<>();
|
|
||||||
|
|
||||||
private final HashMap<String, JingleTransportManager<?>> 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.getTransport();
|
|
||||||
if (transport == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getTransportManager(transport.getNamespace());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static 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<String> it = transportManagers.keySet().iterator();
|
|
||||||
if (it.hasNext()) {
|
|
||||||
return getTransportManager(it.next());
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JingleTransportManager<?> getBestAvailableTransportManager(Set<String> 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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,490 +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 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 {
|
|
||||||
|
|
||||||
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 {
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.adapter;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleDescription;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter that creates a Description object from an element.
|
||||||
|
*/
|
||||||
|
public interface JingleDescriptionAdapter<D extends JingleDescription<?>> {
|
||||||
|
|
||||||
|
D descriptionFromElement(JingleContentElement.Creator creator, JingleContentElement.Senders senders,
|
||||||
|
String contentName, String contentDisposition, JingleContentDescriptionElement element);
|
||||||
|
|
||||||
|
String getNamespace();
|
||||||
|
}
|
|
@ -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.adapter;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleSecurity;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentSecurityElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter that creates a Security object from an element.
|
||||||
|
*/
|
||||||
|
public interface JingleSecurityAdapter<S extends JingleSecurity<?>> {
|
||||||
|
|
||||||
|
S securityFromElement(JingleContentSecurityElement element);
|
||||||
|
|
||||||
|
String getNamespace();
|
||||||
|
}
|
|
@ -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.adapter;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter that creates a Transport element from an element.
|
||||||
|
*/
|
||||||
|
public interface JingleTransportAdapter<T extends JingleTransport<?>> {
|
||||||
|
|
||||||
|
T transportFromElement(JingleContentTransportElement element);
|
||||||
|
|
||||||
|
String getNamespace();
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Florian Schmaus, 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 <a href="https://xmpp.org/extensions/xep-0166.html">XEP-0166: Jingle</a>.
|
||||||
|
* Adapters.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle.adapter;
|
|
@ -14,17 +14,16 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle.transports;
|
package org.jivesoftware.smackx.jingle.callbacks;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback for bytestream session creation of TransportManagers.
|
* Callback used to signal success/failure of Jingle Security components.
|
||||||
*/
|
*/
|
||||||
public interface JingleTransportInitiationCallback {
|
public interface JingleSecurityCallback {
|
||||||
|
|
||||||
void onSessionInitiated(BytestreamSession bytestreamSession);
|
void onSecurityReady(BytestreamSession bytestreamSession);
|
||||||
|
|
||||||
void onException(Exception e);
|
|
||||||
|
|
||||||
|
void onSecurityFailed(Exception e);
|
||||||
}
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.callbacks;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by vanitas on 27.07.17.
|
||||||
|
*/
|
||||||
|
public interface JingleTransportCallback {
|
||||||
|
|
||||||
|
void onTransportReady(BytestreamSession bytestreamSession);
|
||||||
|
|
||||||
|
void onTransportFailed(Exception e);
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Florian Schmaus, 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 <a href="https://xmpp.org/extensions/xep-0166.html">XEP-0166: Jingle</a>.
|
||||||
|
* Callbacks.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle.callbacks;
|
|
@ -0,0 +1,492 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.component;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
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.smack.packet.XMPPError;
|
||||||
|
import org.jivesoftware.smack.util.Async;
|
||||||
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleTransportManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.adapter.JingleDescriptionAdapter;
|
||||||
|
import org.jivesoftware.smackx.jingle.adapter.JingleSecurityAdapter;
|
||||||
|
import org.jivesoftware.smackx.jingle.adapter.JingleTransportAdapter;
|
||||||
|
import org.jivesoftware.smackx.jingle.callbacks.JingleSecurityCallback;
|
||||||
|
import org.jivesoftware.smackx.jingle.callbacks.JingleTransportCallback;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentSecurityElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleReasonElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal class that holds the state of a content in a modifiable form.
|
||||||
|
*/
|
||||||
|
public class JingleContent implements JingleTransportCallback, JingleSecurityCallback {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(JingleContent.class.getName());
|
||||||
|
|
||||||
|
private final JingleContentElement.Creator creator;
|
||||||
|
private final String name;
|
||||||
|
private final String disposition;
|
||||||
|
private JingleSession parent;
|
||||||
|
private JingleContentElement.Senders senders;
|
||||||
|
private JingleDescription<?> description;
|
||||||
|
private JingleTransport<?> transport;
|
||||||
|
private JingleSecurity<?> security;
|
||||||
|
|
||||||
|
private JingleTransport<?> pendingReplacingTransport = null;
|
||||||
|
|
||||||
|
private final Set<String> transportBlacklist = Collections.synchronizedSet(new HashSet<String>());
|
||||||
|
|
||||||
|
public JingleContent(JingleContentElement.Creator creator, JingleContentElement.Senders senders) {
|
||||||
|
this(null, null, null, randomName(), null, creator, senders);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleContent(JingleDescription<?> description, JingleTransport<?> transport, JingleSecurity<?> security, String name, String disposition, JingleContentElement.Creator creator, JingleContentElement.Senders senders) {
|
||||||
|
setDescription(description);
|
||||||
|
setTransport(transport);
|
||||||
|
setSecurity(security);
|
||||||
|
this.name = name;
|
||||||
|
this.disposition = disposition;
|
||||||
|
this.creator = creator;
|
||||||
|
this.senders = senders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JingleContent fromElement(JingleContentElement content) {
|
||||||
|
JingleDescription<?> description = null;
|
||||||
|
JingleTransport<?> transport = null;
|
||||||
|
JingleSecurity<?> security = null;
|
||||||
|
|
||||||
|
JingleContentDescriptionElement descriptionElement = content.getDescription();
|
||||||
|
if (descriptionElement != null) {
|
||||||
|
JingleDescriptionAdapter<?> descriptionAdapter = JingleManager.getJingleDescriptionAdapter(content.getDescription().getNamespace());
|
||||||
|
if (descriptionAdapter != null) {
|
||||||
|
description = descriptionAdapter.descriptionFromElement(content.getCreator(), content.getSenders(), content.getName(), content.getDisposition(), descriptionElement);
|
||||||
|
} else {
|
||||||
|
throw new AssertionError("Unsupported Description: " + descriptionElement.getNamespace());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleContentTransportElement transportElement = content.getTransport();
|
||||||
|
if (transportElement != null) {
|
||||||
|
JingleTransportAdapter<?> transportAdapter = JingleManager.getJingleTransportAdapter(content.getTransport().getNamespace());
|
||||||
|
if (transportAdapter != null) {
|
||||||
|
transport = transportAdapter.transportFromElement(transportElement);
|
||||||
|
} else {
|
||||||
|
throw new AssertionError("Unsupported Transport: " + transportElement.getNamespace());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleContentSecurityElement securityElement = content.getSecurity();
|
||||||
|
if (securityElement != null) {
|
||||||
|
JingleSecurityAdapter<?> securityAdapter = JingleManager.getJingleSecurityAdapter(content.getSecurity().getNamespace());
|
||||||
|
if (securityAdapter != null) {
|
||||||
|
security = securityAdapter.securityFromElement(securityElement);
|
||||||
|
} else {
|
||||||
|
throw new AssertionError("Unsupported Security: " + securityElement.getNamespace());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JingleContent(description, transport, security, content.getName(), content.getDisposition(), content.getCreator(), content.getSenders());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSenders(JingleContentElement.Senders senders) {
|
||||||
|
this.senders = senders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HANDLE_XYZ */
|
||||||
|
|
||||||
|
public IQ handleJingleRequest(JingleElement request, XMPPConnection connection) {
|
||||||
|
switch (request.getAction()) {
|
||||||
|
case content_modify:
|
||||||
|
return handleContentModify(request, connection);
|
||||||
|
case description_info:
|
||||||
|
return handleDescriptionInfo(request, connection);
|
||||||
|
case security_info:
|
||||||
|
return handleSecurityInfo(request, connection);
|
||||||
|
case session_info:
|
||||||
|
return handleSessionInfo(request, connection);
|
||||||
|
case transport_accept:
|
||||||
|
return handleTransportAccept(request, connection);
|
||||||
|
case transport_info:
|
||||||
|
return handleTransportInfo(request, connection);
|
||||||
|
case transport_reject:
|
||||||
|
return handleTransportReject(request, connection);
|
||||||
|
case transport_replace:
|
||||||
|
return handleTransportReplace(request, connection);
|
||||||
|
default:
|
||||||
|
throw new AssertionError("Illegal jingle action: " + request.getAction() + " is not allowed here.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleContentAccept(JingleElement request, XMPPConnection connection) {
|
||||||
|
start(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IQ handleSessionAccept(JingleElement request, XMPPConnection connection) {
|
||||||
|
LOGGER.log(Level.INFO, "RECEIVED SESSION ACCEPT!");
|
||||||
|
JingleContentElement contentElement = null;
|
||||||
|
for (JingleContentElement c : request.getContents()) {
|
||||||
|
if (c.getName().equals(getName())) {
|
||||||
|
contentElement = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentElement == null) {
|
||||||
|
throw new AssertionError("Session Accept did not contain this content.");
|
||||||
|
}
|
||||||
|
|
||||||
|
getTransport().handleSessionAccept(contentElement.getTransport(), connection);
|
||||||
|
start(connection);
|
||||||
|
return IQ.createResultIQ(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQ handleContentModify(JingleElement request, XMPPConnection connection) {
|
||||||
|
return IQ.createErrorResponse(request, XMPPError.Condition.feature_not_implemented);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQ handleDescriptionInfo(JingleElement request, XMPPConnection connection) {
|
||||||
|
return IQ.createErrorResponse(request, XMPPError.Condition.feature_not_implemented);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleContentRemove(JingleSession session, XMPPConnection connection) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQ handleSecurityInfo(JingleElement request, XMPPConnection connection) {
|
||||||
|
return IQ.createErrorResponse(request, XMPPError.Condition.feature_not_implemented);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQ handleSessionInfo(JingleElement request, XMPPConnection connection) {
|
||||||
|
return IQ.createResultIQ(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQ handleTransportAccept(JingleElement request, XMPPConnection connection) {
|
||||||
|
|
||||||
|
if (pendingReplacingTransport == null) {
|
||||||
|
LOGGER.log(Level.WARNING, "Received transport-accept, but apparently we did not try to replace the transport.");
|
||||||
|
return JingleElement.createJingleErrorOutOfOrder(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
transport = pendingReplacingTransport;
|
||||||
|
pendingReplacingTransport = null;
|
||||||
|
|
||||||
|
start(connection);
|
||||||
|
|
||||||
|
return IQ.createResultIQ(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQ handleTransportInfo(JingleElement request, XMPPConnection connection) {
|
||||||
|
assert request.getContents().size() == 1;
|
||||||
|
JingleContentElement content = request.getContents().get(0);
|
||||||
|
|
||||||
|
return transport.handleTransportInfo(content.getTransport().getInfo(), request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQ handleTransportReject(JingleElement request, XMPPConnection connection) {
|
||||||
|
if (pendingReplacingTransport == null) {
|
||||||
|
throw new AssertionError("We didn't try to replace the transport.");
|
||||||
|
}
|
||||||
|
transportBlacklist.add(pendingReplacingTransport.getNamespace());
|
||||||
|
pendingReplacingTransport = null;
|
||||||
|
try {
|
||||||
|
replaceTransport(transportBlacklist, connection);
|
||||||
|
} catch (SmackException.NotConnectedException | SmackException.NoResponseException | XMPPException.XMPPErrorException | InterruptedException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Could not replace transport: " + e, e);
|
||||||
|
}
|
||||||
|
return IQ.createResultIQ(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQ handleTransportReplace(final JingleElement request, final XMPPConnection connection) {
|
||||||
|
//Tie Break?
|
||||||
|
if (pendingReplacingTransport != null) {
|
||||||
|
Async.go(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
connection.createStanzaCollectorAndSend(JingleElement.createJingleErrorTieBreak(request)).nextResultOrThrow();
|
||||||
|
} catch (SmackException.NoResponseException | SmackException.NotConnectedException | InterruptedException | XMPPException.XMPPErrorException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Could not send tie-break: " + e, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return IQ.createResultIQ(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleContentElement contentElement = null;
|
||||||
|
for (JingleContentElement c : request.getContents()) {
|
||||||
|
if (c.getName().equals(getName())) {
|
||||||
|
contentElement = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentElement == null) {
|
||||||
|
throw new AssertionError("Unknown content");
|
||||||
|
}
|
||||||
|
|
||||||
|
final JingleSession session = getParent();
|
||||||
|
final JingleContentTransportElement transportElement = contentElement.getTransport();
|
||||||
|
|
||||||
|
JingleTransportManager tm = session.getJingleManager().getTransportManager(transportElement.getNamespace());
|
||||||
|
|
||||||
|
// Unsupported/Blacklisted transport -> reject.
|
||||||
|
if (tm == null || getTransportBlacklist().contains(transportElement.getNamespace())) {
|
||||||
|
Async.go(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
getParent().getJingleManager().getConnection().createStanzaCollectorAndSend(JingleElement.createTransportReject(session.getOurJid(), session.getPeer(), session.getSessionId(), getCreator(), getName(), transportElement));
|
||||||
|
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Could not send transport-reject: " + e, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//Blacklist current transport
|
||||||
|
this.getTransportBlacklist().add(this.transport.getNamespace());
|
||||||
|
|
||||||
|
this.transport = tm.createTransportForResponder(this, transportElement);
|
||||||
|
Async.go(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
getParent().getJingleManager().getConnection().createStanzaCollectorAndSend(JingleElement.createTransportAccept(session.getOurJid(), session.getPeer(), session.getSessionId(), getCreator(), getName(), transport.getElement()));
|
||||||
|
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Could not send transport-accept: " + e, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
start(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IQ.createResultIQ(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MISCELLANEOUS */
|
||||||
|
|
||||||
|
public JingleContentElement getElement() {
|
||||||
|
JingleContentElement.Builder builder = JingleContentElement.getBuilder()
|
||||||
|
.setName(name)
|
||||||
|
.setCreator(creator)
|
||||||
|
.setSenders(senders)
|
||||||
|
.setDisposition(disposition);
|
||||||
|
|
||||||
|
if (description != null) {
|
||||||
|
builder.setDescription(description.getElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transport != null) {
|
||||||
|
builder.setTransport(transport.getElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (security != null) {
|
||||||
|
builder.setSecurity(security.getElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getTransportBlacklist() {
|
||||||
|
return transportBlacklist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleContentElement.Creator getCreator() {
|
||||||
|
return creator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleContentElement.Senders getSenders() {
|
||||||
|
return senders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParent(JingleSession session) {
|
||||||
|
if (this.parent != session) {
|
||||||
|
this.parent = session;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleSession getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleDescription<?> getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(JingleDescription<?> description) {
|
||||||
|
if (description != null && this.description != description) {
|
||||||
|
this.description = description;
|
||||||
|
description.setParent(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleTransport<?> getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTransport(JingleTransport<?> transport) {
|
||||||
|
if (transport != null && this.transport != transport) {
|
||||||
|
this.transport = transport;
|
||||||
|
transport.setParent(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleSecurity<?> getSecurity() {
|
||||||
|
return security;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecurity(JingleSecurity<?> security) {
|
||||||
|
if (security != null && this.security != security) {
|
||||||
|
this.security = security;
|
||||||
|
security.setParent(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSending() {
|
||||||
|
return (getSenders() == JingleContentElement.Senders.initiator && getParent().isInitiator()) ||
|
||||||
|
(getSenders() == JingleContentElement.Senders.responder && getParent().isResponder()) ||
|
||||||
|
getSenders() == JingleContentElement.Senders.both;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isReceiving() {
|
||||||
|
return (getSenders() == JingleContentElement.Senders.initiator && getParent().isResponder()) ||
|
||||||
|
(getSenders() == JingleContentElement.Senders.responder && getParent().isInitiator()) ||
|
||||||
|
getSenders() == JingleContentElement.Senders.both;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start(final XMPPConnection connection) {
|
||||||
|
transport.prepare(connection);
|
||||||
|
|
||||||
|
if (security != null) {
|
||||||
|
security.prepare(connection, getParent().getPeer());
|
||||||
|
}
|
||||||
|
|
||||||
|
//Establish transport
|
||||||
|
Async.go(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
if (isReceiving()) {
|
||||||
|
LOGGER.log(Level.INFO, "Establish incoming bytestream.");
|
||||||
|
getTransport().establishIncomingBytestreamSession(connection, JingleContent.this, getParent());
|
||||||
|
} else if (isSending()) {
|
||||||
|
LOGGER.log(Level.INFO, "Establish outgoing bytestream.");
|
||||||
|
getTransport().establishOutgoingBytestreamSession(connection, JingleContent.this, getParent());
|
||||||
|
} else {
|
||||||
|
LOGGER.log(Level.INFO, "Neither receiving, nor sending. Assume receiving.");
|
||||||
|
getTransport().establishIncomingBytestreamSession(connection, JingleContent.this, getParent());
|
||||||
|
}
|
||||||
|
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Error establishing connection: " + e, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransportReady(BytestreamSession bytestreamSession) {
|
||||||
|
LOGGER.log(Level.INFO, "TransportReady: " + (isReceiving() ? "Receive" : "Send"));
|
||||||
|
if (bytestreamSession == null) {
|
||||||
|
throw new AssertionError("bytestreamSession MUST NOT be null at this point.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (security != null) {
|
||||||
|
if (isReceiving()) {
|
||||||
|
LOGGER.log(Level.INFO, "Decrypt incoming Bytestream.");
|
||||||
|
getSecurity().decryptIncomingBytestream(bytestreamSession, this);
|
||||||
|
} else if (isSending()) {
|
||||||
|
LOGGER.log(Level.INFO, "Encrypt outgoing Bytestream.");
|
||||||
|
getSecurity().encryptOutgoingBytestream(bytestreamSession, this);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
description.onBytestreamReady(bytestreamSession);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransportFailed(Exception e) {
|
||||||
|
//Add current transport to blacklist.
|
||||||
|
getTransportBlacklist().add(transport.getNamespace());
|
||||||
|
|
||||||
|
//Replace transport.
|
||||||
|
if (getParent().isInitiator()) {
|
||||||
|
try {
|
||||||
|
replaceTransport(getTransportBlacklist(), getParent().getJingleManager().getConnection());
|
||||||
|
} catch (SmackException.NotConnectedException | InterruptedException | SmackException.NoResponseException | XMPPException.XMPPErrorException e1) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Could not send transport-replace: " + e, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSecurityReady(BytestreamSession bytestreamSession) {
|
||||||
|
getDescription().onBytestreamReady(bytestreamSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSecurityFailed(Exception e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Security failed: " + e, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void replaceTransport(Set<String> blacklist, XMPPConnection connection)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException,
|
||||||
|
XMPPException.XMPPErrorException, SmackException.NoResponseException {
|
||||||
|
if (pendingReplacingTransport != null) {
|
||||||
|
throw new AssertionError("Transport replace already pending.");
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleSession session = getParent();
|
||||||
|
JingleManager jingleManager = session.getJingleManager();
|
||||||
|
|
||||||
|
JingleTransportManager rManager = jingleManager.getBestAvailableTransportManager(getParent().getPeer(), blacklist);
|
||||||
|
if (rManager == null) {
|
||||||
|
JingleElement failedTransport = JingleElement.createSessionTerminate(session.getPeer(),
|
||||||
|
session.getSessionId(), JingleReasonElement.Reason.failed_transport);
|
||||||
|
connection.createStanzaCollectorAndSend(failedTransport).nextResultOrThrow();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingReplacingTransport = rManager.createTransportForInitiator(this);
|
||||||
|
|
||||||
|
JingleElement transportReplace = JingleElement.createTransportReplace(session.getInitiator(), session.getPeer(),
|
||||||
|
session.getSessionId(), getCreator(), getName(), pendingReplacingTransport.getElement());
|
||||||
|
|
||||||
|
connection.createStanzaCollectorAndSend(transportReplace).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String randomName() {
|
||||||
|
return "cont-" + StringUtils.randomString(16);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.component;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionInfoElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that represents a contents description component.
|
||||||
|
*/
|
||||||
|
public abstract class JingleDescription<D extends JingleContentDescriptionElement> {
|
||||||
|
|
||||||
|
private JingleContent parent;
|
||||||
|
|
||||||
|
public abstract D getElement();
|
||||||
|
|
||||||
|
public void setParent(JingleContent parent) {
|
||||||
|
if (this.parent != parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract JingleElement handleDescriptionInfo(JingleContentDescriptionInfoElement info);
|
||||||
|
|
||||||
|
public JingleContent getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void onBytestreamReady(BytestreamSession bytestreamSession);
|
||||||
|
|
||||||
|
public abstract String getNamespace();
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.component;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.callbacks.JingleSecurityCallback;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentSecurityElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentSecurityInfoElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that represents a contents security component.
|
||||||
|
*/
|
||||||
|
public abstract class JingleSecurity<D extends JingleContentSecurityElement> {
|
||||||
|
|
||||||
|
private JingleContent parent;
|
||||||
|
|
||||||
|
public abstract D getElement();
|
||||||
|
|
||||||
|
public abstract JingleElement handleSecurityInfo(JingleContentSecurityInfoElement element, JingleElement wrapping);
|
||||||
|
|
||||||
|
public void setParent(JingleContent parent) {
|
||||||
|
if (this.parent != parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleContent getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void decryptIncomingBytestream(BytestreamSession bytestreamSession, JingleSecurityCallback callback);
|
||||||
|
|
||||||
|
public abstract void encryptOutgoingBytestream(BytestreamSession bytestreamSession, JingleSecurityCallback callbacks);
|
||||||
|
|
||||||
|
public abstract String getNamespace();
|
||||||
|
|
||||||
|
public abstract void prepare(XMPPConnection connection, FullJid sender);
|
||||||
|
}
|
|
@ -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.component;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by vanitas on 27.07.17.
|
||||||
|
*/
|
||||||
|
public abstract class JingleSecurityBytestreamSession implements BytestreamSession {
|
||||||
|
|
||||||
|
protected BytestreamSession wrapped;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getReadTimeout() throws IOException {
|
||||||
|
return wrapped.getReadTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReadTimeout(int timeout) throws IOException {
|
||||||
|
wrapped.setReadTimeout(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleSecurityBytestreamSession(BytestreamSession session) {
|
||||||
|
this.wrapped = session;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,424 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.component;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
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.smack.packet.XMPPError;
|
||||||
|
import org.jivesoftware.smack.util.Async;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleDescriptionManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleAction;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleReasonElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.exception.UnsupportedDescriptionException;
|
||||||
|
import org.jivesoftware.smackx.jingle.exception.UnsupportedSecurityException;
|
||||||
|
import org.jivesoftware.smackx.jingle.exception.UnsupportedTransportException;
|
||||||
|
import org.jivesoftware.smackx.jingle.util.Role;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that represents a Jingle session.
|
||||||
|
*/
|
||||||
|
public class JingleSession {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(JingleSession.class.getName());
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<String, JingleContent> contents = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentHashMap<String, JingleContent> proposedContents = new ConcurrentHashMap<>();
|
||||||
|
private final JingleManager jingleManager;
|
||||||
|
|
||||||
|
private final FullJid initiator, responder;
|
||||||
|
private final Role role;
|
||||||
|
private final String sessionId;
|
||||||
|
|
||||||
|
public enum SessionState {
|
||||||
|
fresh, //pre-session-inititate
|
||||||
|
pending, //pre-session-accept
|
||||||
|
active, //pre-session-terminate
|
||||||
|
ended //post-session-terminate
|
||||||
|
}
|
||||||
|
|
||||||
|
private SessionState sessionState;
|
||||||
|
|
||||||
|
public JingleSession(JingleManager manager, FullJid initiator, FullJid responder, Role role, String sessionId) {
|
||||||
|
this.jingleManager = manager;
|
||||||
|
this.initiator = initiator;
|
||||||
|
this.responder = responder;
|
||||||
|
this.role = role;
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
this.sessionState = SessionState.fresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JingleSession fromSessionInitiate(JingleManager manager, JingleElement initiate)
|
||||||
|
throws UnsupportedSecurityException, UnsupportedDescriptionException, UnsupportedTransportException {
|
||||||
|
if (initiate.getAction() != JingleAction.session_initiate) {
|
||||||
|
throw new IllegalArgumentException("Jingle-Action MUST be 'session-initiate'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleSession session = new JingleSession(manager, initiate.getInitiator(), manager.getConnection().getUser().asFullJidOrThrow(), Role.responder, initiate.getSid());
|
||||||
|
List<JingleContentElement> initiateContents = initiate.getContents();
|
||||||
|
|
||||||
|
for (JingleContentElement content : initiateContents) {
|
||||||
|
session.addContent(content, manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
session.sessionState = SessionState.pending;
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendInitiate(XMPPConnection connection) throws SmackException.NotConnectedException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NoResponseException {
|
||||||
|
if (this.sessionState != SessionState.fresh) {
|
||||||
|
throw new IllegalStateException("Session is not in fresh state.");
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.createStanzaCollectorAndSend(createSessionInitiate()).nextResultOrThrow();
|
||||||
|
this.sessionState = SessionState.pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendAccept(XMPPConnection connection) throws SmackException.NotConnectedException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NoResponseException {
|
||||||
|
LOGGER.log(Level.INFO, "Accepted session.");
|
||||||
|
if (this.sessionState != SessionState.pending) {
|
||||||
|
throw new IllegalStateException("Session is not in pending state.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contents.values().size() == 0) {
|
||||||
|
LOGGER.log(Level.WARNING, "0 contents!");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (JingleContent content : contents.values()) {
|
||||||
|
content.start(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.createStanzaCollectorAndSend(createSessionAccept()).nextResultOrThrow();
|
||||||
|
this.sessionState = SessionState.active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleElement createSessionInitiate() {
|
||||||
|
if (role != Role.initiator) {
|
||||||
|
throw new IllegalStateException("Sessions role is not initiator.");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<JingleContentElement> contentElements = new ArrayList<>();
|
||||||
|
for (JingleContent c : contents.values()) {
|
||||||
|
contentElements.add(c.getElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
return JingleElement.createSessionInitiate(getInitiator(), getResponder(), getSessionId(), contentElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleElement createSessionAccept() {
|
||||||
|
if (role != Role.responder) {
|
||||||
|
throw new IllegalStateException("Sessions role is not responder.");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<JingleContentElement> contentElements = new ArrayList<>();
|
||||||
|
for (JingleContent c : contents.values()) {
|
||||||
|
contentElements.add(c.getElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
return JingleElement.createSessionAccept(getInitiator(), getResponder(), getSessionId(), contentElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ handleJingleRequest(JingleElement request) {
|
||||||
|
switch (request.getAction()) {
|
||||||
|
case content_modify:
|
||||||
|
case description_info:
|
||||||
|
case security_info:
|
||||||
|
case session_info:
|
||||||
|
case transport_accept:
|
||||||
|
case transport_info:
|
||||||
|
case transport_reject:
|
||||||
|
case transport_replace:
|
||||||
|
return getSoleAffectedContentOrThrow(request).handleJingleRequest(request, jingleManager.getConnection());
|
||||||
|
case content_accept:
|
||||||
|
return handleContentAccept(request);
|
||||||
|
case content_add:
|
||||||
|
return handleContentAdd(request);
|
||||||
|
case content_reject:
|
||||||
|
return handleContentReject(request);
|
||||||
|
case content_remove:
|
||||||
|
return handleContentRemove(request);
|
||||||
|
case session_accept:
|
||||||
|
return handleSessionAccept(request);
|
||||||
|
case session_initiate:
|
||||||
|
return handleSessionInitiate(request);
|
||||||
|
case session_terminate:
|
||||||
|
return handleSessionTerminate(request);
|
||||||
|
default:
|
||||||
|
throw new AssertionError("Illegal jingle action: " + request.getAction());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ############## Processed in this class ############## */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle incoming session-accept stanza.
|
||||||
|
* @param request session-accept stanza.
|
||||||
|
* @return result.
|
||||||
|
*/
|
||||||
|
private IQ handleSessionAccept(final JingleElement request) {
|
||||||
|
this.sessionState = SessionState.active;
|
||||||
|
|
||||||
|
for (final JingleContent content : contents.values()) {
|
||||||
|
Async.go(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
content.handleSessionAccept(request, jingleManager.getConnection());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return IQ.createResultIQ(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQ handleSessionInitiate(JingleElement request) {
|
||||||
|
JingleDescription<?> description = getSoleContentOrThrow().getDescription();
|
||||||
|
JingleDescriptionManager descriptionManager = jingleManager.getDescriptionManager(description.getNamespace());
|
||||||
|
|
||||||
|
if (descriptionManager == null) {
|
||||||
|
LOGGER.log(Level.WARNING, "Unsupported description type: " + description.getNamespace());
|
||||||
|
return JingleElement.createSessionTerminate(getPeer(), getSessionId(), JingleReasonElement.Reason.unsupported_applications);
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptionManager.notifySessionInitiate(this);
|
||||||
|
|
||||||
|
return IQ.createResultIQ(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQ handleSessionTerminate(JingleElement request) {
|
||||||
|
this.sessionState = SessionState.ended;
|
||||||
|
JingleReasonElement reason = request.getReason();
|
||||||
|
|
||||||
|
if (reason == null) {
|
||||||
|
throw new AssertionError("Reason MUST not be null! (I guess)...");
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Inform client.
|
||||||
|
jingleManager.removeSession(this);
|
||||||
|
|
||||||
|
return IQ.createResultIQ(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQ handleContentAccept(final JingleElement request) {
|
||||||
|
for (JingleContentElement a : request.getContents()) {
|
||||||
|
final JingleContent accepted = proposedContents.get(a.getName());
|
||||||
|
|
||||||
|
if (accepted == null) {
|
||||||
|
throw new AssertionError("Illegal content name!");
|
||||||
|
}
|
||||||
|
|
||||||
|
proposedContents.remove(accepted.getName());
|
||||||
|
contents.put(accepted.getName(), accepted);
|
||||||
|
|
||||||
|
Async.go(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
accepted.handleContentAccept(request, jingleManager.getConnection());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return IQ.createResultIQ(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQ handleContentAdd(JingleElement request) {
|
||||||
|
final JingleContent proposed = getSoleProposedContentOrThrow(request);
|
||||||
|
|
||||||
|
final JingleDescriptionManager descriptionManager = jingleManager.getDescriptionManager(proposed.getDescription().getNamespace());
|
||||||
|
|
||||||
|
if (descriptionManager == null) {
|
||||||
|
throw new AssertionError("DescriptionManager is null: " + proposed.getDescription().getNamespace());
|
||||||
|
}
|
||||||
|
|
||||||
|
Async.go(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
descriptionManager.notifyContentAdd(JingleSession.this, proposed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return IQ.createResultIQ(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQ handleContentReject(JingleElement request) {
|
||||||
|
for (JingleContentElement r : request.getContents()) {
|
||||||
|
final JingleContent rejected = proposedContents.get(r.getName());
|
||||||
|
|
||||||
|
if (rejected == null) {
|
||||||
|
throw new AssertionError("Illegal content name!");
|
||||||
|
}
|
||||||
|
|
||||||
|
proposedContents.remove(rejected.getName());
|
||||||
|
|
||||||
|
/*
|
||||||
|
Async.go(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
rejected.handleContentReject(request, jingleManager.getConnection());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
return IQ.createResultIQ(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQ handleContentRemove(final JingleElement request) {
|
||||||
|
return IQ.createErrorResponse(request, XMPPError.Condition.feature_not_implemented);
|
||||||
|
/*
|
||||||
|
for (JingleContentElement r : request.getContents()) {
|
||||||
|
final JingleContent removed = contents.get(r.getName());
|
||||||
|
|
||||||
|
if (removed == null) {
|
||||||
|
throw new AssertionError("Illegal content name!");
|
||||||
|
}
|
||||||
|
|
||||||
|
contents.remove(removed.getName());
|
||||||
|
|
||||||
|
Async.go(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
removed.handleContentRemove(JingleSession.this, jingleManager.getConnection());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return IQ.createResultIQ(request);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public FullJid getInitiator() {
|
||||||
|
return initiator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FullJid getResponder() {
|
||||||
|
return responder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FullJid getPeer() {
|
||||||
|
return role == Role.initiator ? responder : initiator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FullJid getOurJid() {
|
||||||
|
return role == Role.initiator ? initiator : responder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInitiator() {
|
||||||
|
return role == Role.initiator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isResponder() {
|
||||||
|
return role == Role.responder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSessionId() {
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleManager getJingleManager() {
|
||||||
|
return jingleManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap<JingleContentElement, JingleContent> getAffectedContents(JingleElement request) {
|
||||||
|
HashMap<JingleContentElement, JingleContent> map = new HashMap<>();
|
||||||
|
for (JingleContentElement e : request.getContents()) {
|
||||||
|
JingleContent c = contents.get(e.getName());
|
||||||
|
if (c == null) {
|
||||||
|
throw new AssertionError("Unknown content: " + e.getName());
|
||||||
|
}
|
||||||
|
map.put(e, c);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JingleContent getSoleAffectedContentOrThrow(JingleElement request) {
|
||||||
|
if (request.getContents().size() != 1) {
|
||||||
|
throw new AssertionError("More/less than 1 content in request!");
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleContent content = contents.get(request.getContents().get(0).getName());
|
||||||
|
if (content == null) {
|
||||||
|
throw new AssertionError("Illegal content name!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JingleContent getSoleProposedContentOrThrow(JingleElement request) {
|
||||||
|
if (request.getContents().size() != 1) {
|
||||||
|
throw new AssertionError("More/less than 1 content in request!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return JingleContent.fromElement(request.getContents().get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addContent(JingleContent content) {
|
||||||
|
if (contents.get(content.getName()) != null) {
|
||||||
|
throw new IllegalArgumentException("Session already contains a content with the name " + content.getName());
|
||||||
|
}
|
||||||
|
contents.put(content.getName(), content);
|
||||||
|
content.setParent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addContent(JingleContentElement content, JingleManager manager)
|
||||||
|
throws UnsupportedSecurityException, UnsupportedTransportException, UnsupportedDescriptionException {
|
||||||
|
addContent(JingleContent.fromElement(content));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConcurrentHashMap<String, JingleContent> getContents() {
|
||||||
|
return contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleContent getContent(String name) {
|
||||||
|
return contents.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the only jingle content if one exists, or <code>null</code>. This method will throw an
|
||||||
|
* {@link IllegalStateException} if there is more than one jingle content.
|
||||||
|
*
|
||||||
|
* @return a JingleContent instance or <code>null</code>.
|
||||||
|
* @throws IllegalStateException if there is more than one jingle content.
|
||||||
|
*/
|
||||||
|
public JingleContent getSoleContentOrThrow() {
|
||||||
|
if (contents.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contents.size() > 1) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return contents.values().iterator().next();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionState getSessionState() {
|
||||||
|
return sessionState;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.component;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.callbacks.JingleTransportCallback;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportInfoElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that represents a contents transport component.
|
||||||
|
*/
|
||||||
|
public abstract class JingleTransport<D extends JingleContentTransportElement> {
|
||||||
|
|
||||||
|
private JingleContent parent;
|
||||||
|
private final ArrayList<JingleTransportCandidate<?>> ourCandidates = new ArrayList<>();
|
||||||
|
private final ArrayList<JingleTransportCandidate<?>> theirCandidates = new ArrayList<>();
|
||||||
|
|
||||||
|
protected BytestreamSession bytestreamSession;
|
||||||
|
|
||||||
|
public abstract D getElement();
|
||||||
|
|
||||||
|
public void addOurCandidate(JingleTransportCandidate<?> candidate) {
|
||||||
|
// Insert sorted by descending priority
|
||||||
|
int i;
|
||||||
|
// Find appropriate index
|
||||||
|
for (i = 0; i < ourCandidates.size(); i++) {
|
||||||
|
JingleTransportCandidate<?> c = ourCandidates.get(i);
|
||||||
|
|
||||||
|
if (c == candidate || c.equals(candidate)) {
|
||||||
|
c.setParent(this); // Candidate might equal, but not be same, so set parent just in case
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.getPriority() < candidate.getPriority()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ourCandidates.add(i, candidate);
|
||||||
|
candidate.setParent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTheirCandidate(JingleTransportCandidate<?> candidate) {
|
||||||
|
// Insert sorted by descending priority
|
||||||
|
int i;
|
||||||
|
// Find appropriate index
|
||||||
|
for (i = 0; i < theirCandidates.size(); i++) {
|
||||||
|
JingleTransportCandidate<?> c = theirCandidates.get(i);
|
||||||
|
|
||||||
|
if (c == candidate || c.equals(candidate)) {
|
||||||
|
c.setParent(this); // Candidate might equal, but not be same, so set parent just in case
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.getPriority() < candidate.getPriority()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
theirCandidates.add(i, candidate);
|
||||||
|
candidate.setParent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void prepare(XMPPConnection connection);
|
||||||
|
|
||||||
|
public List<JingleTransportCandidate<?>> getOurCandidates() {
|
||||||
|
return ourCandidates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<JingleTransportCandidate<?>> getTheirCandidates() {
|
||||||
|
return theirCandidates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract String getNamespace();
|
||||||
|
|
||||||
|
public abstract void establishIncomingBytestreamSession(XMPPConnection connection, JingleTransportCallback callback, JingleSession session)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException;
|
||||||
|
|
||||||
|
public abstract void establishOutgoingBytestreamSession(XMPPConnection connection, JingleTransportCallback callback, JingleSession session)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException;
|
||||||
|
|
||||||
|
public abstract IQ handleTransportInfo(JingleContentTransportInfoElement info, JingleElement wrapping);
|
||||||
|
|
||||||
|
public void setParent(JingleContent parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleContent getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void handleSessionAccept(JingleContentTransportElement transportElement, XMPPConnection connection);
|
||||||
|
|
||||||
|
public abstract void cleanup();
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.component;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportCandidateElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that represents a transports candidate component.
|
||||||
|
*/
|
||||||
|
public abstract class JingleTransportCandidate<E extends JingleContentTransportCandidateElement> {
|
||||||
|
|
||||||
|
private JingleTransport<?> parent;
|
||||||
|
private int priority;
|
||||||
|
|
||||||
|
public void setParent(JingleTransport<?> transport) {
|
||||||
|
if (parent != transport) {
|
||||||
|
parent = transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleTransport<?> getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPriority() {
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPriority(int priority) {
|
||||||
|
this.priority = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract E getElement();
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Paul Schaub
|
* Copyright 2017 Florian Schmaus, Paul Schaub
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Smack's API for <a href="https://xmpp.org/extensions/xep-0234.html">XEP-0234: Jingle File Transfer</a>.
|
* Smack's API for <a href="https://xmpp.org/extensions/xep-0166.html">XEP-0166: Jingle</a>.
|
||||||
* Elements.
|
* Internal components.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle_filetransfer.element;
|
package org.jivesoftware.smackx.jingle.component;
|
|
@ -1,235 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright 2003-2007 Jive Software, 2014-2017 Florian Schmaus
|
|
||||||
*
|
|
||||||
* 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.element;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
|
||||||
import org.jivesoftware.smack.util.Objects;
|
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.FullJid;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Jingle element.
|
|
||||||
*
|
|
||||||
* @author Florian Schmaus
|
|
||||||
*/
|
|
||||||
public final class Jingle extends IQ {
|
|
||||||
|
|
||||||
public static final String NAMESPACE = "urn:xmpp:jingle:1";
|
|
||||||
|
|
||||||
public static final String ACTION_ATTRIBUTE_NAME = "action";
|
|
||||||
|
|
||||||
public static final String INITIATOR_ATTRIBUTE_NAME = "initiator";
|
|
||||||
|
|
||||||
public static final String RESPONDER_ATTRIBUTE_NAME = "responder";
|
|
||||||
|
|
||||||
public static final String SESSION_ID_ATTRIBUTE_NAME = "sid";
|
|
||||||
|
|
||||||
public static final String ELEMENT = "jingle";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The session ID related to this session. The session ID is a unique identifier generated by the initiator. This
|
|
||||||
* should match the XML Nmtoken production so that XML character escaping is not needed for characters such as &.
|
|
||||||
*/
|
|
||||||
private final String sessionId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The jingle action. This attribute is required.
|
|
||||||
*/
|
|
||||||
private final JingleAction action;
|
|
||||||
|
|
||||||
private final FullJid initiator;
|
|
||||||
|
|
||||||
private final FullJid responder;
|
|
||||||
|
|
||||||
private final JingleReason reason;
|
|
||||||
|
|
||||||
private final List<JingleContent> contents;
|
|
||||||
|
|
||||||
private Jingle(String sessionId, JingleAction action, FullJid initiator, FullJid responder, JingleReason reason,
|
|
||||||
List<JingleContent> contents) {
|
|
||||||
super(ELEMENT, NAMESPACE);
|
|
||||||
this.sessionId = StringUtils.requireNotNullOrEmpty(sessionId, "Jingle session ID must not be null");
|
|
||||||
this.action = Objects.requireNonNull(action, "Jingle action must not be null");
|
|
||||||
this.initiator = initiator;
|
|
||||||
this.responder = responder;
|
|
||||||
this.reason = reason;
|
|
||||||
if (contents != null) {
|
|
||||||
this.contents = Collections.unmodifiableList(contents);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.contents = Collections.emptyList();
|
|
||||||
}
|
|
||||||
setType(Type.set);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the initiator. The initiator will be the full JID of the entity that has initiated the flow (which may be
|
|
||||||
* different to the "from" address in the IQ)
|
|
||||||
*
|
|
||||||
* @return the initiator
|
|
||||||
*/
|
|
||||||
public FullJid getInitiator() {
|
|
||||||
return initiator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the responder. The responder is the full JID of the entity that has replied to the initiation (which may be
|
|
||||||
* different to the "to" addresss in the IQ).
|
|
||||||
*
|
|
||||||
* @return the responder
|
|
||||||
*/
|
|
||||||
public FullJid getResponder() {
|
|
||||||
return responder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the session ID related to the session. The session ID is a unique identifier generated by the initiator.
|
|
||||||
* This should match the XML Nmtoken production so that XML character escaping is not needed for characters such as
|
|
||||||
* &.
|
|
||||||
*
|
|
||||||
* @return Returns the session ID related to the session.
|
|
||||||
*/
|
|
||||||
public String getSid() {
|
|
||||||
return sessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the action specified in the jingle IQ.
|
|
||||||
*
|
|
||||||
* @return the action.
|
|
||||||
*/
|
|
||||||
public JingleAction getAction() {
|
|
||||||
return action;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JingleReason getReason() {
|
|
||||||
return reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a List of the contents.
|
|
||||||
*
|
|
||||||
* @return the contents.
|
|
||||||
*/
|
|
||||||
public List<JingleContent> getContents() {
|
|
||||||
return contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the only jingle content if one exists, or <code>null</code>. This method will throw an
|
|
||||||
* {@link IllegalStateException} if there is more than one jingle content.
|
|
||||||
*
|
|
||||||
* @return a JingleContent instance or <code>null</code>.
|
|
||||||
* @throws IllegalStateException if there is more than one jingle content.
|
|
||||||
*/
|
|
||||||
public JingleContent getSoleContentOrThrow() {
|
|
||||||
if (contents.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contents.size() > 1) {
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return contents.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
|
|
||||||
xml.optAttribute(INITIATOR_ATTRIBUTE_NAME, getInitiator());
|
|
||||||
xml.optAttribute(RESPONDER_ATTRIBUTE_NAME, getResponder());
|
|
||||||
xml.optAttribute(ACTION_ATTRIBUTE_NAME, getAction());
|
|
||||||
xml.optAttribute(SESSION_ID_ATTRIBUTE_NAME, getSid());
|
|
||||||
xml.rightAngleBracket();
|
|
||||||
|
|
||||||
xml.optElement(reason);
|
|
||||||
|
|
||||||
xml.append(contents);
|
|
||||||
|
|
||||||
return xml;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Builder getBuilder() {
|
|
||||||
return new Builder();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Builder {
|
|
||||||
private String sid;
|
|
||||||
|
|
||||||
private JingleAction action;
|
|
||||||
|
|
||||||
private FullJid initiator;
|
|
||||||
|
|
||||||
private FullJid responder;
|
|
||||||
|
|
||||||
private JingleReason reason;
|
|
||||||
|
|
||||||
private List<JingleContent> contents;
|
|
||||||
|
|
||||||
private Builder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setSessionId(String sessionId) {
|
|
||||||
StringUtils.requireNotNullOrEmpty(sessionId, "Session ID must not be null or empty");
|
|
||||||
this.sid = sessionId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setAction(JingleAction action) {
|
|
||||||
this.action = action;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setInitiator(FullJid initator) {
|
|
||||||
this.initiator = initator;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setResponder(FullJid responder) {
|
|
||||||
this.responder = responder;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder addJingleContent(JingleContent content) {
|
|
||||||
if (contents == null) {
|
|
||||||
contents = new ArrayList<>(1);
|
|
||||||
}
|
|
||||||
contents.add(content);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setReason(JingleReason.Reason reason) {
|
|
||||||
this.reason = new JingleReason(reason);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setReason(JingleReason reason) {
|
|
||||||
this.reason = reason;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Jingle build() {
|
|
||||||
return new Jingle(sid, action, initiator, responder, reason, contents);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -44,7 +44,7 @@ public enum JingleAction {
|
||||||
transport_replace,
|
transport_replace,
|
||||||
;
|
;
|
||||||
|
|
||||||
private static final Map<String, JingleAction> map = new HashMap<String, JingleAction>(
|
private static final Map<String, JingleAction> map = new HashMap<>(
|
||||||
JingleAction.values().length);
|
JingleAction.values().length);
|
||||||
static {
|
static {
|
||||||
for (JingleAction jingleAction : JingleAction.values()) {
|
for (JingleAction jingleAction : JingleAction.values()) {
|
||||||
|
@ -54,7 +54,7 @@ public enum JingleAction {
|
||||||
|
|
||||||
private final String asString;
|
private final String asString;
|
||||||
|
|
||||||
private JingleAction() {
|
JingleAction() {
|
||||||
asString = this.name().replace('_', '-');
|
asString = this.name().replace('_', '-');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,15 @@ import org.jivesoftware.smack.packet.NamedElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An element found usually in 'description' elements.
|
* An element found usually in 'description' elements.
|
||||||
|
* <jingle>
|
||||||
|
* <content>
|
||||||
|
* <description>
|
||||||
|
* <XYZ/> <- We live here.
|
||||||
|
* </description>
|
||||||
|
* <transport/>
|
||||||
|
* <security/>
|
||||||
|
* </content>
|
||||||
|
* </jingle>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class JingleContentDescriptionChildElement implements NamedElement {
|
public abstract class JingleContentDescriptionChildElement implements NamedElement {
|
||||||
|
|
|
@ -25,15 +25,22 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Jingle content description.
|
* Jingle content description.
|
||||||
|
* <jingle>
|
||||||
|
* <content>
|
||||||
|
* <description/> <- This element is us.
|
||||||
|
* <transport/>
|
||||||
|
* <security/>
|
||||||
|
* </content>
|
||||||
|
* </jingle>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class JingleContentDescription implements ExtensionElement {
|
public abstract class JingleContentDescriptionElement implements ExtensionElement {
|
||||||
|
|
||||||
public static final String ELEMENT = "description";
|
public static final String ELEMENT = "description";
|
||||||
|
|
||||||
private final List<NamedElement> payloads;
|
private final List<NamedElement> payloads;
|
||||||
|
|
||||||
protected JingleContentDescription(List<? extends NamedElement> payloads) {
|
protected JingleContentDescriptionElement(List<? extends NamedElement> payloads) {
|
||||||
if (payloads != null) {
|
if (payloads != null) {
|
||||||
this.payloads = Collections.unmodifiableList(payloads);
|
this.payloads = Collections.unmodifiableList(payloads);
|
||||||
}
|
}
|
||||||
|
@ -51,6 +58,10 @@ public abstract class JingleContentDescription implements ExtensionElement {
|
||||||
return payloads;
|
return payloads;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JingleContentDescriptionInfoElement getDescriptionInfo() {
|
||||||
|
return null; //TODO
|
||||||
|
}
|
||||||
|
|
||||||
protected void addExtraAttributes(XmlStringBuilder xml) {
|
protected void addExtraAttributes(XmlStringBuilder xml) {
|
||||||
|
|
||||||
}
|
}
|
|
@ -19,8 +19,8 @@ package org.jivesoftware.smackx.jingle.element;
|
||||||
import org.jivesoftware.smack.packet.NamedElement;
|
import org.jivesoftware.smack.packet.NamedElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract JingleContentTransportInfo element.
|
* Created by vanitas on 28.07.17.
|
||||||
*/
|
*/
|
||||||
public abstract class JingleContentTransportInfo implements NamedElement {
|
public abstract class JingleContentDescriptionInfoElement implements NamedElement {
|
||||||
|
|
||||||
}
|
}
|
|
@ -23,8 +23,13 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Jingle content element.
|
* Jingle content element.
|
||||||
|
* <jingle>
|
||||||
|
* <content> <- Me.
|
||||||
|
* ...
|
||||||
|
* </content>
|
||||||
|
* </jingle>
|
||||||
*/
|
*/
|
||||||
public final class JingleContent implements NamedElement {
|
public final class JingleContentElement implements NamedElement {
|
||||||
|
|
||||||
public static final String ELEMENT = "content";
|
public static final String ELEMENT = "content";
|
||||||
|
|
||||||
|
@ -64,21 +69,24 @@ public final class JingleContent implements NamedElement {
|
||||||
*/
|
*/
|
||||||
private final Senders senders;
|
private final Senders senders;
|
||||||
|
|
||||||
private final JingleContentDescription description;
|
private final JingleContentDescriptionElement description;
|
||||||
|
|
||||||
private final JingleContentTransport transport;
|
private final JingleContentTransportElement transport;
|
||||||
|
|
||||||
|
private final JingleContentSecurityElement security;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a content description..
|
* Creates a content description..
|
||||||
*/
|
*/
|
||||||
private JingleContent(Creator creator, String disposition, String name, Senders senders,
|
private JingleContentElement(Creator creator, String disposition, String name, Senders senders,
|
||||||
JingleContentDescription description, JingleContentTransport transport) {
|
JingleContentDescriptionElement description, JingleContentTransportElement transport, JingleContentSecurityElement security) {
|
||||||
this.creator = Objects.requireNonNull(creator, "Jingle content creator must not be null");
|
this.creator = Objects.requireNonNull(creator, "Jingle content creator must not be null");
|
||||||
this.disposition = disposition;
|
this.disposition = disposition;
|
||||||
this.name = StringUtils.requireNotNullOrEmpty(name, "Jingle content name must not be null or empty");
|
this.name = StringUtils.requireNotNullOrEmpty(name, "Jingle content name must not be null or empty");
|
||||||
this.senders = senders;
|
this.senders = senders;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.transport = transport;
|
this.transport = transport;
|
||||||
|
this.security = security;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Creator getCreator() {
|
public Creator getCreator() {
|
||||||
|
@ -102,28 +110,20 @@ public final class JingleContent implements NamedElement {
|
||||||
*
|
*
|
||||||
* @return The description.
|
* @return The description.
|
||||||
*/
|
*/
|
||||||
public JingleContentDescription getDescription() {
|
public JingleContentDescriptionElement getDescription() {
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an Iterator for the JingleTransports in the packet.
|
* Return the transport of the content.
|
||||||
*
|
* @return transport.
|
||||||
* @return an Iterator for the JingleTransports in the packet.
|
|
||||||
* @deprecated use {@link #getTransport()} instead.
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
public JingleContentTransportElement getTransport() {
|
||||||
public JingleContentTransport getJingleTransport() {
|
return transport;
|
||||||
return getTransport();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public JingleContentSecurityElement getSecurity() {
|
||||||
* Returns an Iterator for the JingleTransports in the packet.
|
return security;
|
||||||
*
|
|
||||||
* @return an Iterator for the JingleTransports in the packet.
|
|
||||||
*/
|
|
||||||
public JingleContentTransport getTransport() {
|
|
||||||
return transport;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -142,6 +142,7 @@ public final class JingleContent implements NamedElement {
|
||||||
|
|
||||||
xml.optAppend(description);
|
xml.optAppend(description);
|
||||||
xml.optElement(transport);
|
xml.optElement(transport);
|
||||||
|
xml.optElement(security);
|
||||||
|
|
||||||
xml.closeElement(this);
|
xml.closeElement(this);
|
||||||
return xml;
|
return xml;
|
||||||
|
@ -160,9 +161,11 @@ public final class JingleContent implements NamedElement {
|
||||||
|
|
||||||
private Senders senders;
|
private Senders senders;
|
||||||
|
|
||||||
private JingleContentDescription description;
|
private JingleContentDescriptionElement description;
|
||||||
|
|
||||||
private JingleContentTransport transport;
|
private JingleContentTransportElement transport;
|
||||||
|
|
||||||
|
private JingleContentSecurityElement security;
|
||||||
|
|
||||||
private Builder() {
|
private Builder() {
|
||||||
}
|
}
|
||||||
|
@ -187,7 +190,7 @@ public final class JingleContent implements NamedElement {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setDescription(JingleContentDescription description) {
|
public Builder setDescription(JingleContentDescriptionElement description) {
|
||||||
if (this.description != null) {
|
if (this.description != null) {
|
||||||
throw new IllegalStateException("Jingle content description already set");
|
throw new IllegalStateException("Jingle content description already set");
|
||||||
}
|
}
|
||||||
|
@ -195,13 +198,18 @@ public final class JingleContent implements NamedElement {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setTransport(JingleContentTransport transport) {
|
public Builder setTransport(JingleContentTransportElement transport) {
|
||||||
this.transport = transport;
|
this.transport = transport;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleContent build() {
|
public Builder setSecurity(JingleContentSecurityElement element) {
|
||||||
return new JingleContent(creator, disposition, name, senders, description, transport);
|
this.security = element;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleContentElement build() {
|
||||||
|
return new JingleContentElement(creator, disposition, name, senders, description, transport, security);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.element;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jingle security element.
|
||||||
|
* <jingle>
|
||||||
|
* <content>
|
||||||
|
* <description/>
|
||||||
|
* <transport/>
|
||||||
|
* <security/> <- That's me :)
|
||||||
|
* </content>
|
||||||
|
* </jingle>
|
||||||
|
*/
|
||||||
|
public abstract class JingleContentSecurityElement implements ExtensionElement {
|
||||||
|
|
||||||
|
public static final String ELEMENT = "security";
|
||||||
|
private JingleContentSecurityInfoElement securityInfo;
|
||||||
|
|
||||||
|
public JingleContentSecurityElement() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleContentSecurityElement(JingleContentSecurityInfoElement info) {
|
||||||
|
this.securityInfo = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getElementName() {
|
||||||
|
return ELEMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleContentSecurityInfoElement getSecurityInfo() {
|
||||||
|
return securityInfo;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,9 +14,13 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle.element;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.NamedElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Smack's API for <a href="https://xmpp.org/extensions/xep-0234.html">XEP-0234: Jingle File Transfer</a>.
|
* Created by vanitas on 19.07.17.
|
||||||
* Providers.
|
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle_filetransfer.provider;
|
public abstract class JingleContentSecurityInfoElement implements NamedElement {
|
||||||
|
|
||||||
|
}
|
|
@ -20,9 +20,18 @@ import org.jivesoftware.smack.packet.NamedElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An element found usually in Jingle 'transport' elements.
|
* An element found usually in Jingle 'transport' elements.
|
||||||
|
* <jingle>
|
||||||
|
* <content>
|
||||||
|
* <description/>
|
||||||
|
* <transport>
|
||||||
|
* <candidate/> <- We are those guys.
|
||||||
|
* <candidate/> <-/
|
||||||
|
* </transport>
|
||||||
|
* </content>
|
||||||
|
* </jingle>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class JingleContentTransportCandidate implements NamedElement {
|
public abstract class JingleContentTransportCandidateElement implements NamedElement {
|
||||||
|
|
||||||
public static final String ELEMENT = "candidate";
|
public static final String ELEMENT = "candidate";
|
||||||
|
|
|
@ -24,35 +24,41 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A jingle transport extension.
|
* A jingle transport extension.
|
||||||
|
* <jingle>
|
||||||
|
* <content>
|
||||||
|
* <description/>
|
||||||
|
* <transport/> <- Voila.
|
||||||
|
* <security/>
|
||||||
|
* </content>
|
||||||
|
* </jingle>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class JingleContentTransport implements ExtensionElement {
|
public abstract class JingleContentTransportElement implements ExtensionElement {
|
||||||
|
|
||||||
public static final String ELEMENT = "transport";
|
public static final String ELEMENT = "transport";
|
||||||
|
|
||||||
protected final List<JingleContentTransportCandidate> candidates;
|
protected final List<JingleContentTransportCandidateElement> candidates;
|
||||||
protected final JingleContentTransportInfo info;
|
protected final JingleContentTransportInfoElement info;
|
||||||
|
|
||||||
protected JingleContentTransport(List<JingleContentTransportCandidate> candidates) {
|
protected JingleContentTransportElement(List<JingleContentTransportCandidateElement> candidates) {
|
||||||
this(candidates, null);
|
this(candidates, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected JingleContentTransport(List<JingleContentTransportCandidate> candidates, JingleContentTransportInfo info) {
|
protected JingleContentTransportElement(List<JingleContentTransportCandidateElement> candidates, JingleContentTransportInfoElement info) {
|
||||||
if (candidates != null) {
|
if (candidates != null) {
|
||||||
this.candidates = Collections.unmodifiableList(candidates);
|
this.candidates = Collections.unmodifiableList(candidates);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.candidates = Collections.emptyList();
|
this.candidates = Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.info = info;
|
this.info = info;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<JingleContentTransportCandidate> getCandidates() {
|
public List<JingleContentTransportCandidateElement> getCandidates() {
|
||||||
return candidates;
|
return candidates;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleContentTransportInfo getInfo() {
|
public JingleContentTransportInfoElement getInfo() {
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.element;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.NamedElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract JingleContentTransportInfo element.
|
||||||
|
* The JingleContentTransportInfo element can have certain states defined by the respective Transport XEP.
|
||||||
|
* Examples are Jingle Socks5Bytestream's <candidate-used/> (Example 5), <candidate-error/> (Example 7) etc.
|
||||||
|
* <jingle>
|
||||||
|
* <content>
|
||||||
|
* <description/>
|
||||||
|
* <transport>
|
||||||
|
* <xyz/> <- This is us.
|
||||||
|
* </transport>
|
||||||
|
* <security/>
|
||||||
|
* </content>
|
||||||
|
* </jingle>
|
||||||
|
*/
|
||||||
|
public abstract class JingleContentTransportInfoElement implements NamedElement {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,570 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2003-2007 Jive Software, 2014-2017 Florian Schmaus
|
||||||
|
*
|
||||||
|
* 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.element;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smack.packet.XMPPError;
|
||||||
|
import org.jivesoftware.smack.util.Objects;
|
||||||
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Jingle element.
|
||||||
|
*
|
||||||
|
* @author Florian Schmaus
|
||||||
|
*/
|
||||||
|
public final class JingleElement extends IQ {
|
||||||
|
|
||||||
|
public static final String NAMESPACE = "urn:xmpp:jingle:1";
|
||||||
|
|
||||||
|
public static final String ACTION_ATTRIBUTE_NAME = "action";
|
||||||
|
|
||||||
|
public static final String INITIATOR_ATTRIBUTE_NAME = "initiator";
|
||||||
|
|
||||||
|
public static final String RESPONDER_ATTRIBUTE_NAME = "responder";
|
||||||
|
|
||||||
|
public static final String SESSION_ID_ATTRIBUTE_NAME = "sid";
|
||||||
|
|
||||||
|
public static final String ELEMENT = "jingle";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The session ID related to this session. The session ID is a unique identifier generated by the initiator. This
|
||||||
|
* should match the XML Nmtoken production so that XML character escaping is not needed for characters such as &.
|
||||||
|
*/
|
||||||
|
private final String sessionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The jingle action. This attribute is required.
|
||||||
|
*/
|
||||||
|
private final JingleAction action;
|
||||||
|
|
||||||
|
private final FullJid initiator;
|
||||||
|
|
||||||
|
private final FullJid responder;
|
||||||
|
|
||||||
|
private final JingleReasonElement reason;
|
||||||
|
|
||||||
|
private final List<JingleContentElement> contents;
|
||||||
|
|
||||||
|
private JingleElement(String sessionId, JingleAction action, FullJid initiator, FullJid responder, JingleReasonElement reason,
|
||||||
|
List<JingleContentElement> contents) {
|
||||||
|
super(ELEMENT, NAMESPACE);
|
||||||
|
this.sessionId = StringUtils.requireNotNullOrEmpty(sessionId, "Jingle session ID must not be null");
|
||||||
|
this.action = Objects.requireNonNull(action, "Jingle action must not be null");
|
||||||
|
this.initiator = initiator;
|
||||||
|
this.responder = responder;
|
||||||
|
this.reason = reason;
|
||||||
|
if (contents != null) {
|
||||||
|
this.contents = Collections.unmodifiableList(contents);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.contents = Collections.emptyList();
|
||||||
|
}
|
||||||
|
setType(Type.set);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the initiator. The initiator will be the full JID of the entity that has initiated the flow (which may be
|
||||||
|
* different to the "from" address in the IQ)
|
||||||
|
*
|
||||||
|
* @return the initiator
|
||||||
|
*/
|
||||||
|
public FullJid getInitiator() {
|
||||||
|
return initiator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the responder. The responder is the full JID of the entity that has replied to the initiation (which may be
|
||||||
|
* different to the "to" addresss in the IQ).
|
||||||
|
*
|
||||||
|
* @return the responder
|
||||||
|
*/
|
||||||
|
public FullJid getResponder() {
|
||||||
|
return responder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the session ID related to the session. The session ID is a unique identifier generated by the initiator.
|
||||||
|
* This should match the XML Nmtoken production so that XML character escaping is not needed for characters such as
|
||||||
|
* &.
|
||||||
|
*
|
||||||
|
* @return Returns the session ID related to the session.
|
||||||
|
*/
|
||||||
|
public String getSid() {
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the action specified in the jingle IQ.
|
||||||
|
*
|
||||||
|
* @return the action.
|
||||||
|
*/
|
||||||
|
public JingleAction getAction() {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleReasonElement getReason() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a List of the contents.
|
||||||
|
*
|
||||||
|
* @return the contents.
|
||||||
|
*/
|
||||||
|
public List<JingleContentElement> getContents() {
|
||||||
|
return contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleContentElement getSoleContentOrThrow() {
|
||||||
|
if (contents.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contents.size() == 1) {
|
||||||
|
return contents.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException("More than one content is present.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
|
||||||
|
xml.optAttribute(INITIATOR_ATTRIBUTE_NAME, getInitiator());
|
||||||
|
xml.optAttribute(RESPONDER_ATTRIBUTE_NAME, getResponder());
|
||||||
|
xml.optAttribute(ACTION_ATTRIBUTE_NAME, getAction());
|
||||||
|
xml.optAttribute(SESSION_ID_ATTRIBUTE_NAME, getSid());
|
||||||
|
xml.rightAngleBracket();
|
||||||
|
|
||||||
|
xml.optElement(reason);
|
||||||
|
|
||||||
|
xml.append(contents);
|
||||||
|
|
||||||
|
return xml;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder getBuilder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Builder {
|
||||||
|
private String sid;
|
||||||
|
|
||||||
|
private JingleAction action;
|
||||||
|
|
||||||
|
private FullJid initiator;
|
||||||
|
|
||||||
|
private FullJid responder;
|
||||||
|
|
||||||
|
private JingleReasonElement reason;
|
||||||
|
|
||||||
|
private List<JingleContentElement> contents;
|
||||||
|
|
||||||
|
private Builder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setSessionId(String sessionId) {
|
||||||
|
this.sid = sessionId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setAction(JingleAction action) {
|
||||||
|
this.action = action;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setInitiator(FullJid initator) {
|
||||||
|
this.initiator = initator;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setResponder(FullJid responder) {
|
||||||
|
this.responder = responder;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder addJingleContent(JingleContentElement content) {
|
||||||
|
if (contents == null) {
|
||||||
|
contents = new ArrayList<>(1);
|
||||||
|
}
|
||||||
|
contents.add(content);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setReason(JingleReasonElement.Reason reason) {
|
||||||
|
this.reason = new JingleReasonElement(reason);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setReason(JingleReasonElement reason) {
|
||||||
|
this.reason = reason;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleElement build() {
|
||||||
|
return new JingleElement(sid, action, initiator, responder, reason, contents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JingleElement createContentAccept(FullJid recipient, String sessionId, List<JingleContentElement> contents) {
|
||||||
|
JingleElement.Builder builder = JingleElement.getBuilder().setAction(JingleAction.content_accept).setSessionId(sessionId);
|
||||||
|
|
||||||
|
for (JingleContentElement element : contents) {
|
||||||
|
builder.addJingleContent(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleElement jingleElement = builder.build();
|
||||||
|
jingleElement.setTo(recipient);
|
||||||
|
|
||||||
|
return jingleElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept a session.
|
||||||
|
* XEP-0166 Example 17.
|
||||||
|
* @param initiator recipient of the stanza.
|
||||||
|
* @param responder our jid.
|
||||||
|
* @param sessionId sessionId.
|
||||||
|
* @param content content
|
||||||
|
* @return session-accept stanza.
|
||||||
|
*/
|
||||||
|
public static JingleElement createSessionAccept(FullJid initiator,
|
||||||
|
FullJid responder,
|
||||||
|
String sessionId,
|
||||||
|
JingleContentElement content) {
|
||||||
|
return createSessionAccept(initiator, responder, sessionId, Collections.singletonList(content));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept a session.
|
||||||
|
* XEP-0166 Example 17.
|
||||||
|
* @param initiator recipient of the stanza.
|
||||||
|
* @param responder our jid.
|
||||||
|
* @param sessionId sessionId.
|
||||||
|
* @param contents contents
|
||||||
|
* @return session-accept stanza.
|
||||||
|
*/
|
||||||
|
public static JingleElement createSessionAccept(FullJid initiator,
|
||||||
|
FullJid responder,
|
||||||
|
String sessionId,
|
||||||
|
List<JingleContentElement> contents) {
|
||||||
|
JingleElement.Builder jb = JingleElement.getBuilder();
|
||||||
|
jb.setResponder(responder)
|
||||||
|
.setAction(JingleAction.session_accept)
|
||||||
|
.setSessionId(sessionId);
|
||||||
|
|
||||||
|
for (JingleContentElement content : contents) {
|
||||||
|
jb.addJingleContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleElement jingle = jb.build();
|
||||||
|
jingle.setTo(initiator);
|
||||||
|
jingle.setFrom(responder);
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate a Jingle session.
|
||||||
|
* XEP-0166 Example 10.
|
||||||
|
* @param initiator our jid.
|
||||||
|
* @param responder jid of the recipient.
|
||||||
|
* @param sessionId sessionId.
|
||||||
|
* @param content content.
|
||||||
|
* @return session-initiate stanza.
|
||||||
|
*/
|
||||||
|
public static JingleElement createSessionInitiate(FullJid initiator,
|
||||||
|
FullJid responder,
|
||||||
|
String sessionId,
|
||||||
|
JingleContentElement content) {
|
||||||
|
return createSessionInitiate(initiator, responder, sessionId, Collections.singletonList(content));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate a Jingle session.
|
||||||
|
* XEP-0166 Example 10.
|
||||||
|
* @param initiator our jid.
|
||||||
|
* @param responder jid of the recipient.
|
||||||
|
* @param sessionId sessionId.
|
||||||
|
* @param contents contents.
|
||||||
|
* @return session-initiate stanza.
|
||||||
|
*/
|
||||||
|
public static JingleElement createSessionInitiate(FullJid initiator,
|
||||||
|
FullJid responder,
|
||||||
|
String sessionId,
|
||||||
|
List<JingleContentElement> contents) {
|
||||||
|
|
||||||
|
JingleElement.Builder builder = JingleElement.getBuilder();
|
||||||
|
builder.setAction(JingleAction.session_initiate)
|
||||||
|
.setSessionId(sessionId)
|
||||||
|
.setInitiator(initiator);
|
||||||
|
|
||||||
|
for (JingleContentElement content : contents) {
|
||||||
|
builder.addJingleContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleElement jingle = builder.build();
|
||||||
|
jingle.setTo(responder);
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a session ping stanza.
|
||||||
|
* XEP-0166 Example 32.
|
||||||
|
* @param recipient recipient of the stanza.
|
||||||
|
* @param sessionId id of the session.
|
||||||
|
* @return ping stanza
|
||||||
|
*/
|
||||||
|
public static JingleElement createSessionPing(FullJid recipient, String sessionId) {
|
||||||
|
JingleElement.Builder jb = JingleElement.getBuilder();
|
||||||
|
jb.setSessionId(sessionId)
|
||||||
|
.setAction(JingleAction.session_info);
|
||||||
|
|
||||||
|
JingleElement jingle = jb.build();
|
||||||
|
jingle.setTo(recipient);
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a session-terminate stanza.
|
||||||
|
* XEP-0166 §6.7.
|
||||||
|
* @param recipient recipient of the stanza.
|
||||||
|
* @param sessionId sessionId.
|
||||||
|
* @param reason reason of termination.
|
||||||
|
* @return session-terminate stanza.
|
||||||
|
*/
|
||||||
|
public static JingleElement createSessionTerminate(FullJid recipient, String sessionId, JingleReasonElement reason) {
|
||||||
|
JingleElement.Builder jb = JingleElement.getBuilder();
|
||||||
|
jb.setAction(JingleAction.session_terminate)
|
||||||
|
.setSessionId(sessionId)
|
||||||
|
.setReason(reason);
|
||||||
|
|
||||||
|
JingleElement jingle = jb.build();
|
||||||
|
jingle.setTo(recipient);
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a session-terminate stanza.
|
||||||
|
* XEP-0166 §6.7.
|
||||||
|
* @param recipient recipient of the stanza.
|
||||||
|
* @param sessionId sessionId.
|
||||||
|
* @param reason reason of termination.
|
||||||
|
* @return session-terminate stanza.
|
||||||
|
*/
|
||||||
|
public static JingleElement createSessionTerminate(FullJid recipient, String sessionId, JingleReasonElement.Reason reason) {
|
||||||
|
return createSessionTerminate(recipient, sessionId, new JingleReasonElement(reason));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel a single contents transfer.
|
||||||
|
* XEP-0234 Example 10.
|
||||||
|
* @param recipient recipient of the stanza.
|
||||||
|
* @param sessionId sessionId.
|
||||||
|
* @param contentCreator creator of the content.
|
||||||
|
* @param contentName name of the content.
|
||||||
|
* @return session-terminate stanza.
|
||||||
|
*/
|
||||||
|
public static JingleElement createSessionTerminateContentCancel(FullJid recipient, String sessionId,
|
||||||
|
JingleContentElement.Creator contentCreator, String contentName) {
|
||||||
|
JingleElement.Builder jb = JingleElement.getBuilder();
|
||||||
|
jb.setAction(JingleAction.session_terminate)
|
||||||
|
.setSessionId(sessionId).setReason(JingleReasonElement.Reason.cancel);
|
||||||
|
|
||||||
|
JingleContentElement.Builder cb = JingleContentElement.getBuilder();
|
||||||
|
cb.setCreator(contentCreator).setName(contentName);
|
||||||
|
|
||||||
|
JingleElement jingle = jb.addJingleContent(cb.build()).build();
|
||||||
|
jingle.setTo(recipient);
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept a transport.
|
||||||
|
* XEP-0260 Example 17.
|
||||||
|
* @param recipient recipient of the stanza
|
||||||
|
* @param initiator initiator of the session
|
||||||
|
* @param sessionId sessionId
|
||||||
|
* @param contentCreator creator of the content
|
||||||
|
* @param contentName name of the content
|
||||||
|
* @param transport transport to accept
|
||||||
|
* @return transport-accept stanza
|
||||||
|
*/
|
||||||
|
public static JingleElement createTransportAccept(FullJid initiator, FullJid recipient, String sessionId,
|
||||||
|
JingleContentElement.Creator contentCreator,
|
||||||
|
String contentName, JingleContentTransportElement transport) {
|
||||||
|
JingleElement.Builder jb = JingleElement.getBuilder();
|
||||||
|
jb.setAction(JingleAction.transport_accept)
|
||||||
|
.setInitiator(initiator)
|
||||||
|
.setSessionId(sessionId);
|
||||||
|
|
||||||
|
JingleContentElement.Builder cb = JingleContentElement.getBuilder();
|
||||||
|
cb.setCreator(contentCreator).setName(contentName).setTransport(transport);
|
||||||
|
|
||||||
|
JingleElement jingle = jb.addJingleContent(cb.build()).build();
|
||||||
|
jingle.setTo(recipient);
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reject a transport.
|
||||||
|
* XEP-0166 §7.2.14.
|
||||||
|
* @param recipient recipient of the stanza
|
||||||
|
* @param initiator initiator of the session
|
||||||
|
* @param sessionId sessionId
|
||||||
|
* @param contentCreator creator of the content
|
||||||
|
* @param contentName name of the content
|
||||||
|
* @param transport transport to reject
|
||||||
|
* @return transport-reject stanza
|
||||||
|
*/
|
||||||
|
public static JingleElement createTransportReject(FullJid initiator, FullJid recipient, String sessionId,
|
||||||
|
JingleContentElement.Creator contentCreator,
|
||||||
|
String contentName, JingleContentTransportElement transport) {
|
||||||
|
JingleElement.Builder jb = JingleElement.getBuilder();
|
||||||
|
jb.setAction(JingleAction.transport_reject)
|
||||||
|
.setInitiator(initiator)
|
||||||
|
.setSessionId(sessionId);
|
||||||
|
|
||||||
|
JingleContentElement.Builder cb = JingleContentElement.getBuilder();
|
||||||
|
cb.setCreator(contentCreator).setName(contentName).setTransport(transport);
|
||||||
|
|
||||||
|
JingleElement jingle = jb.addJingleContent(cb.build()).build();
|
||||||
|
jingle.setTo(recipient);
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace a transport with another one.
|
||||||
|
* XEP-0260 Example 15.
|
||||||
|
* @param recipient recipient of the stanza
|
||||||
|
* @param initiator initiator of the session
|
||||||
|
* @param sessionId sessionId
|
||||||
|
* @param contentCreator creator of the content
|
||||||
|
* @param contentName name of the content
|
||||||
|
* @param transport proposed transport
|
||||||
|
* @return transport-replace stanza
|
||||||
|
*/
|
||||||
|
public static JingleElement createTransportReplace(FullJid initiator, FullJid recipient, String sessionId,
|
||||||
|
JingleContentElement.Creator contentCreator,
|
||||||
|
String contentName, JingleContentTransportElement transport) {
|
||||||
|
JingleElement.Builder jb = JingleElement.getBuilder()
|
||||||
|
.setAction(JingleAction.transport_replace)
|
||||||
|
.setSessionId(sessionId)
|
||||||
|
.setInitiator(initiator);
|
||||||
|
|
||||||
|
JingleContentElement content = JingleContentElement.getBuilder()
|
||||||
|
.setCreator(contentCreator)
|
||||||
|
.setName(contentName)
|
||||||
|
.setTransport(transport).build();
|
||||||
|
|
||||||
|
jb.addJingleContent(content);
|
||||||
|
|
||||||
|
JingleElement jingle = jb.build();
|
||||||
|
jingle.setTo(recipient);
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an error response to a request with an unknown session id.
|
||||||
|
* XEP-0166 Example 29.
|
||||||
|
* @param request request with unknown sessionId.
|
||||||
|
* @return error stanza.
|
||||||
|
*/
|
||||||
|
public static IQ createJingleErrorUnknownSession(JingleElement request) {
|
||||||
|
XMPPError.Builder error = XMPPError.getBuilder();
|
||||||
|
error.setCondition(XMPPError.Condition.item_not_found)
|
||||||
|
.addExtension(JingleErrorElement.UNKNOWN_SESSION);
|
||||||
|
return IQ.createErrorResponse(request, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an error response to a request coming from a unknown initiator.
|
||||||
|
* XEP-0166 Example 12.
|
||||||
|
* @param request request from unknown initiator.
|
||||||
|
* @return error stanza.
|
||||||
|
*/
|
||||||
|
public static IQ createJingleErrorUnknownInitiator(JingleElement request) {
|
||||||
|
XMPPError.Builder b = XMPPError.getBuilder().setType(XMPPError.Type.CANCEL).setCondition(XMPPError.Condition.service_unavailable);
|
||||||
|
return IQ.createErrorResponse(request, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an error response to a request with an unsupported info.
|
||||||
|
* XEP-0166 Example 31.
|
||||||
|
* @param request request with unsupported info.
|
||||||
|
* @return error stanza.
|
||||||
|
*/
|
||||||
|
public static IQ createJingleErrorUnsupportedInfo(JingleElement request) {
|
||||||
|
XMPPError.Builder error = XMPPError.getBuilder();
|
||||||
|
error.setCondition(XMPPError.Condition.feature_not_implemented)
|
||||||
|
.addExtension(JingleErrorElement.UNSUPPORTED_INFO).setType(XMPPError.Type.MODIFY);
|
||||||
|
return IQ.createErrorResponse(request, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an error response to a tie-breaking request.
|
||||||
|
* XEP-0166 Example 34.
|
||||||
|
* @param request tie-breaking request
|
||||||
|
* @return error stanza
|
||||||
|
*/
|
||||||
|
public static IQ createJingleErrorTieBreak(JingleElement request) {
|
||||||
|
XMPPError.Builder error = XMPPError.getBuilder();
|
||||||
|
error.setCondition(XMPPError.Condition.conflict)
|
||||||
|
.addExtension(JingleErrorElement.TIE_BREAK);
|
||||||
|
return IQ.createErrorResponse(request, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an error response to a request that was out of order.
|
||||||
|
* TODO: Find example.
|
||||||
|
* @param request request out of order.
|
||||||
|
* @return error stanza.
|
||||||
|
*/
|
||||||
|
public static IQ createJingleErrorOutOfOrder(JingleElement request) {
|
||||||
|
XMPPError.Builder error = XMPPError.getBuilder();
|
||||||
|
error.setCondition(XMPPError.Condition.unexpected_request)
|
||||||
|
.addExtension(JingleErrorElement.OUT_OF_ORDER);
|
||||||
|
return IQ.createErrorResponse(request, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an error response to a malformed request.
|
||||||
|
* XEP-0166 Ex. 16
|
||||||
|
* @param request malformed request
|
||||||
|
* @return error stanza.
|
||||||
|
*/
|
||||||
|
public static IQ createJingleErrorMalformedRequest(JingleElement request) {
|
||||||
|
XMPPError.Builder error = XMPPError.getBuilder();
|
||||||
|
error.setType(XMPPError.Type.CANCEL);
|
||||||
|
error.setCondition(XMPPError.Condition.bad_request);
|
||||||
|
return IQ.createErrorResponse(request, error);
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,17 +22,17 @@ import java.util.Locale;
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
public final class JingleError implements ExtensionElement {
|
public final class JingleErrorElement implements ExtensionElement {
|
||||||
|
|
||||||
public static String NAMESPACE = "urn:xmpp:jingle:errors:1";
|
public static String NAMESPACE = "urn:xmpp:jingle:errors:1";
|
||||||
|
|
||||||
public static final JingleError OUT_OF_ORDER = new JingleError("out-of-order");
|
static final JingleErrorElement OUT_OF_ORDER = new JingleErrorElement("out-of-order");
|
||||||
|
|
||||||
public static final JingleError TIE_BREAK = new JingleError("tie-break");
|
static final JingleErrorElement TIE_BREAK = new JingleErrorElement("tie-break");
|
||||||
|
|
||||||
public static final JingleError UNKNOWN_SESSION = new JingleError("unknown-session");
|
static final JingleErrorElement UNKNOWN_SESSION = new JingleErrorElement("unknown-session");
|
||||||
|
|
||||||
public static final JingleError UNSUPPORTED_INFO = new JingleError("unsupported-info");
|
static final JingleErrorElement UNSUPPORTED_INFO = new JingleErrorElement("unsupported-info");
|
||||||
|
|
||||||
private final String errorName;
|
private final String errorName;
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ public final class JingleError implements ExtensionElement {
|
||||||
*
|
*
|
||||||
* @param errorName a name describing the error.
|
* @param errorName a name describing the error.
|
||||||
*/
|
*/
|
||||||
private JingleError(final String errorName) {
|
private JingleErrorElement(final String errorName) {
|
||||||
this.errorName = errorName;
|
this.errorName = errorName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ public final class JingleError implements ExtensionElement {
|
||||||
/**
|
/**
|
||||||
* Returns a Action instance associated with the String value.
|
* Returns a Action instance associated with the String value.
|
||||||
*/
|
*/
|
||||||
public static JingleError fromString(String value) {
|
public static JingleErrorElement fromString(String value) {
|
||||||
value = value.toLowerCase(Locale.US);
|
value = value.toLowerCase(Locale.US);
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case "out-of-order":
|
case "out-of-order":
|
|
@ -29,7 +29,7 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
* @see <a href="https://xmpp.org/extensions/xep-0166.html#def-reason">XEP-0166 § 7.4</a>
|
* @see <a href="https://xmpp.org/extensions/xep-0166.html#def-reason">XEP-0166 § 7.4</a>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class JingleReason implements NamedElement {
|
public class JingleReasonElement implements NamedElement {
|
||||||
|
|
||||||
public static final String ELEMENT = "reason";
|
public static final String ELEMENT = "reason";
|
||||||
|
|
||||||
|
@ -37,22 +37,22 @@ public class JingleReason implements NamedElement {
|
||||||
return new AlternativeSession(sessionId);
|
return new AlternativeSession(sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final JingleReason Busy = new JingleReason(Reason.busy);
|
public static final JingleReasonElement Busy = new JingleReasonElement(Reason.busy);
|
||||||
public static final JingleReason Cancel = new JingleReason(Reason.cancel);
|
public static final JingleReasonElement Cancel = new JingleReasonElement(Reason.cancel);
|
||||||
public static final JingleReason ConnectivityError = new JingleReason(Reason.connectivity_error);
|
public static final JingleReasonElement ConnectivityError = new JingleReasonElement(Reason.connectivity_error);
|
||||||
public static final JingleReason Decline = new JingleReason(Reason.decline);
|
public static final JingleReasonElement Decline = new JingleReasonElement(Reason.decline);
|
||||||
public static final JingleReason Expired = new JingleReason(Reason.expired);
|
public static final JingleReasonElement Expired = new JingleReasonElement(Reason.expired);
|
||||||
public static final JingleReason FailedApplication = new JingleReason(Reason.failed_application);
|
public static final JingleReasonElement FailedApplication = new JingleReasonElement(Reason.failed_application);
|
||||||
public static final JingleReason FailedTransport = new JingleReason(Reason.failed_transport);
|
public static final JingleReasonElement FailedTransport = new JingleReasonElement(Reason.failed_transport);
|
||||||
public static final JingleReason GeneralError = new JingleReason(Reason.general_error);
|
public static final JingleReasonElement GeneralError = new JingleReasonElement(Reason.general_error);
|
||||||
public static final JingleReason Gone = new JingleReason(Reason.gone);
|
public static final JingleReasonElement Gone = new JingleReasonElement(Reason.gone);
|
||||||
public static final JingleReason IncompatibleParameters = new JingleReason(Reason.incompatible_parameters);
|
public static final JingleReasonElement IncompatibleParameters = new JingleReasonElement(Reason.incompatible_parameters);
|
||||||
public static final JingleReason MediaError = new JingleReason(Reason.media_error);
|
public static final JingleReasonElement MediaError = new JingleReasonElement(Reason.media_error);
|
||||||
public static final JingleReason SecurityError = new JingleReason(Reason.security_error);
|
public static final JingleReasonElement SecurityError = new JingleReasonElement(Reason.security_error);
|
||||||
public static final JingleReason Success = new JingleReason(Reason.success);
|
public static final JingleReasonElement Success = new JingleReasonElement(Reason.success);
|
||||||
public static final JingleReason Timeout = new JingleReason(Reason.timeout);
|
public static final JingleReasonElement Timeout = new JingleReasonElement(Reason.timeout);
|
||||||
public static final JingleReason UnsupportedApplications = new JingleReason(Reason.unsupported_applications);
|
public static final JingleReasonElement UnsupportedApplications = new JingleReasonElement(Reason.unsupported_applications);
|
||||||
public static final JingleReason UnsupportedTransports = new JingleReason(Reason.unsupported_transports);
|
public static final JingleReasonElement UnsupportedTransports = new JingleReasonElement(Reason.unsupported_transports);
|
||||||
|
|
||||||
public enum Reason {
|
public enum Reason {
|
||||||
alternative_session,
|
alternative_session,
|
||||||
|
@ -104,7 +104,7 @@ public class JingleReason implements NamedElement {
|
||||||
|
|
||||||
protected final Reason reason;
|
protected final Reason reason;
|
||||||
|
|
||||||
public JingleReason(Reason reason) {
|
public JingleReasonElement(Reason reason) {
|
||||||
this.reason = reason;
|
this.reason = reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,9 +129,9 @@ public class JingleReason implements NamedElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class AlternativeSession extends JingleReason {
|
public static class AlternativeSession extends JingleReasonElement {
|
||||||
|
|
||||||
public static final String SID = "sid";
|
public static final String ATTR_SID = "sid";
|
||||||
private final String sessionId;
|
private final String sessionId;
|
||||||
|
|
||||||
public AlternativeSession(String sessionId) {
|
public AlternativeSession(String sessionId) {
|
||||||
|
@ -148,9 +148,9 @@ public class JingleReason implements NamedElement {
|
||||||
xml.rightAngleBracket();
|
xml.rightAngleBracket();
|
||||||
|
|
||||||
xml.openElement(reason.asString);
|
xml.openElement(reason.asString);
|
||||||
xml.openElement(SID);
|
xml.openElement(ATTR_SID);
|
||||||
xml.append(sessionId);
|
xml.append(sessionId);
|
||||||
xml.closeElement(SID);
|
xml.closeElement(ATTR_SID);
|
||||||
xml.closeElement(reason.asString);
|
xml.closeElement(reason.asString);
|
||||||
|
|
||||||
xml.closeElement(this);
|
xml.closeElement(this);
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Florian Schmaus.
|
* Copyright 2017 Florian Schmaus, Paul Schaub.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -19,11 +19,11 @@ package org.jivesoftware.smackx.jingle.element;
|
||||||
import org.jivesoftware.smack.packet.StandardExtensionElement;
|
import org.jivesoftware.smack.packet.StandardExtensionElement;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
public final class UnknownJingleContentDescription extends JingleContentDescription {
|
public final class UnknownJingleContentDescriptionElement extends JingleContentDescriptionElement {
|
||||||
|
|
||||||
private final StandardExtensionElement standardExtensionElement;
|
private final StandardExtensionElement standardExtensionElement;
|
||||||
|
|
||||||
public UnknownJingleContentDescription(StandardExtensionElement standardExtensionElement) {
|
public UnknownJingleContentDescriptionElement(StandardExtensionElement standardExtensionElement) {
|
||||||
super(standardExtensionElement.getElements());
|
super(standardExtensionElement.getElements());
|
||||||
this.standardExtensionElement = standardExtensionElement;
|
this.standardExtensionElement = standardExtensionElement;
|
||||||
}
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.element;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.StandardExtensionElement;
|
||||||
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
|
public final class UnknownJingleContentSecurityElement extends JingleContentSecurityElement {
|
||||||
|
|
||||||
|
private final StandardExtensionElement standardExtensionElement;
|
||||||
|
|
||||||
|
public UnknownJingleContentSecurityElement(StandardExtensionElement standardExtensionElement) {
|
||||||
|
super();
|
||||||
|
this.standardExtensionElement = standardExtensionElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getElementName() {
|
||||||
|
return standardExtensionElement.getElementName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return standardExtensionElement.getNamespace();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XmlStringBuilder toXML() {
|
||||||
|
return standardExtensionElement.toXML();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleContentSecurityInfoElement getSecurityInfo() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StandardExtensionElement getStandardExtensionElement() {
|
||||||
|
return standardExtensionElement;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Florian Schmaus.
|
* Copyright 2017 Florian Schmaus, Paul Schaub.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -21,11 +21,11 @@ import java.util.List;
|
||||||
import org.jivesoftware.smack.packet.StandardExtensionElement;
|
import org.jivesoftware.smack.packet.StandardExtensionElement;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
public final class UnknownJingleContentTransport extends JingleContentTransport {
|
public final class UnknownJingleContentTransportElement extends JingleContentTransportElement {
|
||||||
|
|
||||||
private final StandardExtensionElement standardExtensionElement;
|
private final StandardExtensionElement standardExtensionElement;
|
||||||
|
|
||||||
public UnknownJingleContentTransport(StandardExtensionElement standardExtensionElement) {
|
public UnknownJingleContentTransportElement(StandardExtensionElement standardExtensionElement) {
|
||||||
super(null, null);
|
super(null, null);
|
||||||
this.standardExtensionElement = standardExtensionElement;
|
this.standardExtensionElement = standardExtensionElement;
|
||||||
}
|
}
|
||||||
|
@ -46,12 +46,12 @@ public final class UnknownJingleContentTransport extends JingleContentTransport
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<JingleContentTransportCandidate> getCandidates() {
|
public List<JingleContentTransportCandidateElement> getCandidates() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JingleContentTransportInfo getInfo() {
|
public JingleContentTransportInfoElement getInfo() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by vanitas on 25.07.17.
|
||||||
|
*/
|
||||||
|
public class FailedTransportException extends Exception {
|
||||||
|
|
||||||
|
protected static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public FailedTransportException(Throwable throwable) {
|
||||||
|
super(throwable);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,25 +14,22 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle_filetransfer.element;
|
package org.jivesoftware.smackx.jingle.exception;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
|
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File element.
|
* Exception that gets thrown when an unknown/unsupported description element is received.
|
||||||
*/
|
*/
|
||||||
public class JingleFileTransfer extends JingleContentDescription {
|
public class UnsupportedDescriptionException extends Exception {
|
||||||
public static final String NAMESPACE_V5 = "urn:xmpp:jingle:apps:file-transfer:5";
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
public JingleFileTransfer(List<JingleContentDescriptionChildElement> payloads) {
|
private final String namespace;
|
||||||
super(payloads);
|
|
||||||
|
public UnsupportedDescriptionException(String namespace) {
|
||||||
|
super("Description with namespace " + namespace + " not supported.");
|
||||||
|
this.namespace = namespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getNamespace() {
|
public String getNamespace() {
|
||||||
return NAMESPACE_V5;
|
return namespace;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,20 +14,22 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle.transports;
|
package org.jivesoftware.smackx.jingle.exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by vanitas on 25.06.17.
|
* Exception that gets thrown when an unknown/unsupported security element gets received.
|
||||||
*/
|
*/
|
||||||
public abstract class JingleTransportInitiationException extends Exception {
|
public class UnsupportedSecurityException extends Exception {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final String namespace;
|
||||||
|
|
||||||
public static class ProxyError extends JingleTransportInitiationException {
|
public UnsupportedSecurityException(String namespace) {
|
||||||
private static final long serialVersionUID = 1L;
|
super("Security with namespace " + namespace + " not supported.");
|
||||||
|
this.namespace = namespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class CandidateError extends JingleTransportInitiationException {
|
public String getNamespace() {
|
||||||
private static final long serialVersionUID = 1L;
|
return namespace;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception that gets thrown when an unknown/unsupported transport element gets received.
|
||||||
|
*/
|
||||||
|
public class UnsupportedTransportException extends Exception {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final String namespace;
|
||||||
|
|
||||||
|
public UnsupportedTransportException(String namespace) {
|
||||||
|
super("Transport with namespace " + namespace + " not supported.");
|
||||||
|
this.namespace = namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNamespace() {
|
||||||
|
return namespace;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Florian Schmaus, 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 <a href="https://xmpp.org/extensions/xep-0166.html">XEP-0166: Jingle</a>.
|
||||||
|
* Exceptions.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle.exception;
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Florian Schmaus
|
* Copyright 2017 Florian Schmaus, Paul Schaub
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
|
@ -17,15 +17,15 @@
|
||||||
package org.jivesoftware.smackx.jingle.provider;
|
package org.jivesoftware.smackx.jingle.provider;
|
||||||
|
|
||||||
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionElement;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
|
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
public abstract class JingleContentDescriptionProvider<D extends JingleContentDescription>
|
public abstract class JingleContentDescriptionProvider<D extends JingleContentDescriptionElement>
|
||||||
extends ExtensionElementProvider<D> {
|
extends ExtensionElementProvider<D> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public abstract D parse(XmlPullParser parser, int initialDepth) throws Exception;
|
public abstract D parse(XmlPullParser parser, int initialDepth) throws Exception;
|
||||||
|
|
||||||
|
public abstract String getNamespace();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright 2017 Florian Schmaus
|
|
||||||
*
|
|
||||||
* 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.provider;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
public class JingleContentProviderManager {
|
|
||||||
|
|
||||||
private static final Map<String, JingleContentDescriptionProvider<?>> jingleContentDescriptionProviders = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
private static final Map<String, JingleContentTransportProvider<?>> jingleContentTransportProviders = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public static JingleContentDescriptionProvider<?> addJingleContentDescriptionProvider(String namespace,
|
|
||||||
JingleContentDescriptionProvider<?> provider) {
|
|
||||||
return jingleContentDescriptionProviders.put(namespace, provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JingleContentDescriptionProvider<?> getJingleContentDescriptionProvider(String namespace) {
|
|
||||||
return jingleContentDescriptionProviders.get(namespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JingleContentTransportProvider<?> addJingleContentTransportProvider(String namespace,
|
|
||||||
JingleContentTransportProvider<?> provider) {
|
|
||||||
return jingleContentTransportProviders.put(namespace, provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JingleContentTransportProvider<?> getJingleContentTransportProvider(String namespace) {
|
|
||||||
return jingleContentTransportProviders.get(namespace);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.provider;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentSecurityElement;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by vanitas on 18.07.17.
|
||||||
|
*/
|
||||||
|
public abstract class JingleContentSecurityProvider<D extends JingleContentSecurityElement> extends ExtensionElementProvider<D> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract D parse(XmlPullParser parser, int initialDepth) throws Exception;
|
||||||
|
|
||||||
|
public abstract String getNamespace();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -17,15 +17,15 @@
|
||||||
package org.jivesoftware.smackx.jingle.provider;
|
package org.jivesoftware.smackx.jingle.provider;
|
||||||
|
|
||||||
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
public abstract class JingleContentTransportProvider<T extends JingleContentTransport>
|
public abstract class JingleContentTransportProvider<T extends JingleContentTransportElement>
|
||||||
extends ExtensionElementProvider<T> {
|
extends ExtensionElementProvider<T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public abstract T parse(XmlPullParser parser, int initialDepth) throws Exception;
|
public abstract T parse(XmlPullParser parser, int initialDepth) throws Exception;
|
||||||
|
|
||||||
|
public abstract String getNamespace();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,17 +17,16 @@
|
||||||
package org.jivesoftware.smackx.jingle.provider;
|
package org.jivesoftware.smackx.jingle.provider;
|
||||||
|
|
||||||
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleErrorElement;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleError;
|
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
public class JingleErrorProvider extends ExtensionElementProvider<JingleError> {
|
public class JingleErrorProvider extends ExtensionElementProvider<JingleErrorElement> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JingleError parse(XmlPullParser parser, int initialDepth) throws Exception {
|
public JingleErrorElement parse(XmlPullParser parser, int initialDepth) throws Exception {
|
||||||
String errorName = parser.getName();
|
String errorName = parser.getName();
|
||||||
return JingleError.fromString(errorName);
|
return JingleErrorElement.fromString(errorName);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,144 +22,157 @@ import org.jivesoftware.smack.packet.StandardExtensionElement;
|
||||||
import org.jivesoftware.smack.parsing.StandardExtensionElementProvider;
|
import org.jivesoftware.smack.parsing.StandardExtensionElementProvider;
|
||||||
import org.jivesoftware.smack.provider.IQProvider;
|
import org.jivesoftware.smack.provider.IQProvider;
|
||||||
import org.jivesoftware.smack.util.ParserUtils;
|
import org.jivesoftware.smack.util.ParserUtils;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleManager;
|
||||||
import org.jivesoftware.smackx.jingle.element.Jingle;
|
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleAction;
|
import org.jivesoftware.smackx.jingle.element.JingleAction;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionElement;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
|
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
import org.jivesoftware.smackx.jingle.element.JingleContentSecurityElement;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleReason;
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleReason.Reason;
|
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||||
import org.jivesoftware.smackx.jingle.element.UnknownJingleContentDescription;
|
import org.jivesoftware.smackx.jingle.element.JingleReasonElement;
|
||||||
import org.jivesoftware.smackx.jingle.element.UnknownJingleContentTransport;
|
import org.jivesoftware.smackx.jingle.element.UnknownJingleContentDescriptionElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.UnknownJingleContentSecurityElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.UnknownJingleContentTransportElement;
|
||||||
|
|
||||||
import org.jxmpp.jid.FullJid;
|
import org.jxmpp.jid.FullJid;
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
public class JingleProvider extends IQProvider<Jingle> {
|
public class JingleProvider extends IQProvider<JingleElement> {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(JingleProvider.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(JingleProvider.class.getName());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Jingle parse(XmlPullParser parser, int initialDepth) throws Exception {
|
public JingleElement parse(XmlPullParser parser, int initialDepth) throws Exception {
|
||||||
Jingle.Builder builder = Jingle.getBuilder();
|
JingleElement.Builder builder = JingleElement.getBuilder();
|
||||||
|
|
||||||
String actionString = parser.getAttributeValue("", Jingle.ACTION_ATTRIBUTE_NAME);
|
String actionString = parser.getAttributeValue("", JingleElement.ACTION_ATTRIBUTE_NAME);
|
||||||
if (actionString != null) {
|
if (actionString != null) {
|
||||||
JingleAction action = JingleAction.fromString(actionString);
|
JingleAction action = JingleAction.fromString(actionString);
|
||||||
builder.setAction(action);
|
builder.setAction(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
FullJid initiator = ParserUtils.getFullJidAttribute(parser, Jingle.INITIATOR_ATTRIBUTE_NAME);
|
FullJid initiator = ParserUtils.getFullJidAttribute(parser, JingleElement.INITIATOR_ATTRIBUTE_NAME);
|
||||||
builder.setInitiator(initiator);
|
builder.setInitiator(initiator);
|
||||||
|
|
||||||
FullJid responder = ParserUtils.getFullJidAttribute(parser, Jingle.RESPONDER_ATTRIBUTE_NAME);
|
FullJid responder = ParserUtils.getFullJidAttribute(parser, JingleElement.RESPONDER_ATTRIBUTE_NAME);
|
||||||
builder.setResponder(responder);
|
builder.setResponder(responder);
|
||||||
|
|
||||||
String sessionId = parser.getAttributeValue("", Jingle.SESSION_ID_ATTRIBUTE_NAME);
|
String sessionId = parser.getAttributeValue("", JingleElement.SESSION_ID_ATTRIBUTE_NAME);
|
||||||
builder.setSessionId(sessionId);
|
builder.setSessionId(sessionId);
|
||||||
|
|
||||||
|
|
||||||
outerloop: while (true) {
|
outerloop: while (true) {
|
||||||
int eventType = parser.next();
|
int eventType = parser.next();
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case XmlPullParser.START_TAG:
|
case XmlPullParser.START_TAG:
|
||||||
String tagName = parser.getName();
|
String tagName = parser.getName();
|
||||||
switch (tagName) {
|
switch (tagName) {
|
||||||
case JingleContent.ELEMENT:
|
case JingleContentElement.ELEMENT:
|
||||||
JingleContent content = parseJingleContent(parser, parser.getDepth());
|
JingleContentElement content = parseJingleContent(parser, parser.getDepth());
|
||||||
builder.addJingleContent(content);
|
builder.addJingleContent(content);
|
||||||
break;
|
break;
|
||||||
case JingleReason.ELEMENT:
|
case JingleReasonElement.ELEMENT:
|
||||||
parser.next();
|
parser.next();
|
||||||
String reasonString = parser.getName();
|
String reasonString = parser.getName();
|
||||||
JingleReason reason;
|
JingleReasonElement reason;
|
||||||
if (reasonString.equals("alternative-session")) {
|
if (reasonString.equals("alternative-session")) {
|
||||||
parser.next();
|
parser.next();
|
||||||
String sid = parser.nextText();
|
String sid = parser.nextText();
|
||||||
reason = new JingleReason.AlternativeSession(sid);
|
reason = new JingleReasonElement.AlternativeSession(sid);
|
||||||
} else {
|
} else {
|
||||||
reason = new JingleReason(Reason.fromString(reasonString));
|
reason = new JingleReasonElement(JingleReasonElement.Reason.fromString(reasonString));
|
||||||
|
}
|
||||||
|
builder.setReason(reason);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOGGER.severe("Unknown Jingle element: " + tagName);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
builder.setReason(reason);
|
|
||||||
break;
|
break;
|
||||||
default:
|
case XmlPullParser.END_TAG:
|
||||||
LOGGER.severe("Unknown Jingle element: " + tagName);
|
if (parser.getDepth() == initialDepth) {
|
||||||
break;
|
break outerloop;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case XmlPullParser.END_TAG:
|
|
||||||
if (parser.getDepth() == initialDepth) {
|
|
||||||
break outerloop;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JingleContent parseJingleContent(XmlPullParser parser, final int initialDepth)
|
private static JingleContentElement parseJingleContent(XmlPullParser parser, final int initialDepth)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
JingleContent.Builder builder = JingleContent.getBuilder();
|
JingleContentElement.Builder builder = JingleContentElement.getBuilder();
|
||||||
|
|
||||||
String creatorString = parser.getAttributeValue("", JingleContent.CREATOR_ATTRIBUTE_NAME);
|
String creatorString = parser.getAttributeValue("", JingleContentElement.CREATOR_ATTRIBUTE_NAME);
|
||||||
JingleContent.Creator creator = JingleContent.Creator.valueOf(creatorString);
|
JingleContentElement.Creator creator = JingleContentElement.Creator.valueOf(creatorString);
|
||||||
builder.setCreator(creator);
|
builder.setCreator(creator);
|
||||||
|
|
||||||
String disposition = parser.getAttributeValue("", JingleContent.DISPOSITION_ATTRIBUTE_NAME);
|
String disposition = parser.getAttributeValue("", JingleContentElement.DISPOSITION_ATTRIBUTE_NAME);
|
||||||
builder.setDisposition(disposition);
|
builder.setDisposition(disposition);
|
||||||
|
|
||||||
String name = parser.getAttributeValue("", JingleContent.NAME_ATTRIBUTE_NAME);
|
String name = parser.getAttributeValue("", JingleContentElement.NAME_ATTRIBUTE_NAME);
|
||||||
builder.setName(name);
|
builder.setName(name);
|
||||||
|
|
||||||
String sendersString = parser.getAttributeValue("", JingleContent.SENDERS_ATTRIBUTE_NAME);
|
String sendersString = parser.getAttributeValue("", JingleContentElement.SENDERS_ATTRIBUTE_NAME);
|
||||||
if (sendersString != null) {
|
if (sendersString != null) {
|
||||||
JingleContent.Senders senders = JingleContent.Senders.valueOf(sendersString);
|
JingleContentElement.Senders senders = JingleContentElement.Senders.valueOf(sendersString);
|
||||||
builder.setSenders(senders);
|
builder.setSenders(senders);
|
||||||
}
|
}
|
||||||
|
|
||||||
outerloop: while (true) {
|
outerloop: while (true) {
|
||||||
int eventType = parser.next();
|
int eventType = parser.next();
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case XmlPullParser.START_TAG:
|
case XmlPullParser.START_TAG:
|
||||||
String tagName = parser.getName();
|
String tagName = parser.getName();
|
||||||
String namespace = parser.getNamespace();
|
String namespace = parser.getNamespace();
|
||||||
switch (tagName) {
|
switch (tagName) {
|
||||||
case JingleContentDescription.ELEMENT: {
|
case JingleContentDescriptionElement.ELEMENT: {
|
||||||
JingleContentDescription description;
|
JingleContentDescriptionElement description;
|
||||||
JingleContentDescriptionProvider<?> provider = JingleContentProviderManager.getJingleContentDescriptionProvider(namespace);
|
JingleContentDescriptionProvider<?> provider = JingleManager.getJingleDescriptionProvider(namespace);
|
||||||
if (provider == null) {
|
if (provider == null) {
|
||||||
StandardExtensionElement standardExtensionElement = StandardExtensionElementProvider.INSTANCE.parse(parser);
|
StandardExtensionElement standardExtensionElement = StandardExtensionElementProvider.INSTANCE.parse(parser);
|
||||||
description = new UnknownJingleContentDescription(standardExtensionElement);
|
description = new UnknownJingleContentDescriptionElement(standardExtensionElement);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
description = provider.parse(parser);
|
||||||
|
}
|
||||||
|
builder.setDescription(description);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case JingleContentTransportElement.ELEMENT: {
|
||||||
|
JingleContentTransportElement transport;
|
||||||
|
JingleContentTransportProvider<?> provider = JingleManager.getJingleTransportProvider(namespace);
|
||||||
|
if (provider == null) {
|
||||||
|
StandardExtensionElement standardExtensionElement = StandardExtensionElementProvider.INSTANCE.parse(parser);
|
||||||
|
transport = new UnknownJingleContentTransportElement(standardExtensionElement);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
transport = provider.parse(parser);
|
||||||
|
}
|
||||||
|
builder.setTransport(transport);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case JingleContentSecurityElement.ELEMENT: {
|
||||||
|
JingleContentSecurityElement security;
|
||||||
|
JingleContentSecurityProvider<?> provider = JingleManager.getJingleSecurityProvider(namespace);
|
||||||
|
if (provider == null) {
|
||||||
|
StandardExtensionElement standardExtensionElement = StandardExtensionElementProvider.INSTANCE.parse(parser);
|
||||||
|
security = new UnknownJingleContentSecurityElement(standardExtensionElement);
|
||||||
|
} else {
|
||||||
|
security = provider.parse(parser);
|
||||||
|
}
|
||||||
|
builder.setSecurity(security);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
LOGGER.severe("Unknown Jingle content element: " + tagName);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
description = provider.parse(parser);
|
|
||||||
}
|
|
||||||
builder.setDescription(description);
|
|
||||||
break;
|
break;
|
||||||
}
|
case XmlPullParser.END_TAG:
|
||||||
case JingleContentTransport.ELEMENT: {
|
if (parser.getDepth() == initialDepth) {
|
||||||
JingleContentTransport transport;
|
break outerloop;
|
||||||
JingleContentTransportProvider<?> provider = JingleContentProviderManager.getJingleContentTransportProvider(namespace);
|
|
||||||
if (provider == null) {
|
|
||||||
StandardExtensionElement standardExtensionElement = StandardExtensionElementProvider.INSTANCE.parse(parser);
|
|
||||||
transport = new UnknownJingleContentTransport(standardExtensionElement);
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
transport = provider.parse(parser);
|
|
||||||
}
|
|
||||||
builder.setTransport(transport);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
LOGGER.severe("Unknown Jingle content element: " + tagName);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case XmlPullParser.END_TAG:
|
|
||||||
if (parser.getDepth() == initialDepth) {
|
|
||||||
break outerloop;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.transport;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener that can be registered to get notified about established Bytestreams.
|
||||||
|
*/
|
||||||
|
public interface BytestreamSessionEstablishedListener {
|
||||||
|
|
||||||
|
void onBytestreamSessionEstablished(BytestreamSession session);
|
||||||
|
|
||||||
|
void onBytestreamSessionFailed(Exception exception);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.transport.jingle_ibb;
|
||||||
|
|
||||||
|
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.smack.util.StringUtils;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamListener;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamRequest;
|
||||||
|
import org.jivesoftware.smackx.jingle.callbacks.JingleTransportCallback;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleTransportCandidate;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportInfoElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.element.JingleIBBTransportElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jingle InBandBytestream Transport component.
|
||||||
|
*/
|
||||||
|
public class JingleIBBTransport extends JingleTransport<JingleIBBTransportElement> {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(JingleIBBTransport.class.getName());
|
||||||
|
|
||||||
|
public static final String NAMESPACE_V1 = "urn:xmpp:jingle:transports:ibb:1";
|
||||||
|
public static final String NAMESPACE = NAMESPACE_V1;
|
||||||
|
|
||||||
|
public static final Short DEFAULT_BLOCK_SIZE = 4096;
|
||||||
|
public static final Short MAX_BLOCKSIZE = 8192;
|
||||||
|
|
||||||
|
private final String streamId;
|
||||||
|
private Short blockSize;
|
||||||
|
|
||||||
|
public JingleIBBTransport(String streamId, Short blockSize) {
|
||||||
|
this.streamId = streamId;
|
||||||
|
this.blockSize = blockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleIBBTransport() {
|
||||||
|
this(StringUtils.randomString(10), DEFAULT_BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Short getBlockSize() {
|
||||||
|
return blockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStreamId() {
|
||||||
|
return streamId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleIBBTransportElement getElement() {
|
||||||
|
return new JingleIBBTransportElement(streamId, blockSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSessionAccept(JingleContentTransportElement transportElement, XMPPConnection connection) {
|
||||||
|
JingleIBBTransportElement element = (JingleIBBTransportElement) transportElement;
|
||||||
|
blockSize = (blockSize < element.getBlockSize() ? blockSize : element.getBlockSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void establishIncomingBytestreamSession(final XMPPConnection connection, final JingleTransportCallback callback, final JingleSession session) {
|
||||||
|
final InBandBytestreamManager inBandBytestreamManager = InBandBytestreamManager.getByteStreamManager(connection);
|
||||||
|
LOGGER.log(Level.INFO, "Listen for incoming IBB transports from " + session.getPeer() + ":" + getStreamId());
|
||||||
|
InBandBytestreamListener bytestreamListener = new InBandBytestreamListener() {
|
||||||
|
@Override
|
||||||
|
public void incomingBytestreamRequest(InBandBytestreamRequest request) {
|
||||||
|
LOGGER.log(Level.INFO, "Incoming IBB stream: " + request.getFrom().asFullJidIfPossible() + ":" + request.getSessionID());
|
||||||
|
if (request.getFrom().asFullJidIfPossible().equals(session.getPeer())
|
||||||
|
&& request.getSessionID().equals(getStreamId())) {
|
||||||
|
|
||||||
|
inBandBytestreamManager.removeIncomingBytestreamListener(this);
|
||||||
|
|
||||||
|
BytestreamSession bytestreamSession;
|
||||||
|
try {
|
||||||
|
bytestreamSession = request.accept();
|
||||||
|
} catch (InterruptedException | SmackException e) {
|
||||||
|
callback.onTransportFailed(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleIBBTransport.this.bytestreamSession = bytestreamSession;
|
||||||
|
callback.onTransportReady(JingleIBBTransport.this.bytestreamSession);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
InBandBytestreamManager.getByteStreamManager(connection)
|
||||||
|
.addIncomingBytestreamListener(bytestreamListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void establishOutgoingBytestreamSession(XMPPConnection connection, JingleTransportCallback callback, final JingleSession session) {
|
||||||
|
InBandBytestreamManager inBandBytestreamManager = InBandBytestreamManager.getByteStreamManager(connection);
|
||||||
|
inBandBytestreamManager.setDefaultBlockSize(blockSize);
|
||||||
|
try {
|
||||||
|
JingleIBBTransport.this.bytestreamSession = inBandBytestreamManager.establishSession(session.getPeer(), getStreamId());
|
||||||
|
callback.onTransportReady(this.bytestreamSession);
|
||||||
|
} catch (SmackException.NoResponseException | XMPPException.XMPPErrorException | InterruptedException | SmackException.NotConnectedException e) {
|
||||||
|
callback.onTransportFailed(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanup() {
|
||||||
|
// Nothing to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addOurCandidate(JingleTransportCandidate<?> candidate) {
|
||||||
|
// Sorry, we don't want any candidates.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void prepare(XMPPConnection connection) {
|
||||||
|
// Nothing to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IQ handleTransportInfo(JingleContentTransportInfoElement info, JingleElement wrapping) {
|
||||||
|
return IQ.createResultIQ(wrapping);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.transport.jingle_ibb;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.jingle.adapter.JingleTransportAdapter;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.element.JingleIBBTransportElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter for Jingle InBandBytestream transports.
|
||||||
|
*/
|
||||||
|
public class JingleIBBTransportAdapter implements JingleTransportAdapter<JingleIBBTransport> {
|
||||||
|
@Override
|
||||||
|
public JingleIBBTransport transportFromElement(JingleContentTransportElement element) {
|
||||||
|
JingleIBBTransportElement transport = (JingleIBBTransportElement) element;
|
||||||
|
return new JingleIBBTransport(transport.getStreamId(), transport.getBlockSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return JingleIBBTransport.NAMESPACE;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.transport.jingle_ibb;
|
||||||
|
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.Manager;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleTransportManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleContent;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.provider.JingleIBBTransportProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by vanitas on 21.07.17.
|
||||||
|
*/
|
||||||
|
public final class JingleIBBTransportManager extends Manager implements JingleTransportManager {
|
||||||
|
|
||||||
|
private static final WeakHashMap<XMPPConnection, JingleIBBTransportManager> INSTANCES = new WeakHashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
JingleManager.addJingleTransportAdapter(new JingleIBBTransportAdapter());
|
||||||
|
JingleManager.addJingleTransportProvider(new JingleIBBTransportProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
private JingleIBBTransportManager(XMPPConnection connection) {
|
||||||
|
super(connection);
|
||||||
|
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(getNamespace());
|
||||||
|
JingleManager jingleManager = JingleManager.getInstanceFor(connection);
|
||||||
|
jingleManager.addJingleTransportManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleTransport<?> createTransportForInitiator(JingleContent content) {
|
||||||
|
return new JingleIBBTransport();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleTransport<?> createTransportForResponder(JingleContent content, JingleTransport<?> peersTransport) {
|
||||||
|
JingleIBBTransport other = (JingleIBBTransport) peersTransport;
|
||||||
|
return new JingleIBBTransport(other.getStreamId(), (short) Math.min(other.getBlockSize(), JingleIBBTransport.MAX_BLOCKSIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleTransport<?> createTransportForResponder(JingleContent content, JingleContentTransportElement peersTransportElement) {
|
||||||
|
JingleIBBTransport other = new JingleIBBTransportAdapter().transportFromElement(peersTransportElement);
|
||||||
|
return createTransportForResponder(content, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(JingleTransportManager other) {
|
||||||
|
return getPriority() > other.getPriority() ? 1 : -1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,56 +14,36 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle.transports.jingle_ibb.element;
|
package org.jivesoftware.smackx.jingle.transport.jingle_ibb.element;
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.JingleIBBTransport;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transport Element for JingleInBandBytestream transports.
|
* Transport Element for JingleInBandBytestream transports.
|
||||||
*/
|
*/
|
||||||
public class JingleIBBTransport extends JingleContentTransport {
|
public class JingleIBBTransportElement extends JingleContentTransportElement {
|
||||||
public static final String NAMESPACE_V1 = "urn:xmpp:jingle:transports:ibb:1";
|
|
||||||
public static final String ATTR_BLOCK_SIZE = "block-size";
|
public static final String ATTR_BLOCK_SIZE = "block-size";
|
||||||
public static final String ATTR_SID = "sid";
|
public static final String ATTR_SID = "sid";
|
||||||
|
|
||||||
public static final short DEFAULT_BLOCK_SIZE = 4096;
|
|
||||||
|
|
||||||
private final short blockSize;
|
|
||||||
private final String sid;
|
private final String sid;
|
||||||
|
private final Short blockSize;
|
||||||
|
|
||||||
public JingleIBBTransport() {
|
public JingleIBBTransportElement(String streamId, Short blockSize) {
|
||||||
this(DEFAULT_BLOCK_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JingleIBBTransport(String sid) {
|
|
||||||
this(DEFAULT_BLOCK_SIZE, sid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JingleIBBTransport(short blockSize) {
|
|
||||||
this(blockSize, StringUtils.randomString(24));
|
|
||||||
}
|
|
||||||
|
|
||||||
public JingleIBBTransport(short blockSize, String sid) {
|
|
||||||
super(null);
|
super(null);
|
||||||
if (blockSize > 0) {
|
this.sid = streamId;
|
||||||
this.blockSize = blockSize;
|
this.blockSize = blockSize != null ? blockSize : JingleIBBTransport.DEFAULT_BLOCK_SIZE;
|
||||||
} else {
|
|
||||||
this.blockSize = DEFAULT_BLOCK_SIZE;
|
|
||||||
}
|
|
||||||
this.sid = sid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSessionId() {
|
public Short getBlockSize() {
|
||||||
return sid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public short getBlockSize() {
|
|
||||||
return blockSize;
|
return blockSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getStreamId() {
|
||||||
|
return sid;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addExtraAttributes(XmlStringBuilder xml) {
|
protected void addExtraAttributes(XmlStringBuilder xml) {
|
||||||
xml.attribute(ATTR_BLOCK_SIZE, blockSize);
|
xml.attribute(ATTR_BLOCK_SIZE, blockSize);
|
||||||
|
@ -72,12 +52,12 @@ public class JingleIBBTransport extends JingleContentTransport {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNamespace() {
|
public String getNamespace() {
|
||||||
return NAMESPACE_V1;
|
return JingleIBBTransport.NAMESPACE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object other) {
|
public boolean equals(Object other) {
|
||||||
if (other == null || !(other instanceof JingleIBBTransport)) {
|
if (other == null || !(other instanceof JingleIBBTransportElement)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,4 +19,4 @@
|
||||||
* Smack's API for <a href="https://xmpp.org/extensions/xep-0261.html">XEP-0261: Jingle In-Band Bytestreams</a>.
|
* Smack's API for <a href="https://xmpp.org/extensions/xep-0261.html">XEP-0261: Jingle In-Band Bytestreams</a>.
|
||||||
* Element classes.
|
* Element classes.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle.transports.jingle_ibb.element;
|
package org.jivesoftware.smackx.jingle.transport.jingle_ibb.element;
|
|
@ -18,4 +18,4 @@
|
||||||
/**
|
/**
|
||||||
* Smack's API for <a href="https://xmpp.org/extensions/xep-0261.html">XEP-0261: Jingle In-Band Bytestreams</a>.
|
* Smack's API for <a href="https://xmpp.org/extensions/xep-0261.html">XEP-0261: Jingle In-Band Bytestreams</a>.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle.transports;
|
package org.jivesoftware.smackx.jingle.transport.jingle_ibb;
|
|
@ -14,27 +14,28 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle.transports.jingle_ibb.provider;
|
package org.jivesoftware.smackx.jingle.transport.jingle_ibb.provider;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.util.ParserUtils;
|
||||||
import org.jivesoftware.smackx.jingle.provider.JingleContentTransportProvider;
|
import org.jivesoftware.smackx.jingle.provider.JingleContentTransportProvider;
|
||||||
import org.jivesoftware.smackx.jingle.transports.jingle_ibb.element.JingleIBBTransport;
|
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.JingleIBBTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.element.JingleIBBTransportElement;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse JingleByteStreamTransport elements.
|
* Parse JingleByteStreamTransport elements.
|
||||||
*/
|
*/
|
||||||
public class JingleIBBTransportProvider extends JingleContentTransportProvider<JingleIBBTransport> {
|
public class JingleIBBTransportProvider extends JingleContentTransportProvider<JingleIBBTransportElement> {
|
||||||
@Override
|
@Override
|
||||||
public JingleIBBTransport parse(XmlPullParser parser, int initialDepth) throws Exception {
|
public JingleIBBTransportElement parse(XmlPullParser parser, int initialDepth) throws Exception {
|
||||||
String blockSizeString = parser.getAttributeValue(null, JingleIBBTransport.ATTR_BLOCK_SIZE);
|
Short blockSize = ParserUtils.getShortAttribute(parser, JingleIBBTransportElement.ATTR_BLOCK_SIZE);
|
||||||
String sid = parser.getAttributeValue(null, JingleIBBTransport.ATTR_SID);
|
String sid = parser.getAttributeValue(null, JingleIBBTransportElement.ATTR_SID);
|
||||||
|
return new JingleIBBTransportElement(sid, blockSize);
|
||||||
|
}
|
||||||
|
|
||||||
short blockSize = -1;
|
@Override
|
||||||
if (blockSizeString != null) {
|
public String getNamespace() {
|
||||||
blockSize = Short.valueOf(blockSizeString);
|
return JingleIBBTransport.NAMESPACE;
|
||||||
}
|
|
||||||
|
|
||||||
return new JingleIBBTransport(blockSize, sid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -19,4 +19,4 @@
|
||||||
* Smack's API for <a href="https://xmpp.org/extensions/xep-0261.html">XEP-0261: Jingle In-Band Bytestreams</a>.
|
* Smack's API for <a href="https://xmpp.org/extensions/xep-0261.html">XEP-0261: Jingle In-Band Bytestreams</a>.
|
||||||
* Provider classes.
|
* Provider classes.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle.transports.jingle_ibb.provider;
|
package org.jivesoftware.smackx.jingle.transport.jingle_ibb.provider;
|
|
@ -0,0 +1,462 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.transport.jingle_s5b;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
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.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smack.util.Async;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession;
|
||||||
|
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.callbacks.JingleTransportCallback;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleTransportCandidate;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportCandidateElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportInfoElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.exception.FailedTransportException;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportCandidateElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportInfoElement;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jingle SOCKS5Bytestream transport component.
|
||||||
|
*/
|
||||||
|
public class JingleS5BTransport extends JingleTransport<JingleS5BTransportElement> {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(JingleS5BTransport.class.getName());
|
||||||
|
|
||||||
|
public static final String NAMESPACE_V1 = "urn:xmpp:jingle:transports:s5b:1";
|
||||||
|
public static final String NAMESPACE = NAMESPACE_V1;
|
||||||
|
|
||||||
|
private static final int MAX_TIMEOUT = 10 * 1000;
|
||||||
|
|
||||||
|
private final String sid;
|
||||||
|
|
||||||
|
private String ourDstAddr;
|
||||||
|
private String theirDstAddr;
|
||||||
|
|
||||||
|
private Bytestream.Mode mode;
|
||||||
|
|
||||||
|
// PEERS candidate of OUR choice.
|
||||||
|
private JingleS5BTransportCandidate ourSelectedCandidate;
|
||||||
|
private JingleS5BTransportCandidate theirSelectedCandidate;
|
||||||
|
|
||||||
|
private JingleTransportCallback callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create transport as initiator.
|
||||||
|
* @param initiator initiator of the Jingle session.
|
||||||
|
* @param responder responder.
|
||||||
|
* @param sid sessionId of the Jingle session.
|
||||||
|
* @param mode TCP/UDP.
|
||||||
|
* @param ourCandidates our proxy candidates.
|
||||||
|
*/
|
||||||
|
JingleS5BTransport(FullJid initiator, FullJid responder, String sid, Bytestream.Mode mode, List<JingleTransportCandidate<?>> ourCandidates) {
|
||||||
|
this.sid = sid;
|
||||||
|
this.mode = mode;
|
||||||
|
this.ourDstAddr = Socks5Utils.createDigest(sid, initiator, responder);
|
||||||
|
Socks5Proxy.getSocks5Proxy().addTransfer(ourDstAddr);
|
||||||
|
|
||||||
|
for (JingleTransportCandidate<?> c : ourCandidates) {
|
||||||
|
addOurCandidate(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create simple transport as responder.
|
||||||
|
* @param initiator initiator of the Jingle session.
|
||||||
|
* @param responder responder.
|
||||||
|
* @param ourCandidates our proxy candidates.
|
||||||
|
* @param other transport of the other party.
|
||||||
|
*/
|
||||||
|
JingleS5BTransport(FullJid initiator, FullJid responder, List<JingleTransportCandidate<?>> ourCandidates, JingleS5BTransport other) {
|
||||||
|
this.sid = other.getStreamId();
|
||||||
|
this.mode = other.mode;
|
||||||
|
this.ourDstAddr = Socks5Utils.createDigest(sid, responder, initiator);
|
||||||
|
Socks5Proxy.getSocks5Proxy().addTransfer(ourDstAddr);
|
||||||
|
this.theirDstAddr = other.theirDstAddr;
|
||||||
|
|
||||||
|
for (JingleTransportCandidate<?> c : ourCandidates) {
|
||||||
|
addOurCandidate(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (JingleTransportCandidate<?> c : other.getTheirCandidates()) {
|
||||||
|
addTheirCandidate(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create custom transport as responder.
|
||||||
|
* @param sid sessionId of the Jingle session.
|
||||||
|
* @param mode UPD/TCP.
|
||||||
|
* @param ourDstAddr SOCKS5 destination address (digest)
|
||||||
|
* @param theirDstAddr SOCKS5 destination address (digest)
|
||||||
|
* @param ourCandidates our proxy candidates.
|
||||||
|
* @param theirCandidates their proxy candidates.
|
||||||
|
*/
|
||||||
|
JingleS5BTransport(String sid, Bytestream.Mode mode, String ourDstAddr, String theirDstAddr, List<JingleTransportCandidate<?>> ourCandidates, List<JingleTransportCandidate<?>> theirCandidates) {
|
||||||
|
this.sid = sid;
|
||||||
|
this.mode = mode;
|
||||||
|
this.ourDstAddr = ourDstAddr;
|
||||||
|
Socks5Proxy.getSocks5Proxy().addTransfer(ourDstAddr);
|
||||||
|
this.theirDstAddr = theirDstAddr;
|
||||||
|
|
||||||
|
for (JingleTransportCandidate<?> c : (ourCandidates != null ? ourCandidates :
|
||||||
|
Collections.<JingleS5BTransportCandidate>emptySet())) {
|
||||||
|
addOurCandidate(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (JingleTransportCandidate<?> c : (theirCandidates != null ? theirCandidates :
|
||||||
|
Collections.<JingleS5BTransportCandidate>emptySet())) {
|
||||||
|
addTheirCandidate(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy constructor.
|
||||||
|
* @param original which will be copied.
|
||||||
|
*/
|
||||||
|
public JingleS5BTransport(JingleS5BTransport original) {
|
||||||
|
this.sid = original.sid;
|
||||||
|
this.ourDstAddr = original.ourDstAddr;
|
||||||
|
this.theirDstAddr = original.theirDstAddr;
|
||||||
|
this.mode = original.mode;
|
||||||
|
|
||||||
|
for (JingleTransportCandidate<?> c : original.getOurCandidates()) {
|
||||||
|
addOurCandidate(new JingleS5BTransportCandidate((JingleS5BTransportCandidate) c));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (JingleTransportCandidate<?> c : original.getTheirCandidates()) {
|
||||||
|
addTheirCandidate(new JingleS5BTransportCandidate((JingleS5BTransportCandidate) c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleS5BTransportElement getElement() {
|
||||||
|
JingleS5BTransportElement.Builder builder = JingleS5BTransportElement.getBuilder()
|
||||||
|
.setStreamId(sid)
|
||||||
|
.setDestinationAddress(ourDstAddr)
|
||||||
|
.setMode(mode);
|
||||||
|
|
||||||
|
for (JingleTransportCandidate<?> candidate : getOurCandidates()) {
|
||||||
|
builder.addTransportCandidate((JingleS5BTransportCandidateElement) candidate.getElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStreamId() {
|
||||||
|
return sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOurDstAddr() {
|
||||||
|
return ourDstAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTheirDstAddr() {
|
||||||
|
return theirDstAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bytestream.Mode getMode() {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void prepare(XMPPConnection connection) {
|
||||||
|
JingleSession session = getParent().getParent();
|
||||||
|
if (getOurDstAddr() == null) {
|
||||||
|
ourDstAddr = Socks5Utils.createDigest(getStreamId(), session.getOurJid(), session.getPeer());
|
||||||
|
Socks5Proxy.getSocks5Proxy().addTransfer(ourDstAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == null) {
|
||||||
|
mode = Bytestream.Mode.tcp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getOurCandidates().size() == 0) {
|
||||||
|
List<JingleTransportCandidate<?>> candidates = JingleS5BTransportManager.getInstanceFor(connection).collectCandidates();
|
||||||
|
for (JingleTransportCandidate<?> c : candidates) {
|
||||||
|
addOurCandidate(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void establishIncomingBytestreamSession(XMPPConnection connection, JingleTransportCallback callback, JingleSession session)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
LOGGER.log(Level.INFO, "Establishing incoming bytestream.");
|
||||||
|
this.callback = callback;
|
||||||
|
establishBytestreamSession(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void establishOutgoingBytestreamSession(XMPPConnection connection, JingleTransportCallback callback, JingleSession session)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
LOGGER.log(Level.INFO, "Establishing outgoing bytestream.");
|
||||||
|
this.callback = callback;
|
||||||
|
establishBytestreamSession(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReferenceEquality")
|
||||||
|
private void establishBytestreamSession(XMPPConnection connection)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
Socks5Proxy.getSocks5Proxy().addTransfer(ourDstAddr);
|
||||||
|
this.ourSelectedCandidate = connectToCandidates(MAX_TIMEOUT);
|
||||||
|
|
||||||
|
if (ourSelectedCandidate == CANDIDATE_FAILURE) {
|
||||||
|
connection.createStanzaCollectorAndSend(JingleS5BTransportManager.createCandidateError(this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ourSelectedCandidate == null) {
|
||||||
|
throw new AssertionError("MUST NOT BE NULL.");
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.createStanzaCollectorAndSend(JingleS5BTransportManager.createCandidateUsed(this, ourSelectedCandidate));
|
||||||
|
connectIfReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReferenceEquality")
|
||||||
|
private JingleS5BTransportCandidate connectToCandidates(int timeout) {
|
||||||
|
|
||||||
|
if (getTheirCandidates().size() == 0) {
|
||||||
|
LOGGER.log(Level.INFO, "They provided 0 candidates.");
|
||||||
|
return CANDIDATE_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _timeout = timeout / getTheirCandidates().size(); //TODO: Wise?
|
||||||
|
for (JingleTransportCandidate<?> c : getTheirCandidates()) {
|
||||||
|
JingleS5BTransportCandidate candidate = (JingleS5BTransportCandidate) c;
|
||||||
|
try {
|
||||||
|
return candidate.connect(_timeout, true);
|
||||||
|
} catch (IOException | TimeoutException | InterruptedException | SmackException | XMPPException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Exception while connecting to candidate: " + e, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failed to connect to any candidate.
|
||||||
|
return CANDIDATE_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanup() {
|
||||||
|
Socks5Proxy.getSocks5Proxy().removeTransfer(ourDstAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReferenceEquality")
|
||||||
|
private void connectIfReady() {
|
||||||
|
final JingleSession session = getParent().getParent();
|
||||||
|
|
||||||
|
if (ourSelectedCandidate == null || theirSelectedCandidate == null) {
|
||||||
|
// Not yet ready if we or peer did not yet decide on a candidate.
|
||||||
|
LOGGER.log(Level.INFO, "Not ready.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ourSelectedCandidate == CANDIDATE_FAILURE && theirSelectedCandidate == CANDIDATE_FAILURE) {
|
||||||
|
LOGGER.log(Level.INFO, "Failure.");
|
||||||
|
callback.onTransportFailed(new FailedTransportException(null));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.log(Level.INFO, (session.isInitiator() ? "Initiator" : "Responder") + " is ready.");
|
||||||
|
|
||||||
|
//Determine nominated candidate.
|
||||||
|
JingleS5BTransportCandidate nominated;
|
||||||
|
if (ourSelectedCandidate != CANDIDATE_FAILURE && theirSelectedCandidate != CANDIDATE_FAILURE) {
|
||||||
|
|
||||||
|
if (ourSelectedCandidate.getPriority() > theirSelectedCandidate.getPriority()) {
|
||||||
|
nominated = ourSelectedCandidate;
|
||||||
|
} else if (ourSelectedCandidate.getPriority() < theirSelectedCandidate.getPriority()) {
|
||||||
|
nominated = theirSelectedCandidate;
|
||||||
|
} else {
|
||||||
|
nominated = getParent().getParent().isInitiator() ? ourSelectedCandidate : theirSelectedCandidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (ourSelectedCandidate != CANDIDATE_FAILURE) {
|
||||||
|
nominated = ourSelectedCandidate;
|
||||||
|
} else {
|
||||||
|
nominated = theirSelectedCandidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isProxy = nominated.getType() == JingleS5BTransportCandidateElement.Type.proxy;
|
||||||
|
|
||||||
|
if (nominated == theirSelectedCandidate) {
|
||||||
|
|
||||||
|
LOGGER.log(Level.INFO, "Their choice, so our proposed candidate is used.");
|
||||||
|
|
||||||
|
try {
|
||||||
|
nominated = nominated.connect(MAX_TIMEOUT, false);
|
||||||
|
} catch (InterruptedException | IOException | XMPPException | SmackException | TimeoutException e) {
|
||||||
|
LOGGER.log(Level.INFO, "Could not connect to our candidate.", e);
|
||||||
|
|
||||||
|
Async.go(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
session.getJingleManager().getConnection().createStanzaCollectorAndSend(JingleS5BTransportManager.createProxyError(JingleS5BTransport.this));
|
||||||
|
} catch (SmackException.NotConnectedException | InterruptedException e1) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Could not send proxy error: " + e, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
callback.onTransportFailed(new S5BTransportException.CandidateError(e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isProxy) {
|
||||||
|
LOGGER.log(Level.INFO, "Send candidate-activate.");
|
||||||
|
JingleElement candidateActivate = JingleS5BTransportManager.createCandidateActivated((JingleS5BTransport) nominated.getParent(), nominated);
|
||||||
|
|
||||||
|
try {
|
||||||
|
session.getJingleManager().getConnection().createStanzaCollectorAndSend(candidateActivate)
|
||||||
|
.nextResultOrThrow();
|
||||||
|
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not send candidate-activated", e);
|
||||||
|
callback.onTransportFailed(new S5BTransportException.ProxyError(e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.log(Level.INFO, "Start transmission on " + nominated.getCandidateId());
|
||||||
|
this.bytestreamSession = new Socks5BytestreamSession(nominated.getSocket(), !isProxy);
|
||||||
|
callback.onTransportReady(this.bytestreamSession);
|
||||||
|
|
||||||
|
}
|
||||||
|
//Our choice
|
||||||
|
else {
|
||||||
|
LOGGER.log(Level.INFO, "Our choice, so their candidate was used.");
|
||||||
|
if (!isProxy) {
|
||||||
|
LOGGER.log(Level.INFO, "Start transmission on " + nominated.getCandidateId());
|
||||||
|
this.bytestreamSession = new Socks5BytestreamSession(nominated.getSocket(), true);
|
||||||
|
callback.onTransportReady(this.bytestreamSession);
|
||||||
|
} else {
|
||||||
|
LOGGER.log(Level.INFO, "Our choice was their external proxy. wait for candidate-activate.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSessionAccept(JingleContentTransportElement transportElement, XMPPConnection connection) {
|
||||||
|
JingleS5BTransportElement transport = (JingleS5BTransportElement) transportElement;
|
||||||
|
theirDstAddr = transport.getDestinationAddress();
|
||||||
|
for (JingleContentTransportCandidateElement c : transport.getCandidates()) {
|
||||||
|
JingleS5BTransportCandidateElement candidate = (JingleS5BTransportCandidateElement) c;
|
||||||
|
addTheirCandidate(new JingleS5BTransportCandidate(candidate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IQ handleTransportInfo(JingleContentTransportInfoElement info, JingleElement wrapping) {
|
||||||
|
switch (info.getElementName()) {
|
||||||
|
|
||||||
|
case JingleS5BTransportInfoElement.CandidateUsed.ELEMENT:
|
||||||
|
handleCandidateUsed((JingleS5BTransportInfoElement) info, wrapping);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JingleS5BTransportInfoElement.CandidateActivated.ELEMENT:
|
||||||
|
handleCandidateActivate((JingleS5BTransportInfoElement) info);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JingleS5BTransportInfoElement.CandidateError.ELEMENT:
|
||||||
|
handleCandidateError((JingleS5BTransportInfoElement) info);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JingleS5BTransportInfoElement.ProxyError.ELEMENT:
|
||||||
|
handleProxyError((JingleS5BTransportInfoElement) info);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new AssertionError("Unknown transport-info element: " + info.getElementName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return IQ.createResultIQ(wrapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleCandidateUsed(JingleS5BTransportInfoElement info, JingleElement wrapping) {
|
||||||
|
JingleManager jingleManager = getParent().getParent().getJingleManager();
|
||||||
|
String candidateId = ((JingleS5BTransportInfoElement.CandidateUsed) info).getCandidateId();
|
||||||
|
|
||||||
|
// Received second candidate-used -> out-of-order!
|
||||||
|
if (theirSelectedCandidate != null) {
|
||||||
|
try {
|
||||||
|
jingleManager.getConnection().sendStanza(JingleElement.createJingleErrorOutOfOrder(wrapping));
|
||||||
|
//jingleManager.getConnection().createStanzaCollectorAndSend(JingleElement.createJingleErrorOutOfOrder(wrapping));
|
||||||
|
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Could not respond to candidate-used transport-info: " + e, e);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (JingleTransportCandidate<?> jingleTransportCandidate : getOurCandidates()) {
|
||||||
|
JingleS5BTransportCandidate candidate = (JingleS5BTransportCandidate) jingleTransportCandidate;
|
||||||
|
if (candidate.getCandidateId().equals(candidateId)) {
|
||||||
|
theirSelectedCandidate = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theirSelectedCandidate == null) {
|
||||||
|
LOGGER.log(Level.SEVERE, "ILLEGAL CANDIDATE ID!!!");
|
||||||
|
//TODO: Alert! Illegal candidateId!
|
||||||
|
}
|
||||||
|
|
||||||
|
connectIfReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleCandidateActivate(JingleS5BTransportInfoElement info) {
|
||||||
|
this.bytestreamSession = new Socks5BytestreamSession(ourSelectedCandidate.getSocket(),
|
||||||
|
ourSelectedCandidate.getStreamHost().getJID().asBareJid().equals(getParent().getParent().getPeer().asBareJid()));
|
||||||
|
callback.onTransportReady(this.bytestreamSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleCandidateError(JingleS5BTransportInfoElement info) {
|
||||||
|
theirSelectedCandidate = CANDIDATE_FAILURE;
|
||||||
|
connectIfReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleProxyError(JingleS5BTransportInfoElement info) {
|
||||||
|
callback.onTransportFailed(new S5BTransportException.ProxyError(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dummy candidate used to represent failure.
|
||||||
|
* Kinda depressing, isn't it?
|
||||||
|
*/
|
||||||
|
private final static JingleS5BTransportCandidate CANDIDATE_FAILURE = new JingleS5BTransportCandidate(null, null, -1, null);
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.transport.jingle_s5b;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.jingle.adapter.JingleTransportAdapter;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleTransportCandidate;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportCandidateElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportCandidateElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter for Jingle SOCKS5Bytestream components.
|
||||||
|
*/
|
||||||
|
public class JingleS5BTransportAdapter implements JingleTransportAdapter<JingleS5BTransport> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleS5BTransport transportFromElement(JingleContentTransportElement element) {
|
||||||
|
JingleS5BTransportElement s5b = (JingleS5BTransportElement) element;
|
||||||
|
ArrayList<JingleTransportCandidate<?>> candidates = new ArrayList<>();
|
||||||
|
|
||||||
|
for (JingleContentTransportCandidateElement e : element.getCandidates()) {
|
||||||
|
candidates.add(JingleS5BTransportCandidate.fromElement((JingleS5BTransportCandidateElement) e));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JingleS5BTransport(s5b.getStreamId(), s5b.getMode(), null, s5b.getDestinationAddress(), null, candidates);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return JingleS5BTransport.NAMESPACE;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.transport.jingle_s5b;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
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.smackx.bytestreams.socks5.Socks5Client;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5ClientForInitiator;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleContent;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleTransportCandidate;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportCandidateElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jingle SOCKS5Bytestream transport candidate.
|
||||||
|
*/
|
||||||
|
public class JingleS5BTransportCandidate extends JingleTransportCandidate<JingleS5BTransportCandidateElement> {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(JingleS5BTransportCandidate.class.getName());
|
||||||
|
|
||||||
|
private final String candidateId;
|
||||||
|
private final Bytestream.StreamHost streamHost;
|
||||||
|
private final JingleS5BTransportCandidateElement.Type type;
|
||||||
|
|
||||||
|
private Socket socket;
|
||||||
|
|
||||||
|
JingleS5BTransportCandidate(JingleS5BTransportCandidateElement element) {
|
||||||
|
this(element.getCandidateId(), new Bytestream.StreamHost(element.getJid(), element.getHost(), element.getPort()), element.getPriority(), element.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleS5BTransportCandidate(String candidateId,
|
||||||
|
Bytestream.StreamHost streamHost,
|
||||||
|
int priority,
|
||||||
|
JingleS5BTransportCandidateElement.Type type) {
|
||||||
|
this.candidateId = candidateId;
|
||||||
|
this.streamHost = streamHost;
|
||||||
|
this.type = type;
|
||||||
|
|
||||||
|
setPriority(priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleS5BTransportCandidate(JingleS5BTransportCandidate other) {
|
||||||
|
this(other.candidateId,
|
||||||
|
other.getStreamHost(),
|
||||||
|
other.getPriority(),
|
||||||
|
other.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JingleS5BTransportCandidate fromElement(JingleS5BTransportCandidateElement element) {
|
||||||
|
return new JingleS5BTransportCandidate(element.getCandidateId(), element.getStreamHost(), element.getPriority(), element.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
String getCandidateId() {
|
||||||
|
return candidateId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bytestream.StreamHost getStreamHost() {
|
||||||
|
return streamHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleS5BTransportCandidateElement.Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleS5BTransportCandidateElement getElement() {
|
||||||
|
return new JingleS5BTransportCandidateElement(
|
||||||
|
getCandidateId(), getStreamHost().getAddress(),
|
||||||
|
getStreamHost().getJID(), getStreamHost().getPort(),
|
||||||
|
getPriority(), getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleS5BTransportCandidate connect(int timeout, boolean peersProposal) throws InterruptedException, TimeoutException, SmackException, XMPPException, IOException {
|
||||||
|
JingleS5BTransport transport = (JingleS5BTransport) getParent();
|
||||||
|
|
||||||
|
switch (getType()) {
|
||||||
|
case proxy:
|
||||||
|
case direct:
|
||||||
|
Socks5Client client;
|
||||||
|
if (peersProposal) {
|
||||||
|
String dstAddr = transport.getTheirDstAddr();
|
||||||
|
if (dstAddr == null) {
|
||||||
|
dstAddr = Socks5Utils.createDigest(transport.getStreamId(), transport.getParent().getParent().getPeer(), transport.getParent().getParent().getOurJid());
|
||||||
|
}
|
||||||
|
LOGGER.log(Level.INFO, "Connect to foreign candidate " + getCandidateId() + " using " + dstAddr);
|
||||||
|
LOGGER.log(Level.INFO, getStreamHost().getAddress() + ":" + getStreamHost().getPort() + " " + getStreamHost().getJID().toString() + " " + getType());
|
||||||
|
client = new Socks5Client(getStreamHost(), dstAddr);
|
||||||
|
} else {
|
||||||
|
LOGGER.log(Level.INFO, "Connect to our candidate " + getCandidateId() + " using " + transport.getOurDstAddr());
|
||||||
|
LOGGER.log(Level.INFO, getStreamHost().getAddress() + ":" + getStreamHost().getPort() + " " + getStreamHost().getJID().toString() + " " + getType());
|
||||||
|
JingleContent content = transport.getParent();
|
||||||
|
JingleSession session = content.getParent();
|
||||||
|
client = new Socks5ClientForInitiator(getStreamHost(), transport.getOurDstAddr(), session.getJingleManager().getConnection(), transport.getStreamId(), session.getPeer());
|
||||||
|
}
|
||||||
|
this.socket = client.getSocket(timeout);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOGGER.log(Level.INFO, "Unsupported candidate type: " + getType());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Socket getSocket() {
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (this == other) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(other instanceof JingleS5BTransportCandidate)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleS5BTransportCandidate o = (JingleS5BTransportCandidate) other;
|
||||||
|
|
||||||
|
return o.getCandidateId().equals(this.getCandidateId()) &&
|
||||||
|
o.getType() == this.getType() &&
|
||||||
|
o.getStreamHost().equals(this.getStreamHost());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getCandidateId().hashCode() + 3 * getType().hashCode() + 5 * getStreamHost().hashCode();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,308 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.transport.jingle_s5b;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
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.ConnectionListener;
|
||||||
|
import org.jivesoftware.smack.Manager;
|
||||||
|
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.util.StringUtils;
|
||||||
|
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.disco.ServiceDiscoveryManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleTransportManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleContent;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleTransportCandidate;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleAction;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportCandidateElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportInfoElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.provider.JingleS5BTransportProvider;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager for Jingle SOCKS5 Bytestream transports (XEP-0261).
|
||||||
|
*/
|
||||||
|
public final class JingleS5BTransportManager extends Manager implements JingleTransportManager {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(JingleS5BTransportManager.class.getName());
|
||||||
|
private final Socks5BytestreamManager socks5BytestreamManager = Socks5BytestreamManager.getBytestreamManager(connection());
|
||||||
|
|
||||||
|
private static final WeakHashMap<XMPPConnection, JingleS5BTransportManager> INSTANCES = new WeakHashMap<>();
|
||||||
|
|
||||||
|
private List<Bytestream.StreamHost> localStreamHosts = null;
|
||||||
|
private List<Bytestream.StreamHost> availableStreamHosts = null;
|
||||||
|
|
||||||
|
public static boolean useLocalCandidates = true;
|
||||||
|
public static boolean useExternalCandidates = true;
|
||||||
|
|
||||||
|
static {
|
||||||
|
JingleManager.addJingleTransportAdapter(new JingleS5BTransportAdapter());
|
||||||
|
JingleManager.addJingleTransportProvider(new JingleS5BTransportProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
private JingleS5BTransportManager(XMPPConnection connection) {
|
||||||
|
super(connection);
|
||||||
|
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(getNamespace());
|
||||||
|
JingleManager jingleManager = JingleManager.getInstanceFor(connection);
|
||||||
|
jingleManager.addJingleTransportManager(this);
|
||||||
|
|
||||||
|
connection.addConnectionListener(connectionListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleTransport<?> createTransportForInitiator(JingleContent content) {
|
||||||
|
JingleSession session = content.getParent();
|
||||||
|
String sid = StringUtils.randomString(24);
|
||||||
|
List<JingleTransportCandidate<?>> candidates = collectCandidates();
|
||||||
|
return new JingleS5BTransport(session.getInitiator(), session.getResponder(), sid, Bytestream.Mode.tcp, candidates);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleTransport<?> createTransportForResponder(JingleContent content, JingleTransport<?> peersTransport) {
|
||||||
|
JingleSession session = content.getParent();
|
||||||
|
return new JingleS5BTransport(session.getInitiator(), session.getResponder(), collectCandidates(), (JingleS5BTransport) peersTransport);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleTransport<?> createTransportForResponder(JingleContent content, JingleContentTransportElement peersTransportElement) {
|
||||||
|
JingleS5BTransport other = new JingleS5BTransportAdapter().transportFromElement(peersTransportElement);
|
||||||
|
return createTransportForResponder(content, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority() {
|
||||||
|
return 10000; // SOCKS5 has a high priority
|
||||||
|
}
|
||||||
|
|
||||||
|
List<JingleTransportCandidate<?>> collectCandidates() {
|
||||||
|
List<JingleTransportCandidate<?>> candidates = new ArrayList<>();
|
||||||
|
|
||||||
|
//Local host
|
||||||
|
if (JingleS5BTransportManager.isUseLocalCandidates()) {
|
||||||
|
for (Bytestream.StreamHost host : getLocalStreamHosts()) {
|
||||||
|
candidates.add(new JingleS5BTransportCandidate(StringUtils.randomString(16), host, 100, JingleS5BTransportCandidateElement.Type.proxy));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Bytestream.StreamHost> remoteHosts = Collections.emptyList();
|
||||||
|
if (JingleS5BTransportManager.isUseExternalCandidates()) {
|
||||||
|
try {
|
||||||
|
remoteHosts = getServersStreamHosts();
|
||||||
|
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not determine available StreamHosts.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Bytestream.StreamHost host : remoteHosts) {
|
||||||
|
candidates.add(new JingleS5BTransportCandidate(StringUtils.randomString(16), host, 0, JingleS5BTransportCandidateElement.Type.proxy));
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Bytestream.StreamHost> queryServersStreamHosts() throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
|
||||||
|
List<Jid> proxies = socks5BytestreamManager.determineProxies();
|
||||||
|
return determineStreamHostInfo(proxies);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Bytestream.StreamHost> queryLocalStreamHosts() {
|
||||||
|
return socks5BytestreamManager.getLocalStreamHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Bytestream.StreamHost> getServersStreamHosts() throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
|
||||||
|
if (availableStreamHosts == null) {
|
||||||
|
availableStreamHosts = queryServersStreamHosts();
|
||||||
|
}
|
||||||
|
return availableStreamHosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Bytestream.StreamHost> getLocalStreamHosts() {
|
||||||
|
if (localStreamHosts == null) {
|
||||||
|
localStreamHosts = queryLocalStreamHosts();
|
||||||
|
}
|
||||||
|
return localStreamHosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Bytestream.StreamHost> determineStreamHostInfo(List<Jid> proxies) {
|
||||||
|
List<Bytestream.StreamHost> streamHosts = new ArrayList<>();
|
||||||
|
|
||||||
|
Iterator<Jid> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JingleElement createTransportInfo(JingleS5BTransport transport, JingleS5BTransportInfoElement info) {
|
||||||
|
JingleContent content = transport.getParent();
|
||||||
|
JingleSession session = content.getParent();
|
||||||
|
|
||||||
|
JingleElement.Builder jb = JingleElement.getBuilder()
|
||||||
|
.setSessionId(session.getSessionId())
|
||||||
|
.setAction(JingleAction.transport_info);
|
||||||
|
|
||||||
|
if (session.isInitiator()) {
|
||||||
|
jb.setInitiator(session.getInitiator());
|
||||||
|
} else {
|
||||||
|
jb.setResponder(session.getResponder());
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleContentElement.Builder cb = JingleContentElement.getBuilder()
|
||||||
|
.setCreator(content.getCreator())
|
||||||
|
.setName(content.getName())
|
||||||
|
.setSenders(content.getSenders());
|
||||||
|
|
||||||
|
JingleS5BTransportElement.Builder tb = JingleS5BTransportElement.getBuilder()
|
||||||
|
.setTransportInfo(info)
|
||||||
|
.setStreamId(transport.getStreamId());
|
||||||
|
|
||||||
|
JingleElement jingle = jb.addJingleContent(cb.setTransport(tb.build()).build()).build();
|
||||||
|
jingle.setFrom(session.getOurJid());
|
||||||
|
jingle.setTo(session.getPeer());
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JingleElement createCandidateUsed(JingleS5BTransport transport, JingleS5BTransportCandidate candidate) {
|
||||||
|
return createTransportInfo(transport, new JingleS5BTransportInfoElement.CandidateUsed(candidate.getCandidateId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static JingleElement createCandidateError(JingleS5BTransport transport) {
|
||||||
|
return createTransportInfo(transport, JingleS5BTransportInfoElement.CandidateError.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JingleElement createProxyError(JingleS5BTransport transport) {
|
||||||
|
return createTransportInfo(transport, JingleS5BTransportInfoElement.ProxyError.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JingleElement createCandidateActivated(JingleS5BTransport transport, JingleS5BTransportCandidate candidate) {
|
||||||
|
return createTransportInfo(transport, new JingleS5BTransportInfoElement.CandidateActivated(candidate.getCandidateId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(JingleTransportManager other) {
|
||||||
|
return getPriority() > other.getPriority() ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ConnectionListener connectionListener = new ConnectionListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connected(XMPPConnection connection) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void authenticated(XMPPConnection connection, boolean resumed) {
|
||||||
|
if (connection.equals(connection())) {
|
||||||
|
try {
|
||||||
|
Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy();
|
||||||
|
if (!socks5Proxy.isRunning()) {
|
||||||
|
socks5Proxy.start();
|
||||||
|
}
|
||||||
|
localStreamHosts = queryLocalStreamHosts();
|
||||||
|
availableStreamHosts = queryServersStreamHosts();
|
||||||
|
} catch (InterruptedException | SmackException.NoResponseException | SmackException.NotConnectedException | XMPPException.XMPPErrorException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not query available StreamHosts: " + e, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connectionClosed() {
|
||||||
|
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
|
||||||
|
if (proxy.isRunning()) {
|
||||||
|
Socks5Proxy.getSocks5Proxy().stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connectionClosedOnError(Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reconnectionSuccessful() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reconnectingIn(int seconds) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reconnectionFailed(Exception e) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.transport.jingle_s5b;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.jingle.exception.FailedTransportException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by vanitas on 25.07.17.
|
||||||
|
*/
|
||||||
|
public class S5BTransportException extends FailedTransportException {
|
||||||
|
|
||||||
|
protected static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private S5BTransportException(Throwable throwable) {
|
||||||
|
super(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CandidateError extends S5BTransportException {
|
||||||
|
protected static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
CandidateError(Throwable throwable) {
|
||||||
|
super(throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ProxyError extends S5BTransportException {
|
||||||
|
protected static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
ProxyError(Throwable throwable) {
|
||||||
|
super(throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,15 +14,13 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements;
|
package org.jivesoftware.smackx.jingle.transport.jingle_s5b.element;
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
|
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransportCandidate;
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportCandidateElement;
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
@ -31,9 +29,7 @@ import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
/**
|
/**
|
||||||
* TransportCandidate for Jingle Socks5Bytestream transports.
|
* TransportCandidate for Jingle Socks5Bytestream transports.
|
||||||
*/
|
*/
|
||||||
public final class JingleS5BTransportCandidate extends JingleContentTransportCandidate {
|
public final class JingleS5BTransportCandidateElement extends JingleContentTransportCandidateElement {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(JingleS5BTransportCandidate.class.getName());
|
|
||||||
|
|
||||||
public static final String ATTR_CID = "cid";
|
public static final String ATTR_CID = "cid";
|
||||||
public static final String ATTR_HOST = "host";
|
public static final String ATTR_HOST = "host";
|
||||||
|
@ -49,7 +45,7 @@ public final class JingleS5BTransportCandidate extends JingleContentTransportCan
|
||||||
private final int priority;
|
private final int priority;
|
||||||
private final Type type;
|
private final Type type;
|
||||||
|
|
||||||
public JingleS5BTransportCandidate(String candidateId, String host, Jid jid, int port, int priority, Type type) {
|
public JingleS5BTransportCandidateElement(String candidateId, String host, Jid jid, int port, int priority, Type type) {
|
||||||
|
|
||||||
Objects.requireNonNull(candidateId);
|
Objects.requireNonNull(candidateId);
|
||||||
Objects.requireNonNull(host);
|
Objects.requireNonNull(host);
|
||||||
|
@ -70,7 +66,7 @@ public final class JingleS5BTransportCandidate extends JingleContentTransportCan
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleS5BTransportCandidate(Bytestream.StreamHost streamHost, int priority, Type type) {
|
public JingleS5BTransportCandidateElement(Bytestream.StreamHost streamHost, int priority, Type type) {
|
||||||
this(StringUtils.randomString(24), streamHost.getAddress(), streamHost.getJID(), streamHost.getPort(), priority, type);
|
this(StringUtils.randomString(24), streamHost.getAddress(), streamHost.getJID(), streamHost.getPort(), priority, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,8 +193,8 @@ public final class JingleS5BTransportCandidate extends JingleContentTransportCan
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleS5BTransportCandidate build() {
|
public JingleS5BTransportCandidateElement build() {
|
||||||
return new JingleS5BTransportCandidate(cid, host, jid, port, priority, type);
|
return new JingleS5BTransportCandidateElement(cid, host, jid, port, priority, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements;
|
package org.jivesoftware.smackx.jingle.transport.jingle_s5b.element;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -22,33 +22,33 @@ import java.util.List;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
|
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportCandidateElement;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransportCandidate;
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransportInfo;
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportInfoElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.JingleS5BTransport;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Socks5Bytestream transport element.
|
* Socks5Bytestream transport element.
|
||||||
*/
|
*/
|
||||||
public class JingleS5BTransport extends JingleContentTransport {
|
public class JingleS5BTransportElement extends JingleContentTransportElement {
|
||||||
public static final String NAMESPACE_V1 = "urn:xmpp:jingle:transports:s5b:1";
|
|
||||||
public static final String ATTR_DSTADDR = "dstaddr";
|
public static final String ATTR_DSTADDR = "dstaddr";
|
||||||
public static final String ATTR_MODE = "mode";
|
public static final String ATTR_MODE = "mode";
|
||||||
public static final String ATTR_SID = "sid";
|
public static final String ATTR_SID = "sid";
|
||||||
|
|
||||||
private final String streamId;
|
private final String sid;
|
||||||
private final String dstAddr;
|
private final String dstAddr;
|
||||||
private final Bytestream.Mode mode;
|
private final Bytestream.Mode mode;
|
||||||
|
|
||||||
protected JingleS5BTransport(List<JingleContentTransportCandidate> candidates, JingleContentTransportInfo info, String streamId, String dstAddr, Bytestream.Mode mode) {
|
protected JingleS5BTransportElement(String streamId, List<JingleContentTransportCandidateElement> candidates, JingleContentTransportInfoElement info, String dstAddr, Bytestream.Mode mode) {
|
||||||
super(candidates, info);
|
super(candidates, info);
|
||||||
StringUtils.requireNotNullOrEmpty(streamId, "sid MUST be neither null, nor empty.");
|
StringUtils.requireNotNullOrEmpty(streamId, "sid MUST be neither null, nor empty.");
|
||||||
this.streamId = streamId;
|
this.sid = streamId;
|
||||||
this.dstAddr = dstAddr;
|
this.dstAddr = dstAddr;
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getStreamId() {
|
public String getStreamId() {
|
||||||
return streamId;
|
return sid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDestinationAddress() {
|
public String getDestinationAddress() {
|
||||||
|
@ -61,23 +61,23 @@ public class JingleS5BTransport extends JingleContentTransport {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNamespace() {
|
public String getNamespace() {
|
||||||
return NAMESPACE_V1;
|
return JingleS5BTransport.NAMESPACE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addExtraAttributes(XmlStringBuilder xml) {
|
protected void addExtraAttributes(XmlStringBuilder xml) {
|
||||||
xml.optAttribute(ATTR_DSTADDR, dstAddr);
|
xml.optAttribute(ATTR_DSTADDR, dstAddr);
|
||||||
xml.optAttribute(ATTR_MODE, mode);
|
xml.optAttribute(ATTR_MODE, mode);
|
||||||
xml.attribute(ATTR_SID, streamId);
|
xml.attribute(ATTR_SID, sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasCandidate(String candidateId) {
|
public boolean hasCandidate(String candidateId) {
|
||||||
return getCandidate(candidateId) != null;
|
return getCandidate(candidateId) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleS5BTransportCandidate getCandidate(String candidateId) {
|
public JingleS5BTransportCandidateElement getCandidate(String candidateId) {
|
||||||
for (JingleContentTransportCandidate c : candidates) {
|
for (JingleContentTransportCandidateElement c : candidates) {
|
||||||
JingleS5BTransportCandidate candidate = (JingleS5BTransportCandidate) c;
|
JingleS5BTransportCandidateElement candidate = (JingleS5BTransportCandidateElement) c;
|
||||||
if (candidate.getCandidateId().equals(candidateId)) {
|
if (candidate.getCandidateId().equals(candidateId)) {
|
||||||
return candidate;
|
return candidate;
|
||||||
}
|
}
|
||||||
|
@ -93,8 +93,8 @@ public class JingleS5BTransport extends JingleContentTransport {
|
||||||
private String streamId;
|
private String streamId;
|
||||||
private String dstAddr;
|
private String dstAddr;
|
||||||
private Bytestream.Mode mode;
|
private Bytestream.Mode mode;
|
||||||
private final ArrayList<JingleContentTransportCandidate> candidates = new ArrayList<>();
|
private final ArrayList<JingleContentTransportCandidateElement> candidates = new ArrayList<>();
|
||||||
private JingleContentTransportInfo info;
|
private JingleContentTransportInfoElement info;
|
||||||
|
|
||||||
public Builder setStreamId(String sid) {
|
public Builder setStreamId(String sid) {
|
||||||
this.streamId = sid;
|
this.streamId = sid;
|
||||||
|
@ -111,7 +111,7 @@ public class JingleS5BTransport extends JingleContentTransport {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder addTransportCandidate(JingleS5BTransportCandidate candidate) {
|
public Builder addTransportCandidate(JingleS5BTransportCandidateElement candidate) {
|
||||||
if (info != null) {
|
if (info != null) {
|
||||||
throw new IllegalStateException("Builder has already an info set. " +
|
throw new IllegalStateException("Builder has already an info set. " +
|
||||||
"The transport can only have either an info or transport candidates, not both.");
|
"The transport can only have either an info or transport candidates, not both.");
|
||||||
|
@ -120,7 +120,7 @@ public class JingleS5BTransport extends JingleContentTransport {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setTransportInfo(JingleContentTransportInfo info) {
|
public Builder setTransportInfo(JingleContentTransportInfoElement info) {
|
||||||
if (!candidates.isEmpty()) {
|
if (!candidates.isEmpty()) {
|
||||||
throw new IllegalStateException("Builder has already at least one candidate set. " +
|
throw new IllegalStateException("Builder has already at least one candidate set. " +
|
||||||
"The transport can only have either an info or transport candidates, not both.");
|
"The transport can only have either an info or transport candidates, not both.");
|
||||||
|
@ -132,24 +132,26 @@ public class JingleS5BTransport extends JingleContentTransport {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JingleS5BTransportElement build() {
|
||||||
|
return new JingleS5BTransportElement(streamId, candidates, info, dstAddr, mode);
|
||||||
|
}
|
||||||
|
|
||||||
public Builder setCandidateUsed(String candidateId) {
|
public Builder setCandidateUsed(String candidateId) {
|
||||||
return setTransportInfo(new JingleS5BTransportInfo.CandidateUsed(candidateId));
|
return setTransportInfo(new JingleS5BTransportInfoElement.CandidateUsed(candidateId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setCandidateActivated(String candidateId) {
|
public Builder setCandidateActivated(String candidateId) {
|
||||||
return setTransportInfo(new JingleS5BTransportInfo.CandidateActivated(candidateId));
|
return setTransportInfo(new JingleS5BTransportInfoElement.CandidateActivated(candidateId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setCandidateError() {
|
public Builder setCandidateError() {
|
||||||
return setTransportInfo(JingleS5BTransportInfo.CandidateError.INSTANCE);
|
return setTransportInfo(JingleS5BTransportInfoElement.CandidateError.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setProxyError() {
|
public Builder setProxyError() {
|
||||||
return setTransportInfo(JingleS5BTransportInfo.ProxyError.INSTANCE);
|
return setTransportInfo(JingleS5BTransportInfoElement.ProxyError.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleS5BTransport build() {
|
|
||||||
return new JingleS5BTransport(candidates, info, streamId, dstAddr, mode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,22 +14,22 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements;
|
package org.jivesoftware.smackx.jingle.transport.jingle_s5b.element;
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransportInfo;
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportInfoElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class representing possible SOCKS5 TransportInfo elements.
|
* Class representing possible SOCKS5 TransportInfo elements.
|
||||||
*/
|
*/
|
||||||
public abstract class JingleS5BTransportInfo extends JingleContentTransportInfo {
|
public abstract class JingleS5BTransportInfoElement extends JingleContentTransportInfoElement {
|
||||||
|
|
||||||
public static abstract class JingleS5BCandidateTransportInfo extends JingleS5BTransportInfo {
|
public static abstract class JingleS5BCandidateTransportInfoElement extends JingleS5BTransportInfoElement {
|
||||||
public static final String ATTR_CID = "cid";
|
public static final String ATTR_CID = "cid";
|
||||||
|
|
||||||
private final String candidateId;
|
private final String candidateId;
|
||||||
|
|
||||||
protected JingleS5BCandidateTransportInfo(String candidateId) {
|
JingleS5BCandidateTransportInfoElement(String candidateId) {
|
||||||
this.candidateId = candidateId;
|
this.candidateId = candidateId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,11 +48,11 @@ public abstract class JingleS5BTransportInfo extends JingleContentTransportInfo
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean equals(Object other) {
|
public final boolean equals(Object other) {
|
||||||
if (!(other instanceof JingleS5BCandidateTransportInfo)) {
|
if (!(other instanceof JingleS5BCandidateTransportInfoElement)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
JingleS5BCandidateTransportInfo otherCandidateTransportInfo = (JingleS5BCandidateTransportInfo) other;
|
JingleS5BCandidateTransportInfoElement otherCandidateTransportInfo = (JingleS5BCandidateTransportInfoElement) other;
|
||||||
return toXML().equals(otherCandidateTransportInfo.toXML());
|
return toXML().equals(otherCandidateTransportInfo.toXML());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,8 +62,8 @@ public abstract class JingleS5BTransportInfo extends JingleContentTransportInfo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class CandidateActivated extends JingleS5BCandidateTransportInfo {
|
public static final class CandidateActivated extends JingleS5BCandidateTransportInfoElement {
|
||||||
public static final String ELEMENT = "candidate-activated";
|
public static final String ELEMENT = "activated";
|
||||||
|
|
||||||
public CandidateActivated(String candidateId) {
|
public CandidateActivated(String candidateId) {
|
||||||
super(candidateId);
|
super(candidateId);
|
||||||
|
@ -75,7 +75,8 @@ public abstract class JingleS5BTransportInfo extends JingleContentTransportInfo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class CandidateUsed extends JingleS5BCandidateTransportInfo {
|
|
||||||
|
public static final class CandidateUsed extends JingleS5BCandidateTransportInfoElement {
|
||||||
public static final String ELEMENT = "candidate-used";
|
public static final String ELEMENT = "candidate-used";
|
||||||
|
|
||||||
public CandidateUsed(String candidateId) {
|
public CandidateUsed(String candidateId) {
|
||||||
|
@ -88,12 +89,15 @@ public abstract class JingleS5BTransportInfo extends JingleContentTransportInfo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class CandidateError extends JingleS5BTransportInfo {
|
|
||||||
|
public static final class CandidateError extends JingleS5BTransportInfoElement {
|
||||||
|
|
||||||
public static final CandidateError INSTANCE = new CandidateError();
|
public static final CandidateError INSTANCE = new CandidateError();
|
||||||
|
|
||||||
public static final String ELEMENT = "candidate-error";
|
public static final String ELEMENT = "candidate-error";
|
||||||
|
|
||||||
private CandidateError() {
|
private CandidateError() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -120,12 +124,14 @@ public abstract class JingleS5BTransportInfo extends JingleContentTransportInfo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class ProxyError extends JingleS5BTransportInfo {
|
|
||||||
|
public static final class ProxyError extends JingleS5BTransportInfoElement {
|
||||||
public static final ProxyError INSTANCE = new ProxyError();
|
public static final ProxyError INSTANCE = new ProxyError();
|
||||||
|
|
||||||
public static final String ELEMENT = "proxy-error";
|
public static final String ELEMENT = "proxy-error";
|
||||||
|
|
||||||
private ProxyError() {
|
private ProxyError() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -134,7 +140,7 @@ public abstract class JingleS5BTransportInfo extends JingleContentTransportInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence toXML() {
|
public XmlStringBuilder toXML() {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder();
|
XmlStringBuilder xml = new XmlStringBuilder();
|
||||||
xml.halfOpenElement(this);
|
xml.halfOpenElement(this);
|
||||||
xml.closeEmptyElement();
|
xml.closeEmptyElement();
|
|
@ -19,4 +19,4 @@
|
||||||
* Smack's API for <a href="https://xmpp.org/extensions/xep-0261.html">XEP-0260: Jingle SOCKS5 Bytestreams</a>.
|
* Smack's API for <a href="https://xmpp.org/extensions/xep-0261.html">XEP-0260: Jingle SOCKS5 Bytestreams</a>.
|
||||||
* Element classes.
|
* Element classes.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements;
|
package org.jivesoftware.smackx.jingle.transport.jingle_s5b.element;
|
|
@ -18,4 +18,4 @@
|
||||||
/**
|
/**
|
||||||
* Smack's API for <a href="https://xmpp.org/extensions/xep-0261.html">XEP-0260: Jingle SOCKS5 Bytestreams</a>.
|
* Smack's API for <a href="https://xmpp.org/extensions/xep-0261.html">XEP-0260: Jingle SOCKS5 Bytestreams</a>.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle.transports.jingle_s5b;
|
package org.jivesoftware.smackx.jingle.transport.jingle_s5b;
|
|
@ -14,49 +14,50 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle.transports.jingle_s5b.provider;
|
package org.jivesoftware.smackx.jingle.transport.jingle_s5b.provider;
|
||||||
|
|
||||||
import static org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.Mode.tcp;
|
import static org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.Mode.tcp;
|
||||||
import static org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.Mode.udp;
|
import static org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.Mode.udp;
|
||||||
import static org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportCandidate.ATTR_CID;
|
import static org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportCandidateElement.ATTR_CID;
|
||||||
import static org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportCandidate.ATTR_HOST;
|
import static org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportCandidateElement.ATTR_HOST;
|
||||||
import static org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportCandidate.ATTR_JID;
|
import static org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportCandidateElement.ATTR_JID;
|
||||||
import static org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportCandidate.ATTR_PORT;
|
import static org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportCandidateElement.ATTR_PORT;
|
||||||
import static org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportCandidate.ATTR_PRIORITY;
|
import static org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportCandidateElement.ATTR_PRIORITY;
|
||||||
import static org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportCandidate.ATTR_TYPE;
|
import static org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportCandidateElement.ATTR_TYPE;
|
||||||
import static org.xmlpull.v1.XmlPullParser.END_TAG;
|
import static org.xmlpull.v1.XmlPullParser.END_TAG;
|
||||||
import static org.xmlpull.v1.XmlPullParser.START_TAG;
|
import static org.xmlpull.v1.XmlPullParser.START_TAG;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportElement;
|
||||||
import org.jivesoftware.smackx.jingle.provider.JingleContentTransportProvider;
|
import org.jivesoftware.smackx.jingle.provider.JingleContentTransportProvider;
|
||||||
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport;
|
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.JingleS5BTransport;
|
||||||
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportCandidate;
|
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportCandidateElement;
|
||||||
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportInfo;
|
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportElement;
|
||||||
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportInfo.JingleS5BCandidateTransportInfo;
|
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.element.JingleS5BTransportInfoElement;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provider for JingleSocks5BytestreamTransport elements.
|
* Provider for JingleSocks5BytestreamTransport elements.
|
||||||
*/
|
*/
|
||||||
public class JingleS5BTransportProvider extends JingleContentTransportProvider<JingleS5BTransport> {
|
public class JingleS5BTransportProvider extends JingleContentTransportProvider<JingleS5BTransportElement> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JingleS5BTransport parse(XmlPullParser parser, int initialDepth) throws Exception {
|
public JingleS5BTransportElement parse(XmlPullParser parser, int initialDepth) throws Exception {
|
||||||
JingleS5BTransport.Builder builder = JingleS5BTransport.getBuilder();
|
JingleS5BTransportElement.Builder builder = JingleS5BTransportElement.getBuilder();
|
||||||
|
|
||||||
String streamId = parser.getAttributeValue(null, JingleS5BTransport.ATTR_SID);
|
String streamId = parser.getAttributeValue(null, JingleS5BTransportElement.ATTR_SID);
|
||||||
builder.setStreamId(streamId);
|
builder.setStreamId(streamId);
|
||||||
|
|
||||||
String dstAddr = parser.getAttributeValue(null, JingleS5BTransport.ATTR_DSTADDR);
|
String dstAddr = parser.getAttributeValue(null, JingleS5BTransportElement.ATTR_DSTADDR);
|
||||||
builder.setDestinationAddress(dstAddr);
|
builder.setDestinationAddress(dstAddr);
|
||||||
|
|
||||||
String mode = parser.getAttributeValue(null, JingleS5BTransport.ATTR_MODE);
|
String mode = parser.getAttributeValue(null, JingleS5BTransportElement.ATTR_MODE);
|
||||||
if (mode != null) {
|
if (mode != null) {
|
||||||
builder.setMode(mode.equals(udp.toString()) ? udp : tcp);
|
builder.setMode(mode.equals(udp.toString()) ? udp : tcp);
|
||||||
}
|
}
|
||||||
|
|
||||||
JingleS5BTransportCandidate.Builder cb;
|
JingleS5BTransportCandidateElement.Builder cb;
|
||||||
outerloop: while (true) {
|
outerloop: while (true) {
|
||||||
int tag = parser.nextTag();
|
int tag = parser.nextTag();
|
||||||
String name = parser.getName();
|
String name = parser.getName();
|
||||||
|
@ -64,8 +65,8 @@ public class JingleS5BTransportProvider extends JingleContentTransportProvider<J
|
||||||
case START_TAG: {
|
case START_TAG: {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
|
|
||||||
case JingleS5BTransportCandidate.ELEMENT:
|
case JingleS5BTransportCandidateElement.ELEMENT:
|
||||||
cb = JingleS5BTransportCandidate.getBuilder();
|
cb = JingleS5BTransportCandidateElement.getBuilder();
|
||||||
cb.setCandidateId(parser.getAttributeValue(null, ATTR_CID));
|
cb.setCandidateId(parser.getAttributeValue(null, ATTR_CID));
|
||||||
cb.setHost(parser.getAttributeValue(null, ATTR_HOST));
|
cb.setHost(parser.getAttributeValue(null, ATTR_HOST));
|
||||||
cb.setJid(parser.getAttributeValue(null, ATTR_JID));
|
cb.setJid(parser.getAttributeValue(null, ATTR_JID));
|
||||||
|
@ -78,29 +79,29 @@ public class JingleS5BTransportProvider extends JingleContentTransportProvider<J
|
||||||
|
|
||||||
String typeString = parser.getAttributeValue(null, ATTR_TYPE);
|
String typeString = parser.getAttributeValue(null, ATTR_TYPE);
|
||||||
if (typeString != null) {
|
if (typeString != null) {
|
||||||
cb.setType(JingleS5BTransportCandidate.Type.fromString(typeString));
|
cb.setType(JingleS5BTransportCandidateElement.Type.fromString(typeString));
|
||||||
}
|
}
|
||||||
builder.addTransportCandidate(cb.build());
|
builder.addTransportCandidate(cb.build());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JingleS5BTransportInfo.CandidateActivated.ELEMENT:
|
case JingleS5BTransportInfoElement.CandidateActivated.ELEMENT:
|
||||||
builder.setTransportInfo(new JingleS5BTransportInfo.CandidateActivated(
|
builder.setTransportInfo(new JingleS5BTransportInfoElement.CandidateActivated(
|
||||||
parser.getAttributeValue(null,
|
parser.getAttributeValue(null,
|
||||||
JingleS5BCandidateTransportInfo.ATTR_CID)));
|
JingleS5BTransportInfoElement.JingleS5BCandidateTransportInfoElement.ATTR_CID)));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JingleS5BTransportInfo.CandidateUsed.ELEMENT:
|
case JingleS5BTransportInfoElement.CandidateUsed.ELEMENT:
|
||||||
builder.setTransportInfo(new JingleS5BTransportInfo.CandidateUsed(
|
builder.setTransportInfo(new JingleS5BTransportInfoElement.CandidateUsed(
|
||||||
parser.getAttributeValue(null,
|
parser.getAttributeValue(null,
|
||||||
JingleS5BCandidateTransportInfo.ATTR_CID)));
|
JingleS5BTransportInfoElement.JingleS5BCandidateTransportInfoElement.ATTR_CID)));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JingleS5BTransportInfo.CandidateError.ELEMENT:
|
case JingleS5BTransportInfoElement.CandidateError.ELEMENT:
|
||||||
builder.setTransportInfo(JingleS5BTransportInfo.CandidateError.INSTANCE);
|
builder.setTransportInfo(JingleS5BTransportInfoElement.CandidateError.INSTANCE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JingleS5BTransportInfo.ProxyError.ELEMENT:
|
case JingleS5BTransportInfoElement.ProxyError.ELEMENT:
|
||||||
builder.setTransportInfo(JingleS5BTransportInfo.ProxyError.INSTANCE);
|
builder.setTransportInfo(JingleS5BTransportInfoElement.ProxyError.INSTANCE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +109,7 @@ public class JingleS5BTransportProvider extends JingleContentTransportProvider<J
|
||||||
|
|
||||||
case END_TAG: {
|
case END_TAG: {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case JingleContentTransport.ELEMENT:
|
case JingleContentTransportElement.ELEMENT:
|
||||||
break outerloop;
|
break outerloop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,4 +117,9 @@ public class JingleS5BTransportProvider extends JingleContentTransportProvider<J
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return JingleS5BTransport.NAMESPACE;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -19,4 +19,4 @@
|
||||||
* Smack's API for <a href="https://xmpp.org/extensions/xep-0261.html">XEP-0260: Jingle SOCKS5 Bytestreams</a>.
|
* Smack's API for <a href="https://xmpp.org/extensions/xep-0261.html">XEP-0260: Jingle SOCKS5 Bytestreams</a>.
|
||||||
* Provider classes.
|
* Provider classes.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle.transports.jingle_s5b.provider;
|
package org.jivesoftware.smackx.jingle.transport.jingle_s5b.provider;
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Florian Schmaus, 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 <a href="https://xmpp.org/extensions/xep-0166.html">XEP-0166: Jingle</a>.
|
||||||
|
* Jingle Transport methods.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle.transport;
|
|
@ -1,74 +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.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 <D> JingleContentTransport.
|
|
||||||
*/
|
|
||||||
public abstract class JingleTransportManager<D extends JingleContentTransport> 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<D> 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) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,62 +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.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<T extends JingleContentTransport> {
|
|
||||||
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.getTransport();
|
|
||||||
|
|
||||||
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<T> transportManager();
|
|
||||||
}
|
|
|
@ -1,64 +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.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<JingleIBBTransport> {
|
|
||||||
|
|
||||||
private static final WeakHashMap<XMPPConnection, JingleIBBTransportManager> 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<JingleIBBTransport> transportSession(JingleSession jingleSession) {
|
|
||||||
return new JingleIBBTransportSession(jingleSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void authenticated(XMPPConnection connection, boolean resumed) {
|
|
||||||
//Nothing to do.
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,115 +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.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<JingleIBBTransport> {
|
|
||||||
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<JingleIBBTransport> transportManager() {
|
|
||||||
return JingleIBBTransportManager.getInstanceFor(jingleSession.getConnection());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,234 +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.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<JingleS5BTransport> {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(JingleS5BTransportManager.class.getName());
|
|
||||||
|
|
||||||
private static final WeakHashMap<XMPPConnection, JingleS5BTransportManager> INSTANCES = new WeakHashMap<>();
|
|
||||||
|
|
||||||
private List<Bytestream.StreamHost> localStreamHosts = null;
|
|
||||||
private List<Bytestream.StreamHost> 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<JingleS5BTransport> transportSession(JingleSession jingleSession) {
|
|
||||||
return new JingleS5BTransportSession(jingleSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Bytestream.StreamHost> queryAvailableStreamHosts() throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
|
|
||||||
Socks5BytestreamManager s5m = Socks5BytestreamManager.getBytestreamManager(getConnection());
|
|
||||||
List<Jid> proxies = s5m.determineProxies();
|
|
||||||
return determineStreamHostInfo(proxies);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Bytestream.StreamHost> queryLocalStreamHosts() {
|
|
||||||
return Socks5BytestreamManager.getBytestreamManager(getConnection())
|
|
||||||
.getLocalStreamHost();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Bytestream.StreamHost> getAvailableStreamHosts() throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
|
|
||||||
if (availableStreamHosts == null) {
|
|
||||||
availableStreamHosts = queryAvailableStreamHosts();
|
|
||||||
}
|
|
||||||
return availableStreamHosts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Bytestream.StreamHost> getLocalStreamHosts() {
|
|
||||||
if (localStreamHosts == null) {
|
|
||||||
localStreamHosts = queryLocalStreamHosts();
|
|
||||||
}
|
|
||||||
return localStreamHosts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Bytestream.StreamHost> determineStreamHostInfo(List<Jid> proxies) {
|
|
||||||
XMPPConnection connection = getConnection();
|
|
||||||
List<Bytestream.StreamHost> streamHosts = new ArrayList<>();
|
|
||||||
|
|
||||||
Iterator<Jid> 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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,361 +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.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<JingleS5BTransport> {
|
|
||||||
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<Bytestream.StreamHost> 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).getTransport().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).getTransport().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);
|
|
||||||
}
|
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle;
|
package org.jivesoftware.smackx.jingle.util;
|
||||||
|
|
||||||
import org.jxmpp.jid.FullJid;
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle;
|
package org.jivesoftware.smackx.jingle.util;
|
||||||
|
|
||||||
public enum Role {
|
public enum Role {
|
||||||
initiator,
|
initiator,
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Florian Schmaus, 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 <a href="https://xmpp.org/extensions/xep-0166.html">XEP-0166: Jingle</a>.
|
||||||
|
* Small classes with no real place.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle.util;
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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 static junit.framework.TestCase.assertTrue;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
|
import org.jivesoftware.smackx.jingle.util.FullJidAndSessionId;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.jxmpp.jid.FullJid;
|
||||||
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
|
|
||||||
|
public class FullJidAndSessionIdTest extends SmackTestSuite {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void equalityTest() throws XmppStringprepException {
|
||||||
|
FullJid albert = JidCreate.fullFrom("albert@einstein.emc/squared");
|
||||||
|
String albertId = "10121922";
|
||||||
|
FullJidAndSessionId fns = new FullJidAndSessionId(albert, albertId);
|
||||||
|
|
||||||
|
assertEquals("10121922", fns.getSessionId());
|
||||||
|
assertEquals(JidCreate.fullFrom("albert@einstein.emc/squared"), fns.getFullJid());
|
||||||
|
|
||||||
|
FullJidAndSessionId fns2 = new FullJidAndSessionId(JidCreate.fullFrom("albert@einstein.emc/squared"), "10121922");
|
||||||
|
assertTrue(fns.equals(fns2));
|
||||||
|
assertEquals(fns.hashCode(), fns2.hashCode());
|
||||||
|
|
||||||
|
assertNotSame(fns, new FullJidAndSessionId(JidCreate.fullFrom("albert@einstein.emc/squared"), "11111111"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,160 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.assertNotNull;
|
||||||
|
import static junit.framework.TestCase.assertNotSame;
|
||||||
|
import static junit.framework.TestCase.assertNull;
|
||||||
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.DummyConnection;
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smack.packet.TestIQ;
|
||||||
|
import org.jivesoftware.smack.packet.XMPPError;
|
||||||
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleContent;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleAction;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.util.Role;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the JingleContent class.
|
||||||
|
*/
|
||||||
|
public class JingleContentElementTest extends SmackTestSuite {
|
||||||
|
|
||||||
|
@Test(expected = NullPointerException.class)
|
||||||
|
public void emptyBuilderThrowsTest() {
|
||||||
|
JingleContentElement.Builder builder = JingleContentElement.getBuilder();
|
||||||
|
builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void onlyCreatorBuilderThrowsTest() {
|
||||||
|
JingleContentElement.Builder builder = JingleContentElement.getBuilder();
|
||||||
|
builder.setCreator(JingleContentElement.Creator.initiator);
|
||||||
|
builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parserTest() throws Exception {
|
||||||
|
|
||||||
|
JingleContentElement.Builder builder = JingleContentElement.getBuilder();
|
||||||
|
|
||||||
|
builder.setCreator(JingleContentElement.Creator.initiator);
|
||||||
|
builder.setName("A name");
|
||||||
|
|
||||||
|
JingleContentElement content = builder.build();
|
||||||
|
assertNotNull(content);
|
||||||
|
assertNull(content.getDescription());
|
||||||
|
assertEquals(JingleContentElement.Creator.initiator, content.getCreator());
|
||||||
|
assertEquals("A name", content.getName());
|
||||||
|
|
||||||
|
builder.setSenders(JingleContentElement.Senders.both);
|
||||||
|
content = builder.build();
|
||||||
|
assertEquals(JingleContentElement.Senders.both, content.getSenders());
|
||||||
|
|
||||||
|
builder.setDisposition("session");
|
||||||
|
JingleContentElement content1 = builder.build();
|
||||||
|
assertEquals("session", content1.getDisposition());
|
||||||
|
assertNotSame(content.toXML().toString(), content1.toXML().toString());
|
||||||
|
assertEquals(content1.toXML().toString(), builder.build().toXML().toString());
|
||||||
|
|
||||||
|
String xml =
|
||||||
|
"<content creator='initiator' disposition='session' name='A name' senders='both'>" +
|
||||||
|
"</content>";
|
||||||
|
assertEquals(xml, content1.toXML().toString());
|
||||||
|
|
||||||
|
JingleContent fromElement = JingleContent.fromElement(content1);
|
||||||
|
assertEquals("A name", fromElement.getName());
|
||||||
|
assertEquals(content1.getCreator(), fromElement.getCreator());
|
||||||
|
assertEquals(content1.getSenders(), fromElement.getSenders());
|
||||||
|
assertNull(fromElement.getTransport());
|
||||||
|
assertNull(fromElement.getDescription());
|
||||||
|
assertNull(fromElement.getSecurity());
|
||||||
|
assertXMLEqual(xml, fromElement.getElement().toXML().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void handleJingleRequestTest() {
|
||||||
|
XMPPConnection connection = mock(XMPPConnection.class);
|
||||||
|
JingleContent content = new JingleContent(JingleContentElement.Creator.initiator, JingleContentElement.Senders.initiator);
|
||||||
|
|
||||||
|
IQ descriptionInfoResult = new TestIQ("description_info", "test");
|
||||||
|
IQ securityInfoResult = new TestIQ("description_info", "test");
|
||||||
|
IQ sessionInfoResult = new TestIQ("session_info", "test");
|
||||||
|
IQ transportAcceptResult = new TestIQ("transport_accept", "test");
|
||||||
|
IQ transportInfoResult = new TestIQ("transport_info", "test");
|
||||||
|
IQ transportRejectResult = new TestIQ("transport_reject", "test");
|
||||||
|
IQ transportReplaceResult = new TestIQ("transport_replace", "test");
|
||||||
|
|
||||||
|
JingleElement contentModify = JingleElement.getBuilder().setAction(JingleAction.content_modify).setSessionId("session").build();
|
||||||
|
assertTrue(content.handleJingleRequest(contentModify, connection).getError().getCondition() == XMPPError.Condition.feature_not_implemented);
|
||||||
|
|
||||||
|
JingleElement descriptionInfo = JingleElement.getBuilder().setAction(JingleAction.description_info).setSessionId("session").build();
|
||||||
|
assertTrue(content.handleJingleRequest(descriptionInfo, connection).getError().getCondition() == XMPPError.Condition.feature_not_implemented);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void startTest() throws XmppStringprepException, SmackException.NotConnectedException, InterruptedException {
|
||||||
|
XMPPConnection connection = new DummyConnection();
|
||||||
|
JingleSession session = new JingleSession(JingleManager.getInstanceFor(connection), connection.getUser().asFullJidOrThrow(), JidCreate.fullFrom("bob@baumeister.de/buddl"), Role.initiator, "session");
|
||||||
|
JingleContent content = new JingleContent(JingleContentElement.Creator.initiator, JingleContentElement.Senders.initiator);
|
||||||
|
JingleTransport<?> transport = mock(JingleTransport.class);
|
||||||
|
content.setTransport(transport);
|
||||||
|
session.addContent(content);
|
||||||
|
|
||||||
|
final boolean[] sync = new boolean[1];
|
||||||
|
|
||||||
|
content.start(connection);
|
||||||
|
doAnswer(new Answer<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||||
|
sync[0] = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).when(transport).establishOutgoingBytestreamSession(connection, content, session);
|
||||||
|
|
||||||
|
Date start = new Date();
|
||||||
|
while (!sync[0]) {
|
||||||
|
Date now = new Date();
|
||||||
|
if (now.getTime() - start.getTime() > 2000) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//Unfortunately there are no ResultSyncPoints available, so we have to cheat a little bit.
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(transport).establishOutgoingBytestreamSession(connection, content, session);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,76 +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.assertNotNull;
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test the JingleContent class.
|
|
||||||
*/
|
|
||||||
public class JingleContentTest extends SmackTestSuite {
|
|
||||||
|
|
||||||
@Test(expected = NullPointerException.class)
|
|
||||||
public void emptyBuilderThrowsTest() {
|
|
||||||
JingleContent.Builder builder = JingleContent.getBuilder();
|
|
||||||
builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void onlyCreatorBuilderThrowsTest() {
|
|
||||||
JingleContent.Builder builder = JingleContent.getBuilder();
|
|
||||||
builder.setCreator(JingleContent.Creator.initiator);
|
|
||||||
builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parserTest() throws Exception {
|
|
||||||
|
|
||||||
JingleContent.Builder builder = JingleContent.getBuilder();
|
|
||||||
|
|
||||||
builder.setCreator(JingleContent.Creator.initiator);
|
|
||||||
builder.setName("A name");
|
|
||||||
|
|
||||||
JingleContent content = builder.build();
|
|
||||||
assertNotNull(content);
|
|
||||||
assertNull(content.getDescription());
|
|
||||||
assertEquals(JingleContent.Creator.initiator, content.getCreator());
|
|
||||||
assertEquals("A name", content.getName());
|
|
||||||
|
|
||||||
builder.setSenders(JingleContent.Senders.both);
|
|
||||||
content = builder.build();
|
|
||||||
assertEquals(JingleContent.Senders.both, content.getSenders());
|
|
||||||
|
|
||||||
builder.setDisposition("session");
|
|
||||||
JingleContent content1 = builder.build();
|
|
||||||
assertEquals("session", content1.getDisposition());
|
|
||||||
assertNotSame(content.toXML().toString(), content1.toXML().toString());
|
|
||||||
assertEquals(content1.toXML().toString(), builder.build().toXML().toString());
|
|
||||||
|
|
||||||
String xml =
|
|
||||||
"<content creator='initiator' disposition='session' name='A name' senders='both'>" +
|
|
||||||
"</content>";
|
|
||||||
assertEquals(xml, content1.toXML().toString());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,447 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.assertNotNull;
|
||||||
|
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.DummyConnection;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
|
import org.jivesoftware.smack.test.util.TestUtils;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleAction;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleReasonElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.provider.JingleProvider;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.JingleIBBTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.element.JingleIBBTransportElement;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.jxmpp.jid.FullJid;
|
||||||
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the JingleUtil class.
|
||||||
|
*/
|
||||||
|
public class JingleElementTest extends SmackTestSuite {
|
||||||
|
|
||||||
|
private FullJid romeo;
|
||||||
|
private FullJid juliet;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() throws XmppStringprepException {
|
||||||
|
|
||||||
|
XMPPConnection connection = new DummyConnection(
|
||||||
|
DummyConnection.getDummyConfigurationBuilder()
|
||||||
|
.setUsernameAndPassword("romeo@montague.lit",
|
||||||
|
"iluvJulibabe13").build());
|
||||||
|
JingleManager jm = JingleManager.getInstanceFor(connection);
|
||||||
|
romeo = connection.getUser().asFullJidOrThrow();
|
||||||
|
juliet = JidCreate.fullFrom("juliet@capulet.lit/balcony");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSessionTerminateDeclineTest() throws Exception {
|
||||||
|
JingleElement terminate = JingleElement.createSessionTerminate(juliet, "thisismadness", JingleReasonElement.Reason.decline);
|
||||||
|
String jingleXML =
|
||||||
|
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
||||||
|
"action='session-terminate' " +
|
||||||
|
"sid='thisismadness'>" +
|
||||||
|
"<reason>" +
|
||||||
|
"<decline/>" +
|
||||||
|
"</reason>" +
|
||||||
|
"</jingle>";
|
||||||
|
String xml = getIQXML(romeo, juliet, terminate.getStanzaId(), jingleXML);
|
||||||
|
assertXMLEqual(xml, terminate.toXML().toString());
|
||||||
|
JingleElement jingle = new JingleProvider().parse(TestUtils.getParser(jingleXML));
|
||||||
|
assertNotNull(jingle);
|
||||||
|
assertEquals(jingle.getAction(), JingleAction.session_terminate);
|
||||||
|
assertEquals(jingle.getReason().asEnum(), JingleReasonElement.Reason.decline);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSessionTerminateSuccessTest() throws Exception {
|
||||||
|
JingleElement success = JingleElement.createSessionTerminate(juliet, "thisissparta", JingleReasonElement.Reason.success);
|
||||||
|
String jingleXML =
|
||||||
|
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
||||||
|
"action='session-terminate' " +
|
||||||
|
"sid='thisissparta'>" +
|
||||||
|
"<reason>" +
|
||||||
|
"<success/>" +
|
||||||
|
"</reason>" +
|
||||||
|
"</jingle>";
|
||||||
|
String xml = getIQXML(romeo, juliet, success.getStanzaId(), jingleXML);
|
||||||
|
assertXMLEqual(xml, success.toXML().toString());
|
||||||
|
JingleElement jingle = new JingleProvider().parse(TestUtils.getParser(jingleXML));
|
||||||
|
assertNotNull(jingle);
|
||||||
|
assertEquals(jingle.getAction(), JingleAction.session_terminate);
|
||||||
|
assertEquals(jingle.getReason().asEnum(), JingleReasonElement.Reason.success);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSessionTerminateBusyTest() throws Exception {
|
||||||
|
JingleElement busy = JingleElement.createSessionTerminate(juliet, "thisispatrick", JingleReasonElement.Reason.busy);
|
||||||
|
String jingleXML =
|
||||||
|
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
||||||
|
"action='session-terminate' " +
|
||||||
|
"sid='thisispatrick'>" +
|
||||||
|
"<reason>" +
|
||||||
|
"<busy/>" +
|
||||||
|
"</reason>" +
|
||||||
|
"</jingle>";
|
||||||
|
String xml = getIQXML(romeo, juliet, busy.getStanzaId(), jingleXML);
|
||||||
|
assertXMLEqual(xml, busy.toXML().toString());
|
||||||
|
JingleElement jingle = new JingleProvider().parse(TestUtils.getParser(jingleXML));
|
||||||
|
assertNotNull(jingle);
|
||||||
|
assertEquals(jingle.getAction(), JingleAction.session_terminate);
|
||||||
|
assertEquals(jingle.getReason().asEnum(), JingleReasonElement.Reason.busy);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSessionTerminateAlternativeSessionTest() throws Exception {
|
||||||
|
JingleElement busy = JingleElement.createSessionTerminate(juliet, "thisistherhythm", JingleReasonElement.AlternativeSession("ofthenight"));
|
||||||
|
String jingleXML =
|
||||||
|
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
||||||
|
"action='session-terminate' " +
|
||||||
|
"sid='thisistherhythm'>" +
|
||||||
|
"<reason>" +
|
||||||
|
"<alternative-session>" +
|
||||||
|
"<sid>ofthenight</sid>" +
|
||||||
|
"</alternative-session>" +
|
||||||
|
"</reason>" +
|
||||||
|
"</jingle>";
|
||||||
|
String xml = getIQXML(romeo, juliet, busy.getStanzaId(), jingleXML);
|
||||||
|
assertXMLEqual(xml, busy.toXML().toString());
|
||||||
|
JingleElement jingle = new JingleProvider().parse(TestUtils.getParser(jingleXML));
|
||||||
|
assertNotNull(jingle);
|
||||||
|
assertEquals(jingle.getAction(), JingleAction.session_terminate);
|
||||||
|
assertEquals(jingle.getReason().asEnum(), JingleReasonElement.Reason.alternative_session);
|
||||||
|
JingleReasonElement.AlternativeSession alt = (JingleReasonElement.AlternativeSession) jingle.getReason();
|
||||||
|
assertEquals("ofthenight", alt.getAlternativeSessionId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSessionTerminateCancelTest() throws Exception {
|
||||||
|
JingleElement cancel = JingleElement.createSessionTerminate(juliet, "thisistheend", JingleReasonElement.Reason.cancel);
|
||||||
|
String jingleXML =
|
||||||
|
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
||||||
|
"action='session-terminate' " +
|
||||||
|
"sid='thisistheend'>" +
|
||||||
|
"<reason>" +
|
||||||
|
"<cancel/>" +
|
||||||
|
"</reason>" +
|
||||||
|
"</jingle>";
|
||||||
|
String xml = getIQXML(romeo, juliet, cancel.getStanzaId(), jingleXML);
|
||||||
|
assertXMLEqual(xml, cancel.toXML().toString());
|
||||||
|
JingleElement jingle = new JingleProvider().parse(TestUtils.getParser(jingleXML));
|
||||||
|
assertNotNull(jingle);
|
||||||
|
assertEquals(jingle.getAction(), JingleAction.session_terminate);
|
||||||
|
assertEquals(jingle.getReason().asEnum(), JingleReasonElement.Reason.cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSessionTerminateUnsupportedTransportsTest() throws Exception {
|
||||||
|
JingleElement unsupportedTransports = JingleElement.createSessionTerminate(juliet, "thisisus", JingleReasonElement.Reason.unsupported_transports);
|
||||||
|
String jingleXML =
|
||||||
|
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
||||||
|
"action='session-terminate' " +
|
||||||
|
"sid='thisisus'>" +
|
||||||
|
"<reason>" +
|
||||||
|
"<unsupported-transports/>" +
|
||||||
|
"</reason>" +
|
||||||
|
"</jingle>";
|
||||||
|
String xml = getIQXML(romeo, juliet, unsupportedTransports.getStanzaId(), jingleXML);
|
||||||
|
assertXMLEqual(xml, unsupportedTransports.toXML().toString());
|
||||||
|
JingleElement jingle = new JingleProvider().parse(TestUtils.getParser(jingleXML));
|
||||||
|
assertNotNull(jingle);
|
||||||
|
assertEquals(jingle.getAction(), JingleAction.session_terminate);
|
||||||
|
assertEquals(jingle.getReason().asEnum(), JingleReasonElement.Reason.unsupported_transports);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSessionTerminateUnsupportedApplicationsTest() throws Exception {
|
||||||
|
JingleElement unsupportedApplications = JingleElement.createSessionTerminate(juliet, "thisiswar", JingleReasonElement.Reason.unsupported_applications);
|
||||||
|
String jingleXML =
|
||||||
|
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
||||||
|
"action='session-terminate' " +
|
||||||
|
"sid='thisiswar'>" +
|
||||||
|
"<reason>" +
|
||||||
|
"<unsupported-applications/>" +
|
||||||
|
"</reason>" +
|
||||||
|
"</jingle>";
|
||||||
|
String xml = getIQXML(romeo, juliet, unsupportedApplications.getStanzaId(), jingleXML);
|
||||||
|
assertXMLEqual(xml, unsupportedApplications.toXML().toString());
|
||||||
|
JingleElement jingle = new JingleProvider().parse(TestUtils.getParser(jingleXML));
|
||||||
|
assertNotNull(jingle);
|
||||||
|
assertEquals(jingle.getAction(), JingleAction.session_terminate);
|
||||||
|
assertEquals(jingle.getReason().asEnum(), JingleReasonElement.Reason.unsupported_applications);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSessionTerminateFailedTransportTest() throws IOException, SAXException {
|
||||||
|
JingleElement failedTransport = JingleElement.createSessionTerminate(juliet, "derailed", JingleReasonElement.Reason.failed_transport);
|
||||||
|
String jingleXML =
|
||||||
|
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
||||||
|
"action='session-terminate' " +
|
||||||
|
"sid='derailed'>" +
|
||||||
|
"<reason>" +
|
||||||
|
"<failed-transport/>" +
|
||||||
|
"</reason>" +
|
||||||
|
"</jingle>";
|
||||||
|
String xml = getIQXML(romeo, juliet, failedTransport.getStanzaId(), jingleXML);
|
||||||
|
assertXMLEqual(xml, failedTransport.toXML().toString());
|
||||||
|
assertEquals(JingleAction.session_terminate, failedTransport.getAction());
|
||||||
|
assertEquals(JingleReasonElement.Reason.failed_transport, failedTransport.getReason().asEnum());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSessionTerminateFailedApplicationTest() throws IOException, SAXException {
|
||||||
|
JingleElement failedApplication = JingleElement.createSessionTerminate(juliet, "crashed", JingleReasonElement.Reason.failed_application);
|
||||||
|
String jingleXML =
|
||||||
|
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
||||||
|
"action='session-terminate' " +
|
||||||
|
"sid='crashed'>" +
|
||||||
|
"<reason>" +
|
||||||
|
"<failed-application/>" +
|
||||||
|
"</reason>" +
|
||||||
|
"</jingle>";
|
||||||
|
String xml = getIQXML(romeo, juliet, failedApplication.getStanzaId(), jingleXML);
|
||||||
|
assertXMLEqual(xml, failedApplication.toXML().toString());
|
||||||
|
assertEquals(JingleAction.session_terminate, failedApplication.getAction());
|
||||||
|
assertEquals(JingleReasonElement.Reason.failed_application, failedApplication.getReason().asEnum());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSessionPingTest() throws Exception {
|
||||||
|
JingleElement ping = JingleElement.createSessionPing(juliet, "thisisit");
|
||||||
|
String jingleXML =
|
||||||
|
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
||||||
|
"action='session-info' " +
|
||||||
|
"sid='thisisit'/>";
|
||||||
|
String xml = getIQXML(romeo, juliet, ping.getStanzaId(), jingleXML);
|
||||||
|
assertXMLEqual(xml, ping.toXML().toString());
|
||||||
|
JingleElement jingle = new JingleProvider().parse(TestUtils.getParser(jingleXML));
|
||||||
|
assertNotNull(jingle);
|
||||||
|
assertEquals(JingleAction.session_info, jingle.getAction());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSessionTerminateContentCancelTest() throws Exception {
|
||||||
|
JingleElement cancel = JingleElement.createSessionTerminateContentCancel(juliet, "thisismumbo#5", JingleContentElement.Creator.initiator, "content123");
|
||||||
|
String jingleXML =
|
||||||
|
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
||||||
|
"action='session-terminate' " +
|
||||||
|
"sid='thisismumbo#5'>" +
|
||||||
|
"<content creator='initiator' name='content123'/>" +
|
||||||
|
"<reason>" +
|
||||||
|
"<cancel/>" +
|
||||||
|
"</reason>" +
|
||||||
|
"</jingle>";
|
||||||
|
String xml = getIQXML(romeo, juliet, cancel.getStanzaId(), jingleXML);
|
||||||
|
assertXMLEqual(xml, cancel.toXML().toString());
|
||||||
|
JingleElement jingle = new JingleProvider().parse(TestUtils.getParser(jingleXML));
|
||||||
|
assertNotNull(jingle);
|
||||||
|
assertEquals(JingleAction.session_terminate, jingle.getAction());
|
||||||
|
assertEquals(JingleReasonElement.Reason.cancel, jingle.getReason().asEnum());
|
||||||
|
assertEquals("thisismumbo#5", jingle.getSid());
|
||||||
|
JingleContentElement content = jingle.getContents().get(0);
|
||||||
|
assertNotNull(content);
|
||||||
|
assertEquals("content123", content.getName());
|
||||||
|
assertEquals(JingleContentElement.Creator.initiator, content.getCreator());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSessionTerminateIncompatibleParameters() throws IOException, SAXException {
|
||||||
|
JingleElement terminate = JingleElement.createSessionTerminate(juliet, "incompatibleSID", JingleReasonElement.Reason.incompatible_parameters);
|
||||||
|
String jingleXML =
|
||||||
|
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
||||||
|
"action='session-terminate' " +
|
||||||
|
"sid='incompatibleSID'>" +
|
||||||
|
"<reason>" +
|
||||||
|
"<incompatible-parameters/>" +
|
||||||
|
"</reason>" +
|
||||||
|
"</jingle>";
|
||||||
|
String xml = getIQXML(romeo, juliet, terminate.getStanzaId(), jingleXML);
|
||||||
|
assertXMLEqual(xml, terminate.toXML().toString());
|
||||||
|
assertEquals(JingleReasonElement.Reason.incompatible_parameters, terminate.getReason().asEnum());
|
||||||
|
assertEquals("incompatibleSID", terminate.getSid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createTransportAcceptTest() throws IOException, SAXException {
|
||||||
|
JingleElement transportAccept = JingleElement.createTransportAccept(juliet, romeo, "transAcc", JingleContentElement.Creator.initiator, "cname", new JingleIBBTransportElement("transid", JingleIBBTransport.DEFAULT_BLOCK_SIZE));
|
||||||
|
String jingleXML =
|
||||||
|
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
||||||
|
"action='transport-accept' " +
|
||||||
|
"initiator='" + juliet + "' " +
|
||||||
|
"sid='transAcc'>" +
|
||||||
|
"<content creator='initiator' name='cname'>" +
|
||||||
|
"<transport xmlns='urn:xmpp:jingle:transports:ibb:1' " +
|
||||||
|
"block-size='" + JingleIBBTransport.DEFAULT_BLOCK_SIZE + "' " +
|
||||||
|
"sid='transid'/>" +
|
||||||
|
"</content>" +
|
||||||
|
"</jingle>";
|
||||||
|
String xml = getIQXML(juliet, romeo, transportAccept.getStanzaId(), jingleXML);
|
||||||
|
assertXMLEqual(xml, transportAccept.toXML().toString());
|
||||||
|
assertEquals(JingleAction.transport_accept, transportAccept.getAction());
|
||||||
|
assertEquals("transAcc", transportAccept.getSid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createTransportRejectTest() {
|
||||||
|
//TODO: Find example
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createTransportReplaceTest() throws IOException, SAXException {
|
||||||
|
JingleElement transportReplace = JingleElement.createTransportReplace(juliet, romeo, "transAcc", JingleContentElement.Creator.initiator, "cname", new JingleIBBTransportElement("transid", JingleIBBTransport.DEFAULT_BLOCK_SIZE));
|
||||||
|
String jingleXML =
|
||||||
|
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
||||||
|
"action='transport-replace' " +
|
||||||
|
"initiator='" + juliet + "' " +
|
||||||
|
"sid='transAcc'>" +
|
||||||
|
"<content creator='initiator' name='cname'>" +
|
||||||
|
"<transport xmlns='urn:xmpp:jingle:transports:ibb:1' " +
|
||||||
|
"block-size='" + JingleIBBTransport.DEFAULT_BLOCK_SIZE + "' " +
|
||||||
|
"sid='transid'/>" +
|
||||||
|
"</content>" +
|
||||||
|
"</jingle>";
|
||||||
|
String xml = getIQXML(juliet, romeo, transportReplace.getStanzaId(), jingleXML);
|
||||||
|
assertXMLEqual(xml, transportReplace.toXML().toString());
|
||||||
|
assertEquals(JingleAction.transport_replace, transportReplace.getAction());
|
||||||
|
assertEquals("transAcc", transportReplace.getSid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createErrorMalformedRequestTest() throws Exception {
|
||||||
|
JingleElement j = defaultJingle(romeo, "error123");
|
||||||
|
IQ error = JingleElement.createJingleErrorMalformedRequest(j);
|
||||||
|
String xml =
|
||||||
|
"<iq " +
|
||||||
|
"from='" + romeo + "' " +
|
||||||
|
"id='" + j.getStanzaId() + "' " +
|
||||||
|
"type='error'>" +
|
||||||
|
"<error type='cancel'>" +
|
||||||
|
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
|
||||||
|
"</error>" +
|
||||||
|
"</iq>";
|
||||||
|
assertXMLEqual(xml, error.toXML().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createErrorTieBreakTest() throws IOException, SAXException {
|
||||||
|
JingleElement j = defaultJingle(romeo, "thisistie");
|
||||||
|
IQ error = JingleElement.createJingleErrorTieBreak(j);
|
||||||
|
String xml =
|
||||||
|
"<iq " +
|
||||||
|
"from='" + romeo + "' " +
|
||||||
|
"id='" + j.getStanzaId() + "' " +
|
||||||
|
"type='error'>" +
|
||||||
|
"<error type='cancel'>" +
|
||||||
|
"<conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
|
||||||
|
"<tie-break xmlns='urn:xmpp:jingle:errors:1'/>" +
|
||||||
|
"</error>" +
|
||||||
|
"</iq>";
|
||||||
|
assertXMLEqual(xml, error.toXML().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createErrorUnknownSessionTest() throws IOException, SAXException {
|
||||||
|
JingleElement j = defaultJingle(romeo, "youknownothingjohnsnow");
|
||||||
|
IQ error = JingleElement.createJingleErrorUnknownSession(j);
|
||||||
|
String xml =
|
||||||
|
"<iq " +
|
||||||
|
"from='" + romeo + "' " +
|
||||||
|
"id='" + j.getStanzaId() + "' " +
|
||||||
|
"type='error'>" +
|
||||||
|
"<error type='cancel'>" +
|
||||||
|
"<item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
|
||||||
|
"<unknown-session xmlns='urn:xmpp:jingle:errors:1'/>" +
|
||||||
|
"</error>" +
|
||||||
|
"</iq>";
|
||||||
|
assertXMLEqual(xml, error.toXML().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createErrorUnknownInitiatorTest() throws IOException, SAXException {
|
||||||
|
JingleElement j = defaultJingle(romeo, "iamyourfather");
|
||||||
|
IQ error = JingleElement.createJingleErrorUnknownInitiator(j);
|
||||||
|
String xml =
|
||||||
|
"<iq " +
|
||||||
|
"from='" + romeo + "' " +
|
||||||
|
"id='" + j.getStanzaId() + "' " +
|
||||||
|
"type='error'>" +
|
||||||
|
"<error type='cancel'>" +
|
||||||
|
"<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
|
||||||
|
"</error>" +
|
||||||
|
"</iq>";
|
||||||
|
assertXMLEqual(xml, error.toXML().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createErrorOutOfOrderTest() throws IOException, SAXException {
|
||||||
|
JingleElement j = defaultJingle(romeo, "yourfatheriam");
|
||||||
|
IQ error = JingleElement.createJingleErrorOutOfOrder(j);
|
||||||
|
String xml =
|
||||||
|
"<iq " +
|
||||||
|
"from='" + romeo + "' " +
|
||||||
|
"id='" + j.getStanzaId() + "' " +
|
||||||
|
"type='error'>" +
|
||||||
|
//"<error type='cancel'>" +
|
||||||
|
"<error type='modify'>" + //TODO: Why?
|
||||||
|
"<unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
|
||||||
|
"<out-of-order xmlns='urn:xmpp:jingle:errors:1'/>" +
|
||||||
|
"</error>" +
|
||||||
|
"</iq>";
|
||||||
|
assertXMLEqual(xml, error.toXML().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createErrorUnsupportedInfoTest() throws IOException, SAXException {
|
||||||
|
JingleElement j = defaultJingle(romeo, "thisstatementiswrong");
|
||||||
|
IQ error = JingleElement.createJingleErrorUnsupportedInfo(j);
|
||||||
|
String xml =
|
||||||
|
"<iq " +
|
||||||
|
"from='" + romeo + "' " +
|
||||||
|
"id='" + j.getStanzaId() + "' " +
|
||||||
|
"type='error'>" +
|
||||||
|
"<error type='modify'>" +
|
||||||
|
"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
|
||||||
|
"<unsupported-info xmlns='urn:xmpp:jingle:errors:1'/>" +
|
||||||
|
"</error>" +
|
||||||
|
"</iq>";
|
||||||
|
assertXMLEqual(xml, error.toXML().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getIQXML(FullJid from, FullJid to, String stanzaId, String jingleXML) {
|
||||||
|
return "<iq id='" + stanzaId + "' to='" + to + "' type='set'>" +
|
||||||
|
jingleXML +
|
||||||
|
"</iq>";
|
||||||
|
}
|
||||||
|
|
||||||
|
private JingleElement defaultJingle(FullJid recipient, String sessionId) {
|
||||||
|
return JingleElement.createSessionPing(recipient, sessionId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ import static junit.framework.TestCase.assertEquals;
|
||||||
|
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
import org.jivesoftware.smack.test.util.TestUtils;
|
import org.jivesoftware.smack.test.util.TestUtils;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleError;
|
import org.jivesoftware.smackx.jingle.element.JingleErrorElement;
|
||||||
import org.jivesoftware.smackx.jingle.provider.JingleErrorProvider;
|
import org.jivesoftware.smackx.jingle.provider.JingleErrorProvider;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -28,39 +28,39 @@ import org.junit.Test;
|
||||||
/**
|
/**
|
||||||
* Test the JingleError class.
|
* Test the JingleError class.
|
||||||
*/
|
*/
|
||||||
public class JingleErrorTest extends SmackTestSuite {
|
public class JingleErrorElementTest extends SmackTestSuite {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void tieBreakTest() throws Exception {
|
public void tieBreakTest() throws Exception {
|
||||||
String xml = "<tie-break xmlns='urn:xmpp:jingle:errors:1'/>";
|
String xml = "<tie-break xmlns='urn:xmpp:jingle:errors:1'/>";
|
||||||
JingleError error = new JingleErrorProvider().parse(TestUtils.getParser(xml));
|
JingleErrorElement error = new JingleErrorProvider().parse(TestUtils.getParser(xml));
|
||||||
assertEquals(xml, error.toXML().toString());
|
assertEquals(xml, error.toXML().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void unknownSessionTest() throws Exception {
|
public void unknownSessionTest() throws Exception {
|
||||||
String xml = "<unknown-session xmlns='urn:xmpp:jingle:errors:1'/>";
|
String xml = "<unknown-session xmlns='urn:xmpp:jingle:errors:1'/>";
|
||||||
JingleError error = new JingleErrorProvider().parse(TestUtils.getParser(xml));
|
JingleErrorElement error = new JingleErrorProvider().parse(TestUtils.getParser(xml));
|
||||||
assertEquals(xml, error.toXML().toString());
|
assertEquals(xml, error.toXML().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void unsupportedInfoTest() throws Exception {
|
public void unsupportedInfoTest() throws Exception {
|
||||||
String xml = "<unsupported-info xmlns='urn:xmpp:jingle:errors:1'/>";
|
String xml = "<unsupported-info xmlns='urn:xmpp:jingle:errors:1'/>";
|
||||||
JingleError error = new JingleErrorProvider().parse(TestUtils.getParser(xml));
|
JingleErrorElement error = new JingleErrorProvider().parse(TestUtils.getParser(xml));
|
||||||
assertEquals(xml, error.toXML().toString());
|
assertEquals(xml, error.toXML().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void outOfOrderTest() throws Exception {
|
public void outOfOrderTest() throws Exception {
|
||||||
String xml = "<out-of-order xmlns='urn:xmpp:jingle:errors:1'/>";
|
String xml = "<out-of-order xmlns='urn:xmpp:jingle:errors:1'/>";
|
||||||
JingleError error = new JingleErrorProvider().parse(TestUtils.getParser(xml));
|
JingleErrorElement error = new JingleErrorProvider().parse(TestUtils.getParser(xml));
|
||||||
assertEquals(xml, error.toXML().toString());
|
assertEquals(xml, error.toXML().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void illegalArgumentTest() {
|
public void illegalArgumentTest() {
|
||||||
JingleError.fromString("inexistent-error");
|
JingleErrorElement.fromString("inexistent-error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,67 +20,67 @@ import static junit.framework.TestCase.assertEquals;
|
||||||
|
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleReason;
|
import org.jivesoftware.smackx.jingle.element.JingleReasonElement;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test JingleReason functionality.
|
* Test JingleReason functionality.
|
||||||
*/
|
*/
|
||||||
public class JingleReasonTest extends SmackTestSuite {
|
public class JingleReasonElementTest extends SmackTestSuite {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parserTest() {
|
public void parserTest() {
|
||||||
assertEquals("<reason><success/></reason>",
|
assertEquals("<reason><success/></reason>",
|
||||||
JingleReason.Success.toXML().toString());
|
JingleReasonElement.Success.toXML().toString());
|
||||||
assertEquals("<reason><busy/></reason>",
|
assertEquals("<reason><busy/></reason>",
|
||||||
JingleReason.Busy.toXML().toString());
|
JingleReasonElement.Busy.toXML().toString());
|
||||||
assertEquals("<reason><cancel/></reason>",
|
assertEquals("<reason><cancel/></reason>",
|
||||||
JingleReason.Cancel.toXML().toString());
|
JingleReasonElement.Cancel.toXML().toString());
|
||||||
assertEquals("<reason><connectivity-error/></reason>",
|
assertEquals("<reason><connectivity-error/></reason>",
|
||||||
JingleReason.ConnectivityError.toXML().toString());
|
JingleReasonElement.ConnectivityError.toXML().toString());
|
||||||
assertEquals("<reason><decline/></reason>",
|
assertEquals("<reason><decline/></reason>",
|
||||||
JingleReason.Decline.toXML().toString());
|
JingleReasonElement.Decline.toXML().toString());
|
||||||
assertEquals("<reason><expired/></reason>",
|
assertEquals("<reason><expired/></reason>",
|
||||||
JingleReason.Expired.toXML().toString());
|
JingleReasonElement.Expired.toXML().toString());
|
||||||
assertEquals("<reason><unsupported-transports/></reason>",
|
assertEquals("<reason><unsupported-transports/></reason>",
|
||||||
JingleReason.UnsupportedTransports.toXML().toString());
|
JingleReasonElement.UnsupportedTransports.toXML().toString());
|
||||||
assertEquals("<reason><failed-transport/></reason>",
|
assertEquals("<reason><failed-transport/></reason>",
|
||||||
JingleReason.FailedTransport.toXML().toString());
|
JingleReasonElement.FailedTransport.toXML().toString());
|
||||||
assertEquals("<reason><general-error/></reason>",
|
assertEquals("<reason><general-error/></reason>",
|
||||||
JingleReason.GeneralError.toXML().toString());
|
JingleReasonElement.GeneralError.toXML().toString());
|
||||||
assertEquals("<reason><gone/></reason>",
|
assertEquals("<reason><gone/></reason>",
|
||||||
JingleReason.Gone.toXML().toString());
|
JingleReasonElement.Gone.toXML().toString());
|
||||||
assertEquals("<reason><media-error/></reason>",
|
assertEquals("<reason><media-error/></reason>",
|
||||||
JingleReason.MediaError.toXML().toString());
|
JingleReasonElement.MediaError.toXML().toString());
|
||||||
assertEquals("<reason><security-error/></reason>",
|
assertEquals("<reason><security-error/></reason>",
|
||||||
JingleReason.SecurityError.toXML().toString());
|
JingleReasonElement.SecurityError.toXML().toString());
|
||||||
assertEquals("<reason><unsupported-applications/></reason>",
|
assertEquals("<reason><unsupported-applications/></reason>",
|
||||||
JingleReason.UnsupportedApplications.toXML().toString());
|
JingleReasonElement.UnsupportedApplications.toXML().toString());
|
||||||
assertEquals("<reason><timeout/></reason>",
|
assertEquals("<reason><timeout/></reason>",
|
||||||
JingleReason.Timeout.toXML().toString());
|
JingleReasonElement.Timeout.toXML().toString());
|
||||||
assertEquals("<reason><failed-application/></reason>",
|
assertEquals("<reason><failed-application/></reason>",
|
||||||
JingleReason.FailedApplication.toXML().toString());
|
JingleReasonElement.FailedApplication.toXML().toString());
|
||||||
assertEquals("<reason><incompatible-parameters/></reason>",
|
assertEquals("<reason><incompatible-parameters/></reason>",
|
||||||
JingleReason.IncompatibleParameters.toXML().toString());
|
JingleReasonElement.IncompatibleParameters.toXML().toString());
|
||||||
assertEquals("<reason><alternative-session><sid>1234</sid></alternative-session></reason>",
|
assertEquals("<reason><alternative-session><sid>1234</sid></alternative-session></reason>",
|
||||||
JingleReason.AlternativeSession("1234").toXML().toString());
|
JingleReasonElement.AlternativeSession("1234").toXML().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = NullPointerException.class)
|
@Test(expected = NullPointerException.class)
|
||||||
public void alternativeSessionEmptyStringTest() {
|
public void alternativeSessionEmptyStringTest() {
|
||||||
// Alternative sessionID must not be empty
|
// Alternative sessionID must not be empty
|
||||||
JingleReason.AlternativeSession("");
|
JingleReasonElement.AlternativeSession("");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = NullPointerException.class)
|
@Test(expected = NullPointerException.class)
|
||||||
public void alternativeSessionNullStringTest() {
|
public void alternativeSessionNullStringTest() {
|
||||||
// Alternative sessionID must not be null
|
// Alternative sessionID must not be null
|
||||||
JingleReason.AlternativeSession(null);
|
JingleReasonElement.AlternativeSession(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void illegalArgumentTest() {
|
public void illegalArgumentTest() {
|
||||||
JingleReason.Reason.fromString("illegal-reason");
|
JingleReasonElement.Reason.fromString("illegal-reason");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,81 +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.assertNotNull;
|
|
||||||
import static junit.framework.TestCase.assertTrue;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
|
||||||
|
|
||||||
import org.jivesoftware.smackx.jingle.element.Jingle;
|
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleAction;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.jxmpp.jid.FullJid;
|
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
|
||||||
import org.jxmpp.stringprep.XmppStringprepException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test the Jingle class.
|
|
||||||
*/
|
|
||||||
public class JingleTest extends SmackTestSuite {
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void emptyBuilderTest() {
|
|
||||||
Jingle.Builder builder = Jingle.getBuilder();
|
|
||||||
builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = NullPointerException.class)
|
|
||||||
public void onlySessionIdBuilderTest() {
|
|
||||||
String sessionId = "testSessionId";
|
|
||||||
|
|
||||||
Jingle.Builder builder = Jingle.getBuilder();
|
|
||||||
builder.setSessionId(sessionId);
|
|
||||||
builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parserTest() throws XmppStringprepException {
|
|
||||||
String sessionId = "testSessionId";
|
|
||||||
|
|
||||||
Jingle.Builder builder = Jingle.getBuilder();
|
|
||||||
builder.setSessionId(sessionId);
|
|
||||||
builder.setAction(JingleAction.session_initiate);
|
|
||||||
|
|
||||||
FullJid romeo = JidCreate.fullFrom("romeo@montague.lit/orchard");
|
|
||||||
FullJid juliet = JidCreate.fullFrom("juliet@capulet.lit/balcony");
|
|
||||||
builder.setInitiator(romeo);
|
|
||||||
builder.setResponder(juliet);
|
|
||||||
|
|
||||||
Jingle jingle = builder.build();
|
|
||||||
assertNotNull(jingle);
|
|
||||||
assertEquals(romeo, jingle.getInitiator());
|
|
||||||
assertEquals(juliet, jingle.getResponder());
|
|
||||||
assertEquals(jingle.getAction(), JingleAction.session_initiate);
|
|
||||||
assertEquals(sessionId, jingle.getSid());
|
|
||||||
|
|
||||||
String xml = "<jingle xmlns='urn:xmpp:jingle:1' " +
|
|
||||||
"initiator='romeo@montague.lit/orchard' " +
|
|
||||||
"responder='juliet@capulet.lit/balcony' " +
|
|
||||||
"action='session-initiate' " +
|
|
||||||
"sid='" + sessionId + "'>" +
|
|
||||||
"</jingle>";
|
|
||||||
assertTrue(jingle.toXML().toString().contains(xml));
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue