Cancel declines file offers
This commit is contained in:
parent
a08e835a52
commit
6c51424949
|
@ -20,7 +20,12 @@ 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.component.JingleSession;
|
||||
import org.jivesoftware.smackx.jingle.element.JingleElement;
|
||||
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;
|
||||
|
@ -33,9 +38,6 @@ public abstract class JingleFileTransfer extends JingleDescription<JingleFileTra
|
|||
public static final String NAMESPACE_V5 = "urn:xmpp:jingle:apps:file-transfer:5";
|
||||
public static final String NAMESPACE = NAMESPACE_V5;
|
||||
|
||||
public abstract boolean isOffer();
|
||||
public abstract boolean isRequest();
|
||||
|
||||
protected State state;
|
||||
protected JingleFileTransferFile file;
|
||||
|
||||
|
@ -45,6 +47,10 @@ public abstract class JingleFileTransfer extends JingleDescription<JingleFileTra
|
|||
this.file = file;
|
||||
}
|
||||
|
||||
public abstract boolean isOffer();
|
||||
|
||||
public abstract boolean isRequest();
|
||||
|
||||
@Override
|
||||
public void addProgressListener(ProgressListener listener) {
|
||||
progressListeners.add(listener);
|
||||
|
@ -57,8 +63,24 @@ public abstract class JingleFileTransfer extends JingleDescription<JingleFileTra
|
|||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
//TODO
|
||||
public void cancel(XMPPConnection connection) throws SmackException.NotConnectedException, InterruptedException {
|
||||
JingleSession session = getParent().getParent();
|
||||
switch (state) {
|
||||
case pending:
|
||||
if (session.isResponder()) {
|
||||
connection.createStanzaCollectorAndSend(JingleElement.createSessionTerminate(session.getPeer(), session.getSessionId(), JingleReasonElement.Reason.decline));
|
||||
} else {
|
||||
connection.createStanzaCollectorAndSend(JingleElement.createSessionTerminate(session.getPeer(), session.getSessionId(), JingleReasonElement.Reason.cancel));
|
||||
}
|
||||
break;
|
||||
|
||||
case active:
|
||||
connection.createStanzaCollectorAndSend(JingleElement.createSessionTerminate(session.getPeer(), session.getSessionId(), JingleReasonElement.Reason.cancel));
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
getParent().onContentCancel();
|
||||
}
|
||||
|
||||
public void notifyProgressListeners(float progress) {
|
||||
|
|
|
@ -21,6 +21,9 @@ 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;
|
||||
|
||||
|
@ -28,6 +31,8 @@ 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;
|
||||
|
@ -45,6 +50,7 @@ public class JingleIncomingFileOffer extends AbstractJingleFileOffer implements
|
|||
|
||||
public JingleIncomingFileOffer(JingleFileTransferChildElement offer) {
|
||||
super(new JingleFileTransferFile.RemoteFile(offer));
|
||||
this.state = State.pending;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -58,12 +64,25 @@ public class JingleIncomingFileOffer extends AbstractJingleFileOffer implements
|
|||
throw new IllegalStateException("Target OutputStream is null");
|
||||
}
|
||||
|
||||
state = State.active;
|
||||
|
||||
HashElement hashElement = file.getHashElement();
|
||||
MessageDigest digest = null;
|
||||
if (hashElement != null) {
|
||||
digest = HashManager.getMessageDigest(hashElement.getAlgorithm());
|
||||
LOGGER.log(Level.INFO, "File offer had checksum: " + digest.toString());
|
||||
}
|
||||
|
||||
LOGGER.log(Level.INFO, "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];
|
||||
|
@ -79,6 +98,7 @@ public class JingleIncomingFileOffer extends AbstractJingleFileOffer implements
|
|||
} catch (IOException e) {
|
||||
LOGGER.log(Level.SEVERE, "Cannot get InputStream from BytestreamSession: " + e, e);
|
||||
} finally {
|
||||
state = State.ended;
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
|
@ -97,7 +117,17 @@ public class JingleIncomingFileOffer extends AbstractJingleFileOffer implements
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.INFO, "CHECKSUM MATCHED :)");
|
||||
}
|
||||
}
|
||||
notifyProgressListenersFinished();
|
||||
getParent().onContentFinished();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -114,6 +144,7 @@ public class JingleIncomingFileOffer extends AbstractJingleFileOffer implements
|
|||
public void accept(XMPPConnection connection, File target)
|
||||
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
|
||||
SmackException.NoResponseException, IOException {
|
||||
state = State.negotiating;
|
||||
|
||||
if (!target.exists()) {
|
||||
target.createNewFile();
|
||||
|
@ -131,6 +162,8 @@ public class JingleIncomingFileOffer extends AbstractJingleFileOffer implements
|
|||
public void accept(XMPPConnection connection, OutputStream stream)
|
||||
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
|
||||
SmackException.NoResponseException {
|
||||
state = State.negotiating;
|
||||
|
||||
target = stream;
|
||||
|
||||
JingleSession session = getParent().getParent();
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
*/
|
||||
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.JingleFileTransferFile;
|
||||
import org.jivesoftware.smackx.jingle_filetransfer.listener.ProgressListener;
|
||||
|
@ -31,5 +33,5 @@ public interface JingleFileTransferController extends JingleDescriptionControlle
|
|||
|
||||
JingleFileTransferFile getFile();
|
||||
|
||||
void cancel();
|
||||
void cancel(XMPPConnection connection) throws SmackException.NotConnectedException, InterruptedException;
|
||||
}
|
||||
|
|
|
@ -94,10 +94,10 @@ public final class JingleManager extends Manager {
|
|||
// We have not seen this session before.
|
||||
// Either it is fresh, or unknown.
|
||||
if (session == null) {
|
||||
LOGGER.log(Level.INFO, connection().getUser().asFullJidOrThrow() + " received unknown session: " + jingle.getFrom().asFullJidOrThrow() + " " + jingle.getSid());
|
||||
if (jingle.getAction() == JingleAction.session_initiate) {
|
||||
//fresh. phew!
|
||||
try {
|
||||
LOGGER.log(Level.INFO, "Create new session with " + jingle.getFrom() + ": " + jingle.getSid());
|
||||
session = JingleSession.fromSessionInitiate(JingleManager.this, jingle);
|
||||
jingleSessions.put(fullJidAndSessionId, session);
|
||||
} catch (UnsupportedDescriptionException e) {
|
||||
|
@ -113,6 +113,7 @@ public final class JingleManager extends Manager {
|
|||
|
||||
} else {
|
||||
// Unknown session. Error!
|
||||
LOGGER.log(Level.INFO, connection().getUser().asFullJidOrThrow() + " received unknown session: " + jingle.getFrom().asFullJidOrThrow() + " " + jingle.getSid());
|
||||
return JingleElement.createJingleErrorUnknownSession(jingle);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -178,6 +178,7 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
|
|||
}
|
||||
|
||||
public void handleContentRemove(JingleSession session, XMPPConnection connection) {
|
||||
|
||||
}
|
||||
|
||||
private IQ handleSecurityInfo(JingleElement request, XMPPConnection connection) {
|
||||
|
@ -210,17 +211,22 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
|
|||
return transport.handleTransportInfo(content.getTransport().getInfo(), request);
|
||||
}
|
||||
|
||||
private IQ handleTransportReject(JingleElement request, XMPPConnection connection) {
|
||||
private IQ handleTransportReject(JingleElement request, final XMPPConnection connection) {
|
||||
if (pendingReplacingTransport == null) {
|
||||
throw new AssertionError("We didn't try to replace the transport.");
|
||||
}
|
||||
transportBlacklist.add(pendingReplacingTransport.getNamespace());
|
||||
pendingReplacingTransport = null;
|
||||
try {
|
||||
replaceTransport(transportBlacklist, connection);
|
||||
} catch (SmackException.NotConnectedException | SmackException.NoResponseException | XMPPException.XMPPErrorException | InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, "Could not replace transport: " + e, e);
|
||||
}
|
||||
Async.go(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
transportBlacklist.add(pendingReplacingTransport.getNamespace());
|
||||
pendingReplacingTransport = null;
|
||||
try {
|
||||
replaceTransport(transportBlacklist, connection);
|
||||
} catch (SmackException.NotConnectedException | SmackException.NoResponseException | XMPPException.XMPPErrorException | InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, "Could not replace transport: " + e, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
return IQ.createResultIQ(request);
|
||||
}
|
||||
|
||||
|
@ -460,6 +466,20 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal
|
|||
LOGGER.log(Level.SEVERE, "Security failed: " + e, e);
|
||||
}
|
||||
|
||||
public void onContentFinished() {
|
||||
JingleSession session = getParent();
|
||||
session.onContentFinished(this);
|
||||
}
|
||||
|
||||
public void onContentFailed(Exception e) {
|
||||
|
||||
}
|
||||
|
||||
public void onContentCancel() {
|
||||
JingleSession session = getParent();
|
||||
session.onContentCancel(this);
|
||||
}
|
||||
|
||||
private void replaceTransport(Set<String> blacklist, XMPPConnection connection)
|
||||
throws SmackException.NotConnectedException, InterruptedException,
|
||||
XMPPException.XMPPErrorException, SmackException.NoResponseException {
|
||||
|
|
|
@ -145,6 +145,58 @@ public class JingleSession {
|
|||
return JingleElement.createSessionAccept(getInitiator(), getResponder(), getSessionId(), contentElements);
|
||||
}
|
||||
|
||||
void onContentFinished(JingleContent jingleContent) {
|
||||
if (contents.get(jingleContent.getName()) == null) {
|
||||
LOGGER.log(Level.WARNING, "Session does not contain content " + jingleContent.getName() + ". Ignore contentFinished.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (contents.size() == 1) {
|
||||
//Only content has finished. End session.
|
||||
terminateSession(JingleReasonElement.Reason.success);
|
||||
return;
|
||||
}
|
||||
|
||||
// Session has still active contents left.
|
||||
/*
|
||||
try {
|
||||
jingleManager.getConnection().createStanzaCollectorAndSend(JingleElement.createSessionTerminateContentCancel(
|
||||
getPeer(), getSessionId(), jingleContent.getCreator(), jingleContent.getName()));
|
||||
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, "Could not send content-cancel: " + e, e);
|
||||
}
|
||||
contents.remove(jingleContent.getName());
|
||||
*/
|
||||
}
|
||||
|
||||
void onContentCancel(JingleContent jingleContent) {
|
||||
if (contents.get(jingleContent.getName()) == null) {
|
||||
LOGGER.log(Level.WARNING, "Session does not contain content " + jingleContent.getName() + ". Ignore onContentCancel.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (contents.size() == 1) {
|
||||
terminateSession(JingleReasonElement.Reason.cancel);
|
||||
jingleManager.removeSession(this);
|
||||
} else {
|
||||
try {
|
||||
jingleManager.getConnection().createStanzaCollectorAndSend(JingleElement.createSessionTerminateContentCancel(getPeer(), getSessionId(), jingleContent.getCreator(), jingleContent.getName()));
|
||||
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, "Could not send content-cancel: " + e, e);
|
||||
}
|
||||
contents.remove(jingleContent.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public void terminateSession(JingleReasonElement.Reason reason) {
|
||||
try {
|
||||
jingleManager.getConnection().createStanzaCollectorAndSend(JingleElement.createSessionTerminate(getPeer(), getSessionId(), reason));
|
||||
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, "Could not send session-terminate: " + e, e);
|
||||
}
|
||||
jingleManager.removeSession(this);
|
||||
}
|
||||
|
||||
public IQ handleJingleRequest(JingleElement request) {
|
||||
switch (request.getAction()) {
|
||||
case content_modify:
|
||||
|
@ -198,17 +250,27 @@ public class JingleSession {
|
|||
}
|
||||
|
||||
private IQ handleSessionInitiate(JingleElement request) {
|
||||
JingleDescription<?> description = getSoleContentOrThrow().getDescription();
|
||||
final JingleDescription<?> description = getSoleContentOrThrow().getDescription();
|
||||
final JingleDescriptionManager descriptionManager = jingleManager.getDescriptionManager(description.getNamespace());
|
||||
|
||||
if (descriptionManager == null) {
|
||||
LOGGER.log(Level.WARNING, "Unsupported description type: " + description.getNamespace());
|
||||
return JingleElement.createSessionTerminate(getPeer(), getSessionId(), JingleReasonElement.Reason.unsupported_applications);
|
||||
|
||||
}
|
||||
Async.go(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
descriptionManager.notifySessionInitiate(JingleSession.this);
|
||||
if (descriptionManager == null) {
|
||||
|
||||
LOGGER.log(Level.WARNING, "Unsupported description type: " + description.getNamespace());
|
||||
try {
|
||||
jingleManager.getConnection().createStanzaCollectorAndSend(JingleElement.createSessionTerminate(getPeer(), getSessionId(), JingleReasonElement.Reason.unsupported_applications));
|
||||
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||
LOGGER.log(Level.SEVERE, "Could not send session-terminate: " + e, e);
|
||||
}
|
||||
|
||||
} else {
|
||||
descriptionManager.notifySessionInitiate(JingleSession.this);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue