1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-11-23 20:42:06 +01:00

Allow sending streams in JET

This commit is contained in:
vanitasvitae 2017-08-16 19:58:43 +02:00
parent 7e7be0f47b
commit 524660c870
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
7 changed files with 119 additions and 50 deletions

View file

@ -17,13 +17,21 @@
package org.jivesoftware.smackx.jet; package org.jivesoftware.smackx.jet;
import java.io.File; 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.HashMap;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.crypto.NoSuchPaddingException;
import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.provider.ExtensionElementProvider; import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smackx.ciphers.Aes256GcmNoPadding; import org.jivesoftware.smackx.ciphers.Aes256GcmNoPadding;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 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.element.JingleContentElement;
import org.jivesoftware.smackx.jingle.util.Role; import org.jivesoftware.smackx.jingle.util.Role;
import org.jivesoftware.smackx.jingle_filetransfer.JingleFileTransferManager; 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.component.JingleOutgoingFileOffer;
import org.jivesoftware.smackx.jingle_filetransfer.controller.OutgoingFileOfferController; 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."); throw new IllegalArgumentException("File MUST NOT be null and MUST exist.");
} }
ServiceDiscoveryManager disco = ServiceDiscoveryManager.getInstanceFor(connection()); throwIfRecipientLacksSupport(recipient);
if (!disco.supportsFeature(recipient, getNamespace()) || !disco.supportsFeature(recipient, envelopeManager.getJingleEnvelopeNamespace())) {
throw new SmackException.FeatureNotSupportedException(getNamespace(), recipient);
}
JingleSession session = jingleManager.createSession(Role.initiator, recipient); JingleSession session = jingleManager.createSession(Role.initiator, recipient);
@ -113,6 +119,30 @@ public final class JetManager extends Manager implements JingleDescriptionManage
return offer; 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) { public void registerEnvelopeManager(JingleEnvelopeManager method) {
envelopeManagers.put(method.getJingleEnvelopeNamespace(), 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) { public void notifyContentAdd(JingleSession session, JingleContent content) {
JingleFileTransferManager.getInstanceFor(connection()).notifyContentAdd(session, 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);
}
}
} }

View file

@ -130,7 +130,7 @@ public final class JingleFileTransferManager extends Manager implements JingleDe
return offer; 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())) { if (!ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(recipient, getNamespace())) {
throw new SmackException.FeatureNotSupportedException(getNamespace(), recipient); throw new SmackException.FeatureNotSupportedException(getNamespace(), recipient);
} }

View file

@ -19,12 +19,9 @@ package org.jivesoftware.smackx.jingle_filetransfer.component;
/** /**
* Created by vanitas on 22.07.17. * Created by vanitas on 22.07.17.
*/ */
public abstract class AbstractJingleFileOffer<D extends JingleFileTransferFile> extends JingleFileTransfer { public abstract class AbstractJingleFileOffer extends JingleFileTransfer {
AbstractJingleFileOffer(D fileTransferFile) { AbstractJingleFileOffer(JingleFileTransferFile fileTransferFile) {
super(fileTransferFile); super(fileTransferFile);
} }
@Override
public abstract D getFile();
} }

View file

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

View file

@ -38,7 +38,7 @@ import org.jivesoftware.smackx.jingle_filetransfer.element.JingleFileTransferChi
* Behind the scenes logic of an incoming Jingle file offer. * Behind the scenes logic of an incoming Jingle file offer.
* Created by vanitas on 26.07.17. * Created by vanitas on 26.07.17.
*/ */
public class JingleIncomingFileOffer extends AbstractJingleFileOffer<JingleFileTransferFile.RemoteFile> implements IncomingFileOfferController { public class JingleIncomingFileOffer extends AbstractJingleFileOffer implements IncomingFileOfferController {
private static final Logger LOGGER = Logger.getLogger(JingleIncomingFileOffer.class.getName()); private static final Logger LOGGER = Logger.getLogger(JingleIncomingFileOffer.class.getName());
private OutputStream target; private OutputStream target;

View file

@ -33,17 +33,18 @@ import org.jivesoftware.smackx.jingle_filetransfer.controller.OutgoingFileOfferC
/** /**
* Created by vanitas on 26.07.17. * Created by vanitas on 26.07.17.
*/ */
public class JingleOutgoingFileOffer extends AbstractJingleFileOffer<JingleFileTransferFile.LocalFile> implements OutgoingFileOfferController { public class JingleOutgoingFileOffer extends AbstractJingleFileOffer implements OutgoingFileOfferController {
private static final Logger LOGGER = Logger.getLogger(JingleOutgoingFileOffer.class.getName()); private static final Logger LOGGER = Logger.getLogger(JingleOutgoingFileOffer.class.getName());
private final InputStream source; private final InputStream source;
public JingleOutgoingFileOffer(File file) throws FileNotFoundException { 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) { public JingleOutgoingFileOffer(JingleFileTransferFile.StreamFile streamFile, InputStream inputStream) {
super(localFile); super(streamFile);
this.source = inputStream; this.source = inputStream;
} }
@ -79,14 +80,13 @@ public class JingleOutgoingFileOffer extends AbstractJingleFileOffer<JingleFileT
} catch (IOException e) { } catch (IOException e) {
LOGGER.log(Level.SEVERE, "Exception while sending file: " + e, e); LOGGER.log(Level.SEVERE, "Exception while sending file: " + e, e);
} finally { } finally {
if (source != null) {
try { try {
source.close(); source.close();
} catch (IOException e) { } catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not close FileInputStream: " + e, e); LOGGER.log(Level.SEVERE, "Could not close FileInputStream: " + e, e);
} }
} }
}
notifyProgressListenersFinished(); notifyProgressListenersFinished();
} }

View file

@ -16,21 +16,24 @@
*/ */
package org.jivesoftware.smackx.jet; package org.jivesoftware.smackx.jet;
import static org.jivesoftware.smackx.jingle_filetransfer.JingleFileTransferIntegrationTest.prepareNewTestFile;
import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.cleanServerSideTraces; import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.cleanServerSideTraces;
import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.setUpOmemoManager; import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.setUpOmemoManager;
import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.subscribe; import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.subscribe;
import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.unidirectionalTrust; import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.unidirectionalTrust;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import java.io.File; import java.io.ByteArrayInputStream;
import java.io.FileInputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.Random;
import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jingle.transport.jingle_ibb.JingleIBBTransportManager; import org.jivesoftware.smackx.jingle.transport.jingle_ibb.JingleIBBTransportManager;
import org.jivesoftware.smackx.jingle.transport.jingle_s5b.JingleS5BTransportManager;
import org.jivesoftware.smackx.jingle_filetransfer.JingleFileTransferManager; import org.jivesoftware.smackx.jingle_filetransfer.JingleFileTransferManager;
import org.jivesoftware.smackx.jingle_filetransfer.component.JingleFileTransferFile;
import org.jivesoftware.smackx.jingle_filetransfer.controller.IncomingFileOfferController; import org.jivesoftware.smackx.jingle_filetransfer.controller.IncomingFileOfferController;
import org.jivesoftware.smackx.jingle_filetransfer.listener.IncomingFileOfferListener; import org.jivesoftware.smackx.jingle_filetransfer.listener.IncomingFileOfferListener;
import org.jivesoftware.smackx.jingle_filetransfer.listener.ProgressListener; import org.jivesoftware.smackx.jingle_filetransfer.listener.ProgressListener;
@ -51,20 +54,9 @@ public class JetIntegrationTest extends AbstractOmemoIntegrationTest {
private OmemoManager oa, ob; private OmemoManager oa, ob;
private JetManager ja, jb; private JetManager ja, jb;
private JingleIBBTransportManager ia, ib; private JingleIBBTransportManager ia, ib;
private JingleS5BTransportManager sa, sb;
private OmemoStore<?,?,?,?,?,?,?,?,?> store; private OmemoStore<?,?,?,?,?,?,?,?,?> 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) public JetIntegrationTest(SmackIntegrationTestEnvironment environment)
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
SmackException.NoResponseException, TestNotPossibleException { SmackException.NoResponseException, TestNotPossibleException {
@ -80,6 +72,8 @@ public class JetIntegrationTest extends AbstractOmemoIntegrationTest {
jb = JetManager.getInstanceFor(conTwo); jb = JetManager.getInstanceFor(conTwo);
ia = JingleIBBTransportManager.getInstanceFor(conOne); ia = JingleIBBTransportManager.getInstanceFor(conOne);
ib = JingleIBBTransportManager.getInstanceFor(conTwo); ib = JingleIBBTransportManager.getInstanceFor(conTwo);
sa = JingleS5BTransportManager.getInstanceFor(conOne);
sb = JingleS5BTransportManager.getInstanceFor(conTwo);
JetManager.registerEnvelopeProvider(OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL, new OmemoVAxolotlProvider()); JetManager.registerEnvelopeProvider(OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL, new OmemoVAxolotlProvider());
} }
@ -89,6 +83,8 @@ public class JetIntegrationTest extends AbstractOmemoIntegrationTest {
final SimpleResultSyncPoint received = new SimpleResultSyncPoint(); final SimpleResultSyncPoint received = new SimpleResultSyncPoint();
Random weakRandom = new Random();
//Setup OMEMO //Setup OMEMO
subscribe(oa, ob, "Bob"); subscribe(oa, ob, "Bob");
subscribe(ob, oa, "Alice"); subscribe(ob, oa, "Alice");
@ -100,8 +96,10 @@ public class JetIntegrationTest extends AbstractOmemoIntegrationTest {
ja.registerEnvelopeManager(oa); ja.registerEnvelopeManager(oa);
jb.registerEnvelopeManager(ob); jb.registerEnvelopeManager(ob);
File source = prepareNewTestFile("source"); byte[] sourceBytes = new byte[16000];
final File target = new File(tempDir, "target"); weakRandom.nextBytes(sourceBytes);
InputStream sourceStream = new ByteArrayInputStream(sourceBytes);
final ByteArrayOutputStream targetStream = new ByteArrayOutputStream(16000);
JingleFileTransferManager.getInstanceFor(conTwo).addIncomingFileOfferListener(new IncomingFileOfferListener() { JingleFileTransferManager.getInstanceFor(conTwo).addIncomingFileOfferListener(new IncomingFileOfferListener() {
@Override @Override
@ -123,27 +121,18 @@ public class JetIntegrationTest extends AbstractOmemoIntegrationTest {
received.signal(); received.signal();
} }
}); });
offer.accept(conTwo, target); offer.accept(conTwo, targetStream);
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException | IOException e) { } catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException | IOException e) {
received.signal(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); received.waitForResult(60 * 1000);
FileInputStream sIn = new FileInputStream(source); assertArrayEquals(sourceBytes, targetStream.toByteArray());
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);
} }
@Override @Override