Add Jingle changes to smack-experimental
This commit is contained in:
parent
428a8af033
commit
c0d59d8d49
|
@ -16,28 +16,201 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle_filetransfer;
|
package org.jivesoftware.smackx.jingle_filetransfer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smack.Manager;
|
import org.jivesoftware.smack.Manager;
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleDescriptionManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleTransportManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleContent;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.JingleIBBTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.util.Role;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.adapter.JingleFileTransferAdapter;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleFile;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleFileTransfer;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleIncomingFileOffer;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleIncomingFileRequest;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleOutgoingFileOffer;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleOutgoingFileRequest;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.controller.OutgoingFileOfferController;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.controller.OutgoingFileRequestController;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.listener.IncomingFileOfferListener;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.listener.IncomingFileRequestListener;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.provider.JingleFileTransferProvider;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manager for JingleFileTransfer (XEP-0234).
|
* Manager for XEP-0234 - JingleFileTransfers.
|
||||||
*/
|
*/
|
||||||
public final class JingleFileTransferManager extends Manager {
|
public final class JingleFileTransferManager extends Manager implements JingleDescriptionManager {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(JingleFileTransferManager.class.getName());
|
||||||
|
|
||||||
private static final WeakHashMap<XMPPConnection, JingleFileTransferManager> INSTANCES = new WeakHashMap<>();
|
private static final WeakHashMap<XMPPConnection, JingleFileTransferManager> INSTANCES = new WeakHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to the JingleManager of the connection.
|
||||||
|
*/
|
||||||
|
private final JingleManager jingleManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listeners for incoming file offers.
|
||||||
|
*/
|
||||||
|
private final List<IncomingFileOfferListener> offerListeners =
|
||||||
|
Collections.synchronizedList(new ArrayList<IncomingFileOfferListener>());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listeners for incoming file requests.
|
||||||
|
*/
|
||||||
|
private final List<IncomingFileRequestListener> requestListeners =
|
||||||
|
Collections.synchronizedList(new ArrayList<IncomingFileRequestListener>());
|
||||||
|
|
||||||
|
static {
|
||||||
|
// Register adapters and providers.
|
||||||
|
JingleManager.addJingleDescriptionAdapter(new JingleFileTransferAdapter());
|
||||||
|
JingleManager.addJingleDescriptionProvider(new JingleFileTransferProvider());
|
||||||
|
}
|
||||||
|
|
||||||
private JingleFileTransferManager(XMPPConnection connection) {
|
private JingleFileTransferManager(XMPPConnection connection) {
|
||||||
super(connection);
|
super(connection);
|
||||||
|
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(getNamespace());
|
||||||
|
jingleManager = JingleManager.getInstanceFor(connection);
|
||||||
|
jingleManager.addJingleDescriptionManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JingleFileTransferManager getInstanceFor(XMPPConnection connection) {
|
public static JingleFileTransferManager getInstanceFor(XMPPConnection connection) {
|
||||||
JingleFileTransferManager manager = INSTANCES.get(connection);
|
JingleFileTransferManager manager = INSTANCES.get(connection);
|
||||||
|
|
||||||
if (manager == null) {
|
if (manager == null) {
|
||||||
manager = new JingleFileTransferManager(connection);
|
manager = new JingleFileTransferManager(connection);
|
||||||
INSTANCES.put(connection, manager);
|
INSTANCES.put(connection, manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
return manager;
|
return manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OutgoingFileOfferController sendFile(File file, FullJid to)
|
||||||
|
throws SmackException.NotConnectedException, InterruptedException, XMPPException.XMPPErrorException,
|
||||||
|
SmackException.NoResponseException, SmackException.FeatureNotSupportedException, IOException, NoSuchAlgorithmException {
|
||||||
|
return sendFile(file, JingleFile.fromFile(file, null, null, null), to);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutgoingFileOfferController sendFile(File file, JingleFile metadata, FullJid to) throws SmackException.FeatureNotSupportedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, FileNotFoundException {
|
||||||
|
if (file == null || !file.exists()) {
|
||||||
|
throw new IllegalArgumentException("File MUST NOT be null and MUST exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInputStream fi = new FileInputStream(file);
|
||||||
|
|
||||||
|
return sendStream(fi, metadata, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutgoingFileOfferController sendStream(final InputStream stream, JingleFile metadata, FullJid recipient) throws SmackException.FeatureNotSupportedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
|
||||||
|
if (!ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(recipient, getNamespace())) {
|
||||||
|
throw new SmackException.FeatureNotSupportedException(getNamespace(), recipient);
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleSession session = jingleManager.createSession(Role.initiator, recipient);
|
||||||
|
|
||||||
|
JingleContent content = new JingleContent(JingleContentElement.Creator.initiator, JingleContentElement.Senders.initiator);
|
||||||
|
session.addContent(content);
|
||||||
|
|
||||||
|
JingleOutgoingFileOffer outgoingFileOffer = new JingleOutgoingFileOffer(stream, metadata);
|
||||||
|
|
||||||
|
content.setDescription(outgoingFileOffer);
|
||||||
|
|
||||||
|
JingleTransportManager transportManager = jingleManager.getBestAvailableTransportManager(recipient);
|
||||||
|
if (transportManager == null) {
|
||||||
|
// At least Jingle IBB should be supported.
|
||||||
|
throw new SmackException.FeatureNotSupportedException(JingleIBBTransport.NAMESPACE, recipient);
|
||||||
|
}
|
||||||
|
JingleTransport<?> transport = transportManager.createTransportForInitiator(content);
|
||||||
|
content.setTransport(transport);
|
||||||
|
|
||||||
|
session.sendInitiate(connection());
|
||||||
|
|
||||||
|
return outgoingFileOffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutgoingFileRequestController requestFile(JingleFile metadata, FullJid from) {
|
||||||
|
JingleOutgoingFileRequest request = new JingleOutgoingFileRequest(metadata);
|
||||||
|
|
||||||
|
// TODO at some point.
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addIncomingFileOfferListener(IncomingFileOfferListener listener) {
|
||||||
|
offerListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeIncomingFileOfferListener(IncomingFileOfferListener listener) {
|
||||||
|
offerListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void notifyIncomingFileOfferListeners(JingleIncomingFileOffer offer) {
|
||||||
|
LOGGER.log(Level.FINE, "Incoming File transfer: [" + offer.getNamespace() + ", "
|
||||||
|
+ offer.getParent().getTransport().getNamespace() + ", "
|
||||||
|
+ (offer.getParent().getSecurity() != null ? offer.getParent().getSecurity().getNamespace() : "") + "]");
|
||||||
|
for (IncomingFileOfferListener l : offerListeners) {
|
||||||
|
l.onIncomingFileOffer(offer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addIncomingFileRequestListener(IncomingFileRequestListener listener) {
|
||||||
|
requestListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeIncomingFileRequestListener(IncomingFileRequestListener listener) {
|
||||||
|
requestListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void notifyIncomingFileRequestListeners(JingleIncomingFileRequest request) {
|
||||||
|
for (IncomingFileRequestListener l : requestListeners) {
|
||||||
|
l.onIncomingFileRequest(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return JingleFileTransfer.NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyTransfer(JingleFileTransfer transfer) {
|
||||||
|
if (transfer.isOffer()) {
|
||||||
|
notifyIncomingFileOfferListeners((JingleIncomingFileOffer) transfer);
|
||||||
|
} else {
|
||||||
|
notifyIncomingFileRequestListeners((JingleIncomingFileRequest) transfer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void notifySessionInitiate(JingleSession session) {
|
||||||
|
JingleContent content = session.getSoleContentOrThrow();
|
||||||
|
notifyTransfer((JingleFileTransfer) content.getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void notifyContentAdd(JingleSession session, JingleContent content) {
|
||||||
|
notifyTransfer((JingleFileTransfer) content.getDescription());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.adapter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.NamedElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.adapter.JingleDescriptionAdapter;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleFileTransfer;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleIncomingFileOffer;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleIncomingFileRequest;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChildElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter to convert JingleFileTransferElements (element) to JingleFileTransfer objects (component).
|
||||||
|
*/
|
||||||
|
public class JingleFileTransferAdapter implements JingleDescriptionAdapter<JingleFileTransfer> {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(JingleFileTransferAdapter.class.getName());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleFileTransfer descriptionFromElement(JingleContentElement.Creator creator, JingleContentElement.Senders senders,
|
||||||
|
String contentName, String contentDisposition, JingleContentDescriptionElement element) {
|
||||||
|
JingleFileTransferElement description = (JingleFileTransferElement) element;
|
||||||
|
List<NamedElement> children = description.getJingleContentDescriptionChildren();
|
||||||
|
assert children.size() == 1;
|
||||||
|
JingleFileTransferChildElement file = (JingleFileTransferChildElement) children.get(0);
|
||||||
|
|
||||||
|
if (senders == JingleContentElement.Senders.initiator) {
|
||||||
|
return new JingleIncomingFileOffer(file);
|
||||||
|
} else if (senders == JingleContentElement.Senders.responder) {
|
||||||
|
return new JingleIncomingFileRequest(file);
|
||||||
|
} else {
|
||||||
|
if (senders == null) {
|
||||||
|
LOGGER.log(Level.INFO, "Senders is null. Gajim workaround: assume 'initiator'.");
|
||||||
|
return new JingleIncomingFileOffer(file);
|
||||||
|
}
|
||||||
|
throw new AssertionError("Senders attribute MUST be either initiator or responder. Is: " + senders);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return JingleFileTransfer.NAMESPACE;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>.
|
||||||
|
* Adapters.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer.adapter;
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents the base of a JingleFileOffer.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractJingleFileOffer extends JingleFileTransfer {
|
||||||
|
|
||||||
|
AbstractJingleFileOffer(JingleFile fileTransferFile) {
|
||||||
|
super(fileTransferFile);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing the base of a JingleFileRequest.
|
||||||
|
* TODO: Implement.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractJingleFileRequest extends JingleFileTransfer {
|
||||||
|
|
||||||
|
AbstractJingleFileRequest(JingleFile fileTransferFile) {
|
||||||
|
super(fileTransferFile);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer.component;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.DigestInputStream;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.hashes.HashManager;
|
||||||
|
import org.jivesoftware.smackx.hashes.element.HashElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChildElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent a file sent in a file transfer. This class contains metadata of the transferred file.
|
||||||
|
*/
|
||||||
|
public class JingleFile {
|
||||||
|
|
||||||
|
private String name, description, mediaType;
|
||||||
|
private long size;
|
||||||
|
private Date date;
|
||||||
|
private HashElement hashElement;
|
||||||
|
|
||||||
|
public static JingleFile fromFile(File file, String description, String mediaType, HashManager.ALGORITHM hashAlgorithm) throws NoSuchAlgorithmException, IOException {
|
||||||
|
|
||||||
|
HashElement hashElement = null;
|
||||||
|
if (hashAlgorithm != null) {
|
||||||
|
hashElement = calculateHash(file, hashAlgorithm);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JingleFile(file.getName(), description, file.length(), mediaType, new Date(file.lastModified()), hashElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleFile(String name, String description, long size, String mediaType, Date date, HashElement hashElement) {
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
this.size = size;
|
||||||
|
this.mediaType = mediaType;
|
||||||
|
this.date = date;
|
||||||
|
this.hashElement = hashElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleFile(JingleFileTransferChildElement element) {
|
||||||
|
this.name = element.getName();
|
||||||
|
this.description = element.getDescription();
|
||||||
|
this.size = element.getSize();
|
||||||
|
this.mediaType = element.getMediaType();
|
||||||
|
this.date = element.getDate();
|
||||||
|
this.hashElement = element.getHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HashElement calculateHash(File file, HashManager.ALGORITHM algorithm) throws NoSuchAlgorithmException, IOException {
|
||||||
|
if (file == null || !file.exists()) {
|
||||||
|
throw new IllegalArgumentException("File MUST NOT be null and MUST exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageDigest digest = HashManager.getMessageDigest(algorithm);
|
||||||
|
if (digest == null) {
|
||||||
|
throw new NoSuchAlgorithmException("No algorithm for " + algorithm + " found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInputStream fi = new FileInputStream(file);
|
||||||
|
DigestInputStream di = new DigestInputStream(fi, digest);
|
||||||
|
|
||||||
|
while (di.available() > 0) {
|
||||||
|
di.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] d = di.getMessageDigest().digest();
|
||||||
|
|
||||||
|
return new HashElement(algorithm, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleFileTransferChildElement getElement() {
|
||||||
|
JingleFileTransferChildElement.Builder builder = JingleFileTransferChildElement.getBuilder();
|
||||||
|
builder.setDate(getDate());
|
||||||
|
builder.setSize(getSize());
|
||||||
|
builder.setName(getName());
|
||||||
|
builder.setDescription(getDescription());
|
||||||
|
builder.setMediaType(getMediaType());
|
||||||
|
builder.setHash(getHashElement());
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDate() {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMediaType() {
|
||||||
|
return mediaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashElement getHashElement() {
|
||||||
|
return hashElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMediaType(String mediaType) {
|
||||||
|
this.mediaType = mediaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSize(long size) {
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDate(Date date) {
|
||||||
|
this.date = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHashElement(HashElement hashElement) {
|
||||||
|
this.hashElement = hashElement;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.component;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleDescription;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleReasonElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.controller.JingleFileTransferController;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.listener.ProgressListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class of a Jingle File Transfer.
|
||||||
|
*/
|
||||||
|
public abstract class JingleFileTransfer extends JingleDescription<JingleFileTransferElement> implements JingleFileTransferController {
|
||||||
|
|
||||||
|
public static final String NAMESPACE_V5 = "urn:xmpp:jingle:apps:file-transfer:5";
|
||||||
|
public static final String NAMESPACE = NAMESPACE_V5;
|
||||||
|
|
||||||
|
protected State state;
|
||||||
|
protected JingleFile metadata;
|
||||||
|
protected float percentage;
|
||||||
|
|
||||||
|
private final List<ProgressListener> progressListeners = Collections.synchronizedList(new ArrayList<ProgressListener>());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new JingleFileTransfer.
|
||||||
|
* @param metadata metadata of the transferred file.
|
||||||
|
*/
|
||||||
|
JingleFileTransfer(JingleFile metadata) {
|
||||||
|
this.metadata = metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a file offer?
|
||||||
|
* @return file offer?
|
||||||
|
*/
|
||||||
|
public abstract boolean isOffer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a file request?
|
||||||
|
* @return file request?
|
||||||
|
*/
|
||||||
|
public abstract boolean isRequest();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addProgressListener(ProgressListener listener) {
|
||||||
|
progressListeners.add(listener);
|
||||||
|
// TODO: Notify new listener?
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeProgressListener(ProgressListener listener) {
|
||||||
|
progressListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel(XMPPConnection connection) throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
getParent().onContentCancel();
|
||||||
|
state = State.cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify ProgressListeners, that the transmission has started. This happens after the negotiation is complete,
|
||||||
|
* when the transports are ready.
|
||||||
|
*/
|
||||||
|
public void notifyProgressListenersStarted() {
|
||||||
|
for (ProgressListener p : progressListeners) {
|
||||||
|
p.started();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify ProgressListeners, that the transmission has been terminated. This might happen at all times during the
|
||||||
|
* lifetime of the session.
|
||||||
|
* @param reason reason of termination.
|
||||||
|
*/
|
||||||
|
public void notifyProgressListenersTerminated(JingleReasonElement.Reason reason) {
|
||||||
|
for (ProgressListener p : progressListeners) {
|
||||||
|
p.terminated(reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return progress as a float value between 0 and 1.
|
||||||
|
* If the transmission has not yet started, return -1.
|
||||||
|
* @return -1 or percentage in [0,1]
|
||||||
|
*/
|
||||||
|
public float getPercentage() {
|
||||||
|
if (state == State.pending || state == State.negotiating) {
|
||||||
|
return -1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return percentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return JingleFileTransfer.NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleContentTerminate(JingleReasonElement.Reason reason) {
|
||||||
|
notifyProgressListenersTerminated(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleFileTransferElement getElement() {
|
||||||
|
return new JingleFileTransferElement(metadata.getElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public State getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleFile getMetadata() {
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.component;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.security.DigestInputStream;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.Arrays;
|
||||||
|
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.smackx.bytestreams.BytestreamSession;
|
||||||
|
import org.jivesoftware.smackx.hashes.HashManager;
|
||||||
|
import org.jivesoftware.smackx.hashes.element.HashElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.component.JingleSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionInfoElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleReasonElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.controller.IncomingFileOfferController;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChildElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behind the scenes logic of an incoming Jingle file offer (They offer a file).
|
||||||
|
*/
|
||||||
|
public class JingleIncomingFileOffer extends AbstractJingleFileOffer implements IncomingFileOfferController {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(JingleIncomingFileOffer.class.getName());
|
||||||
|
private OutputStream target;
|
||||||
|
|
||||||
|
public JingleIncomingFileOffer(JingleFileTransferChildElement offer) {
|
||||||
|
super(new JingleFile(offer));
|
||||||
|
this.state = State.pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleElement handleDescriptionInfo(JingleContentDescriptionInfoElement info) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBytestreamReady(BytestreamSession bytestreamSession) {
|
||||||
|
if (target == null) {
|
||||||
|
throw new IllegalStateException("Target OutputStream is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == State.negotiating) {
|
||||||
|
state = State.active;
|
||||||
|
notifyProgressListenersStarted();
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashElement hashElement = metadata.getHashElement();
|
||||||
|
MessageDigest digest = null;
|
||||||
|
if (hashElement != null) {
|
||||||
|
digest = HashManager.getMessageDigest(hashElement.getAlgorithm());
|
||||||
|
LOGGER.log(Level.FINE, "File offer had checksum: " + digest.toString() + ": " + hashElement.getHashB64());
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.log(Level.FINE, "Receive file");
|
||||||
|
|
||||||
|
InputStream inputStream = null;
|
||||||
|
try {
|
||||||
|
inputStream = bytestreamSession.getInputStream();
|
||||||
|
|
||||||
|
if (digest != null) {
|
||||||
|
inputStream = new DigestInputStream(inputStream, digest);
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = 0;
|
||||||
|
int read = 0;
|
||||||
|
byte[] bufbuf = new byte[4096];
|
||||||
|
while ((length = inputStream.read(bufbuf)) >= 0) {
|
||||||
|
if (getState() == State.cancelled) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
target.write(bufbuf, 0, length);
|
||||||
|
read += length;
|
||||||
|
LOGGER.log(Level.FINER, "Read " + read + " (" + length + ") of " + metadata.getSize() + " bytes.");
|
||||||
|
|
||||||
|
percentage = ((float) read) / ((float) metadata.getSize());
|
||||||
|
|
||||||
|
if (read == (int) metadata.getSize()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOGGER.log(Level.FINE, "Reading/Writing finished.");
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Cannot get InputStream from BytestreamSession.", e);
|
||||||
|
} finally {
|
||||||
|
state = State.ended;
|
||||||
|
if (inputStream != null) {
|
||||||
|
try {
|
||||||
|
inputStream.close();
|
||||||
|
LOGGER.log(Level.FINER, "CipherInputStream closed.");
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Could not close InputStream.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target != null) {
|
||||||
|
try {
|
||||||
|
target.close();
|
||||||
|
LOGGER.log(Level.FINER, "FileOutputStream closed.");
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Could not close OutputStream.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (digest != null) {
|
||||||
|
byte[] mDigest = ((DigestInputStream) inputStream).getMessageDigest().digest();
|
||||||
|
if (!Arrays.equals(hashElement.getHash(), mDigest)) {
|
||||||
|
LOGGER.log(Level.WARNING, "CHECKSUM MISMATCH!");
|
||||||
|
} else {
|
||||||
|
LOGGER.log(Level.FINE, "CHECKSUM MATCHED :)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifyProgressListenersTerminated(JingleReasonElement.Reason.success);
|
||||||
|
getParent().onContentFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOffer() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRequest() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(XMPPConnection connection, File target)
|
||||||
|
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
|
||||||
|
SmackException.NoResponseException, IOException {
|
||||||
|
state = State.negotiating;
|
||||||
|
|
||||||
|
if (!target.exists()) {
|
||||||
|
target.createNewFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.target = new FileOutputStream(target);
|
||||||
|
|
||||||
|
JingleSession session = getParent().getParent();
|
||||||
|
if (session.getSessionState() == JingleSession.SessionState.pending) {
|
||||||
|
session.sendAccept(connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(XMPPConnection connection, OutputStream stream)
|
||||||
|
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
|
||||||
|
SmackException.NoResponseException {
|
||||||
|
state = State.negotiating;
|
||||||
|
|
||||||
|
target = stream;
|
||||||
|
|
||||||
|
JingleSession session = getParent().getParent();
|
||||||
|
if (session.getSessionState() == JingleSession.SessionState.pending) {
|
||||||
|
session.sendAccept(connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.component;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionInfoElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.controller.IncomingFileRequestController;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChildElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backend logic of an incoming file request (They request a file).
|
||||||
|
* TODO: Implement.
|
||||||
|
*/
|
||||||
|
public class JingleIncomingFileRequest extends AbstractJingleFileRequest implements IncomingFileRequestController {
|
||||||
|
|
||||||
|
public JingleIncomingFileRequest(JingleFileTransferChildElement request) {
|
||||||
|
super(new JingleFile(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleFileTransferElement getElement() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleElement handleDescriptionInfo(JingleContentDescriptionInfoElement info) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOffer() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRequest() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBytestreamReady(BytestreamSession bytestreamSession) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer.component;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionInfoElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleReasonElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.controller.OutgoingFileOfferController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backend logic of an outgoing file offer (We offer a file).
|
||||||
|
*/
|
||||||
|
public class JingleOutgoingFileOffer extends AbstractJingleFileOffer implements OutgoingFileOfferController {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(JingleOutgoingFileOffer.class.getName());
|
||||||
|
|
||||||
|
private final InputStream source;
|
||||||
|
|
||||||
|
public JingleOutgoingFileOffer(File file, JingleFile metadata) throws FileNotFoundException {
|
||||||
|
super(metadata);
|
||||||
|
this.source = new FileInputStream(file);
|
||||||
|
this.state = State.pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleOutgoingFileOffer(InputStream inputStream, JingleFile metadata) {
|
||||||
|
super(metadata);
|
||||||
|
this.source = inputStream;
|
||||||
|
this.state = State.pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleElement handleDescriptionInfo(JingleContentDescriptionInfoElement info) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBytestreamReady(BytestreamSession bytestreamSession) {
|
||||||
|
if (source == null) {
|
||||||
|
throw new IllegalStateException("Source InputStream is null!");
|
||||||
|
}
|
||||||
|
|
||||||
|
state = State.active;
|
||||||
|
|
||||||
|
notifyProgressListenersStarted();
|
||||||
|
|
||||||
|
OutputStream outputStream = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
outputStream = bytestreamSession.getOutputStream();
|
||||||
|
|
||||||
|
byte[] buf = new byte[8192];
|
||||||
|
|
||||||
|
int written = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (getState() == State.cancelled) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int r = source.read(buf);
|
||||||
|
if (r < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
outputStream.write(buf, 0, r);
|
||||||
|
written += r;
|
||||||
|
percentage = ((float) getMetadata().getSize()) / ((float) written);
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Exception while sending file.", e);
|
||||||
|
} finally {
|
||||||
|
state = State.ended;
|
||||||
|
try {
|
||||||
|
source.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Could not close FileInputStream.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyProgressListenersTerminated(JingleReasonElement.Reason.success);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOffer() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRequest() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer.component;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionInfoElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.controller.OutgoingFileRequestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backend logic of an outgoing file request (We request a file).
|
||||||
|
*/
|
||||||
|
public class JingleOutgoingFileRequest extends AbstractJingleFileRequest implements OutgoingFileRequestController {
|
||||||
|
|
||||||
|
public JingleOutgoingFileRequest(JingleFile file) {
|
||||||
|
super(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JingleElement handleDescriptionInfo(JingleContentDescriptionInfoElement info) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOffer() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRequest() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBytestreamReady(BytestreamSession bytestreamSession) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>.
|
||||||
|
* Internal classes.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer.component;
|
|
@ -0,0 +1,55 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.controller;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface with methods of an incoming file offer, that are exposed to the user.
|
||||||
|
*/
|
||||||
|
public interface IncomingFileOfferController extends JingleFileTransferController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept an incoming file offer.
|
||||||
|
* @param connection connection.
|
||||||
|
* @param target file to save the incoming data to.
|
||||||
|
* @throws InterruptedException
|
||||||
|
* @throws XMPPException.XMPPErrorException
|
||||||
|
* @throws SmackException.NotConnectedException
|
||||||
|
* @throws SmackException.NoResponseException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
void accept(XMPPConnection connection, File target) throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException, IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept an incoming file offer.
|
||||||
|
* @param connection connection.
|
||||||
|
* @param outputStream outputStream, to stream the incoming data to.
|
||||||
|
* @throws InterruptedException
|
||||||
|
* @throws XMPPException.XMPPErrorException
|
||||||
|
* @throws SmackException.NotConnectedException
|
||||||
|
* @throws SmackException.NoResponseException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
void accept(XMPPConnection connection, OutputStream outputStream) throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException, IOException;
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.controller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface with methods of an incoming file request, that are exposed to the user.
|
||||||
|
*/
|
||||||
|
public interface IncomingFileRequestController extends JingleFileTransferController {
|
||||||
|
// TODO: Declare methods.
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.controller;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smackx.jingle.JingleDescriptionController;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleFile;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.listener.ProgressListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface with methods of a jingle file transfer, that are exposed to the user.
|
||||||
|
*/
|
||||||
|
public interface JingleFileTransferController extends JingleDescriptionController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a ProgressListener.
|
||||||
|
* @param listener listener
|
||||||
|
*/
|
||||||
|
void addProgressListener(ProgressListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a ProgressListener.
|
||||||
|
* @param listener listener
|
||||||
|
*/
|
||||||
|
void removeProgressListener(ProgressListener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the JingleFile object containing metadata about the transferred file.
|
||||||
|
* @return metadata
|
||||||
|
*/
|
||||||
|
JingleFile getMetadata();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actively cancel the file transfer.
|
||||||
|
* @param connection connection
|
||||||
|
* @throws SmackException.NotConnectedException
|
||||||
|
* @throws InterruptedException
|
||||||
|
*/
|
||||||
|
void cancel(XMPPConnection connection) throws SmackException.NotConnectedException, InterruptedException;
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.controller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface with methods of an outgoing file offer, that are exposed to the user.
|
||||||
|
*/
|
||||||
|
public interface OutgoingFileOfferController extends JingleFileTransferController {
|
||||||
|
// TODO: Declare methods.
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.controller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface with methods of an outgoing file request, that are exposed to the user.
|
||||||
|
*/
|
||||||
|
public interface OutgoingFileRequestController extends JingleFileTransferController {
|
||||||
|
// TODO: Declare methods.
|
||||||
|
}
|
|
@ -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>.
|
||||||
|
* Controller.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer.controller;
|
|
@ -19,21 +19,29 @@ package org.jivesoftware.smackx.jingle_filetransfer.element;
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleFileTransfer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checksum element.
|
* Checksum element declared in <a href="https://xmpp.org/extensions/xep-0234.html#hash">XEP-0234</a>.
|
||||||
*/
|
*/
|
||||||
public class Checksum implements ExtensionElement {
|
public class ChecksumElement implements ExtensionElement {
|
||||||
|
|
||||||
public static final String ELEMENT = "checksum";
|
public static final String ELEMENT = "checksum";
|
||||||
public static final String ATTR_CREATOR = "creator";
|
public static final String ATTR_CREATOR = "creator";
|
||||||
public static final String ATTR_NAME = "name";
|
public static final String ATTR_NAME = "name";
|
||||||
|
|
||||||
private final JingleContent.Creator creator;
|
private final JingleContentElement.Creator creator;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final JingleFileTransferChild file;
|
private final JingleFileTransferChildElement file;
|
||||||
|
|
||||||
public Checksum(JingleContent.Creator creator, String name, JingleFileTransferChild file) {
|
/**
|
||||||
|
* Create a new ChecksumElement.
|
||||||
|
* @param creator creator of the content (party that added the file to the transmission).
|
||||||
|
* @param name name of the content.
|
||||||
|
* @param file metadata of the file.
|
||||||
|
*/
|
||||||
|
public ChecksumElement(JingleContentElement.Creator creator, String name, JingleFileTransferChildElement file) {
|
||||||
this.creator = creator;
|
this.creator = creator;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.file = Objects.requireNonNull(file, "file MUST NOT be null.");
|
this.file = Objects.requireNonNull(file, "file MUST NOT be null.");
|
||||||
|
@ -58,6 +66,6 @@ public class Checksum implements ExtensionElement {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNamespace() {
|
public String getNamespace() {
|
||||||
return JingleFileTransfer.NAMESPACE_V5;
|
return JingleFileTransfer.NAMESPACE;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -24,9 +24,10 @@ import org.jivesoftware.smackx.hashes.element.HashElement;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement;
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content of type File.
|
* Metadata about the transferred file.
|
||||||
*/
|
*/
|
||||||
public class JingleFileTransferChild extends JingleContentDescriptionChildElement {
|
public class JingleFileTransferChildElement extends JingleContentDescriptionChildElement {
|
||||||
|
|
||||||
public static final String ELEMENT = "file";
|
public static final String ELEMENT = "file";
|
||||||
public static final String ELEM_DATE = "date";
|
public static final String ELEM_DATE = "date";
|
||||||
public static final String ELEM_DESC = "desc";
|
public static final String ELEM_DESC = "desc";
|
||||||
|
@ -39,10 +40,20 @@ public class JingleFileTransferChild extends JingleContentDescriptionChildElemen
|
||||||
private final HashElement hash;
|
private final HashElement hash;
|
||||||
private final String mediaType;
|
private final String mediaType;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final int size;
|
private final long size;
|
||||||
private final Range range;
|
private final Range range;
|
||||||
|
|
||||||
public JingleFileTransferChild(Date date, String desc, HashElement hash, String mediaType, String name, int size, Range range) {
|
/**
|
||||||
|
* Create a new JingleFileTransferChildElement.
|
||||||
|
* @param date last-modified date of the file.
|
||||||
|
* @param desc description of the file.
|
||||||
|
* @param hash hash value of the file (see XEP-0300).
|
||||||
|
* @param mediaType mediaType (https://www.iana.org/assignments/media-types/media-types.xhtml).
|
||||||
|
* @param name name of the file.
|
||||||
|
* @param size size of the file in bytes.
|
||||||
|
* @param range range of the transfer (see https://xmpp.org/extensions/xep-0234.html#range).
|
||||||
|
*/
|
||||||
|
public JingleFileTransferChildElement(Date date, String desc, HashElement hash, String mediaType, String name, long size, Range range) {
|
||||||
this.date = date;
|
this.date = date;
|
||||||
this.desc = desc;
|
this.desc = desc;
|
||||||
this.hash = hash;
|
this.hash = hash;
|
||||||
|
@ -52,30 +63,58 @@ public class JingleFileTransferChild extends JingleContentDescriptionChildElemen
|
||||||
this.range = range;
|
this.range = range;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the last-modified date of the file.
|
||||||
|
* @return date.
|
||||||
|
*/
|
||||||
public Date getDate() {
|
public Date getDate() {
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the description of the file.
|
||||||
|
* @return description.
|
||||||
|
*/
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the hash of the file.
|
||||||
|
* @return hash.
|
||||||
|
*/
|
||||||
public HashElement getHash() {
|
public HashElement getHash() {
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the mediaType of the file (https://www.iana.org/assignments/media-types/media-types.xhtml).
|
||||||
|
* @return media-type.
|
||||||
|
*/
|
||||||
public String getMediaType() {
|
public String getMediaType() {
|
||||||
return mediaType;
|
return mediaType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the file.
|
||||||
|
* @return filename.
|
||||||
|
*/
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSize() {
|
/**
|
||||||
|
* Return the size of the file in bytes.
|
||||||
|
* @return size.
|
||||||
|
*/
|
||||||
|
public long getSize() {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In case of a ranged transfer: Return the range of the transmission. Otherwise return null.
|
||||||
|
* @return range of the transfer.
|
||||||
|
*/
|
||||||
public Range getRange() {
|
public Range getRange() {
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
@ -96,7 +135,7 @@ public class JingleFileTransferChild extends JingleContentDescriptionChildElemen
|
||||||
sb.optElement(ELEM_NAME, name);
|
sb.optElement(ELEM_NAME, name);
|
||||||
sb.optElement(range);
|
sb.optElement(range);
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
sb.element(ELEM_SIZE, Integer.toString(size));
|
sb.element(ELEM_SIZE, Long.toString(size));
|
||||||
}
|
}
|
||||||
sb.optElement(hash);
|
sb.optElement(hash);
|
||||||
sb.closeElement(this);
|
sb.closeElement(this);
|
||||||
|
@ -113,7 +152,7 @@ public class JingleFileTransferChild extends JingleContentDescriptionChildElemen
|
||||||
private HashElement hash;
|
private HashElement hash;
|
||||||
private String mediaType;
|
private String mediaType;
|
||||||
private String name;
|
private String name;
|
||||||
private int size;
|
private long size;
|
||||||
private Range range;
|
private Range range;
|
||||||
|
|
||||||
private Builder() {
|
private Builder() {
|
||||||
|
@ -134,6 +173,14 @@ public class JingleFileTransferChild extends JingleContentDescriptionChildElemen
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the media type of the file.
|
||||||
|
* This is a MIME type from this list:
|
||||||
|
* https://www.iana.org/assignments/media-types/media-types.xhtml
|
||||||
|
* Default should be application/octet-stream.
|
||||||
|
* @param mediaType new media type.
|
||||||
|
* @return builder.
|
||||||
|
*/
|
||||||
public Builder setMediaType(String mediaType) {
|
public Builder setMediaType(String mediaType) {
|
||||||
this.mediaType = mediaType;
|
this.mediaType = mediaType;
|
||||||
return this;
|
return this;
|
||||||
|
@ -144,7 +191,7 @@ public class JingleFileTransferChild extends JingleContentDescriptionChildElemen
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setSize(int size) {
|
public Builder setSize(long size) {
|
||||||
this.size = size;
|
this.size = size;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -154,8 +201,8 @@ public class JingleFileTransferChild extends JingleContentDescriptionChildElemen
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JingleFileTransferChild build() {
|
public JingleFileTransferChildElement build() {
|
||||||
return new JingleFileTransferChild(date, desc, hash, mediaType, name, size, range);
|
return new JingleFileTransferChildElement(date, desc, hash, mediaType, name, size, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setFile(File file) {
|
public Builder setFile(File file) {
|
|
@ -16,23 +16,31 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.jingle_filetransfer.element;
|
package org.jivesoftware.smackx.jingle_filetransfer.element;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
|
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement;
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleFileTransfer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File element.
|
* Jingle File Transfer Element.
|
||||||
*/
|
*/
|
||||||
public class JingleFileTransfer extends JingleContentDescription {
|
public class JingleFileTransferElement extends JingleContentDescriptionElement {
|
||||||
public static final String NAMESPACE_V5 = "urn:xmpp:jingle:apps:file-transfer:5";
|
|
||||||
|
|
||||||
public JingleFileTransfer(List<JingleContentDescriptionChildElement> payloads) {
|
public JingleFileTransferElement(JingleContentDescriptionChildElement payload) {
|
||||||
|
this(Collections.singletonList(payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleFileTransferElement(List<JingleContentDescriptionChildElement> payloads) {
|
||||||
super(payloads);
|
super(payloads);
|
||||||
|
if (payloads.size() != 1) {
|
||||||
|
throw new IllegalArgumentException("Jingle File Transfers only support one payload element.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNamespace() {
|
public String getNamespace() {
|
||||||
return NAMESPACE_V5;
|
return JingleFileTransfer.NAMESPACE;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -29,22 +29,22 @@ public class Range implements NamedElement {
|
||||||
public static final String ATTR_OFFSET = "offset";
|
public static final String ATTR_OFFSET = "offset";
|
||||||
public static final String ATTR_LENGTH = "length";
|
public static final String ATTR_LENGTH = "length";
|
||||||
|
|
||||||
private final int offset, length;
|
private final Long offset, length;
|
||||||
private final HashElement hash;
|
private final HashElement hash;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Range element with default values.
|
* Create a Range element with default values.
|
||||||
*/
|
*/
|
||||||
public Range() {
|
public Range() {
|
||||||
this(0, -1, null);
|
this(null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Range element with specified length.
|
* Create a Range element with specified length.
|
||||||
* @param length length of the transmitted data in bytes.
|
* @param length length of the transmitted data in bytes.
|
||||||
*/
|
*/
|
||||||
public Range(int length) {
|
public Range(Long length) {
|
||||||
this(0, length, null);
|
this(null, length, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,7 +52,7 @@ public class Range implements NamedElement {
|
||||||
* @param offset offset in bytes from the beginning of the transmitted data.
|
* @param offset offset in bytes from the beginning of the transmitted data.
|
||||||
* @param length number of bytes that shall be transferred.
|
* @param length number of bytes that shall be transferred.
|
||||||
*/
|
*/
|
||||||
public Range(int offset, int length) {
|
public Range(Long offset, Long length) {
|
||||||
this(offset, length, null);
|
this(offset, length, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ public class Range implements NamedElement {
|
||||||
* @param length number of bytes that shall be transferred.
|
* @param length number of bytes that shall be transferred.
|
||||||
* @param hash hash of the bytes in the specified range.
|
* @param hash hash of the bytes in the specified range.
|
||||||
*/
|
*/
|
||||||
public Range(int offset, int length, HashElement hash) {
|
public Range(Long offset, Long length, HashElement hash) {
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
this.hash = hash;
|
this.hash = hash;
|
||||||
|
@ -73,7 +73,7 @@ public class Range implements NamedElement {
|
||||||
* This marks the begin of the specified range.
|
* This marks the begin of the specified range.
|
||||||
* @return offset
|
* @return offset
|
||||||
*/
|
*/
|
||||||
public int getOffset() {
|
public Long getOffset() {
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ public class Range implements NamedElement {
|
||||||
* Return the length of the range.
|
* Return the length of the range.
|
||||||
* @return length
|
* @return length
|
||||||
*/
|
*/
|
||||||
public int getLength() {
|
public Long getLength() {
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,12 +102,8 @@ public class Range implements NamedElement {
|
||||||
public CharSequence toXML(String enclosingNamespace) {
|
public CharSequence toXML(String enclosingNamespace) {
|
||||||
XmlStringBuilder sb = new XmlStringBuilder(this);
|
XmlStringBuilder sb = new XmlStringBuilder(this);
|
||||||
|
|
||||||
if (offset > 0) {
|
sb.optAttribute(ATTR_OFFSET, offset);
|
||||||
sb.attribute(ATTR_OFFSET, offset);
|
sb.optAttribute(ATTR_LENGTH, length);
|
||||||
}
|
|
||||||
if (length > 0) {
|
|
||||||
sb.attribute(ATTR_LENGTH, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hash != null) {
|
if (hash != null) {
|
||||||
sb.rightAngleBracket();
|
sb.rightAngleBracket();
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.listener;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.controller.IncomingFileOfferController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for incoming file offers.
|
||||||
|
*/
|
||||||
|
public interface IncomingFileOfferListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify client of an incoming file offer.
|
||||||
|
* @param offer offer.
|
||||||
|
*/
|
||||||
|
void onIncomingFileOffer(IncomingFileOfferController offer);
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.listener;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.controller.IncomingFileRequestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for incoming file requests.
|
||||||
|
*/
|
||||||
|
public interface IncomingFileRequestListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the client of an incoming file request.
|
||||||
|
* @param request request.
|
||||||
|
*/
|
||||||
|
void onIncomingFileRequest(IncomingFileRequestController request);
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.listener;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleReasonElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProgressListener that listens for progress changes of a file transfer.
|
||||||
|
*/
|
||||||
|
public interface ProgressListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bytestream transmission has stared. This usually happens after the negotiation is finished.
|
||||||
|
*/
|
||||||
|
void started();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transfer has been terminated. This might happen at all times.
|
||||||
|
* @param reason reason (eg. cancel).
|
||||||
|
*/
|
||||||
|
void terminated(JingleReasonElement.Reason reason);
|
||||||
|
}
|
|
@ -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>.
|
||||||
|
* Listeners.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer.listener;
|
|
@ -20,50 +20,53 @@ import static org.xmlpull.v1.XmlPullParser.END_TAG;
|
||||||
import static org.xmlpull.v1.XmlPullParser.START_TAG;
|
import static org.xmlpull.v1.XmlPullParser.START_TAG;
|
||||||
|
|
||||||
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||||
|
import org.jivesoftware.smack.util.ParserUtils;
|
||||||
import org.jivesoftware.smackx.hashes.element.HashElement;
|
import org.jivesoftware.smackx.hashes.element.HashElement;
|
||||||
import org.jivesoftware.smackx.hashes.provider.HashElementProvider;
|
import org.jivesoftware.smackx.hashes.provider.HashElementProvider;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContent;
|
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.element.Checksum;
|
import org.jivesoftware.smackx.jingle_filetransfer.element.ChecksumElement;
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChild;
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChildElement;
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.element.Range;
|
import org.jivesoftware.smackx.jingle_filetransfer.element.Range;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provider for the Checksum element.
|
* Provider for the Checksum element.
|
||||||
*/
|
*/
|
||||||
public class ChecksumProvider extends ExtensionElementProvider<Checksum> {
|
public class ChecksumProvider extends ExtensionElementProvider<ChecksumElement> {
|
||||||
|
|
||||||
|
private static final HashElementProvider hashProvider = new HashElementProvider();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Checksum parse(XmlPullParser parser, int initialDepth) throws Exception {
|
public ChecksumElement parse(XmlPullParser parser, int initialDepth) throws Exception {
|
||||||
JingleContent.Creator creator = null;
|
JingleContentElement.Creator creator = null;
|
||||||
String creatorString = parser.getAttributeValue(null, Checksum.ATTR_CREATOR);
|
String creatorString = parser.getAttributeValue(null, ChecksumElement.ATTR_CREATOR);
|
||||||
if (creatorString != null) {
|
if (creatorString != null) {
|
||||||
creator = JingleContent.Creator.valueOf(creatorString);
|
creator = JingleContentElement.Creator.valueOf(creatorString);
|
||||||
}
|
}
|
||||||
String name = parser.getAttributeValue(null, Checksum.ATTR_NAME);
|
String name = parser.getAttributeValue(null, ChecksumElement.ATTR_NAME);
|
||||||
|
|
||||||
|
|
||||||
JingleFileTransferChild.Builder cb = JingleFileTransferChild.getBuilder();
|
JingleFileTransferChildElement.Builder cb = JingleFileTransferChildElement.getBuilder();
|
||||||
HashElement hashElement = null;
|
HashElement hashElement = null;
|
||||||
Range range = null;
|
Range range = null;
|
||||||
|
|
||||||
boolean go = true;
|
boolean go = true;
|
||||||
while (go) {
|
while (go) {
|
||||||
int tag = parser.nextTag();
|
int tag = parser.nextTag();
|
||||||
String n = parser.getText();
|
String n = parser.getName();
|
||||||
|
|
||||||
if (tag == START_TAG) {
|
if (tag == START_TAG) {
|
||||||
switch (n) {
|
switch (n) {
|
||||||
case HashElement.ELEMENT:
|
case HashElement.ELEMENT:
|
||||||
hashElement = new HashElementProvider().parse(parser);
|
hashElement = hashProvider.parse(parser);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Range.ELEMENT:
|
case Range.ELEMENT:
|
||||||
String offset = parser.getAttributeValue(null, Range.ATTR_OFFSET);
|
Long offset = ParserUtils.getLongAttribute(parser, Range.ATTR_OFFSET);
|
||||||
String length = parser.getAttributeValue(null, Range.ATTR_LENGTH);
|
Long length = ParserUtils.getLongAttribute(parser, Range.ATTR_LENGTH);
|
||||||
int o = offset == null ? 0 : Integer.parseInt(offset);
|
range = new Range(offset, length);
|
||||||
int l = length == null ? -1 : Integer.parseInt(length);
|
|
||||||
range = new Range(o, l);
|
|
||||||
}
|
}
|
||||||
} else if (tag == END_TAG) {
|
} else if (tag == END_TAG) {
|
||||||
switch (n) {
|
switch (n) {
|
||||||
|
@ -74,7 +77,7 @@ public class ChecksumProvider extends ExtensionElementProvider<Checksum> {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JingleFileTransferChild.ELEMENT:
|
case JingleFileTransferChildElement.ELEMENT:
|
||||||
if (hashElement != null) {
|
if (hashElement != null) {
|
||||||
cb.setHash(hashElement);
|
cb.setHash(hashElement);
|
||||||
}
|
}
|
||||||
|
@ -85,6 +88,6 @@ public class ChecksumProvider extends ExtensionElementProvider<Checksum> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new Checksum(creator, name, cb.build());
|
return new ChecksumElement(creator, name, cb.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,12 +21,14 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.util.ParserUtils;
|
||||||
import org.jivesoftware.smackx.hashes.element.HashElement;
|
import org.jivesoftware.smackx.hashes.element.HashElement;
|
||||||
import org.jivesoftware.smackx.hashes.provider.HashElementProvider;
|
import org.jivesoftware.smackx.hashes.provider.HashElementProvider;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement;
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescriptionChildElement;
|
||||||
import org.jivesoftware.smackx.jingle.provider.JingleContentDescriptionProvider;
|
import org.jivesoftware.smackx.jingle.provider.JingleContentDescriptionProvider;
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransfer;
|
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleFileTransfer;
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChild;
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChildElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferElement;
|
||||||
import org.jivesoftware.smackx.jingle_filetransfer.element.Range;
|
import org.jivesoftware.smackx.jingle_filetransfer.element.Range;
|
||||||
|
|
||||||
import org.jxmpp.util.XmppDateTime;
|
import org.jxmpp.util.XmppDateTime;
|
||||||
|
@ -36,18 +38,15 @@ import org.xmlpull.v1.XmlPullParser;
|
||||||
* Provider for JingleContentDescriptionFileTransfer elements.
|
* Provider for JingleContentDescriptionFileTransfer elements.
|
||||||
*/
|
*/
|
||||||
public class JingleFileTransferProvider
|
public class JingleFileTransferProvider
|
||||||
extends JingleContentDescriptionProvider<JingleFileTransfer> {
|
extends JingleContentDescriptionProvider<JingleFileTransferElement> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JingleFileTransfer parse(XmlPullParser parser, int initialDepth) throws Exception {
|
public JingleFileTransferElement parse(XmlPullParser parser, int initialDepth) throws Exception {
|
||||||
ArrayList<JingleContentDescriptionChildElement> payloads = new ArrayList<>();
|
ArrayList<JingleContentDescriptionChildElement> payloads = new ArrayList<>();
|
||||||
boolean inRange = false;
|
boolean inRange = false;
|
||||||
JingleFileTransferChild.Builder builder = JingleFileTransferChild.getBuilder();
|
JingleFileTransferChildElement.Builder builder = JingleFileTransferChildElement.getBuilder();
|
||||||
HashElement inRangeHash = null;
|
HashElement inRangeHash = null;
|
||||||
|
Long length = null, offset = null;
|
||||||
int offset = 0;
|
|
||||||
int length = -1;
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
||||||
int tag = parser.nextTag();
|
int tag = parser.nextTag();
|
||||||
|
@ -55,32 +54,30 @@ public class JingleFileTransferProvider
|
||||||
|
|
||||||
if (tag == START_TAG) {
|
if (tag == START_TAG) {
|
||||||
switch (elem) {
|
switch (elem) {
|
||||||
case JingleFileTransferChild.ELEM_DATE:
|
case JingleFileTransferChildElement.ELEM_DATE:
|
||||||
builder.setDate(XmppDateTime.parseXEP0082Date(parser.nextText()));
|
builder.setDate(XmppDateTime.parseXEP0082Date(parser.nextText()));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JingleFileTransferChild.ELEM_DESC:
|
case JingleFileTransferChildElement.ELEM_DESC:
|
||||||
builder.setDescription(parser.nextText());
|
builder.setDescription(parser.nextText());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JingleFileTransferChild.ELEM_MEDIA_TYPE:
|
case JingleFileTransferChildElement.ELEM_MEDIA_TYPE:
|
||||||
builder.setMediaType(parser.nextText());
|
builder.setMediaType(parser.nextText());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JingleFileTransferChild.ELEM_NAME:
|
case JingleFileTransferChildElement.ELEM_NAME:
|
||||||
builder.setName(parser.nextText());
|
builder.setName(parser.nextText());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JingleFileTransferChild.ELEM_SIZE:
|
case JingleFileTransferChildElement.ELEM_SIZE:
|
||||||
builder.setSize(Integer.parseInt(parser.nextText()));
|
builder.setSize(Integer.parseInt(parser.nextText()));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Range.ELEMENT:
|
case Range.ELEMENT:
|
||||||
inRange = true;
|
inRange = true;
|
||||||
String offsetString = parser.getAttributeValue(null, Range.ATTR_OFFSET);
|
offset = ParserUtils.getLongAttribute(parser, Range.ATTR_OFFSET);
|
||||||
String lengthString = parser.getAttributeValue(null, Range.ATTR_LENGTH);
|
length = ParserUtils.getLongAttribute(parser, Range.ATTR_LENGTH);
|
||||||
offset = (offsetString != null ? Integer.parseInt(offsetString) : 0);
|
|
||||||
length = (lengthString != null ? Integer.parseInt(lengthString) : -1);
|
|
||||||
|
|
||||||
if (parser.isEmptyElementTag()) {
|
if (parser.isEmptyElementTag()) {
|
||||||
inRange = false;
|
inRange = false;
|
||||||
|
@ -106,15 +103,20 @@ public class JingleFileTransferProvider
|
||||||
inRangeHash = null;
|
inRangeHash = null;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JingleFileTransferChild.ELEMENT:
|
case JingleFileTransferChildElement.ELEMENT:
|
||||||
payloads.add(builder.build());
|
payloads.add(builder.build());
|
||||||
builder = JingleFileTransferChild.getBuilder();
|
builder = JingleFileTransferChildElement.getBuilder();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JingleFileTransfer.ELEMENT:
|
case JingleFileTransferElement.ELEMENT:
|
||||||
return new JingleFileTransfer(payloads);
|
return new JingleFileTransferElement(payloads);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return JingleFileTransfer.NAMESPACE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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 static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
|
import org.jivesoftware.smack.test.util.TestUtils;
|
||||||
|
import org.jivesoftware.smackx.hashes.HashManager;
|
||||||
|
import org.jivesoftware.smackx.hashes.element.HashElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleContentElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.ChecksumElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChildElement;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.Range;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.provider.ChecksumProvider;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by vanitas on 12.07.17.
|
||||||
|
*/
|
||||||
|
public class ChecksumTest extends SmackTestSuite {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parserTest() throws Exception {
|
||||||
|
HashElement hash = new HashElement(HashManager.ALGORITHM.SHA_256, "f4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGk=");
|
||||||
|
JingleFileTransferChildElement file = new JingleFileTransferChildElement(null, null, hash, null, null, -1, null);
|
||||||
|
ChecksumElement checksum = new ChecksumElement(JingleContentElement.Creator.initiator, "name", file);
|
||||||
|
|
||||||
|
String xml = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' creator='initiator' name='name'>" +
|
||||||
|
"<file>" +
|
||||||
|
"<hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>f4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGk=</hash>" +
|
||||||
|
"</file>" +
|
||||||
|
"</checksum>";
|
||||||
|
|
||||||
|
assertXMLEqual(xml, checksum.toXML(null).toString());
|
||||||
|
assertXMLEqual(xml, new ChecksumProvider().parse(TestUtils.getParser(xml)).toXML(null).toString());
|
||||||
|
|
||||||
|
Range range = new Range(12L,34L);
|
||||||
|
file = new JingleFileTransferChildElement(null, null, hash, null, null, -1, range);
|
||||||
|
checksum = new ChecksumElement(JingleContentElement.Creator.initiator, "name", file);
|
||||||
|
|
||||||
|
xml = "<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:5' creator='initiator' name='name'>" +
|
||||||
|
"<file>" +
|
||||||
|
"<range offset='12' length='34'/>" +
|
||||||
|
"<hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>f4OxZX/x/FO5LcGBSKHWXfwtSx+j1ncoSt3SABJtkGk=</hash>" +
|
||||||
|
"</file>" +
|
||||||
|
"</checksum>";
|
||||||
|
assertXMLEqual(xml, checksum.toXML(null).toString());
|
||||||
|
assertXMLEqual(xml, new ChecksumProvider().parse(TestUtils.getParser(xml)).toXML(null).toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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 static junit.framework.TestCase.assertFalse;
|
||||||
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleIncomingFileOffer;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChildElement;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class IncomingFileTransferTest extends SmackTestSuite {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void incomingFileOfferTest() {
|
||||||
|
Date date = new Date();
|
||||||
|
JingleFileTransferChildElement offerElement = new JingleFileTransferChildElement(date, "description", null, "application/octet-stream", "name", 1234, null);
|
||||||
|
JingleIncomingFileOffer offer = new JingleIncomingFileOffer(offerElement);
|
||||||
|
assertTrue(offer.isOffer());
|
||||||
|
assertFalse(offer.isRequest());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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 static junit.framework.TestCase.fail;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleReasonElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.JingleIBBTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.JingleIBBTransportManager;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleFile;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.controller.IncomingFileOfferController;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.controller.OutgoingFileOfferController;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.listener.IncomingFileOfferListener;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.listener.ProgressListener;
|
||||||
|
|
||||||
|
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
|
||||||
|
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
||||||
|
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
||||||
|
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
|
||||||
|
public class JingleFileTransferCancelIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
|
|
||||||
|
public JingleFileTransferCancelIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||||
|
super(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public void setup() {
|
||||||
|
JingleFileTransferManager.getInstanceFor(conOne);
|
||||||
|
JingleIBBTransportManager.getInstanceFor(conOne);
|
||||||
|
ServiceDiscoveryManager.getInstanceFor(conOne).addFeature(JingleIBBTransport.NAMESPACE);
|
||||||
|
JingleFileTransferManager.getInstanceFor(conThree);
|
||||||
|
JingleIBBTransportManager.getInstanceFor(conThree);
|
||||||
|
ServiceDiscoveryManager.getInstanceFor(conThree).addFeature(JingleIBBTransport.NAMESPACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void senderCancelTest() throws Exception {
|
||||||
|
final SimpleResultSyncPoint s1 = new SimpleResultSyncPoint();
|
||||||
|
|
||||||
|
byte[] payload = new byte[320000];
|
||||||
|
new Random().nextBytes(payload);
|
||||||
|
|
||||||
|
JingleFileTransferManager.getInstanceFor(conThree).addIncomingFileOfferListener(new IncomingFileOfferListener() {
|
||||||
|
@Override
|
||||||
|
public void onIncomingFileOffer(IncomingFileOfferController offer) {
|
||||||
|
offer.addProgressListener(new ProgressListener() {
|
||||||
|
@Override
|
||||||
|
public void started() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void terminated(JingleReasonElement.Reason reason) {
|
||||||
|
if (reason == JingleReasonElement.Reason.cancel) {
|
||||||
|
s1.signal();
|
||||||
|
} else {
|
||||||
|
s1.signalFailure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
offer.accept(conThree, new ByteArrayOutputStream());
|
||||||
|
} catch (InterruptedException | IOException | SmackException.NoResponseException | SmackException.NotConnectedException | XMPPException.XMPPErrorException e) {
|
||||||
|
fail(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final OutgoingFileOfferController out = JingleFileTransferManager.getInstanceFor(conOne).sendStream(new ByteArrayInputStream(payload), new JingleFile("name", null, 320000, null, null, null), conThree.getUser().asFullJidOrThrow());
|
||||||
|
out.addProgressListener(new ProgressListener() {
|
||||||
|
@Override
|
||||||
|
public void started() {
|
||||||
|
try {
|
||||||
|
out.cancel(conOne);
|
||||||
|
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||||
|
fail(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void terminated(JingleReasonElement.Reason reason) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
s1.waitForResult(60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void receiverCancelTest() throws Exception {
|
||||||
|
final SimpleResultSyncPoint s1 = new SimpleResultSyncPoint();
|
||||||
|
|
||||||
|
byte[] payload = new byte[320000];
|
||||||
|
new Random().nextBytes(payload);
|
||||||
|
|
||||||
|
JingleFileTransferManager.getInstanceFor(conThree).addIncomingFileOfferListener(new IncomingFileOfferListener() {
|
||||||
|
@Override
|
||||||
|
public void onIncomingFileOffer(final IncomingFileOfferController offer) {
|
||||||
|
offer.addProgressListener(new ProgressListener() {
|
||||||
|
@Override
|
||||||
|
public void started() {
|
||||||
|
try {
|
||||||
|
offer.cancel(conThree);
|
||||||
|
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||||
|
fail(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void terminated(JingleReasonElement.Reason reason) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
offer.accept(conThree, new ByteArrayOutputStream());
|
||||||
|
} catch (InterruptedException | IOException | SmackException.NoResponseException | SmackException.NotConnectedException | XMPPException.XMPPErrorException e) {
|
||||||
|
fail(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final OutgoingFileOfferController out = JingleFileTransferManager.getInstanceFor(conOne).sendStream(new ByteArrayInputStream(payload), new JingleFile("name", null, 320000, null, null, null), conThree.getUser().asFullJidOrThrow());
|
||||||
|
out.addProgressListener(new ProgressListener() {
|
||||||
|
@Override
|
||||||
|
public void started() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void terminated(JingleReasonElement.Reason reason) {
|
||||||
|
if (reason == JingleReasonElement.Reason.cancel) {
|
||||||
|
s1.signal();
|
||||||
|
} else {
|
||||||
|
s1.signalFailure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
s1.waitForResult(60 * 1000);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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 static junit.framework.TestCase.fail;
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleReasonElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.JingleIBBTransportManager;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.controller.IncomingFileOfferController;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.controller.OutgoingFileOfferController;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.listener.IncomingFileOfferListener;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.listener.ProgressListener;
|
||||||
|
|
||||||
|
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
|
||||||
|
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
||||||
|
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
||||||
|
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by vanitas on 29.06.17.
|
||||||
|
*/
|
||||||
|
public class JingleFileTransferIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
|
|
||||||
|
private static final File tempDir;
|
||||||
|
|
||||||
|
static {
|
||||||
|
String userHome = System.getProperty("user.home");
|
||||||
|
if (userHome != null) {
|
||||||
|
File f = new File(userHome);
|
||||||
|
tempDir = new File(f, ".config/smack-integration-test/");
|
||||||
|
} else {
|
||||||
|
tempDir = new File("int_test_jingle");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleFileTransferIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||||
|
super(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void basicFileTransferTest() throws Exception {
|
||||||
|
JingleIBBTransportManager.getInstanceFor(conOne);
|
||||||
|
JingleIBBTransportManager.getInstanceFor(conTwo);
|
||||||
|
|
||||||
|
|
||||||
|
final SimpleResultSyncPoint resultSyncPoint1 = new SimpleResultSyncPoint();
|
||||||
|
final SimpleResultSyncPoint resultSyncPoint2 = new SimpleResultSyncPoint();
|
||||||
|
|
||||||
|
FullJid alice = conOne.getUser().asFullJidOrThrow();
|
||||||
|
FullJid bob = conTwo.getUser().asFullJidOrThrow();
|
||||||
|
|
||||||
|
File source = prepareNewTestFile("source");
|
||||||
|
final File target = new File(tempDir, "target");
|
||||||
|
|
||||||
|
JingleFileTransferManager aftm = JingleFileTransferManager.getInstanceFor(conOne);
|
||||||
|
JingleFileTransferManager bftm = JingleFileTransferManager.getInstanceFor(conTwo);
|
||||||
|
|
||||||
|
bftm.addIncomingFileOfferListener(new IncomingFileOfferListener() {
|
||||||
|
@Override
|
||||||
|
public void onIncomingFileOffer(IncomingFileOfferController offer) {
|
||||||
|
|
||||||
|
offer.addProgressListener(new ProgressListener() {
|
||||||
|
@Override
|
||||||
|
public void started() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void terminated(JingleReasonElement.Reason reason) {
|
||||||
|
if (reason == JingleReasonElement.Reason.success) {
|
||||||
|
resultSyncPoint2.signal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
offer.accept(conTwo, target);
|
||||||
|
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException | IOException e) {
|
||||||
|
fail(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final OutgoingFileOfferController sending = aftm.sendFile(source, bob);
|
||||||
|
|
||||||
|
sending.addProgressListener(new ProgressListener() {
|
||||||
|
@Override
|
||||||
|
public void started() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void terminated(JingleReasonElement.Reason reason) {
|
||||||
|
if (reason == JingleReasonElement.Reason.success) {
|
||||||
|
resultSyncPoint1.signal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
resultSyncPoint1.waitForResult(60 * 1000);
|
||||||
|
resultSyncPoint2.waitForResult(60 * 1000);
|
||||||
|
|
||||||
|
byte[] sBytes = new byte[(int) source.length()];
|
||||||
|
byte[] tBytes = new byte[(int) target.length()];
|
||||||
|
try {
|
||||||
|
FileInputStream fi = new FileInputStream(source);
|
||||||
|
fi.read(sBytes);
|
||||||
|
fi.close();
|
||||||
|
fi = new FileInputStream(target);
|
||||||
|
fi.read(tBytes);
|
||||||
|
} catch (IOException e) {
|
||||||
|
fail("Could not read files: " + e.toString() + " " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
assertArrayEquals(sBytes, tBytes);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File prepareNewTestFile(String name) {
|
||||||
|
File testFile = new File(tempDir, name);
|
||||||
|
try {
|
||||||
|
if (!testFile.exists()) {
|
||||||
|
testFile.createNewFile();
|
||||||
|
}
|
||||||
|
FileOutputStream fo = new FileOutputStream(testFile);
|
||||||
|
byte[] rand = new byte[16000];
|
||||||
|
INSECURE_RANDOM.nextBytes(rand);
|
||||||
|
fo.write(rand);
|
||||||
|
fo.close();
|
||||||
|
return testFile;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void cleanup() {
|
||||||
|
Socks5Proxy.getSocks5Proxy().stop();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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 static junit.framework.TestCase.fail;
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleReasonElement;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.JingleIBBTransportManager;
|
||||||
|
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.JingleS5BTransportManager;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.controller.IncomingFileOfferController;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.controller.OutgoingFileOfferController;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.listener.IncomingFileOfferListener;
|
||||||
|
import org.jivesoftware.smackx.jingle_filetransfer.listener.ProgressListener;
|
||||||
|
|
||||||
|
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
|
||||||
|
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
||||||
|
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
||||||
|
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
||||||
|
public class JingleFileTransferTransportFallbackIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
|
|
||||||
|
private static final File tempDir;
|
||||||
|
|
||||||
|
static {
|
||||||
|
String userHome = System.getProperty("user.home");
|
||||||
|
if (userHome != null) {
|
||||||
|
File f = new File(userHome);
|
||||||
|
tempDir = new File(f, ".config/smack-integration-test/");
|
||||||
|
} else {
|
||||||
|
tempDir = new File("int_test_jingle");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleFileTransferTransportFallbackIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||||
|
super(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void crippleS5B() {
|
||||||
|
// Manipulate the Manager so that it'll fail.
|
||||||
|
JingleS5BTransportManager.useExternalCandidates = false;
|
||||||
|
JingleS5BTransportManager.useLocalCandidates = false;
|
||||||
|
// *evil super villain laughter*
|
||||||
|
}
|
||||||
|
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void S5BtoIBBfallbackTest() throws Exception {
|
||||||
|
|
||||||
|
JingleS5BTransportManager.getInstanceFor(conOne);
|
||||||
|
JingleS5BTransportManager.getInstanceFor(conTwo);
|
||||||
|
|
||||||
|
// Use Jingle IBB Transport as fallback.
|
||||||
|
JingleIBBTransportManager.getInstanceFor(conOne);
|
||||||
|
JingleIBBTransportManager.getInstanceFor(conTwo);
|
||||||
|
|
||||||
|
|
||||||
|
final SimpleResultSyncPoint resultSyncPoint1 = new SimpleResultSyncPoint();
|
||||||
|
final SimpleResultSyncPoint resultSyncPoint2 = new SimpleResultSyncPoint();
|
||||||
|
|
||||||
|
FullJid alice = conOne.getUser().asFullJidOrThrow();
|
||||||
|
FullJid bob = conTwo.getUser().asFullJidOrThrow();
|
||||||
|
|
||||||
|
File source = prepareNewTestFile("source");
|
||||||
|
final File target = new File(tempDir, "target");
|
||||||
|
|
||||||
|
JingleFileTransferManager aftm = JingleFileTransferManager.getInstanceFor(conOne);
|
||||||
|
JingleFileTransferManager bftm = JingleFileTransferManager.getInstanceFor(conTwo);
|
||||||
|
|
||||||
|
bftm.addIncomingFileOfferListener(new IncomingFileOfferListener() {
|
||||||
|
@Override
|
||||||
|
public void onIncomingFileOffer(IncomingFileOfferController offer) {
|
||||||
|
offer.addProgressListener(new ProgressListener() {
|
||||||
|
@Override
|
||||||
|
public void started() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void terminated(JingleReasonElement.Reason reason) {
|
||||||
|
if (reason == JingleReasonElement.Reason.success) {
|
||||||
|
resultSyncPoint2.signal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
offer.accept(conTwo, target);
|
||||||
|
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException | IOException e) {
|
||||||
|
fail(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final OutgoingFileOfferController sending = aftm.sendFile(source, bob);
|
||||||
|
|
||||||
|
sending.addProgressListener(new ProgressListener() {
|
||||||
|
@Override
|
||||||
|
public void started() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void terminated(JingleReasonElement.Reason reason) {
|
||||||
|
if (reason == JingleReasonElement.Reason.success) {
|
||||||
|
resultSyncPoint1.signal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
resultSyncPoint1.waitForResult(60 * 1000);
|
||||||
|
resultSyncPoint2.waitForResult(60 * 1000);
|
||||||
|
|
||||||
|
byte[] sBytes = new byte[(int) source.length()];
|
||||||
|
byte[] tBytes = new byte[(int) target.length()];
|
||||||
|
try {
|
||||||
|
FileInputStream fi = new FileInputStream(source);
|
||||||
|
fi.read(sBytes);
|
||||||
|
fi.close();
|
||||||
|
fi = new FileInputStream(target);
|
||||||
|
fi.read(tBytes);
|
||||||
|
} catch (IOException e) {
|
||||||
|
fail("Could not read files: " + e.toString() + " " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
assertArrayEquals(sBytes, tBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cureS5B() {
|
||||||
|
JingleS5BTransportManager.useExternalCandidates = true;
|
||||||
|
JingleS5BTransportManager.useLocalCandidates = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File prepareNewTestFile(String name) {
|
||||||
|
File testFile = new File(tempDir, name);
|
||||||
|
try {
|
||||||
|
if (!testFile.exists()) {
|
||||||
|
testFile.createNewFile();
|
||||||
|
}
|
||||||
|
FileOutputStream fo = new FileOutputStream(testFile);
|
||||||
|
byte[] rand = new byte[16000];
|
||||||
|
INSECURE_RANDOM.nextBytes(rand);
|
||||||
|
fo.write(rand);
|
||||||
|
fo.close();
|
||||||
|
return testFile;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public void cleanup() {
|
||||||
|
Socks5Proxy.getSocks5Proxy().stop();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for XEP-0234 - Jingle File Transfer.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.jingle_filetransfer;
|
Loading…
Reference in New Issue