diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java index 63dbadf58..d99317682 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java @@ -299,6 +299,19 @@ public final class InBandBytestreamManager extends Manager implements Bytestream this.ignoredBytestreamRequests.add(sessionID); } + /** + * Use this method to clear the next incoming In-Band Bytestream request containing the given + * session ID. + *
+ * This method should be used if you are awaiting an In-Band Bytestream request as a reply to + * another stanza (e.g. file transfer). + * + * @param sessionID to be be cleared + */ + public void removeIgnoredBytestreamRequest(String sessionID) { + this.ignoredBytestreamRequests.remove(sessionID); + } + /** * Returns the default block size that is used for all outgoing in-band bytestreams for this * connection. diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java index 3ff082df2..23a92175e 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSession.java @@ -216,18 +216,21 @@ public class InBandBytestreamSession implements BytestreamSession { if (this.inputStream.isClosed && this.outputStream.isClosed) { this.isClosed = true; - // send close request - Close close = new Close(this.byteStreamRequest.getSessionID()); - close.setTo(this.remoteJID); - try { - connection.createStanzaCollectorAndSend(close).nextResultOrThrow(); - } - catch (Exception e) { - // Sadly we are unable to use the IOException(Throwable) constructor because this - // constructor is only supported from Android API 9 on. - IOException ioException = new IOException(); - ioException.initCause(e); - throw ioException; + // Do not send close stream IQ if on receive, otherwiese XMPPError: item-not-found - cancel + if (!in) { + // send close request + Close close = new Close(this.byteStreamRequest.getSessionID()); + close.setTo(this.remoteJID); + try { + connection.createStanzaCollectorAndSend(close).nextResultOrThrow(); + } + catch (Exception e) { + // Sadly we are unable to use the IOException(Throwable) constructor because this + // constructor is only supported from Android API 9 on. + IOException ioException = new IOException(); + ioException.initCause(e); + throw ioException; + } } this.inputStream.cleanup(); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FaultTolerantNegotiator.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FaultTolerantNegotiator.java index 5fc7bf6c3..8dc34a9bf 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FaultTolerantNegotiator.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FaultTolerantNegotiator.java @@ -65,11 +65,20 @@ public class FaultTolerantNegotiator extends StreamNegotiator { @Override public InputStream createIncomingStream(final StreamInitiation initiation) throws SmackException, XMPPErrorException, InterruptedException { // This could be either an xep47 ibb 'open' iq or an xep65 streamhost iq + InputStream inputStream = null; IQ initiationSet = initiateIncomingStream(connection(), initiation); StreamNegotiator streamNegotiator = determineNegotiator(initiationSet); - return streamNegotiator.negotiateIncomingStream(initiationSet); + try { + inputStream = streamNegotiator.negotiateIncomingStream(initiationSet); + } catch (Exception ex) { + if ((streamNegotiator instanceof Socks5TransferNegotiator) + && (secondaryNegotiator instanceof IBBTransferNegotiator)) { + inputStream = ((IBBTransferNegotiator) secondaryNegotiator).getIbbIncomingStream(initiation); + } + } + return inputStream; } private StreamNegotiator determineNegotiator(Stanza streamInitiation) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/IBBTransferNegotiator.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/IBBTransferNegotiator.java index ed7c328c5..3f00e0482 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/IBBTransferNegotiator.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/IBBTransferNegotiator.java @@ -18,13 +18,16 @@ package org.jivesoftware.smackx.filetransfer; import java.io.InputStream; import java.io.OutputStream; +import java.util.logging.Logger; +import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.packet.Stanza; +import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamListener; import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamRequest; import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession; @@ -46,6 +49,7 @@ import org.jxmpp.jid.Jid; */ public class IBBTransferNegotiator extends StreamNegotiator { + private static final Logger LOGGER = Logger.getLogger(IBBTransferNegotiator.class.getName()); private final InBandBytestreamManager manager; /** @@ -114,7 +118,42 @@ public class IBBTransferNegotiator extends StreamNegotiator { private ByteStreamRequest(InBandBytestreamManager manager, Open byteStreamRequest) { super(manager, byteStreamRequest); } - } + InputStream getIbbIncomingStream(final StreamInitiation initiation) throws InterruptedException, SmackException { + + IbbBytestreamListener listener = new IbbBytestreamListener(); + manager.removeIgnoredBytestreamRequest(initiation.getSessionID()); + manager.addIncomingBytestreamListener(listener); + return listener.getStream(); + } + + private static class IbbBytestreamListener extends InBandBytestreamListener { + + InputStream inputStream = null; + + public InputStream getStream() throws InterruptedException, SmackException { + + int wait = 50; // wait for 10 seconds + do { + Thread.sleep(200); + if (wait-- < 0) { + throw new SmackException("IBB fallback incoming stream wait timed out"); + } + } while (inputStream == null); + return inputStream; + } + + @Override + public void incomingBytestreamRequest(InBandBytestreamRequest request) { + + try { + InBandBytestreamSession session = request.accept(); + session.setCloseBothStreamsEnabled(false); + inputStream = session.getInputStream(); + } catch (SmackException.NotConnectedException | InterruptedException e) { + LOGGER.warning("Error in InBand Bytestream Request: " + e.getMessage()); + } + } + } }