Cancel declines file offers

This commit is contained in:
vanitasvitae 2017-08-20 13:46:30 +02:00
parent a08e835a52
commit 6c51424949
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
6 changed files with 159 additions and 19 deletions

View File

@ -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) {

View File

@ -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();

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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 {

View File

@ -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);
}
}
});