mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-26 08:12:05 +01:00
Merge pull request #144 from vanitasvitae/jingleFTElements
Add Jingle File Transfer elements and JingleUtil class
This commit is contained in:
commit
cd2d55944f
44 changed files with 2981 additions and 157 deletions
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer;
|
||||||
|
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.Manager;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager for JingleFileTransfer (XEP-0234).
|
||||||
|
*/
|
||||||
|
public final class JingleFileTransferManager extends Manager {
|
||||||
|
|
||||||
|
private static final WeakHashMap<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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer.element;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
|
import org.jivesoftware.smack.util.Objects;
|
||||||
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checksum element.
|
||||||
|
*/
|
||||||
|
public class Checksum implements ExtensionElement {
|
||||||
|
public static final String ELEMENT = "checksum";
|
||||||
|
public static final String ATTR_CREATOR = "creator";
|
||||||
|
public static final String ATTR_NAME = "name";
|
||||||
|
|
||||||
|
private final JingleContent.Creator creator;
|
||||||
|
private final String name;
|
||||||
|
private final JingleFileTransferChild file;
|
||||||
|
|
||||||
|
public Checksum(JingleContent.Creator creator, String name, JingleFileTransferChild file) {
|
||||||
|
this.creator = creator;
|
||||||
|
this.name = name;
|
||||||
|
this.file = Objects.requireNonNull(file, "file MUST NOT be null.");
|
||||||
|
Objects.requireNonNull(file.getHash(), "file MUST contain at least one hash element.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getElementName() {
|
||||||
|
return ELEMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence toXML() {
|
||||||
|
XmlStringBuilder sb = new XmlStringBuilder(this);
|
||||||
|
sb.optAttribute(ATTR_CREATOR, creator);
|
||||||
|
sb.optAttribute(ATTR_NAME, name);
|
||||||
|
sb.rightAngleBracket();
|
||||||
|
sb.element(file);
|
||||||
|
sb.closeElement(this);
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return JingleFileTransfer.NAMESPACE_V5;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer.element;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File element.
|
||||||
|
*/
|
||||||
|
public class JingleFileTransfer extends JingleContentDescription {
|
||||||
|
public static final String NAMESPACE_V5 = "urn:xmpp:jingle:apps:file-transfer:5";
|
||||||
|
|
||||||
|
public JingleFileTransfer(List<JingleContentDescriptionChildElement> payloads) {
|
||||||
|
super(payloads);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return NAMESPACE_V5;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer.element;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
import org.jivesoftware.smackx.hashes.element.HashElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content of type File.
|
||||||
|
*/
|
||||||
|
public class JingleFileTransferChild extends JingleContentDescriptionChildElement {
|
||||||
|
public static final String ELEMENT = "file";
|
||||||
|
public static final String ELEM_DATE = "date";
|
||||||
|
public static final String ELEM_DESC = "desc";
|
||||||
|
public static final String ELEM_MEDIA_TYPE = "media-type";
|
||||||
|
public static final String ELEM_NAME = "name";
|
||||||
|
public static final String ELEM_SIZE = "size";
|
||||||
|
|
||||||
|
private final Date date;
|
||||||
|
private final String desc;
|
||||||
|
private final HashElement hash;
|
||||||
|
private final String mediaType;
|
||||||
|
private final String name;
|
||||||
|
private final int size;
|
||||||
|
private final Range range;
|
||||||
|
|
||||||
|
public JingleFileTransferChild(Date date, String desc, HashElement hash, String mediaType, String name, int size, Range range) {
|
||||||
|
this.date = date;
|
||||||
|
this.desc = desc;
|
||||||
|
this.hash = hash;
|
||||||
|
this.mediaType = mediaType;
|
||||||
|
this.name = name;
|
||||||
|
this.size = size;
|
||||||
|
this.range = range;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDate() {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashElement getHash() {
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMediaType() {
|
||||||
|
return mediaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Range getRange() {
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getElementName() {
|
||||||
|
return ELEMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence toXML() {
|
||||||
|
XmlStringBuilder sb = new XmlStringBuilder(this);
|
||||||
|
sb.rightAngleBracket();
|
||||||
|
|
||||||
|
sb.optElement(ELEM_DATE, date);
|
||||||
|
sb.optElement(ELEM_DESC, desc);
|
||||||
|
sb.optElement(ELEM_MEDIA_TYPE, mediaType);
|
||||||
|
sb.optElement(ELEM_NAME, name);
|
||||||
|
sb.optElement(range);
|
||||||
|
if (size > 0) {
|
||||||
|
sb.element(ELEM_SIZE, Integer.toString(size));
|
||||||
|
}
|
||||||
|
sb.optElement(hash);
|
||||||
|
sb.closeElement(this);
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder getBuilder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Builder {
|
||||||
|
private Date date;
|
||||||
|
private String desc;
|
||||||
|
private HashElement hash;
|
||||||
|
private String mediaType;
|
||||||
|
private String name;
|
||||||
|
private int size;
|
||||||
|
private Range range;
|
||||||
|
|
||||||
|
private Builder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setDate(Date date) {
|
||||||
|
this.date = date;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setDescription(String desc) {
|
||||||
|
this.desc = desc;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setHash(HashElement hash) {
|
||||||
|
this.hash = hash;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setMediaType(String mediaType) {
|
||||||
|
this.mediaType = mediaType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setSize(int size) {
|
||||||
|
this.size = size;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setRange(Range range) {
|
||||||
|
this.range = range;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleFileTransferChild build() {
|
||||||
|
return new JingleFileTransferChild(date, desc, hash, mediaType, name, size, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setFile(File file) {
|
||||||
|
return setDate(new Date(file.lastModified()))
|
||||||
|
.setName(file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf("/") + 1))
|
||||||
|
.setSize((int) file.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright © 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer.element;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.NamedElement;
|
||||||
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
import org.jivesoftware.smackx.hashes.element.HashElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RangeElement which specifies, which range of a file shall be transferred.
|
||||||
|
*/
|
||||||
|
public class Range implements NamedElement {
|
||||||
|
|
||||||
|
public static final String ELEMENT = "range";
|
||||||
|
public static final String ATTR_OFFSET = "offset";
|
||||||
|
public static final String ATTR_LENGTH = "length";
|
||||||
|
|
||||||
|
private final int offset, length;
|
||||||
|
private final HashElement hash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Range element with default values.
|
||||||
|
*/
|
||||||
|
public Range() {
|
||||||
|
this(0, -1, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Range element with specified length.
|
||||||
|
* @param length length of the transmitted data in bytes.
|
||||||
|
*/
|
||||||
|
public Range(int length) {
|
||||||
|
this(0, length, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Range element with specified offset and length.
|
||||||
|
* @param offset offset in bytes from the beginning of the transmitted data.
|
||||||
|
* @param length number of bytes that shall be transferred.
|
||||||
|
*/
|
||||||
|
public Range(int offset, int length) {
|
||||||
|
this(offset, length, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Range element with specified offset, length and hash.
|
||||||
|
* @param offset offset in bytes from the beginning of the transmitted data.
|
||||||
|
* @param length number of bytes that shall be transferred.
|
||||||
|
* @param hash hash of the bytes in the specified range.
|
||||||
|
*/
|
||||||
|
public Range(int offset, int length, HashElement hash) {
|
||||||
|
this.offset = offset;
|
||||||
|
this.length = length;
|
||||||
|
this.hash = hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the index of the offset.
|
||||||
|
* This marks the begin of the specified range.
|
||||||
|
* @return offset
|
||||||
|
*/
|
||||||
|
public int getOffset() {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the length of the range.
|
||||||
|
* @return length
|
||||||
|
*/
|
||||||
|
public int getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the hash element that contains a checksum of the bytes specified in the range.
|
||||||
|
* @return hash element
|
||||||
|
*/
|
||||||
|
public HashElement getHash() {
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getElementName() {
|
||||||
|
return ELEMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence toXML() {
|
||||||
|
XmlStringBuilder sb = new XmlStringBuilder(this);
|
||||||
|
|
||||||
|
if (offset > 0) {
|
||||||
|
sb.attribute(ATTR_OFFSET, offset);
|
||||||
|
}
|
||||||
|
if (length > 0) {
|
||||||
|
sb.attribute(ATTR_LENGTH, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hash != null) {
|
||||||
|
sb.rightAngleBracket();
|
||||||
|
sb.element(hash);
|
||||||
|
sb.closeElement(this);
|
||||||
|
} else {
|
||||||
|
sb.closeEmptyElement();
|
||||||
|
}
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (other == null || !(other instanceof Range)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.hashCode() == other.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return toXML().toString().hashCode();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smack's API for <a href="https://xmpp.org/extensions/xep-0234.html">XEP-0234: Jingle File Transfer</a>.
|
||||||
|
* Elements.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer.element;
|
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smack's API for <a href="https://xmpp.org/extensions/xep-0234.html">XEP-0234: Jingle File Transfer</a>.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer;
|
|
@ -0,0 +1,90 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer.provider;
|
||||||
|
|
||||||
|
import static org.xmlpull.v1.XmlPullParser.END_TAG;
|
||||||
|
import static org.xmlpull.v1.XmlPullParser.START_TAG;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||||
|
import org.jivesoftware.smackx.hashes.element.HashElement;
|
||||||
|
import org.jivesoftware.smackx.hashes.provider.HashElementProvider;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.Checksum;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChild;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.Range;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider for the Checksum element.
|
||||||
|
*/
|
||||||
|
public class ChecksumProvider extends ExtensionElementProvider<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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer.provider;
|
||||||
|
|
||||||
|
import static org.xmlpull.v1.XmlPullParser.END_TAG;
|
||||||
|
import static org.xmlpull.v1.XmlPullParser.START_TAG;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.hashes.element.HashElement;
|
||||||
|
import org.jivesoftware.smackx.hashes.provider.HashElementProvider;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.provider.JingleContentDescriptionProvider;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransfer;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChild;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.Range;
|
||||||
|
|
||||||
|
import org.jxmpp.util.XmppDateTime;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider for JingleContentDescriptionFileTransfer elements.
|
||||||
|
*/
|
||||||
|
public class JingleFileTransferProvider
|
||||||
|
extends JingleContentDescriptionProvider<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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smack's API for <a href="https://xmpp.org/extensions/xep-0234.html">XEP-0234: Jingle File Transfer</a>.
|
||||||
|
* Providers.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer.provider;
|
|
@ -538,7 +538,7 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream
|
||||||
* @throws NotConnectedException
|
* @throws NotConnectedException
|
||||||
* @throws InterruptedException
|
* @throws InterruptedException
|
||||||
*/
|
*/
|
||||||
private List<Jid> determineProxies() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
public List<Jid> determineProxies() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
XMPPConnection connection = connection();
|
XMPPConnection connection = connection();
|
||||||
ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
|
ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
|
||||||
|
|
||||||
|
@ -634,7 +634,7 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream
|
||||||
* @return the stream host information of the local SOCKS5 proxy or null if local SOCKS5 proxy
|
* @return the stream host information of the local SOCKS5 proxy or null if local SOCKS5 proxy
|
||||||
* is not running
|
* is not running
|
||||||
*/
|
*/
|
||||||
private List<StreamHost> getLocalStreamHost() {
|
public List<StreamHost> getLocalStreamHost() {
|
||||||
XMPPConnection connection = connection();
|
XMPPConnection connection = connection();
|
||||||
// get local proxy singleton
|
// get local proxy singleton
|
||||||
Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy();
|
Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy();
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class Socks5BytestreamSession implements BytestreamSession {
|
||||||
/* flag to indicate if this session is a direct or mediated connection */
|
/* flag to indicate if this session is a direct or mediated connection */
|
||||||
private final boolean isDirect;
|
private final boolean isDirect;
|
||||||
|
|
||||||
protected Socks5BytestreamSession(Socket socket, boolean isDirect) {
|
public Socks5BytestreamSession(Socket socket, boolean isDirect) {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.isDirect = isDirect;
|
this.isDirect = isDirect;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;
|
||||||
*
|
*
|
||||||
* @author Henning Staib
|
* @author Henning Staib
|
||||||
*/
|
*/
|
||||||
class Socks5Client {
|
public class Socks5Client {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(Socks5Client.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(Socks5Client.class.getName());
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ import org.jxmpp.jid.Jid;
|
||||||
*
|
*
|
||||||
* @author Henning Staib
|
* @author Henning Staib
|
||||||
*/
|
*/
|
||||||
class Socks5ClientForInitiator extends Socks5Client {
|
public class Socks5ClientForInitiator extends Socks5Client {
|
||||||
|
|
||||||
/* the XMPP connection used to communicate with the SOCKS5 proxy */
|
/* the XMPP connection used to communicate with the SOCKS5 proxy */
|
||||||
private WeakReference<XMPPConnection> connection;
|
private WeakReference<XMPPConnection> connection;
|
||||||
|
|
|
@ -348,7 +348,7 @@ public final class Socks5Proxy {
|
||||||
*
|
*
|
||||||
* @param digest to be added to the list of allowed transfers
|
* @param digest to be added to the list of allowed transfers
|
||||||
*/
|
*/
|
||||||
protected void addTransfer(String digest) {
|
public void addTransfer(String digest) {
|
||||||
this.allowedConnections.add(digest);
|
this.allowedConnections.add(digest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ import org.jxmpp.jid.Jid;
|
||||||
*
|
*
|
||||||
* @author Henning Staib
|
* @author Henning Staib
|
||||||
*/
|
*/
|
||||||
class Socks5Utils {
|
public class Socks5Utils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a SHA-1 digest of the given parameters as specified in <a
|
* Returns a SHA-1 digest of the given parameters as specified in <a
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pair of jid and sessionId.
|
||||||
|
*/
|
||||||
|
public class FullJidAndSessionId {
|
||||||
|
private final FullJid fullJid;
|
||||||
|
private final String sessionId;
|
||||||
|
|
||||||
|
public FullJidAndSessionId(FullJid fullJid, String sessionId) {
|
||||||
|
this.fullJid = fullJid;
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FullJid getFullJid() {
|
||||||
|
return fullJid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSessionId() {
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hashCode = 31 * fullJid.hashCode();
|
||||||
|
hashCode = 31 * hashCode + sessionId.hashCode();
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (!(other instanceof FullJidAndSessionId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FullJidAndSessionId otherFullJidAndSessionId = (FullJidAndSessionId) other;
|
||||||
|
return fullJid.equals(otherFullJidAndSessionId.fullJid)
|
||||||
|
&& sessionId.equals(otherFullJidAndSessionId.sessionId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,9 @@ package org.jivesoftware.smackx.jingle;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
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.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smack.Manager;
|
import org.jivesoftware.smack.Manager;
|
||||||
|
@ -27,13 +30,15 @@ import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
|
||||||
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
|
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.IQ.Type;
|
import org.jivesoftware.smack.packet.IQ.Type;
|
||||||
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smackx.jingle.element.Jingle;
|
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.JingleContent;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.jingle_ibb.JingleIBBTransportManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.JingleS5BTransportManager;
|
||||||
|
|
||||||
import org.jxmpp.jid.FullJid;
|
import org.jxmpp.jid.FullJid;
|
||||||
import org.jxmpp.jid.Jid;
|
|
||||||
|
|
||||||
public final class JingleManager extends Manager {
|
public final class JingleManager extends Manager {
|
||||||
|
|
||||||
|
@ -41,6 +46,12 @@ public final class JingleManager extends Manager {
|
||||||
|
|
||||||
private static final Map<XMPPConnection, JingleManager> INSTANCES = new WeakHashMap<>();
|
private static final Map<XMPPConnection, JingleManager> INSTANCES = new WeakHashMap<>();
|
||||||
|
|
||||||
|
private static final ExecutorService threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
||||||
|
|
||||||
|
public static ExecutorService getThreadPool() {
|
||||||
|
return threadPool;
|
||||||
|
}
|
||||||
|
|
||||||
public static synchronized JingleManager getInstanceFor(XMPPConnection connection) {
|
public static synchronized JingleManager getInstanceFor(XMPPConnection connection) {
|
||||||
JingleManager jingleManager = INSTANCES.get(connection);
|
JingleManager jingleManager = INSTANCES.get(connection);
|
||||||
if (jingleManager == null) {
|
if (jingleManager == null) {
|
||||||
|
@ -54,45 +65,53 @@ public final class JingleManager extends Manager {
|
||||||
|
|
||||||
private final Map<FullJidAndSessionId, JingleSessionHandler> jingleSessionHandlers = new ConcurrentHashMap<>();
|
private final Map<FullJidAndSessionId, JingleSessionHandler> jingleSessionHandlers = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private final JingleUtil jutil;
|
||||||
|
|
||||||
private JingleManager(XMPPConnection connection) {
|
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(Jingle.ELEMENT, Jingle.NAMESPACE, Type.set, Mode.async) {
|
||||||
@Override
|
@Override
|
||||||
public IQ handleIQRequest(IQ iqRequest) {
|
public IQ handleIQRequest(IQ iqRequest) {
|
||||||
final Jingle jingle = (Jingle) iqRequest;
|
final Jingle jingle = (Jingle) iqRequest;
|
||||||
|
|
||||||
if (jingle.getContents().isEmpty()) {
|
FullJid fullFrom = jingle.getFrom().asFullJidOrThrow();
|
||||||
Jid from = jingle.getFrom();
|
String sid = jingle.getSid();
|
||||||
assert (from != null);
|
FullJidAndSessionId fullJidAndSessionId = new FullJidAndSessionId(fullFrom, sid);
|
||||||
FullJid fullFrom = from.asFullJidOrThrow();
|
|
||||||
String sid = jingle.getSid();
|
|
||||||
FullJidAndSessionId fullJidAndSessionId = new FullJidAndSessionId(fullFrom, sid);
|
|
||||||
JingleSessionHandler jingleSessionHandler = jingleSessionHandlers.get(fullJidAndSessionId);
|
|
||||||
if (jingleSessionHandler == null) {
|
|
||||||
// TODO handle non existing jingle session handler.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return jingleSessionHandler.handleJingleSessionRequest(jingle, sid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jingle.getContents().size() > 1) {
|
JingleSessionHandler sessionHandler = jingleSessionHandlers.get(fullJidAndSessionId);
|
||||||
LOGGER.severe("Jingle IQs with more then one content element are currently not supported by Smack");
|
if (sessionHandler != null) {
|
||||||
return null;
|
//Handle existing session
|
||||||
}
|
return sessionHandler.handleJingleSessionRequest(jingle);
|
||||||
|
}
|
||||||
|
|
||||||
JingleContent content = jingle.getContents().get(0);
|
if (jingle.getAction() == JingleAction.session_initiate) {
|
||||||
JingleContentDescription description = content.getDescription();
|
|
||||||
JingleHandler jingleDescriptionHandler = descriptionHandlers.get(
|
JingleContent content = jingle.getContents().get(0);
|
||||||
description.getNamespace());
|
JingleContentDescription description = content.getDescription();
|
||||||
if (jingleDescriptionHandler == null) {
|
JingleHandler jingleDescriptionHandler = descriptionHandlers.get(
|
||||||
// TODO handle non existing content description handler.
|
description.getNamespace());
|
||||||
return null;
|
|
||||||
}
|
if (jingleDescriptionHandler == null) {
|
||||||
return jingleDescriptionHandler.handleJingleRequest(jingle);
|
//Unsupported Application
|
||||||
|
LOGGER.log(Level.WARNING, "Unsupported Jingle application.");
|
||||||
|
return jutil.createSessionTerminateUnsupportedApplications(fullFrom, sid);
|
||||||
}
|
}
|
||||||
});
|
return jingleDescriptionHandler.handleJingleRequest(jingle);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Unknown session
|
||||||
|
LOGGER.log(Level.WARNING, "Unknown session.");
|
||||||
|
return jutil.createErrorUnknownSession(jingle);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//Register transports.
|
||||||
|
JingleTransportMethodManager transportMethodManager = JingleTransportMethodManager.getInstanceFor(connection);
|
||||||
|
transportMethodManager.registerTransportManager(JingleIBBTransportManager.getInstanceFor(connection));
|
||||||
|
transportMethodManager.registerTransportManager(JingleS5BTransportManager.getInstanceFor(connection));
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleHandler registerDescriptionHandler(String namespace, JingleHandler handler) {
|
public JingleHandler registerDescriptionHandler(String namespace, JingleHandler handler) {
|
||||||
|
@ -109,30 +128,7 @@ public final class JingleManager extends Manager {
|
||||||
return jingleSessionHandlers.remove(fullJidAndSessionId);
|
return jingleSessionHandlers.remove(fullJidAndSessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class FullJidAndSessionId {
|
public static String randomId() {
|
||||||
final FullJid fullJid;
|
return StringUtils.randomString(24);
|
||||||
final String sessionId;
|
|
||||||
|
|
||||||
private FullJidAndSessionId(FullJid fullJid, String sessionId) {
|
|
||||||
this.fullJid = fullJid;
|
|
||||||
this.sessionId = sessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int hashCode = 31 * fullJid.hashCode();
|
|
||||||
hashCode = 31 * hashCode + sessionId.hashCode();
|
|
||||||
return hashCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object other) {
|
|
||||||
if (!(other instanceof FullJidAndSessionId)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
FullJidAndSessionId otherFullJidAndSessionId = (FullJidAndSessionId) other;
|
|
||||||
return fullJid.equals(otherFullJidAndSessionId.fullJid)
|
|
||||||
&& sessionId.equals(otherFullJidAndSessionId.sessionId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,28 +16,108 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle;
|
package org.jivesoftware.smackx.jingle;
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
// TODO: Is this class still required? If not, then remove it.
|
import org.jivesoftware.smack.SmackException;
|
||||||
public class JingleSession {
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.Jingle;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.JingleTransportSession;
|
||||||
|
|
||||||
private final Jid initiator;
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
||||||
private final Jid responder;
|
public abstract class JingleSession implements JingleSessionHandler {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(JingleSession.class.getName());
|
||||||
|
protected HashSet<String> failedTransportMethods = new HashSet<>();
|
||||||
|
|
||||||
private final String sid;
|
protected final FullJid local;
|
||||||
|
|
||||||
public JingleSession(Jid initiator, Jid responder, String sid) {
|
protected final FullJid remote;
|
||||||
this.initiator = initiator;
|
|
||||||
this.responder = responder;
|
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.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
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hashCode = 31 + initiator.hashCode();
|
int hashCode = 31 + getInitiator().hashCode();
|
||||||
hashCode = 31 * hashCode + responder.hashCode();
|
hashCode = 31 * hashCode + getResponder().hashCode();
|
||||||
hashCode = 31 * hashCode + sid.hashCode();
|
hashCode = 31 * hashCode + getSessionId().hashCode();
|
||||||
return hashCode;
|
return hashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +128,113 @@ public class JingleSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
JingleSession otherJingleSession = (JingleSession) other;
|
JingleSession otherJingleSession = (JingleSession) other;
|
||||||
return initiator.equals(otherJingleSession.initiator) && responder.equals(otherJingleSession.responder)
|
return getInitiator().equals(otherJingleSession.getInitiator())
|
||||||
&& sid.equals(otherJingleSession.sid);
|
&& getResponder().equals(otherJingleSession.getResponder())
|
||||||
|
&& sid.equals(otherJingleSession.sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IQ handleJingleSessionRequest(Jingle jingle) {
|
||||||
|
try {
|
||||||
|
switch (jingle.getAction()) {
|
||||||
|
case content_accept:
|
||||||
|
return handleContentAccept(jingle);
|
||||||
|
case content_add:
|
||||||
|
return handleContentAdd(jingle);
|
||||||
|
case content_modify:
|
||||||
|
return handleContentModify(jingle);
|
||||||
|
case content_reject:
|
||||||
|
return handleContentReject(jingle);
|
||||||
|
case content_remove:
|
||||||
|
return handleContentRemove(jingle);
|
||||||
|
case description_info:
|
||||||
|
return handleDescriptionInfo(jingle);
|
||||||
|
case session_info:
|
||||||
|
return handleSessionInfo(jingle);
|
||||||
|
case security_info:
|
||||||
|
return handleSecurityInfo(jingle);
|
||||||
|
case session_accept:
|
||||||
|
return handleSessionAccept(jingle);
|
||||||
|
case transport_accept:
|
||||||
|
return handleTransportAccept(jingle);
|
||||||
|
case transport_info:
|
||||||
|
return transportSession.handleTransportInfo(jingle);
|
||||||
|
case session_initiate:
|
||||||
|
return handleSessionInitiate(jingle);
|
||||||
|
case transport_reject:
|
||||||
|
return handleTransportReject(jingle);
|
||||||
|
case session_terminate:
|
||||||
|
return handleSessionTerminate(jingle);
|
||||||
|
case transport_replace:
|
||||||
|
return handleTransportReplace(jingle);
|
||||||
|
default:
|
||||||
|
return IQ.createResultIQ(jingle);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
|
||||||
|
return null; //TODO:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleSessionInitiate(Jingle sessionInitiate) throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
|
return IQ.createResultIQ(sessionInitiate);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleSessionTerminate(Jingle sessionTerminate) {
|
||||||
|
return IQ.createResultIQ(sessionTerminate);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleSessionInfo(Jingle sessionInfo) {
|
||||||
|
return IQ.createResultIQ(sessionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleSessionAccept(Jingle sessionAccept) throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
return IQ.createResultIQ(sessionAccept);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleContentAdd(Jingle contentAdd) {
|
||||||
|
return IQ.createResultIQ(contentAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleContentAccept(Jingle contentAccept) {
|
||||||
|
return IQ.createResultIQ(contentAccept);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleContentModify(Jingle contentModify) {
|
||||||
|
return IQ.createResultIQ(contentModify);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleContentReject(Jingle contentReject) {
|
||||||
|
return IQ.createResultIQ(contentReject);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleContentRemove(Jingle contentRemove) {
|
||||||
|
return IQ.createResultIQ(contentRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleDescriptionInfo(Jingle descriptionInfo) {
|
||||||
|
return IQ.createResultIQ(descriptionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleSecurityInfo(Jingle securityInfo) {
|
||||||
|
return IQ.createResultIQ(securityInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleTransportAccept(Jingle transportAccept) throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
return IQ.createResultIQ(transportAccept);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleTransportReplace(Jingle transportReplace)
|
||||||
|
throws InterruptedException, XMPPException.XMPPErrorException,
|
||||||
|
SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
|
return IQ.createResultIQ(transportReplace);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IQ handleTransportReject(Jingle transportReject) {
|
||||||
|
return IQ.createResultIQ(transportReject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract XMPPConnection getConnection();
|
||||||
|
|
||||||
|
public abstract void onTransportMethodFailed(String namespace);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,6 @@ import org.jivesoftware.smackx.jingle.element.Jingle;
|
||||||
|
|
||||||
public interface JingleSessionHandler {
|
public interface JingleSessionHandler {
|
||||||
|
|
||||||
IQ handleJingleSessionRequest(Jingle jingle, String sessionId);
|
IQ handleJingleSessionRequest(Jingle jingle);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.Manager;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.Jingle;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.JingleTransportManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.jingle_ibb.element.JingleIBBTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager where TransportMethods are registered.
|
||||||
|
*/
|
||||||
|
public final class JingleTransportMethodManager extends Manager {
|
||||||
|
|
||||||
|
private static final WeakHashMap<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.getJingleTransport();
|
||||||
|
if (transport == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getTransportManager(transport.getNamespace());
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleTransportManager<?> getBestAvailableTransportManager(XMPPConnection connection) {
|
||||||
|
return getInstanceFor(connection).getBestAvailableTransportManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleTransportManager<?> getBestAvailableTransportManager() {
|
||||||
|
JingleTransportManager<?> tm;
|
||||||
|
for (String ns : transportPreference) {
|
||||||
|
tm = getTransportManager(ns);
|
||||||
|
if (tm != null) {
|
||||||
|
return tm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,508 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smack.packet.XMPPError;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.Jingle;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleAction;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleError;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleReason;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Util to quickly create and send jingle stanzas.
|
||||||
|
*/
|
||||||
|
public class JingleUtil {
|
||||||
|
|
||||||
|
private final XMPPConnection connection;
|
||||||
|
|
||||||
|
public JingleUtil(XMPPConnection connection) {
|
||||||
|
this.connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionInitiate(FullJid recipient,
|
||||||
|
String sessionId,
|
||||||
|
JingleContent.Creator contentCreator,
|
||||||
|
String contentName,
|
||||||
|
JingleContent.Senders contentSenders,
|
||||||
|
JingleContentDescription description,
|
||||||
|
JingleContentTransport transport) {
|
||||||
|
|
||||||
|
Jingle.Builder jb = Jingle.getBuilder();
|
||||||
|
jb.setAction(JingleAction.session_initiate)
|
||||||
|
.setSessionId(sessionId)
|
||||||
|
.setInitiator(connection.getUser());
|
||||||
|
|
||||||
|
JingleContent.Builder cb = JingleContent.getBuilder();
|
||||||
|
cb.setCreator(contentCreator)
|
||||||
|
.setName(contentName)
|
||||||
|
.setSenders(contentSenders)
|
||||||
|
.setDescription(description)
|
||||||
|
.setTransport(transport);
|
||||||
|
|
||||||
|
Jingle jingle = jb.addJingleContent(cb.build()).build();
|
||||||
|
jingle.setFrom(connection.getUser());
|
||||||
|
jingle.setTo(recipient);
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionInitiateFileOffer(FullJid recipient,
|
||||||
|
String sessionId,
|
||||||
|
JingleContent.Creator contentCreator,
|
||||||
|
String contentName,
|
||||||
|
JingleContentDescription description,
|
||||||
|
JingleContentTransport transport) {
|
||||||
|
return createSessionInitiate(recipient, sessionId, contentCreator, contentName,
|
||||||
|
JingleContent.Senders.initiator, description, transport);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendSessionInitiateFileOffer(FullJid recipient,
|
||||||
|
String sessionId,
|
||||||
|
JingleContent.Creator contentCreator,
|
||||||
|
String contentName,
|
||||||
|
JingleContentDescription description,
|
||||||
|
JingleContentTransport transport)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException,
|
||||||
|
XMPPException.XMPPErrorException, SmackException.NoResponseException {
|
||||||
|
|
||||||
|
Jingle jingle = createSessionInitiateFileOffer(recipient, sessionId, contentCreator, contentName, description, transport);
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendSessionInitiate(FullJid recipient,
|
||||||
|
String sessionId,
|
||||||
|
JingleContent.Creator contentCreator,
|
||||||
|
String contentName,
|
||||||
|
JingleContent.Senders contentSenders,
|
||||||
|
JingleContentDescription description,
|
||||||
|
JingleContentTransport transport)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException,
|
||||||
|
XMPPException.XMPPErrorException, SmackException.NoResponseException {
|
||||||
|
|
||||||
|
Jingle jingle = createSessionInitiate(recipient, sessionId, contentCreator, contentName, contentSenders,
|
||||||
|
description, transport);
|
||||||
|
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionAccept(FullJid recipient,
|
||||||
|
String sessionId,
|
||||||
|
JingleContent.Creator contentCreator,
|
||||||
|
String contentName,
|
||||||
|
JingleContent.Senders contentSenders,
|
||||||
|
JingleContentDescription description,
|
||||||
|
JingleContentTransport transport) {
|
||||||
|
|
||||||
|
Jingle.Builder jb = Jingle.getBuilder();
|
||||||
|
jb.setResponder(connection.getUser())
|
||||||
|
.setAction(JingleAction.session_accept)
|
||||||
|
.setSessionId(sessionId);
|
||||||
|
|
||||||
|
JingleContent.Builder cb = JingleContent.getBuilder();
|
||||||
|
cb.setCreator(contentCreator)
|
||||||
|
.setName(contentName)
|
||||||
|
.setSenders(contentSenders)
|
||||||
|
.setDescription(description)
|
||||||
|
.setTransport(transport);
|
||||||
|
|
||||||
|
Jingle jingle = jb.addJingleContent(cb.build()).build();
|
||||||
|
jingle.setTo(recipient);
|
||||||
|
jingle.setFrom(connection.getUser());
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendSessionAccept(FullJid recipient,
|
||||||
|
String sessionId,
|
||||||
|
JingleContent.Creator contentCreator,
|
||||||
|
String contentName,
|
||||||
|
JingleContent.Senders contentSenders,
|
||||||
|
JingleContentDescription description,
|
||||||
|
JingleContentTransport transport)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException,
|
||||||
|
XMPPException.XMPPErrorException, SmackException.NoResponseException {
|
||||||
|
|
||||||
|
Jingle jingle = createSessionAccept(recipient, sessionId, contentCreator, contentName, contentSenders,
|
||||||
|
description, transport);
|
||||||
|
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionTerminate(FullJid recipient, String sessionId, JingleReason reason) {
|
||||||
|
Jingle.Builder jb = Jingle.getBuilder();
|
||||||
|
jb.setAction(JingleAction.session_terminate)
|
||||||
|
.setSessionId(sessionId)
|
||||||
|
.setReason(reason);
|
||||||
|
|
||||||
|
Jingle jingle = jb.build();
|
||||||
|
jingle.setFrom(connection.getUser());
|
||||||
|
jingle.setTo(recipient);
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionTerminate(FullJid recipient, String sessionId, JingleReason.Reason reason) {
|
||||||
|
return createSessionTerminate(recipient, sessionId, new JingleReason(reason));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQ sendSessionTerminate(FullJid recipient, String sessionId, JingleReason.Reason reason)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException,
|
||||||
|
XMPPException.XMPPErrorException, SmackException.NoResponseException {
|
||||||
|
|
||||||
|
return sendSessionTerminate(recipient, sessionId, new JingleReason(reason));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQ sendSessionTerminate(FullJid recipient, String sessionId, JingleReason reason)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException,
|
||||||
|
XMPPException.XMPPErrorException, SmackException.NoResponseException {
|
||||||
|
|
||||||
|
Jingle jingle = createSessionTerminate(recipient, sessionId, reason);
|
||||||
|
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionTerminateDecline(FullJid recipient, String sessionId) {
|
||||||
|
return createSessionTerminate(recipient, sessionId, JingleReason.Reason.decline);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendSessionTerminateDecline(FullJid recipient, String sessionId)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException,
|
||||||
|
XMPPException.XMPPErrorException, SmackException.NoResponseException {
|
||||||
|
|
||||||
|
Jingle jingle = createSessionTerminateDecline(recipient, sessionId);
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionTerminateSuccess(FullJid recipient, String sessionId) {
|
||||||
|
return createSessionTerminate(recipient, sessionId, JingleReason.Reason.success);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendSessionTerminateSuccess(FullJid recipient, String sessionId)
|
||||||
|
throws InterruptedException, XMPPException.XMPPErrorException,
|
||||||
|
SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
|
|
||||||
|
Jingle jingle = createSessionTerminateSuccess(recipient, sessionId);
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionTerminateBusy(FullJid recipient, String sessionId) {
|
||||||
|
return createSessionTerminate(recipient, sessionId, JingleReason.Reason.busy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendSessionTerminateBusy(FullJid recipient, String sessionId)
|
||||||
|
throws InterruptedException, XMPPException.XMPPErrorException,
|
||||||
|
SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
|
|
||||||
|
Jingle jingle = createSessionTerminateBusy(recipient, sessionId);
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionTerminateAlternativeSession(FullJid recipient, String sessionId, String altSessionId) {
|
||||||
|
return createSessionTerminate(recipient, sessionId, JingleReason.AlternativeSession(altSessionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendSessionTerminateAlternativeSession(FullJid recipient, String sessionId, String altSessionId)
|
||||||
|
throws InterruptedException, XMPPException.XMPPErrorException,
|
||||||
|
SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
|
|
||||||
|
Jingle jingle = createSessionTerminateAlternativeSession(recipient, sessionId, altSessionId);
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionTerminateCancel(FullJid recipient, String sessionId) {
|
||||||
|
return createSessionTerminate(recipient, sessionId, JingleReason.Reason.cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendSessionTerminateCancel(FullJid recipient,
|
||||||
|
String sessionId)
|
||||||
|
throws InterruptedException, XMPPException.XMPPErrorException,
|
||||||
|
SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
|
|
||||||
|
Jingle jingle = createSessionTerminateCancel(recipient, sessionId);
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionTerminateContentCancel(FullJid recipient, String sessionId,
|
||||||
|
JingleContent.Creator contentCreator, String contentName) {
|
||||||
|
Jingle.Builder jb = Jingle.getBuilder();
|
||||||
|
jb.setAction(JingleAction.session_terminate)
|
||||||
|
.setSessionId(sessionId);
|
||||||
|
|
||||||
|
JingleContent.Builder cb = JingleContent.getBuilder();
|
||||||
|
cb.setCreator(contentCreator).setName(contentName);
|
||||||
|
|
||||||
|
Jingle jingle = jb.addJingleContent(cb.build()).build();
|
||||||
|
jingle.setFrom(connection.getUser());
|
||||||
|
jingle.setTo(recipient);
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendSessionTerminateContentCancel(FullJid recipient, String sessionId,
|
||||||
|
JingleContent.Creator contentCreator, String contentName)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException,
|
||||||
|
XMPPException.XMPPErrorException, SmackException.NoResponseException {
|
||||||
|
Jingle jingle = createSessionTerminateContentCancel(recipient, sessionId, contentCreator, contentName);
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionTerminateUnsupportedTransports(FullJid recipient, String sessionId) {
|
||||||
|
return createSessionTerminate(recipient, sessionId, JingleReason.Reason.unsupported_transports);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendSessionTerminateUnsupportedTransports(FullJid recipient, String sessionId)
|
||||||
|
throws InterruptedException, XMPPException.XMPPErrorException,
|
||||||
|
SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
|
Jingle jingle = createSessionTerminateUnsupportedTransports(recipient, sessionId);
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionTerminateFailedTransport(FullJid recipient, String sessionId) {
|
||||||
|
return createSessionTerminate(recipient, sessionId, JingleReason.Reason.failed_transport);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendSessionTerminateFailedTransport(FullJid recipient, String sessionId)
|
||||||
|
throws InterruptedException, XMPPException.XMPPErrorException,
|
||||||
|
SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
|
Jingle jingle = createSessionTerminateFailedTransport(recipient, sessionId);
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionTerminateUnsupportedApplications(FullJid recipient, String sessionId) {
|
||||||
|
return createSessionTerminate(recipient, sessionId, JingleReason.Reason.unsupported_applications);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendSessionTerminateUnsupportedApplications(FullJid recipient, String sessionId)
|
||||||
|
throws InterruptedException, XMPPException.XMPPErrorException,
|
||||||
|
SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
|
Jingle jingle = createSessionTerminateUnsupportedApplications(recipient, sessionId);
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionTerminateFailedApplication(FullJid recipient, String sessionId) {
|
||||||
|
return createSessionTerminate(recipient, sessionId, JingleReason.Reason.failed_application);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendSessionTerminateFailedApplication(FullJid recipient, String sessionId)
|
||||||
|
throws InterruptedException, XMPPException.XMPPErrorException,
|
||||||
|
SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
|
Jingle jingle = createSessionTerminateFailedApplication(recipient, sessionId);
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionTerminateIncompatibleParameters(FullJid recipient, String sessionId) {
|
||||||
|
return createSessionTerminate(recipient, sessionId, JingleReason.Reason.incompatible_parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendSessionTerminateIncompatibleParameters(FullJid recipient, String sessionId)
|
||||||
|
throws InterruptedException, XMPPException.XMPPErrorException,
|
||||||
|
SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
|
Jingle jingle = createSessionTerminateIncompatibleParameters(recipient, sessionId);
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendContentRejectFileNotAvailable(FullJid recipient, String sessionId, JingleContentDescription description) {
|
||||||
|
return null; //TODO Later
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createSessionPing(FullJid recipient, String sessionId) {
|
||||||
|
Jingle.Builder jb = Jingle.getBuilder();
|
||||||
|
jb.setSessionId(sessionId)
|
||||||
|
.setAction(JingleAction.session_info);
|
||||||
|
|
||||||
|
Jingle jingle = jb.build();
|
||||||
|
jingle.setFrom(connection.getUser());
|
||||||
|
jingle.setTo(recipient);
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendSessionPing(FullJid recipient, String sessionId)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException,
|
||||||
|
XMPPException.XMPPErrorException, SmackException.NoResponseException {
|
||||||
|
Jingle jingle = createSessionPing(recipient, sessionId);
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ createAck(Jingle jingle) {
|
||||||
|
return IQ.createResultIQ(jingle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendAck(Jingle jingle) throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
connection.sendStanza(createAck(jingle));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createTransportReplace(FullJid recipient, FullJid initiator, String sessionId,
|
||||||
|
JingleContent.Creator contentCreator, String contentName,
|
||||||
|
JingleContentTransport transport) {
|
||||||
|
Jingle.Builder jb = Jingle.getBuilder();
|
||||||
|
jb.setInitiator(initiator)
|
||||||
|
.setSessionId(sessionId)
|
||||||
|
.setAction(JingleAction.transport_replace);
|
||||||
|
|
||||||
|
JingleContent.Builder cb = JingleContent.getBuilder();
|
||||||
|
cb.setName(contentName).setCreator(contentCreator).setTransport(transport);
|
||||||
|
Jingle jingle = jb.addJingleContent(cb.build()).build();
|
||||||
|
|
||||||
|
jingle.setTo(recipient);
|
||||||
|
jingle.setFrom(connection.getUser());
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendTransportReplace(FullJid recipient, FullJid initiator, String sessionId,
|
||||||
|
JingleContent.Creator contentCreator, String contentName,
|
||||||
|
JingleContentTransport transport)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException,
|
||||||
|
XMPPException.XMPPErrorException, SmackException.NoResponseException {
|
||||||
|
Jingle jingle = createTransportReplace(recipient, initiator, sessionId, contentCreator, contentName, transport);
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createTransportAccept(FullJid recipient, FullJid initiator, String sessionId,
|
||||||
|
JingleContent.Creator contentCreator, String contentName,
|
||||||
|
JingleContentTransport transport) {
|
||||||
|
Jingle.Builder jb = Jingle.getBuilder();
|
||||||
|
jb.setAction(JingleAction.transport_accept)
|
||||||
|
.setInitiator(initiator)
|
||||||
|
.setSessionId(sessionId);
|
||||||
|
|
||||||
|
JingleContent.Builder cb = JingleContent.getBuilder();
|
||||||
|
cb.setCreator(contentCreator).setName(contentName).setTransport(transport);
|
||||||
|
|
||||||
|
Jingle jingle = jb.addJingleContent(cb.build()).build();
|
||||||
|
jingle.setTo(recipient);
|
||||||
|
jingle.setFrom(connection.getUser());
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendTransportAccept(FullJid recipient, FullJid initiator, String sessionId,
|
||||||
|
JingleContent.Creator contentCreator, String contentName,
|
||||||
|
JingleContentTransport transport)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException,
|
||||||
|
XMPPException.XMPPErrorException, SmackException.NoResponseException {
|
||||||
|
Jingle jingle = createTransportAccept(recipient, initiator, sessionId, contentCreator, contentName, transport);
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jingle createTransportReject(FullJid recipient, FullJid initiator, String sessionId,
|
||||||
|
JingleContent.Creator contentCreator, String contentName,
|
||||||
|
JingleContentTransport transport) {
|
||||||
|
Jingle.Builder jb = Jingle.getBuilder();
|
||||||
|
jb.setAction(JingleAction.transport_reject)
|
||||||
|
.setInitiator(initiator)
|
||||||
|
.setSessionId(sessionId);
|
||||||
|
|
||||||
|
JingleContent.Builder cb = JingleContent.getBuilder();
|
||||||
|
cb.setCreator(contentCreator).setName(contentName).setTransport(transport);
|
||||||
|
|
||||||
|
Jingle jingle = jb.addJingleContent(cb.build()).build();
|
||||||
|
jingle.setTo(recipient);
|
||||||
|
jingle.setFrom(connection.getUser());
|
||||||
|
|
||||||
|
return jingle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ sendTransportReject(FullJid recipient, FullJid initiator, String sessionId,
|
||||||
|
JingleContent.Creator contentCreator, String contentName,
|
||||||
|
JingleContentTransport transport)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException,
|
||||||
|
XMPPException.XMPPErrorException, SmackException.NoResponseException {
|
||||||
|
Jingle jingle = createTransportReject(recipient, initiator, sessionId, contentCreator, contentName, transport);
|
||||||
|
return connection.createStanzaCollectorAndSend(jingle).nextResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ####################################################################################################
|
||||||
|
*/
|
||||||
|
|
||||||
|
public IQ createErrorUnknownSession(Jingle request) {
|
||||||
|
XMPPError.Builder error = XMPPError.getBuilder();
|
||||||
|
error.setCondition(XMPPError.Condition.item_not_found)
|
||||||
|
.addExtension(JingleError.UNKNOWN_SESSION);
|
||||||
|
return IQ.createErrorResponse(request, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendErrorUnknownSession(Jingle request)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
connection.sendStanza(createErrorUnknownSession(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ createErrorUnknownInitiator(Jingle request) {
|
||||||
|
return IQ.createErrorResponse(request, XMPPError.Condition.service_unavailable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendErrorUnknownInitiator(Jingle request)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
connection.sendStanza(createErrorUnknownInitiator(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ createErrorUnsupportedInfo(Jingle request) {
|
||||||
|
XMPPError.Builder error = XMPPError.getBuilder();
|
||||||
|
error.setCondition(XMPPError.Condition.feature_not_implemented)
|
||||||
|
.addExtension(JingleError.UNSUPPORTED_INFO);
|
||||||
|
return IQ.createErrorResponse(request, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendErrorUnsupportedInfo(Jingle request)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
connection.sendStanza(createErrorUnsupportedInfo(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ createErrorTieBreak(Jingle request) {
|
||||||
|
XMPPError.Builder error = XMPPError.getBuilder();
|
||||||
|
error.setCondition(XMPPError.Condition.conflict)
|
||||||
|
.addExtension(JingleError.TIE_BREAK);
|
||||||
|
return IQ.createErrorResponse(request, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendErrorTieBreak(Jingle request)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
connection.sendStanza(createErrorTieBreak(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ createErrorOutOfOrder(Jingle request) {
|
||||||
|
XMPPError.Builder error = XMPPError.getBuilder();
|
||||||
|
error.setCondition(XMPPError.Condition.unexpected_request)
|
||||||
|
.addExtension(JingleError.OUT_OF_ORDER);
|
||||||
|
return IQ.createErrorResponse(request, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendErrorOutOfOrder(Jingle request)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
connection.sendStanza(createErrorOutOfOrder(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ createErrorMalformedRequest(Jingle request) {
|
||||||
|
return IQ.createErrorResponse(request, XMPPError.Condition.bad_request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendErrorMalformedRequest(Jingle request)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
connection.sendStanza(createErrorMalformedRequest(request));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle;
|
||||||
|
|
||||||
|
public enum Role {
|
||||||
|
initiator,
|
||||||
|
responder,
|
||||||
|
;
|
||||||
|
}
|
|
@ -122,6 +122,10 @@ public final class Jingle extends IQ {
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JingleReason getReason() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a List of the contents.
|
* Get a List of the contents.
|
||||||
*
|
*
|
||||||
|
|
|
@ -104,7 +104,7 @@ public class JingleReason implements NamedElement {
|
||||||
|
|
||||||
protected final Reason reason;
|
protected final Reason reason;
|
||||||
|
|
||||||
protected JingleReason(Reason reason) {
|
public JingleReason(Reason reason) {
|
||||||
this.reason = reason;
|
this.reason = reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +124,10 @@ public class JingleReason implements NamedElement {
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Reason asEnum() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class AlternativeSession extends JingleReason {
|
public static class AlternativeSession extends JingleReason {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle.transports;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for bytestream session creation of TransportManagers.
|
||||||
|
*/
|
||||||
|
public interface JingleTransportInitiationCallback {
|
||||||
|
|
||||||
|
void onSessionInitiated(BytestreamSession bytestreamSession);
|
||||||
|
|
||||||
|
void onException(Exception e);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle.transports;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by vanitas on 25.06.17.
|
||||||
|
*/
|
||||||
|
public abstract class JingleTransportInitiationException extends Exception {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|
||||||
|
public static class ProxyError extends JingleTransportInitiationException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CandidateError extends JingleTransportInitiationException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle.transports;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.ConnectionListener;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager for a JingleTransport method.
|
||||||
|
* @param <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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle.transports;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.Jingle;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by vanitas on 20.06.17.
|
||||||
|
*/
|
||||||
|
public abstract class JingleTransportSession<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.getJingleTransport();
|
||||||
|
|
||||||
|
if (t != null && t.getNamespace().equals(getNamespace())) {
|
||||||
|
setTheirProposal(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void setTheirProposal(JingleContentTransport transport);
|
||||||
|
|
||||||
|
public abstract void initiateOutgoingSession(JingleTransportInitiationCallback callback);
|
||||||
|
|
||||||
|
public abstract void initiateIncomingSession(JingleTransportInitiationCallback callback);
|
||||||
|
|
||||||
|
public abstract String getNamespace();
|
||||||
|
|
||||||
|
public abstract IQ handleTransportInfo(Jingle transportInfo);
|
||||||
|
|
||||||
|
public abstract JingleTransportManager<T> transportManager();
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle.transports.jingle_ibb;
|
||||||
|
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.provider.JingleContentProviderManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.JingleTransportManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.JingleTransportSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.jingle_ibb.element.JingleIBBTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.jingle_ibb.provider.JingleIBBTransportProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager for Jingle InBandBytestream transports (XEP-0261).
|
||||||
|
*/
|
||||||
|
public final class JingleIBBTransportManager extends JingleTransportManager<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.
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle.transports.jingle_ibb;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.BytestreamListener;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.BytestreamRequest;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.Jingle;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.JingleTransportInitiationCallback;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.JingleTransportManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.JingleTransportSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.jingle_ibb.element.JingleIBBTransport;
|
||||||
|
|
||||||
|
public class JingleIBBTransportSession extends JingleTransportSession<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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,234 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle.transports.jingle_s5b;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.Jingle;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleAction;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
||||||
|
import org.jivesoftware.smackx.jingle.provider.JingleContentProviderManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.JingleTransportManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.JingleTransportSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.provider.JingleS5BTransportProvider;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.FullJid;
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager for Jingle SOCKS5 Bytestream transports (XEP-0261).
|
||||||
|
*/
|
||||||
|
public final class JingleS5BTransportManager extends JingleTransportManager<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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,361 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle.transports.jingle_s5b;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Client;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5ClientForInitiator;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Utils;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.Jingle;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransportCandidate;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.JingleTransportInitiationCallback;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.JingleTransportSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportCandidate;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransportInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler that handles Jingle Socks5Bytestream transports (XEP-0260).
|
||||||
|
*/
|
||||||
|
public class JingleS5BTransportSession extends JingleTransportSession<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).getJingleTransport().getInfo();
|
||||||
|
|
||||||
|
switch (info.getElementName()) {
|
||||||
|
case JingleS5BTransportInfo.CandidateUsed.ELEMENT:
|
||||||
|
return handleCandidateUsed(transportInfo);
|
||||||
|
|
||||||
|
case JingleS5BTransportInfo.CandidateActivated.ELEMENT:
|
||||||
|
return handleCandidateActivate(transportInfo);
|
||||||
|
|
||||||
|
case JingleS5BTransportInfo.CandidateError.ELEMENT:
|
||||||
|
return handleCandidateError(transportInfo);
|
||||||
|
|
||||||
|
case JingleS5BTransportInfo.ProxyError.ELEMENT:
|
||||||
|
return handleProxyError(transportInfo);
|
||||||
|
}
|
||||||
|
//We should never go here, but lets be gracious...
|
||||||
|
return IQ.createResultIQ(transportInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ handleCandidateUsed(Jingle jingle) {
|
||||||
|
JingleS5BTransportInfo info = (JingleS5BTransportInfo) jingle.getContents().get(0).getJingleTransport().getInfo();
|
||||||
|
String candidateId = ((JingleS5BTransportInfo.CandidateUsed) info).getCandidateId();
|
||||||
|
theirChoice = new UsedCandidate(ourProposal, ourProposal.getCandidate(candidateId), null);
|
||||||
|
|
||||||
|
if (theirChoice.candidate == null) {
|
||||||
|
/*
|
||||||
|
TODO: Booooooh illegal candidateId!! Go home!!!!11elf
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
connectIfReady();
|
||||||
|
|
||||||
|
return IQ.createResultIQ(jingle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ handleCandidateActivate(Jingle jingle) {
|
||||||
|
LOGGER.log(Level.INFO, "handleCandidateActivate");
|
||||||
|
Socks5BytestreamSession bs = new Socks5BytestreamSession(ourChoice.socket,
|
||||||
|
ourChoice.candidate.getJid().asBareJid().equals(jingleSession.getRemote().asBareJid()));
|
||||||
|
callback.onSessionInitiated(bs);
|
||||||
|
return IQ.createResultIQ(jingle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ handleCandidateError(Jingle jingle) {
|
||||||
|
theirChoice = CANDIDATE_FAILURE;
|
||||||
|
connectIfReady();
|
||||||
|
return IQ.createResultIQ(jingle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ handleProxyError(Jingle jingle) {
|
||||||
|
//TODO
|
||||||
|
return IQ.createResultIQ(jingle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine, which candidate (ours/theirs) is the nominated one.
|
||||||
|
* Connect to this candidate. If it is a proxy and it is ours, activate it and connect.
|
||||||
|
* If its a proxy and it is theirs, wait for activation.
|
||||||
|
* If it is not a proxy, just connect.
|
||||||
|
*/
|
||||||
|
private void connectIfReady() {
|
||||||
|
JingleContent content = jingleSession.getContents().get(0);
|
||||||
|
if (ourChoice == null || theirChoice == null) {
|
||||||
|
// Not yet ready.
|
||||||
|
LOGGER.log(Level.INFO, "Not ready.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ourChoice == CANDIDATE_FAILURE && theirChoice == CANDIDATE_FAILURE) {
|
||||||
|
LOGGER.log(Level.INFO, "Failure.");
|
||||||
|
jingleSession.onTransportMethodFailed(getNamespace());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.log(Level.INFO, "Ready.");
|
||||||
|
|
||||||
|
//Determine nominated candidate.
|
||||||
|
UsedCandidate nominated;
|
||||||
|
if (ourChoice != CANDIDATE_FAILURE && theirChoice != CANDIDATE_FAILURE) {
|
||||||
|
if (ourChoice.candidate.getPriority() > theirChoice.candidate.getPriority()) {
|
||||||
|
nominated = ourChoice;
|
||||||
|
} else if (ourChoice.candidate.getPriority() < theirChoice.candidate.getPriority()) {
|
||||||
|
nominated = theirChoice;
|
||||||
|
} else {
|
||||||
|
nominated = jingleSession.isInitiator() ? ourChoice : theirChoice;
|
||||||
|
}
|
||||||
|
} else if (ourChoice != CANDIDATE_FAILURE) {
|
||||||
|
nominated = ourChoice;
|
||||||
|
} else {
|
||||||
|
nominated = theirChoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nominated == theirChoice) {
|
||||||
|
LOGGER.log(Level.INFO, "Their choice, so our proposed candidate is used.");
|
||||||
|
boolean isProxy = nominated.candidate.getType() == JingleS5BTransportCandidate.Type.proxy;
|
||||||
|
try {
|
||||||
|
nominated = connectToOurCandidate(nominated.candidate);
|
||||||
|
} catch (InterruptedException | IOException | XMPPException | SmackException | TimeoutException e) {
|
||||||
|
LOGGER.log(Level.INFO, "Could not connect to our candidate.", e);
|
||||||
|
//TODO: Proxy-Error
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isProxy) {
|
||||||
|
LOGGER.log(Level.INFO, "Is external proxy. Activate it.");
|
||||||
|
Bytestream activate = new Bytestream(ourProposal.getStreamId());
|
||||||
|
activate.setMode(null);
|
||||||
|
activate.setType(IQ.Type.set);
|
||||||
|
activate.setTo(nominated.candidate.getJid());
|
||||||
|
activate.setToActivate(jingleSession.getRemote());
|
||||||
|
activate.setFrom(jingleSession.getLocal());
|
||||||
|
try {
|
||||||
|
jingleSession.getConnection().createStanzaCollectorAndSend(activate).nextResultOrThrow();
|
||||||
|
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not activate proxy.", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.log(Level.INFO, "Send candidate-activate.");
|
||||||
|
Jingle candidateActivate = transportManager().createCandidateActivated(
|
||||||
|
jingleSession.getRemote(), jingleSession.getInitiator(), jingleSession.getSessionId(),
|
||||||
|
content.getSenders(), content.getCreator(), content.getName(), nominated.transport.getStreamId(),
|
||||||
|
nominated.candidate.getCandidateId());
|
||||||
|
try {
|
||||||
|
jingleSession.getConnection().createStanzaCollectorAndSend(candidateActivate)
|
||||||
|
.nextResultOrThrow();
|
||||||
|
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not send candidate-activated", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.log(Level.INFO, "Start transmission.");
|
||||||
|
Socks5BytestreamSession bs = new Socks5BytestreamSession(nominated.socket, !isProxy);
|
||||||
|
callback.onSessionInitiated(bs);
|
||||||
|
|
||||||
|
}
|
||||||
|
//Our choice
|
||||||
|
else {
|
||||||
|
LOGGER.log(Level.INFO, "Our choice, so their candidate was used.");
|
||||||
|
boolean isProxy = nominated.candidate.getType() == JingleS5BTransportCandidate.Type.proxy;
|
||||||
|
if (!isProxy) {
|
||||||
|
LOGGER.log(Level.INFO, "Direct connection.");
|
||||||
|
Socks5BytestreamSession bs = new Socks5BytestreamSession(nominated.socket, true);
|
||||||
|
callback.onSessionInitiated(bs);
|
||||||
|
} else {
|
||||||
|
LOGGER.log(Level.INFO, "Our choice was their external proxy. wait for candidate-activate.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleS5BTransportManager transportManager() {
|
||||||
|
return JingleS5BTransportManager.getInstanceFor(jingleSession.getConnection());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class UsedCandidate {
|
||||||
|
private final Socket socket;
|
||||||
|
private final JingleS5BTransport transport;
|
||||||
|
private final JingleS5BTransportCandidate candidate;
|
||||||
|
|
||||||
|
public UsedCandidate(JingleS5BTransport transport, JingleS5BTransportCandidate candidate, Socket socket) {
|
||||||
|
this.socket = socket;
|
||||||
|
this.transport = transport;
|
||||||
|
this.candidate = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final UsedCandidate CANDIDATE_FAILURE = new UsedCandidate(null, null, null);
|
||||||
|
}
|
|
@ -93,7 +93,7 @@ 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 ArrayList<JingleContentTransportCandidate> candidates = new ArrayList<>();
|
private final ArrayList<JingleContentTransportCandidate> candidates = new ArrayList<>();
|
||||||
private JingleContentTransportInfo info;
|
private JingleContentTransportInfo info;
|
||||||
|
|
||||||
public Builder setStreamId(String sid) {
|
public Builder setStreamId(String sid) {
|
||||||
|
@ -112,11 +112,22 @@ public class JingleS5BTransport extends JingleContentTransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder addTransportCandidate(JingleS5BTransportCandidate candidate) {
|
public Builder addTransportCandidate(JingleS5BTransportCandidate candidate) {
|
||||||
|
if (info != null) {
|
||||||
|
throw new IllegalStateException("Builder has already an info set. " +
|
||||||
|
"The transport can only have either an info or transport candidates, not both.");
|
||||||
|
}
|
||||||
this.candidates.add(candidate);
|
this.candidates.add(candidate);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setTransportInfo(JingleContentTransportInfo info) {
|
public Builder setTransportInfo(JingleContentTransportInfo info) {
|
||||||
|
if (!candidates.isEmpty()) {
|
||||||
|
throw new IllegalStateException("Builder has already at least one candidate set. " +
|
||||||
|
"The transport can only have either an info or transport candidates, not both.");
|
||||||
|
}
|
||||||
|
if (this.info != null) {
|
||||||
|
throw new IllegalStateException("Builder has already an info set.");
|
||||||
|
}
|
||||||
this.info = info;
|
this.info = info;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,8 +54,12 @@ public final class JingleS5BTransportCandidate extends JingleContentTransportCan
|
||||||
Objects.requireNonNull(candidateId);
|
Objects.requireNonNull(candidateId);
|
||||||
Objects.requireNonNull(host);
|
Objects.requireNonNull(host);
|
||||||
Objects.requireNonNull(jid);
|
Objects.requireNonNull(jid);
|
||||||
|
|
||||||
if (priority < 0) {
|
if (priority < 0) {
|
||||||
throw new IllegalArgumentException("Priority MUST be present and NOT less than 0.");
|
throw new IllegalArgumentException("Priority MUST NOT be less than 0.");
|
||||||
|
}
|
||||||
|
if (port < 0) {
|
||||||
|
throw new IllegalArgumentException("Port MUST NOT be less than 0.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cid = candidateId;
|
this.cid = candidateId;
|
||||||
|
@ -66,8 +70,8 @@ public final class JingleS5BTransportCandidate extends JingleContentTransportCan
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleS5BTransportCandidate(Bytestream.StreamHost streamHost, int priority) {
|
public JingleS5BTransportCandidate(Bytestream.StreamHost streamHost, int priority, Type type) {
|
||||||
this(StringUtils.randomString(24), streamHost.getAddress(), streamHost.getJID(), streamHost.getPort(), priority, Type.proxy);
|
this(StringUtils.randomString(24), streamHost.getAddress(), streamHost.getJID(), streamHost.getPort(), priority, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smack's API for <a href="https://xmpp.org/extensions/xep-0261.html">XEP-0261: Jingle In-Band Bytestreams</a>.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle.transports;
|
|
@ -102,10 +102,15 @@ public class Socks5ProxyTest {
|
||||||
@Test
|
@Test
|
||||||
public void shouldPreserveAddressOrderOnInsertions() {
|
public void shouldPreserveAddressOrderOnInsertions() {
|
||||||
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
|
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
|
||||||
List<String> addresses = new ArrayList<String>(proxy.getLocalAddresses());
|
List<String> addresses = new ArrayList<>(proxy.getLocalAddresses());
|
||||||
addresses.add("1");
|
|
||||||
addresses.add("2");
|
for (int i = 1 ; i <= 3; i++) {
|
||||||
addresses.add("3");
|
String addr = Integer.toString(i);
|
||||||
|
if (!addresses.contains(addr)) {
|
||||||
|
addresses.add(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (String address : addresses) {
|
for (String address : addresses) {
|
||||||
proxy.addLocalAddress(address);
|
proxy.addLocalAddress(address);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle;
|
||||||
|
|
||||||
|
import static junit.framework.TestCase.assertEquals;
|
||||||
|
import static junit.framework.TestCase.assertNull;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
|
import org.jivesoftware.smackx.jingle.provider.JingleContentProviderManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.jingle_ibb.element.JingleIBBTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.jingle_ibb.provider.JingleIBBTransportProvider;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.provider.JingleS5BTransportProvider;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for the JingleContentProviderManager.
|
||||||
|
*/
|
||||||
|
public class JingleContentProviderManagerTest extends SmackTestSuite {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void transportProviderTest() {
|
||||||
|
assertNull(JingleContentProviderManager.getJingleContentTransportProvider(JingleIBBTransport.NAMESPACE_V1));
|
||||||
|
assertNull(JingleContentProviderManager.getJingleContentTransportProvider(JingleS5BTransport.NAMESPACE_V1));
|
||||||
|
|
||||||
|
JingleIBBTransportProvider ibbProvider = new JingleIBBTransportProvider();
|
||||||
|
JingleContentProviderManager.addJingleContentTransportProvider(JingleIBBTransport.NAMESPACE_V1, ibbProvider);
|
||||||
|
assertEquals(ibbProvider, JingleContentProviderManager.getJingleContentTransportProvider(JingleIBBTransport.NAMESPACE_V1));
|
||||||
|
|
||||||
|
assertNull(JingleContentProviderManager.getJingleContentTransportProvider(JingleS5BTransport.NAMESPACE_V1));
|
||||||
|
JingleS5BTransportProvider s5bProvider = new JingleS5BTransportProvider();
|
||||||
|
JingleContentProviderManager.addJingleContentTransportProvider(JingleS5BTransport.NAMESPACE_V1, s5bProvider);
|
||||||
|
assertEquals(s5bProvider, JingleContentProviderManager.getJingleContentTransportProvider(JingleS5BTransport.NAMESPACE_V1));
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,6 @@ import static junit.framework.TestCase.assertNotSame;
|
||||||
import static junit.framework.TestCase.assertNull;
|
import static junit.framework.TestCase.assertNull;
|
||||||
|
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -46,7 +45,7 @@ public class JingleContentTest extends SmackTestSuite {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parserTest() {
|
public void parserTest() throws Exception {
|
||||||
|
|
||||||
JingleContent.Builder builder = JingleContent.getBuilder();
|
JingleContent.Builder builder = JingleContent.getBuilder();
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,9 @@ package org.jivesoftware.smackx.jingle;
|
||||||
import static junit.framework.TestCase.assertEquals;
|
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.smackx.jingle.element.JingleError;
|
import org.jivesoftware.smackx.jingle.element.JingleError;
|
||||||
|
import org.jivesoftware.smackx.jingle.provider.JingleErrorProvider;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -30,20 +31,37 @@ import org.junit.Test;
|
||||||
public class JingleErrorTest extends SmackTestSuite {
|
public class JingleErrorTest extends SmackTestSuite {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parserTest() {
|
public void tieBreakTest() throws Exception {
|
||||||
assertEquals("<out-of-order xmlns='urn:xmpp:jingle:errors:1'/>",
|
String xml = "<tie-break xmlns='urn:xmpp:jingle:errors:1'/>";
|
||||||
JingleError.fromString("out-of-order").toXML().toString());
|
JingleError error = new JingleErrorProvider().parse(TestUtils.getParser(xml));
|
||||||
assertEquals("<tie-break xmlns='urn:xmpp:jingle:errors:1'/>",
|
assertEquals(xml, error.toXML().toString());
|
||||||
JingleError.fromString("tie-break").toXML().toString());
|
}
|
||||||
assertEquals("<unknown-session xmlns='urn:xmpp:jingle:errors:1'/>",
|
|
||||||
JingleError.fromString("unknown-session").toXML().toString());
|
@Test
|
||||||
assertEquals("<unsupported-info xmlns='urn:xmpp:jingle:errors:1'/>",
|
public void unknownSessionTest() throws Exception {
|
||||||
JingleError.fromString("unsupported-info").toXML().toString());
|
String xml = "<unknown-session xmlns='urn:xmpp:jingle:errors:1'/>";
|
||||||
assertEquals("unknown-session", JingleError.fromString("unknown-session").getMessage());
|
JingleError error = new JingleErrorProvider().parse(TestUtils.getParser(xml));
|
||||||
|
assertEquals(xml, error.toXML().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void unsupportedInfoTest() throws Exception {
|
||||||
|
String xml = "<unsupported-info xmlns='urn:xmpp:jingle:errors:1'/>";
|
||||||
|
JingleError error = new JingleErrorProvider().parse(TestUtils.getParser(xml));
|
||||||
|
assertEquals(xml, error.toXML().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void outOfOrderTest() throws Exception {
|
||||||
|
String xml = "<out-of-order xmlns='urn:xmpp:jingle:errors:1'/>";
|
||||||
|
JingleError error = new JingleErrorProvider().parse(TestUtils.getParser(xml));
|
||||||
|
assertEquals(xml, error.toXML().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void illegalArgumentTest() {
|
public void illegalArgumentTest() {
|
||||||
JingleError.fromString("inexistent-error");
|
JingleError.fromString("inexistent-error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright 2017 Paul Schaub
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.jivesoftware.smackx.jingle;
|
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
|
||||||
import static junit.framework.TestCase.assertNotSame;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.jxmpp.jid.Jid;
|
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
|
||||||
import org.jxmpp.stringprep.XmppStringprepException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test JingleSession class.
|
|
||||||
*/
|
|
||||||
public class JingleSessionTest extends SmackTestSuite {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void sessionTest() throws XmppStringprepException {
|
|
||||||
Jid romeo = JidCreate.from("romeo@montague.lit");
|
|
||||||
Jid juliet = JidCreate.from("juliet@capulet.lit");
|
|
||||||
String sid = StringUtils.randomString(24);
|
|
||||||
|
|
||||||
JingleSession s1 = new JingleSession(romeo, juliet, sid);
|
|
||||||
JingleSession s2 = new JingleSession(juliet, romeo, sid);
|
|
||||||
JingleSession s3 = new JingleSession(romeo, juliet, StringUtils.randomString(23));
|
|
||||||
JingleSession s4 = new JingleSession(juliet, romeo, sid);
|
|
||||||
|
|
||||||
assertNotSame(s1, s2);
|
|
||||||
assertNotSame(s1, s3);
|
|
||||||
assertNotSame(s2, s3);
|
|
||||||
assertEquals(s2, s4);
|
|
||||||
assertEquals(s2.hashCode(), s4.hashCode());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.DummyConnection;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.Jingle;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.jxmpp.jid.FullJid;
|
||||||
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the JingleUtil class.
|
||||||
|
*/
|
||||||
|
public class JingleUtilTest extends SmackTestSuite {
|
||||||
|
|
||||||
|
private XMPPConnection connection;
|
||||||
|
private JingleUtil jutil;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
connection = new DummyConnection(
|
||||||
|
DummyConnection.getDummyConfigurationBuilder()
|
||||||
|
.setUsernameAndPassword("romeo@montague.lit",
|
||||||
|
"iluvJulibabe13").build());
|
||||||
|
jutil = new JingleUtil(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sessionInitiateTest() throws XmppStringprepException {
|
||||||
|
FullJid romeo = connection.getUser().asFullJidOrThrow();
|
||||||
|
FullJid juliet = JidCreate.fullFrom("juliet@capulet.example/yn0cl4bnw0yr3vym");
|
||||||
|
|
||||||
|
String sid = "851ba2";
|
||||||
|
String contentName = "a-file-offer";
|
||||||
|
Jingle jingle = jutil.createSessionInitiate(juliet, sid,
|
||||||
|
JingleContent.Creator.initiator, contentName, JingleContent.Senders.initiator, null, null);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
String expected =
|
||||||
|
"<iq from='" + romeo.toString() + "' " +
|
||||||
|
"id='nzu25s8' " +
|
||||||
|
"to='juliet@capulet.example/yn0cl4bnw0yr3vym' " +
|
||||||
|
"type='set'>" +
|
||||||
|
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
||||||
|
"action='session-initiate' " +
|
||||||
|
"initiator='romeo@montague.example/dr4hcr0st3lup4c' " +
|
||||||
|
"sid='851ba2'>" +
|
||||||
|
"<content creator='initiator' name='a-file-offer' senders='initiator'>" +
|
||||||
|
"<description xmlns='urn:xmpp:jingle:apps:file-transfer:5'>" +
|
||||||
|
"<file>" +
|
||||||
|
"<date>1969-07-21T02:56:15Z</date>" +
|
||||||
|
"<desc>This is a test. If this were a real file...</desc>" +
|
||||||
|
"<media-type>text/plain</media-type>" +
|
||||||
|
"<name>test.txt</name>" +
|
||||||
|
"<range/>" +
|
||||||
|
"<size>6144</size>" +
|
||||||
|
"<hash xmlns='urn:xmpp:hashes:2' " +
|
||||||
|
"algo='sha-1'>w0mcJylzCn+AfvuGdqkty2+KP48=</hash>" +
|
||||||
|
"</file>" +
|
||||||
|
"</description>" +
|
||||||
|
"<transport xmlns='urn:xmpp:jingle:transports:s5b:1' " +
|
||||||
|
"mode='tcp' " +
|
||||||
|
"sid='vj3hs98y'> " +
|
||||||
|
"<candidate cid='hft54dqy' " +
|
||||||
|
"host='192.168.4.1' " +
|
||||||
|
"jid='romeo@montague.example/dr4hcr0st3lup4c' " +
|
||||||
|
"port='5086' " +
|
||||||
|
"priority='8257636' " +
|
||||||
|
"type='direct'/>" +
|
||||||
|
"<candidate cid='hutr46fe' " +
|
||||||
|
"host='24.24.24.1' " +
|
||||||
|
"jid='romeo@montague.example/dr4hcr0st3lup4c' " +
|
||||||
|
"port='5087' " +
|
||||||
|
"priority='8258636' " +
|
||||||
|
"type='direct'/>" +
|
||||||
|
"</transport>" +
|
||||||
|
"</content>" +
|
||||||
|
"</jingle>" +
|
||||||
|
"</iq>";
|
||||||
|
}
|
||||||
|
}
|
|
@ -69,5 +69,8 @@ public class JingleIBBTransportTest extends SmackTestSuite {
|
||||||
|
|
||||||
assertEquals(transport3.getNamespace(), JingleIBBTransport.NAMESPACE_V1);
|
assertEquals(transport3.getNamespace(), JingleIBBTransport.NAMESPACE_V1);
|
||||||
assertEquals(transport3.getElementName(), "transport");
|
assertEquals(transport3.getElementName(), "transport");
|
||||||
|
|
||||||
|
JingleIBBTransport transport4 = new JingleIBBTransport("session-id");
|
||||||
|
assertEquals(JingleIBBTransport.DEFAULT_BLOCK_SIZE, transport4.getBlockSize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,14 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle.transports.jingle_s5b;
|
package org.jivesoftware.smackx.jingle.transports.jingle_s5b;
|
||||||
|
|
||||||
|
import static junit.framework.TestCase.assertEquals;
|
||||||
|
import static junit.framework.TestCase.assertFalse;
|
||||||
|
import static junit.framework.TestCase.assertNotNull;
|
||||||
import static junit.framework.TestCase.assertNull;
|
import static junit.framework.TestCase.assertNull;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static junit.framework.TestCase.assertTrue;
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
|
|
||||||
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.bytestreams.socks5.packet.Bytestream;
|
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
|
||||||
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport;
|
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport;
|
||||||
|
@ -29,7 +32,9 @@ import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTr
|
||||||
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.provider.JingleS5BTransportProvider;
|
import org.jivesoftware.smackx.jingle.transports.jingle_s5b.provider.JingleS5BTransportProvider;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.jxmpp.jid.FullJid;
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test Provider and serialization.
|
* Test Provider and serialization.
|
||||||
|
@ -76,8 +81,13 @@ public class JingleS5BTransportTest extends SmackTestSuite {
|
||||||
assertEquals(Bytestream.Mode.tcp, transport.getMode());
|
assertEquals(Bytestream.Mode.tcp, transport.getMode());
|
||||||
assertEquals(3, transport.getCandidates().size());
|
assertEquals(3, transport.getCandidates().size());
|
||||||
|
|
||||||
|
assertTrue(transport.hasCandidate("hft54dqy"));
|
||||||
|
assertFalse(transport.hasCandidate("invalidId"));
|
||||||
JingleS5BTransportCandidate candidate1 =
|
JingleS5BTransportCandidate candidate1 =
|
||||||
(JingleS5BTransportCandidate) transport.getCandidates().get(0);
|
(JingleS5BTransportCandidate) transport.getCandidates().get(0);
|
||||||
|
assertEquals(candidate1, transport.getCandidate("hft54dqy"));
|
||||||
|
assertNotNull(candidate1.getStreamHost());
|
||||||
|
assertEquals(JingleS5BTransportCandidate.Type.direct.getWeight(), candidate1.getType().getWeight());
|
||||||
assertEquals("hft54dqy", candidate1.getCandidateId());
|
assertEquals("hft54dqy", candidate1.getCandidateId());
|
||||||
assertEquals("192.168.4.1", candidate1.getHost());
|
assertEquals("192.168.4.1", candidate1.getHost());
|
||||||
assertEquals(JidCreate.from("romeo@montague.lit/orchard"), candidate1.getJid());
|
assertEquals(JidCreate.from("romeo@montague.lit/orchard"), candidate1.getJid());
|
||||||
|
@ -128,6 +138,7 @@ public class JingleS5BTransportTest extends SmackTestSuite {
|
||||||
JingleS5BTransport proxyErrorTransport = new JingleS5BTransportProvider()
|
JingleS5BTransport proxyErrorTransport = new JingleS5BTransportProvider()
|
||||||
.parse(TestUtils.getParser(proxyError));
|
.parse(TestUtils.getParser(proxyError));
|
||||||
assertNull(proxyErrorTransport.getDestinationAddress());
|
assertNull(proxyErrorTransport.getDestinationAddress());
|
||||||
|
assertNotNull(proxyErrorTransport.getInfo());
|
||||||
assertNotNull(candidateErrorTransport.getInfo());
|
assertNotNull(candidateErrorTransport.getInfo());
|
||||||
assertEquals("vj3hs98y", proxyErrorTransport.getStreamId());
|
assertEquals("vj3hs98y", proxyErrorTransport.getStreamId());
|
||||||
assertEquals(JingleS5BTransportInfo.ProxyError(),
|
assertEquals(JingleS5BTransportInfo.ProxyError(),
|
||||||
|
@ -140,7 +151,7 @@ public class JingleS5BTransportTest extends SmackTestSuite {
|
||||||
"</transport>";
|
"</transport>";
|
||||||
JingleS5BTransport candidateUsedTransport = new JingleS5BTransportProvider()
|
JingleS5BTransport candidateUsedTransport = new JingleS5BTransportProvider()
|
||||||
.parse(TestUtils.getParser(candidateUsed));
|
.parse(TestUtils.getParser(candidateUsed));
|
||||||
assertNotNull(candidateErrorTransport.getInfo());
|
assertNotNull(candidateUsedTransport.getInfo());
|
||||||
assertEquals(JingleS5BTransportInfo.CandidateUsed("hr65dqyd"),
|
assertEquals(JingleS5BTransportInfo.CandidateUsed("hr65dqyd"),
|
||||||
candidateUsedTransport.getInfo());
|
candidateUsedTransport.getInfo());
|
||||||
assertEquals("hr65dqyd",
|
assertEquals("hr65dqyd",
|
||||||
|
@ -154,7 +165,9 @@ public class JingleS5BTransportTest extends SmackTestSuite {
|
||||||
"</transport>";
|
"</transport>";
|
||||||
JingleS5BTransport candidateActivatedTransport = new JingleS5BTransportProvider()
|
JingleS5BTransport candidateActivatedTransport = new JingleS5BTransportProvider()
|
||||||
.parse(TestUtils.getParser(candidateActivated));
|
.parse(TestUtils.getParser(candidateActivated));
|
||||||
|
assertNotNull(candidateActivatedTransport.getInfo());
|
||||||
assertNotNull(candidateErrorTransport.getInfo());
|
assertNotNull(candidateErrorTransport.getInfo());
|
||||||
|
|
||||||
assertEquals(JingleS5BTransportInfo.CandidateActivated("hr65dqyd"),
|
assertEquals(JingleS5BTransportInfo.CandidateActivated("hr65dqyd"),
|
||||||
candidateActivatedTransport.getInfo());
|
candidateActivatedTransport.getInfo());
|
||||||
assertEquals("hr65dqyd",
|
assertEquals("hr65dqyd",
|
||||||
|
@ -162,4 +175,50 @@ public class JingleS5BTransportTest extends SmackTestSuite {
|
||||||
candidateActivatedTransport.getInfo()).getCandidateId());
|
candidateActivatedTransport.getInfo()).getCandidateId());
|
||||||
assertEquals(candidateActivated, candidateActivatedTransport.toXML().toString());
|
assertEquals(candidateActivated, candidateActivatedTransport.toXML().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void candidateBuilderInvalidPortTest() {
|
||||||
|
JingleS5BTransportCandidate.getBuilder().setPort(-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void candidateBuilderInvalidPriorityTest() {
|
||||||
|
JingleS5BTransportCandidate.getBuilder().setPriority(-1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void transportCandidateIllegalPriorityTest() throws XmppStringprepException {
|
||||||
|
FullJid jid = JidCreate.fullFrom("test@test.test/test");
|
||||||
|
JingleS5BTransportCandidate candidate = new JingleS5BTransportCandidate(
|
||||||
|
"cid", "host", jid, 5555, -30, JingleS5BTransportCandidate.Type.proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void transportCandidateIllegalPortTest() throws XmppStringprepException {
|
||||||
|
FullJid jid = JidCreate.fullFrom("test@test.test/test");
|
||||||
|
JingleS5BTransportCandidate candidate = new JingleS5BTransportCandidate(
|
||||||
|
"cid", "host", jid, -5555, 30, JingleS5BTransportCandidate.Type.proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void candidateFromStreamHostTest() throws XmppStringprepException {
|
||||||
|
FullJid jid = JidCreate.fullFrom("test@test.test/test");
|
||||||
|
String host = "host.address";
|
||||||
|
int port = 1234;
|
||||||
|
Bytestream.StreamHost streamHost = new Bytestream.StreamHost(jid, host, port);
|
||||||
|
|
||||||
|
JingleS5BTransportCandidate candidate = new JingleS5BTransportCandidate(streamHost, 2000, JingleS5BTransportCandidate.Type.direct);
|
||||||
|
|
||||||
|
assertEquals(2000, candidate.getPriority());
|
||||||
|
assertEquals(jid, candidate.getJid());
|
||||||
|
assertEquals(host, candidate.getHost());
|
||||||
|
assertEquals(port, candidate.getPort());
|
||||||
|
|
||||||
|
assertEquals(streamHost.toXML().toString(), candidate.getStreamHost().toXML().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void typeFromIllegalStringTest() {
|
||||||
|
JingleS5BTransportCandidate.Type.fromString("illegal-type");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue