Smack/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/IncomingJingleFileOffer.java

258 lines
11 KiB
Java
Raw Normal View History

2017-06-18 16:47:49 +02:00
/**
*
* 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;
2017-06-19 14:44:35 +02:00
import java.io.File;
2017-07-11 13:30:17 +02:00
import java.util.concurrent.Future;
2017-06-19 15:26:10 +02:00
import java.util.logging.Level;
import java.util.logging.Logger;
2017-06-19 14:44:35 +02:00
2017-06-19 15:26:10 +02:00
import org.jivesoftware.smack.SmackException;
2017-06-19 14:44:35 +02:00
import org.jivesoftware.smack.XMPPConnection;
2017-06-19 15:26:10 +02:00
import org.jivesoftware.smack.XMPPException;
2017-06-19 14:44:35 +02:00
import org.jivesoftware.smack.packet.IQ;
2017-06-21 16:11:13 +02:00
import org.jivesoftware.smackx.bytestreams.BytestreamSession;
import org.jivesoftware.smackx.jingle.JingleManager;
2017-06-19 15:26:10 +02:00
import org.jivesoftware.smackx.jingle.JingleTransportMethodManager;
2017-06-18 16:47:49 +02:00
import org.jivesoftware.smackx.jingle.Role;
2017-07-19 15:17:12 +02:00
import org.jivesoftware.smackx.jingle3.element.JingleContentElement;
import org.jivesoftware.smackx.jingle3.element.JingleElement;
import org.jivesoftware.smackx.jingle3.element.JingleReasonElement;
2017-06-21 16:11:13 +02:00
import org.jivesoftware.smackx.jingle.transports.JingleTransportInitiationCallback;
2017-06-23 22:48:28 +02:00
import org.jivesoftware.smackx.jingle.transports.JingleTransportManager;
2017-06-19 14:44:35 +02:00
import org.jivesoftware.smackx.jingle_filetransfer.callback.IncomingFileOfferCallback;
2017-06-19 19:22:59 +02:00
import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransfer;
import org.jivesoftware.smackx.jingle_filetransfer.handler.FileTransferHandler;
2017-06-18 16:47:49 +02:00
import org.jxmpp.jid.FullJid;
/**
* We are the responder and we are the recipient.
2017-06-18 16:47:49 +02:00
*/
public class IncomingJingleFileOffer extends JingleFileTransferSession implements IncomingFileOfferCallback {
private static final Logger LOGGER = Logger.getLogger(IncomingJingleFileOffer.class.getName());
2017-07-19 15:17:12 +02:00
private JingleElement pendingSessionInitiate = null;
2017-06-23 23:41:40 +02:00
private ReceiveTask receivingThread;
private File target;
2017-06-21 14:11:42 +02:00
@Override
public void cancel() {
2017-07-11 13:30:17 +02:00
if (state == State.active) {
Future<?> task = queued.get(0);
if (task != null) {
task.cancel(true);
queued.remove(task);
}
2017-07-19 15:17:12 +02:00
notifyEndedListeners(JingleReasonElement.Reason.cancel);
2017-07-11 13:30:17 +02:00
}
}
2017-06-21 14:11:42 +02:00
public enum State {
fresh,
pending,
sent_transport_replace,
active,
2017-06-23 22:48:28 +02:00
terminated
2017-06-21 14:11:42 +02:00
}
private State state;
2017-06-19 15:26:10 +02:00
public IncomingJingleFileOffer(XMPPConnection connection, FullJid initiator, String sid) {
super(connection, initiator, connection.getUser().asFullJidOrThrow(), Role.responder, sid, Type.offer);
2017-06-21 18:03:31 +02:00
state = State.fresh;
2017-06-19 14:44:35 +02:00
}
2017-07-19 15:17:12 +02:00
public IncomingJingleFileOffer(XMPPConnection connection, JingleElement request) {
this(connection, request.getInitiator(), request.getSid());
2017-06-19 14:44:35 +02:00
}
@Override
2017-07-19 15:17:12 +02:00
public IQ handleSessionInitiate(final JingleElement initiate)
2017-06-24 17:46:03 +02:00
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
SmackException.NoResponseException {
2017-06-21 14:11:42 +02:00
JingleTransportMethodManager tm = JingleTransportMethodManager.getInstanceFor(connection);
2017-06-19 14:44:35 +02:00
2017-06-21 14:11:42 +02:00
if (state != State.fresh) {
//Out of order (initiate after accept)
2017-06-22 14:47:39 +02:00
LOGGER.log(Level.WARNING, "Action " + initiate.getAction() + " is out of order!");
2017-06-19 14:44:35 +02:00
return jutil.createErrorOutOfOrder(initiate);
}
2017-06-24 17:46:03 +02:00
this.contents.addAll(initiate.getContents());
this.file = (JingleFileTransfer) contents.get(0).getDescription();
2017-06-21 14:11:42 +02:00
2017-06-23 22:48:28 +02:00
JingleTransportManager<?> transportManager = tm.getTransportManager(initiate);
2017-06-21 14:11:42 +02:00
if (transportManager == null) {
2017-06-21 15:54:20 +02:00
//Try fallback.
2017-06-21 14:11:42 +02:00
pendingSessionInitiate = initiate;
transportManager = tm.getBestAvailableTransportManager();
if (transportManager == null) {
2017-06-21 15:54:20 +02:00
//No usable transports.
2017-06-22 14:47:39 +02:00
LOGGER.log(Level.WARNING, "No usable transports.");
2017-07-17 20:18:08 +02:00
connection.createStanzaCollectorAndSend(jutil.createSessionTerminateUnsupportedTransports(getInitiator(), getSessionId()));
2017-06-21 14:11:42 +02:00
state = State.terminated;
return jutil.createAck(initiate);
}
2017-06-23 22:48:28 +02:00
transportSession = transportManager.transportSession(this);
2017-06-24 17:46:03 +02:00
jutil.sendTransportReplace(initiate.getFrom().asFullJidOrThrow(), getInitiator(),
getSessionId(), contents.get(0).getCreator(), contents.get(0).getName(),
transportSession.createTransport());
2017-06-21 14:11:42 +02:00
state = State.sent_transport_replace;
return jutil.createAck(initiate);
}
2017-06-19 19:22:59 +02:00
2017-06-23 22:48:28 +02:00
transportSession = transportManager.transportSession(this);
2017-06-24 17:46:03 +02:00
transportSession.processJingle(initiate);
2017-06-23 22:48:28 +02:00
2017-06-21 14:11:42 +02:00
state = State.pending;
2017-06-24 17:46:03 +02:00
JingleFileTransferManager.getInstanceFor(connection).notifyIncomingFileOffer(initiate,
IncomingJingleFileOffer.this);
2017-06-19 15:26:10 +02:00
return jutil.createAck(initiate);
}
2017-06-19 14:44:35 +02:00
@Override
2017-07-19 15:17:12 +02:00
public IQ handleTransportReplace(final JingleElement transportReplace)
throws InterruptedException, XMPPException.XMPPErrorException,
SmackException.NotConnectedException, SmackException.NoResponseException {
final JingleTransportManager<?> replacementManager = JingleTransportMethodManager.getInstanceFor(connection)
.getTransportManager(transportReplace);
queued.add(JingleManager.getThreadPool().submit(new Runnable() {
@Override
public void run() {
try {
if (replacementManager != null) {
LOGGER.log(Level.INFO, "Accept transport-replace.");
IncomingJingleFileOffer.this.transportSession = replacementManager.transportSession(IncomingJingleFileOffer.this);
transportSession.processJingle(transportReplace);
transportSession.initiateIncomingSession(new JingleTransportInitiationCallback() {
@Override
public void onSessionInitiated(BytestreamSession bytestreamSession) {
LOGGER.log(Level.INFO, "Bytestream initiated. Start receiving.");
receivingThread = new ReceiveTask(IncomingJingleFileOffer.this, bytestreamSession, file, target);
queued.add(JingleManager.getThreadPool().submit(receivingThread));
}
@Override
public void onException(Exception e) {
LOGGER.log(Level.SEVERE, "EXCEPTION IN INCOMING SESSION: ", e);
}
});
jutil.sendTransportAccept(transportReplace.getFrom().asFullJidOrThrow(),
transportReplace.getInitiator(), transportReplace.getSid(),
getContents().get(0).getCreator(), getContents().get(0).getName(),
transportSession.createTransport());
} else {
LOGGER.log(Level.INFO, "Unsupported transport. Reject transport-replace.");
jutil.sendTransportReject(transportReplace.getFrom().asFullJidOrThrow(), transportReplace.getInitiator(),
transportReplace.getSid(), getContents().get(0).getCreator(),
2017-07-17 20:18:08 +02:00
getContents().get(0).getName(), transportReplace.getContents().get(0).getTransport());
}
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
LOGGER.log(Level.SEVERE, "Help me please!", e);
}
}
}));
return jutil.createAck(transportReplace);
}
2017-06-19 15:26:10 +02:00
@Override
2017-07-19 15:17:12 +02:00
public IQ handleTransportAccept(JingleElement transportAccept) {
2017-06-22 14:47:39 +02:00
LOGGER.log(Level.INFO, "Received transport-accept.");
2017-06-21 14:11:42 +02:00
if (state != State.sent_transport_replace) {
2017-06-22 14:47:39 +02:00
LOGGER.log(Level.WARNING, "Session is in state " + state + ", so the transport-accept is out of order.");
2017-06-21 14:11:42 +02:00
return jutil.createErrorOutOfOrder(transportAccept);
}
2017-06-19 14:44:35 +02:00
2017-06-24 17:46:03 +02:00
JingleFileTransferManager.getInstanceFor(connection)
.notifyIncomingFileOffer(pendingSessionInitiate, this);
transportSession.processJingle(transportAccept);
2017-06-21 14:11:42 +02:00
state = State.pending;
return jutil.createAck(transportAccept);
}
2017-06-29 21:53:57 +02:00
@Override
public void onTransportMethodFailed(String namespace) {
//Nothing to do.
}
2017-06-21 14:11:42 +02:00
@Override
2017-07-19 15:17:12 +02:00
public FileTransferHandler acceptIncomingFileOffer(final JingleElement request, final File target) {
this.target = target;
2017-06-22 14:47:39 +02:00
LOGGER.log(Level.INFO, "Client accepted incoming file offer. Try to start receiving.");
2017-06-23 22:48:28 +02:00
if (transportSession == null) {
2017-06-19 15:26:10 +02:00
//Unsupported transport
LOGGER.log(Level.WARNING, "Unsupported Transport method.");
try {
2017-06-21 14:11:42 +02:00
jutil.sendSessionTerminateUnsupportedTransports(request.getFrom().asFullJidOrThrow(), sid);
2017-06-24 17:46:03 +02:00
} catch (InterruptedException | SmackException.NoResponseException |
SmackException.NotConnectedException | XMPPException.XMPPErrorException e) {
2017-06-19 15:26:10 +02:00
LOGGER.log(Level.SEVERE, "Could not send session-terminate: " + e, e);
2017-06-19 14:44:35 +02:00
}
return null;
2017-06-19 15:26:10 +02:00
}
2017-06-24 12:14:05 +02:00
state = State.active;
2017-06-27 20:27:38 +02:00
try {
jutil.sendSessionAccept(getInitiator(), sid, getContents().get(0).getCreator(),
2017-07-19 15:17:12 +02:00
getContents().get(0).getName(), JingleContentElement.Senders.initiator, file,
2017-06-27 20:27:38 +02:00
transportSession.createTransport());
} catch (SmackException.NotConnectedException | SmackException.NoResponseException |
XMPPException.XMPPErrorException | InterruptedException e) {
LOGGER.log(Level.WARNING, "Could not send session-accept.", e);
}
2017-06-24 12:14:05 +02:00
transportSession.initiateIncomingSession(new JingleTransportInitiationCallback() {
2017-06-22 14:47:39 +02:00
@Override
2017-06-24 12:14:05 +02:00
public void onSessionInitiated(BytestreamSession bytestreamSession) {
2017-06-26 23:18:34 +02:00
LOGGER.log(Level.INFO, "Bytestream initiated. Start receiving.");
receivingThread = new ReceiveTask(IncomingJingleFileOffer.this, bytestreamSession, file, target);
queued.add(JingleManager.getThreadPool().submit(receivingThread));
started = true;
notifyStartedListeners();
2017-06-24 12:14:05 +02:00
}
@Override
public void onException(Exception e) {
2017-06-27 20:27:38 +02:00
LOGGER.log(Level.SEVERE, "EXCEPTION IN INCOMING SESSION: ", e);
2017-06-22 14:47:39 +02:00
}
2017-06-24 12:14:05 +02:00
});
return this;
2017-06-19 15:26:10 +02:00
}
@Override
2017-07-19 15:17:12 +02:00
public void declineIncomingFileOffer(JingleElement request) {
2017-06-21 14:11:42 +02:00
state = State.terminated;
2017-06-19 15:26:10 +02:00
try {
jutil.sendSessionTerminateDecline(request.getInitiator(), request.getSid());
2017-06-24 17:46:03 +02:00
} catch (SmackException.NotConnectedException | SmackException.NoResponseException |
XMPPException.XMPPErrorException | InterruptedException e) {
2017-06-19 15:26:10 +02:00
LOGGER.log(Level.SEVERE, "Could not send session-terminate: " + e, e);
}
}
2017-06-18 16:47:49 +02:00
}