diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jet/JetManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jet/JetManager.java index a1ad1586e..50bb66d1f 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jet/JetManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jet/JetManager.java @@ -17,13 +17,21 @@ package org.jivesoftware.smackx.jet; import java.io.File; +import java.io.InputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.util.HashMap; import java.util.WeakHashMap; import java.util.logging.Logger; +import javax.crypto.NoSuchPaddingException; + import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.provider.ExtensionElementProvider; import org.jivesoftware.smackx.ciphers.Aes256GcmNoPadding; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; @@ -37,6 +45,7 @@ import org.jivesoftware.smackx.jingle.component.JingleSession; import org.jivesoftware.smackx.jingle.element.JingleContentElement; import org.jivesoftware.smackx.jingle.util.Role; import org.jivesoftware.smackx.jingle_filetransfer.JingleFileTransferManager; +import org.jivesoftware.smackx.jingle_filetransfer.component.JingleFileTransferFile; import org.jivesoftware.smackx.jingle_filetransfer.component.JingleOutgoingFileOffer; import org.jivesoftware.smackx.jingle_filetransfer.controller.OutgoingFileOfferController; @@ -87,10 +96,7 @@ public final class JetManager extends Manager implements JingleDescriptionManage throw new IllegalArgumentException("File MUST NOT be null and MUST exist."); } - ServiceDiscoveryManager disco = ServiceDiscoveryManager.getInstanceFor(connection()); - if (!disco.supportsFeature(recipient, getNamespace()) || !disco.supportsFeature(recipient, envelopeManager.getJingleEnvelopeNamespace())) { - throw new SmackException.FeatureNotSupportedException(getNamespace(), recipient); - } + throwIfRecipientLacksSupport(recipient); JingleSession session = jingleManager.createSession(Role.initiator, recipient); @@ -113,6 +119,30 @@ public final class JetManager extends Manager implements JingleDescriptionManage return offer; } + public OutgoingFileOfferController sendEncryptedStream(InputStream inputStream, JingleFileTransferFile.StreamFile file, FullJid recipient, JingleEnvelopeManager envelopeManager) + throws XMPPException.XMPPErrorException, SmackException.FeatureNotSupportedException, SmackException.NotConnectedException, + InterruptedException, SmackException.NoResponseException, NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, + JingleEnvelopeManager.JingleEncryptionException, NoSuchProviderException, InvalidAlgorithmParameterException { + + throwIfRecipientLacksSupport(recipient); + JingleSession session = jingleManager.createSession(Role.initiator, recipient); + + JingleContent content = new JingleContent(JingleContentElement.Creator.initiator, JingleContentElement.Senders.initiator); + session.addContent(content); + + JingleOutgoingFileOffer offer = new JingleOutgoingFileOffer(file, inputStream); + content.setDescription(offer); + + JingleTransportManager transportManager = jingleManager.getBestAvailableTransportManager(recipient); + content.setTransport(transportManager.createTransportForInitiator(content)); + + JetSecurity security = new JetSecurity(envelopeManager, recipient, content.getName(), Aes256GcmNoPadding.NAMESPACE); + content.setSecurity(security); + session.sendInitiate(connection()); + + return offer; + } + public void registerEnvelopeManager(JingleEnvelopeManager method) { envelopeManagers.put(method.getJingleEnvelopeNamespace(), method); } @@ -151,4 +181,10 @@ public final class JetManager extends Manager implements JingleDescriptionManage public void notifyContentAdd(JingleSession session, JingleContent content) { JingleFileTransferManager.getInstanceFor(connection()).notifyContentAdd(session, content); } + + private void throwIfRecipientLacksSupport(FullJid recipient) throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, SmackException.FeatureNotSupportedException { + if (!ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(recipient, getNamespace())) { + throw new SmackException.FeatureNotSupportedException(getNamespace(), recipient); + } + } } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java index 6da5bd2f0..725209e6a 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java @@ -130,7 +130,7 @@ public final class JingleFileTransferManager extends Manager implements JingleDe return offer; } - public OutgoingFileOfferController sendStream(final InputStream stream, JingleFileTransferFile.LocalFile file, FullJid recipient) throws SmackException.FeatureNotSupportedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException { + public OutgoingFileOfferController sendStream(final InputStream stream, JingleFileTransferFile.StreamFile file, FullJid recipient) throws SmackException.FeatureNotSupportedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException { if (!ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(recipient, getNamespace())) { throw new SmackException.FeatureNotSupportedException(getNamespace(), recipient); } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/component/AbstractJingleFileOffer.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/component/AbstractJingleFileOffer.java index 0d2b7b959..ad5583f69 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/component/AbstractJingleFileOffer.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/component/AbstractJingleFileOffer.java @@ -19,12 +19,9 @@ package org.jivesoftware.smackx.jingle_filetransfer.component; /** * Created by vanitas on 22.07.17. */ -public abstract class AbstractJingleFileOffer extends JingleFileTransfer { +public abstract class AbstractJingleFileOffer extends JingleFileTransfer { - AbstractJingleFileOffer(D fileTransferFile) { + AbstractJingleFileOffer(JingleFileTransferFile fileTransferFile) { super(fileTransferFile); } - - @Override - public abstract D getFile(); } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/component/JingleFileTransferFile.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/component/JingleFileTransferFile.java index a28f90589..03c709b1c 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/component/JingleFileTransferFile.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/component/JingleFileTransferFile.java @@ -175,4 +175,51 @@ public abstract class JingleFileTransferFile { } } + public static class StreamFile extends JingleFileTransferFile { + + private String name, description, mediaType; + private long size; + private Date date; + private HashElement hashElement; + + public StreamFile(String name, long size, String description, String mediaType, Date date, HashElement hashElement) { + this.name = name; + this.size = size; + this.description = description; + this.mediaType = mediaType; + this.date = date; + this.hashElement = hashElement; + } + + @Override + public Date getDate() { + return date; + } + + @Override + public long getSize() { + return size; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getMediaType() { + return mediaType; + } + + @Override + public HashElement getHashElement() { + return hashElement; + } + } + } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/component/JingleIncomingFileOffer.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/component/JingleIncomingFileOffer.java index 20390ff88..9097c4f90 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/component/JingleIncomingFileOffer.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/component/JingleIncomingFileOffer.java @@ -38,7 +38,7 @@ import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChi * Behind the scenes logic of an incoming Jingle file offer. * Created by vanitas on 26.07.17. */ -public class JingleIncomingFileOffer extends AbstractJingleFileOffer implements IncomingFileOfferController { +public class JingleIncomingFileOffer extends AbstractJingleFileOffer implements IncomingFileOfferController { private static final Logger LOGGER = Logger.getLogger(JingleIncomingFileOffer.class.getName()); private OutputStream target; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/component/JingleOutgoingFileOffer.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/component/JingleOutgoingFileOffer.java index 4a9615db4..d0354664b 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/component/JingleOutgoingFileOffer.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/component/JingleOutgoingFileOffer.java @@ -33,17 +33,18 @@ import org.jivesoftware.smackx.jingle_filetransfer.controller.OutgoingFileOfferC /** * Created by vanitas on 26.07.17. */ -public class JingleOutgoingFileOffer extends AbstractJingleFileOffer implements OutgoingFileOfferController { +public class JingleOutgoingFileOffer extends AbstractJingleFileOffer implements OutgoingFileOfferController { private static final Logger LOGGER = Logger.getLogger(JingleOutgoingFileOffer.class.getName()); private final InputStream source; public JingleOutgoingFileOffer(File file) throws FileNotFoundException { - this(new JingleFileTransferFile.LocalFile(file), new FileInputStream(file)); + super(new JingleFileTransferFile.LocalFile(file)); + this.source = new FileInputStream(file); } - public JingleOutgoingFileOffer(JingleFileTransferFile.LocalFile localFile, InputStream inputStream) { - super(localFile); + public JingleOutgoingFileOffer(JingleFileTransferFile.StreamFile streamFile, InputStream inputStream) { + super(streamFile); this.source = inputStream; } @@ -79,12 +80,11 @@ public class JingleOutgoingFileOffer extends AbstractJingleFileOffer store; - 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 JetIntegrationTest(SmackIntegrationTestEnvironment environment) throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, TestNotPossibleException { @@ -80,6 +72,8 @@ public class JetIntegrationTest extends AbstractOmemoIntegrationTest { jb = JetManager.getInstanceFor(conTwo); ia = JingleIBBTransportManager.getInstanceFor(conOne); ib = JingleIBBTransportManager.getInstanceFor(conTwo); + sa = JingleS5BTransportManager.getInstanceFor(conOne); + sb = JingleS5BTransportManager.getInstanceFor(conTwo); JetManager.registerEnvelopeProvider(OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL, new OmemoVAxolotlProvider()); } @@ -89,6 +83,8 @@ public class JetIntegrationTest extends AbstractOmemoIntegrationTest { final SimpleResultSyncPoint received = new SimpleResultSyncPoint(); + Random weakRandom = new Random(); + //Setup OMEMO subscribe(oa, ob, "Bob"); subscribe(ob, oa, "Alice"); @@ -100,8 +96,10 @@ public class JetIntegrationTest extends AbstractOmemoIntegrationTest { ja.registerEnvelopeManager(oa); jb.registerEnvelopeManager(ob); - File source = prepareNewTestFile("source"); - final File target = new File(tempDir, "target"); + byte[] sourceBytes = new byte[16000]; + weakRandom.nextBytes(sourceBytes); + InputStream sourceStream = new ByteArrayInputStream(sourceBytes); + final ByteArrayOutputStream targetStream = new ByteArrayOutputStream(16000); JingleFileTransferManager.getInstanceFor(conTwo).addIncomingFileOfferListener(new IncomingFileOfferListener() { @Override @@ -123,27 +121,18 @@ public class JetIntegrationTest extends AbstractOmemoIntegrationTest { received.signal(); } }); - offer.accept(conTwo, target); + offer.accept(conTwo, targetStream); } catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException | IOException e) { received.signal(e); } } }); - ja.sendEncryptedFile(source, conTwo.getUser().asFullJidOrThrow(), oa); + ja.sendEncryptedStream(sourceStream, new JingleFileTransferFile.StreamFile("test", sourceBytes.length, "desc", null, null, null), conTwo.getUser().asFullJidOrThrow(), oa); received.waitForResult(60 * 1000); - FileInputStream sIn = new FileInputStream(source); - FileInputStream tIn = new FileInputStream(target); - - byte[] sB = new byte[(int) source.length()]; - byte[] tB = new byte[(int) target.length()]; - - sIn.read(sB); - tIn.read(tB); - - assertArrayEquals(sB, tB); + assertArrayEquals(sourceBytes, targetStream.toByteArray()); } @Override