diff --git a/briar-api/src/org/briarproject/api/contact/ContactManager.java b/briar-api/src/org/briarproject/api/contact/ContactManager.java index a40a2b08c456db34f9b432795c2dfd01449fa54d..5573e3ea4591d342f8e33922903997e850c9eb38 100644 --- a/briar-api/src/org/briarproject/api/contact/ContactManager.java +++ b/briar-api/src/org/briarproject/api/contact/ContactManager.java @@ -45,11 +45,11 @@ public interface ContactManager { void setContactActive(ContactId c, boolean active) throws DbException; /** Return true if a contact with this name and public key already exists */ - boolean contactExists(Transaction txn, AuthorId remoteAuthorID, + boolean contactExists(Transaction txn, AuthorId remoteAuthorId, AuthorId localAuthorId) throws DbException; /** Return true if a contact with this name and public key already exists */ - boolean contactExists(AuthorId remoteAuthorID, AuthorId localAuthorId) + boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId) throws DbException; interface AddContactHook { diff --git a/briar-api/src/org/briarproject/api/introduction/IntroductionConstants.java b/briar-api/src/org/briarproject/api/introduction/IntroductionConstants.java index 64bc309830b2f18ea2e17d8f115a842c2970b6fe..1ccf2d48053a8f14e2151202982b580493370e71 100644 --- a/briar-api/src/org/briarproject/api/introduction/IntroductionConstants.java +++ b/briar-api/src/org/briarproject/api/introduction/IntroductionConstants.java @@ -23,7 +23,6 @@ public interface IntroductionConstants { String MSG = "msg"; String ACCEPT = "accept"; String TIME = "time"; - String DEVICE_ID = "deviceId"; String TRANSPORT = "transport"; String MESSAGE_ID = "messageId"; String MESSAGE_TIME = "timestamp"; diff --git a/briar-api/src/org/briarproject/api/plugins/ConnectionRegistry.java b/briar-api/src/org/briarproject/api/plugins/ConnectionRegistry.java index 6de3d00c5b55e34b4ca4a364a6056f4a510fa6cd..dc9376195387a5da851a9aab37a67707bc470e1c 100644 --- a/briar-api/src/org/briarproject/api/plugins/ConnectionRegistry.java +++ b/briar-api/src/org/briarproject/api/plugins/ConnectionRegistry.java @@ -16,5 +16,7 @@ public interface ConnectionRegistry { Collection<ContactId> getConnectedContacts(TransportId t); + boolean isConnected(ContactId c, TransportId t); + boolean isConnected(ContactId c); } diff --git a/briar-api/src/org/briarproject/api/properties/TransportPropertyManager.java b/briar-api/src/org/briarproject/api/properties/TransportPropertyManager.java index 445329ba95a6100c1def5d4a9f87363c5b3a3f94..8b63ae612bc4705e779b44e503e57e810fed4ccf 100644 --- a/briar-api/src/org/briarproject/api/properties/TransportPropertyManager.java +++ b/briar-api/src/org/briarproject/api/properties/TransportPropertyManager.java @@ -1,6 +1,5 @@ package org.briarproject.api.properties; -import org.briarproject.api.DeviceId; import org.briarproject.api.TransportId; import org.briarproject.api.contact.ContactId; import org.briarproject.api.db.DbException; @@ -14,7 +13,7 @@ public interface TransportPropertyManager { * Stores the given properties received while adding a contact - they will * be superseded by any properties synced from the contact. */ - void addRemoteProperties(Transaction txn, ContactId c, DeviceId dev, + void addRemoteProperties(Transaction txn, ContactId c, Map<TransportId, TransportProperties> props) throws DbException; /** Returns the local transport properties for all transports. */ diff --git a/briar-core/src/org/briarproject/contact/ContactExchangeTaskImpl.java b/briar-core/src/org/briarproject/contact/ContactExchangeTaskImpl.java index 943d02de98598359dedbf1767411a6334b8ed504..8fa1501816cb8771763d765995f0ec283fb14421 100644 --- a/briar-core/src/org/briarproject/contact/ContactExchangeTaskImpl.java +++ b/briar-core/src/org/briarproject/contact/ContactExchangeTaskImpl.java @@ -10,17 +10,22 @@ import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.KeyParser; import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.crypto.Signature; +import org.briarproject.api.data.BdfList; import org.briarproject.api.data.BdfReader; import org.briarproject.api.data.BdfReaderFactory; import org.briarproject.api.data.BdfWriter; import org.briarproject.api.data.BdfWriterFactory; import org.briarproject.api.db.ContactExistsException; +import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DbException; +import org.briarproject.api.db.Transaction; import org.briarproject.api.identity.Author; import org.briarproject.api.identity.AuthorFactory; import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.plugins.ConnectionManager; import org.briarproject.api.plugins.duplex.DuplexTransportConnection; +import org.briarproject.api.properties.TransportProperties; +import org.briarproject.api.properties.TransportPropertyManager; import org.briarproject.api.system.Clock; import org.briarproject.api.transport.StreamReaderFactory; import org.briarproject.api.transport.StreamWriterFactory; @@ -29,13 +34,19 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.GeneralSecurityException; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; import java.util.logging.Logger; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; +import static org.briarproject.api.TransportId.MAX_TRANSPORT_ID_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH; +import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT; +import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH; public class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask { @@ -43,35 +54,40 @@ public class ContactExchangeTaskImpl extends Thread private static final Logger LOG = Logger.getLogger(ContactExchangeTaskImpl.class.getName()); + private final DatabaseComponent db; private final AuthorFactory authorFactory; private final BdfReaderFactory bdfReaderFactory; private final BdfWriterFactory bdfWriterFactory; private final Clock clock; private final ConnectionManager connectionManager; private final ContactManager contactManager; + private final TransportPropertyManager transportPropertyManager; private final CryptoComponent crypto; private final StreamReaderFactory streamReaderFactory; private final StreamWriterFactory streamWriterFactory; - private ContactExchangeListener listener; - private LocalAuthor localAuthor; - private DuplexTransportConnection conn; - private TransportId transportId; - private SecretKey masterSecret; - private boolean alice; + private volatile ContactExchangeListener listener; + private volatile LocalAuthor localAuthor; + private volatile DuplexTransportConnection conn; + private volatile TransportId transportId; + private volatile SecretKey masterSecret; + private volatile boolean alice; - public ContactExchangeTaskImpl(AuthorFactory authorFactory, - BdfReaderFactory bdfReaderFactory, + public ContactExchangeTaskImpl(DatabaseComponent db, + AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory, BdfWriterFactory bdfWriterFactory, Clock clock, ConnectionManager connectionManager, ContactManager contactManager, + TransportPropertyManager transportPropertyManager, CryptoComponent crypto, StreamReaderFactory streamReaderFactory, StreamWriterFactory streamWriterFactory) { + this.db = db; this.authorFactory = authorFactory; this.bdfReaderFactory = bdfReaderFactory; this.bdfWriterFactory = bdfWriterFactory; this.clock = clock; this.connectionManager = connectionManager; this.contactManager = contactManager; + this.transportPropertyManager = transportPropertyManager; this.crypto = crypto; this.streamReaderFactory = streamReaderFactory; this.streamWriterFactory = streamWriterFactory; @@ -93,24 +109,12 @@ public class ContactExchangeTaskImpl extends Thread @Override public void run() { - // Derive the header keys for the transport streams - SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true); - SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false); - BdfReader r; - BdfWriter w; + // Get the transport connection's input and output streams + InputStream in; + OutputStream out; try { - // Create the readers - InputStream streamReader = - streamReaderFactory.createInvitationStreamReader( - conn.getReader().getInputStream(), - alice ? bobHeaderKey : aliceHeaderKey); - r = bdfReaderFactory.createReader(streamReader); - // Create the writers - OutputStream streamWriter = - streamWriterFactory.createInvitationStreamWriter( - conn.getWriter().getOutputStream(), - alice ? aliceHeaderKey : bobHeaderKey); - w = bdfWriterFactory.createWriter(streamWriter); + in = conn.getReader().getInputStream(); + out = conn.getWriter().getOutputStream(); } catch (IOException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); listener.contactExchangeFailed(); @@ -118,6 +122,32 @@ public class ContactExchangeTaskImpl extends Thread return; } + // Get the local transport properties + Map<TransportId, TransportProperties> localProperties, remoteProperties; + try { + localProperties = transportPropertyManager.getLocalProperties(); + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + listener.contactExchangeFailed(); + tryToClose(conn, true); + return; + } + + // Derive the header keys for the transport streams + SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true); + SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false); + + // Create the readers + InputStream streamReader = + streamReaderFactory.createInvitationStreamReader(in, + alice ? bobHeaderKey : aliceHeaderKey); + BdfReader r = bdfReaderFactory.createReader(streamReader); + // Create the writers + OutputStream streamWriter = + streamWriterFactory.createInvitationStreamWriter(out, + alice ? aliceHeaderKey : bobHeaderKey); + BdfWriter w = bdfWriterFactory.createWriter(streamWriter); + // Derive the nonces to be signed byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true); byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false); @@ -130,13 +160,19 @@ public class ContactExchangeTaskImpl extends Thread if (alice) { sendPseudonym(w, aliceNonce); sendTimestamp(w, localTimestamp); + sendTransportProperties(w, localProperties); + w.flush(); remoteAuthor = receivePseudonym(r, bobNonce); remoteTimestamp = receiveTimestamp(r); + remoteProperties = receiveTransportProperties(r); } else { remoteAuthor = receivePseudonym(r, aliceNonce); remoteTimestamp = receiveTimestamp(r); + remoteProperties = receiveTransportProperties(r); sendPseudonym(w, bobNonce); sendTimestamp(w, localTimestamp); + sendTransportProperties(w, localProperties); + w.flush(); } // Close the outgoing stream and expect EOF on the incoming stream w.close(); @@ -159,7 +195,7 @@ public class ContactExchangeTaskImpl extends Thread try { // Add the contact ContactId contactId = addContact(remoteAuthor, masterSecret, - timestamp, alice); + timestamp, alice, remoteProperties); // Reuse the connection as a transport connection connectionManager.manageOutgoingConnection(contactId, transportId, conn); @@ -187,19 +223,22 @@ public class ContactExchangeTaskImpl extends Thread signature.update(nonce); byte[] sig = signature.sign(); // Write the name, public key and signature + w.writeListStart(); w.writeString(localAuthor.getName()); w.writeRaw(localAuthor.getPublicKey()); w.writeRaw(sig); - w.flush(); + w.writeListEnd(); LOG.info("Sent pseudonym"); } private Author receivePseudonym(BdfReader r, byte[] nonce) throws GeneralSecurityException, IOException { // Read the name, public key and signature + r.readListStart(); String name = r.readString(MAX_AUTHOR_NAME_LENGTH); byte[] publicKey = r.readRaw(MAX_PUBLIC_KEY_LENGTH); byte[] sig = r.readRaw(MAX_SIGNATURE_LENGTH); + r.readListEnd(); LOG.info("Received pseudonym"); // Verify the signature Signature signature = crypto.getSignature(); @@ -217,7 +256,6 @@ public class ContactExchangeTaskImpl extends Thread private void sendTimestamp(BdfWriter w, long timestamp) throws IOException { w.writeLong(timestamp); - w.flush(); LOG.info("Sent timestamp"); } @@ -228,11 +266,56 @@ public class ContactExchangeTaskImpl extends Thread return timestamp; } + private void sendTransportProperties(BdfWriter w, + Map<TransportId, TransportProperties> local) throws IOException { + w.writeListStart(); + for (Entry<TransportId, TransportProperties> e : local.entrySet()) + w.writeList(BdfList.of(e.getKey().getString(), e.getValue())); + w.writeListEnd(); + } + + private Map<TransportId, TransportProperties> receiveTransportProperties( + BdfReader r) throws IOException { + Map<TransportId, TransportProperties> remote = + new HashMap<TransportId, TransportProperties>(); + r.readListStart(); + while (!r.hasListEnd()) { + r.readListStart(); + String id = r.readString(MAX_TRANSPORT_ID_LENGTH); + if (id.isEmpty()) throw new FormatException(); + TransportProperties p = new TransportProperties(); + r.readDictionaryStart(); + while (!r.hasDictionaryEnd()) { + if (p.size() == MAX_PROPERTIES_PER_TRANSPORT) + throw new FormatException(); + String key = r.readString(MAX_PROPERTY_LENGTH); + String value = r.readString(MAX_PROPERTY_LENGTH); + p.put(key, value); + } + r.readDictionaryEnd(); + r.readListEnd(); + remote.put(new TransportId(id), p); + } + r.readListEnd(); + return remote; + } + private ContactId addContact(Author remoteAuthor, SecretKey master, - long timestamp, boolean alice) throws DbException { - // Add the contact to the database - return contactManager.addContact(remoteAuthor, localAuthor.getId(), - master, timestamp, alice, true); + long timestamp, boolean alice, + Map<TransportId, TransportProperties> remoteProperties) + throws DbException { + ContactId contactId; + Transaction txn = db.startTransaction(false); + try { + contactId = contactManager.addContact(txn, remoteAuthor, + localAuthor.getId(), master, timestamp, alice, true); + transportPropertyManager.addRemoteProperties(txn, contactId, + remoteProperties); + txn.setComplete(); + } finally { + db.endTransaction(txn); + } + return contactId; } private void tryToClose(DuplexTransportConnection conn, diff --git a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java index 23ec838802d8ad75c512e970720a185b566e254c..5c5c81d1cc85d8c54ddf30b604f1e40a8a0188f6 100644 --- a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java +++ b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java @@ -126,18 +126,18 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook { } @Override - public boolean contactExists(Transaction txn, AuthorId remoteAuthorID, + public boolean contactExists(Transaction txn, AuthorId remoteAuthorId, AuthorId localAuthorId) throws DbException { - return db.containsContact(txn, remoteAuthorID, localAuthorId); + return db.containsContact(txn, remoteAuthorId, localAuthorId); } @Override - public boolean contactExists(AuthorId remoteAuthorID, + public boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId) throws DbException { boolean exists = false; Transaction txn = db.startTransaction(true); try { - exists = contactExists(txn, remoteAuthorID, localAuthorId); + exists = contactExists(txn, remoteAuthorId, localAuthorId); txn.setComplete(); } finally { db.endTransaction(txn); diff --git a/briar-core/src/org/briarproject/contact/ContactModule.java b/briar-core/src/org/briarproject/contact/ContactModule.java index fb5c290ce2d3ad0915f57a8e44a7c6d71d4b0aec..2cdf6079eddc4bba0cb54911ce1461d3c3522375 100644 --- a/briar-core/src/org/briarproject/contact/ContactModule.java +++ b/briar-core/src/org/briarproject/contact/ContactModule.java @@ -5,10 +5,11 @@ import org.briarproject.api.contact.ContactManager; import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.data.BdfReaderFactory; import org.briarproject.api.data.BdfWriterFactory; +import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.identity.AuthorFactory; import org.briarproject.api.identity.IdentityManager; -import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.plugins.ConnectionManager; +import org.briarproject.api.properties.TransportPropertyManager; import org.briarproject.api.system.Clock; import org.briarproject.api.transport.StreamReaderFactory; import org.briarproject.api.transport.StreamWriterFactory; @@ -28,22 +29,23 @@ public class ContactModule { @Provides @Singleton - ContactManager getContactManager(LifecycleManager lifecycleManager, - IdentityManager identityManager, + ContactManager getContactManager(IdentityManager identityManager, ContactManagerImpl contactManager) { identityManager.registerRemoveIdentityHook(contactManager); return contactManager; } @Provides - ContactExchangeTask provideContactExchangeTask( + ContactExchangeTask provideContactExchangeTask(DatabaseComponent db, AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory, BdfWriterFactory bdfWriterFactory, Clock clock, ConnectionManager connectionManager, ContactManager contactManager, + TransportPropertyManager transportPropertyManager, CryptoComponent crypto, StreamReaderFactory streamReaderFactory, StreamWriterFactory streamWriterFactory) { - return new ContactExchangeTaskImpl(authorFactory, bdfReaderFactory, + return new ContactExchangeTaskImpl(db, authorFactory, bdfReaderFactory, bdfWriterFactory, clock, connectionManager, contactManager, - crypto, streamReaderFactory, streamWriterFactory); + transportPropertyManager, crypto, streamReaderFactory, + streamWriterFactory); } } diff --git a/briar-core/src/org/briarproject/db/JdbcDatabase.java b/briar-core/src/org/briarproject/db/JdbcDatabase.java index 536161562462bc9ce06144418701eb11d4b78510..475dbdc2ff87447739ffdd372dec1b82c4cb45ab 100644 --- a/briar-core/src/org/briarproject/db/JdbcDatabase.java +++ b/briar-core/src/org/briarproject/db/JdbcDatabase.java @@ -63,8 +63,8 @@ import static org.briarproject.db.ExponentialBackoff.calculateExpiry; */ abstract class JdbcDatabase implements Database<Connection> { - private static final int SCHEMA_VERSION = 22; - private static final int MIN_SCHEMA_VERSION = 22; + private static final int SCHEMA_VERSION = 23; + private static final int MIN_SCHEMA_VERSION = 23; private static final String CREATE_SETTINGS = "CREATE TABLE settings" diff --git a/briar-core/src/org/briarproject/introduction/IntroduceeEngine.java b/briar-core/src/org/briarproject/introduction/IntroduceeEngine.java index 7be3184086617a1e34fff0aab621b3f390e3fb3f..6f76540d081a4ef7baf314ead7d9709eccb4c664 100644 --- a/briar-core/src/org/briarproject/introduction/IntroduceeEngine.java +++ b/briar-core/src/org/briarproject/introduction/IntroduceeEngine.java @@ -34,7 +34,6 @@ import static org.briarproject.api.introduction.IntroduceeProtocolState.FINISHED import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT; import static org.briarproject.api.introduction.IntroductionConstants.ANSWERED; import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1; -import static org.briarproject.api.introduction.IntroductionConstants.DEVICE_ID; import static org.briarproject.api.introduction.IntroductionConstants.EXISTS; import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY; import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID; @@ -108,7 +107,6 @@ public class IntroduceeEngine if (localState.getBoolean(ACCEPT)) { msg.put(TIME, localState.getLong(OUR_TIME)); msg.put(E_PUBLIC_KEY, localState.getRaw(OUR_PUBLIC_KEY)); - msg.put(DEVICE_ID, localAction.getRaw(DEVICE_ID)); msg.put(TRANSPORT, localAction.getDictionary(TRANSPORT)); } messages.add(msg); @@ -231,7 +229,6 @@ public class IntroduceeEngine if (msg.getBoolean(ACCEPT)) { localState.put(TIME, msg.getLong(TIME)); localState.put(E_PUBLIC_KEY, msg.getRaw(E_PUBLIC_KEY)); - localState.put(DEVICE_ID, msg.getRaw(DEVICE_ID)); localState.put(TRANSPORT, msg.getDictionary(TRANSPORT)); } } diff --git a/briar-core/src/org/briarproject/introduction/IntroduceeManager.java b/briar-core/src/org/briarproject/introduction/IntroduceeManager.java index 2049fba3ff5d636ee2acb8b48090184b3b77c715..be16ff1e495206ec06006de66e8e4708b808a19c 100644 --- a/briar-core/src/org/briarproject/introduction/IntroduceeManager.java +++ b/briar-core/src/org/briarproject/introduction/IntroduceeManager.java @@ -2,7 +2,6 @@ package org.briarproject.introduction; import org.briarproject.api.Bytes; -import org.briarproject.api.DeviceId; import org.briarproject.api.FormatException; import org.briarproject.api.TransportId; import org.briarproject.api.clients.ClientHelper; @@ -47,7 +46,6 @@ import static org.briarproject.api.introduction.IntroductionConstants.ADDED_CONT import static org.briarproject.api.introduction.IntroductionConstants.ANSWERED; import static org.briarproject.api.introduction.IntroductionConstants.CONTACT; import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1; -import static org.briarproject.api.introduction.IntroductionConstants.DEVICE_ID; import static org.briarproject.api.introduction.IntroductionConstants.EXISTS; import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY; import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID; @@ -167,7 +165,6 @@ class IntroduceeManager { // get data to connect and derive a shared secret later long now = clock.currentTimeMillis(); - byte[] deviceId = db.getDeviceId(txn).getBytes(); KeyPair keyPair = cryptoComponent.generateAgreementKeyPair(); byte[] publicKey = keyPair.getPublic().getEncoded(); byte[] privateKey = keyPair.getPrivate().getEncoded(); @@ -183,14 +180,12 @@ class IntroduceeManager { // define action BdfDictionary localAction = new BdfDictionary(); localAction.put(TYPE, TYPE_RESPONSE); - localAction.put(DEVICE_ID, deviceId); localAction.put(TRANSPORT, encodeTransportProperties(transportProperties)); // start engine and process its state update IntroduceeEngine engine = new IntroduceeEngine(); - processStateUpdate(txn, - engine.onLocalAction(state, localAction)); + processStateUpdate(txn, engine.onLocalAction(state, localAction)); } public void declineIntroduction(Transaction txn, final SessionId sessionId) @@ -313,11 +308,10 @@ class IntroduceeManager { localState.put(ADDED_CONTACT_ID, contactId.getInt()); // let the transport manager know how to connect to the contact - DeviceId deviceId = new DeviceId(localState.getRaw(DEVICE_ID)); Map<TransportId, TransportProperties> transportProperties = parseTransportProperties(localState); transportPropertyManager.addRemoteProperties(txn, contactId, - deviceId, transportProperties); + transportProperties); // delete the ephemeral private key by overwriting with NULL value // this ensures future ephemeral keys can not be recovered when diff --git a/briar-core/src/org/briarproject/introduction/IntroducerManager.java b/briar-core/src/org/briarproject/introduction/IntroducerManager.java index 1b35fc26c0363c8ccbde45b93accb2cdf165e30a..3cc906aedf32c085c132acd818a1152a773f19e2 100644 --- a/briar-core/src/org/briarproject/introduction/IntroducerManager.java +++ b/briar-core/src/org/briarproject/introduction/IntroducerManager.java @@ -20,8 +20,6 @@ import org.briarproject.util.StringUtils; import java.io.IOException; import java.util.logging.Logger; -import javax.inject.Inject; - import static java.util.logging.Level.WARNING; import static org.briarproject.api.introduction.IntroducerProtocolState.PREPARE_REQUESTS; import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_1; diff --git a/briar-core/src/org/briarproject/introduction/IntroductionValidator.java b/briar-core/src/org/briarproject/introduction/IntroductionValidator.java index addf58ea49749522873ff662e506e42055d42dfa..b8af1be12f7e224b379ad7016f4a2e47bdde81cf 100644 --- a/briar-core/src/org/briarproject/introduction/IntroductionValidator.java +++ b/briar-core/src/org/briarproject/introduction/IntroductionValidator.java @@ -1,8 +1,6 @@ package org.briarproject.introduction; -import org.briarproject.api.DeviceId; import org.briarproject.api.FormatException; -import org.briarproject.api.TransportId; import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfList; @@ -13,10 +11,10 @@ import org.briarproject.api.sync.Message; import org.briarproject.api.system.Clock; import org.briarproject.clients.BdfMessageValidator; +import static org.briarproject.api.TransportId.MAX_TRANSPORT_ID_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT; -import static org.briarproject.api.introduction.IntroductionConstants.DEVICE_ID; import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY; import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_ID; import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME; @@ -31,6 +29,7 @@ import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ABORT import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK; import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST; import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE; +import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT; import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH; import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH; @@ -102,17 +101,16 @@ class IntroductionValidator extends BdfMessageValidator { private BdfDictionary validateResponse(BdfList message) throws FormatException { - checkSize(message, 3, 7); + checkSize(message, 3, 6); // parse accept/decline boolean accept = message.getBoolean(2); long time = 0; byte[] pubkey = null; - byte[] deviceId = null; BdfDictionary tp = new BdfDictionary(); if (accept) { - checkSize(message, 7); + checkSize(message, 6); // parse timestamp time = message.getLong(3); @@ -121,16 +119,13 @@ class IntroductionValidator extends BdfMessageValidator { pubkey = message.getRaw(4); checkLength(pubkey, 0, MAX_PUBLIC_KEY_LENGTH); - // parse device ID - deviceId = message.getRaw(5); - checkLength(deviceId, DeviceId.LENGTH); - // parse transport properties - tp = message.getDictionary(6); + tp = message.getDictionary(5); if (tp.size() < 1) throw new FormatException(); for (String tId : tp.keySet()) { - checkLength(tId, 1, TransportId.MAX_TRANSPORT_ID_LENGTH); + checkLength(tId, 1, MAX_TRANSPORT_ID_LENGTH); BdfDictionary tProps = tp.getDictionary(tId); + checkSize(tProps, MAX_PROPERTIES_PER_TRANSPORT); for (String propId : tProps.keySet()) { checkLength(propId, 0, MAX_PROPERTY_LENGTH); String prop = tProps.getString(propId); @@ -147,7 +142,6 @@ class IntroductionValidator extends BdfMessageValidator { if (accept) { d.put(TIME, time); d.put(E_PUBLIC_KEY, pubkey); - d.put(DEVICE_ID, deviceId); d.put(TRANSPORT, tp); } return d; diff --git a/briar-core/src/org/briarproject/introduction/MessageEncoder.java b/briar-core/src/org/briarproject/introduction/MessageEncoder.java index 87bf4d5e6a4e63a0a5d35f793d7e30b653020437..12153122cd51323d3975cfcbcc38b48fac39c15a 100644 --- a/briar-core/src/org/briarproject/introduction/MessageEncoder.java +++ b/briar-core/src/org/briarproject/introduction/MessageEncoder.java @@ -5,7 +5,6 @@ import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfList; import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT; -import static org.briarproject.api.introduction.IntroductionConstants.DEVICE_ID; import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY; import static org.briarproject.api.introduction.IntroductionConstants.MSG; import static org.briarproject.api.introduction.IntroductionConstants.NAME; @@ -21,7 +20,8 @@ import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPO public class MessageEncoder { - public static BdfList encodeMessage(BdfDictionary d) throws FormatException { + public static BdfList encodeMessage(BdfDictionary d) + throws FormatException { BdfList body; long type = d.getLong(TYPE); @@ -39,7 +39,8 @@ public class MessageEncoder { return body; } - private static BdfList encodeRequest(BdfDictionary d) throws FormatException { + private static BdfList encodeRequest(BdfDictionary d) + throws FormatException { BdfList list = BdfList.of(TYPE_REQUEST, d.getRaw(SESSION_ID), d.getString(NAME), d.getRaw(PUBLIC_KEY)); @@ -49,14 +50,14 @@ public class MessageEncoder { return list; } - private static BdfList encodeResponse(BdfDictionary d) throws FormatException { + private static BdfList encodeResponse(BdfDictionary d) + throws FormatException { BdfList list = BdfList.of(TYPE_RESPONSE, d.getRaw(SESSION_ID), d.getBoolean(ACCEPT)); if (d.getBoolean(ACCEPT)) { list.add(d.getLong(TIME)); list.add(d.getRaw(E_PUBLIC_KEY)); - list.add(d.getRaw(DEVICE_ID)); list.add(d.getDictionary(TRANSPORT)); } // TODO Sign the response, see #256 diff --git a/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java b/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java index 2eb502cf1e929aa30f9dbed84d11a1ee6d069328..3c9f5fb48ed3d792a8180ae93bd5e8cc1e04e0eb 100644 --- a/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java +++ b/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java @@ -191,7 +191,7 @@ class ConnectionManagerImpl implements ConnectionManager { } if (ctx == null) { LOG.warning("Could not allocate stream context"); - disposeWriter(false); + disposeWriter(true); return; } connectionRegistry.registerConnection(contactId, transportId); @@ -286,7 +286,7 @@ class ConnectionManagerImpl implements ConnectionManager { } if (ctx == null) { LOG.warning("Could not allocate stream context"); - disposeWriter(false); + disposeWriter(true); return; } try { @@ -351,10 +351,9 @@ class ConnectionManagerImpl implements ConnectionManager { } if (ctx == null) { LOG.warning("Could not allocate stream context"); - disposeWriter(false); + disposeWriter(true); return; } - connectionRegistry.registerConnection(contactId, transportId); // Start the incoming session on another thread ioExecutor.execute(new Runnable() { public void run() { @@ -369,8 +368,6 @@ class ConnectionManagerImpl implements ConnectionManager { } catch (IOException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); disposeWriter(true); - } finally { - connectionRegistry.unregisterConnection(contactId, transportId); } } @@ -382,17 +379,17 @@ class ConnectionManagerImpl implements ConnectionManager { ctx = keyManager.getStreamContext(transportId, tag); } catch (IOException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - disposeReader(true, true); + disposeReader(true, false); return; } catch (DbException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - disposeReader(true, true); + disposeReader(true, false); return; } // Unrecognised tags are suspicious in this case if (ctx == null) { LOG.warning("Unrecognised tag for returning stream"); - disposeReader(true, true); + disposeReader(true, false); return; } // Check that the stream comes from the expected contact @@ -401,6 +398,7 @@ class ConnectionManagerImpl implements ConnectionManager { disposeReader(true, true); return; } + connectionRegistry.registerConnection(contactId, transportId); try { // Create and run the incoming session incomingSession = createIncomingSession(ctx, reader); @@ -409,6 +407,8 @@ class ConnectionManagerImpl implements ConnectionManager { } catch (IOException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); disposeReader(true, true); + } finally { + connectionRegistry.unregisterConnection(contactId, transportId); } } diff --git a/briar-core/src/org/briarproject/plugins/ConnectionRegistryImpl.java b/briar-core/src/org/briarproject/plugins/ConnectionRegistryImpl.java index 86cfab5a18b5fdc7d3908af341c274cdbc6d453f..0fe9c3b4b4cae11e0f407d61498acd62a89b9ff9 100644 --- a/briar-core/src/org/briarproject/plugins/ConnectionRegistryImpl.java +++ b/briar-core/src/org/briarproject/plugins/ConnectionRegistryImpl.java @@ -41,7 +41,7 @@ class ConnectionRegistryImpl implements ConnectionRegistry { } public void registerConnection(ContactId c, TransportId t) { - LOG.info("Connection registered"); + if (LOG.isLoggable(INFO)) LOG.info("Connection registered: " + t); boolean firstConnection = false; lock.lock(); try { @@ -63,7 +63,6 @@ class ConnectionRegistryImpl implements ConnectionRegistry { } finally { lock.unlock(); } - if (firstConnection) { LOG.info("Contact connected"); eventBus.broadcast(new ContactConnectedEvent(c)); @@ -71,7 +70,7 @@ class ConnectionRegistryImpl implements ConnectionRegistry { } public void unregisterConnection(ContactId c, TransportId t) { - LOG.info("Connection unregistered"); + if (LOG.isLoggable(INFO)) LOG.info("Connection unregistered: " + t); boolean lastConnection = false; lock.lock(); try { @@ -95,15 +94,13 @@ class ConnectionRegistryImpl implements ConnectionRegistry { } finally { lock.unlock(); } - if (lastConnection) { LOG.info("Contact disconnected"); eventBus.broadcast(new ContactDisconnectedEvent(c)); } } - public Collection<ContactId> getConnectedContacts( - TransportId t) { + public Collection<ContactId> getConnectedContacts(TransportId t) { lock.lock(); try { Map<ContactId, Integer> m = connections.get(t); @@ -115,7 +112,16 @@ class ConnectionRegistryImpl implements ConnectionRegistry { } finally { lock.unlock(); } + } + public boolean isConnected(ContactId c, TransportId t) { + lock.lock(); + try { + Map<ContactId, Integer> m = connections.get(t); + return m != null && m.containsKey(c); + } finally { + lock.unlock(); + } } public boolean isConnected(ContactId c) { @@ -125,6 +131,5 @@ class ConnectionRegistryImpl implements ConnectionRegistry { } finally { lock.unlock(); } - } } diff --git a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java index 9f2f85b978f47a876d0b6701f0cca2d017b756a5..4eb8da75b93907cb3e5ef1a03fbea07f9e74d21c 100644 --- a/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java +++ b/briar-core/src/org/briarproject/plugins/PluginManagerImpl.java @@ -3,13 +3,17 @@ package org.briarproject.plugins; import org.briarproject.api.TransportId; import org.briarproject.api.contact.ContactId; import org.briarproject.api.db.DbException; +import org.briarproject.api.event.ContactStatusChangedEvent; +import org.briarproject.api.event.Event; import org.briarproject.api.event.EventBus; +import org.briarproject.api.event.EventListener; import org.briarproject.api.event.TransportDisabledEvent; import org.briarproject.api.event.TransportEnabledEvent; import org.briarproject.api.lifecycle.IoExecutor; import org.briarproject.api.lifecycle.Service; import org.briarproject.api.lifecycle.ServiceException; import org.briarproject.api.plugins.ConnectionManager; +import org.briarproject.api.plugins.ConnectionRegistry; import org.briarproject.api.plugins.Plugin; import org.briarproject.api.plugins.PluginCallback; import org.briarproject.api.plugins.PluginConfig; @@ -46,7 +50,7 @@ import javax.inject.Inject; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; -class PluginManagerImpl implements PluginManager, Service { +class PluginManagerImpl implements PluginManager, Service, EventListener { private static final Logger LOG = Logger.getLogger(PluginManagerImpl.class.getName()); @@ -56,6 +60,7 @@ class PluginManagerImpl implements PluginManager, Service { private final PluginConfig pluginConfig; private final Poller poller; private final ConnectionManager connectionManager; + private final ConnectionRegistry connectionRegistry; private final SettingsManager settingsManager; private final TransportPropertyManager transportPropertyManager; private final UiCallback uiCallback; @@ -67,6 +72,7 @@ class PluginManagerImpl implements PluginManager, Service { PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus, PluginConfig pluginConfig, Poller poller, ConnectionManager connectionManager, + ConnectionRegistry connectionRegistry, SettingsManager settingsManager, TransportPropertyManager transportPropertyManager, UiCallback uiCallback) { @@ -75,6 +81,7 @@ class PluginManagerImpl implements PluginManager, Service { this.pluginConfig = pluginConfig; this.poller = poller; this.connectionManager = connectionManager; + this.connectionRegistry = connectionRegistry; this.settingsManager = settingsManager; this.transportPropertyManager = transportPropertyManager; this.uiCallback = uiCallback; @@ -106,10 +113,14 @@ class PluginManagerImpl implements PluginManager, Service { } catch (InterruptedException e) { throw new ServiceException(e); } + // Listen for events + eventBus.addListener(this); } @Override public void stopService() throws ServiceException { + // Stop listening for events + eventBus.removeListener(this); // Stop the poller LOG.info("Stopping poller"); poller.stop(); @@ -122,9 +133,6 @@ class PluginManagerImpl implements PluginManager, Service { LOG.info("Stopping duplex plugins"); for (DuplexPlugin plugin : duplexPlugins) ioExecutor.execute(new PluginStopper(plugin, latch)); - plugins.clear(); - simplexPlugins.clear(); - duplexPlugins.clear(); // Wait for all the plugins to stop try { latch.await(); @@ -151,6 +159,47 @@ class PluginManagerImpl implements PluginManager, Service { return Collections.unmodifiableList(supported); } + @Override + public void eventOccurred(Event e) { + if (e instanceof ContactStatusChangedEvent) { + ContactStatusChangedEvent c = (ContactStatusChangedEvent) e; + if (c.isActive()) connectToContact(c.getContactId()); + } + } + + private void connectToContact(ContactId c) { + for (SimplexPlugin s : simplexPlugins) + if (s.shouldPoll()) connectToContact(c, s); + for (DuplexPlugin d : duplexPlugins) + if (d.shouldPoll()) connectToContact(c, d); + } + + private void connectToContact(final ContactId c, final SimplexPlugin p) { + ioExecutor.execute(new Runnable() { + public void run() { + TransportId t = p.getId(); + if (!connectionRegistry.isConnected(c, t)) { + TransportConnectionWriter w = p.createWriter(c); + if (w != null) + connectionManager.manageOutgoingConnection(c, t, w); + } + } + }); + } + + private void connectToContact(final ContactId c, final DuplexPlugin p) { + ioExecutor.execute(new Runnable() { + public void run() { + TransportId t = p.getId(); + if (!connectionRegistry.isConnected(c, t)) { + DuplexTransportConnection d = p.createConnection(c); + if (d != null) + connectionManager.manageOutgoingConnection(c, t, d); + } + } + }); + } + private class SimplexPluginStarter implements Runnable { private final SimplexPluginFactory factory; diff --git a/briar-core/src/org/briarproject/plugins/PollerImpl.java b/briar-core/src/org/briarproject/plugins/PollerImpl.java index 50e79b3b27f77349908628121cd73597a6aee637..938997a7646d573188bd8716871c5196e10347b1 100644 --- a/briar-core/src/org/briarproject/plugins/PollerImpl.java +++ b/briar-core/src/org/briarproject/plugins/PollerImpl.java @@ -45,12 +45,13 @@ class PollerImpl implements Poller { public void addPlugin(Plugin p) { // Randomise first polling interval - schedule(p, randomise(p.getPollingInterval()), false); + if (p.shouldPoll()) + schedule(p, randomise(p.getPollingInterval()), false); } public void pollNow(Plugin p) { // Randomise next polling interval - schedule(p, 0, true); + if (p.shouldPoll()) schedule(p, 0, true); } private int randomise(int interval) { diff --git a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java index f393e3eaa29f9c49a88df097e189797d11e5d18c..5663b6151a823399eaf800f8031bdf2dfd281ea8 100644 --- a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java +++ b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java @@ -1,6 +1,5 @@ package org.briarproject.properties; -import org.briarproject.api.DeviceId; import org.briarproject.api.FormatException; import org.briarproject.api.TransportId; import org.briarproject.api.clients.Client; @@ -73,10 +72,9 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, db.addGroup(txn, g); db.setVisibleToContact(txn, c.getId(), g.getId(), true); // Copy the latest local properties into the group - DeviceId dev = db.getDeviceId(txn); Map<TransportId, TransportProperties> local = getLocalProperties(txn); for (Entry<TransportId, TransportProperties> e : local.entrySet()) { - storeMessage(txn, g.getId(), dev, e.getKey(), e.getValue(), 1, + storeMessage(txn, g.getId(), e.getKey(), e.getValue(), 1, true, true); } } @@ -87,11 +85,11 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, } @Override - public void addRemoteProperties(Transaction txn, ContactId c, DeviceId dev, + public void addRemoteProperties(Transaction txn, ContactId c, Map<TransportId, TransportProperties> props) throws DbException { Group g = getContactGroup(db.getContact(txn, c)); for (Entry<TransportId, TransportProperties> e : props.entrySet()) { - storeMessage(txn, g.getId(), dev, e.getKey(), e.getValue(), 0, + storeMessage(txn, g.getId(), e.getKey(), e.getValue(), 0, false, false); } } @@ -189,16 +187,15 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, } if (changed) { // Store the merged properties in the local group - DeviceId dev = db.getDeviceId(txn); long version = latest == null ? 1 : latest.version + 1; - storeMessage(txn, localGroup.getId(), dev, t, merged, - version, true, false); + storeMessage(txn, localGroup.getId(), t, merged, version, + true, false); // Store the merged properties in each contact's group for (Contact c : db.getContacts(txn)) { Group g = getContactGroup(c); latest = findLatest(txn, g.getId(), t, true); version = latest == null ? 1 : latest.version + 1; - storeMessage(txn, g.getId(), dev, t, merged, version, + storeMessage(txn, g.getId(), t, merged, version, true, true); } } @@ -235,11 +232,11 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, } } - private void storeMessage(Transaction txn, GroupId g, DeviceId dev, - TransportId t, TransportProperties p, long version, boolean local, - boolean shared) throws DbException { + private void storeMessage(Transaction txn, GroupId g, TransportId t, + TransportProperties p, long version, boolean local, boolean shared) + throws DbException { try { - BdfList body = encodeProperties(dev, t, p, version); + BdfList body = encodeProperties(t, p, version); long now = clock.currentTimeMillis(); Message m = clientHelper.createMessage(g, now, body); BdfDictionary meta = new BdfDictionary(); @@ -252,9 +249,9 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, } } - private BdfList encodeProperties(DeviceId dev, TransportId t, - TransportProperties p, long version) { - return BdfList.of(dev, t.getString(), version, p); + private BdfList encodeProperties(TransportId t, TransportProperties p, + long version) { + return BdfList.of(t.getString(), version, p); } private Map<TransportId, LatestUpdate> findLatest(Transaction txn, @@ -295,8 +292,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, private TransportProperties parseProperties(BdfList message) throws FormatException { - // Device ID, transport ID, version, properties - BdfDictionary dictionary = message.getDictionary(3); + // Transport ID, version, properties + BdfDictionary dictionary = message.getDictionary(2); TransportProperties p = new TransportProperties(); for (String key : dictionary.keySet()) p.put(key, dictionary.getString(key)); diff --git a/briar-core/src/org/briarproject/properties/TransportPropertyValidator.java b/briar-core/src/org/briarproject/properties/TransportPropertyValidator.java index b26775d7360abed07d1b69ec9a80b946f79071cb..fc40dd342a65ccc619fc7071119590270a66c6e4 100644 --- a/briar-core/src/org/briarproject/properties/TransportPropertyValidator.java +++ b/briar-core/src/org/briarproject/properties/TransportPropertyValidator.java @@ -1,7 +1,6 @@ package org.briarproject.properties; import org.briarproject.api.FormatException; -import org.briarproject.api.UniqueId; import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfList; @@ -25,19 +24,16 @@ public class TransportPropertyValidator extends BdfMessageValidator { @Override protected BdfDictionary validateMessage(Message m, Group g, BdfList body) throws FormatException { - // Device ID, transport ID, version, properties - checkSize(body, 4); - // Device ID - byte[] deviceId = body.getRaw(0); - checkLength(deviceId, UniqueId.LENGTH); + // Transport ID, version, properties + checkSize(body, 3); // Transport ID - String transportId = body.getString(1); + String transportId = body.getString(0); checkLength(transportId, 1, MAX_TRANSPORT_ID_LENGTH); // Version - long version = body.getLong(2); + long version = body.getLong(1); if (version < 0) throw new FormatException(); // Properties - BdfDictionary dictionary = body.getDictionary(3); + BdfDictionary dictionary = body.getDictionary(2); checkSize(dictionary, 0, MAX_PROPERTIES_PER_TRANSPORT); for (String key : dictionary.keySet()) { checkLength(key, 0, MAX_PROPERTY_LENGTH); diff --git a/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java b/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java index 872985ff15f92e9759349064cbe26c40dd4014f1..6627225c3ddf3417e92b3a2316d4d52baab1e6f9 100644 --- a/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java +++ b/briar-tests/src/org/briarproject/plugins/PluginManagerImplTest.java @@ -1,13 +1,20 @@ package org.briarproject.plugins; import org.briarproject.BriarTestCase; +import org.briarproject.ImmediateExecutor; import org.briarproject.api.TransportId; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.event.ContactStatusChangedEvent; import org.briarproject.api.event.EventBus; +import org.briarproject.api.event.EventListener; import org.briarproject.api.plugins.ConnectionManager; +import org.briarproject.api.plugins.ConnectionRegistry; import org.briarproject.api.plugins.PluginConfig; +import org.briarproject.api.plugins.TransportConnectionWriter; import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginFactory; +import org.briarproject.api.plugins.duplex.DuplexTransportConnection; import org.briarproject.api.plugins.simplex.SimplexPlugin; import org.briarproject.api.plugins.simplex.SimplexPluginCallback; import org.briarproject.api.plugins.simplex.SimplexPluginFactory; @@ -36,6 +43,8 @@ public class PluginManagerImplTest extends BriarTestCase { final Poller poller = context.mock(Poller.class); final ConnectionManager connectionManager = context.mock(ConnectionManager.class); + final ConnectionRegistry connectionRegistry = + context.mock(ConnectionRegistry.class); final SettingsManager settingsManager = context.mock(SettingsManager.class); final TransportPropertyManager transportPropertyManager = @@ -63,6 +72,7 @@ public class PluginManagerImplTest extends BriarTestCase { final TransportId duplexFailId = new TransportId("duplex1"); context.checking(new Expectations() {{ + // start() // First simplex plugin oneOf(pluginConfig).getSimplexFactories(); will(returnValue(Arrays.asList(simplexFactory, @@ -103,6 +113,11 @@ public class PluginManagerImplTest extends BriarTestCase { oneOf(duplexFailFactory).createPlugin(with(any( DuplexPluginCallback.class))); will(returnValue(null)); // Failed to create a plugin + // Start listening for events + oneOf(eventBus).addListener(with(any(EventListener.class))); + // stop() + // Stop listening for events + oneOf(eventBus).removeListener(with(any(EventListener.class))); // Stop the poller oneOf(poller).stop(); // Stop the plugins @@ -111,8 +126,8 @@ public class PluginManagerImplTest extends BriarTestCase { }}); PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus, - pluginConfig, poller, connectionManager, settingsManager, - transportPropertyManager, uiCallback); + pluginConfig, poller, connectionManager, connectionRegistry, + settingsManager, transportPropertyManager, uiCallback); // Two plugins should be started and stopped p.startService(); @@ -120,4 +135,151 @@ public class PluginManagerImplTest extends BriarTestCase { context.assertIsSatisfied(); } + + @Test + public void testConnectToNewContact() throws Exception { + Mockery context = new Mockery(); + final Executor ioExecutor = new ImmediateExecutor(); + final EventBus eventBus = context.mock(EventBus.class); + final PluginConfig pluginConfig = context.mock(PluginConfig.class); + final Poller poller = context.mock(Poller.class); + final ConnectionManager connectionManager = + context.mock(ConnectionManager.class); + final ConnectionRegistry connectionRegistry = + context.mock(ConnectionRegistry.class); + final SettingsManager settingsManager = + context.mock(SettingsManager.class); + final TransportPropertyManager transportPropertyManager = + context.mock(TransportPropertyManager.class); + final UiCallback uiCallback = context.mock(UiCallback.class); + final TransportConnectionWriter transportConnectionWriter = + context.mock(TransportConnectionWriter.class); + final DuplexTransportConnection duplexTransportConnection = + context.mock(DuplexTransportConnection.class); + + final ContactId contactId = new ContactId(234); + + // Two simplex plugins: one supports polling, the other doesn't + final SimplexPluginFactory simplexFactory = + context.mock(SimplexPluginFactory.class); + final SimplexPlugin simplexPlugin = context.mock(SimplexPlugin.class); + final TransportId simplexId = new TransportId("simplex"); + final SimplexPluginFactory simplexFactory1 = + context.mock(SimplexPluginFactory.class, "simplexFactory1"); + final SimplexPlugin simplexPlugin1 = + context.mock(SimplexPlugin.class, "simplexPlugin1"); + final TransportId simplexId1 = new TransportId("simplex1"); + + // Two duplex plugins: one supports polling, the other doesn't + final DuplexPluginFactory duplexFactory = + context.mock(DuplexPluginFactory.class); + final DuplexPlugin duplexPlugin = context.mock(DuplexPlugin.class); + final TransportId duplexId = new TransportId("duplex"); + final DuplexPluginFactory duplexFactory1 = + context.mock(DuplexPluginFactory.class, "duplexFactory1"); + final DuplexPlugin duplexPlugin1 = + context.mock(DuplexPlugin.class, "duplexPlugin1"); + final TransportId duplexId1 = new TransportId("duplex1"); + + context.checking(new Expectations() {{ + // start() + // First simplex plugin + oneOf(pluginConfig).getSimplexFactories(); + will(returnValue(Arrays.asList(simplexFactory, simplexFactory1))); + oneOf(simplexFactory).getId(); + will(returnValue(simplexId)); + oneOf(simplexFactory).createPlugin(with(any( + SimplexPluginCallback.class))); + will(returnValue(simplexPlugin)); // Created + oneOf(simplexPlugin).start(); + will(returnValue(true)); // Started + oneOf(simplexPlugin).shouldPoll(); + will(returnValue(true)); // Should poll + oneOf(poller).addPlugin(simplexPlugin); + // Second simplex plugin + oneOf(simplexFactory1).getId(); + will(returnValue(simplexId1)); + oneOf(simplexFactory1).createPlugin(with(any( + SimplexPluginCallback.class))); + will(returnValue(simplexPlugin1)); // Created + oneOf(simplexPlugin1).start(); + will(returnValue(true)); // Started + oneOf(simplexPlugin1).shouldPoll(); + will(returnValue(false)); // Should not poll + // First duplex plugin + oneOf(pluginConfig).getDuplexFactories(); + will(returnValue(Arrays.asList(duplexFactory, duplexFactory1))); + oneOf(duplexFactory).getId(); + will(returnValue(duplexId)); + oneOf(duplexFactory).createPlugin(with(any( + DuplexPluginCallback.class))); + will(returnValue(duplexPlugin)); // Created + oneOf(duplexPlugin).start(); + will(returnValue(true)); // Started + oneOf(duplexPlugin).shouldPoll(); + will(returnValue(true)); // Should poll + oneOf(poller).addPlugin(duplexPlugin); + // Second duplex plugin + oneOf(duplexFactory1).getId(); + will(returnValue(duplexId1)); + oneOf(duplexFactory1).createPlugin(with(any( + DuplexPluginCallback.class))); + will(returnValue(duplexPlugin1)); // Created + oneOf(duplexPlugin1).start(); + will(returnValue(true)); // Started + oneOf(duplexPlugin1).shouldPoll(); + will(returnValue(false)); // Should not poll + // Start listening for events + oneOf(eventBus).addListener(with(any(EventListener.class))); + // eventOccurred() + // First simplex plugin + oneOf(simplexPlugin).shouldPoll(); + will(returnValue(true)); + oneOf(simplexPlugin).getId(); + will(returnValue(simplexId)); + oneOf(connectionRegistry).isConnected(contactId, simplexId); + will(returnValue(false)); + oneOf(simplexPlugin).createWriter(contactId); + will(returnValue(transportConnectionWriter)); + oneOf(connectionManager).manageOutgoingConnection(contactId, + simplexId, transportConnectionWriter); + // Second simplex plugin + oneOf(simplexPlugin1).shouldPoll(); + will(returnValue(false)); + // First duplex plugin + oneOf(duplexPlugin).shouldPoll(); + will(returnValue(true)); + oneOf(duplexPlugin).getId(); + will(returnValue(duplexId)); + oneOf(connectionRegistry).isConnected(contactId, duplexId); + will(returnValue(false)); + oneOf(duplexPlugin).createConnection(contactId); + will(returnValue(duplexTransportConnection)); + oneOf(connectionManager).manageOutgoingConnection(contactId, + duplexId, duplexTransportConnection); + // Second duplex plugin + oneOf(duplexPlugin1).shouldPoll(); + will(returnValue(false)); + // stop() + // Stop listening for events + oneOf(eventBus).removeListener(with(any(EventListener.class))); + // Stop the poller + oneOf(poller).stop(); + // Stop the plugins + oneOf(simplexPlugin).stop(); + oneOf(simplexPlugin1).stop(); + oneOf(duplexPlugin).stop(); + oneOf(duplexPlugin1).stop(); + }}); + + PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus, + pluginConfig, poller, connectionManager, connectionRegistry, + settingsManager, transportPropertyManager, uiCallback); + + p.startService(); + p.eventOccurred(new ContactStatusChangedEvent(contactId, true)); + p.stopService(); + + context.assertIsSatisfied(); + } } diff --git a/briar-tests/src/org/briarproject/properties/TransportPropertyValidatorTest.java b/briar-tests/src/org/briarproject/properties/TransportPropertyValidatorTest.java index 94378cb0130a6d4c098e0d15d56b41e9bb334f7c..bc61508117b31fcb0a4425022c4122f5d50e3614 100644 --- a/briar-tests/src/org/briarproject/properties/TransportPropertyValidatorTest.java +++ b/briar-tests/src/org/briarproject/properties/TransportPropertyValidatorTest.java @@ -2,10 +2,8 @@ package org.briarproject.properties; import org.briarproject.BriarTestCase; import org.briarproject.TestUtils; -import org.briarproject.api.DeviceId; import org.briarproject.api.FormatException; import org.briarproject.api.TransportId; -import org.briarproject.api.UniqueId; import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfList; @@ -28,110 +26,83 @@ import static org.junit.Assert.assertEquals; public class TransportPropertyValidatorTest extends BriarTestCase { private final TransportId transportId; - private final DeviceId deviceId; private final BdfDictionary bdfDictionary; private final Group group; private final Message message; private final TransportPropertyValidator tpv; - public TransportPropertyValidatorTest() { + public TransportPropertyValidatorTest() { transportId = new TransportId("test"); - deviceId = new DeviceId(TestUtils.getRandomId()); - bdfDictionary = new BdfDictionary(); + bdfDictionary = new BdfDictionary(); - GroupId groupId = new GroupId(TestUtils.getRandomId()); - ClientId clientId = new ClientId(TestUtils.getRandomId()); - byte[] descriptor = TestUtils.getRandomBytes(12); - group = new Group(groupId, clientId, descriptor); + GroupId groupId = new GroupId(TestUtils.getRandomId()); + ClientId clientId = new ClientId(TestUtils.getRandomId()); + byte[] descriptor = TestUtils.getRandomBytes(12); + group = new Group(groupId, clientId, descriptor); - MessageId messageId = new MessageId(TestUtils.getRandomId()); - long timestamp = System.currentTimeMillis(); - byte[] body = TestUtils.getRandomBytes(123); - message = new Message(messageId, groupId, timestamp, body); + MessageId messageId = new MessageId(TestUtils.getRandomId()); + long timestamp = System.currentTimeMillis(); + byte[] body = TestUtils.getRandomBytes(123); + message = new Message(messageId, groupId, timestamp, body); - Mockery context = new Mockery(); - ClientHelper clientHelper = context.mock(ClientHelper.class); - MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class); - Clock clock = context.mock(Clock.class); + Mockery context = new Mockery(); + ClientHelper clientHelper = context.mock(ClientHelper.class); + MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class); + Clock clock = context.mock(Clock.class); tpv = new TransportPropertyValidator(clientHelper, metadataEncoder, clock); - } - - @Test - public void testValidateProperMessage() throws IOException { - - BdfList body = BdfList.of(deviceId, transportId.getString(), 4, - bdfDictionary); - - BdfDictionary result = tpv.validateMessage(message, group, body); - - assertEquals("test", result.getString("transportId")); - assertEquals(result.getLong("version").longValue(), 4); } - @Test(expected = FormatException.class) - public void testValidateWrongVersionValue() throws IOException { + @Test + public void testValidateProperMessage() throws IOException { - /* Will create a negative version number */ - BdfList body = BdfList.of(deviceId, transportId.getString(), -1, - bdfDictionary); - tpv.validateMessage(message, group, body); - } + BdfList body = BdfList.of(transportId.getString(), 4, bdfDictionary); - @Test(expected = FormatException.class) - public void testValidateWrongVersionType() throws IOException { + BdfDictionary result = tpv.validateMessage(message, group, body); - /* Instead of sending a version number I'm sending a dict */ - BdfList body = BdfList.of(deviceId, transportId.getString(), - bdfDictionary, bdfDictionary); - tpv.validateMessage(message, group, body); + assertEquals("test", result.getString("transportId")); + assertEquals(4, result.getLong("version").longValue()); } - @Test(expected = FormatException.class) - public void testValidateShortDeviceId() throws IOException { + @Test(expected = FormatException.class) + public void testValidateWrongVersionValue() throws IOException { - /* Will create a Device Id with a short length, getRaw should work */ - BdfList body = BdfList.of(new byte[UniqueId.LENGTH - 1], - transportId.getString(), 1, bdfDictionary); + BdfList body = BdfList.of(transportId.getString(), -1, bdfDictionary); tpv.validateMessage(message, group, body); } - @Test(expected = FormatException.class) - public void testValidateLongDeviceId() throws IOException { + @Test(expected = FormatException.class) + public void testValidateWrongVersionType() throws IOException { - BdfList body = BdfList.of(new byte[UniqueId.LENGTH + 1], - transportId.getString(), 1, bdfDictionary); + BdfList body = BdfList.of(transportId.getString(), bdfDictionary, + bdfDictionary); tpv.validateMessage(message, group, body); } - @Test(expected = FormatException.class) - public void testValidateWrongDeviceId() throws IOException { + @Test(expected = FormatException.class) + public void testValidateLongTransportId() throws IOException { - BdfList body = BdfList.of(bdfDictionary, transportId.getString(), 1, - bdfDictionary); + String wrongTransportIdString = + TestUtils.getRandomString(MAX_TRANSPORT_ID_LENGTH + 1); + BdfList body = BdfList.of(wrongTransportIdString, 4, bdfDictionary); tpv.validateMessage(message, group, body); } - @Test(expected = FormatException.class) - public void testValidateLongTransportId() throws IOException { + @Test(expected = FormatException.class) + public void testValidateEmptyTransportId() throws IOException { - /* Generate a string or arbitrary length for the transport id*/ - String wrongTransportIdString = - TestUtils.getRandomString(MAX_TRANSPORT_ID_LENGTH + 1); - BdfList body = BdfList.of(deviceId, wrongTransportIdString, 4, - bdfDictionary); + BdfList body = BdfList.of("", 4, bdfDictionary); tpv.validateMessage(message, group, body); } - @Test(expected = FormatException.class) - public void testValidateTooManyProperties() throws IOException { + @Test(expected = FormatException.class) + public void testValidateTooManyProperties() throws IOException { - /* Generate a big map for the BdfDictionary*/ - BdfDictionary d = new BdfDictionary(); + BdfDictionary d = new BdfDictionary(); for (int i = 0; i < MAX_PROPERTIES_PER_TRANSPORT + 1; i++) d.put(String.valueOf(i), i); - BdfList body = BdfList.of(deviceId, transportId.getString(), 4, d); + BdfList body = BdfList.of(transportId.getString(), 4, d); tpv.validateMessage(message, group, body); } }