diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/component/JingleContent.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/component/JingleContent.java index 3026667e8..537bef5d1 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/component/JingleContent.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/component/JingleContent.java @@ -67,41 +67,6 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal private final List callbacks = Collections.synchronizedList(new ArrayList()); private final Set transportBlacklist = Collections.synchronizedSet(new HashSet()); - private short state; - - public enum STATE { - pending_accept((short) 1), - pending_transmission_start((short) 2), - pending_transport_replace((short) 4), - transmission_in_progress((short) 8), - transmission_successful((short) 16), - transmission_failed((short) 32), - transmission_cancelled((short) 64), - ; - - final short value; - - STATE(short value) { - this.value = value; - } - - short getValue() { - return value; - } - } - - public void addState(STATE state) { - this.state |= state.getValue(); - } - - public void removeState(STATE state) { - this.state ^= state.getValue(); - } - - public boolean hasState(STATE state) { - return (this.state & state.getValue()) == state.getValue(); - } - public JingleContent(JingleContentElement.Creator creator, JingleContentElement.Senders senders) { this(null, null, null, randomName(), null, creator, senders); } @@ -114,7 +79,6 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal this.disposition = disposition; this.creator = creator; this.senders = senders; - this.addState(STATE.pending_accept); } public static JingleContent fromElement(JingleContentElement content) { @@ -226,7 +190,7 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal public IQ handleTransportAccept(JingleElement request, XMPPConnection connection) { - if (pendingReplacingTransport == null || !hasState(STATE.pending_transport_replace)) { + if (pendingReplacingTransport == null) { LOGGER.log(Level.WARNING, "Received transport-accept, but apparently we did not try to replace the transport."); return JingleElement.createJingleErrorOutOfOrder(request); } @@ -234,8 +198,6 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal transport = pendingReplacingTransport; pendingReplacingTransport = null; - removeState(STATE.pending_transport_replace); - onAccept(connection); return IQ.createResultIQ(request); @@ -249,13 +211,12 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal } public IQ handleTransportReject(JingleElement request, XMPPConnection connection) { - removeState(STATE.pending_transport_replace); return IQ.createResultIQ(request); } public IQ handleTransportReplace(final JingleElement request, final XMPPConnection connection) { //Tie Break? - if (hasState(STATE.pending_transport_replace)) { + if (pendingReplacingTransport != null) { Async.go(new Runnable() { @Override public void run() { @@ -487,7 +448,7 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal private void replaceTransport(Set blacklist, XMPPConnection connection) throws SmackException.NotConnectedException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NoResponseException { - if (hasState(STATE.pending_transport_replace)) { + if (pendingReplacingTransport != null) { throw new AssertionError("Transport replace already pending."); } @@ -508,7 +469,6 @@ public class JingleContent implements JingleTransportCallback, JingleSecurityCal session.getSessionId(), getCreator(), getName(), pendingReplacingTransport.getElement()); connection.createStanzaCollectorAndSend(transportReplace).nextResultOrThrow(); - addState(STATE.pending_transport_replace); } public static String randomName() { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transport/jingle_s5b/JingleS5BTransportManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transport/jingle_s5b/JingleS5BTransportManager.java index 5537ad7a6..4973ec17e 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transport/jingle_s5b/JingleS5BTransportManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transport/jingle_s5b/JingleS5BTransportManager.java @@ -65,8 +65,8 @@ public final class JingleS5BTransportManager extends Manager implements JingleTr private List localStreamHosts = null; private List availableStreamHosts = null; - private static boolean useLocalCandidates = true; - private static boolean useExternalCandidates = true; + public static boolean useLocalCandidates = true; + public static boolean useExternalCandidates = true; static { JingleManager.addJingleTransportAdapter(new JingleS5BTransportAdapter()); diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/jft/JingleFileTransferTransportFallbackTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/jft/JingleFileTransferTransportFallbackTest.java new file mode 100644 index 000000000..ca1462ff9 --- /dev/null +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/jft/JingleFileTransferTransportFallbackTest.java @@ -0,0 +1,180 @@ +package org.jivesoftware.smackx.jft; + +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 java.util.ArrayList; +import java.util.concurrent.Future; +import java.util.logging.Level; + +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy; +import org.jivesoftware.smackx.jft.controller.IncomingFileOfferController; +import org.jivesoftware.smackx.jft.controller.OutgoingFileOfferController; +import org.jivesoftware.smackx.jft.listener.IncomingFileOfferListener; +import org.jivesoftware.smackx.jft.listener.ProgressListener; +import org.jivesoftware.smackx.jingle.transport.jingle_ibb.JingleIBBTransportManager; +import org.jivesoftware.smackx.jingle.transport.jingle_s5b.JingleS5BTransportManager; + +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 JingleFileTransferTransportFallbackTest 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 JingleFileTransferTransportFallbackTest(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); + + final ArrayList> receiveFuture = new ArrayList<>(); //Uglaay + + bftm.addIncomingFileOfferListener(new IncomingFileOfferListener() { + @Override + public void onIncomingFileOffer(IncomingFileOfferController offer) { + LOGGER.log(Level.INFO, "INCOMING FILE TRANSFER!"); + + offer.addProgressListener(new ProgressListener() { + @Override + public void started() { + + } + + @Override + public void progress(float percent) { + + } + + @Override + public void finished() { + resultSyncPoint2.signal(); + } + }); + + try { + receiveFuture.add(offer.accept(conTwo, target)); + } catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) { + fail(e.toString()); + } + } + }); + + OutgoingFileOfferController sending = aftm.sendFile(source, bob); + + sending.addProgressListener(new ProgressListener() { + @Override + public void started() { + + } + + @Override + public void progress(float percent) { + + } + + @Override + public void finished() { + 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) { + LOGGER.log(Level.SEVERE, "Could not read files."); + fail(); + } + + assertArrayEquals(sBytes, tBytes); + LOGGER.log(Level.INFO, "SUCCESSFULLY SENT AND RECEIVED"); + } + + @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(); + } +}